From 4873dd0ae5266df68af2fea3b5fc13277ce4d5c3 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 6 Aug 2026 14:14:38 +0800 Subject: [PATCH] fix(chat): keep new conversation model controls inline --- CHANGELOG.md | 4 ++++ PROGRESS.md | 3 ++- core/__init__.py | 2 +- tests/test_static_vendor.py | 5 ++++- web/static/dev.html | 8 +++++++- web/static/js/chat.js | 39 ++++++++++++++++++++++++------------- web/static/ui.css | 10 +++++----- 7 files changed, 49 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fb8a3e..b54c19a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ > 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。 > 工程口径的完整记录见 `PROGRESS.md` / git log。 +## 0.62.5 — 2026-08-06 + +- 修复新对话顶部模型控件换行以及生图、生视频模型入口缺失的问题;对话模型与媒体模型现在保持在同一行。 + ## 0.62.4 — 2026-08-06 - 开启新对话时可以在顶部直接选择对话模型,第一条消息会使用所选模型;工作目录候选改为浮层展示,展开时不再挤出对话区滚动条。 diff --git a/PROGRESS.md b/PROGRESS.md index 729aaee..af624d6 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -2,7 +2,7 @@ > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`。 -最后更新:2026-08-06(新对话创建前选择模型,bump 0.62.4) +最后更新:2026-08-06(新对话模型控件单行修复,bump 0.62.5) --- @@ -23,6 +23,7 @@ ### 2026-08-06 +- **08-06 / 0.62.5 / 新对话模型控件单行 + 媒体入口恢复**:新对话顶部改用专用的 `draft-controls` 单行布局,不再复用历史 task meta 的强制换行 spacer;模型清单异步加载完成后统一重绘草稿 meta,同时恢复既有“⚙ 媒体”入口及生图/生视频选择弹层。相关静态前端、无 DB 路由及更新日志共 34 项 unittest、JavaScript 语法与 diff 检查通过;当前环境无可用浏览器实例,未连接生产 DB。无 schema、migration、HTTP API、依赖或运行方式变化。 - **08-06 / 0.62.4 / 新对话创建前选择模型 + 目录浮层优化**:快速新对话草稿在顶部复用既有模型选择位置,只展示当前用户可用模型并在首次发送或上传附件懒创建 task 时持久化所选档位;清单失败继续由后端默认模型兜底。工作目录候选改为视口级浮层,按可用空间自动向上或向下展开,不再扩大 `chat-stream` 滚动范围;窄屏保持标题与模型同排。相关静态前端、无 DB 路由及更新日志共 34 项 unittest、JavaScript 语法与 diff 检查通过;当前环境无可用浏览器实例,未连接生产 DB。无 schema、migration、HTTP API、依赖或运行方式变化。 - **08-06 / 0.62.3 / PPT 取消定向模型**:删除 ppt skill frontmatter 的 `glm.pro52` 定向配置,load 后继续沿用 task 当前模型,显式选模型与通用 skill 定向机制保持不变。同步更新 Skill 清单与设计记录;定向模型 7 项 unittest、PPT frontmatter 解析及 diff 检查通过;无 schema、migration、HTTP API、依赖或运行方式变化,未连接生产 DB。 - **08-06 / 0.62.2 / 外部结果持久导出 + task 点目录查看开关**:外部大响应缓存统一迁入 user/task 隔离的 `.zcbot_cache`,兼容读取 0.62.1 旧目录;用户明确要求保存时新增 `external_system_result_export`,重验当前连接授权后把完整响应及 operation/参数/查询时间溯源导出到 `data/external/`,不受 24h TTL 影响。媒体 prompt/model/cost sidecar 改存 `figures|videos/.meta/`;文件面板默认隐藏所有 dotdir,当前 task 工作目录子树可用 `.*` 开关查看 `.meta/.build/.preview` 等目录,user_root 平台目录和点文件始终不展示。完整 483 项 unittest 全绿(17 skip),Node 语法、Ruff 致命规则与 diff 检查通过;当前环境无可用浏览器实例,真实点击留部署后冒烟。无 schema、migration 或依赖变化,未连接生产 DB。 diff --git a/core/__init__.py b/core/__init__.py index 05df632..ec20931 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.62.4" +__version__ = "0.62.5" diff --git a/tests/test_static_vendor.py b/tests/test_static_vendor.py index 4b7ec32..09e7744 100644 --- a/tests/test_static_vendor.py +++ b/tests/test_static_vendor.py @@ -115,7 +115,10 @@ class StaticVendorTests(unittest.TestCase): self.assertIn("draftModelProfile", state_js) self.assertIn("state.draftModelProfile = defaultDraftModelProfile();", chat_js) self.assertIn('model_profile: state.draftModelProfile || ""', chat_js) - self.assertIn('$("chat-meta").classList.add("draft-meta");', chat_js) + self.assertIn("function renderDraftChatMeta()", chat_js) + self.assertIn("${renderMediaModelTrigger()}", chat_js) + self.assertIn("bindMediaModelTrigger();", chat_js) + self.assertIn("#chat-meta.draft-meta { flex-wrap:nowrap; }", html) self.assertNotIn("new-chat-model-row", chat_js) self.assertIn("function positionDraftFolderOptions()", chat_js) self.assertIn("#new-chat-dir-options { position:fixed;", html) diff --git a/web/static/dev.html b/web/static/dev.html index 1b82ecc..cd5ca13 100644 --- a/web/static/dev.html +++ b/web/static/dev.html @@ -721,6 +721,12 @@ font-size: 12px; color: var(--muted); display: flex; gap: 12px; align-items: center; flex-wrap: wrap; } #chat-meta .tid { font-family: var(--mono); color: var(--text); } #chat-meta .spacer { flex: 1; } + #chat-meta.draft-meta { flex-wrap:nowrap; } + #chat-meta.draft-meta .draft-title { flex:none; font-weight:600; } + #chat-meta.draft-meta .draft-controls { + display:flex; align-items:center; justify-content:flex-end; gap:8px; + min-width:0; margin-left:auto; + } /* 模型下拉标签:桌面文字 / 手机 emoji 二选一(swap 在 @media 640px 内) */ .mdl-icon { display: none; } /* 同 wd 并发软警告 banner — 非阻塞,只提示中间产物互覆风险 */ @@ -1498,8 +1504,8 @@ .mdl-text { display: none; } .mdl-icon { display: inline; } #chat-meta select { max-width: 48vw; } - #chat-meta.draft-meta > .spacer { flex:1 1 auto; height:auto; } #chat-meta.draft-meta .mdl-wrap { flex:0 1 auto; } + #chat-meta.draft-meta select { width:auto !important; max-width:48vw; } /* 对话面板顶栏:藏 "对话" label / spacer,5 个按钮自然换行,文字 nowrap 防内部断行 */ #pane-mid > .pane-head { flex-wrap: wrap; gap: 6px; padding: 6px 8px; } #pane-mid > .pane-head > .label, diff --git a/web/static/js/chat.js b/web/static/js/chat.js index 491690e..0fd543e 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -65,6 +65,7 @@ export async function loadModels() { // embed + task_id 场景下 selectTask 可能在 loadModels 完成前就跑完 renderChatMeta, // 此时 models 为空 → 模型下拉不渲染。loadModels 收尾时如果已选中 task,补一次 chat-meta 重渲。 if (state.taskMeta) renderChatMeta(); + else if (!state.taskId && $("chat-meta").classList.contains("draft-meta")) renderDraftChatMeta(); } // loadTaskList:默认 reset(filters/refresh/写操作后),append=true 由 sentinel observer 触发 @@ -437,6 +438,24 @@ function syncDraftModelSelect() { sel.onchange = () => { state.draftModelProfile = sel.value || ""; }; } +function renderDraftChatMeta() { + const meta = $("chat-meta"); + meta.classList.add("draft-meta"); + meta.innerHTML = ` + 新对话 + + + 模型 + + + ${renderMediaModelTrigger()} + `; + syncDraftModelSelect(); + bindMediaModelTrigger(); +} + let _draftFolderActiveIndex = -1; let _draftFolderResizeObserver = null; @@ -669,16 +688,7 @@ export async function showNewConversationDraft() { renderConcurrentWarning(); renderTaskProgressDock([]); renderOutlineRail(); - $("chat-meta").classList.add("draft-meta"); - $("chat-meta").innerHTML = ` - 新对话 - - - 模型 - - `; + renderDraftChatMeta(); $("chat-stream").innerHTML = `

