fix: canvas image/video download - fix data URL parsing in cvDownloadPreview (v1.5.21, hot 1.5.23)
This commit is contained in:
+1009
-131
File diff suppressed because it is too large
Load Diff
+232
-6
@@ -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>
|
||||
|
||||
@@ -137,6 +137,9 @@ h3 { margin: 0; font-size: 16px; }
|
||||
.pc-badge.plugin { background: rgba(124,133,255,.16); color: var(--accent); }
|
||||
.pc-badge.skill { background: rgba(74,222,128,.15); color: var(--ok); }
|
||||
.pc-badge.plat { background: rgba(245,158,11,.16); color: #f59e0b; }
|
||||
.pc-badge.warn { background: rgba(245,158,11,.16); color: #f59e0b; }
|
||||
.cl-warn { margin-top: 6px; font-size: 12px; color: #f59e0b;
|
||||
background: rgba(245,158,11,.1); border-radius: 8px; padding: 6px 9px; }
|
||||
.pc-badge.on { background: rgba(74,222,128,.18); color: var(--ok); }
|
||||
/* 全局代理:两种模式说明块 */
|
||||
.proxy-modes { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 14px; }
|
||||
@@ -167,6 +170,12 @@ h3 { margin: 0; font-size: 16px; }
|
||||
.cl-item .cl-src { color: var(--muted); font-size: 12px; }
|
||||
.cl-item .cl-size { margin-left: auto; color: var(--muted); font-size: 12px; }
|
||||
|
||||
/* 路由分组:按协议族分两栏,各一个单选,可同时激活 */
|
||||
#router-groups { display: flex; gap: 14px; flex-wrap: wrap; margin-bottom: 16px; }
|
||||
.router-col { flex: 1; min-width: 280px; display: flex; flex-direction: column; gap: 8px; }
|
||||
.router-col-head { display: flex; align-items: baseline; gap: 8px; padding: 0 2px 2px; }
|
||||
.router-col .empty.small { font-size: 12px; padding: 10px 14px; }
|
||||
|
||||
/* 子标签 */
|
||||
.subtabs { display: flex; gap: 6px; margin-bottom: 18px; }
|
||||
.subtab { border: 1px solid var(--border); background: var(--card); color: var(--muted);
|
||||
@@ -316,6 +325,16 @@ h3 { margin: 0; font-size: 16px; }
|
||||
border: 1px solid var(--border); color: var(--text); border-radius: 10px;
|
||||
padding: 9px 12px; outline: none; font-size: 13px; }
|
||||
.grp-select:focus { border-color: var(--accent); }
|
||||
.grp-1m-list { margin-top: 8px; display: flex; flex-direction: column; gap: 2px;
|
||||
max-height: 180px; overflow-y: auto; border: 1px solid var(--border);
|
||||
border-radius: 10px; padding: 6px; background: var(--bg-2); }
|
||||
.grp-1m-item { display: flex; align-items: center; gap: 8px; padding: 5px 7px;
|
||||
border-radius: 7px; cursor: pointer; font-size: 13px; }
|
||||
.grp-1m-item:hover { background: var(--card); }
|
||||
.grp-1m-item input { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; flex: none; }
|
||||
.grp-1m-item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.claudedesk-block > span:first-child { font-weight: 600; font-size: 13px; }
|
||||
.claudedesk-block .btn:disabled { opacity: .45; cursor: not-allowed; }
|
||||
|
||||
/* —— 会话记忆 —— */
|
||||
.mem-item { background: var(--card); border: 1px solid var(--border);
|
||||
@@ -666,3 +685,209 @@ body.cv-noscroll { overflow: hidden; }
|
||||
.voice-dot[data-state="restarting"], .voice-dot[data-state="connecting"], .voice-dot[data-state="injecting"], .voice-dot[data-state="checking"] { background: #60a5fa; }
|
||||
.voice-dot[data-state="error"] { background: #ef4444; box-shadow: 0 0 8px rgba(239,68,68,.7); }
|
||||
.voice-dot[data-state="running_no_port"] { background: #f59e0b; }
|
||||
.codex-link-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
min-height: 36px;
|
||||
padding: 5px 7px 5px 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: var(--card);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.codex-link-label { font-weight: 700; font-size: 12px; }
|
||||
.codex-link-status .btn.small { padding: 5px 10px; }
|
||||
|
||||
/* ===== 员工团队 ===== */
|
||||
#tab-team { padding: 0; }
|
||||
.tm-wrap { display: grid; grid-template-columns: 380px 1fr; gap: 16px; height: 100%; padding: 16px 20px; box-sizing: border-box; }
|
||||
.tm-input-col { display: flex; flex-direction: column; gap: 12px; overflow-y: auto; padding-right: 4px; }
|
||||
.tm-title { font-size: 18px; margin: 0; }
|
||||
.tm-sub { color: var(--muted); font-size: 12.5px; line-height: 1.5; margin: 0; }
|
||||
.tm-req { width: 100%; min-height: 130px; resize: vertical; box-sizing: border-box;
|
||||
background: var(--card); border: 1px solid var(--border); color: var(--text);
|
||||
border-radius: 10px; padding: 12px; font-size: 13.5px; line-height: 1.6; font-family: inherit; }
|
||||
.tm-req:focus { border-color: var(--accent); outline: none; }
|
||||
.tm-field { display: flex; flex-direction: column; gap: 6px; }
|
||||
.tm-label { font-size: 12.5px; font-weight: 600; color: var(--text); }
|
||||
.tm-hint { font-size: 11.5px; color: var(--muted); line-height: 1.4; }
|
||||
.tm-row { display: flex; gap: 8px; align-items: center; }
|
||||
.tm-select, .tm-text { background: var(--card); border: 1px solid var(--border); color: var(--text);
|
||||
border-radius: 9px; padding: 8px 10px; font-size: 13px; font-family: inherit; flex: 1; min-width: 0; }
|
||||
.tm-select:focus, .tm-text:focus { border-color: var(--accent); outline: none; }
|
||||
.tm-chips { display: flex; gap: 6px; }
|
||||
.tm-chip { border: 1px solid var(--border); background: var(--card); color: var(--muted);
|
||||
padding: 6px 14px; border-radius: 8px; font-size: 12.5px; cursor: pointer; }
|
||||
.tm-chip.active { color: #fff; background: var(--grad); border-color: transparent; }
|
||||
.tm-gen { margin-top: 4px; padding: 11px; font-size: 14px; }
|
||||
|
||||
.tm-result-col { display: flex; flex-direction: column; background: var(--bg-2);
|
||||
border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; min-height: 0; }
|
||||
.tm-result-head { display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 12px 16px; border-bottom: 1px solid var(--border); }
|
||||
.tm-result-title { font-weight: 600; font-size: 14px; }
|
||||
.tm-result-body { flex: 1; overflow-y: auto; padding: 16px; min-height: 0; }
|
||||
.tm-empty, .tm-loading, .tm-error { display: flex; flex-direction: column; align-items: center;
|
||||
justify-content: center; text-align: center; height: 100%; gap: 8px; color: var(--muted); }
|
||||
.tm-empty-ico { font-size: 34px; }
|
||||
.tm-empty-title { font-size: 15px; color: var(--text); margin: 0; }
|
||||
.tm-empty-sub { font-size: 12.5px; margin: 0; }
|
||||
.tm-error { color: var(--danger); font-size: 13.5px; padding: 20px; }
|
||||
.tm-spinner { width: 34px; height: 34px; border: 3px solid var(--border);
|
||||
border-top-color: var(--accent); border-radius: 50%; animation: tmspin .8s linear infinite; }
|
||||
@keyframes tmspin { to { transform: rotate(360deg); } }
|
||||
.tm-loading-text { font-size: 13px; }
|
||||
.tm-team-head { margin-bottom: 14px; }
|
||||
.tm-team-name { font-size: 18px; font-weight: 700; }
|
||||
.tm-team-summary { color: var(--muted); font-size: 13px; margin-top: 4px; line-height: 1.5; }
|
||||
.tm-team-stack { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
|
||||
.tm-stack-chip { background: var(--card-2); border: 1px solid var(--border); color: var(--text);
|
||||
padding: 3px 10px; border-radius: 20px; font-size: 11.5px; }
|
||||
.tm-conv-card, .tm-emp-card { background: var(--card); border: 1px solid var(--border);
|
||||
border-radius: 12px; padding: 14px 16px; margin-bottom: 12px; }
|
||||
.tm-emp-card.lead { border-color: var(--accent); box-shadow: 0 0 0 1px rgba(124,133,255,.25); }
|
||||
.tm-conv-title { font-weight: 700; font-size: 14px; margin-bottom: 6px; }
|
||||
.tm-conv-owner { font-size: 12px; color: var(--muted); margin-bottom: 12px; line-height: 1.5; }
|
||||
.tm-conv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
.tm-cc-label { font-size: 12px; font-weight: 600; color: var(--accent); margin-bottom: 4px; }
|
||||
.tm-conv-col ul { margin: 0; padding-left: 16px; font-size: 12px; color: var(--muted); line-height: 1.55; }
|
||||
.tm-emp-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.tm-emp-name { font-weight: 700; font-size: 14.5px; }
|
||||
.tm-emp-badge { background: var(--grad); color: #fff; font-size: 10.5px; padding: 2px 7px; border-radius: 6px; }
|
||||
.tm-emp-id { font-family: ui-monospace, monospace; font-size: 11.5px; color: var(--muted);
|
||||
background: var(--card-2); padding: 1px 6px; border-radius: 5px; }
|
||||
.tm-emp-duty { font-size: 13px; color: var(--text); margin-top: 5px; line-height: 1.5; }
|
||||
.tm-sec-label { font-size: 11.5px; font-weight: 600; color: var(--accent); margin: 10px 0 4px; }
|
||||
.tm-resp, .tm-flow { margin: 0; padding-left: 16px; font-size: 12.5px; color: var(--muted); line-height: 1.55; }
|
||||
.tm-collab { display: flex; flex-direction: column; gap: 3px; margin-top: 8px; }
|
||||
.tm-ec-row { display: flex; gap: 8px; font-size: 12px; color: var(--muted); }
|
||||
.tm-ec-k { color: var(--accent); font-weight: 600; min-width: 32px; }
|
||||
.tm-ec-row code, .tm-collab code { font-family: ui-monospace, monospace; font-size: 11px;
|
||||
background: var(--card-2); padding: 1px 5px; border-radius: 4px; color: var(--text); }
|
||||
.tm-cons { display: flex; flex-wrap: wrap; gap: 5px; }
|
||||
.tm-con-chip { font-size: 11px; background: var(--card-2); border: 1px solid var(--border);
|
||||
color: var(--muted); padding: 2px 8px; border-radius: 6px; }
|
||||
.tm-file-block { margin-top: 10px; }
|
||||
.tm-file-toggle { border: 1px dashed var(--border); background: transparent; color: var(--accent);
|
||||
font-size: 12px; padding: 5px 10px; border-radius: 7px; cursor: pointer; width: 100%; }
|
||||
.tm-file-toggle:hover { border-color: var(--accent); }
|
||||
.tm-file-view { margin-top: 8px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
|
||||
.tm-file-head { display: flex; justify-content: space-between; align-items: center;
|
||||
padding: 6px 10px; background: var(--card-2); border-bottom: 1px solid var(--border); }
|
||||
.tm-file-name { font-family: ui-monospace, monospace; font-size: 11.5px; color: var(--muted); }
|
||||
.tm-file-copy { border: 1px solid var(--border); background: var(--card); color: var(--text);
|
||||
font-size: 11px; padding: 3px 10px; border-radius: 6px; cursor: pointer; }
|
||||
.tm-file-pre { margin: 0; padding: 12px; font-family: ui-monospace, monospace; font-size: 11.5px;
|
||||
line-height: 1.55; color: var(--text); white-space: pre-wrap; word-break: break-word;
|
||||
max-height: 340px; overflow-y: auto; background: var(--bg); }
|
||||
@media (max-width: 820px) { .tm-wrap { grid-template-columns: 1fr; } .tm-conv-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
/* —— ②.⑥ 提示词提取 —— */
|
||||
.pm-wrap { display: grid; grid-template-columns: 380px 1fr; gap: 18px; padding: 14px 18px; height: calc(100vh - 220px); overflow: hidden; }
|
||||
.pm-input-col { display: flex; flex-direction: column; gap: 12px; overflow-y: auto; padding-right: 4px; }
|
||||
.pm-result-col { display: flex; flex-direction: column; overflow: hidden; }
|
||||
.pm-result-head { display: flex; align-items: center; gap: 10px; padding: 0 0 8px; border-bottom: 1px solid var(--border); }
|
||||
.pm-result-title { font-weight: 600; font-size: 14px; }
|
||||
.pm-meta-model { font-size: 11px; color: var(--muted); font-family: ui-monospace, monospace; }
|
||||
.pm-meta-conf { font-size: 11px; padding: 2px 8px; border-radius: 10px; font-weight: 600; }
|
||||
.pm-conf-high { background: #1a3a1a; color: #4ade80; }
|
||||
.pm-conf-medium { background: #3a2e0a; color: #fbbf24; }
|
||||
.pm-conf-low { background: #3a1a1a; color: #ff6b6b; }
|
||||
|
||||
.pm-result-body { flex: 1; overflow-y: auto; padding-right: 4px; }
|
||||
|
||||
.pm-dropzone { border: 2px dashed var(--border); border-radius: 12px; padding: 28px 16px;
|
||||
text-align: center; cursor: pointer; transition: border-color .2s; min-height: 150px; display: flex; align-items: center; justify-content: center; }
|
||||
.pm-dropzone:hover, .pm-dropzone.drag { border-color: var(--accent); }
|
||||
.pm-dz-icon { font-size: 36px; margin-bottom: 8px; }
|
||||
.pm-dz-title { font-size: 14px; font-weight: 600; margin-bottom: 4px; }
|
||||
.pm-dz-sub { font-size: 12px; color: var(--muted); }
|
||||
.pm-dz-preview { position: relative; }
|
||||
.pm-dz-preview img { max-width: 100%; max-height: 200px; border-radius: 10px; object-fit: contain; }
|
||||
.pm-dz-clear { position: absolute; top: 6px; right: 6px; background: rgba(0,0,0,0.65); color: #fff;
|
||||
border: none; border-radius: 50%; width: 24px; height: 24px; font-size: 14px; cursor: pointer; line-height: 1; }
|
||||
|
||||
.pm-field { display: flex; flex-direction: column; gap: 4px; }
|
||||
.pm-label { font-size: 12.5px; font-weight: 600; color: var(--muted); }
|
||||
.pm-row { display: flex; gap: 8px; align-items: center; }
|
||||
.pm-row .pm-select { flex: 1; min-width: 0; }
|
||||
.pm-row .btn { flex-shrink: 0; white-space: nowrap; }
|
||||
.pm-select { width: 100%; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--border);
|
||||
background: var(--card); color: var(--text); font-size: 13px; }
|
||||
.pm-hint { font-size: 11px; color: var(--muted); line-height: 1.4; }
|
||||
.pm-gen { width: 100%; padding: 12px; font-size: 15px; font-weight: 600; border-radius: 10px; }
|
||||
|
||||
.pm-empty, .pm-loading, .pm-error { display: flex; flex-direction: column; align-items: center;
|
||||
justify-content: center; text-align: center; padding: 40px 20px; }
|
||||
.pm-empty-ico { font-size: 48px; margin-bottom: 12px; }
|
||||
.pm-empty-title { font-size: 15px; font-weight: 600; margin: 0 0 6px; }
|
||||
.pm-empty-sub { font-size: 13px; color: var(--muted); margin: 0 0 14px; }
|
||||
.pm-empty-feats { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
|
||||
.pm-empty-feats span { font-size: 11px; background: var(--card-2); border: 1px solid var(--border);
|
||||
padding: 3px 10px; border-radius: 14px; color: var(--muted); }
|
||||
.pm-spinner { width: 32px; height: 32px; border: 3px solid var(--border); border-top-color: var(--accent);
|
||||
border-radius: 50%; animation: spin .8s linear infinite; margin-bottom: 12px; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.pm-loading-text { font-size: 13px; color: var(--muted); }
|
||||
.pm-error { color: var(--danger); font-size: 13px; }
|
||||
|
||||
.pm-content { display: flex; flex-direction: column; gap: 12px; }
|
||||
.pm-card { border: 1px solid var(--border); border-radius: 10px; padding: 14px; background: var(--card); }
|
||||
.pm-card.primary { border-color: var(--accent); border-width: 1.5px; }
|
||||
.pm-card.subtle { background: var(--card-2); border-style: dashed; }
|
||||
.pm-card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
|
||||
.pm-card-title { font-size: 12px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
|
||||
.pm-card-title em { font-style: normal; color: var(--accent); font-size: 10px; margin-left: 4px; }
|
||||
.pm-prompt-text { font-size: 13.5px; line-height: 1.65; margin: 0; color: var(--text); }
|
||||
.pm-prompt-text.mono { font-family: ui-monospace, monospace; font-size: 12px; }
|
||||
.pm-prompt-text.zh-sub { color: var(--muted); font-size: 12px; margin-top: 4px; }
|
||||
.pm-copy-btn { border: 1px solid var(--border); background: var(--card-2); color: var(--text);
|
||||
font-size: 11px; padding: 3px 10px; border-radius: 6px; cursor: pointer; }
|
||||
.pm-copy-btn:hover { border-color: var(--accent); color: var(--accent); }
|
||||
|
||||
.pm-tags-wrap { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.pm-tag { font-size: 11px; background: var(--card-2); border: 1px solid var(--border);
|
||||
padding: 3px 10px; border-radius: 14px; color: var(--text); }
|
||||
|
||||
.pm-breakdown { display: flex; flex-direction: column; gap: 6px; }
|
||||
.pm-bd-row { display: flex; gap: 12px; padding: 8px 10px; border-radius: 8px; background: var(--card-2); border: 1px solid var(--border); }
|
||||
.pm-bd-label { display: flex; flex-direction: column; min-width: 90px; flex-shrink: 0; }
|
||||
.pm-bd-label-zh { font-size: 12px; font-weight: 600; color: var(--text); }
|
||||
.pm-bd-label-en { font-size: 10px; color: var(--muted); }
|
||||
.pm-bd-values { display: flex; flex-direction: column; }
|
||||
.pm-bd-zh { font-size: 12px; color: var(--text); line-height: 1.5; }
|
||||
.pm-bd-en { font-size: 11px; color: var(--muted); line-height: 1.5; margin-top: 1px; }
|
||||
|
||||
.pm-notes { font-size: 12px; color: var(--muted); line-height: 1.5; margin: 0; padding: 10px 12px;
|
||||
border-left: 3px solid var(--accent); background: var(--card-2); border-radius: 0 8px 8px 0; }
|
||||
|
||||
@media (max-width: 820px) { .pm-wrap { grid-template-columns: 1fr; } }
|
||||
|
||||
/* —— ②.⑦ Token 用量面板 —— */
|
||||
.tk-body { padding: 14px 18px; max-width: 600px; display: flex; flex-direction: column; gap: 14px; }
|
||||
.tk-card { border: 1px solid var(--border); border-radius: 10px; padding: 16px; background: var(--card); }
|
||||
.tk-card.hero { text-align: center; padding: 28px 16px; border-color: var(--accent); border-width: 1.5px; background: linear-gradient(135deg, var(--card) 0%, var(--card-2) 100%); }
|
||||
.tk-hero-num { font-size: 42px; font-weight: 700; font-variant-numeric: tabular-nums; background: linear-gradient(135deg, #6366f1, #a855f7, #ec4899); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
||||
.tk-hero-label { font-size: 13px; color: var(--muted); margin-top: 4px; }
|
||||
.tk-breakdown { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 12px; font-size: 11px; color: var(--muted); }
|
||||
.tk-breakdown span { display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; border: 1px solid var(--border); border-radius: 999px; background: var(--card); }
|
||||
.tk-breakdown b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
|
||||
.tk-hero-date { font-size: 11px; color: var(--muted); margin-top: 2px; opacity: .7; }
|
||||
|
||||
.tk-card-title { font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 10px; text-transform: uppercase; letter-spacing: .5px; }
|
||||
|
||||
.tk-preview { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--bg); }
|
||||
.tk-preview-bar { display: flex; justify-content: center; align-items: center; padding: 12px 14px; min-height: 58px; background: #211a16; border-bottom: 1px solid var(--border); }
|
||||
.tk-preview-meter { position: relative; width: min(424px, 100%); height: 32px; }
|
||||
.tk-preview-track { position: absolute; left: 0; right: 0; top: 50%; height: 28px; transform: translateY(-50%); border-radius: 8px; background: #fff; overflow: hidden; box-shadow: 0 1px 0 rgba(255,255,255,.92) inset, 0 0 0 1px rgba(15,23,42,.10), 0 5px 16px rgba(0,0,0,.28); }
|
||||
.tk-preview-segments { position: absolute; left: 18px; right: 72px; top: 50%; display: grid; grid-template-columns: repeat(8,1fr); gap: 3px; height: 12px; transform: translateY(-50%); }
|
||||
.tk-preview-seg { position: relative; overflow: hidden; border-radius: 3px; transform: skewX(-7deg); background: linear-gradient(180deg,rgba(138,217,255,.22),rgba(23,103,204,.22)); box-shadow: 0 0 0 1px rgba(54,157,239,.18) inset; }
|
||||
.tk-preview-seg::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--tk-preview-seg-fill,0%); border-radius: inherit; background: linear-gradient(180deg,#52c8ff 0%,#2673dd 58%,#1554c5 100%); box-shadow: 0 0 9px rgba(42,163,255,.72), 0 1px 0 rgba(255,255,255,.45) inset; transition: width .6s ease; }
|
||||
.tk-preview-badge { position: absolute; right: 18px; top: 50%; z-index: 2; display: inline-flex; align-items: center; gap: 3px; height: 18px; padding: 0 7px 0 5px; border-radius: 7px; transform: translateY(-50%); color: #fff; font-size: 11px; font-weight: 800; letter-spacing: 0; white-space: nowrap; background: linear-gradient(180deg,#63c8ff 0%,#2c77df 64%,#1f60c9 100%); box-shadow: 0 0 12px rgba(53,159,246,.75), 0 1px 0 rgba(255,255,255,.45) inset; }
|
||||
.tk-preview-check { display: inline-flex; align-items: center; justify-content: center; width: 12px; height: 12px; border-radius: 50%; background: #fff; color: #266bd5; font-size: 10px; line-height: 1; }
|
||||
.tk-preview-cap { position: absolute; right: 7px; bottom: 4px; width: 10px; height: 12px; border-radius: 3px 3px 4px 4px; background: linear-gradient(180deg,#dcffff 0%,#61ddfb 100%); box-shadow: 0 0 8px rgba(82,222,255,.75); opacity: .95; }
|
||||
.tk-preview-cap::before { content: ""; position: absolute; left: 2px; right: 2px; top: -3px; height: 3px; border-radius: 2px 2px 0 0; background: #cfffff; }
|
||||
.tk-preview-input { padding: 10px 12px; font-size: 12px; color: var(--muted); }
|
||||
.tk-row { display: flex; gap: 10px; align-items: center; }
|
||||
.tk-diag { margin-top: 8px; word-break: break-all; }
|
||||
|
||||
Reference in New Issue
Block a user