65 KiB
Quick Panel 功能完善实施计划
一、背景与现状分析
1.1 PRD 核心目标
Quick Panel 是一个类 Raycast 的浏览器命令面板,提供:
- 统一入口:标签页/书签/历史/命令的聚合搜索
- 键盘优先:全程键盘操作,快捷键丰富
- 本地优先:数据本地处理,隐私安全
- 差异化能力:利用 CDP/Native Host/MCP 实现独特功能
1.2 当前实现状态(P0 + 部分 P1,更新于 2026-01-03)
| 模块 | 状态 | 说明 |
|---|---|---|
| Shadow DOM 宿主 | ✅ 完成 | ui/shadow-host.ts 样式隔离、事件阻断、主题同步 |
| AI Chat 面板 | ✅ 完成 | ui/ai-chat-panel.ts 流式对话、SSE、取消 |
| AI Chat View | ✅ 完成 | ui/ai-chat-view.ts 可嵌入的 View 组件(Phase 1) |
| 双视图 Shell | ✅ 完成 | ui/panel-shell.ts 已集成到 Controller |
| SearchEngine | ✅ 完成 | core/search-engine.ts 已接入 UI |
| Tabs Provider | ✅ 完成 | providers/tabs-provider.ts 已接入 SearchView |
| Search View | ✅ 完成 | ui/search-view.ts 搜索视图容器(Phase 2) |
| 键盘导航 | ✅ 完成 | core/keyboard-controller.ts IME 支持(Phase 3) |
| 结果列表 UI | ✅ 完成 | 集成在 search-view.ts,支持 favicon 优先显示 |
| 动作面板 UI | ✅ 完成 | ui/action-panel.ts 支持 Tab 动作(Phase 4) |
| Bookmarks Provider | ✅ 完成 | providers/bookmarks-provider.ts 书签搜索(Phase 5) |
| History Provider | ✅ 完成 | providers/history-provider.ts 历史搜索(Phase 5) |
| Content Provider | ✅ 完成 | providers/content-provider.ts 内容搜索(Phase 7) |
| Commands Provider | ✅ 完成 | providers/commands-provider.ts 页面/标签命令(Phase 5) |
| 使用历史追踪 | ✅ 完成 | 最近使用记录与排序(Phase 6) |
| Workspaces Provider | ✅ 完成 | providers/workspaces-provider.ts 会话快照(Phase 11) |
| Diagnostics Suite | ✅ 完成 | Debug Bundle + API Detective(Phase 13) |
| Clipboard History | ✅ 完成 | providers/clipboard-provider.ts + background/quick-panel/clipboard-handler.ts(Phase 15.1) |
| Quick Notes | ✅ 完成 | providers/notes-provider.ts + background/quick-panel/notes-handler.ts(Phase 15.2) |
| Focus Mode | ✅ 完成 | providers/focus-provider.ts + background/quick-panel/focus-handler.ts(Phase 15.3) |
| Web Monitor | ✅ 完成 | providers/monitor-provider.ts + background/quick-panel/monitor-handler.ts(Phase 15.4) |
| Tool Audit Log | ✅ 完成 | providers/audit-provider.ts + background/quick-panel/audit-handler.ts(Phase 14) |
注:PRD 中的 “AI 编排与可控工具层(Agent Mode)/ 个人效率与提醒” 已完成第一版(Phase 14/15);后续仍可扩展(见下文)。
1.3 关键架构冲突(✅ 已解决)
当前存在 双 overlay 冲突:
panel-shell.ts:139创建.qp-overlay/.qp-panelai-chat-panel.ts:272也创建.qp-overlay/.qp-panelController 直接使用 AI Chat(绕过 Shell)
解决方案(Phase 1 已实现):
ai-chat-view.ts- 不创建 overlay,只渲染内容到 mount pointsai-chat-panel.ts- 改为 Shell + View 的 wrapper,保持 API 兼容index.ts- Controller 使用 Shell 作为唯一容器
二、实施策略:增量迁移
采用 增量迁移 策略,保持现有 AI Chat 功能可用的同时逐步引入搜索面板能力。
核心原则
- Shell 作为唯一容器
- AI Chat 改造为可嵌入的 View
- 搜索功能逐步接入
- 保持向后兼容
三、阶段划分
Phase 1: 架构重构 - Shell 统一容器
目标:让 Shell 成为唯一容器,AI Chat 作为其 chat view
1.1 重构 AI Chat Panel 为 View 组件
- 新建
ui/ai-chat-view.ts- 不创建 overlay/panel,只渲染内容- Header 内容 →
headerChatMount - Messages/Empty →
contentChatMount - Composer →
footerChatMount
- Header 内容 →
- 修改
ai-chat-panel.ts- 改为内部调用 shell + view 的 wrapper(保持 API 兼容)
1.2 重构 Controller
- 修改
index.ts- 使用
mountQuickPanelShell作为唯一容器 - 默认显示 search view(而非直接显示 chat)
- 支持 view 切换
- 使用
关键文件
app/chrome-extension/shared/quick-panel/
├── index.ts # 修改
├── ui/
│ ├── panel-shell.ts # 保持
│ ├── ai-chat-panel.ts # 修改(wrapper)
│ └── ai-chat-view.ts # 新建
Phase 2: 搜索主链路
目标:打通 SearchEngine + Provider → UI 的完整链路
2.1 实现搜索视图 UI
- 新建
ui/search-view.ts- 搜索视图容器- 挂载 SearchInput 到
headerSearchMount - 挂载 QuickEntries 到
contentSearchMount - 挂载 ResultList 到
contentSearchMount - 挂载 Footer 提示 到
footerSearchMount
- 挂载 SearchInput 到
2.2 实现结果列表
- 新建
ui/result-list.ts- 虚拟滚动(处理大量结果)
- 选中态管理
- 空状态/加载状态
- 新建
ui/result-item.ts- 图标/标题/副标题
- 选中态样式
- Favicon 显示
2.3 接入 SearchEngine
- 修改
ui/search-view.ts- 创建 SearchEngine 实例
- 注册 TabsProvider
- 监听 SearchInput 输入,调用 engine.schedule()
- 渲染搜索结果
关键文件
app/chrome-extension/shared/quick-panel/ui/
├── search-view.ts # 新建
├── result-list.ts # 新建
├── result-item.ts # 新建
└── search-input.ts # 已存在,确认接入
Phase 3: 键盘导航系统
目标:实现 PRD 要求的完整键盘交互
3.1 KeyboardController 核心
- 新建
core/keyboard-controller.ts- 状态机:
search|chat|action-panel - 事件监听:ShadowRoot capture 级别
- 快捷键映射表
- 状态机:
3.2 搜索视图键盘交互
| 快捷键 | 动作 |
|---|---|
↑/↓ |
列表导航 |
Enter |
执行默认动作 |
Cmd+Enter |
新标签打开 |
Tab/→ |
打开动作面板 |
Backspace/← |
返回(输入框为空时) |
Esc |
关闭面板 |
Cmd+T/B/H |
切换 Scope |
3.3 动作面板键盘交互
| 快捷键 | 动作 |
|---|---|
↑/↓ |
动作列表导航 |
Enter |
执行选中动作 |
Esc/← |
返回搜索视图 |
关键文件
app/chrome-extension/shared/quick-panel/core/
└── keyboard-controller.ts # 新建
Phase 4: 动作面板
目标:实现二级动作选择界面
4.1 动作面板 UI
- 新建
ui/action-panel.ts- 动作列表渲染
- 键盘导航
- 执行后关闭
- 危险动作样式(tone: danger)
4.2 扩展 Tabs 动作
前端(tabs-provider.ts):
- 复制 URL
- 复制 Markdown
- 固定/取消固定
- 静音/取消静音
后端(tabs-handler.ts)新增消息类型:
QUICK_PANEL_TAB_SET_PINNEDQUICK_PANEL_TAB_SET_MUTED
关键文件
app/chrome-extension/shared/quick-panel/
├── ui/action-panel.ts # 新建
├── providers/tabs-provider.ts # 修改
app/chrome-extension/entrypoints/background/quick-panel/
└── tabs-handler.ts # 修改
app/chrome-extension/common/
└── message-types.ts # 修改
Phase 5: 更多 Providers
目标:实现 PRD P0 的完整 Provider 体系
5.1 Bookmarks Provider
- 新建
providers/bookmarks-provider.ts - 新建
background/quick-panel/bookmarks-handler.ts - 复用现有
tools/browser/bookmark.ts的 chrome.bookmarks API
5.2 History Provider
- 新建
providers/history-provider.ts - 新建
background/quick-panel/history-handler.ts - 复用现有
tools/browser/history.ts的 chrome.history API
5.3 Commands Provider
- 新建
providers/commands-provider.ts - 新建
core/command-registry.ts - 新建
commands/目录page-commands.ts- 复制链接/Markdown/截图tab-commands.ts- 新建标签/窗口
Phase 6: 使用历史追踪
目标:实现最近使用记录与排序
6.1 HistoryTracker 模块
- 新建
core/history-tracker.tsrecordUsage(key, ts)- 记录使用getSignals(keys)- 获取使用信号getRecentList()- 获取最近使用列表- 存储:
chrome.storage.local
6.2 排序集成
- 修改 Controller/SearchView
- 获取搜索结果后,注入 usage signal 做二次排序
- 面板打开时优先显示最近使用
Phase 7: 内容搜索 Provider
目标:实现 PRD 的“内容搜索”能力(scope: c ),可在打开的标签页中按正文内容检索。
7.1 后台内容缓存
- 新建
background/quick-panel/content-handler.ts - 复用
inject-scripts/web-fetcher-helper.js(Readability)抽取可读正文 - 内容截断到 50KB(best-effort),并限制缓存最多 200 个标签页
- 触发时机(best-effort):
tabs.onUpdated(status === 'complete')webNavigation.onHistoryStateUpdated(SPA 路由变化)tabs.onRemoved(清理缓存)
7.2 Content Provider
- 新建
providers/content-provider.ts - 默认动作:切换到匹配标签页;
Cmd/Ctrl+Enter以新标签打开 URL - 二级动作:新标签打开、复制 URL、关闭标签页
Phase 8: 标签页高级管理(Commands)
目标:实现 PRD P1 的高频标签页管理能力,并复用现有 > Commands 体系(不新增 UI 复杂度)。
8.1 新增命令项(Commands Provider)
- 修改
providers/commands-provider.ts- Close other tabs(关闭当前窗口其它未固定标签)
- Close tabs to the right(关闭右侧未固定标签)
- Discard inactive tabs(丢弃当前窗口未固定的非激活标签,释放内存)
- Merge all windows(将其它窗口的标签移动到当前窗口)
8.2 扩展后台命令执行能力
- 修改
background/quick-panel/page-commands-handler.ts- 新增 page command:
close_other_tabs/close_tabs_to_right/discard_inactive_tabs/merge_all_windows
- 新增 page command:
- 修改
common/message-types.ts- 扩展
QuickPanelPageCommandunion
- 扩展
Phase 9: 多引擎搜索 & 快捷链接(来自 .docs/tansuo.md)
目标:在 Quick Panel 内用前缀快速打开常用搜索引擎与 URL 模板(例如 g react hooks、gh openai、npm vitest),减少上下文切换。
9.1 Scope / Prefix 扩展(输入协议)
- 修改
core/types.ts- 扩展
QuickPanelScope/QUICK_PANEL_SCOPES(新增若干 “Web Search” scope) - 扩展
parseScopePrefixedQuery:识别g/gh/npm/so/mdn等前缀(按 PRD 精简)
- 扩展
- 约束:新增 scope 仅前缀触发,不强制增加新的键盘切换快捷键,避免 UI 复杂度膨胀
9.2 Web Search Provider
- 新建
providers/web-search-provider.ts- scope 驱动:不同 scope → 不同 engine URL 模板(Google/GitHub/NPM/StackOverflow/MDN…)
- 动作:Enter 在当前标签打开;
Cmd/Ctrl+Enter新标签打开(复用 openMode →QUICK_PANEL_OPEN_URL)
- 新建
core/url-template.ts(纯函数)- 统一处理 URL 模板填充与编码规则(
{query}、可选的{rawQuery}等) - 提供可测试的最小接口:
buildSearchUrl(engine, query)
- 统一处理 URL 模板填充与编码规则(
9.3 配置(可选)
- 可选:支持用户自定义搜索引擎模板(
chrome.storage.sync),并提供默认模板集作为 fallback- 说明:建议优先做 “内置 5-8 个引擎 + 可编辑” 的闭环,再考虑更泛化的模板 DSL
Phase 10: 页面工具(Zen / Dark / Reader / Outline / Clean URL / PIP / Allow Copy)
目标:补齐 PRD P1 “页面工具”能力,优先以 > Commands 落地(低 UI 成本),并复用既有 background bridge。
10.1 无参数 Commands(toggle / one-shot 优先)
- 修改
providers/commands-provider.ts- Page Skins:VS Code / Terminal / Retro / Paper / Off(始终显示 “Skin mode” 水印,避免产生“伪装”歧义)
- Clean URL:去除常见追踪参数(utm/fbclid/gclid 等),支持复制与新标签打开
- Zen Mode:注入 CSS 隐藏干扰元素(best-effort),支持关闭还原
- Force Dark:CSS filter 强制暗色(best-effort),支持关闭还原
- Allow Copy:解除复制/选中限制(best-effort,不承诺对所有站点有效)
- Picture-in-Picture:对页面内可用
<video>触发 PIP - Privacy Curtain:一键遮罩/模糊当前页面(屏幕共享/公共场景的隐私保护)
- 修改
background/quick-panel/page-commands-handler.ts- 统一走
QUICK_PANEL_PAGE_COMMAND,按 command 分支注入脚本或调用现有 tool
- 统一走
- 修改
common/message-types.ts- 扩展
QuickPanelPageCommandunion + 消息 typing
- 扩展
10.2 Reader / Outline(需要“列表型结果”的承载)
- Reader Mode(建议优先)
- 复用
inject-scripts/web-fetcher-helper.js(Readability)抽取正文 - 输出策略二选一(需要产品决策):
- 方案 A:新增
ui/reader-view.ts(作为第三视图,复用 Shell) - 方案 B:生成纯 HTML/Markdown 并在新标签打开(更轻量、可先闭环)
- 方案 A:新增
- 复用
- Page Outline(目录)
- 注入脚本抽取 H1-H6(含 text + id + offset),并提供跳转能力(
scrollIntoView) - 承载方式建议同 Reader:要么独立 view,要么先做 “复制目录为 Markdown”
- 注入脚本抽取 H1-H6(含 text + id + offset),并提供跳转能力(
Phase 11: 会话 / 工作区(Session Time Machine)
目标:把 “保存/恢复一组标签页” 做成一等能力,支持项目上下文切换,并为后续 AI 编排提供可复用的基础工具。
11.1 后台存储与消息协议
- 新建
background/quick-panel/workspaces-handler.ts- 负责:保存快照、列出快照、恢复快照、删除快照
- 存储:
chrome.storage.local(先闭环;后续可选迁移到storage.sync)
- 修改
common/message-types.ts- 新增:
QUICK_PANEL_WORKSPACES_LIST/QUICK_PANEL_WORKSPACES_SAVE/QUICK_PANEL_WORKSPACES_OPEN/QUICK_PANEL_WORKSPACES_DELETE
- 新增:
11.2 Provider & UX
- 新建
providers/workspaces-provider.ts- 建议 scope 前缀:
ws(仅前缀触发) - 空查询:展示最近快照 + “Save current session”
- 有查询:过滤匹配,并提供虚拟条目 “Save session as ” 以承载命名输入
- 打开动作:当前窗口恢复 / 新窗口恢复(至少二选一)
- 建议 scope 前缀:
- 风险控制
- 恢复快照可能导致批量打开标签:需要二次确认策略(UI/交互待确认)
- Incognito 与 normal 之间禁止互相恢复(避免隐私边界破坏)
Phase 12: 工具箱(文本转换 + 开发者常用)
目标:把 .docs/tansuo.md 中高频 “瑞士军刀” 能力落到 Quick Panel,优先以可测试的纯函数实现,并在 UI 上以 “结果 + 一键复制” 闭环。
12.1 文本/数据转换(Argument Commands)
- 典型命令(示例):
> json {...}/> base64 encode .../> url decode .../> ts 1700000000/> uuid/> jwt <token> - 实现建议:
- 扩展
providers/commands-provider.ts:引入 “argument command” 概念(基于 query tokens 动态生成结果) - 新建
core/toolbox/:放置可复用纯函数(json/url/base64/ts/jwt/uuid/regex) - 结果呈现:subtitle 展示截断预览,动作面板提供 Copy/Copy raw 等
- 扩展
12.2 开发者工具(复用现有 browser tools)
- 复用
app/chrome-extension/entrypoints/background/tools/browser/*:- Cookie 查看/导出(
chrome.cookies) - LocalStorage/SessionStorage 读取(注入脚本)
- Network capture(webRequest/debugger 两种实现)
- Performance trace、Console capture、Read Page 等
- Cookie 查看/导出(
- Quick Panel 侧落地形式:
- 先做 “
>Commands → 执行并导出/复制结果” 的闭环 - 复杂交互(列表/筛选)再升级为独立 view(避免早期 UI 失控)
- 先做 “
Phase 13: 差异化诊断套件(Debug Bundle / API Detective)
目标:把 PRD P2 的差异化能力做成可售卖的核心卖点:一键采集 Bug 报告包、抓包反推 API。
13.1 Debug Bundle(一键 Bug 报告)
- 编排现有 Tools(截图/Console/Network/Performance),输出到 Downloads(zip 或目录)
- 关键要求:
- 可取消、可重试、失败可定位(每一步单独 error)
- 高风险能力(debugger/network body)需要显式提示/确认
13.2 API Detective(抓包反推 API)
- Quick Panel 命令开始抓包 → 用户执行操作 → 命令停止抓包 → 列表展示关键请求
- 输出能力:复制为 curl / fetch、请求重放(复用
network-request)
Phase 14: AI 编排与可控工具层(Agent Mode,探索)
目标:将上述能力封装为可被 AI 调用的 “Tools”,提供权限分级、确认门槛与可回放 action log,支撑商业级可控性。
- 方向(来自
.docs/tansuo.md):- tool schema(输入/输出契约)+ 统一执行通道
- 风险分级(读/写/破坏性/外部网络/本地文件)+ 二次确认
- Plan mode:先展示计划,再执行
- Action log:可审计、可回放、可撤销(best-effort)
Phase 15: 个人效率与提醒(Clipboard / Notes / Pomodoro / Focus / Monitor)
目标:落地 .docs/tansuo.md 中的高频个人效率能力:剪贴板历史、快速笔记、番茄钟/专注模式、网页监控与提醒(分阶段)。
15.1 Clipboard History(先做 Quick Panel 内来源)
- 新建
providers/clipboard-provider.ts(建议 scope 前缀:clip) - 新建
background/quick-panel/clipboard-handler.ts- 仅记录来自 Quick Panel 的复制行为(Copy URL/Copy Markdown/工具箱 Copy 结果等)
- 可选增加显式命令:
> clip save(在用户手势内读取当前剪贴板,避免无授权读取)
15.2 Quick Notes(本地优先)
- 新建
providers/notes-provider.ts(建议 scope 前缀:note) - 新建
background/quick-panel/notes-handler.ts- 通过 query 创建/搜索笔记,默认本地存储(
chrome.storage.local)
- 通过 query 创建/搜索笔记,默认本地存储(
15.3 Pomodoro / Focus Mode(专注与防打断)
- 新建
providers/focus-provider.ts(或以>Commands 先闭环) - 后台使用
chrome.alarms实现计时与状态持久化 - 网站屏蔽策略(可选):
- 复用
declarativeNetRequest动态规则,在专注期间阻断/重定向干扰域名 - 提供一键暂停/延长(带“心理 friction”)
- Implemented timed blocking snooze/resume (
focus snooze 5/focus resume-blocking) to avoid “forgotten disable” scenarios.
- 复用
15.4 Web Monitor / Price Track(可选)
- 后台定时抽取页面关键元素(best-effort),发生变化时提示用户
- 备注:通知形态需要产品决策(面板内提示 vs 增加
notifications权限)
四、文件变更清单
新建文件
app/chrome-extension/shared/quick-panel/
├── ui/
│ ├── ai-chat-view.ts # AI Chat 作为 View 组件
│ ├── search-view.ts # 搜索视图容器
│ ├── result-list.ts # 结果列表
│ ├── result-item.ts # 结果项
│ └── action-panel.ts # 动作面板
├── core/
│ ├── keyboard-controller.ts # 键盘控制器
│ ├── history-tracker.ts # 使用历史追踪
│ ├── content-search.ts # 内容搜索(snippet + token scoring)
│ └── command-registry.ts # 命令注册表
├── providers/
│ ├── bookmarks-provider.ts # 书签 Provider
│ ├── history-provider.ts # 历史 Provider
│ ├── content-provider.ts # 内容 Provider
│ └── commands-provider.ts # 命令 Provider
└── commands/
├── page-commands.ts # 页面操作命令
└── tab-commands.ts # 标签页命令
app/chrome-extension/entrypoints/background/quick-panel/
├── bookmarks-handler.ts # 书签后台处理
├── history-handler.ts # 历史后台处理
└── content-handler.ts # 内容搜索后台处理
修改文件
app/chrome-extension/shared/quick-panel/
├── index.ts # Controller 重构
├── ui/
│ ├── ai-chat-panel.ts # 改为 Shell + View 的 wrapper
│ └── panel-shell.ts # 可能需要微调
├── providers/
│ └── tabs-provider.ts # 扩展动作
app/chrome-extension/shared/quick-panel/ui/
└── search-view.ts # 更新 placeholder/scopes
app/chrome-extension/shared/quick-panel/providers/
└── index.ts # 导出 Content provider
app/chrome-extension/shared/quick-panel/providers/
└── commands-provider.ts # 新增标签管理命令(P1)
app/chrome-extension/entrypoints/background/quick-panel/
└── tabs-handler.ts # 新增 pin/mute 消息处理
app/chrome-extension/entrypoints/background/quick-panel/
└── page-commands-handler.ts # 扩展 page commands(P1)
app/chrome-extension/entrypoints/background/
└── index.ts # 初始化 Content handler
app/chrome-extension/common/
└── message-types.ts # 新增消息类型
Phase 9+ 预期文件增量(部分已实现)
说明:以下为从
.docs/tansuo.md提炼出的可落地 backlog 对应的预期文件增量,具体以 Phase 9+ 的产品决策为准。
新建(建议)
app/chrome-extension/shared/quick-panel/providers/clipboard-provider.tsapp/chrome-extension/entrypoints/background/quick-panel/clipboard-handler.tsapp/chrome-extension/shared/quick-panel/providers/notes-provider.tsapp/chrome-extension/entrypoints/background/quick-panel/notes-handler.tsapp/chrome-extension/shared/quick-panel/providers/focus-provider.ts- (可选)
app/chrome-extension/shared/quick-panel/ui/reader-view.ts
已实现(摘录)
app/chrome-extension/shared/quick-panel/providers/web-search-provider.tsapp/chrome-extension/shared/quick-panel/core/url-template.tsapp/chrome-extension/shared/quick-panel/providers/workspaces-provider.tsapp/chrome-extension/entrypoints/background/quick-panel/workspaces-handler.tsapp/chrome-extension/shared/quick-panel/core/toolbox/*
修改(必需)
app/chrome-extension/shared/quick-panel/core/types.ts(scope/prefix 扩展)app/chrome-extension/common/message-types.ts(workspaces + page tools + toolbox 协议)app/chrome-extension/entrypoints/background/quick-panel/page-commands-handler.ts(新增页面工具命令)app/chrome-extension/shared/quick-panel/providers/commands-provider.ts(argument commands + 页面工具命令入口)
五、消息协议扩展
新增消息类型
// Tabs
QUICK_PANEL_TABS_QUERY: 'quick_panel_tabs_query',
QUICK_PANEL_TAB_ACTIVATE: 'quick_panel_tab_activate',
QUICK_PANEL_TAB_CLOSE: 'quick_panel_tab_close',
// Tabs 二级动作
QUICK_PANEL_TAB_SET_PINNED: 'quick_panel_tab_set_pinned',
QUICK_PANEL_TAB_SET_MUTED: 'quick_panel_tab_set_muted',
// Bookmarks
QUICK_PANEL_BOOKMARKS_QUERY: 'quick_panel_bookmarks_query',
QUICK_PANEL_BOOKMARK_REMOVE: 'quick_panel_bookmark_remove',
// History
QUICK_PANEL_HISTORY_QUERY: 'quick_panel_history_query',
QUICK_PANEL_HISTORY_DELETE: 'quick_panel_history_delete',
// Content
QUICK_PANEL_CONTENT_QUERY: 'quick_panel_content_query',
// Navigation & Commands
QUICK_PANEL_OPEN_URL: 'quick_panel_open_url',
QUICK_PANEL_PAGE_COMMAND: 'quick_panel_page_command',
// Usage
QUICK_PANEL_USAGE_RECORD: 'quick_panel_usage_record',
QUICK_PANEL_USAGE_GET_ENTRIES: 'quick_panel_usage_get_entries',
QUICK_PANEL_USAGE_LIST_RECENT: 'quick_panel_usage_list_recent',
// Workspaces (Phase 11+)
QUICK_PANEL_WORKSPACES_LIST: 'quick_panel_workspaces_list',
QUICK_PANEL_WORKSPACES_SAVE: 'quick_panel_workspaces_save',
QUICK_PANEL_WORKSPACES_OPEN: 'quick_panel_workspaces_open',
QUICK_PANEL_WORKSPACES_DELETE: 'quick_panel_workspaces_delete',
// Clipboard / Notes / Focus (Phase 15+)
QUICK_PANEL_CLIPBOARD_LIST: 'quick_panel_clipboard_list',
QUICK_PANEL_CLIPBOARD_SAVE: 'quick_panel_clipboard_save',
QUICK_PANEL_NOTES_QUERY: 'quick_panel_notes_query',
QUICK_PANEL_NOTES_CREATE: 'quick_panel_notes_create',
QUICK_PANEL_NOTES_DELETE: 'quick_panel_notes_delete',
QUICK_PANEL_FOCUS_START: 'quick_panel_focus_start',
QUICK_PANEL_FOCUS_STOP: 'quick_panel_focus_stop',
QUICK_PANEL_FOCUS_STATUS: 'quick_panel_focus_status',
六、验收标准
Phase 1 完成标准 ✅ (2025-12-30)
- Shell 作为唯一容器
- AI Chat 可通过搜索 "ai" 或 Tab 键切换进入
- 现有 AI Chat 功能无回归(ai-chat-panel.ts 保持 API 兼容)
Phase 2 完成标准 ✅ (2025-12-30)
- 输入关键词可搜索标签页
- 结果列表正确显示(支持 favicon 优先)
- 点击结果可切换到对应标签页
Phase 3 完成标准 ✅ (2025-12-30)
- ↑↓ 键可导航结果列表
- Enter 执行默认动作
- Tab/→ 打开动作面板(AI 条目则切换到 Chat)
- Esc 关闭面板(或先关闭动作面板)
- IME 输入法兼容(isComposing 守卫)
Phase 4 完成标准 ✅ (2025-12-30)
- 动作面板正确显示
- 可执行所有标签页动作
- Pin/Mute 功能正常
Phase 5 完成标准 ✅ (2025-12-31)
- 书签搜索功能正常
- 历史搜索功能正常
- 基础命令可执行
Phase 6 完成标准 ✅ (2025-12-31)
- 使用记录正确保存(chrome.storage.local,防抖写入)
- 面板打开显示最近使用(空查询时刷新 recent list)
- 搜索结果融合使用频次排序(applyUsageBoost 二次排序)
Phase 7 完成标准 ✅ (2026-01-03)
cscope 可返回匹配结果并显示 snippet- 后台自动缓存正文(best-effort),并在 SPA 路由变化后更新
- 受限页面不会导致错误扩散(无法抓取则跳过)
Phase 8 完成标准 ✅ (2026-01-03)
>Commands 增加标签管理命令(close others / close right / discard inactive / merge windows)- 后台执行使用同一
QUICK_PANEL_PAGE_COMMAND通道(不新增消息体系复杂度)
Phase 9 完成标准 ✅ (2026-01-03)
g/gh/npm/so/mdn前缀可用,并只返回 Web Search 结果(不污染 tabs/bookmarks/history)- Enter /
Cmd/Ctrl+Enter的打开行为与openMode一致(复用QUICK_PANEL_OPEN_URL) - URL 模板填充与编码逻辑有单元测试覆盖(
core/url-template.ts)
Phase 10 完成标准 ✅ (2026-01-03)
>页面工具命令可用:Clean URL / Zen / Dark / Allow Copy / PIP / Privacy Curtain,并且支持关闭还原- Reader Mode 形成闭环(in-page overlay,Esc/Close 关闭还原)
- 注入失败(受限页/权限)不影响 Quick Panel 主链路(best-effort)
Phase 11 完成标准 ✅ (2026-01-03)
wsscope 可保存/列出/搜索/恢复/删除工作区,并支持命名输入(通过 query 生成虚拟条目)- 恢复快照不跨 incognito 边界;批量操作具备确认门槛(默认新窗口打开,当前窗口恢复需要
Cmd/Ctrl+Enter) - 存储 schema 包含版本字段,支持后续迁移与回滚
Phase 12 完成标准 ✅ (2026-01-03)
- Argument Commands 覆盖 ≥5 个高频工具(已实现:json/url/base64/ts/uuid/jwt),并可一键复制结果
- 核心逻辑以纯函数实现,并补齐单元测试(
core/toolbox/*+tests/quick-panel/toolbox.test.ts) - 开发者工具以 “命令执行 + 导出/复制结果” 先闭环(已实现:Console export / Network capture (10s) / Performance trace (5s) / read_page export)
Phase 13 完成标准 ✅ (2026-01-03)
- Debug Bundle 可在 Downloads 生成报告包(目录形式,包含 screenshot/console/network/performance/read_page + manifest.json)
- API Detective 支持 start/stop 抓包、列表展示请求、复制 curl/fetch 片段,并支持请求重放(Replay 标记为危险动作)
- 支持取消与错误定位(Debug Bundle 每一步单独记录 success/error,并提供 cancel 命令;API Detective 高风险抓包提供显式“danger”入口)
Phase 14 完成标准 ✅ (2026-01-03)
- tool schema + 权限分级 + 二次确认机制落地(覆盖高风险动作)
- action log 可审计(至少记录:工具、参数摘要、结果摘要、时间)
- 支持 Plan mode:先展示计划,再执行(用户可确认/取消)
Phase 15 完成标准 ✅ (2026-01-03)
clipscope 可查看/搜索剪贴板历史(覆盖 Quick Panel 内复制来源),并支持 pin/复制(Phase 15.1 ✅)notescope 可创建/搜索笔记并本地持久化(Phase 15.2 ✅)- Pomodoro/Focus 可启动/停止计时并恢复状态;(可选)站点屏蔽规则可一键暂停/延长(Phase 15.3 ✅)
- Focus blocking supports timed snooze/resume (Phase 15.3 hardening ✅)
- (可选)
monscope 可创建/管理网页监控并记录变更提醒(无 notifications 权限,使用徽标 + 面板内列表)(Phase 15.4 ✅)
七、风险与应对
| 风险 | 应对措施 |
|---|---|
| AI Chat 改造影响现有功能 | 保留兼容 wrapper,增量测试 |
| 键盘快捷键与网站冲突 | ShadowRoot capture + stopPropagation |
| 大量标签页性能问题 | 虚拟滚动 + 防抖 + 分页 |
| 特殊页面无法注入 | 提供 Popup fallback(P1) |
| Scope 前缀扩展误触发 | 仅识别“带空格”的前缀(如 g ),并在 UI 明确展示当前 scope |
| 批量操作/诊断采集的隐私风险 | 高风险命令显式提示与确认;默认本地生成与存储;可清理日志与产物 |
| 页面工具注入兼容性风险 | Best-effort + 可关闭还原;失败不阻断主链路;避免持久化侵入式修改 |
八、已确认决策
| 决策项 | 结论 |
|---|---|
| AI Chat 定位 | 作为二级视图:通过 Tab 键或搜索 "ai" 选中进入,类 Raycast 交互 |
| 实施节奏 | 按 Phase 分批:先完成 Phase 1-3,验证后继续 |
| 快捷键 | 保持 Cmd+Shift+U:避免与其他软件冲突 |
待确认决策(Phase 9+,来自 .docs/tansuo.md)
- Scope 扩展策略:为每个引擎新增 scope(
g/gh/npm/...) vs 引入可配置的前缀路由(更灵活但需要改动 query 协议) - Reader/Outline 承载方式:新增 view(更一致) vs 新标签打开(更轻量、先闭环)
- Workspaces 存储与同步:
chrome.storage.local(先闭环) vsstorage.sync(跨设备,但容量/延迟/隐私需评估) - 批量/破坏性动作的确认门槛:统一确认 UI(一次性) vs 仅对高风险命令提示(更轻量)
- Tab 分组能力是否纳入 P1:如需要
tabGroups权限,需评估安装劝退成本与替代方案 - 诊断套件输出格式:zip vs 目录;是否默认采集 response body(隐私/体积/权限)
- AI Tooling:默认 “Plan mode” 是否开启、工具权限分级粒度、action log 的保存与导出策略
九、第一批实施详细任务(Phase 1-3)
任务清单
Phase 1: 架构重构 - Shell 统一容器
Task 1.1: 创建 AI Chat View 组件
- 文件:
ui/ai-chat-view.ts(新建) - 职责:
- 提供
mountQuickPanelAiChatView(options)函数 - 接收 mount points:
headerMount,headerRightMount,contentMount,footerMount - 不创建 overlay/panel,只渲染内容
- Header: 标题/副标题/流式状态指示
- Content: 消息列表/空状态
- Footer: 输入框/发送按钮/停止按钮/提示
- 提供
- 复用现有
ai-chat-panel.ts的核心逻辑
Task 1.2: 修改 AI Chat Panel 为兼容 Wrapper
- 文件:
ui/ai-chat-panel.ts(修改) - 改动:内部调用
mountQuickPanelShell+mountQuickPanelAiChatView - 保持现有 API 不变,确保向后兼容
Task 1.3: 重构 Controller
- 文件:
index.ts(修改) - 改动:
- 使用 Shell 作为唯一容器
- 默认显示 search view(空白/最近使用)
- 支持
setView('search' | 'chat')切换 - 监听 Shell 的
onRequestClose
Phase 2: 搜索主链路
Task 2.1: 创建搜索视图容器
- 文件:
ui/search-view.ts(新建) - 职责:
- 提供
mountQuickPanelSearchView(options)函数 - 接收 Shell 的 mount points
- 协调 SearchInput + QuickEntries + ResultList + Footer
- 管理 SearchEngine 实例
- 处理搜索状态
- 提供
Task 2.2: 实现结果列表组件
- 文件:
ui/result-list.ts(新建) - 职责:
- 渲染
SearchResult[] - 选中态管理(selectedIndex)
- 空状态/加载状态/错误状态
- 滚动到选中项(scrollIntoView)
- 渲染
- 样式:复用
.qp-*样式变量
Task 2.3: 实现结果项组件
- 文件:
ui/result-item.ts(新建) - 职责:
- 渲染单个结果项
- 图标(emoji/favicon/SVG)
- 标题 + 副标题
- 选中态/hover 态样式
- 右侧快捷键提示
Task 2.4: 注册 TabsProvider 并接入 UI
- 文件:
ui/search-view.ts(修改) - 改动:
- 创建 SearchEngine 实例
- 注册
createTabsProvider() - 监听 SearchInput 的
onChange - 调用
engine.schedule()执行搜索 - 将结果传递给 ResultList
Task 2.5: 实现 Footer 提示栏
- 文件:
ui/search-footer.ts(新建) - 职责:
- 显示快捷键提示:
↑↓ 导航 ↵ 选择 Tab 动作 Esc 关闭 - 显示当前 Scope 标签
- 显示搜索状态
- 显示快捷键提示:
Phase 3: 键盘导航系统
Task 3.1: 创建 KeyboardController
- 文件:
core/keyboard-controller.ts(新建) - 职责:
- 状态机管理:
{ view: 'search' | 'chat', subState: 'list' | 'action-panel' } - 在 ShadowRoot capture 级别监听键盘事件
- 输入控件免打扰守卫(input/textarea 焦点时不拦截字母键)
- 快捷键映射表
- 状态机管理:
Task 3.2: 实现搜索视图键盘交互
- 快捷键实现:
↑/↓ - 导航结果列表(调用 ResultList.selectPrev/selectNext) Enter - 执行默认动作 Cmd+Enter - 新标签打开(如适用) Tab/→ - 打开动作面板(或进入 AI Chat) Backspace - 返回(输入框为空时) Esc - 关闭面板
Task 3.3: AI Chat 进入逻辑
- 场景 1:输入框空白时按 Tab → 显示 "AI Assistant" 作为特殊结果项
- 场景 2:搜索 "ai" → 结果列表显示 "AI Assistant" 入口
- 场景 3:选中 "AI Assistant" 后按 Enter → 切换到 chat view
Task 3.4: 集成到 Controller
- 文件:
index.ts(修改) - 改动:
- 创建 KeyboardController 实例
- 传递给 SearchView 和 ChatView
- 在 Shell 显示时激活,隐藏时禁用
第一批完成标准
- Shell 作为唯一容器工作
- 搜索框可输入并搜索标签页
- 结果列表正确显示标签页
- ↑↓ 键可导航结果列表
- Enter 可切换到选中标签页
- Tab 或搜索 "ai" 可进入 AI Chat
- Esc 关闭面板
- 现有 AI Chat 功能无回归
十、技术实现细节
10.1 AI Chat View 拆分细节
需要从 ai-chat-panel.ts 剥离的"容器逻辑"(迁移到 Shell + Controller):
- Overlay/Panel 创建:
:272,:277 - Backdrop click 关闭:
:934 - Close button:
:943 - 全局 ESC 关闭:
:974
需要保留在 Chat View 的"内容逻辑":
- 消息渲染与 streaming 更新
- Composer(banner/textarea/actionBtn)
- 发送/取消请求
- Context 收集
- Textarea auto-resize
滚动处理:
- 使用 Shell 的
elements.content作为唯一 scroll container - Chat View 的
emptyEl/messagesEl直接挂到contentChatMount createQuickPanelMessageRenderer({ scrollContainer })传入shellElements.content
视图切换时的滚动处理:
// shell.onViewChange
if (view === 'chat') {
// 有消息时滚动到底部,否则 scrollTop = 0
if (hasMessages) renderer.scrollToBottom();
else shellElements.content.scrollTop = 0;
} else if (view === 'search') {
shellElements.content.scrollTop = 0;
}
10.2 SearchInput 集成方式
现有 API(ui/search-input.ts:141):
const searchInput = createSearchInput({
container: shellElements.headerSearchMount,
initialScope: 'all',
placeholder: 'Search tabs, bookmarks, commands...',
autoFocus: true,
availableScopes: ['all', 'tabs', 'commands'], // Phase 1 可用
onChange: ({ scope, query }) => {
searchEngine.schedule({ scope, query });
},
});
无需修改,直接可用。
10.3 QuickEntries 集成方式
现有 API(ui/quick-entries.ts:72):
const quickEntries = createQuickEntries({
container: shellElements.contentSearchMount,
scopes: ['tabs', 'bookmarks', 'history', 'commands'],
onSelect: (scope) => {
searchInput.setScope(scope);
searchInput.focus();
},
});
// Phase 1 只有 tabs,禁用其他
quickEntries.setDisabled('bookmarks', true);
quickEntries.setDisabled('history', true);
// commands 可以保留(用于 AI 入口)
10.4 结果列表样式(需新增)
在 ui/styles.ts 新增以下 CSS 类:
/* 结果列表容器 */
.qp-results {
display: flex;
flex-direction: column;
gap: 2px;
}
/* 结果项 */
.qp-result {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
border-radius: var(--ac-radius-inner);
cursor: pointer;
transition: background 0.15s;
}
.qp-result:hover {
background: var(--qp-input-bg);
}
.qp-result[data-selected='true'] {
background: var(--ac-accent-subtle);
}
/* 结果图标 */
.qp-result-icon {
width: 20px;
height: 20px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
}
/* 结果内容 */
.qp-result-content {
flex: 1;
min-width: 0;
}
.qp-result-title {
font-size: 13px;
color: var(--ac-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.qp-result-subtitle {
font-size: 11px;
color: var(--ac-text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 结果右侧提示 */
.qp-result-hint {
flex-shrink: 0;
font-size: 11px;
color: var(--ac-text-subtle);
}
10.5 KeyboardController 状态机
interface KeyboardState {
view: 'search' | 'chat';
subState: 'idle' | 'results' | 'action-panel';
selectedIndex: number;
}
// 快捷键映射
const SEARCH_VIEW_KEYS = {
ArrowUp: 'selectPrev',
ArrowDown: 'selectNext',
Enter: 'executeDefault',
'Meta+Enter': 'executeInNewTab',
Tab: 'openActionPanel',
Escape: 'close',
Backspace: 'backOrClear', // 输入框空时返回
};
// 输入控件守卫
function shouldIgnoreKey(e: KeyboardEvent): boolean {
const target = e.target as HTMLElement;
const isInput =
target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable;
// 在输入控件中,只拦截导航键,不拦截字母键
if (isInput) {
return !['ArrowUp', 'ArrowDown', 'Escape', 'Enter', 'Tab'].includes(e.key);
}
return false;
}
10.6 AI Chat 入口实现
作为特殊搜索结果:
// 在 SearchView 中注入 AI 入口
function injectAiEntry(results: SearchResult[], query: string): SearchResult[] {
// 场景 1: 空查询时显示 AI 入口
// 场景 2: 查询包含 "ai" 时显示
const shouldShowAi = query.trim() === '' || query.toLowerCase().includes('ai');
if (shouldShowAi) {
results.push({
id: '__ai_assistant__',
provider: 'system',
title: 'AI Assistant',
subtitle: 'Chat with AI about this page',
icon: '✨',
data: { type: 'ai-entry' },
score: query.toLowerCase().includes('ai') ? 100 : 50,
});
}
return results;
}
// 执行动作时检查
function executeResult(result: SearchResult): void {
if (result.id === '__ai_assistant__') {
shell.setView('chat');
return;
}
// ... 正常执行 provider 动作
}
十一、实施顺序建议
为确保增量可测试,建议按以下顺序实施:
Phase 1.1 → Phase 1.2 → Phase 1.3 → 测试 AI Chat 无回归 ✅
↓
Phase 2.5 → Phase 2.2 → Phase 2.3 → Phase 2.1 → Phase 2.4 → 测试搜索链路 ✅
↓
Phase 3.1 → Phase 3.2 → Phase 3.3 → Phase 3.4 → 测试键盘导航 ✅
↓
Phase 4 → 测试动作面板 ✅
↓
Phase 5 → 测试 Bookmarks/History/Commands Providers ✅
↓
Phase 6 → 测试使用历史追踪 ✅
解释:
- Phase 1 必须按顺序(先拆分 View,再改 wrapper,最后改 Controller)
- Phase 2 先做 Footer(简单),再做列表组件,最后组装
- Phase 3 必须在 Phase 2 完成后才能测试
十二、实施记录
Phase 1-3 实施记录 (2025-12-30)
新建文件
| 文件 | 说明 |
|---|---|
ui/ai-chat-view.ts |
AI Chat 作为可嵌入的 View 组件,不创建 overlay |
ui/search-view.ts |
搜索视图容器,协调 SearchInput + QuickEntries + ResultList |
core/keyboard-controller.ts |
键盘控制器,ShadowRoot capture 级别,IME 兼容 |
修改文件
| 文件 | 修改内容 |
|---|---|
ui/ai-chat-panel.ts |
改为 Shell + View 的 wrapper,保持 API 兼容 |
ui/styles.ts |
新增搜索相关 CSS 样式,修复 --ac-text-danger |
index.ts |
Controller 重构使用 Shell,集成 SearchEngine/TabsProvider/KeyboardController |
Codex Review 修复记录
| 问题 | 修复 |
|---|---|
| Result execution broken | 通过 provider.getActions(result) 查找 provider |
| Icon typing mismatch | 支持 string | Node,favicon 优先显示 |
| Loading state not rendered | 设置 loading 后立即调用 renderResults() |
| CSS undefined token | --ac-text-danger → --ac-danger |
| IME composition | 添加 event.isComposing 守卫 |
| Stale search results | 清除查询时调用 searchEngine.cancelActive() |
| Results during loading | 设置 loading 时清空 results[] 防止执行旧结果 |
| Favicon priority | 优先显示 data.favIconUrl,失败回退到 icon |
架构决策
| 决策 | 说明 |
|---|---|
| Shell 唯一容器 | 解决双 overlay 冲突,统一管理 search/chat 视图 |
| View 可嵌入 | AI Chat View 不创建 overlay,渲染到 mount points |
| 资源持久化 | SearchEngine/AgentBridge 跨 show/hide 持久化,支持缓存和会话 |
| 键盘分层 | KeyboardController 在 ShadowRoot capture 级别,守卫输入控件 |
Phase 4 实施记录 (2025-12-30)
新建文件
| 文件 | 说明 |
|---|---|
ui/action-panel.ts |
动作面板组件,显示结果的可用操作列表 |
修改文件
| 文件 | 修改内容 |
|---|---|
common/message-types.ts |
新增 QUICK_PANEL_TAB_SET_PINNED 和 QUICK_PANEL_TAB_SET_MUTED 消息类型及对应的 Payload/Response 接口 |
background/quick-panel/tabs-handler.ts |
新增 handleSetPinned 和 handleSetMuted 处理函数,注册消息监听器 |
providers/tabs-provider.ts |
扩展 TabsSearchResultData 增加 audible/muted 字段;新增 writeToClipboard 和 formatMarkdownLink 辅助函数;扩展 getActions 返回完整动作列表(Switch/CopyURL/CopyMarkdown/Pin/Mute/Close) |
ui/search-view.ts |
集成 ActionPanel;新增 onGetActions/onActionExecute 选项;扩展 Manager API(openActionPanel/closeActionPanel/isActionPanelOpen/actionPanelSelectPrev/Next/ExecuteSelected) |
ui/styles.ts |
新增动作面板 CSS 样式(.qp-action-backdrop/.qp-action-panel/.qp-action-header/.qp-action-list/.qp-action-item) |
core/keyboard-controller.ts |
新增 isActionPanelOpen/onCloseActionPanel/onActionPanelNavigateUp/Down/Select 选项;ArrowLeft/ArrowRight 条件拦截;新增 handleActionPanelKey 处理函数 |
index.ts |
连接 KeyboardController 与 SearchView 的动作面板方法;传递 onGetActions 回调获取 provider actions |
动作面板功能
| 动作 ID | 标题 | 快捷键 | 说明 |
|---|---|---|---|
tabs.activate |
Switch to tab | Enter | 切换到标签页 |
tabs.copyUrl |
Copy URL | Cmd+C | 复制 URL 到剪贴板 |
tabs.copyMarkdown |
Copy as Markdown | Cmd+Shift+C | 复制为 Markdown 链接 |
tabs.pin/unpin |
Pin/Unpin tab | Cmd+P | 固定/取消固定标签页 |
tabs.mute/unmute |
Mute/Unmute tab | Cmd+M | 静音/取消静音(仅对 audible/muted 标签显示) |
tabs.close |
Close tab | Cmd+W | 关闭标签页(danger 样式) |
键盘交互
| 按键 | 结果列表模式 | 动作面板模式 |
|---|---|---|
| ↑/↓ | 导航结果列表 | 导航动作列表 |
| Tab/→ | 打开动作面板 | 关闭动作面板 |
| Enter | 执行默认动作 | 执行选中动作 |
| Esc | 关闭面板 | 关闭动作面板 |
| ← | - | 关闭动作面板 |
架构决策
| 决策 | 说明 |
|---|---|
| 动作面板作为子模式 | 动作面板是 SearchView 的内部状态,不是独立的 Shell view |
| ESC 语义分层 | Esc 先关闭动作面板,再关闭 Quick Panel |
| ArrowLeft/Right 条件拦截 | 仅当动作面板打开时拦截,否则让输入框处理光标移动 |
| 动作执行后关闭面板 | 执行任何动作后自动关闭 Quick Panel |
Phase 5 实施记录 (2025-12-31)
新建文件
| 文件 | 说明 |
|---|---|
providers/provider-utils.ts |
共享工具函数:writeToClipboard, formatMarkdownLink, computeWeightedTokenScore |
providers/bookmarks-provider.ts |
书签搜索 Provider,优先级 20,包含在 'all' scope |
providers/history-provider.ts |
历史搜索 Provider,优先级 10,包含在 'all' scope |
providers/commands-provider.ts |
命令 Provider,优先级 0,不包含在 'all' scope(需 '>' 前缀) |
background/quick-panel/bookmarks-handler.ts |
书签查询后台处理 |
background/quick-panel/history-handler.ts |
历史查询后台处理 |
background/quick-panel/page-commands-handler.ts |
页面命令和 URL 打开后台处理 |
修改文件
| 文件 | 修改内容 |
|---|---|
common/message-types.ts |
新增 QUICK_PANEL_BOOKMARKS_QUERY、QUICK_PANEL_HISTORY_QUERY、QUICK_PANEL_OPEN_URL、QUICK_PANEL_PAGE_COMMAND 消息类型及接口 |
providers/tabs-provider.ts |
重构使用 provider-utils.ts 中的共享工具函数 |
providers/index.ts |
导出新增的 bookmarks/history/commands provider |
background/index.ts |
注册新的后台处理器 |
index.ts |
注册所有 provider 到 SearchEngine,更新 availableScopes |
消息类型
| 消息类型 | Payload | Response | 说明 |
|---|---|---|---|
QUICK_PANEL_BOOKMARKS_QUERY |
{ query, maxResults } |
{ success, bookmarks } |
书签搜索 |
QUICK_PANEL_HISTORY_QUERY |
{ query, maxResults } |
{ success, items } |
历史搜索 |
QUICK_PANEL_OPEN_URL |
{ url, disposition } |
{ success } |
打开 URL(current_tab/new_tab/background_tab) |
QUICK_PANEL_PAGE_COMMAND |
{ command } |
{ success } |
执行页面命令 |
命令列表
| 命令 ID | 标题 | 说明 |
|---|---|---|
page.reload |
Reload | 刷新当前页面 |
page.back |
Back | 历史后退 |
page.forward |
Forward | 历史前进 |
page.stop |
Stop | 停止加载 |
tab.close |
Close tab | 关闭当前标签页 |
tab.duplicate |
Duplicate tab | 复制当前标签页 |
tab.togglePin |
Toggle pin | 切换固定状态 |
tab.toggleMute |
Toggle mute | 切换静音状态 |
copy.url |
Copy URL | 复制当前页面 URL |
copy.markdown |
Copy as Markdown | 复制为 Markdown 链接 |
window.newTab |
New tab | 新建标签页 |
window.newWindow |
New window | 新建窗口 |
架构决策
| 决策 | 说明 |
|---|---|
| Commands 不在 'all' scope | 用户需用 '>' 前缀显式进入命令模式,避免干扰搜索 |
| 默认动作为当前标签页 | Bookmarks/History 默认在当前标签页打开,符合用户直觉 |
| 共享工具函数 | 提取 clipboard/markdown/scoring 到 provider-utils.ts,DRY 原则 |
| Provider 优先级 | tabs(50) > bookmarks(20) > history(10) > commands(0),tabs 最重要 |
| 评分公式统一 | 所有 provider 使用 computeWeightedTokenScore,确保一致的搜索体验 |
Codex Review 修复记录 (2025-12-31)
| 问题 | 修复 |
|---|---|
| writeToClipboard 无 fallback | 现代 API 失败时正确回退到 execCommand |
| formatMarkdownLink URL 不完整 | URL 中的 () 现在正确 percent-encode |
| URL 安全验证缺失 | 阻止 javascript:, data: 等危险 scheme |
| maxResults 无上限 | 后台处理器限制最大 500 条防止过载 |
Phase 6 实施记录 (2025-12-31)
新建文件
| 文件 | 说明 |
|---|---|
core/usage-key.ts |
使用键生成规则:url:<origin+path> 或 cmd:<commandId> |
core/history-tracker.ts |
使用历史追踪器,频次算法:30*recency + 10*frequency |
修改文件
| 文件 | 修改内容 |
|---|---|
ui/search-view.ts |
集成 HistoryTracker;新增 refreshRecentList 空查询显示最近使用;新增 applyUsageBoost 频次排序 |
index.ts |
添加 ensureHistoryTracker 函数;recordResultUsage 记录使用;传递 historyTracker 到 SearchView |
关键设计决策
| 决策 | 说明 |
|---|---|
| 隐私优先的 URL 归一化 | 仅保留 origin + pathname,剥离 query/hash/credentials,仅追踪 http/https |
| 频次算法 | recency = exp(-ageHours * ln(2) / 72)(3天半衰期),frequency = log1p(count) / log1p(30) |
| 最大 boost 分数 | 40 分(30 分 recency + 10 分 frequency) |
| 存储机制 | chrome.storage.local |
| 最佳努力原则 | 追踪失败不影响核心搜索体验 |
Codex Review 修复记录 (2025-12-31)
| 问题 | 修复 |
|---|---|
| Flush 写入丢失 | 等待 in-flight flush 完成后重新检查 dirty;失败后重新调度 flush |
| URL 隐私泄露 | 剥离 query string(常含敏感 token/ID),仅追踪 http/https 协议 |
| applyUsageBoost 失败破坏 UX | 添加 try-catch,失败时返回原始结果 |
| 异步竞态条件 | 在 applyUsageBoost 后再次检查 staleness |
| 文档注释不准确 | 修正 frecency 公式注释中的 half-life 实现说明 |
频次算法详解
// Recency: 指数衰减,半衰期 72 小时(3天)
// 使用后 3 天,recency ≈ 0.5
// 使用后 1 周,recency ≈ 0.25
recency = exp((-ageHours * ln(2)) / halfLifeHours);
// Frequency: 对数增长,上限 30 次
// count=1 → 0.21, count=5 → 0.53, count=30 → 1.0
frequency = log1p(count) / log1p(countCap);
// Boost: 总分 0-40
// recency 权重 3 倍于 frequency,强调"最近使用"
boost = 30 * recency + 10 * frequency;
使用示例
// 记录使用
const usageKey = computeUsageKey(result); // "url:https://example.com/page"
await historyTracker.recordUsage(usageKey);
// 获取频次信号
const signals = await historyTracker.getSignals(['url:https://example.com/page']);
const signal = signals.get('url:https://example.com/page');
// { lastUsedAt: 1704000000000, count: 5, recency: 0.87, frequency: 0.53, boost: 31.4 }
// 获取最近使用列表(空查询时显示)
const recentItems = await historyTracker.getRecentList(20);
Phase 6.1 IndexedDB 迁移记录 (2025-12-31)
背景
原 Phase 6 实现使用 chrome.storage.local 存储使用历史,存在并发写入问题:
- chrome.storage.local 需要完整对象写入(无增量更新)
- 多标签页同时写入会导致数据丢失(后写覆盖先写)
- 防抖机制可缓解但无法完全解决
解决方案
迁移到 IndexedDB + 消息桥模式:
- IndexedDB 在 Background Service Worker 中运行(extension-origin)
- 每个 key 独立 readwrite 事务,避免并发覆盖
- Content scripts 通过
chrome.runtime.sendMessage调用 - 自动从 chrome.storage.local 迁移数据
- 失败时回退到 chrome.storage.local
新建文件
| 文件 | 说明 |
|---|---|
background/quick-panel/usage-history-handler.ts |
IndexedDB 后台处理器,含迁移逻辑和 fallback |
修改文件
| 文件 | 修改内容 |
|---|---|
common/message-types.ts |
新增 QUICK_PANEL_USAGE_RECORD、QUICK_PANEL_USAGE_GET_ENTRIES、QUICK_PANEL_USAGE_LIST_RECENT 消息类型及接口 |
core/history-tracker.ts |
重写为消息桥客户端,移除本地存储逻辑,保留 frecency 计算 |
background/index.ts |
注册 initQuickPanelUsageHistoryHandler |
IndexedDB Schema
// 数据库
const DB_NAME = 'quick_panel_usage';
const DB_VERSION = 1;
// 对象存储
const STORE = 'usage';
// 复合主键: [namespace, key]
// 索引
const IDX_NAMESPACE_LAST_USED_AT = 'namespace_lastUsedAt'; // 最近使用列表
const IDX_NAMESPACE_LAST_USED_AT_COUNT = 'namespace_lastUsedAt_count'; // LRU 淘汰
// 记录结构
interface UsageEntryRecord {
namespace: string;
key: string;
lastUsedAt: number;
count: number;
}
// 迁移标记
interface UsageMetaRecord {
namespace: string;
key: '__meta__';
schemaVersion: 1;
migratedAt: number;
legacyUpdatedAt: number;
}
消息协议
| 消息类型 | Payload | Response | 说明 |
|---|---|---|---|
QUICK_PANEL_USAGE_RECORD |
{ namespace, key, maxEntries } |
{ success } |
记录使用事件 |
QUICK_PANEL_USAGE_GET_ENTRIES |
{ namespace, keys } |
{ success, entries } |
批量获取条目 |
QUICK_PANEL_USAGE_LIST_RECENT |
{ namespace, limit } |
{ success, items } |
最近使用列表 |
架构决策
| 决策 | 说明 |
|---|---|
| Background-owned IndexedDB | Content script 的 indexedDB 是 page-origin 作用域,无法跨页面共享 |
| Per-key readwrite 事务 | 避免并发写入导致的数据覆盖,每次操作只锁定单个 key |
| 迁移标记 (meta) | 使用特殊 key 标记迁移完成,防止重复迁移 |
| 错误传播迁移读取 | readLegacyStoreForMigration 不吞异常,防止读取失败时误标记已迁移 |
| Legacy fallback | IndexedDB 失败时自动回退到 chrome.storage.local |
| 客户端 key 限制 | getSignals 客户端限制 2000 keys,减少无效 IPC |
Codex Review 修复记录 (2025-12-31)
| 问题 | 修复 |
|---|---|
| 迁移读取失败被吞掉 | 新增 readLegacyStoreForMigration 函数,传播错误防止误标记 |
| 大型 legacy 存储阻塞迁移 | 迁移前检查 size,超过 MAX_ENTRIES_HARD_CAP 则先截断 |
| 负数 lastUsedAt 破坏索引 | legacy 解析和 IDB 写入时 clamp 到 >= 0 |
| 损坏记录导致 NaN 传播 | recordUsageInIdb 使用 clampInt 处理 existing count |
| 客户端发送过多 keys | getSignals 添加 .slice(0, MAX_KEYS_PER_REQUEST) |
| IDB 范围边界不够宽 | IDB_NUMBER_MIN/MAX 改用 Number.MIN/MAX_SAFE_INTEGER |
数据流
┌─────────────────┐ sendMessage ┌──────────────────────┐
│ Content Script │ ─────────────────────▶│ Background Worker │
│ HistoryTracker │ │ usage-history-handler│
│ │ ◀─────────────────────│ │
│ (frecency calc)│ response │ ┌────────────────┐ │
└─────────────────┘ │ │ IndexedDB │ │
│ │ quick_panel_ │ │
│ │ usage │ │
│ └────────────────┘ │
│ │ │
│ ▼ fallback │
│ ┌────────────────┐ │
│ │chrome.storage │ │
│ │.local (legacy) │ │
│ └────────────────┘ │
└──────────────────────┘