ui(css): dev.html 圆角降一档 + 抽 token + modal 基类化 (style -11%)

抽 5 组 CSS token(语义色组 / 圆角分档 / mono / transition / shadow),顶栏按钮 hover + dd-item + badge 全切到 token(同色 selector 合并:export ≈ sp-copy 蓝、abandon ≈ sp-move 橙);4 个 modal 抽 .modal 基类(fixed/inset/bg/.show 五属性合并);.msg .body 与 file-preview .md-render 合并 markdown 渲染规则。圆角主流档 6px → 4px,modal card 8~12px → 6~8px,art-chip 999px 保留(胶囊语言)。功能 0 改动,JS 一行没动。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-05-21 21:50:17 +08:00
parent fa6cb72103
commit 6f9391dee3
2 changed files with 137 additions and 203 deletions

View File

@ -2,7 +2,7 @@
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9` > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`
最后更新:2026-05-21(system prompt 注入 task 预选 skill 提示) 最后更新:2026-05-21(dev.html CSS 精简 + 圆角降档 + modal 基类化)
--- ---
@ -23,6 +23,7 @@
### 2026-05-21 ### 2026-05-21
- **dev.html CSS 精简 + 圆角降档 + modal 基类化(style 块 589 → 522 行,-11%)**:为了"没那么圆润"统一调整。引入 CSS tokens:① 语义色组 `--c-green/blue/purple/orange/red` + 同色 `-bg/-bd` 三件套,顶栏 5 个按钮 hover + dd-item + badge.completed + sp-copy/sp-move 全切到 token(同色 selector 合并:export ≈ sp-copy 蓝、abandon ≈ sp-move 橙,各省 1 条规则);② 圆角分档 `--r-sm/md/lg/xl` = 3/4/6/8px,主流 button/input/msg/menu 从 6px 降到 4px,modal card 从 8~12px 降到 6~8px,art-chip 999px 保留(胶囊语言);③ `--mono`/`--t`/`--shadow-card` 收敛重复 font-family/transition/box-shadow。④ 4 个 modal(`#admin-modal/#src-picker-modal/#new-task-modal/#file-preview-modal`)抽 `.modal` 基类(fixed/inset/bg/display/.show 五属性合并),id 选择器只留 z-index + 宽高差异;HTML 同步加 `class="modal"`(JS `classList.add("show")` 不动)。⑤ `.msg .body` 与 file-preview `.md-render` 合并 markdown 渲染规则(`.msg .body x, .md-render x { ... }`,从两套 17 条缩到一套 17 条多 selector)。⑥ `button:disabled` 全局兜底,删散落 2 处单独写;`#login input:focus` 与 `#admin-modal input:focus` 合并(规则一字不差)。`.dev-item.act-export/rename` 同色合并到一行。功能 0 改动,JS 完全不动;`.dd-item` 颜色微变(原 #2e7d32#27ae60 等,因为统一到 5 组色 token)是可接受的副作用。
- **工作目录回到原生 `<select>` + sentinel + 二级 input(modal + 顶部 filter)**:combobox 方案推翻 —— 即使 show 时不过滤,modal 里 wd 因联动有值之后用户的直觉仍然是"我得点开下拉看选项",自己实现的 panel 总不如浏览器原生 select 稳。改回 select 范式:① modal `nt-wd-sel` 第一项 sentinel `+ 新建「<name>」`(label 由 `updateSentinelLabel` 跟 name 实时刷)+ 其后已有目录列表;sentinel 选中时显示二级 `nt-wd-new` 输入框默认值跟随 name,选已有目录时隐藏。`wdManuallyEdited` 锚到二级 input 上(用户改它就脱钩,清空恢复跟随)。② 顶部 `filter-wd` 也改成 `<select>`,首项 `(全部目录)`,onchange → `loadTaskList`;原 input 的 debounce listener 删,搜索 `filter-q` 的 debounce 保留独立写。③ `loadFolderSuggestions` 拉数据 + 新增 `populateFolderSelects` 灌两个 select(保留当前选中值);`enterApp` 启动时 fire-and-forget 预拉一次让左 pane 一打开就有选项。④ hint 在"输入新名恰好命中已有"时提示"将复用而非新建"。combobox 工厂 + .combo CSS + datalist 残留全删。 - **工作目录回到原生 `<select>` + sentinel + 二级 input(modal + 顶部 filter)**:combobox 方案推翻 —— 即使 show 时不过滤,modal 里 wd 因联动有值之后用户的直觉仍然是"我得点开下拉看选项",自己实现的 panel 总不如浏览器原生 select 稳。改回 select 范式:① modal `nt-wd-sel` 第一项 sentinel `+ 新建「<name>」`(label 由 `updateSentinelLabel` 跟 name 实时刷)+ 其后已有目录列表;sentinel 选中时显示二级 `nt-wd-new` 输入框默认值跟随 name,选已有目录时隐藏。`wdManuallyEdited` 锚到二级 input 上(用户改它就脱钩,清空恢复跟随)。② 顶部 `filter-wd` 也改成 `<select>`,首项 `(全部目录)`,onchange → `loadTaskList`;原 input 的 debounce listener 删,搜索 `filter-q` 的 debounce 保留独立写。③ `loadFolderSuggestions` 拉数据 + 新增 `populateFolderSelects` 灌两个 select(保留当前选中值);`enterApp` 启动时 fire-and-forget 预拉一次让左 pane 一打开就有选项。④ hint 在"输入新名恰好命中已有"时提示"将复用而非新建"。combobox 工厂 + .combo CSS + datalist 残留全删。
- **新建任务弹窗工作目录改 combobox + name 联动**:`web/static/dev.html` modal 里 `nt-wd-sel``<select>` 改成 `<input list="folders-datalist">`,删 `+ 新建目录…` sentinel + 二级 `nt-wd-new` 输入框;加 `wdManuallyEdited` flag —— name 输入时若 flag=false 自动同步到 wd(programmatic 改 value 不触发 wd input 事件不会假阳性),wd 非空输入置 flag=true 脱钩,wd 清空重置 flag=false 但保持空(避免 backspace 想换名字时被立刻填回打断);submit 保留 `working_dir || name` fallback 兜底空值。`loadFolderSuggestions` 不再渲染 select options,只灌共享 datalist + 缓存到 `state.folders` 供 hint 比对"命中已有/新建"。label 文案 `(可选,留空 → 用任务名...)``(默认跟随任务名;可输入新名或选已有目录复用)`,更直观。 - **新建任务弹窗工作目录改 combobox + name 联动**:`web/static/dev.html` modal 里 `nt-wd-sel``<select>` 改成 `<input list="folders-datalist">`,删 `+ 新建目录…` sentinel + 二级 `nt-wd-new` 输入框;加 `wdManuallyEdited` flag —— name 输入时若 flag=false 自动同步到 wd(programmatic 改 value 不触发 wd input 事件不会假阳性),wd 非空输入置 flag=true 脱钩,wd 清空重置 flag=false 但保持空(避免 backspace 想换名字时被立刻填回打断);submit 保留 `working_dir || name` fallback 兜底空值。`loadFolderSuggestions` 不再渲染 select options,只灌共享 datalist + 缓存到 `state.folders` 供 hint 比对"命中已有/新建"。label 文案 `(可选,留空 → 用任务名...)``(默认跟随任务名;可输入新名或选已有目录复用)`,更直观。
- **system prompt 注入 task 预选 skill 提示**:`core/agent_builder.py::_build_system_prompt` 加 `task_skill` 参数,非空时在"工作目录与 task 上下文"段加一行 `- **task 预选 skill**: \`<name>\` — 用户创建时声明的主 skill`;空字符串走老路径,prompt 字节级一致。LLM 拿到这条事实 + `general_v1.md:17-23` 已有的"对应 skill 领域先 load_skill" 规则自然组合 → 主动 load。否决"直接把完整 SKILL.md 预注入 prompt"方案 —— 那会把 `tasks.skill` 从 metadata 升格成 binding,需要同步改 DESIGN.md / 想清楚 PATCH 改 skill 的语义,投入产出比不划算;轻量提示保渐进披露三层架构不动。 - **system prompt 注入 task 预选 skill 提示**:`core/agent_builder.py::_build_system_prompt` 加 `task_skill` 参数,非空时在"工作目录与 task 上下文"段加一行 `- **task 预选 skill**: \`<name>\` — 用户创建时声明的主 skill`;空字符串走老路径,prompt 字节级一致。LLM 拿到这条事实 + `general_v1.md:17-23` 已有的"对应 skill 领域先 load_skill" 规则自然组合 → 主动 load。否决"直接把完整 SKILL.md 预注入 prompt"方案 —— 那会把 `tasks.skill` 从 metadata 升格成 binding,需要同步改 DESIGN.md / 想清楚 PATCH 改 skill 的语义,投入产出比不划算;轻量提示保渐进披露三层架构不动。

View File

@ -25,6 +25,21 @@
--code-bg: #f4f4f4; --code-bg: #f4f4f4;
--user-bg: #eef4fb; --user-bg: #eef4fb;
--asst-bg: #ffffff; --asst-bg: #ffffff;
/* 语义色组:done/export/clear/abandon/delete 按钮 + dd-item + badge 共用 */
--c-green: #27ae60; --c-green-bg: #e9f7ef; --c-green-bd: #a9dfbf;
--c-blue: #2980b9; --c-blue-bg: #ebf5fb; --c-blue-bd: #aed6f1;
--c-purple: #8e44ad; --c-purple-bg: #f5eef8; --c-purple-bd: #d2b4de;
--c-orange: #e67e22; --c-orange-bg: #fef5e7; --c-orange-bd: #f5cba7;
--c-red: #c0392b; --c-red-bg: #fdedec; --c-red-bd: #f5b7b1;
/* 圆角:各档下调一档(没那么圆润) */
--r-sm: 3px; /* code / 小标签 */
--r-md: 4px; /* button / input / 消息气泡 / 卡片元素 */
--r-lg: 6px; /* modal card / 中型容器 */
--r-xl: 8px; /* 大 modal card(登录卡) */
--shadow-card: 0 12px 32px rgba(0,0,0,.18);
--shadow-card-lg: 0 20px 60px rgba(0,0,0,.12), 0 2px 6px rgba(0,0,0,.04);
--mono: ui-monospace, "Cascadia Code", "SF Mono", Consolas, monospace;
--t: all .15s;
} }
* { box-sizing: border-box; } * { box-sizing: border-box; }
html, body { height: 100%; margin: 0; } html, body { height: 100%; margin: 0; }
@ -33,27 +48,36 @@
color: var(--text); background: var(--bg); color: var(--text); background: var(--bg);
overflow: hidden; /* 视窗锁死,所有滚动在 pane 内 */ overflow: hidden; /* 视窗锁死,所有滚动在 pane 内 */
} }
button, input, textarea, select { button, input, textarea, select { font: inherit; color: inherit; }
font: inherit; color: inherit;
}
button { button {
background: #fff; border: 1px solid var(--border); background: #fff; border: 1px solid var(--border);
padding: 4px 10px; border-radius: 6px; cursor: pointer; padding: 4px 10px; border-radius: var(--r-md); cursor: pointer;
transition: color .15s, border-color .15s, background .15s, box-shadow .15s; transition: var(--t);
} }
button:hover { background: var(--hover); } button:hover:not(:disabled) { background: var(--hover); }
button:disabled { opacity: 0.4; cursor: not-allowed; }
button.primary { background: var(--accent); color: #fff; border-color: var(--accent); } button.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
button.primary:hover { filter: brightness(1.08); } button.primary:hover { filter: brightness(1.08); }
button.danger:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); } button.danger:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
input:not([type="checkbox"]):not([type="radio"]):not([type="file"]), input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
textarea, select { textarea, select {
background: #fff; border: 1px solid var(--border); background: #fff; border: 1px solid var(--border);
padding: 5px 8px; border-radius: 6px; width: 100%; padding: 5px 8px; border-radius: var(--r-md); width: 100%;
} }
input[type="checkbox"], input[type="radio"] { cursor: pointer; } input[type="checkbox"], input[type="radio"] { cursor: pointer; }
textarea { resize: vertical; min-height: 60px; } textarea { resize: vertical; min-height: 60px; }
a { color: var(--accent); text-decoration: none; } a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; } a:hover { text-decoration: underline; }
/* 4 个 modal 共用骨架(admin / src-picker / new-task / file-preview) */
.modal {
position: fixed; inset: 0; background: rgba(0,0,0,0.4);
display: none; align-items: center; justify-content: center;
}
.modal.show { display: flex; }
.modal > .card {
background: var(--panel); border-radius: var(--r-lg);
box-shadow: var(--shadow-card);
}
/* ───── login overlay ───── */ /* ───── login overlay ───── */
#login { #login {
@ -67,9 +91,9 @@
#login .card { #login .card {
background: var(--panel); background: var(--panel);
padding: 32px 36px 28px; padding: 32px 36px 28px;
border-radius: 12px; border-radius: var(--r-xl);
width: 380px; width: 380px;
box-shadow: 0 20px 60px rgba(0,0,0,.12), 0 2px 6px rgba(0,0,0,.04); box-shadow: var(--shadow-card-lg);
border: 1px solid rgba(0,0,0,.04); border: 1px solid rgba(0,0,0,.04);
animation: login-in .35s cubic-bezier(.2,.7,.2,1); animation: login-in .35s cubic-bezier(.2,.7,.2,1);
} }
@ -77,11 +101,9 @@
from { opacity: 0; transform: translateY(8px); } from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); } to { opacity: 1; transform: translateY(0); }
} }
#login .brand { #login .brand { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
display: flex; align-items: center; gap: 10px; margin-bottom: 4px;
}
#login .brand .logo { #login .brand .logo {
width: 32px; height: 32px; border-radius: 8px; width: 32px; height: 32px; border-radius: var(--r-md);
background: linear-gradient(135deg, var(--accent), #8e2a20); background: linear-gradient(135deg, var(--accent), #8e2a20);
color: #fff; font-weight: 700; font-size: 16px; color: #fff; font-weight: 700; font-size: 16px;
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
@ -94,23 +116,20 @@
font-size: 12px; color: var(--muted); letter-spacing: .2px; font-size: 12px; color: var(--muted); letter-spacing: .2px;
} }
#login input { #login input {
padding: 9px 12px; border-radius: 6px; padding: 9px 12px; border-radius: var(--r-md);
border: 1px solid var(--border); background: #fafafa; border: 1px solid var(--border); background: #fafafa;
transition: border-color .15s, background .15s, box-shadow .15s; transition: var(--t);
} }
#login input:hover { background: #fff; } #login input:hover { background: #fff; }
#login input:focus { #login input:focus, #admin-modal input:focus {
outline: none; background: #fff; border-color: var(--accent); outline: none; background: #fff; border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(192,57,43,.12); box-shadow: 0 0 0 3px rgba(192,57,43,.12);
} }
#login .err { #login .err { color: var(--accent); font-size: 12px; margin-top: 12px; min-height: 1em; }
color: var(--accent); font-size: 12px; margin-top: 12px;
min-height: 1em; transition: opacity .15s;
}
#login .actions { margin-top: 18px; display: flex; gap: 8px; } #login .actions { margin-top: 18px; display: flex; gap: 8px; }
#login .actions .primary { #login .actions .primary {
flex: 1; padding: 9px 14px; font-size: 14px; font-weight: 500; flex: 1; padding: 9px 14px; font-size: 14px; font-weight: 500;
border-radius: 6px; transition: filter .15s, transform .05s, box-shadow .15s; border-radius: var(--r-md); transition: var(--t);
box-shadow: 0 2px 6px rgba(192,57,43,.25); box-shadow: 0 2px 6px rgba(192,57,43,.25);
} }
#login .actions .primary:hover { box-shadow: 0 4px 12px rgba(192,57,43,.35); } #login .actions .primary:hover { box-shadow: 0 4px 12px rgba(192,57,43,.35); }
@ -122,8 +141,7 @@
#login .tabs button { #login .tabs button {
background: none; border: none; border-bottom: 2px solid transparent; background: none; border: none; border-bottom: 2px solid transparent;
padding: 8px 4px; margin-right: 16px; font-size: 13px; padding: 8px 4px; margin-right: 16px; font-size: 13px;
color: var(--muted); cursor: pointer; color: var(--muted); cursor: pointer; transition: var(--t);
transition: color .15s, border-color .15s;
} }
#login .tabs button:hover { color: var(--text); background: none; } #login .tabs button:hover { color: var(--text); background: none; }
#login .tabs button.active { color: var(--accent); border-bottom-color: var(--accent); } #login .tabs button.active { color: var(--accent); border-bottom-color: var(--accent); }
@ -133,48 +151,28 @@
from { opacity: 0; transform: translateY(2px); } from { opacity: 0; transform: translateY(2px); }
to { opacity: 1; transform: translateY(0); } to { opacity: 1; transform: translateY(0); }
} }
#login code { #login code { background: var(--code-bg); padding: 1px 5px; border-radius: var(--r-sm); font-size: 11.5px; }
background: var(--code-bg); padding: 1px 5px; border-radius: 3px; #login .card-footer { margin-top: 10px; display: flex; justify-content: flex-end; }
font-size: 11.5px;
}
#login .card-footer {
margin-top: 10px; display: flex; justify-content: flex-end;
}
#login .ghost-link { #login .ghost-link {
color: var(--muted); font-size: 12px; text-decoration: none; color: var(--muted); font-size: 12px; text-decoration: none;
padding: 2px 4px; border-radius: 4px; transition: color .15s, background .15s; padding: 2px 4px; border-radius: var(--r-md); transition: var(--t);
} }
#login .ghost-link:hover { color: var(--accent); background: var(--accent-soft); } #login .ghost-link:hover { color: var(--accent); background: var(--accent-soft); }
/* ───── admin add-user modal ───── */ /* ───── admin add-user modal ───── */
#admin-modal { #admin-modal { z-index: 110; }
position: fixed; inset: 0; background: rgba(0,0,0,0.4); #admin-modal .card { padding: 20px 24px; width: 360px; }
display: none; align-items: center; justify-content: center; z-index: 110;
}
#admin-modal.show { display: flex; }
#admin-modal .card {
background: var(--panel); padding: 20px 24px; border-radius: 10px;
width: 360px; box-shadow: 0 16px 40px rgba(0,0,0,.18);
}
#admin-modal h3 { margin: 0 0 12px; font-size: 15px; } #admin-modal h3 { margin: 0 0 12px; font-size: 15px; }
#admin-modal label { #admin-modal label {
display: block; margin-top: 10px; margin-bottom: 4px; display: block; margin-top: 10px; margin-bottom: 4px;
font-size: 12px; color: var(--muted); font-size: 12px; color: var(--muted);
} }
#admin-modal input { #admin-modal input {
width: 100%; padding: 8px 10px; border-radius: 6px; width: 100%; padding: 8px 10px; border-radius: var(--r-md);
border: 1px solid var(--border); background: #fafafa; border: 1px solid var(--border); background: #fafafa;
} }
#admin-modal input:focus { #admin-modal .err { color: var(--accent); font-size: 12px; margin-top: 10px; min-height: 1em; }
outline: none; background: #fff; border-color: var(--accent); #admin-modal .actions { margin-top: 14px; display: flex; gap: 8px; justify-content: flex-end; }
box-shadow: 0 0 0 3px rgba(192,57,43,.12);
}
#admin-modal .err {
color: var(--accent); font-size: 12px; margin-top: 10px; min-height: 1em;
}
#admin-modal .actions {
margin-top: 14px; display: flex; gap: 8px; justify-content: flex-end;
}
/* ───── 3-pane layout ───── */ /* ───── 3-pane layout ───── */
#app { display: none; height: 100vh; } #app { display: none; height: 100vh; }
@ -197,14 +195,14 @@
} }
header .brand { display: flex; align-items: center; gap: 8px; } header .brand { display: flex; align-items: center; gap: 8px; }
header .brand .logo { header .brand .logo {
width: 24px; height: 24px; border-radius: 6px; width: 24px; height: 24px; border-radius: var(--r-md);
background: linear-gradient(135deg, var(--accent), #8e2a20); background: linear-gradient(135deg, var(--accent), #8e2a20);
color: #fff; font-weight: 700; font-size: 13px; color: #fff; font-weight: 700; font-size: 13px;
display: flex; align-items: center; justify-content: center; display: flex; align-items: center; justify-content: center;
box-shadow: 0 2px 6px rgba(192,57,43,.28); box-shadow: 0 2px 6px rgba(192,57,43,.28);
} }
header .title { font-weight: 600; font-size: 15px; letter-spacing: .2px; } header .title { font-weight: 600; font-size: 15px; letter-spacing: .2px; }
header .who { color: var(--muted); font-size: 12px; font-family: monospace; } header .who { color: var(--muted); font-size: 12px; font-family: var(--mono); }
header .spacer { flex: 1; } header .spacer { flex: 1; }
.pane { border-right: 1px solid var(--border); background: var(--panel); overflow: auto; min-height: 0; } .pane { border-right: 1px solid var(--border); background: var(--panel); overflow: auto; min-height: 0; }
@ -225,26 +223,28 @@
background: #fff; background: #fff;
border-bottom: 1px solid var(--border-soft); border-bottom: 1px solid var(--border-soft);
} }
/* 对话顶栏按钮:常态中性 + hover 上语义色 — 完成 绿/导出 蓝/清空 紫/废弃 橙/删除 红 */ /* 对话顶栏按钮:常态中性 + hover 上语义色 — 完成 绿/导出 蓝/清空 紫/废弃 橙/删除 红
#btn-done:hover:not(:disabled) { color: #27ae60; border-color: #a9dfbf; background: #e9f7ef; } 同色组合并 selector(export ≈ sp-copy 蓝, abandon ≈ sp-move 橙) */
#btn-export:hover:not(:disabled) { color: #2980b9; border-color: #aed6f1; background: #ebf5fb; } #btn-done:hover:not(:disabled) { color: var(--c-green); border-color: var(--c-green-bd); background: var(--c-green-bg); }
#btn-clear-msgs:hover:not(:disabled) { color: #8e44ad; border-color: #d2b4de; background: #f5eef8; } #btn-export:hover:not(:disabled),
#btn-abandon:hover:not(:disabled) { color: #e67e22; border-color: #f5cba7; background: #fef5e7; } #sp-copy:hover:not(:disabled) { color: var(--c-blue); border-color: var(--c-blue-bd); background: var(--c-blue-bg); }
#btn-delete-task:hover:not(:disabled) { color: #c0392b; border-color: #f5b7b1; background: #fdedec; } #btn-clear-msgs:hover:not(:disabled) { color: var(--c-purple); border-color: var(--c-purple-bd); background: var(--c-purple-bg); }
#pane-mid > .pane-head > button.small:disabled { opacity: 0.4; cursor: not-allowed; } #btn-abandon:hover:not(:disabled),
#sp-move:hover:not(:disabled) { color: var(--c-orange); border-color: var(--c-orange-bd); background: var(--c-orange-bg); }
#btn-delete-task:hover:not(:disabled) { color: var(--c-red); border-color: var(--c-red-bd); background: var(--c-red-bg); }
/* ───── floating dropdown menu ───── */ /* ───── floating dropdown menu ───── */
/* 单例:position: fixed 逃出 pane overflow 裁剪;右上角触发,向下展开 */ /* 单例:position: fixed 逃出 pane overflow 裁剪;右上角触发,向下展开 */
.dd-toggle { .dd-toggle {
padding: 2px 6px; font-size: 14px; line-height: 1; padding: 2px 6px; font-size: 14px; line-height: 1;
background: transparent; border: 1px solid transparent; background: transparent; border: 1px solid transparent;
color: var(--muted); border-radius: 3px; cursor: pointer; color: var(--muted); border-radius: var(--r-sm); cursor: pointer;
} }
.dd-toggle:hover { background: var(--hover); color: var(--text); border-color: var(--border); } .dd-toggle:hover { background: var(--hover); color: var(--text); border-color: var(--border); }
#floating-menu { #floating-menu {
display: none; position: fixed; display: none; position: fixed;
min-width: 132px; background: #fff; min-width: 132px; background: #fff;
border: 1px solid var(--border); border-radius: 6px; border: 1px solid var(--border); border-radius: var(--r-md);
box-shadow: 0 4px 14px rgba(0,0,0,0.12); box-shadow: 0 4px 14px rgba(0,0,0,0.12);
z-index: 60; padding: 4px 0; z-index: 60; padding: 4px 0;
} }
@ -258,12 +258,10 @@
.dd-item:hover { background: var(--hover); } .dd-item:hover { background: var(--hover); }
.dd-item:disabled { color: var(--muted); cursor: not-allowed; opacity: 0.55; } .dd-item:disabled { color: var(--muted); cursor: not-allowed; opacity: 0.55; }
.dd-item:disabled:hover { background: transparent; } .dd-item:disabled:hover { background: transparent; }
.dd-item.act-complete { color: #2e7d32; } .dd-item.act-complete, .dd-item.act-download { color: var(--c-green); }
.dd-item.act-abandon { color: #c77800; } .dd-item.act-abandon { color: var(--c-orange); }
.dd-item.act-export { color: #1565c0; } .dd-item.act-export, .dd-item.act-rename { color: var(--c-blue); }
.dd-item.act-rename { color: #1565c0; } .dd-item.act-delete { color: var(--accent); }
.dd-item.act-download { color: #2e7d32; }
.dd-item.act-delete { color: var(--accent); }
/* ───── task list ───── */ /* ───── task list ───── */
.task-row { .task-row {
@ -284,92 +282,82 @@
.task-row .meta .num.right-group { margin-left: auto; } /* 把数字+时间整组挤到右侧 */ .task-row .meta .num.right-group { margin-left: auto; } /* 把数字+时间整组挤到右侧 */
.task-row .meta .time-ago { flex-shrink: 0; text-align: right; min-width: 64px; } .task-row .meta .time-ago { flex-shrink: 0; text-align: right; min-width: 64px; }
.task-row .badge { .task-row .badge {
display: inline-block; padding: 0 6px; border-radius: 8px; font-size: 11px; display: inline-block; padding: 0 6px; border-radius: var(--r-md); font-size: 11px;
background: #eef; color: #336; background: #eef; color: #336;
} }
.badge.completed { background: #e8f5e9; color: #2e7d32; } .badge.completed { background: var(--c-green-bg); color: var(--c-green); }
.badge.abandoned { background: #fde9e7; color: var(--accent); } .badge.abandoned { background: var(--accent-soft); color: var(--accent); }
.badge.active { background: #eef; color: #336; } .badge.active { background: #eef; color: #336; }
.empty { padding: 24px; color: var(--muted); text-align: center; font-size: 13px; } .empty { padding: 24px; color: var(--muted); text-align: center; font-size: 13px; }
/* ───── chat ───── */ /* ───── chat ───── */
#chat-meta { padding: 8px 12px; border-bottom: 1px solid var(--border); background: #fafafa; #chat-meta { padding: 8px 12px; border-bottom: 1px solid var(--border); background: #fafafa;
font-size: 12px; color: var(--muted); display: flex; gap: 12px; align-items: center; flex-wrap: wrap; } font-size: 12px; color: var(--muted); display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
#chat-meta .tid { font-family: monospace; color: var(--text); } #chat-meta .tid { font-family: var(--mono); color: var(--text); }
#chat-meta .spacer { flex: 1; } #chat-meta .spacer { flex: 1; }
/* 同 wd 并发软警告 banner — 非阻塞,只提示中间产物互覆风险 */ /* 同 wd 并发软警告 banner — 非阻塞,只提示中间产物互覆风险 */
#wd-concurrent-warn { padding: 6px 12px; border-bottom: 1px solid #f0c36d; #wd-concurrent-warn { padding: 6px 12px; border-bottom: 1px solid #f0c36d;
background: #fff8e1; color: #6a4500; font-size: 12px; } background: #fff8e1; color: #6a4500; font-size: 12px; }
#wd-concurrent-warn .tname { font-weight: 600; } #wd-concurrent-warn .tname { font-weight: 600; }
#wd-concurrent-warn .rs { font-family: monospace; opacity: 0.7; } #wd-concurrent-warn .rs { font-family: var(--mono); opacity: 0.7; }
#chat-stream { #chat-stream {
flex: 1; overflow-y: auto; overflow-x: hidden; padding: 12px; flex: 1; overflow-y: auto; overflow-x: hidden; padding: 12px;
display: flex; flex-direction: column; gap: 8px; display: flex; flex-direction: column; gap: 8px;
min-height: 0; /* 同上,允许在 flex 容器里收缩 + 触发自身滚动 */ min-height: 0; /* 允许在 flex 容器里收缩 + 触发自身滚动 */
}
.msg {
border: 1px solid var(--border); border-radius: 6px; padding: 8px 12px;
max-width: 92%;
} }
.msg { border: 1px solid var(--border); border-radius: var(--r-md); padding: 8px 12px; max-width: 92%; }
.msg.user { background: var(--user-bg); align-self: flex-end; } .msg.user { background: var(--user-bg); align-self: flex-end; }
.msg.assistant, .msg.system, .msg.tool, .msg.error { background: var(--asst-bg); align-self: flex-start; } .msg.assistant, .msg.system, .msg.tool, .msg.error { background: var(--asst-bg); align-self: flex-start; }
.msg.error { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); } .msg.error { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.cancelled-badge { margin-top: 8px; padding: 4px 10px; font-size: 12px; color: var(--accent); background: var(--accent-soft); border: 1px dashed var(--accent); border-radius: 4px; display: inline-block; } .cancelled-badge { margin-top: 8px; padding: 4px 10px; font-size: 12px; color: var(--accent); background: var(--accent-soft); border: 1px dashed var(--accent); border-radius: var(--r-md); display: inline-block; }
.msg .role { font-size: 11px; color: var(--muted); margin-bottom: 2px; font-family: monospace; } .msg .role { font-size: 11px; color: var(--muted); margin-bottom: 2px; font-family: var(--mono); }
.msg .body { word-wrap: break-word; font-size: 14px; line-height: 1.55; } .msg .body { word-wrap: break-word; font-size: 14px; line-height: 1.55; }
.msg .body.streaming::after { content: "▌"; color: var(--accent); animation: blink 1s infinite; } .msg .body.streaming::after { content: "▌"; color: var(--accent); animation: blink 1s infinite; }
@keyframes blink { 0%,49% { opacity: 1; } 50%,100% { opacity: 0; } } @keyframes blink { 0%,49% { opacity: 1; } 50%,100% { opacity: 0; } }
/* markdown 输出元素紧凑化 */ /* markdown 输出:.msg .body 与 file-preview .md-render 共用一组规则 */
.msg .body > :first-child { margin-top: 0; } .msg .body > :first-child, .md-render > :first-child { margin-top: 0; }
.msg .body > :last-child { margin-bottom: 0; } .msg .body > :last-child, .md-render > :last-child { margin-bottom: 0; }
.msg .body p { margin: 0.4em 0; } .msg .body p, .md-render p { margin: 0.4em 0; }
.msg .body h1, .msg .body h2, .msg .body h3, .msg .body h4 { .msg .body h1, .msg .body h2, .msg .body h3, .msg .body h4,
.md-render h1, .md-render h2, .md-render h3, .md-render h4 {
margin: 0.8em 0 0.3em; line-height: 1.3; margin: 0.8em 0 0.3em; line-height: 1.3;
} }
.msg .body h1 { font-size: 1.4em; } .msg .body h1, .md-render h1 { font-size: 1.4em; }
.msg .body h2 { font-size: 1.25em; } .msg .body h2, .md-render h2 { font-size: 1.25em; }
.msg .body h3 { font-size: 1.1em; } .msg .body h3, .md-render h3 { font-size: 1.1em; }
.msg .body h4 { font-size: 1em; font-weight: 600; } .msg .body h4, .md-render h4 { font-size: 1em; font-weight: 600; }
.msg .body ul, .msg .body ol { margin: 0.4em 0; padding-left: 1.6em; } .msg .body ul, .msg .body ol, .md-render ul, .md-render ol { margin: 0.4em 0; padding-left: 1.6em; }
.msg .body li { margin: 0.15em 0; } .msg .body li, .md-render li { margin: 0.15em 0; }
.msg .body li > p { margin: 0.15em 0; } .msg .body li > p, .md-render li > p { margin: 0.15em 0; }
.msg .body blockquote { .msg .body blockquote, .md-render blockquote {
margin: 0.4em 0; padding: 4px 12px; border-left: 3px solid var(--accent); margin: 0.4em 0; padding: 4px 12px; border-left: 3px solid var(--accent);
background: var(--accent-soft); color: #555; background: var(--accent-soft); color: #555;
} }
.msg .body code:not(pre code) { .msg .body code:not(pre code), .md-render code:not(pre code) {
background: var(--code-bg); padding: 1px 5px; border-radius: 3px; background: var(--code-bg); padding: 1px 5px; border-radius: var(--r-sm);
font-family: ui-monospace, "Cascadia Code", Consolas, monospace; font-family: var(--mono); font-size: 0.92em;
font-size: 0.92em;
} }
.msg .body pre { .msg .body pre, .md-render pre {
margin: 0.5em 0; padding: 10px; background: #f6f8fa; border-radius: 4px; margin: 0.5em 0; padding: 10px; background: #f6f8fa; border-radius: var(--r-md);
overflow-x: auto; font-size: 12.5px; line-height: 1.4; overflow-x: auto; font-size: 12.5px; line-height: 1.4;
} }
.msg .body pre code { .msg .body pre code, .md-render pre code { font-family: var(--mono); background: transparent; padding: 0; }
font-family: ui-monospace, "Cascadia Code", Consolas, monospace; .msg .body table, .md-render table { border-collapse: collapse; margin: 0.5em 0; font-size: 13px; }
background: transparent; padding: 0; .msg .body th, .msg .body td, .md-render th, .md-render td {
}
.msg .body table {
border-collapse: collapse; margin: 0.5em 0; font-size: 13px;
}
.msg .body th, .msg .body td {
border: 1px solid var(--border); padding: 4px 8px; border: 1px solid var(--border); padding: 4px 8px;
} }
.msg .body th { background: #fafafa; font-weight: 600; } .msg .body th, .md-render th { background: #fafafa; font-weight: 600; }
.msg .body a { color: var(--accent); } .msg .body a, .md-render a { color: var(--accent); }
.msg .body img { max-width: 100%; } .msg .body img, .md-render img { max-width: 100%; }
.msg .body hr { border: none; border-top: 1px solid var(--border); margin: 0.8em 0; } .msg .body hr, .md-render hr { border: none; border-top: 1px solid var(--border); margin: 0.8em 0; }
.tool-call { .tool-call { margin-top: 6px; font-family: var(--mono); font-size: 12px; }
margin-top: 6px; font-family: ui-monospace, Consolas, monospace; font-size: 12px;
}
.tool-call summary { .tool-call summary {
cursor: pointer; padding: 4px 6px; background: var(--code-bg); border-radius: 3px; cursor: pointer; padding: 4px 6px; background: var(--code-bg); border-radius: var(--r-sm);
color: #555; color: #555;
} }
.tool-call summary:hover { background: #ebebeb; } .tool-call summary:hover { background: #ebebeb; }
.tool-call pre { .tool-call pre {
margin: 4px 0 0; padding: 8px; background: var(--code-bg); border-radius: 3px; margin: 4px 0 0; padding: 8px; background: var(--code-bg); border-radius: var(--r-sm);
overflow-x: auto; max-height: 300px; white-space: pre-wrap; overflow-x: auto; max-height: 300px; white-space: pre-wrap;
} }
/* media tool 摘要 banner(model / size / cost / elapsed,折叠态也可见) */ /* media tool 摘要 banner(model / size / cost / elapsed,折叠态也可见) */
@ -378,36 +366,31 @@
margin-left: 8px; font-size: 11px; vertical-align: middle; margin-left: 8px; font-size: 11px; vertical-align: middle;
} }
.tool-banner .kv { .tool-banner .kv {
padding: 1px 6px; border-radius: 3px; background: #fff; padding: 1px 6px; border-radius: var(--r-sm); background: #fff;
border: 1px solid var(--border); color: #555; border: 1px solid var(--border); color: #555;
} }
.tool-banner .kv.cost { color: #b34a4a; border-color: #e0c4c4; } .tool-banner .kv.cost { color: #b34a4a; border-color: #e0c4c4; }
.tool-banner .kv.model { color: var(--accent); border-color: #e0c4c4; } .tool-banner .kv.model { color: var(--accent); border-color: #e0c4c4; }
/* ───── artifact chips(对话内点产物预览/下载) ───── */ /* ───── artifact chips(对话内点产物预览/下载) ───── */
.artifact-bar { .artifact-bar { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 4px; font-family: var(--mono); }
margin-top: 4px; display: flex; flex-wrap: wrap; gap: 4px;
font-family: ui-monospace, Consolas, monospace;
}
.art-chip { .art-chip {
font: inherit; font-size: 11px; line-height: 1.4; font: inherit; font-size: 11px; line-height: 1.4;
padding: 2px 8px 2px 6px; border: 1px solid var(--border); padding: 2px 8px 2px 6px; border: 1px solid var(--border);
background: #fff; color: #555; border-radius: 999px; cursor: pointer; background: #fff; color: #555; border-radius: 999px; cursor: pointer;
max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
display: inline-flex; align-items: center; gap: 4px; display: inline-flex; align-items: center; gap: 4px;
transition: color .12s, border-color .12s, background .12s; transition: var(--t);
} }
.art-chip::before { content: "📄"; font-size: 11px; } .art-chip::before { content: "📄"; font-size: 11px; }
.art-chip:hover { .art-chip:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}
/* 内联图片/视频:产物 chip 替代,fetch 完直接展示 */ /* 内联图片/视频:产物 chip 替代,fetch 完直接展示 */
.art-media { .art-media {
border: 1px solid var(--border); border-radius: 6px; overflow: hidden; border: 1px solid var(--border); border-radius: var(--r-md); overflow: hidden;
background: #fff; display: inline-block; line-height: 0; background: #fff; display: inline-block; line-height: 0;
} }
.art-media .art-media-loading, .art-media .art-media-error { .art-media .art-media-loading, .art-media .art-media-error {
display: inline-block; padding: 6px 10px; font-size: 11px; display: inline-block; padding: 6px 10px; font-size: 11px;
color: var(--muted); line-height: 1.4; font-family: ui-monospace, Consolas, monospace; color: var(--muted); line-height: 1.4; font-family: var(--mono);
} }
.art-media .art-media-error { color: #b34a4a; } .art-media .art-media-error { color: #b34a4a; }
.art-media img { .art-media img {
@ -437,7 +420,7 @@
} }
.file-row:hover { background: var(--hover); } .file-row:hover { background: var(--hover); }
.file-row .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .file-row .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-row .size { font-size: 11px; color: var(--muted); font-family: monospace; } .file-row .size { font-size: 11px; color: var(--muted); font-family: var(--mono); }
.ico-dir::before { content: "▸ "; color: var(--accent); } .ico-dir::before { content: "▸ "; color: var(--accent); }
.ico-file::before { content: "· "; color: var(--muted); } .ico-file::before { content: "· "; color: var(--muted); }
@ -453,16 +436,10 @@
#file-droparea.show { display: flex; } #file-droparea.show { display: flex; }
/* ───── source picker modal(选入文件:勾源 + 复制/移动到主区当前目录) ───── */ /* ───── source picker modal(选入文件:勾源 + 复制/移动到主区当前目录) ───── */
#src-picker-modal { #src-picker-modal { z-index: 95; }
position: fixed; inset: 0; background: rgba(0,0,0,0.4);
display: none; align-items: center; justify-content: center; z-index: 95;
}
#src-picker-modal.show { display: flex; }
#src-picker-modal .card { #src-picker-modal .card {
background: var(--panel); border-radius: 8px;
width: 560px; max-height: 82vh; width: 560px; max-height: 82vh;
display: flex; flex-direction: column; display: flex; flex-direction: column;
box-shadow: 0 12px 32px rgba(0,0,0,.18);
} }
#src-picker-modal h3 { #src-picker-modal h3 {
margin: 0; padding: 14px 18px; font-size: 16px; margin: 0; padding: 14px 18px; font-size: 16px;
@ -477,19 +454,12 @@
padding: 8px 18px; font-size: 12px; color: var(--muted); padding: 8px 18px; font-size: 12px; color: var(--muted);
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
} }
#sp-crumbs { #sp-crumbs { padding: 8px 14px; border-bottom: 1px solid var(--border); font-size: 12px; background: #fafafa; }
padding: 8px 14px; border-bottom: 1px solid var(--border);
font-size: 12px; background: #fafafa;
}
#sp-crumbs a { margin-right: 4px; } #sp-crumbs a { margin-right: 4px; }
#sp-list { #sp-list { flex: 1; overflow: auto; min-height: 240px; max-height: 50vh; }
flex: 1; overflow: auto;
min-height: 240px; max-height: 50vh;
}
#sp-list .sp-row { #sp-list .sp-row {
padding: 6px 14px; border-bottom: 1px solid var(--border); padding: 6px 14px; border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 8px; display: flex; align-items: center; gap: 8px; font-size: 13px;
font-size: 13px;
} }
#sp-list .sp-row:hover { background: var(--hover); } #sp-list .sp-row:hover { background: var(--hover); }
#sp-list .sp-row .sp-cb { flex-shrink: 0; margin: 0; } #sp-list .sp-row .sp-cb { flex-shrink: 0; margin: 0; }
@ -498,42 +468,24 @@
cursor: pointer; cursor: pointer;
} }
#sp-list .sp-row.disabled .sp-name { color: var(--muted); cursor: not-allowed; } #sp-list .sp-row.disabled .sp-name { color: var(--muted); cursor: not-allowed; }
#sp-list .sp-row .sp-size { font-size: 11px; color: var(--muted); font-family: monospace; } #sp-list .sp-row .sp-size { font-size: 11px; color: var(--muted); font-family: var(--mono); }
#sp-list .empty { padding: 18px; color: var(--muted); text-align: center; font-size: 12px; }
#src-picker-modal .actions { #src-picker-modal .actions {
padding: 12px 18px; border-top: 1px solid var(--border); padding: 12px 18px; border-top: 1px solid var(--border);
display: flex; gap: 8px; align-items: center; display: flex; gap: 8px; align-items: center;
} }
#src-picker-modal .actions .count { #src-picker-modal .actions .count { flex: 1; font-size: 12px; color: var(--muted); }
flex: 1; font-size: 12px; color: var(--muted);
}
#sp-copy:hover:not(:disabled) { color: #1565c0; border-color: #aed6f1; background: #ebf5fb; }
#sp-move:hover:not(:disabled) { color: #c77800; border-color: #f5cba7; background: #fef5e7; }
#sp-copy:disabled, #sp-move:disabled { opacity: 0.4; cursor: not-allowed; }
/* ───── new task modal ───── */ /* ───── new task modal ───── */
#new-task-modal { #new-task-modal { z-index: 80; }
position: fixed; inset: 0; background: rgba(0,0,0,0.4); #new-task-modal .card { padding: 20px; width: 420px; }
display: none; align-items: center; justify-content: center; z-index: 80;
}
#new-task-modal.show { display: flex; }
#new-task-modal .card {
background: var(--panel); padding: 20px; border-radius: 8px;
width: 420px; box-shadow: 0 12px 32px rgba(0,0,0,.18);
}
#new-task-modal h3 { margin: 0 0 12px; font-size: 16px; } #new-task-modal h3 { margin: 0 0 12px; font-size: 16px; }
#new-task-modal label { display: block; margin-top: 8px; font-size: 12px; color: var(--muted); } #new-task-modal label { display: block; margin-top: 8px; font-size: 12px; color: var(--muted); }
#new-task-modal .err { color: var(--accent); font-size: 12px; margin-top: 8px; min-height: 1em; } #new-task-modal .err { color: var(--accent); font-size: 12px; margin-top: 8px; min-height: 1em; }
#new-task-modal .actions { margin-top: 14px; display: flex; gap: 8px; justify-content: flex-end; } #new-task-modal .actions { margin-top: 14px; display: flex; gap: 8px; justify-content: flex-end; }
/* ───── file preview modal ───── */ /* ───── file preview modal(略深的遮罩 0.5 + 更重阴影) ───── */
#file-preview-modal { #file-preview-modal { background: rgba(0,0,0,0.5); z-index: 90; }
position: fixed; inset: 0; background: rgba(0,0,0,0.5);
display: none; align-items: center; justify-content: center; z-index: 90;
}
#file-preview-modal.show { display: flex; }
#file-preview-modal .card { #file-preview-modal .card {
background: var(--panel); border-radius: 8px;
width: 90vw; height: 90vh; max-width: 1200px; width: 90vw; height: 90vh; max-width: 1200px;
display: flex; flex-direction: column; display: flex; flex-direction: column;
box-shadow: 0 12px 32px rgba(0,0,0,.22); box-shadow: 0 12px 32px rgba(0,0,0,.22);
@ -546,15 +498,9 @@
flex: 1; font-weight: 500; overflow: hidden; flex: 1; font-weight: 500; overflow: hidden;
text-overflow: ellipsis; white-space: nowrap; text-overflow: ellipsis; white-space: nowrap;
} }
#file-preview-modal .body { #file-preview-modal .body { flex: 1; overflow: auto; padding: 12px; position: relative; }
flex: 1; overflow: auto; padding: 12px; position: relative; #file-preview-modal .body.center { display: flex; align-items: center; justify-content: center; }
} #file-preview-modal .body .ph { color: var(--muted); font-size: 13px; text-align: center; }
#file-preview-modal .body.center {
display: flex; align-items: center; justify-content: center;
}
#file-preview-modal .body .ph {
color: var(--muted); font-size: 13px; text-align: center;
}
#file-preview-modal .body img.preview-img { #file-preview-modal .body img.preview-img {
max-width: 100%; max-height: 100%; object-fit: contain; max-width: 100%; max-height: 100%; object-fit: contain;
display: block; margin: 0 auto; display: block; margin: 0 auto;
@ -562,23 +508,14 @@
#file-preview-modal .body video.preview-video { #file-preview-modal .body video.preview-video {
max-width: 100%; max-height: 100%; display: block; margin: 0 auto; outline: none; max-width: 100%; max-height: 100%; display: block; margin: 0 auto; outline: none;
} }
#file-preview-modal .body iframe.preview-frame { #file-preview-modal .body iframe.preview-frame { width: 100%; height: 100%; border: 0; }
width: 100%; height: 100%; border: 0;
}
#file-preview-modal .body pre.preview-text { #file-preview-modal .body pre.preview-text {
margin: 0; padding: 8px; background: var(--code-bg); margin: 0; padding: 8px; background: var(--code-bg);
border-radius: 4px; white-space: pre-wrap; word-break: break-word; border-radius: var(--r-md); white-space: pre-wrap; word-break: break-word;
font-family: ui-monospace, "SF Mono", Consolas, monospace; font-family: var(--mono); font-size: 12px; line-height: 1.5;
font-size: 12px; line-height: 1.5;
} }
#file-preview-modal .body .md-render { /* .md-render 通用样式已与 .msg .body 合并到上方 chat 段;这里只保留 file-preview 专属 */
max-width: 860px; margin: 0 auto; line-height: 1.7; #file-preview-modal .body .md-render { max-width: 860px; margin: 0 auto; line-height: 1.7; }
}
#file-preview-modal .body .md-render pre {
background: var(--code-bg); padding: 10px; border-radius: 4px; overflow: auto;
}
#file-preview-modal .body .md-render code { background: var(--code-bg); padding: 1px 4px; border-radius: 3px; }
#file-preview-modal .body .md-render pre code { background: transparent; padding: 0; }
#file-preview-modal .body .docx-host { background: #fff; } #file-preview-modal .body .docx-host { background: #fff; }
#file-preview-modal .body .xlsx-tabs { #file-preview-modal .body .xlsx-tabs {
display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 8px; display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 8px;
@ -587,12 +524,8 @@
#file-preview-modal .body .xlsx-tabs button.active { #file-preview-modal .body .xlsx-tabs button.active {
background: var(--accent-soft); border-color: var(--accent); color: var(--accent); background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
} }
#file-preview-modal .body .xlsx-sheet { #file-preview-modal .body .xlsx-sheet { overflow: auto; }
overflow: auto; #file-preview-modal .body .xlsx-sheet table { border-collapse: collapse; font-size: 12px; }
}
#file-preview-modal .body .xlsx-sheet table {
border-collapse: collapse; font-size: 12px;
}
#file-preview-modal .body .xlsx-sheet td, #file-preview-modal .body .xlsx-sheet th { #file-preview-modal .body .xlsx-sheet td, #file-preview-modal .body .xlsx-sheet th {
border: 1px solid var(--border); padding: 4px 8px; white-space: nowrap; border: 1px solid var(--border); padding: 4px 8px; white-space: nowrap;
} }
@ -649,7 +582,7 @@
</div> </div>
<!-- ───── admin add-user modal ───── --> <!-- ───── admin add-user modal ───── -->
<div id="admin-modal"> <div id="admin-modal" class="modal">
<div class="card"> <div class="card">
<h3>添加用户</h3> <h3>添加用户</h3>
<label for="ad-email">邮箱</label> <label for="ad-email">邮箱</label>
@ -759,7 +692,7 @@
</div> </div>
<!-- ───── source picker modal(选入文件:勾源 → 复制/移动到主区当前目录) ───── --> <!-- ───── source picker modal(选入文件:勾源 → 复制/移动到主区当前目录) ───── -->
<div id="src-picker-modal"> <div id="src-picker-modal" class="modal">
<div class="card"> <div class="card">
<h3> <h3>
<span>选入到</span> <span>选入到</span>
@ -781,7 +714,7 @@
<div id="floating-menu"></div> <div id="floating-menu"></div>
<!-- ───── new task modal ───── --> <!-- ───── new task modal ───── -->
<div id="new-task-modal"> <div id="new-task-modal" class="modal">
<div class="card"> <div class="card">
<h3>新建任务</h3> <h3>新建任务</h3>
<label for="nt-name">任务名(必填)</label> <label for="nt-name">任务名(必填)</label>
@ -809,7 +742,7 @@
</div> </div>
<!-- ───── file preview modal ───── --> <!-- ───── file preview modal ───── -->
<div id="file-preview-modal"> <div id="file-preview-modal" class="modal">
<div class="card"> <div class="card">
<div class="hdr"> <div class="hdr">
<span class="name" id="fp-name"></span> <span class="name" id="fp-name"></span>