diff --git a/docs/superpowers/specs/2026-07-23-cell-toolbar-actions-design.md b/docs/superpowers/specs/2026-07-23-cell-toolbar-actions-design.md new file mode 100644 index 0000000..14e3c9c --- /dev/null +++ b/docs/superpowers/specs/2026-07-23-cell-toolbar-actions-design.md @@ -0,0 +1,74 @@ +# AI 操作按钮迁入原生 Cell Toolbar — 设计 + +- 日期: 2026-07-23 +- 状态: 已批准(用户于 2026-07-23 确认) + +## 背景与问题 + +当前 `✨ 优化` / `🐛 排错` / `🪄 编辑` 三个按钮渲染在每个 cell **底部** 的 `ICellFooter` 里,链路为:`installOpenCodeEverywhere` 替换每个 notebook 的 `contentFactory` → `OpenCodeCellContentFactory.createCellFooter()` → `OpenCodeCellFooter` widget。 + +问题:按钮位置不符合预期 —— 应出现在每个 cell **右上角**、JupyterLab 原生 cell toolbar 的上移/下移按钮(`move-cell-up` / `move-cell-down`)旁边。 + +## 已确认的决策 + +| 问题 | 决策 | +|---|---| +| 可见性语义 | 跟随原生 toolbar:只在 **active cell** 右上角显示(与上移/下移按钮行为严格一致) | +| 底部 footer | **删除**,三个按钮全部上移 | +| 点击交互 | 本次不变:`🪄 编辑` 仍用 `window.prompt`;结果仍走 `Notification`;inline prompt box / diff 面板留待后续 | +| 作用范围 | 仅 `CodeCell`;Markdown 等 cell 不显示 AI 按钮(现状 footer 挂在所有 cell 上,属顺带修正) | + +## 机制(JupyterLab 4.6 真实扩展点,已核实源码) + +- 原生 cell toolbar 由内置 `@jupyterlab/cell-toolbar-extension` 提供,toolbar factory 名为 `'Cell'`,settings 驱动;默认按钮(含 `move-cell-up` / `move-cell-down`,rank 均为默认 50)由其 schema 的 `jupyter.lab.toolbars` 贡献。 +- 第三方扩展通过 `IToolbarWidgetRegistry.addFactory('Cell', '', factory)` 注册 widget factory;**factory 收到具体的 `Cell` 实例**,toolbar 由 tracker 按 cell model 缓存。 +- 自己的 `schema/plugin.json` 顶层加 `"jupyter.lab.toolbars": { "Cell": [...] }` 即可把该项贡献进原生 toolbar,无需 DOM 注入或 CSS 定位。 +- 原生 toolbar 只渲染在 active cell 内(`activeCellChanged` 时移动),空间不足时自动隐藏 —— 我们的按钮随它一起获得这些行为。 + +## 设计 + +### 组件改造 + +- `src/components/opencode_cell_footer.ts` → 改造为 `src/components/opencode_cell_actions.ts`: + - `OpenCodeCellActions extends Widget`,**构造函数直接接收 `CodeCell`**(factory 传入),不再在 `onAfterAttach` 里靠 `this.parent instanceof CodeCell` 解析宿主。 + - `model.contentChanged` / `stateChanged` 订阅保持,`onBeforeDetach` 摘除。 + - 渲染逻辑不变:`✨ 优化` / `🐛 排错`(无 error output 时禁用) / `🪄 编辑`;loading 时全部禁用。 + - 错误提示统一走 `Notification`,widget 内不再渲染 error span(toolbar 空间小)。 + - 模块级 `setOpenCodeRuntime()` 运行时注入机制不变。 +- 删除 `src/components/opencode_cell_factory.ts`、`src/components/opencode_installer.ts`。 + +### 注册与配置 + +- `src/index.ts`: + - `optional` 增加 `IToolbarWidgetRegistry`(来自 `@jupyterlab/apputils`,已是依赖)。 + - activate 中:`toolbarRegistry.addFactory('Cell', 'opencode-cell-actions', (cell) => cell instanceof CodeCell ? new OpenCodeCellActions(cell) : new Widget())`;非 CodeCell 返回空 widget。 + - 移除 `installOpenCodeEverywhere` 调用及 import。 +- `schema/plugin.json` 顶层增加: + ```json + "jupyter.lab.toolbars": { + "Cell": [{ "name": "opencode-cell-actions", "rank": 100 }] + } + ``` + 默认按钮 rank 为 50,我们排 100 → 出现在 delete-cell 右侧,紧邻上移/下移按钮组。 +- `style/base.css`:新增 `.opencode-cell-actions` 紧凑按钮样式(适配原生 toolbar 高度)。 + +### 上下文提取(不变) + +widget attach 后仍用现有 `extractCellContextFromCell`(沿 `cell.parent` 链 duck-typing 找 `NotebookPanel`)。cell 实例改由构造参数传入,比现在更可靠。 + +### 测试与文档 + +- `src/__tests__/opencode_cell_footer.spec.ts` → `opencode_cell_actions.spec.ts`:构造时直接传 cell,删除 `Object.defineProperty(footer, 'parent', ...)` 相关 hack,断言不变(三按钮渲染、禁用态)。 +- `design.md` 第 2 节交互描述更新:悬浮 toolbar → 原生 cell toolbar(仅 active cell 显示)。 + +## 兼容性 + +- 纯前端 UI 搬迁;REST 契约、Python server 端零改动。 +- `cell-toolbar-extension` 被禁用时,我们的按钮与原生按钮一起消失(行为一致,可接受)。 +- 移除 contentFactory 覆写后,不再受"已打开的 cell 需 reload 才生效"的限制。 +- `IToolbarWidgetRegistry` 声明为 `optional`,registry 缺失时插件仍可激活(按钮不显示)。 + +## 验证 + +- `jlpm test`、`jlpm lint:check`、`jlpm build` 全部通过。 +- 手动:`jupyter lab` 打开 notebook,active cell 右上角出现三个按钮,位于 delete 按钮右侧;无错误的 cell 上 `🐛 排错` 禁用;切换 active cell 按钮随之移动。