From 6efda0a157489d84b675a97fe5698e646948bc31 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sat, 18 Jul 2026 22:42:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(task):=20=E4=BB=BB=E5=8A=A1=E6=94=B9?= =?UTF-8?q?=E5=90=8D=20UI=E2=80=94=E2=80=94=E2=8B=AF=20=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E5=8A=A0=E3=80=8C=E9=87=8D=E5=91=BD=E5=90=8D=E3=80=8D,?= =?UTF-8?q?=E6=94=B9=E5=AE=8C=E5=88=97=E8=A1=A8+=E9=A1=B6=E6=A0=8F?= =?UTF-8?q?=E5=85=A8=E5=90=8C=E6=AD=A5(bump=200.58.41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端 PATCH /v1/tasks/{id} 早支持 name(validate_task_name 校验)但前端无入口。 taskMenuItems 加「重命名」项(列表行 ⋯ 与顶栏 ⋯ 共用此菜单,一处插入两处生效), renameTask() 走 prompt→PATCH→更 state.tasksById/taskMeta.name + renderChatMeta() + loadTaskList() 全同步。刻意只改 DB 显示名,不动 working_dir/磁盘宪法文件 (定位锚是 task_id/short_id,name 设计为自由可变标签)。仅改 web/static/js/chat.js。 Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 4 ++++ PROGRESS.md | 1 + core/__init__.py | 2 +- web/static/js/chat.js | 23 +++++++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ade0c53..eea61d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ > 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。 > 工程口径的完整记录见 `PROGRESS.md` / git log。 +## 0.58.41 — 2026-07-18 + +- 可以给任务改名了:在任务列表行或聊天区顶部的「⋯」菜单里选「重命名」即可修改任务显示名,改完列表和标题栏立即同步。改名只动显示名,不影响工作目录和已生成的文件。 + ## 0.58.39 — 2026-07-18 - 登录更省心:只要在持续使用,登录状态会在临近过期时自动续期,不用再每隔几天重新登录一次。长时间没打开才需要重新登录。 diff --git a/PROGRESS.md b/PROGRESS.md index cce44d8..662f23d 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -23,6 +23,7 @@ ### 2026-07 +- **07-18 / 0.58.41**:任务改名 UI——后端 `PATCH /v1/tasks/{id}` 早支持 `name`(validate_task_name 校验)但前端无入口;`taskMenuItems` 加「重命名」项(列表行 ⋯ 与顶栏 ⋯ 共用此菜单,一处插入两处生效),`renameTask()` 走 prompt→PATCH→更 `state.tasksById`/`taskMeta.name` + `renderChatMeta()` + `loadTaskList()` 全同步。刻意只改 DB 显示名,不动 working_dir/磁盘宪法文件(定位锚是 task_id/short_id,name 设计为自由可变标签)。仅改 `web/static/js/chat.js`。 - **07-18 / 0.58.39**:JWT 滑动续签——活跃用户临期(剩余寿命 < TTL 1/4,env `ZCBOT_JWT_REFRESH_THRESHOLD_SECONDS` 可调、0 关)由 `_authenticate` 中间件就地重签发新 token 走响应头 `X-Refreshed-Token`/`X-Token-Expires-At`,前端 `api()` 无感吸收换 `state.token`,不再因 7d TTL 掉线。additive 惠及 platform 层 + SPA(不认退回原重登行为);CORS 加 `expose_headers`。 - **07-17 / 0.58.38**:修定时简报产物文件链接点开 404——推送端 `_build_push_message` 把产物写成完整 `/v1/files/download?path=` URL,撞前端相对路径拦截器致二次包装 404。A 拦截器识别 `^/?v1/files/` 抽真 rel 救 DB 历史存量 + B 推送端源头收敛为 user_root 相对 rel(企微 send_file 独立通道不碰此链接,不受影响)。 - **07-17 / 0.58.37**:修 task d9c74a5a 三处独立缺陷——① chip 提取 `_TAIL_CLS` 补排除 `*`(粗体路径尾 `**` 被吞成 404 chip)② docx 加 `EMOJI_RE` + `add_fonted_runs`,emoji 段走 Segoe UI Emoji 补缺字(不破坏化学式下标)③ `collect_md_files` 让 docx 三 profile 认单文件,兑现 render.py 单文件承诺、不再被迫建 sections/。 diff --git a/core/__init__.py b/core/__init__.py index 30edc3f..8205a6b 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1,3 +1,3 @@ # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # 改版本只动这一行。 -__version__ = "0.58.40" +__version__ = "0.58.41" diff --git a/web/static/js/chat.js b/web/static/js/chat.js index ba80669..dc766e2 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -345,6 +345,8 @@ function taskMenuItems(t) { onclick: () => setTaskStatus(t.task_id, "completed", t.name || "(未命名)") }, { act: "abandon", label: "废弃", cls: "act-abandon", disabled: !isActive, onclick: () => setTaskStatus(t.task_id, "abandoned", t.name || "(未命名)") }, + { act: "rename", label: "重命名", cls: "act-rename", + onclick: () => renameTask(t.task_id, t.name || "") }, { act: "export", label: "导出对话", cls: "act-export", disabled: !hasMsg, onclick: () => exportTask(t.task_id) }, { act: "clear", label: "清空对话", cls: "act-clear", disabled: !hasMsg || running, @@ -2532,6 +2534,27 @@ async function clearMessages(tid, name, nMsg) { } } +async function renameTask(tid, curName) { + // 只改 tasks.name(显示名),不动 working_dir/磁盘产物 —— 定位主锚是 task_id/short_id。 + // 后端 PATCH 带 validate_task_name 校验;成功后前端各处(列表/标题栏)重渲染即同步。 + const next = prompt("将任务重命名为:", curName || ""); + if (next == null) return; + const trimmed = next.trim(); + if (!trimmed || trimmed === curName) return; + try { + const updated = await api("PATCH", "/v1/tasks/" + tid, { name: trimmed }); + if (state.tasksById[tid]) state.tasksById[tid].name = updated.name; + if (state.taskId === tid && state.taskMeta) { + state.taskMeta = { ...state.taskMeta, name: updated.name }; + renderChatMeta(); // 顶部标题栏立即刷新 + } + loadTaskList(); // 左栏列表重拉,行内名字同步 + } catch (e) { + if (e.status === 401) { logout(); return; } + alert("重命名失败:" + e.message); + } +} + async function setTaskStatus(tid, status, name) { const labels = { completed: "已完成", abandoned: "已废弃" }; if (!confirm(`确认将「${name || tid.slice(0,8)}」置为「${labels[status] || status}」?`)) return;