fix: canvas image/video download - fix data URL parsing in cvDownloadPreview (v1.5.21, hot 1.5.23)

This commit is contained in:
mac
2026-07-20 08:48:56 +08:00
parent 42e23663ee
commit aadd2b04ae
28 changed files with 5881 additions and 329 deletions
+102 -1
View File
@@ -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());