@deepseek-harness-tui/dsh-tui 0.8.8 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -8
- package/bin/dsh-tui.js +1 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +4 -2
- package/lib/types/components/ActivityPicker.d.ts +4 -1
- package/lib/types/components/ActivityPicker.d.ts.map +1 -1
- package/lib/types/components/ActivityPicker.js +2 -2
- package/lib/types/components/BtwPanel.d.ts.map +1 -1
- package/lib/types/components/BtwPanel.js +1 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts +2 -1
- package/lib/types/components/Chat/SubagentMessage.d.ts.map +1 -1
- package/lib/types/components/Chat/SubagentMessage.js +11 -3
- package/lib/types/components/CommandSuggestions.d.ts +21 -4
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/CommandSuggestions.js +41 -14
- package/lib/types/components/EffortSlider.d.ts +4 -1
- package/lib/types/components/EffortSlider.d.ts.map +1 -1
- package/lib/types/components/EffortSlider.js +4 -2
- package/lib/types/components/ExtensionDialog.js +6 -2
- package/lib/types/components/FileSuggestions.d.ts +24 -7
- package/lib/types/components/FileSuggestions.d.ts.map +1 -1
- package/lib/types/components/FileSuggestions.js +39 -12
- package/lib/types/components/GoalTodoPanel.d.ts +26 -2
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +86 -17
- package/lib/types/components/HelpMenu.d.ts +7 -1
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +12 -4
- package/lib/types/components/HistorySearchDialog.d.ts +4 -1
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
- package/lib/types/components/HistorySearchDialog.js +2 -2
- package/lib/types/components/LangPicker.d.ts +14 -0
- package/lib/types/components/LangPicker.d.ts.map +1 -0
- package/lib/types/components/LangPicker.js +17 -0
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +3 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +12 -3
- package/lib/types/components/MessageList.d.ts +43 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +553 -41
- package/lib/types/components/ModelPicker.d.ts +4 -1
- package/lib/types/components/ModelPicker.d.ts.map +1 -1
- package/lib/types/components/ModelPicker.js +2 -2
- package/lib/types/components/PermissionsPicker.d.ts +21 -0
- package/lib/types/components/PermissionsPicker.d.ts.map +1 -0
- package/lib/types/components/PermissionsPicker.js +27 -0
- package/lib/types/components/PlanPicker.d.ts +15 -0
- package/lib/types/components/PlanPicker.d.ts.map +1 -0
- package/lib/types/components/PlanPicker.js +18 -0
- package/lib/types/components/PresetPicker.d.ts +4 -1
- package/lib/types/components/PresetPicker.d.ts.map +1 -1
- package/lib/types/components/PresetPicker.js +2 -2
- package/lib/types/components/PromptInput.d.ts +4 -3
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +248 -29
- package/lib/types/components/RewindPicker.d.ts +12 -1
- package/lib/types/components/RewindPicker.d.ts.map +1 -1
- package/lib/types/components/RewindPicker.js +6 -4
- package/lib/types/components/ScrollbarGutter.d.ts +28 -0
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -0
- package/lib/types/components/ScrollbarGutter.js +68 -0
- package/lib/types/components/Select.d.ts +8 -1
- package/lib/types/components/Select.d.ts.map +1 -1
- package/lib/types/components/Select.js +4 -2
- package/lib/types/components/SkillsPicker.d.ts +4 -1
- package/lib/types/components/SkillsPicker.d.ts.map +1 -1
- package/lib/types/components/SkillsPicker.js +2 -2
- package/lib/types/components/SubagentCard.d.ts +2 -1
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +6 -1
- package/lib/types/components/SubagentDashboard.d.ts +4 -0
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +9 -1
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
- package/lib/types/components/SubagentDetailScene.js +5 -4
- package/lib/types/components/SuggestionCard.d.ts +63 -0
- package/lib/types/components/SuggestionCard.d.ts.map +1 -0
- package/lib/types/components/SuggestionCard.js +77 -0
- package/lib/types/components/ThemePicker.d.ts +4 -1
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +2 -2
- package/lib/types/components/ThinkingToggle.d.ts +4 -1
- package/lib/types/components/ThinkingToggle.d.ts.map +1 -1
- package/lib/types/components/ThinkingToggle.js +2 -2
- package/lib/types/components/TimelineRail.d.ts +53 -0
- package/lib/types/components/TimelineRail.d.ts.map +1 -0
- package/lib/types/components/TimelineRail.js +190 -0
- package/lib/types/components/WorkspaceFlowPicker.d.ts +6 -1
- package/lib/types/components/WorkspaceFlowPicker.d.ts.map +1 -1
- package/lib/types/components/WorkspaceFlowPicker.js +4 -2
- package/lib/types/components/WorkspaceMenuPicker.d.ts +19 -0
- package/lib/types/components/WorkspaceMenuPicker.d.ts.map +1 -0
- package/lib/types/components/WorkspaceMenuPicker.js +19 -0
- package/lib/types/components/WorkspacePicker.d.ts +4 -1
- package/lib/types/components/WorkspacePicker.d.ts.map +1 -1
- package/lib/types/components/WorkspacePicker.js +2 -2
- package/lib/types/components/approvals/ApprovalPanel.d.ts.map +1 -1
- package/lib/types/components/approvals/ApprovalPanel.js +5 -1
- package/lib/types/components/design-system/ListItem.d.ts +8 -1
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +8 -3
- package/lib/types/components/messages/AssistantTextMessage.d.ts +5 -2
- package/lib/types/components/messages/AssistantTextMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantTextMessage.js +6 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +10 -4
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +4 -2
- package/lib/types/components/messages/UserPromptMessage.d.ts +2 -1
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +34 -2
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +13 -2
- package/lib/types/components/sessions/SessionListRow.d.ts +4 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +4 -2
- package/lib/types/components/trajectory/HotspotView.d.ts +7 -1
- package/lib/types/components/trajectory/HotspotView.d.ts.map +1 -1
- package/lib/types/components/trajectory/HotspotView.js +8 -2
- package/lib/types/components/trajectory/Ledger.d.ts +9 -1
- package/lib/types/components/trajectory/Ledger.d.ts.map +1 -1
- package/lib/types/components/trajectory/Ledger.js +23 -5
- package/lib/types/components/trajectory/WaveBand.d.ts +10 -1
- package/lib/types/components/trajectory/WaveBand.d.ts.map +1 -1
- package/lib/types/components/trajectory/WaveBand.js +7 -2
- package/lib/types/dsh-adapter/channel.d.ts +16 -5
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +332 -29
- package/lib/types/dsh-adapter/index.d.ts +10 -2
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +4 -1
- package/lib/types/dsh-adapter/plugin.d.ts +0 -9
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +136 -6
- package/lib/types/i18n.d.ts +176 -15
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +51 -8
- package/lib/types/ink/components/AlternateScreen.d.ts.map +1 -1
- package/lib/types/ink/components/AlternateScreen.js +13 -1
- package/lib/types/ink/components/App.d.ts +12 -1
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +101 -4
- package/lib/types/ink/components/ClockContext.d.ts.map +1 -1
- package/lib/types/ink/components/ClockContext.js +2 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +170 -91
- package/lib/types/ink/constants.d.ts +9 -0
- package/lib/types/ink/constants.d.ts.map +1 -1
- package/lib/types/ink/constants.js +9 -0
- package/lib/types/ink/events/click-event.d.ts +7 -16
- package/lib/types/ink/events/click-event.d.ts.map +1 -1
- package/lib/types/ink/events/click-event.js +16 -19
- package/lib/types/ink/events/event-handlers.d.ts +11 -1
- package/lib/types/ink/events/event-handlers.d.ts.map +1 -1
- package/lib/types/ink/events/event-handlers.js +2 -0
- package/lib/types/ink/events/input-event.d.ts +9 -0
- package/lib/types/ink/events/input-event.d.ts.map +1 -1
- package/lib/types/ink/events/input-event.js +4 -0
- package/lib/types/ink/events/pointer-event.d.ts +61 -0
- package/lib/types/ink/events/pointer-event.d.ts.map +1 -0
- package/lib/types/ink/events/pointer-event.js +61 -0
- package/lib/types/ink/events/wheel-event.d.ts +17 -0
- package/lib/types/ink/events/wheel-event.d.ts.map +1 -0
- package/lib/types/ink/events/wheel-event.js +20 -0
- package/lib/types/ink/geometry-trace.d.ts +51 -0
- package/lib/types/ink/geometry-trace.d.ts.map +1 -0
- package/lib/types/ink/geometry-trace.js +83 -0
- package/lib/types/ink/hit-test.d.ts +56 -1
- package/lib/types/ink/hit-test.d.ts.map +1 -1
- package/lib/types/ink/hit-test.js +146 -8
- package/lib/types/ink/ink.d.ts +58 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +230 -29
- package/lib/types/ink/node-cache.d.ts +5 -0
- package/lib/types/ink/node-cache.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.d.ts +8 -0
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +101 -12
- package/lib/types/ink/render-node-to-output.d.ts +10 -0
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +111 -18
- package/lib/types/ink/timeline-rail.d.ts +138 -0
- package/lib/types/ink/timeline-rail.d.ts.map +1 -0
- package/lib/types/ink/timeline-rail.js +179 -0
- package/lib/types/ink/wrap-text.d.ts.map +1 -1
- package/lib/types/ink/wrap-text.js +47 -5
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +819 -337
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
- package/lib/types/screens/SessionBrowser.js +56 -29
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +64 -22
- package/lib/types/screens/StatusLine.d.ts +2 -1
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +17 -1
- package/lib/types/screens/TrajectoryScene.d.ts.map +1 -1
- package/lib/types/screens/TrajectoryScene.js +81 -14
- package/lib/types/screens/chatOverlay.d.ts +221 -0
- package/lib/types/screens/chatOverlay.d.ts.map +1 -0
- package/lib/types/screens/chatOverlay.js +114 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +42 -6
- package/lib/types/tuiDisplayPrefs.d.ts +15 -0
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +9 -0
- package/lib/types/utils/debug.d.ts +10 -0
- package/lib/types/utils/debug.d.ts.map +1 -1
- package/lib/types/utils/debug.js +24 -0
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +1 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -50,6 +50,10 @@
|
|
|
50
50
|
- **终端原生交互**:流式 Markdown、结构化工具卡、命令与文件补全、`@` 文件引用
|
|
51
51
|
(消息任意位置补全,文本附加内容,PNG/JPEG/WebP/GIF 作为持久图片块发送)、历史搜索、消息选择、
|
|
52
52
|
inline/alternate-screen 两种渲染模式,以及 `/lang` 中英界面语言切换。
|
|
53
|
+
- **时间线导航**:Grok 式轮次 rail 覆盖**全部轮次(含折叠轮)**——折叠窗口只露
|
|
54
|
+
最近几轮时 rail 仍全景可达,点击刻度揭示对应轮并滚动到位;未钉底时
|
|
55
|
+
`Enter`/`End` 一键回底(远距一步到位不闪空白),新消息 pill 常驻可点击;
|
|
56
|
+
右侧 gutter 支持 timeline / scrollbar / hidden 三态设置。
|
|
53
57
|
- **可观察的 Agent 状态**:实时工作状态、上下文分段进度、TPS、缓存命中率、
|
|
54
58
|
推理等级、输入/输出 token 与 Git/会话信息。
|
|
55
59
|
- **完整会话工作流**:`/resume`、`/new`、`/compact`、`/export`、`/btw` 侧问、
|
|
@@ -57,7 +61,10 @@
|
|
|
57
61
|
- **DSH 官方能力接入**:Agent preset、Skills、MCP、Goals、Todos、子代理、
|
|
58
62
|
`ask_user_question` 问卷都通过现有服务或注册表连接。
|
|
59
63
|
- **为长会话设计**:事件驱动投影、差分终端输出、消息虚拟化、回放合并与有界缓存,
|
|
60
|
-
|
|
64
|
+
避免渲染成本和内存随会话无限增长;热路径**指纹缓存做到每帧零分配**
|
|
65
|
+
(3200 行会话每 16ms tick 省下 ~200KB GC 压力),wrapText 与 markdown
|
|
66
|
+
token 走全局 LRU 跨挂载复用,主屏打开分帧补画、渲染折叠窗口 300→120 行,
|
|
67
|
+
长会话恢复直达内容(跳过开场动画、落点锚定最新消息末行)。
|
|
61
68
|
|
|
62
69
|
## 界面预览
|
|
63
70
|
|
|
@@ -101,13 +108,18 @@ Herdr 的 `agent start --kind dsh-tui`、session 身份与服务重启后自动
|
|
|
101
108
|
已上架 VS Code Marketplace**)见
|
|
102
109
|
[在 VS Code 中运行 dsh-TUI](docs/vscode.md)。
|
|
103
110
|
|
|
104
|
-
TUI
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
TUI 启动后会在后台检查当前 registry 是否有新版本;发现更新时直接输入 `/update`
|
|
112
|
+
即可一键升级。`/update` 会更新当前 `dsh-tui` profile 中实际运行的 runtime、校验
|
|
113
|
+
安装结果,然后自动重启并恢复当前会话。
|
|
114
|
+
|
|
115
|
+
通过全局 `dsh-tui` 命令启动时,新版会自动将全局入口迁移/对齐为 delegating
|
|
116
|
+
launcher(委托式启动器):全局命令只负责转交给 profile 内副本,后续启动逻辑始终
|
|
117
|
+
跟随 profile 版本。
|
|
118
|
+
|
|
119
|
+
正常情况下不再需要额外执行:
|
|
108
120
|
|
|
109
121
|
```sh
|
|
110
|
-
npm install -g @deepseek-harness-tui/dsh-tui
|
|
122
|
+
npm install -g @deepseek-harness-tui/dsh-tui
|
|
111
123
|
```
|
|
112
124
|
|
|
113
125
|
旧版 `dsh-cc-tui` / `cc-tui` profile 的迁移命令与兼容数据说明见
|
|
@@ -147,13 +159,14 @@ npm install -g @deepseek-harness-tui/dsh-tui@<profile-version>
|
|
|
147
159
|
`⌘` 需终端支持扩展键盘协议(iTerm2 / kitty / WezTerm / ghostty / tmux);
|
|
148
160
|
macOS 自带 Terminal.app 会自行消费 `⌘` 快捷键,请继续使用 `Ctrl`。
|
|
149
161
|
|
|
150
|
-
|
|
162
|
+
**鼠标(全屏模式默认开启;`fullscreen: false` 可退回 inline 主屏模式)**
|
|
151
163
|
|
|
152
164
|
| 操作 | 功能 |
|
|
153
165
|
|---|---|
|
|
154
166
|
| 拖拽选择 | 应用内文本选区,**松开即复制**(OSC 52 + `wl-copy`/`xclip`/`xsel` 原生兜底;tmux 内走 `load-buffer -w`),复制后自动取消选区并弹出「已复制 N 个字符」提示 |
|
|
155
167
|
| 双击 / 三击 | 选词 / 选行,同样即选即复制 |
|
|
156
|
-
| 滚轮 | 仅在 fullscreen 且鼠标跟踪启用时:Help 打开时滚动帮助,否则滚动消息列表(±3
|
|
168
|
+
| 滚轮 | 仅在 fullscreen 且鼠标跟踪启用时:Help 打开时滚动帮助,否则滚动消息列表(±3 行/格);inline 模式不会把滚轮事件交给 TUI |
|
|
169
|
+
| 单击时间线 rail 刻度 | 跳到对应轮次——rail 覆盖全部轮次(含折叠轮),点折叠刻度会先揭示该轮再滚动到位 |
|
|
157
170
|
| `Esc` | 拖拽进行中取消选区(不复制) |
|
|
158
171
|
| 单击消息行 | 展开/收起该行 |
|
|
159
172
|
| 单击「加载更早消息」/「ctrl+e 显示前 N 条」 | 加载更早消息 / 展开全部 |
|
|
@@ -259,6 +272,12 @@ compaction 和持久化继续由 DSH 服务拥有。更详细的模块边界与
|
|
|
259
272
|
- **事件驱动渲染**:`session/event` 事件流 → 增量差分渲染,滚动状态独立维护。
|
|
260
273
|
- **布局级虚拟化**:长会话的每帧成本从 O(全会话) 降到 O(可视窗口)——屏幕外的
|
|
261
274
|
消息行渲染为"量高占位符",其子树完全不参与布局。
|
|
275
|
+
- **零分配热路径**:visibleRows 管线(切片/过滤/边距)按 rows 身份、长度与
|
|
276
|
+
Uint8Array 流位指纹缓存——滚动 tick 上零数组/Map 分配,turn 落定的原地
|
|
277
|
+
写入也即时触发重建(空行过滤不滞后);wrapText 与 markdown token 走全局
|
|
278
|
+
LRU,跨挂载复用测量结果。
|
|
279
|
+
- **分帧补画与落点锚定**:主屏打开先挂尾部窗口再分帧补画历史;`/resume` 后
|
|
280
|
+
以最新消息末行可见且可达为终态断言,长会话恢复跳过开场动画直达内容。
|
|
262
281
|
- **上下文进度条**:参考 pi-nano-context 算法(最大余数法分段着色 + 多级缩略读数)。
|
|
263
282
|
- **TPS 仪表**:参考 pi-tps-meter——流式 1/8 格 gauge、历史 min-max sparkline、
|
|
264
283
|
速度语义色(≥50 绿 / ≥20 黄 / <20 红)。
|
package/bin/dsh-tui.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AAElF,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,qBAAqB,EAAE,CAAA;AAEpG;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AAElF,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,qEAAqE;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,KAAK,SAAS,qBAAqB,EAAE,CAAA;AAEpG;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,YAAY,EA0DxC,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAElD,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAEpD,CAAA;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAIhG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAIhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,OAAO,CAKT;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,SAAS,YAAY,EAAmB,GAC7C,YAAY,EAAE,CAKhB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,SAAS,YAAY,EAAmB,EAC/C,QAAQ,GAAE,eAA0B,GACnC,iBAAiB,EAAE,CAuCrB"}
|
package/lib/types/commands.js
CHANGED
|
@@ -44,7 +44,6 @@ export const LOCAL_COMMANDS = [
|
|
|
44
44
|
{ name: 'provider', description: 'Add an LLM provider (catalog or custom API endpoint)' },
|
|
45
45
|
{ name: 'login', description: 'Show API credential status' },
|
|
46
46
|
{ name: 'logout', description: 'Clear the API credential' },
|
|
47
|
-
{ name: 'permissions', description: 'Show permission policy status' },
|
|
48
47
|
{ name: 'add-dir', description: 'Show the filesystem policy scope' },
|
|
49
48
|
{ name: 'hooks', description: 'Show hooks status' },
|
|
50
49
|
{ name: 'mcp', description: 'Show MCP status' },
|
|
@@ -154,7 +153,10 @@ export function completeCommands(input, roots = LOCAL_COMMANDS, children = () =>
|
|
|
154
153
|
if (!input.startsWith('/') || /[\r\n]/u.test(input))
|
|
155
154
|
return [];
|
|
156
155
|
const body = input.slice(1);
|
|
157
|
-
|
|
156
|
+
// Token charset includes `. : /` so provider/model specs (e.g.
|
|
157
|
+
// `deepseek/deepseek-v4-flash`, `openai/gpt-4.1`) survive as ONE token —
|
|
158
|
+
// the /model completion matches its candidates against the whole spec.
|
|
159
|
+
if (!/^[a-z0-9_.:\/-]*(?:[\t ]+[a-z0-9_.:\/-]*)*$/iu.test(body))
|
|
158
160
|
return [];
|
|
159
161
|
const trailingSeparator = /[\t ]$/u.test(body);
|
|
160
162
|
const tokens = body.split(/[\t ]+/u);
|
|
@@ -6,8 +6,11 @@ import React from 'react';
|
|
|
6
6
|
* frame preview, `❯` focus pointer and `✓` on the active preset. Enter
|
|
7
7
|
* applies through `channel.setActivityFrames`, Esc cancels.
|
|
8
8
|
*/
|
|
9
|
-
export declare function ActivityPicker({ focusIndex, currentPreset, }: {
|
|
9
|
+
export declare function ActivityPicker({ focusIndex, currentPreset, onPick, }: {
|
|
10
10
|
focusIndex: number;
|
|
11
11
|
currentPreset: string | undefined;
|
|
12
|
+
/** Mouse pick (fullscreen): clicked row's absolute index (Chat applies
|
|
13
|
+
* the same code path as the keyboard Enter). */
|
|
14
|
+
onPick?: (index: number) => void;
|
|
12
15
|
}): React.ReactNode;
|
|
13
16
|
//# sourceMappingURL=ActivityPicker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityPicker.d.ts","sourceRoot":"","sources":["../../../src/components/ActivityPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,aAAa,
|
|
1
|
+
{"version":3,"file":"ActivityPicker.d.ts","sourceRoot":"","sources":["../../../src/components/ActivityPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAQzB;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,aAAa,EACb,MAAM,GACP,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC;qDACiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA2BlB"}
|
|
@@ -12,12 +12,12 @@ import { FRAME_PRESETS, PRESET_NAMES } from './activityFrames.js';
|
|
|
12
12
|
* frame preview, `❯` focus pointer and `✓` on the active preset. Enter
|
|
13
13
|
* applies through `channel.setActivityFrames`, Esc cancels.
|
|
14
14
|
*/
|
|
15
|
-
export function ActivityPicker({ focusIndex, currentPreset, }) {
|
|
15
|
+
export function ActivityPicker({ focusIndex, currentPreset, onPick, }) {
|
|
16
16
|
return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('picker-title-activity') }) }), _jsx(Select, { options: PRESET_NAMES.map(name => ({
|
|
17
17
|
value: name,
|
|
18
18
|
label: name,
|
|
19
19
|
description: name === 'random'
|
|
20
20
|
? t('activity-random-each-preset')
|
|
21
21
|
: FRAME_PRESETS[name].frames.slice(0, 5).join(' '),
|
|
22
|
-
})), focusIndex: focusIndex, selectedValue: currentPreset }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }) }));
|
|
22
|
+
})), focusIndex: focusIndex, selectedValue: currentPreset, onPick: onPick ? index => onPick(index) : undefined }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-confirm-exit') }) })] }) }));
|
|
23
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BtwPanel.d.ts","sourceRoot":"","sources":["../../../src/components/BtwPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,MAAM,EACN,KAAK,EACL,SAAS,EACT,OAAO,EACP,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"BtwPanel.d.ts","sourceRoot":"","sources":["../../../src/components/BtwPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,MAAM,EACN,KAAK,EACL,SAAS,EACT,OAAO,EACP,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CA+DlB"}
|
|
@@ -43,5 +43,5 @@ export function BtwPanel({ question, answer, error, streaming, onClose, onCopy,
|
|
|
43
43
|
event.stopImmediatePropagation();
|
|
44
44
|
});
|
|
45
45
|
const settled = answer !== '' || error !== undefined;
|
|
46
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "warning", bold: true, children: "/btw " }), _jsx(Text, { dimColor: true, children: question })] }), _jsx(Box, { flexDirection: "column", maxHeight: Math.max(5, rows - 8), children: _jsx(Box, { marginLeft: 2, flexDirection: "column", flexGrow: 1, children: _jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: error !== undefined ? (_jsx(Text, { color: "error", children: error })) : answer !== '' ? (_jsx(Markdown, { cacheTokens: false, children: answer })) : (_jsxs(Box, { children: [_jsx(SpinnerGlyph, { frame: frame, messageColor: "warning" }), _jsxs(Text, { color: "warning", children: [" ", t('btw-answering')] })] })) }) }) }), _jsx(Text, { dimColor: true, children: settled ? t('btw-hint-done') : streaming ? t('btw-hint-loading') : t('btw-hint-done') })] }));
|
|
46
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "warning", bold: true, children: "/btw " }), _jsx(Text, { dimColor: true, children: question })] }), _jsx(Box, { flexDirection: "column", maxHeight: Math.max(5, rows - 8), children: _jsx(Box, { marginLeft: 2, flexDirection: "column", flexGrow: 1, children: _jsx(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: error !== undefined ? (_jsx(Text, { color: "error", children: error })) : answer !== '' ? (_jsx(Markdown, { cacheTokens: false, children: answer })) : (_jsxs(Box, { children: [_jsx(SpinnerGlyph, { frame: frame, messageColor: "warning" }), _jsxs(Text, { color: "warning", children: [" ", t('btw-answering')] })] })) }) }) }), _jsx(Box, { onClick: settled ? onCopy : undefined, children: _jsx(Text, { dimColor: true, children: settled ? t('btw-hint-done') : streaming ? t('btw-hint-loading') : t('btw-hint-done') }) })] }));
|
|
47
47
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { SubagentRow } from '../../dsh-adapter/channel.js';
|
|
3
|
+
import type { ClickEvent } from '../../ink/events/click-event.js';
|
|
3
4
|
/**
|
|
4
5
|
* Borderless, fixed-height activity card embedded directly in the transcript
|
|
5
6
|
* (Kimi Code visual language). Running: header + one-line current tool + a
|
|
@@ -14,6 +15,6 @@ export declare function SubagentMessage({ subagent, addMargin, activityFrames, o
|
|
|
14
15
|
addMargin: boolean;
|
|
15
16
|
activityFrames?: string;
|
|
16
17
|
isExpanded: boolean;
|
|
17
|
-
onClick:
|
|
18
|
+
onClick?(event: ClickEvent): void;
|
|
18
19
|
}): React.ReactNode;
|
|
19
20
|
//# sourceMappingURL=SubagentMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubagentMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/Chat/SubagentMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"SubagentMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/Chat/SubagentMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAO/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAyCjE;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,EAAE;IAChF,QAAQ,EAAE,WAAW,CAAA;IACrB,SAAS,EAAE,OAAO,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,GAAG,KAAK,CAAC,SAAS,CAwFlB"}
|
|
@@ -58,10 +58,16 @@ function clipLine(text, maxWidth) {
|
|
|
58
58
|
* setting as the main spinner.
|
|
59
59
|
*/
|
|
60
60
|
export function SubagentMessage({ subagent, addMargin, activityFrames, onClick }) {
|
|
61
|
-
const
|
|
61
|
+
const settled = subagent.status === 'completed' || subagent.status === 'failed' || subagent.status === 'cancelled';
|
|
62
|
+
// 动画订阅仅限运行中的卡片:settled 后传 null 退出共享 clock(keepAlive
|
|
63
|
+
// 归零 → interval 清除),否则历史里的每张完成卡片都以 120ms 永久驱动
|
|
64
|
+
// React commit。viewportRef 必须挂到根节点——useTerminalViewport 初始
|
|
65
|
+
// isVisible:true,ref 不挂就永远不修正(虚拟化滚出视口的卡片继续动画)。
|
|
66
|
+
const [viewportRef, time] = useAnimationFrame(settled ? null : 120);
|
|
62
67
|
const { columns } = useTerminalSize();
|
|
63
68
|
const info = status(subagent);
|
|
64
|
-
const
|
|
69
|
+
const [hovered, setHovered] = React.useState(false);
|
|
70
|
+
const clickable = onClick !== undefined;
|
|
65
71
|
const elapsed = subagent.completedAt ? subagent.durationMs : Date.now() - subagent.startedAt;
|
|
66
72
|
const lastRunning = [...subagent.toolCalls].reverse().find(tool => tool.status === 'running');
|
|
67
73
|
const previousDone = lastRunning
|
|
@@ -71,5 +77,7 @@ export function SubagentMessage({ subagent, addMargin, activityFrames, onClick }
|
|
|
71
77
|
const activity = settled ? [] : subagent.outputLines.slice(-WATERFALL_ROWS);
|
|
72
78
|
const runningGlyph = preset.frames[Math.floor(time / preset.intervalMs) % preset.frames.length] ?? '·';
|
|
73
79
|
const rowWidth = Math.max(20, (columns ?? 80) - WATERFALL_GUTTER);
|
|
74
|
-
|
|
80
|
+
// 点击打开详情场景;hover 不刷整行背景(转录视觉保持安静),只把状态
|
|
81
|
+
// glyph 提亮为品牌色作为可点指示。
|
|
82
|
+
return _jsxs(Box, { flexDirection: "column", marginTop: addMargin ? 1 : 0, paddingLeft: 2, ref: viewportRef, onClick: onClick, onMouseEnter: clickable ? () => setHovered(true) : undefined, onMouseLeave: clickable ? () => setHovered(false) : undefined, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: hovered && clickable ? 'claude' : info.color, children: settled ? info.glyph : ` ${runningGlyph}` }), _jsx(Text, { bold: true, color: hovered && clickable ? 'claude' : undefined, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), subagent.effort && _jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { dimColor: true, children: subagent.effort })] }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { dimColor: true, children: duration(elapsed) }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { dimColor: true, children: tokens(subagent) }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsxs(Text, { dimColor: true, children: [subagent.toolCalls.length, " tools"] }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: info.color, children: info.label })] }), !settled && (lastRunning ?? previousDone) !== undefined && (_jsx(Text, { wrap: "truncate", children: lastRunning !== undefined ? (_jsxs(_Fragment, { children: [previousDone !== undefined && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { color: "success", children: "\u2713" }), _jsx(Text, { color: toolNameColor(previousDone.name), children: previousDone.name }), _jsx(Text, { dimColor: true, children: ' · ' })] })), lastRunning.argsPreview === undefined && (_jsx(Text, { color: toolNameColor(lastRunning.name), children: lastRunning.name })), lastRunning.argsPreview !== undefined && (_jsxs(_Fragment, { children: [_jsx(Text, { color: toolNameColor(lastRunning.name), children: lastRunning.name }), _jsx(Text, { dimColor: true, children: ` (${clipLine(lastRunning.argsPreview.replace(/\s+/g, ' ').trim(), Math.max(10, rowWidth - lastRunning.name.length - 6))})` })] }))] })) : (previousDone !== undefined && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { color: "success", children: "\u2713" }), _jsx(Text, { color: toolNameColor(previousDone.name), children: previousDone.name }), previousDone.argsPreview !== undefined && (_jsx(Text, { dimColor: true, children: ` (${clipLine(previousDone.argsPreview.replace(/\s+/g, ' ').trim(), Math.max(10, rowWidth - previousDone.name.length - 6))})` }))] }))) })), !settled && Array.from({ length: WATERFALL_ROWS }, (_, index) => (_jsx(Text, { dimColor: true, wrap: "truncate", children: ` │ ${clipLine(activity[index] ?? '', rowWidth)}` }, `${subagent.agentId}-wf-${index}`))), settled && subagent.status === 'failed' && subagent.error && (_jsx(Text, { color: "error", wrap: "truncate", children: ` └ ${clipLine(subagent.error, rowWidth)}` }))] });
|
|
75
83
|
}
|
|
@@ -2,15 +2,32 @@ import React from 'react';
|
|
|
2
2
|
import type { LocalCommand } from '../commands.js';
|
|
3
3
|
/**
|
|
4
4
|
* The slash-command suggestion overlay, mirroring Claude Code's
|
|
5
|
-
* `PromptInputFooterSuggestions.tsx` (command layout only)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* `PromptInputFooterSuggestions.tsx` (command layout only) wrapped in the
|
|
6
|
+
* shared rounded `SuggestionCard`:
|
|
7
|
+
*
|
|
8
|
+
* ╭─ 命令 · 共 12 项 ──────────────────────╮
|
|
9
|
+
* │ ❯ compact Compact the conversation … │ ← 选中:❯ + 加粗 + suggestion 色
|
|
10
|
+
* │ compare Compare selected messages … │ ← 名字里命中输入的段提亮
|
|
11
|
+
* │ ↑2 · ↓3 │ ← 仅当列表被窗口裁剪
|
|
12
|
+
* ╰─────────────────────────────────────────╯
|
|
13
|
+
*
|
|
14
|
+
* 未选中行整体 dim,唯独名字里匹配当前查询 token 的前缀以正常亮度提亮
|
|
15
|
+
* (bold 与 dim 在终端互斥,故提亮用非 dim 而非加粗);选中行整行
|
|
16
|
+
* suggestion 色、名字加粗、前置 ❯ 指针。
|
|
8
17
|
*/
|
|
9
|
-
export declare function CommandSuggestions({ commands, selectedIndex, columns, }: {
|
|
18
|
+
export declare function CommandSuggestions({ commands, selectedIndex, columns, query, accent, onPick, onWheelStep, }: {
|
|
10
19
|
commands: readonly (LocalCommand & {
|
|
11
20
|
descriptionKey?: string;
|
|
12
21
|
})[];
|
|
13
22
|
selectedIndex: number;
|
|
14
23
|
columns: number;
|
|
24
|
+
/** 原始 `/…` 输入;其最后一段 token 用于名字前缀高亮。 */
|
|
25
|
+
query?: string;
|
|
26
|
+
accent?: 'promptBorder' | 'planMode';
|
|
27
|
+
/** 鼠标点击行(fullscreen):上报过滤后列表的绝对索引(与键盘
|
|
28
|
+
* selectedIndex 同一索引空间),接受路径由 PromptInput 复用。 */
|
|
29
|
+
onPick?: (index: number) => void;
|
|
30
|
+
/** 滚轮步进(fullscreen):±1 移动选中行。 */
|
|
31
|
+
onWheelStep?: (step: 1 | -1) => void;
|
|
15
32
|
}): React.ReactNode;
|
|
16
33
|
//# sourceMappingURL=CommandSuggestions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandSuggestions.d.ts","sourceRoot":"","sources":["../../../src/components/CommandSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"CommandSuggestions.d.ts","sourceRoot":"","sources":["../../../src/components/CommandSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAMlD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,aAAa,EACb,OAAO,EACP,KAAU,EACV,MAAM,EACN,MAAM,EACN,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC,YAAY,GAAG;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAA;IACjE,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,cAAc,GAAG,UAAU,CAAA;IACpC;sDACkD;IAClD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,iCAAiC;IACjC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAA;CACrC,GAAG,KAAK,CAAC,SAAS,CAmFlB"}
|
|
@@ -1,35 +1,62 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '../ui.js';
|
|
3
3
|
import { stringWidth } from '../ink/stringWidth.js';
|
|
4
4
|
import { truncateToWidth } from '../ink/truncateToWidth.js';
|
|
5
5
|
import { localizedDescription } from '../commands.js';
|
|
6
|
+
import { t } from '../i18n.js';
|
|
7
|
+
import { POINTER } from '../cc/figures.js';
|
|
8
|
+
import { SuggestionCard, cardContentWidth, splitQueryMatch } from './SuggestionCard.js';
|
|
6
9
|
/**
|
|
7
10
|
* The slash-command suggestion overlay, mirroring Claude Code's
|
|
8
|
-
* `PromptInputFooterSuggestions.tsx` (command layout only)
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
+
* `PromptInputFooterSuggestions.tsx` (command layout only) wrapped in the
|
|
12
|
+
* shared rounded `SuggestionCard`:
|
|
13
|
+
*
|
|
14
|
+
* ╭─ 命令 · 共 12 项 ──────────────────────╮
|
|
15
|
+
* │ ❯ compact Compact the conversation … │ ← 选中:❯ + 加粗 + suggestion 色
|
|
16
|
+
* │ compare Compare selected messages … │ ← 名字里命中输入的段提亮
|
|
17
|
+
* │ ↑2 · ↓3 │ ← 仅当列表被窗口裁剪
|
|
18
|
+
* ╰─────────────────────────────────────────╯
|
|
19
|
+
*
|
|
20
|
+
* 未选中行整体 dim,唯独名字里匹配当前查询 token 的前缀以正常亮度提亮
|
|
21
|
+
* (bold 与 dim 在终端互斥,故提亮用非 dim 而非加粗);选中行整行
|
|
22
|
+
* suggestion 色、名字加粗、前置 ❯ 指针。
|
|
11
23
|
*/
|
|
12
|
-
export function CommandSuggestions({ commands, selectedIndex, columns, }) {
|
|
24
|
+
export function CommandSuggestions({ commands, selectedIndex, columns, query = '', accent, onPick, onWheelStep, }) {
|
|
13
25
|
if (commands.length === 0)
|
|
14
26
|
return null;
|
|
15
|
-
// Cap the command name column at 40% of
|
|
16
|
-
// description has space (same as Claude Code).
|
|
17
|
-
const
|
|
27
|
+
// Cap the command name column at 40% of the card's content width to ensure
|
|
28
|
+
// the description has space (same ratio as Claude Code).
|
|
29
|
+
const usable = cardContentWidth(columns);
|
|
30
|
+
const maxNameWidth = Math.floor(usable * 0.4);
|
|
18
31
|
const nameWidth = Math.min(Math.max(...commands.map(c => stringWidth(c.name))) + 5, maxNameWidth);
|
|
19
32
|
const maxVisible = 5;
|
|
20
33
|
const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), commands.length - maxVisible));
|
|
21
34
|
const visible = commands.slice(startIndex, startIndex + maxVisible);
|
|
22
|
-
|
|
35
|
+
const above = startIndex;
|
|
36
|
+
const below = commands.length - (startIndex + visible.length);
|
|
37
|
+
// 前缀高亮取查询的最后一段 token(`/plan of` 高亮 `of` 命中的段)。
|
|
38
|
+
const queryToken = query.replace(/^\//, '').match(/[^ \t]*$/)?.[0] ?? '';
|
|
39
|
+
const title = `${t('sugg-commands-title')} · ${t('sugg-count', { n: commands.length })}`;
|
|
40
|
+
const footer = above > 0 || below > 0
|
|
41
|
+
? [
|
|
42
|
+
above > 0 ? t('sugg-more-above', { n: above }) : null,
|
|
43
|
+
below > 0 ? t('sugg-more-below', { n: below }) : null,
|
|
44
|
+
]
|
|
45
|
+
.filter((part) => part !== null)
|
|
46
|
+
.join(' · ')
|
|
47
|
+
: null;
|
|
48
|
+
return (_jsx(SuggestionCard, { title: title, columns: columns, accent: accent, footer: footer, onRowPick: onPick ? index => onPick(startIndex + index) : undefined, onWheelStep: onWheelStep, rows: visible.map(command => {
|
|
23
49
|
const isSelected = command.name === commands[selectedIndex]?.name;
|
|
24
|
-
const padded = command.name +
|
|
25
|
-
' '.repeat(Math.max(0, nameWidth - stringWidth(command.name)));
|
|
26
50
|
const tagText = command.tag ? `[${command.tag}] ` : '';
|
|
27
51
|
const tagWidth = stringWidth(tagText);
|
|
28
|
-
|
|
52
|
+
// 行预算:内容宽 − 前导空格 1 − 指针列 2。
|
|
53
|
+
const descriptionWidth = Math.max(0, usable - 3 - nameWidth - tagWidth);
|
|
29
54
|
const rawDescription = localizedDescription(command);
|
|
30
55
|
const description = stringWidth(rawDescription) > descriptionWidth
|
|
31
56
|
? truncateToWidth(rawDescription, descriptionWidth - 1) + '…'
|
|
32
57
|
: rawDescription;
|
|
33
|
-
|
|
58
|
+
const parts = splitQueryMatch(command.name, queryToken);
|
|
59
|
+
const padAfter = Math.max(0, nameWidth - stringWidth(command.name));
|
|
60
|
+
return (_jsxs(Text, { wrap: "truncate", children: [' ', isSelected ? (_jsx(Text, { color: "suggestion", bold: true, children: `${POINTER} ${command.name}${' '.repeat(padAfter)}` })) : parts ? (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ` ${parts.before}` }), _jsx(Text, { children: parts.match }), _jsx(Text, { dimColor: true, children: `${parts.after}${' '.repeat(padAfter)}` })] })) : (_jsx(Text, { dimColor: true, children: ` ${command.name}${' '.repeat(padAfter)}` })), tagText ? _jsx(Text, { dimColor: true, children: tagText }) : null, _jsx(Text, { color: isSelected ? 'suggestion' : undefined, dimColor: !isSelected, children: description })] }, command.name));
|
|
34
61
|
}) }));
|
|
35
62
|
}
|
|
@@ -7,9 +7,12 @@ import type { EffortOption } from '../dsh-adapter/channel.js';
|
|
|
7
7
|
* or Esc just closes it). The current level carries `✓`; the focused level's
|
|
8
8
|
* description renders below the row.
|
|
9
9
|
*/
|
|
10
|
-
export declare function EffortSlider({ options, focusIndex, currentId, }: {
|
|
10
|
+
export declare function EffortSlider({ options, focusIndex, currentId, onPick, }: {
|
|
11
11
|
options: readonly EffortOption[];
|
|
12
12
|
focusIndex: number;
|
|
13
13
|
currentId: string | undefined;
|
|
14
|
+
/** Mouse pick (fullscreen): click a tier = move there and live-apply —
|
|
15
|
+
* the same semantics as the ←/→ keys (the slider IS the control). */
|
|
16
|
+
onPick?: (index: number) => void;
|
|
14
17
|
}): React.ReactNode;
|
|
15
18
|
//# sourceMappingURL=EffortSlider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EffortSlider.d.ts","sourceRoot":"","sources":["../../../src/components/EffortSlider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAE7D;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,UAAU,EACV,SAAS,
|
|
1
|
+
{"version":3,"file":"EffortSlider.d.ts","sourceRoot":"","sources":["../../../src/components/EffortSlider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAE7D;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,OAAO,EACP,UAAU,EACV,SAAS,EACT,MAAM,GACP,EAAE;IACD,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B;0EACsE;IACtE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA4ClB"}
|
|
@@ -11,7 +11,9 @@ import { HintLine } from './design-system/HintLine.js';
|
|
|
11
11
|
* or Esc just closes it). The current level carries `✓`; the focused level's
|
|
12
12
|
* description renders below the row.
|
|
13
13
|
*/
|
|
14
|
-
export function EffortSlider({ options, focusIndex, currentId, }) {
|
|
14
|
+
export function EffortSlider({ options, focusIndex, currentId, onPick, }) {
|
|
15
15
|
const focused = options[focusIndex];
|
|
16
|
-
return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('picker-title-effort') }) }), _jsx(Box, { flexDirection: "row", children: options.map((option, index) => (_jsxs(React.Fragment, { children: [index > 0 ? (_jsx(Text, { dimColor: true, children: " \u2500\u2500 " })) : null, _jsx(
|
|
16
|
+
return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: t('picker-title-effort') }) }), _jsx(Box, { flexDirection: "row", children: options.map((option, index) => (_jsxs(React.Fragment, { children: [index > 0 ? (_jsx(Text, { dimColor: true, children: " \u2500\u2500 " })) : null, _jsx(Box, { onClick: onPick ? () => onPick(index) : undefined, backgroundColor: onPick !== undefined && index !== focusIndex
|
|
17
|
+
? 'userMessageBackgroundHover'
|
|
18
|
+
: undefined, children: _jsx(Text, { inverse: index === focusIndex, bold: index === focusIndex, children: option.name }) }), option.id === currentId ? _jsx(Text, { color: "remember", children: "\u2713" }) : null] }, option.id))) }), focused?.description !== undefined ? (_jsx(Text, { dimColor: true, children: focused.description })) : null, _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-adjust-done') }) })] }) }));
|
|
17
19
|
}
|
|
@@ -87,7 +87,9 @@ function SelectDialog({ dialog, onDecide, onCancel, }) {
|
|
|
87
87
|
const { start, end } = listWindow(dialog.options.map(option => (option.description === undefined ? 1 : 2)), focusIndex, Math.max(terminalRows - 10, 2));
|
|
88
88
|
return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: dialog.title }) }), dialog.options.slice(start, end).map((option, index) => {
|
|
89
89
|
const absoluteIndex = start + index;
|
|
90
|
-
return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, description: option.description, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < dialog.options.length,
|
|
90
|
+
return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex, description: option.description, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < dialog.options.length,
|
|
91
|
+
// Click = decide this option (same as Enter on it).
|
|
92
|
+
onClick: () => onDecide(option.id), children: option.label }, option.id));
|
|
91
93
|
})] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-select-exit') }) })] }));
|
|
92
94
|
}
|
|
93
95
|
function ConfirmDialog({ dialog, onDecide, onCancel, }) {
|
|
@@ -123,7 +125,9 @@ function ConfirmDialog({ dialog, onDecide, onCancel, }) {
|
|
|
123
125
|
onDecide(focusRef.current === 0);
|
|
124
126
|
}
|
|
125
127
|
}, { isActive: true });
|
|
126
|
-
return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "remember", bold: true, children: dialog.title }), dialog.message !== undefined && _jsx(Text, { dimColor: true, children: dialog.message })] }), labels.map((label, index) => (_jsx(ListItem, { isFocused: index === focusIndex,
|
|
128
|
+
return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "remember", bold: true, children: dialog.title }), dialog.message !== undefined && _jsx(Text, { dimColor: true, children: dialog.message })] }), labels.map((label, index) => (_jsx(ListItem, { isFocused: index === focusIndex,
|
|
129
|
+
// Click = decide this row (same as Enter on it).
|
|
130
|
+
onClick: () => onDecide(index === 0), children: label }, label)))] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-select-exit') }) })] }));
|
|
127
131
|
}
|
|
128
132
|
function InputDialog({ dialog, onDecide, onCancel, }) {
|
|
129
133
|
const [value, setValue] = React.useState(dialog.initial);
|
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { FileCandidate } from '../utils/fileSuggestions.js';
|
|
3
3
|
/**
|
|
4
|
-
* The `@` file-completion overlay in CC's suggestion style
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* The `@` file-completion overlay in CC's suggestion style, wrapped in the
|
|
5
|
+
* shared rounded `SuggestionCard` (与 `/` 命令菜单同一视觉语言):
|
|
6
|
+
*
|
|
7
|
+
* ╭─ 文件 · 共 12 项 ──────────────────────╮
|
|
8
|
+
* │ ❯ ▸ src/components/ directory │
|
|
9
|
+
* │ + README.md file │
|
|
10
|
+
* ╰─────────────────────────────────────────╯
|
|
11
|
+
*
|
|
12
|
+
* Full relative path (directory suffix stripped) + a `file`/`directory`
|
|
13
|
+
* description; the selected row renders `❯` + bold in the theme's
|
|
14
|
+
* `suggestion` color, others dim with the query-matched prefix of the name
|
|
15
|
+
* highlighted (non-dim). The name column is padded to a fixed display width
|
|
16
|
+
* so the description column keeps its contract at narrow terminals (pinned
|
|
17
|
+
* by verify-cjk-truncate.tsx); every pad/truncate uses display width, so CJK
|
|
18
|
+
* names never split a glyph.
|
|
10
19
|
*/
|
|
11
|
-
export declare function FileSuggestions({ files, selectedIndex, columns, }: {
|
|
20
|
+
export declare function FileSuggestions({ files, selectedIndex, columns, query, accent, onPick, onWheelStep, }: {
|
|
12
21
|
files: readonly FileCandidate[];
|
|
13
22
|
selectedIndex: number;
|
|
14
23
|
columns: number;
|
|
24
|
+
/** `@` 触发 token 里已输入的查询(`mention.query`),用于名字前缀高亮。 */
|
|
25
|
+
query?: string;
|
|
26
|
+
accent?: 'promptBorder' | 'planMode';
|
|
27
|
+
/** 鼠标点击行(fullscreen):上报过滤后列表的绝对索引(与键盘
|
|
28
|
+
* selectedIndex 同一索引空间),接受路径由 PromptInput 复用。 */
|
|
29
|
+
onPick?: (index: number) => void;
|
|
30
|
+
/** 滚轮步进(fullscreen):±1 移动选中行。 */
|
|
31
|
+
onWheelStep?: (step: 1 | -1) => void;
|
|
15
32
|
}): React.ReactNode;
|
|
16
33
|
//# sourceMappingURL=FileSuggestions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSuggestions.d.ts","sourceRoot":"","sources":["../../../src/components/FileSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"FileSuggestions.d.ts","sourceRoot":"","sources":["../../../src/components/FileSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAKhE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,aAAa,EACb,OAAO,EACP,KAAU,EACV,MAAM,EACN,MAAM,EACN,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,SAAS,aAAa,EAAE,CAAA;IAC/B,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,cAAc,GAAG,UAAU,CAAA;IACpC;sDACkD;IAClD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,iCAAiC;IACjC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAA;CACrC,GAAG,KAAK,CAAC,SAAS,CAgFlB"}
|
|
@@ -1,28 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '../ui.js';
|
|
3
3
|
import { stringWidth } from '../ink/stringWidth.js';
|
|
4
4
|
import { truncateToWidth } from '../ink/truncateToWidth.js';
|
|
5
|
+
import { t } from '../i18n.js';
|
|
6
|
+
import { POINTER } from '../cc/figures.js';
|
|
7
|
+
import { SuggestionCard, cardContentWidth, splitQueryMatch } from './SuggestionCard.js';
|
|
5
8
|
/**
|
|
6
|
-
* The `@` file-completion overlay in CC's suggestion style
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* The `@` file-completion overlay in CC's suggestion style, wrapped in the
|
|
10
|
+
* shared rounded `SuggestionCard` (与 `/` 命令菜单同一视觉语言):
|
|
11
|
+
*
|
|
12
|
+
* ╭─ 文件 · 共 12 项 ──────────────────────╮
|
|
13
|
+
* │ ❯ ▸ src/components/ directory │
|
|
14
|
+
* │ + README.md file │
|
|
15
|
+
* ╰─────────────────────────────────────────╯
|
|
16
|
+
*
|
|
17
|
+
* Full relative path (directory suffix stripped) + a `file`/`directory`
|
|
18
|
+
* description; the selected row renders `❯` + bold in the theme's
|
|
19
|
+
* `suggestion` color, others dim with the query-matched prefix of the name
|
|
20
|
+
* highlighted (non-dim). The name column is padded to a fixed display width
|
|
21
|
+
* so the description column keeps its contract at narrow terminals (pinned
|
|
22
|
+
* by verify-cjk-truncate.tsx); every pad/truncate uses display width, so CJK
|
|
23
|
+
* names never split a glyph.
|
|
12
24
|
*/
|
|
13
|
-
export function FileSuggestions({ files, selectedIndex, columns, }) {
|
|
25
|
+
export function FileSuggestions({ files, selectedIndex, columns, query = '', accent, onPick, onWheelStep, }) {
|
|
14
26
|
if (files.length === 0)
|
|
15
27
|
return null;
|
|
28
|
+
const usable = cardContentWidth(columns);
|
|
16
29
|
const maxVisible = 6;
|
|
17
30
|
const safeIndex = Math.min(Math.max(0, selectedIndex), files.length - 1);
|
|
18
31
|
const startIndex = Math.max(0, Math.min(safeIndex - Math.floor(maxVisible / 2), Math.max(0, files.length - maxVisible)));
|
|
19
32
|
const visible = files.slice(startIndex, startIndex + maxVisible);
|
|
33
|
+
const above = startIndex;
|
|
34
|
+
const below = files.length - (startIndex + visible.length);
|
|
20
35
|
const nameOf = (file) => file.kind === 'directory' && file.path.endsWith('/')
|
|
21
36
|
? file.path.slice(0, -1)
|
|
22
37
|
: file.path;
|
|
23
38
|
const NAME_COLUMN = 20;
|
|
24
|
-
|
|
25
|
-
|
|
39
|
+
// 行预算:内容宽 − 前导空格 1 − 指针列 2 − 图标列 2。
|
|
40
|
+
const descriptionWidth = Math.max(0, usable - 25);
|
|
41
|
+
const title = `${t('sugg-files-title')} · ${t('sugg-count', { n: files.length })}`;
|
|
42
|
+
const footer = above > 0 || below > 0
|
|
43
|
+
? [
|
|
44
|
+
above > 0 ? t('sugg-more-above', { n: above }) : null,
|
|
45
|
+
below > 0 ? t('sugg-more-below', { n: below }) : null,
|
|
46
|
+
]
|
|
47
|
+
.filter((part) => part !== null)
|
|
48
|
+
.join(' · ')
|
|
49
|
+
: null;
|
|
50
|
+
return (_jsx(SuggestionCard, { title: title, columns: columns, accent: accent, footer: footer, onRowPick: onPick ? index => onPick(startIndex + index) : undefined, onWheelStep: onWheelStep, rows: visible.map(file => {
|
|
26
51
|
const isSelected = file.id === files[safeIndex]?.id;
|
|
27
52
|
const name = nameOf(file);
|
|
28
53
|
const icon = file.kind === 'directory' ? '▸ ' : '+ ';
|
|
@@ -31,6 +56,8 @@ export function FileSuggestions({ files, selectedIndex, columns, }) {
|
|
|
31
56
|
const renderedDescription = stringWidth(description) > descriptionWidth
|
|
32
57
|
? truncateToWidth(description, Math.max(0, descriptionWidth - 1)) + '…'
|
|
33
58
|
: description;
|
|
34
|
-
|
|
59
|
+
const parts = splitQueryMatch(name, query);
|
|
60
|
+
const pad = ' '.repeat(Math.max(1, NAME_COLUMN - stringWidth(name)));
|
|
61
|
+
return (_jsxs(Text, { wrap: "truncate", children: [' ', isSelected ? (_jsx(Text, { color: "suggestion", bold: true, children: `${POINTER} ${icon}${padded}` })) : parts ? (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ` ${icon}${parts.before}` }), _jsx(Text, { children: parts.match }), _jsx(Text, { dimColor: true, children: `${parts.after}${pad}` })] })) : (_jsx(Text, { dimColor: true, children: ` ${icon}${padded}` })), _jsx(Text, { color: isSelected ? 'suggestion' : undefined, dimColor: !isSelected, children: renderedDescription })] }, file.id));
|
|
35
62
|
}) }));
|
|
36
63
|
}
|
|
@@ -1,13 +1,37 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type { Channel } from '../dsh-adapter/channel.js';
|
|
2
|
+
import type { Channel, ChannelGoal } from '../dsh-adapter/channel.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compact goal chip for the status footer: phase glyph + rounds, colored by
|
|
5
|
+
* phase. `minimal` swaps the glyph for a text form, per the minimal-mode
|
|
6
|
+
* no-emoji contract.
|
|
7
|
+
*/
|
|
8
|
+
export declare function GoalStatusChip({ goal, minimal }: {
|
|
9
|
+
goal: ChannelGoal;
|
|
10
|
+
minimal?: boolean;
|
|
11
|
+
}): React.ReactNode;
|
|
3
12
|
/**
|
|
4
13
|
* Live goal + todo panel above the prompt input. Data rides on the channel:
|
|
5
14
|
* `channel.goal` is folded from `goal/change` context events and
|
|
6
15
|
* `channel.todos` from `todo/write` whole-list snapshots, so every model
|
|
7
16
|
* update re-renders this panel in real time (no polling). Renders nothing
|
|
8
17
|
* while both slots are empty.
|
|
18
|
+
*
|
|
19
|
+
* The todo section folds two ways: completed rows fold automatically once
|
|
20
|
+
* the agent goes idle (the header's `✓ done/total` count keeps the
|
|
21
|
+
* summary), and `collapsed` folds the whole section to that single header
|
|
22
|
+
* line any time — including mid-turn, where the line still previews the
|
|
23
|
+
* in-progress task. `onToggle` is the click affordance for the header row;
|
|
24
|
+
* the ctrl/cmd+q hotkey in Chat drives the same state.
|
|
25
|
+
*
|
|
26
|
+
* Goal elapsed time is the panel's own wall clock (started when the goal
|
|
27
|
+
* id first appears, frozen on completion) — deliberately not derived from
|
|
28
|
+
* session-event timestamps.
|
|
9
29
|
*/
|
|
10
|
-
export declare function GoalTodoPanel({ channel }: {
|
|
30
|
+
export declare function GoalTodoPanel({ channel, collapsed, onToggle, }: {
|
|
11
31
|
channel: Channel;
|
|
32
|
+
/** Fold the whole todo section to its summary header line. */
|
|
33
|
+
collapsed?: boolean;
|
|
34
|
+
/** Toggle the fold (click on the header row; shares the hotkey state). */
|
|
35
|
+
onToggle?: () => void;
|
|
12
36
|
}): React.ReactNode;
|
|
13
37
|
//# sourceMappingURL=GoalTodoPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoalTodoPanel.d.ts","sourceRoot":"","sources":["../../../src/components/GoalTodoPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"GoalTodoPanel.d.ts","sourceRoot":"","sources":["../../../src/components/GoalTodoPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAiB,MAAM,2BAA2B,CAAA;AAmCpF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,OAAe,EAAE,EAAE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAQnH;AA2CD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,SAAiB,EACjB,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,GAAG,KAAK,CAAC,SAAS,CAmIlB"}
|