feat(task): 任务改名 UI——⋯ 菜单加「重命名」,改完列表+顶栏全同步(bump 0.58.41)

后端 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) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-18 22:42:17 +08:00
parent e53211f9c8
commit 6efda0a157
4 changed files with 29 additions and 1 deletions

View File

@ -5,6 +5,10 @@
> 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。 > 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。
> 工程口径的完整记录见 `PROGRESS.md` / git log。 > 工程口径的完整记录见 `PROGRESS.md` / git log。
## 0.58.41 — 2026-07-18
- 可以给任务改名了:在任务列表行或聊天区顶部的「⋯」菜单里选「重命名」即可修改任务显示名,改完列表和标题栏立即同步。改名只动显示名,不影响工作目录和已生成的文件。
## 0.58.39 — 2026-07-18 ## 0.58.39 — 2026-07-18
- 登录更省心:只要在持续使用,登录状态会在临近过期时自动续期,不用再每隔几天重新登录一次。长时间没打开才需要重新登录。 - 登录更省心:只要在持续使用,登录状态会在临近过期时自动续期,不用再每隔几天重新登录一次。长时间没打开才需要重新登录。

View File

@ -23,6 +23,7 @@
### 2026-07 ### 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-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.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/。 - **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/。

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.58.40" __version__ = "0.58.41"

View File

@ -345,6 +345,8 @@ function taskMenuItems(t) {
onclick: () => setTaskStatus(t.task_id, "completed", t.name || "(未命名)") }, onclick: () => setTaskStatus(t.task_id, "completed", t.name || "(未命名)") },
{ act: "abandon", label: "废弃", cls: "act-abandon", disabled: !isActive, { act: "abandon", label: "废弃", cls: "act-abandon", disabled: !isActive,
onclick: () => setTaskStatus(t.task_id, "abandoned", t.name || "(未命名)") }, 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, { act: "export", label: "导出对话", cls: "act-export", disabled: !hasMsg,
onclick: () => exportTask(t.task_id) }, onclick: () => exportTask(t.task_id) },
{ act: "clear", label: "清空对话", cls: "act-clear", disabled: !hasMsg || running, { 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) { async function setTaskStatus(tid, status, name) {
const labels = { completed: "已完成", abandoned: "已废弃" }; const labels = { completed: "已完成", abandoned: "已废弃" };
if (!confirm(`确认将「${name || tid.slice(0,8)}」置为「${labels[status] || status}」?`)) return; if (!confirm(`确认将「${name || tid.slice(0,8)}」置为「${labels[status] || status}」?`)) return;