diff --git a/PROGRESS.md b/PROGRESS.md index 98e19f9..4ee8618 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -21,6 +21,14 @@ ## 已完成关键能力 +### 2026-06-13 / 前端 UI 优化:中栏操作收菜单 + 阅读限宽 + 色彩收敛 + +- **中栏顶栏 5 按钮 → 「完成」+「⋯」菜单**:原导出/清空/完成/废弃/删除 平铺,与任务行的 `⋯` 浮层菜单两套范式打架,且破坏性操作(废弃/删除)平铺易误点、移动端挤。改为只留高频「完成」+ 一个 `⋯`,菜单复用 `taskMenuItems`(过滤掉 complete);单一事实源,两处共用。顺带把「清空」在菜单里按 `run_status` 也禁用(taskMeta 带该字段,修了之前菜单清空运行中会 409-after-confirm 的小坑)。 +- **消息阅读限宽**:`.msg` 由 `max-width:92%` 收到 `min(92%,48rem)`(assistant ~60-80 字/行),user 气泡 `min(92%,36rem)`;宽屏长文不再满屏铺开难回扫,窄屏 92% 仍生效。 +- **色彩负载收敛**:语义色由"每个操作一色"改为"颜色=后果"——正向(完成/下载)绿、破坏性(废弃橙/删除红),中性(导出/清空)不着色;移除紫色"清空"与蓝色"导出"。删掉已不存在的顶栏按钮 hover 规则(保留 file-picker 的 sp-copy/sp-move)。 +- 改动文件:`dev.html`(中栏 markup + 三处 CSS)、`chat.js`(菜单接线 + renderChatMeta/deleteTask 收口)。**未动**左栏 4 行筛选头折叠(点 2,行为变化较大,留作下一步)。 +- bump 0.12.1 → 0.12.2(patch:UI 重构 + 样式)。 + ### 2026-06-13 / 前端小修:导出按钮简写 + 任务菜单加清空 + 移动端 task 可滚 + admin 自适应 - **顶栏「导出对话记录」→「导出对话」**:与「清空对话」对齐(`dev.html` 按钮 + `chat.js` 任务菜单 export 项同步)。 diff --git a/core/__init__.py b/core/__init__.py index 3764135..9d9acea 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.12.1" +__version__ = "0.12.2" diff --git a/web/static/dev.html b/web/static/dev.html index 2b7e53e..8cba74b 100644 --- a/web/static/dev.html +++ b/web/static/dev.html @@ -413,15 +413,11 @@ background: #fff; border-bottom: 1px solid var(--border-soft); } - /* 对话顶栏按钮:常态中性 + hover 上语义色 — 完成 绿/导出 蓝/清空 紫/废弃 橙/删除 红 - 同色组合并 selector(export ≈ sp-copy 蓝, abandon ≈ sp-move 橙) */ + /* 对话顶栏只剩「完成」(绿)+「⋯」菜单;其余操作收进浮层菜单按语义色(见 .dd-item.act-*)。 + file-picker 的 sp-copy/sp-move 仍复用蓝/橙。 */ #btn-done:hover:not(:disabled) { color: var(--c-green); border-color: var(--c-green-bd); background: var(--c-green-bg); } - #btn-export:hover:not(:disabled), #sp-copy:hover:not(:disabled) { color: var(--c-blue); border-color: var(--c-blue-bd); background: var(--c-blue-bg); } - #btn-clear-msgs:hover:not(:disabled) { color: var(--c-purple); border-color: var(--c-purple-bd); background: var(--c-purple-bg); } - #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 ───── */ /* 单例:position: fixed 逃出 pane overflow 裁剪;右上角触发,向下展开 */ @@ -448,10 +444,11 @@ .dd-item:hover { background: var(--hover); } .dd-item:disabled { color: var(--muted); cursor: not-allowed; opacity: 0.55; } .dd-item:disabled:hover { background: transparent; } + /* 菜单项颜色 = 操作后果:正向(完成/下载)绿,破坏性(废弃)橙、(删除)红; + 中性操作(导出/清空)不着色,降低色彩负载。rename(文件菜单)保留蓝。 */ .dd-item.act-complete, .dd-item.act-download { color: var(--c-green); } .dd-item.act-abandon { color: var(--c-orange); } - .dd-item.act-export, .dd-item.act-rename { color: var(--c-blue); } - .dd-item.act-clear { color: var(--c-purple); } + .dd-item.act-rename { color: var(--c-blue); } .dd-item.act-delete { color: var(--accent); } /* ───── task list ───── */ @@ -498,8 +495,10 @@ display: flex; flex-direction: column; gap: 8px; min-height: 0; /* 允许在 flex 容器里收缩 + 触发自身滚动 */ } - .msg { border: 1px solid var(--border); border-radius: var(--r-md); padding: 8px 12px; max-width: 92%; animation: msg-in .22s cubic-bezier(.2,.7,.2,1); } - .msg.user { background: var(--user-bg); align-self: flex-end; } + /* 阅读宽度:assistant/system/tool 限到 ~48rem(约 60-80 字/行,长文不至于满屏铺开难回扫); + user 气泡更窄(36rem)。宽屏下提升可读性,窄屏 92% 仍生效(min 取小者) */ + .msg { border: 1px solid var(--border); border-radius: var(--r-md); padding: 8px 12px; max-width: min(92%, 48rem); animation: msg-in .22s cubic-bezier(.2,.7,.2,1); } + .msg.user { background: var(--user-bg); align-self: flex-end; max-width: min(92%, 36rem); } .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); } .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; } @@ -1241,11 +1240,8 @@
对话 - - - - +
(未选中任务)
diff --git a/web/static/js/chat.js b/web/static/js/chat.js index e7a98f2..5540959 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -174,6 +174,8 @@ function renderTaskList(tasks, append = false) { function taskMenuItems(t) { const isActive = t.status === "active"; const hasMsg = (t.n_messages || 0) > 0; + // run_status 仅 taskMeta(中栏 ⋯)带;列表行摘要无此字段 → undefined → running=false(与改前一致) + const running = t.run_status === "running" || t.run_status === "cancelling"; return [ { act: "complete", label: "完成", cls: "act-complete", disabled: !isActive, onclick: () => setTaskStatus(t.task_id, "completed", t.name || "(未命名)") }, @@ -181,7 +183,7 @@ function taskMenuItems(t) { onclick: () => setTaskStatus(t.task_id, "abandoned", t.name || "(未命名)") }, { act: "export", label: "导出对话", cls: "act-export", disabled: !hasMsg, onclick: () => exportTask(t.task_id) }, - { act: "clear", label: "清空对话", cls: "act-clear", disabled: !hasMsg, + { act: "clear", label: "清空对话", cls: "act-clear", disabled: !hasMsg || running, onclick: () => clearMessages(t.task_id, t.name || "(未命名)", t.n_messages || 0) }, { act: "delete", label: "删除", cls: "act-delete", onclick: () => deleteTask(t.task_id, t.name || "(未命名)", t.n_messages || 0) }, @@ -314,15 +316,9 @@ function renderChatMeta() { $("chat-form").style.display = active ? "flex" : "none"; syncOptimizeBtn(); $("btn-done").disabled = !active; - $("btn-abandon").disabled = !active; - $("btn-delete-task").disabled = false; // delete 不限 status(用户显式 confirm) - // 导出 / 清空:只要选中 task 就允许点(不按 n_messages 门禁 —— 历史 bug: - // 清空后 n_messages=0 disable,但新对话进来后 taskMeta 不重渲一直 disable; - // 0 条时点击不会出错(导出空 docx / 清空 confirm 显 0 条),让 UX 一致更省心)。 - $("btn-export").disabled = false; - // 清空对话:仅活跃 run 期间禁用(后端 409,confirm 通过后才报错 UX 差) - const running = t.run_status === "running" || t.run_status === "cancelling"; - $("btn-clear-msgs").disabled = running; + // ⋯ 菜单:选中即可用;各项 enable/disable(完成/废弃按 status、清空按 run_status+n_messages) + // 全在 taskMenuItems 内部判定,这里只管整体可用性。 + $("btn-task-menu").disabled = false; } function renderModelDropdown(t) { @@ -1080,17 +1076,11 @@ function appendErrorCard(msg) { // ───── done / abandon / delete / export ───── $("btn-done").onclick = () => state.taskId && setTaskStatus(state.taskId, "completed", (state.taskMeta && state.taskMeta.name) || ""); -$("btn-abandon").onclick = () => state.taskId && setTaskStatus(state.taskId, "abandoned", (state.taskMeta && state.taskMeta.name) || ""); -$("btn-delete-task").onclick = () => { - if (!state.taskId) return; - const t = state.taskMeta || {}; - deleteTask(state.taskId, t.name || "(未命名)", t.n_messages || 0); -}; -$("btn-export").onclick = () => state.taskId && exportTask(state.taskId); -$("btn-clear-msgs").onclick = () => { - if (!state.taskId) return; - const t = state.taskMeta || {}; - clearMessages(state.taskId, t.name || "(未命名)", t.n_messages || 0); +// 其余操作(废弃/导出/清空/删除)走与任务行同款 ⋯ 浮层菜单;「完成」已是独立按钮 → 菜单里去掉 +$("btn-task-menu").onclick = (e) => { + if (!state.taskId || !state.taskMeta) return; + e.stopPropagation(); + showMenu($("btn-task-menu"), taskMenuItems(state.taskMeta).filter((it) => it.act !== "complete")); }; async function clearMessages(tid, name, nMsg) { @@ -1138,10 +1128,7 @@ async function deleteTask(tid, name, nMsg) { renderTaskProgressDock([]); $("chat-form").style.display = "none"; $("btn-done").disabled = true; - $("btn-abandon").disabled = true; - $("btn-delete-task").disabled = true; - $("btn-export").disabled = true; - $("btn-clear-msgs").disabled = true; + $("btn-task-menu").disabled = true; } loadTaskList(); loadFiles();