Add context menus to task and file lists

This commit is contained in:
caoqianming 2026-07-29 16:35:20 +08:00
parent bcdbdd4738
commit 61648f1114
6 changed files with 49 additions and 5 deletions

View File

@ -5,6 +5,10 @@
> 所以不是每个版本号都有条目。条目格式 `## <版本> — <日期>`,新条目加在最上面。
> 工程口径的完整记录见 `PROGRESS.md` / git log。
## 0.60.12 — 2026-07-29
- 任务列表以及文件、文件夹列表现在支持右键操作,可在鼠标位置直接打开与「⋯」按钮一致的菜单;菜单靠近屏幕边缘时会自动调整位置。
## 0.60.11 — 2026-07-29
- 网页工作台进一步回归清爽白色界面:助手回复改为开放式文档流,工具调用与结果合并成可展开的紧凑活动项;同时修复语音输入停止后按钮仍显示“停止”的问题。

View File

@ -2,7 +2,7 @@
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`
最后更新:2026-07-29(Web 对话流去卡片化,bump 0.60.11)
最后更新:2026-07-29(任务与文件列表右键菜单,bump 0.60.12)
---
@ -23,6 +23,7 @@
### 2026-07-29
- **07-29 / 0.60.12 / 任务与文件列表右键菜单**:任务行、文件行和目录行增加原生 `contextmenu` 入口,直接复用既有 `taskMenuItems` / `fileMenuItems`,因此与「⋯」按钮保持相同权限、状态禁用、确认弹框和后端调用;单例浮层菜单增加按鼠标坐标定位及视口双向钳制,右键任务不切换当前会话、右键文件不触发打开。纯前端加法,无 API、schema、migration 或依赖变化3 个 JS 文件语法及 diff 格式检查通过,浏览器实例不可用故真实鼠标回放留页面复核。
- **07-29 / 0.60.11 / Web 对话流去卡片化 + 工具活动合并 + 语音状态修复**:工作台背景统一以纯白为主,浅灰仅保留在 hover、代码块等局部状态助手历史/实时回复移除重复角色标签、大卡片边框、左色条与运行态红框,改为左锚开放文档流。历史态按 `tool_call_id` 将 assistant 调用与 tool result 合并,实时态复用同一 details 原位更新结果,统一为默认折叠单行活动项(执行中/成功/失败状态明确),产物 chip、媒体 banner 与后台进程活化继续保留;顶部 task progress dock 的位置和逻辑未改。另修短按语音转连续录音时直接覆写按钮 `textContent`、销毁 SVG/label 导致停止后无法恢复“语音”的问题,收口为 class/label/ARIA 状态函数。相关 Python 24 项 + Node 5 项及 JS 语法、diff 格式检查通过;无 API、schema、migration 或依赖变化。
- **07-29 / 0.60.10 / Web 工作台移动端空间优化**:根据第二轮手机实图反馈把暖白/浅米灰改回中性白色桌面助手回答、工具卡与任务进度恢复左侧阅读轴手机端压缩顶栏、Tab、任务操作区、模型区、消息间距和 composer高频操作键由 44px 收至 40px、输入框最低高度由 64px 收至 52px并隐藏空闲态“请选择文件保存位置/就绪”占位行,运行、上传与错误等有效状态提示继续按需显示。相关 Python 21 项 + Node 5 项及 JS 语法、diff 格式检查通过;无 API、schema、migration 或依赖变化。
- **07-29 / 0.60.9 / Web 工作台视觉复核修正(桌面 + 手机实图)**:据上线截图把冷蓝灰改为暖白/浅米灰;任务与文件行恢复明确分隔,助手消息收进居中 52rem 文档型回答块,工具与进度对齐同一阅读轨道;同时修掉 `task_progress`-only assistant 被过滤工具后仍留下空“助手”壳。手机端 composer 改为独立状态行 + 3 个 44px SVG 图标键 + 发送键,缩短 placeholder、加入安全区并压缩操作/模型区;录音与润色状态改只更新 label/ARIA不再覆盖 SVG DOM。相关 Python 21 项 + Node 5 项及 JS 语法、HTML/SVG 结构、CSS 括号检查通过;无 API、schema、migration 或依赖变化。

View File

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

View File

@ -5,7 +5,7 @@
// 各入口模块顶层自绑;对外导出 loadTaskList / loadModels / selectTask
// (供 main enterApp、embed、files、newtask)。
import { state, LS_TASK_FILTERS_COLLAPSED } from "./state.js";
import { $, showMenu } from "./dom.js";
import { $, showMenu, showMenuAtPoint } from "./dom.js";
import { api } from "./api.js";
import { escapeHtml, fmtTime, fmtTokens, fmtTimeAgo, taskUsageTooltip, formatTaskUsage, formatContextStats, formatUsageStats, ctxRingHtml } from "./format.js";
import { renderMd, highlightIn } from "./markdown.js";
@ -238,6 +238,12 @@ function renderTaskList(tasks, append = false) {
if (e.target.closest(".dd-toggle")) return; // 菜单按钮点击不触发选中
selectTask(el.dataset.tid);
};
el.oncontextmenu = (e) => {
e.preventDefault();
const t = state.tasksById[el.dataset.tid];
if (!t) return;
showMenuAtPoint(e.clientX, e.clientY, taskMenuItems(t));
};
const btn = el.querySelector(".task-menu");
if (btn) {
btn.onclick = (e) => {

View File

@ -6,7 +6,7 @@ export const $ = (id) => document.getElementById(id);
// ───── floating dropdown menu (single instance) ─────
// 用 position: fixed 单例避免被 pane overflow 裁剪;按位置算出右上角对齐
let _menuItems = null;
export function showMenu(triggerEl, items) {
function renderMenu(items) {
_menuItems = items;
const menu = $("floating-menu");
menu.innerHTML = items.map((it) => {
@ -23,6 +23,20 @@ export function showMenu(triggerEl, items) {
if (item && item.onclick) item.onclick();
};
});
return menu;
}
function clampMenuPosition(menu, left, top) {
const mh = menu.offsetHeight || 120;
const mw = menu.offsetWidth || 132;
menu.style.left = Math.max(4, Math.min(left, window.innerWidth - mw - 4)) + "px";
menu.style.right = "auto";
menu.style.top = Math.max(4, Math.min(top, window.innerHeight - mh - 4)) + "px";
menu.style.visibility = "";
}
export function showMenu(triggerEl, items) {
const menu = renderMenu(items);
// 默认右下展开;若空间不足则改向上
const rect = triggerEl.getBoundingClientRect();
menu.style.visibility = "hidden";
@ -45,6 +59,15 @@ export function showMenu(triggerEl, items) {
}
menu.style.visibility = "";
}
// 右键菜单按指针位置展开;靠近视口边缘时向内收,避免操作项被裁掉。
export function showMenuAtPoint(clientX, clientY, items) {
const menu = renderMenu(items);
menu.style.visibility = "hidden";
menu.classList.add("show");
clampMenuPosition(menu, clientX, clientY);
}
export function hideMenu() {
_menuItems = null;
$("floating-menu").classList.remove("show");
@ -54,6 +77,8 @@ document.addEventListener("click", (e) => {
if (e.target.closest("#floating-menu")) return;
hideMenu();
}, true);
// 在非自定义菜单区域右键时,先收掉可能残留的浮层;列表行的冒泡处理随后会重新打开。
document.addEventListener("contextmenu", hideMenu, true);
window.addEventListener("resize", hideMenu);
// 滚动 pane 时菜单位置失效,直接关
document.addEventListener("scroll", hideMenu, true);

View File

@ -5,7 +5,7 @@
// 反向依赖:openFilePreview(preview)、logout(auth)、以及 main 的 glue
// downloadFile / selectTask / loadTaskList / loadFolderSuggestions(后续随 media/tasks/newtask 模块化再迁)。
import { state } from "./state.js";
import { $, showMenu } from "./dom.js";
import { $, showMenu, showMenuAtPoint } from "./dom.js";
import { api } from "./api.js";
import { escapeHtml, humanSize } from "./format.js";
import { openFilePreview } from "./preview.js";
@ -267,6 +267,14 @@ function renderFiles(data) {
showMenu(btn, fileMenuItems(e));
};
});
$("file-list").querySelectorAll(".file-row").forEach((row) => {
row.oncontextmenu = (ev) => {
ev.preventDefault();
const e = state.entriesByRel[row.dataset.rel];
if (!e) return;
showMenuAtPoint(ev.clientX, ev.clientY, fileMenuItems(e));
};
});
}
function fileMenuItems(e) {