开始一段新对话

@@ -711,7 +721,6 @@ export async function showNewConversationDraft() { $("btn-task-menu").disabled = true; syncOptimizeBtn(); bindDraftControls(); - syncDraftModelSelect(); syncDraftAction(); loadFiles(); if (mqPhone.matches) setMobileView("mv-mid"); @@ -861,8 +870,7 @@ function renderChatMeta() { const sel = $("chat-model-sel"); if (sel) sel.onchange = onChangeModel; // 生图/生视频 收进 ⚙ 弹层(低频),点开时再渲染 select 并接 onChange - const mmBtn = $("media-model-btn"); - if (mmBtn) mmBtn.onclick = (e) => { e.stopPropagation(); openMediaModelPop(mmBtn); }; + bindMediaModelTrigger(); const active = t.status === "active"; $("chat-form").style.display = active ? "flex" : "none"; syncOptimizeBtn(); @@ -898,6 +906,11 @@ function renderMediaModelTrigger() { return ``; } +function bindMediaModelTrigger() { + const btn = $("media-model-btn"); + if (btn) btn.onclick = (e) => { e.stopPropagation(); openMediaModelPop(btn); }; +} + // 弹层里一行 = 标签(icon + 文字) + select。沿用原 onChange,选中值仍只进 state.* 随下条消息发。 function mediaSelectRow(icon, label, id, list, cur, title) { const opts = list.map(m => diff --git a/web/static/ui.css b/web/static/ui.css index 115609b..ebbb8aa 100644 --- a/web/static/ui.css +++ b/web/static/ui.css @@ -762,11 +762,6 @@ header .who { height: 0; } - #chat-meta.draft-meta > .spacer { - flex: 1 1 auto; - height: auto; - } - #chat-meta .mdl-wrap { flex: 1 1 auto; min-width: 0; @@ -776,6 +771,11 @@ header .who { flex: 0 1 auto; } + #chat-meta.draft-meta select { + width: auto !important; + max-width: 48vw; + } + #chat-meta select { width: 100% !important; max-width: none;