fix: canvas image/video download - fix data URL parsing in cvDownloadPreview (v1.5.21, hot 1.5.23)
This commit is contained in:
+102
-1
@@ -1,6 +1,6 @@
|
||||
// main.js —— 主进程:窗口、IPC、GitHub(isomorphic-git)、市场 API。
|
||||
// 市场地址/密钥【只在主进程】,绝不下发前端。契约见 CONTRACT.md。
|
||||
const { app, BrowserWindow, ipcMain, dialog, shell, Tray, Menu, nativeImage } = require("electron");
|
||||
const { app, BrowserWindow, ipcMain, dialog, shell, Tray, Menu, nativeImage, clipboard } = require("electron");
|
||||
const { autoUpdater } = require("electron-updater");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
@@ -15,7 +15,11 @@ const sessions = require("./lib/sessions");
|
||||
const providerSync = require("./lib/provider_sync");
|
||||
const router = require("./lib/router");
|
||||
const canvas = require("./lib/canvas");
|
||||
const team = require("./lib/team");
|
||||
const promptAnalysis = require("./lib/prompt-analysis");
|
||||
const claudedesk = require("./lib/claudedesk");
|
||||
const voice = require("./lib/voice_cdp");
|
||||
const tokenWatch = require("./lib/token_watch");
|
||||
const proxy = require("./lib/proxy");
|
||||
const memory = require("./lib/memory");
|
||||
const { extractZip } = require("./lib/zip");
|
||||
@@ -235,6 +239,20 @@ app.whenReady().then(async () => {
|
||||
} catch (e) {
|
||||
log(`启动迁移 plugin 异常: ${e.message || e}`);
|
||||
}
|
||||
// 启动自愈:把当前激活分组的全部文本模型先回填进 Codex 模型目录。
|
||||
// 目的——用户在 Codex 本体的模型选择器里切模型(不经本 App)时,若目标模型不在目录里,
|
||||
// Codex 会以未知模型启动、侧栏历史还原失败 = "切模型后会话记录消失"。先回填即根治,
|
||||
// 且既有安装无需重新保存分组即可自动修复。异步、失败不动目录(离线安全)。
|
||||
try {
|
||||
const active = router.llmGroup(); // 当前激活且配齐 baseUrl+key+model 的中转分组(排除默认直连)
|
||||
if (active) {
|
||||
router.syncCatalogFromProvider(active)
|
||||
.then((r) => { if (r && r.ok && (r.added || r.revealed)) log(`启动同步模型目录:新增 ${r.added} · 显示 ${r.revealed} · 共 ${r.total}`); })
|
||||
.catch((e) => log(`启动同步模型目录失败:${e.message || e}`));
|
||||
}
|
||||
} catch (e) {
|
||||
log(`启动同步模型目录异常:${e.message || e}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 统一退出:清理代理(杀 sing-box + 还原网络) → 停本地路由 → 销毁托盘 → 退出。
|
||||
@@ -1323,6 +1341,28 @@ ipcMain.handle("router-fetch-models", async (_e, { baseUrl, key, groupId }) => {
|
||||
return router.fetchModels(baseUrl, k);
|
||||
});
|
||||
|
||||
// —— 接入 Claude 桌面 App(第三方推理 / gateway 模式)——
|
||||
ipcMain.handle("claudedesk-status", () => claudedesk.status());
|
||||
ipcMain.handle("claudedesk-apply", (_e, { groupId }) => claudedesk.applyGroup(groupId));
|
||||
ipcMain.handle("claudedesk-restore", () => claudedesk.restoreOfficial());
|
||||
ipcMain.handle("claudedesk-snippet", (_e, { groupId }) => {
|
||||
const r = claudedesk.snippet(groupId);
|
||||
if (r.ok) { try { clipboard.writeText(r.text); } catch {} }
|
||||
return r.ok ? { ok: true } : r;
|
||||
});
|
||||
ipcMain.handle("claudedesk-export", async (_e, { groupId, format }) => {
|
||||
const r = claudedesk.exportConfig(groupId, format);
|
||||
if (!r.ok) return r;
|
||||
const dl = await dialog.showSaveDialog(mainWindow, {
|
||||
title: "导出 Claude 桌面配置",
|
||||
defaultPath: r.filename,
|
||||
filters: [{ name: r.ext.toUpperCase(), extensions: [r.ext] }],
|
||||
});
|
||||
if (dl.canceled || !dl.filePath) return { ok: false, canceled: true };
|
||||
try { fs.writeFileSync(dl.filePath, r.content); } catch (e) { return { ok: false, error: String(e.message || e) }; }
|
||||
return { ok: true, path: dl.filePath };
|
||||
});
|
||||
|
||||
// —— 创意画布(中转执行层)——
|
||||
ipcMain.handle("canvas-status", () => canvas.status());
|
||||
ipcMain.handle("canvas-run", (e, { kind, params }) =>
|
||||
@@ -1335,6 +1375,63 @@ ipcMain.handle("canvas-run", (e, { kind, params }) =>
|
||||
ipcMain.handle("canvas-groups", () => canvas.groups());
|
||||
ipcMain.handle("canvas-models", (_e, { groupId }) => canvas.listModels(groupId));
|
||||
|
||||
// —— 多 Agent 员工团队生成(需求 → 团队 → 打包 zip)——
|
||||
ipcMain.handle("team-generate", async (_e, params) => {
|
||||
try {
|
||||
const result = await team.generate(params || {});
|
||||
return { ok: true, result };
|
||||
} catch (err) {
|
||||
return { ok: false, error: String((err && err.message) || err) };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("team-preview", (_e, params) => {
|
||||
try {
|
||||
return { ok: true, ...team.preview(params || {}) };
|
||||
} catch (err) {
|
||||
return { ok: false, error: String((err && err.message) || err) };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("team-pack", async (_e, params) => {
|
||||
try {
|
||||
const { buffer, filename } = team.pack(params || {});
|
||||
const r = await dialog.showSaveDialog(mainWindow, {
|
||||
title: "保存员工团队配置包",
|
||||
defaultPath: filename,
|
||||
filters: [{ name: "Zip 压缩包", extensions: ["zip"] }],
|
||||
});
|
||||
if (r.canceled || !r.filePath) return { ok: false, canceled: true };
|
||||
fs.writeFileSync(r.filePath, buffer);
|
||||
return { ok: true, path: r.filePath };
|
||||
} catch (err) {
|
||||
return { ok: false, error: String((err && err.message) || err) };
|
||||
}
|
||||
});
|
||||
|
||||
// —— 提示词提取(图片 → 反推生图提示词)——
|
||||
ipcMain.handle("prompt-groups", () => {
|
||||
try {
|
||||
return { ok: true, groups: promptAnalysis.listGroups() };
|
||||
} catch (err) {
|
||||
return { ok: false, error: String((err && err.message) || err) };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("prompt-models", async (_e, { groupId }) => {
|
||||
try {
|
||||
const models = await promptAnalysis.listModels(groupId);
|
||||
return { ok: true, models };
|
||||
} catch (err) {
|
||||
return { ok: false, error: String((err && err.message) || err) };
|
||||
}
|
||||
});
|
||||
ipcMain.handle("prompt-analyze", async (_e, params) => {
|
||||
try {
|
||||
const result = await promptAnalysis.analyze(params || {});
|
||||
return { ok: true, ...result };
|
||||
} catch (err) {
|
||||
return { ok: false, error: String((err && err.message) || err) };
|
||||
}
|
||||
});
|
||||
|
||||
// —— Codex 语音输入注入(CDP)——
|
||||
ipcMain.handle("voice-status", () => voice.status());
|
||||
ipcMain.handle("voice-connect", (e, opts) =>
|
||||
@@ -1348,6 +1445,10 @@ ipcMain.handle("voice-disconnect", () => voice.disconnect());
|
||||
ipcMain.handle("voice-stt-info", () => voice.sttInfo());
|
||||
ipcMain.handle("voice-probe-stt", async () => voice.probeStt());
|
||||
|
||||
// —— Token 用量监控(注入 Codex 页面,复用 CDP 连接)——
|
||||
ipcMain.handle("token-status", async () => tokenWatch.tokenStatus());
|
||||
ipcMain.handle("token-reset", async () => tokenWatch.tokenReset());
|
||||
|
||||
// —— 全局 / 系统代理 ——
|
||||
ipcMain.handle("proxy-status", async () => proxy.status());
|
||||
ipcMain.handle("proxy-get-config", async () => proxy.getConfig());
|
||||
|
||||
Reference in New Issue
Block a user