feat: 视觉能力去 Electron 化,经 HostBridge.screen 注入(迁移阶段1-A9)
- HostBridge 新增可选 screen 服务(listDisplays/primaryDisplay/dipToScreenPoint/ displayMatching/captureDisplay),vision 3 处、cv-vision 2 处动态 import(electron) 全部改为 requireScreenService();未注入时抛稳定的 host_unavailable - demo 宿主 src/main/index.ts 用 desktopCapturer/screen 实现 screen 服务 - check-deps.mjs 升级:新增动态 import()/require() 扫描(旧正则实测 0 命中的盲区), 合成用例验证命中,升级后真实源码 0 违规 - 包依赖闭包:补声明 @cantoo/pdf-lib、@napi-rs/canvas、pdfjs-dist - cv-vision 测试从 vi.mock(electron) 改为宿主桥注入,11/11 通过
This commit is contained in:
1 parent
1da00827ac
commit
1a20dd72d1
7 files changed
+161
-62
No files matched your search
+35
-3
@@ -1,4 +1,4 @@
|
||||
import { app, BrowserWindow, dialog, ipcMain, screen, shell } from "electron";
|
||||
import { app, BrowserWindow, desktopCapturer, dialog, ipcMain, screen, shell } from "electron";
|
||||
import path from "node:path";
|
||||
import type { AppInfo, AppSettings, FlowDefinition, FlowSaveOptions, FlowSaveResult, FlowSummary, FlowsImportResult } from "../shared/types";
|
||||
import {
|
||||
@@ -24,7 +24,38 @@ import { logMain } from "./logger";
|
||||
// 子流程仓库在宿主(flows.ts),包内能力经 host-bridge 取用(阶段 2 正式化 HostBridge)
|
||||
setSubflowSource({ listFlows: async () => listFlows(), getFlow: async (id) => getFlow(id) });
|
||||
|
||||
// 宿主能力桥(决策点 4 最小面):人机交互窗口工厂 / DPI 坐标换算 / AI 配置读取
|
||||
// 宿主屏幕服务(迁移阶段1-A9):视觉能力经宿主桥拿 desktopCapturer/screen,Core 零 Electron 依赖
|
||||
const hostScreenService = {
|
||||
listDisplays: () =>
|
||||
screen.getAllDisplays().map((display) => ({
|
||||
id: String(display.id),
|
||||
bounds: display.bounds,
|
||||
scaleFactor: display.scaleFactor || 1
|
||||
})),
|
||||
primaryDisplay: () => {
|
||||
const display = screen.getPrimaryDisplay();
|
||||
return { id: String(display.id), bounds: display.bounds, scaleFactor: display.scaleFactor || 1 };
|
||||
},
|
||||
dipToScreenPoint: (point: { x: number; y: number }) => screen.dipToScreenPoint(point),
|
||||
displayMatching: (rect: { x: number; y: number; width: number; height: number }) => {
|
||||
const display = screen.getDisplayMatching(rect);
|
||||
return { id: String(display.id), bounds: display.bounds, scaleFactor: display.scaleFactor || 1 };
|
||||
},
|
||||
captureDisplay: async (displayId: string, wantWidth: number, wantHeight: number): Promise<Uint8Array> => {
|
||||
const sources = await desktopCapturer.getSources({
|
||||
types: ["screen"],
|
||||
thumbnailSize: { width: wantWidth, height: wantHeight }
|
||||
});
|
||||
const source =
|
||||
sources.find((item) => item.display_id === displayId) ||
|
||||
sources.find((item) => item.thumbnail.getSize().width === wantWidth) ||
|
||||
sources[0];
|
||||
if (!source || source.thumbnail.isEmpty()) throw new Error(`无法获取显示器 ${displayId} 的屏幕截图`);
|
||||
return source.thumbnail.toPNG();
|
||||
}
|
||||
};
|
||||
|
||||
// 宿主能力桥(决策点 4 最小面):人机交互窗口工厂 / DPI 坐标换算 / AI 配置读取 / 屏幕服务
|
||||
setHostBridge({
|
||||
createFormWindow: ({ title }) => {
|
||||
const win = new BrowserWindow({
|
||||
@@ -46,7 +77,8 @@ setHostBridge({
|
||||
loadAiConfig: () => {
|
||||
const ai = loadSettings().aiConfig;
|
||||
return { baseUrl: ai.baseUrl ?? "", apiKey: ai.apiKey ?? "", model: ai.model ?? "", visionModel: ai.visionModel ?? "" };
|
||||
}
|
||||
},
|
||||
screen: hostScreenService
|
||||
});
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
Reference in new issue
Block a user