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
+232 -6
View File
@@ -84,6 +84,13 @@
<p class="muted">tokendun 官方自研插件与内置工具</p>
</div>
<div class="head-actions">
<div class="codex-link-status" title="Codex 桌面连接状态;语音输入与 Token 用量共用此连接">
<span id="codex-link-dot" class="voice-dot" data-state="unknown"></span>
<span class="codex-link-label">Codex</span>
<span id="codex-link-phase-text" class="muted small">未连接</span>
<button class="btn ghost small" id="codex-link-connect-btn" data-voice-link-connect>连接</button>
<button class="btn ghost small hidden" id="codex-link-disconnect-btn" data-voice-link-disconnect>断开</button>
</div>
<input id="plugin-search" class="search" type="search" placeholder="搜索插件 / 作者…" />
<button id="plugin-refresh" class="btn ghost">刷新</button>
</div>
@@ -105,7 +112,22 @@
<div class="tool-card">
<div class="tool-top"><span class="tool-ico">🎤</span><div class="tool-name">语音输入</div></div>
<div class="tool-desc">给 Codex 输入框注入麦克风按钮,说话即转文字填入。需由工具箱重启 Codex 一次以启用调试通道(登录态保留)。</div>
<div class="tool-foot"><button class="btn primary small" data-open-panel="voice">打开</button></div>
<div class="tool-foot"><button class="btn primary small" id="voice-enable-card-btn" data-voice-enable data-enable-label="启用" data-enabled-label="已启用">启用</button></div>
</div>
<div class="tool-card">
<div class="tool-top"><span class="tool-ico">🧑‍💼</span><div class="tool-name">员工团队</div></div>
<div class="tool-desc">用一段话描述项目需求,自动拆解出一支带约束、能协作的 AI 员工团队(含总控 + 契约 + 文件边界 + 协同规范),打包成 Codex / Claude Code 可直接用的配置 zip。</div>
<div class="tool-foot"><button class="btn primary small" data-open-panel="team">打开</button></div>
</div>
<div class="tool-card">
<div class="tool-top"><span class="tool-ico">🎨</span><div class="tool-name">提示词提取</div></div>
<div class="tool-desc">上传图片或视频,AI 自动反推出能复现它的生图提示词。中英双语、七维拆解、负面提示词、关键词标签,直接用你的中转分组运行。</div>
<div class="tool-foot"><button class="btn primary small" data-open-panel="prompt">打开</button></div>
</div>
<div class="tool-card">
<div class="tool-top"><span class="tool-ico">⚡</span><div class="tool-name">Token 用量</div></div>
<div class="tool-desc">在 Codex 输入框下方、模型选择左侧显示每日 Token 累计消耗量。复用语音 CDP 连接自动注入,无需额外配置。每天 24 点自动清零。</div>
<div class="tool-foot"><button class="btn primary small" data-open-panel="token">打开</button></div>
</div>
</div>
@@ -130,14 +152,13 @@
<div class="head-actions">
<span id="voice-dot" class="voice-dot" data-state="unknown"></span>
<span id="voice-phase-text" class="muted small">未连接</span>
<button id="voice-connect-btn" class="btn primary">连接</button>
<button id="voice-disconnect-btn" class="btn ghost hidden">断开</button>
<button id="voice-enable-btn" class="btn primary" data-voice-enable data-enable-label="启用语音输入" data-enabled-label="已启用">启用语音输入</button>
</div>
</div>
<div class="voice-body">
<div class="voice-card">
<div class="voice-card-title">状态</div>
<div id="voice-detail" class="muted small">点击「连接」开始。若 Codex 正在运行,会提示你确认重启 Codex 以启用调试端口。</div>
<div id="voice-detail" class="muted small">点击「启用语音输入」开始。若 Codex 正在运行,会提示你确认重启 Codex 以启用调试端口。</div>
</div>
<div class="voice-card">
<div class="voice-card-title">转写分组 / 模型</div>
@@ -155,7 +176,7 @@
<div class="voice-card">
<div class="voice-card-title">使用说明</div>
<ol class="voice-ol">
<li>点击「连接」。若 Codex 在运行且未带调试端口,会弹窗提示重启(当前窗口会关闭一次,登录态保留)。</li>
<li>点击「启用语音输入」。若 Codex 在运行且未带调试端口,会弹窗提示重启(当前窗口会关闭一次,登录态保留)。</li>
<li>连接成功后,Codex 输入框旁出现 🎤 按钮。点击 → 授权麦克风权限 → 说话 → 再次点击停止。</li>
<li>转写完成后文字自动填入输入框。首次需在「系统设置 → 隐私 → 麦克风」允许 ChatGPT。</li>
<li>macOS 误杀进程风险已规避:仅在确认后重启。Codex 升级后若按钮消失,可能需等待工具箱适配。</li>
@@ -201,6 +222,192 @@
</div>
</section>
<!-- 员工团队:需求 → AI 员工团队 → 打包 zip -->
<section class="panel" id="tab-team">
<div class="tm-wrap">
<!-- 左侧:需求输入 + 配置 -->
<div class="tm-input-col">
<h2 class="tm-title"><button class="btn ghost small back-btn" data-back-plugins>← 插件市场</button> 生成 AI 员工团队</h2>
<p class="tm-sub">用一段话描述项目需求,自动拆解出一支带约束、能协作的 AI 员工团队(含总控),打包成 Codex / Claude Code 可直接用的配置。</p>
<textarea id="tm-req" class="tm-req" placeholder="例如:做一个电商后台管理系统,需要商品管理、订单处理、库存、用户权限和数据看板,前端要好看,后端要稳。"></textarea>
<div class="tm-field">
<label class="tm-label">目标工具</label>
<div class="tm-chips" id="tm-tool">
<button class="tm-chip active" data-tool="codex">Codex</button>
<button class="tm-chip" data-tool="claude">Claude Code</button>
</div>
<div class="tm-hint" id="tm-tool-hint">生成 .toml 员工 + 总控(调度 / 审核)+ AGENTS.md</div>
</div>
<div class="tm-field">
<label class="tm-label">分析模型(中转分组)</label>
<div class="tm-row">
<select class="tm-select" id="tm-group"></select>
<button class="btn small ghost" id="tm-fetch">🔄 拉取模型</button>
</div>
<select class="tm-select" id="tm-model" style="margin-top:8px"></select>
<div class="tm-hint" id="tm-model-hint">先在『路由中转』配置好分组,再选具体模型</div>
</div>
<div class="tm-field">
<label class="tm-label">员工运行模型(可选)</label>
<input id="tm-agent-model" class="tm-text" placeholder="留空=用上面的分析模型,如 gpt-4o" />
<div class="tm-hint" id="tm-agent-hint">写进每个员工的 model 字段,改成你运行器支持的模型名</div>
</div>
<button id="tm-gen" class="btn primary tm-gen" disabled>生成员工团队</button>
</div>
<!-- 右侧:结果 -->
<div class="tm-result-col">
<div class="tm-result-head">
<span class="tm-result-title">员工团队</span>
<button class="btn small primary" id="tm-download" hidden>⬇ 打包下载 .zip</button>
</div>
<div class="tm-result-body">
<div class="tm-empty" id="tm-empty">
<div class="tm-empty-ico">🧑‍💼→👥</div>
<p class="tm-empty-title">描述需求,自动组建 AI 员工团队</p>
<p class="tm-empty-sub">拆解职责 · 定义契约 · 划分文件边界 · 生成协同规范</p>
</div>
<div class="tm-loading" id="tm-loading" hidden>
<div class="tm-spinner"></div>
<div class="tm-loading-text" id="tm-loading-text">正在分析需求、拆解团队…</div>
</div>
<div class="tm-content" id="tm-content" hidden>
<div class="tm-team-head" id="tm-team-head"></div>
<div id="tm-team-list"></div>
</div>
<div class="tm-error" id="tm-error" hidden></div>
</div>
</div>
</div>
</section>
<!-- ②.⑥ 提示词提取(图片 → 反推生图提示词) -->
<section class="panel" id="tab-prompt">
<div class="panel-head">
<div>
<h2><button class="btn ghost small back-btn" data-back-plugins>← 插件市场</button> 提示词提取</h2>
<p class="muted">上传图片或视频,AI 自动分析九个视觉维度,反推出能复现它的中英双语生图提示词。直接用你的中转分组运行。</p>
</div>
</div>
<div class="pm-wrap">
<!-- 左侧:上传 + 设置 -->
<div class="pm-input-col">
<div class="pm-dropzone" id="pm-dropzone">
<input type="file" id="pm-fileInput" accept="image/*,video/*" hidden />
<div class="pm-dz-empty" id="pm-dzEmpty">
<div class="pm-dz-icon">📤</div>
<div class="pm-dz-title">拖拽图片 / 视频到这里</div>
<div class="pm-dz-sub">或点击选择 · JPG / PNG / WebP / MP4</div>
</div>
<div class="pm-dz-preview" id="pm-dzPreview" hidden>
<img id="pm-previewImg" alt="预览" />
<button class="pm-dz-clear" id="pm-clearBtn" title="移除">✕</button>
</div>
</div>
<div class="pm-field">
<label class="pm-label">分析模型(中转分组)</label>
<div class="pm-row">
<select class="pm-select" id="pm-group"></select>
<button class="btn small ghost" id="pm-fetch">🔄 拉取模型</button>
</div>
<select class="pm-select" id="pm-model" style="margin-top:8px"></select>
<div class="pm-hint" id="pm-model-hint">先在『路由中转』配置好分组,再选具体模型(需要视觉/多模态模型)</div>
</div>
<button id="pm-analyze" class="btn primary pm-gen" disabled>开始分析</button>
</div>
<!-- 右侧:结果(分析完成后动态渲染,平时只展示空状态) -->
<div class="pm-result-col">
<div class="pm-result-body" id="pm-result-body">
<div class="pm-empty" id="pm-empty">
<div class="pm-empty-ico">🖼️→📝</div>
<p class="pm-empty-title">上传素材,反推它的生图提示词</p>
<p class="pm-empty-sub">精准分析九个视觉维度,输出可直接复现的双语提示词</p>
<div class="pm-empty-feats"><span>七维拆解</span><span>中英双语</span><span>负面提示词</span><span>关键词标签</span></div>
</div>
</div>
</div>
</div>
</section>
<!-- ②.⑦ Token 用量(Codex 输入框下方工具栏进度条) -->
<section class="panel" id="tab-token">
<div class="panel-head">
<div>
<h2><button class="btn ghost small back-btn" data-back-plugins>← 插件市场</button> Token 用量</h2>
<p class="muted">在 Codex 输入框下方、模型选择左侧显示 Token 累计消耗条。复用语音 CDP 连接自动注入,每天 24 点自动清零。</p>
</div>
<div class="head-actions">
<span id="tk-dot" class="voice-dot" data-state="unknown"></span>
<span id="tk-phase-text" class="muted small">未连接</span>
</div>
</div>
<div class="tk-body">
<!-- 今日用量 Hero -->
<div class="tk-card hero">
<div class="tk-hero-num" id="tk-total">—</div>
<div class="tk-hero-label">今日累计消耗</div>
<div class="tk-breakdown" id="tk-breakdown">
<span>输入 <b id="tk-input">0</b></span>
<span>输出 <b id="tk-output">0</b></span>
<span>缓存 <b id="tk-cached">0</b></span>
<span>估算 <b id="tk-estimate">0</b></span>
<span>未知 <b id="tk-unknown">0</b></span>
</div>
<div class="tk-hero-date" id="tk-date"></div>
</div>
<!-- Codex 效果预览 -->
<div class="tk-card">
<div class="tk-card-title">Codex 输入框下方效果</div>
<div class="tk-preview">
<div class="tk-preview-bar" id="tk-preview-bar">
<div class="tk-preview-meter">
<div class="tk-preview-track">
<div class="tk-preview-segments" id="tk-preview-segments">
<i class="tk-preview-seg"></i><i class="tk-preview-seg"></i><i class="tk-preview-seg"></i><i class="tk-preview-seg"></i>
<i class="tk-preview-seg"></i><i class="tk-preview-seg"></i><i class="tk-preview-seg"></i><i class="tk-preview-seg"></i>
</div>
<span class="tk-preview-badge"><span class="tk-preview-check">✓</span><span id="tk-preview-badge-text">0 token</span></span>
<span class="tk-preview-cap"></span>
</div>
</div>
</div>
<div class="tk-preview-input">输入消息…</div>
</div>
</div>
<!-- 操作 -->
<div class="tk-card">
<div class="tk-card-title">操作</div>
<div class="tk-row">
<button id="tk-refresh" class="btn ghost">🔄 刷新用量</button>
<button id="tk-reset" class="btn danger small">↺ 手动清零</button>
</div>
<div class="muted small" style="margin-top:8px">
用量数据保存在 Codex 页面 localStorage 中,跨天自动清零。<br />需要先在「插件市场」主页连接 Codex(Token 脚本随连接一起注入)。
</div>
<div class="tk-diag muted small" id="tk-diag"></div>
</div>
<div class="tk-card">
<div class="tk-card-title">工作原理</div>
<ol class="voice-ol">
<li>语音连接 Codex 时,Token 监控脚本自动注入同一个 CDP 连接。</li>
<li>主进程优先读取 Codex 本地 session 的 <code>token_count</code> 累计快照,网络 <code>usage</code> 仅作为兼容兜底。</li>
<li>用量条挂在输入框下方工具栏,优先放在模型选择左侧。</li>
</ol>
</div>
</div>
</section>
<!-- ③ 会话记录 -->
<section class="panel" id="tab-sessions">
<div class="panel-head">
@@ -321,7 +528,7 @@
</div>
<div class="panel-head" style="margin-top:18px">
<div><h3>中转分组</h3><p class="muted">每个分组:名称 + API 地址 + 密钥 +(可选)模型。选中谁就转发到谁。</p></div>
<div><h3>中转分组</h3><p class="muted">每个分组:名称 + API 地址 + 密钥 +(可选)模型。按协议分两栏:Codex/GPT 与 Claude Code 各选一个,<b>两者可同时激活一起用</b>。</p></div>
<button id="grp-add" class="btn">+ 新增分组</button>
</div>
<div id="router-groups" class="checklist"></div>
@@ -337,6 +544,7 @@
<select id="grp-mode" class="grp-select" style="margin-top:0">
<option value="responses">Responses 直连(OpenAI / TokenDun 等 Codex 原生)</option>
<option value="chat">Chat 补全 · 自动转换(国产:DeepSeek / GLM / Kimi / 通义)</option>
<option value="claude">Claude Code(接入 Claude Code CLI,写入 ~/.claude/settings.json)</option>
</select>
</label>
<label class="field grow" style="margin-top:10px"><span>API 地址(base_url)</span><input id="grp-url" placeholder="https://api.xxx.com" /></label>
@@ -362,6 +570,11 @@
<select id="grp-model-list" class="grp-select hidden"><option value="">— 从下拉选择模型 —</option></select>
<span class="muted small" id="grp-model-hint"></span>
</div>
<div class="field grow" id="grp-1m-field" style="margin-top:10px; display:none">
<span>1M 长上下文(勾选支持百万上下文的模型;转发这些模型时自动带 1M beta 头)</span>
<div id="grp-1m-list" class="grp-1m-list"></div>
<span class="muted small">仅对本身支持 1M 的模型有效(如 GPT/GLM/Claude 的 1M 版)。勾选后经此分组转发该模型即解锁 1M 窗口。</span>
</div>
<div class="field grow" style="margin-top:10px">
<span>上下文窗口 tokens(国产模型建议填,Codex 据此自动压缩)</span>
<div class="row" style="gap:6px; flex-wrap:wrap; align-items:center">
@@ -372,6 +585,19 @@
</div>
<span class="muted small">DeepSeek≈64K/128K · GLM≈128K · Kimi≈128K/200K · 通义≈128K。留空则用 Codex 默认(可能过大导致超限)</span>
</div>
<div class="field grow claudedesk-block" id="grp-claudedesk" style="margin-top:14px; border-top:1px solid var(--border); padding-top:14px">
<span>接入 Claude 桌面 App(第三方推理)</span>
<div class="muted small" id="grp-cd-hint" style="margin:2px 0 8px">把该分组一键写入 Claude 桌面 App,重启 App 即用你的中转。仅支持 Anthropic 协议分组。</div>
<div class="row" style="gap:8px; flex-wrap:wrap; align-items:center">
<button type="button" class="btn primary small" id="grp-cd-apply">⚡ 一键接入</button>
<button type="button" class="btn ghost small" id="grp-cd-export">导出配置文件</button>
<button type="button" class="btn ghost small" id="grp-cd-copy">复制配置</button>
<button type="button" class="btn small" id="grp-cd-restore" style="border-color:var(--danger); color:var(--danger)">↩ 还原官方登录</button>
</div>
<div class="muted small" id="grp-cd-status" style="margin-top:8px"></div>
</div>
<div class="row between" style="margin-top:16px">
<span></span>
<button id="grp-save" class="btn primary">保存</button>