@deepseek-harness-tui/dsh-tui 0.7.1 → 0.7.3
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 +7 -5
- package/bin/dsh-tui.js +42 -1
- package/cordis.patch.yml +10 -0
- package/lib/types/commands.js +1 -1
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +6 -1
- package/lib/types/components/HistorySearchDialog.d.ts.map +1 -1
- package/lib/types/components/HistorySearchDialog.js +5 -1
- package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
- package/lib/types/components/LoadedContextPanel.js +1 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +5 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +52 -74
- package/lib/types/components/SearchBox.d.ts +4 -0
- package/lib/types/components/SearchBox.d.ts.map +1 -1
- package/lib/types/components/SearchBox.js +101 -2
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts +4 -1
- package/lib/types/components/Spinner/SpinnerAnimationRow.d.ts.map +1 -1
- package/lib/types/components/Spinner/SpinnerAnimationRow.js +10 -5
- package/lib/types/components/WorkingSpinner.d.ts +3 -1
- package/lib/types/components/WorkingSpinner.d.ts.map +1 -1
- package/lib/types/components/WorkingSpinner.js +2 -2
- package/lib/types/components/design-system/ThemedText.d.ts +9 -3
- package/lib/types/components/design-system/ThemedText.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedText.js +3 -5
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +52 -8
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +12 -3
- package/lib/types/components/sessions/SessionListRow.d.ts +26 -0
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionListRow.js +35 -0
- package/lib/types/components/sessions/SessionPreview.d.ts +25 -0
- package/lib/types/components/sessions/SessionPreview.d.ts.map +1 -0
- package/lib/types/components/sessions/SessionPreview.js +60 -0
- package/lib/types/dsh-adapter/channel.d.ts +19 -4
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +76 -82
- package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +8 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +1 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +10 -1
- package/lib/types/dsh-adapter/packaged-presets.d.ts +22 -0
- package/lib/types/dsh-adapter/packaged-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/packaged-presets.js +93 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +53 -7
- package/lib/types/dsh-adapter/presets.d.ts +16 -0
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +23 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/providerWizard.js +3 -1
- package/lib/types/dsh-adapter/sessions/digest.d.ts +29 -0
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/digest.js +224 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts +100 -0
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/frames.js +246 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts +50 -0
- package/lib/types/dsh-adapter/sessions/header.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/header.js +62 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts +17 -0
- package/lib/types/dsh-adapter/sessions/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/index.js +15 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts +41 -0
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/list.js +213 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts +48 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/store.js +164 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts +142 -0
- package/lib/types/dsh-adapter/sessions/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/types.js +14 -0
- package/lib/types/i18n.d.ts +150 -22
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +45 -9
- package/lib/types/ink/components/Text.d.ts +9 -1
- package/lib/types/ink/components/Text.d.ts.map +1 -1
- package/lib/types/ink/components/Text.js +7 -6
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +4 -2
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +76 -9
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +4 -1
- package/lib/types/ink/terminal.d.ts +40 -4
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +57 -5
- package/lib/types/ink/termio/tokenize.d.ts +6 -0
- package/lib/types/ink/termio/tokenize.d.ts.map +1 -1
- package/lib/types/ink/termio/tokenize.js +18 -4
- package/lib/types/screens/Chat.d.ts +3 -2
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +104 -160
- package/lib/types/screens/SessionBrowser.d.ts +37 -0
- package/lib/types/screens/SessionBrowser.d.ts.map +1 -0
- package/lib/types/screens/SessionBrowser.js +452 -0
- package/lib/types/sessionHistory.d.ts +0 -8
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessions/format.d.ts +117 -0
- package/lib/types/sessions/format.d.ts.map +1 -0
- package/lib/types/sessions/format.js +244 -0
- package/lib/types/sessions/view.d.ts +125 -0
- package/lib/types/sessions/view.d.ts.map +1 -0
- package/lib/types/sessions/view.js +234 -0
- package/lib/types/update.d.ts +21 -7
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +66 -16
- package/lib/types/utils/paths.d.ts.map +1 -1
- package/lib/types/utils/paths.js +4 -1
- package/package.json +37 -9
- package/presets/liangshen/.dsh-tui-managed.json +5 -0
- package/presets/liangshen/agent.cordis.yml +389 -0
- package/presets/liangshen/compaction-epoch.mjs +81 -0
- package/presets/liangshen/custom-bash.mjs +126 -0
- package/presets/liangshen/instruction-hint.mjs +181 -0
- package/presets/liangshen/preset.yml +3 -0
- package/presets/liangshen/skill-search.mjs +142 -0
- package/presets/liangshen/tool-bootstrap.mjs +300 -0
- package/lib/types/components/ResumePicker.d.ts +0 -26
- package/lib/types/components/ResumePicker.d.ts.map +0 -1
- package/lib/types/components/ResumePicker.js +0 -50
package/README.md
CHANGED
|
@@ -77,7 +77,8 @@ sh install.sh
|
|
|
77
77
|
`dsh-tui --resume` 恢复上次会话;Windows 也可用仓库里的 `dsh-tui.cmd`(等价)。
|
|
78
78
|
|
|
79
79
|
在 VS Code 中运行的完整指南(内置终端直接使用 + companion 扩展
|
|
80
|
-
`dsh-tui-vscode
|
|
80
|
+
`dsh-tui-vscode`——**真实集成终端承载、体验与 Claude Code 官方扩展几乎一致、
|
|
81
|
+
已上架 VS Code Marketplace**)见
|
|
81
82
|
[在 VS Code 中运行 dsh-TUI](docs/vscode.md)。
|
|
82
83
|
|
|
83
84
|
TUI 启动后会在后台检查 npm 是否有新版本;发现更新时会提示,输入 `/update`
|
|
@@ -134,7 +135,7 @@ macOS 自带 Terminal.app 会自行消费 `⌘` 快捷键,请继续使用 `Ctr
|
|
|
134
135
|
|
|
135
136
|
| 分组 | 命令 |
|
|
136
137
|
|---|---|
|
|
137
|
-
| 会话 | `/new` 新会话 · `/resume`
|
|
138
|
+
| 会话 | `/new` 新会话 · `/resume` 会话浏览器(搜索、预览、跨项目、折叠子 agent 运行) · `/rename` 重命名会话 · `/workspace resume|rename|open` 管理工作区 · `/clear` 清屏 · `/compact` 压缩 · `/export` 导出 Markdown · `/trace` 轨迹场景(亦可 `Ctrl+T`) |
|
|
138
139
|
| 状态 | `/status` 会话信息 · `/cost` token 用量 · `/doctor` 环境自检 · `/config` 配置来源 · `/init` 创建 AGENTS.md |
|
|
139
140
|
| 模型 | `/model` 选择器 · `/thinking` 思考显示 · `/tokens` token 明细 · `/theme` 主题选择器 · `/lang` 中英界面切换 |
|
|
140
141
|
| 账号/策略 | `/provider` 添加模型提供方 · `/login` 凭证状态 · `/logout` 登出说明 · `/permissions` 权限说明 · `/add-dir` 文件策略范围 · `/hooks` · `/mcp` · `/memory` |
|
|
@@ -151,7 +152,7 @@ macOS 自带 Terminal.app 会自行消费 `⌘` 快捷键,请继续使用 `Ctr
|
|
|
151
152
|
| [主题系统](docs/themes.md) | 内置主题、自动检测、自定义 JSON 主题与校验规则 |
|
|
152
153
|
| [交互与命令](docs/interaction.md) | 快捷键、鼠标、问卷、slash command 与会话工作流 |
|
|
153
154
|
| [架构与限制](docs/architecture.md) | 运行链路、渲染与持久化设计、安全边界、已知限制 |
|
|
154
|
-
| [VS Code 使用指南](docs/vscode.md) | 在 VS Code 集成终端运行 dsh-tui
|
|
155
|
+
| [VS Code 使用指南](docs/vscode.md) | 在 VS Code 集成终端运行 dsh-tui;companion 扩展 `dsh-tui-vscode` 提供与 Claude Code 官方扩展几乎一致的体验(已上架 Marketplace) |
|
|
155
156
|
| [贡献与开发约定](docs/contributing.md) | 贡献流程、仓库地图、构建产物、验证矩阵与修改规则 |
|
|
156
157
|
| [插件开发指南](docs/plugins.md) | 插件接缝(会话事件 / 槽位 / 技能 / 主题 / prompt 段)、契约、规范与收录 |
|
|
157
158
|
|
|
@@ -159,7 +160,8 @@ macOS 自带 Terminal.app 会自行消费 `⌘` 快捷键,请继续使用 `Ctr
|
|
|
159
160
|
|
|
160
161
|
## 配置与扩展
|
|
161
162
|
|
|
162
|
-
- **Agent preset**:四种官方 Agent 模式(`standard` / `code` / `minimal` / `cordis
|
|
163
|
+
- **Agent preset**:四种官方 Agent 模式(`standard` / `code` / `minimal` / `cordis`)和
|
|
164
|
+
TUI 随包提供的“梁神模式”(`liangshen`),
|
|
163
165
|
`/preset` 切换;已产生对话的会话不可切换,空白会话立即生效。默认 preset 持久化
|
|
164
166
|
在 `~/.dsh-tui/agent-preset.json`;`/model` 的选择持久化在 `~/.dsh-tui/model.json`。
|
|
165
167
|
详见[配置参考](docs/configuration.md#agent-preset)。
|
|
@@ -205,7 +207,7 @@ compaction 和持久化继续由 DSH 服务拥有。更详细的模块边界与
|
|
|
205
207
|
- **TPS 仪表**:参考 pi-tps-meter——流式 1/8 格 gauge、历史 min-max sparkline、
|
|
206
208
|
速度语义色(≥50 绿 / ≥20 黄 / <20 红)。
|
|
207
209
|
- **working-activity 生态**:工作状态行复用
|
|
208
|
-
[dsh-working-activity](https://github.com/ccch1mneyyy/
|
|
210
|
+
[dsh-working-activity](https://github.com/ccch1mneyyy/working-activity)
|
|
209
211
|
的纯状态机,在进程内从基础会话事件派生,不向共享日志写入 UI 状态。
|
|
210
212
|
- **终端粘贴**:raw 模式下 Ctrl+V 由应用接管,按平台读取系统剪贴板——Windows
|
|
211
213
|
走 PowerShell `Get-Clipboard`,macOS 走 `osascript`/`pbpaste`,Linux 自动探测
|
package/bin/dsh-tui.js
CHANGED
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
* 2. 检测 $DSH_HOME/profiles/dsh-tui 是否已初始化,未初始化则自动执行
|
|
10
10
|
* `dsh plugin --profile dsh-tui add @deepseek-harness-tui/dsh-tui@<本包版本>`
|
|
11
11
|
* 自举——版本号与本包对齐,避免 pnpm store 缓存带来的旧版漂移;
|
|
12
|
-
* 3.
|
|
12
|
+
* 3. 已初始化但版本与本包不一致时按方向处理(issue #183):profile
|
|
13
|
+
* 更新(前向错位)打印一行提示后继续启动(TUI 内 /update 或重新
|
|
14
|
+
* add);profile 次版本更旧(反向错位)拒绝启动并给出对齐命令——
|
|
15
|
+
* 该方向 dsh CLI 会把启动器的 bundle patch 套到 profile 旧包上,
|
|
16
|
+
* 启动必然以模块解析错误崩溃;
|
|
13
17
|
* 4. 透传全部参数启动 `dsh --profile dsh-tui`。
|
|
14
18
|
*
|
|
15
19
|
* `--resume` 由本启动器拦截:读取 TUI 保留的 ~/.dsh-tui/resume.txt
|
|
@@ -65,10 +69,37 @@ const MSG = {
|
|
|
65
69
|
` 更新 profile:在 TUI 内执行 /update,或运行:\n` +
|
|
66
70
|
` dsh plugin --profile ${PROFILE} add ${PACKAGE}@latest`,
|
|
67
71
|
},
|
|
72
|
+
// Reverse skew (issue #183): the dsh CLI reads the bundle patch from the
|
|
73
|
+
// FIRST copy found from its own install anchor — this globally installed
|
|
74
|
+
// launcher — while the plugin modules load from the profile's copy. A
|
|
75
|
+
// launcher minor NEWER than the profile means the patch may reference
|
|
76
|
+
// subpath exports the profile's older package does not have, and boot
|
|
77
|
+
// crashes opaquely (ERR_PACKAGE_PATH_NOT_EXPORTED) before any TUI code
|
|
78
|
+
// runs. Fail loud with the fix instead. (Forward skew degrades to a
|
|
79
|
+
// working local-workspace fallback since 0.7.2 — soft note below.)
|
|
80
|
+
profileOlderThanLauncher: {
|
|
81
|
+
en: (installed, own) =>
|
|
82
|
+
`[dsh-tui] cannot start: the profile runs v${installed} but this launcher is v${own}.\n` +
|
|
83
|
+
` The launcher's bundle patch would be applied to the profile's older package,\n` +
|
|
84
|
+
` which does not export everything the patch references — boot would crash.\n` +
|
|
85
|
+
` Align the profile with the launcher:\n` +
|
|
86
|
+
` dsh plugin --profile ${PROFILE} add ${PACKAGE}@${own}\n` +
|
|
87
|
+
` (or update everything to the latest release: dsh plugin --profile ${PROFILE} add ${PACKAGE}@latest)`,
|
|
88
|
+
zh: (installed, own) =>
|
|
89
|
+
`[dsh-tui] 无法启动:profile 内运行的是 v${installed},而启动器是 v${own}。\n` +
|
|
90
|
+
` 启动器的 bundle patch 会套用到 profile 里的旧版包上,其中缺少 patch 引用\n` +
|
|
91
|
+
` 的子路径导出——启动会以模块解析错误崩溃。请让 profile 与启动器对齐:\n` +
|
|
92
|
+
` dsh plugin --profile ${PROFILE} add ${PACKAGE}@${own}\n` +
|
|
93
|
+
` (或全部升到最新:dsh plugin --profile ${PROFILE} add ${PACKAGE}@latest)`,
|
|
94
|
+
},
|
|
68
95
|
launchFailed: {
|
|
69
96
|
en: err => `[dsh-tui] Failed to launch: ${err.message}`,
|
|
70
97
|
zh: err => `[dsh-tui] 启动失败:${err.message}`,
|
|
71
98
|
},
|
|
99
|
+
profileExited: {
|
|
100
|
+
en: code => `[dsh-tui] dsh profile exited with code ${code}. Run it directly for diagnostics:\n dsh --profile ${PROFILE}`,
|
|
101
|
+
zh: code => `[dsh-tui] dsh profile 已退出(退出码 ${code})。可直接运行以下命令查看诊断:\n dsh --profile ${PROFILE}`,
|
|
102
|
+
},
|
|
72
103
|
legacyEnv: {
|
|
73
104
|
en: (oldName, newName) => `[dsh-tui] note: env ${oldName} was renamed to ${newName}; the old name no longer takes effect.`,
|
|
74
105
|
zh: (oldName, newName) => `[dsh-tui] 提示:环境变量 ${oldName} 已更名为 ${newName},旧名不再生效。`,
|
|
@@ -126,6 +157,15 @@ if (installedVersion === undefined) {
|
|
|
126
157
|
process.exit(add.status ?? 1)
|
|
127
158
|
}
|
|
128
159
|
} else if (installedVersion !== ownVersion) {
|
|
160
|
+
// Reverse skew is fatal (see MSG.profileOlderThanLauncher): compare
|
|
161
|
+
// major/minor only — patch-level differences never move the patch surface.
|
|
162
|
+
const majorMinor = v => v.split('-')[0].split('.').slice(0, 2).map(Number)
|
|
163
|
+
const [installedMajor, installedMinor] = majorMinor(installedVersion)
|
|
164
|
+
const [ownMajor, ownMinor] = majorMinor(ownVersion)
|
|
165
|
+
if (installedMajor < ownMajor || (installedMajor === ownMajor && installedMinor < ownMinor)) {
|
|
166
|
+
console.error(MSG.profileOlderThanLauncher[lang](installedVersion, ownVersion))
|
|
167
|
+
process.exit(1)
|
|
168
|
+
}
|
|
129
169
|
console.error(MSG.versionMismatch[lang](installedVersion, ownVersion))
|
|
130
170
|
}
|
|
131
171
|
|
|
@@ -197,6 +237,7 @@ child.on('exit', (code, signal) => {
|
|
|
197
237
|
if (signal) {
|
|
198
238
|
process.kill(process.pid, signal)
|
|
199
239
|
} else {
|
|
240
|
+
if (code !== null && code !== 0) console.error(MSG.profileExited[lang](code))
|
|
200
241
|
process.exit(code ?? 0)
|
|
201
242
|
}
|
|
202
243
|
})
|
package/cordis.patch.yml
CHANGED
|
@@ -218,6 +218,16 @@
|
|
|
218
218
|
# The entry-level dependency augments the plugin's `agents` inject and
|
|
219
219
|
# prevents startup Session creation from racing WorkspaceRegistry init.
|
|
220
220
|
inject: [workspaceRegistry, agents, tuiWorkspaces]
|
|
221
|
+
# `tuiWorkspaces` lives in the entry-level inject ONLY as an ordering
|
|
222
|
+
# guarantee alongside the dsh-tui-workspaces row above — it is
|
|
223
|
+
# deliberately absent from the plugin's code-level inject (issue
|
|
224
|
+
# #183): the dsh CLI may read this patch from an older copy of the
|
|
225
|
+
# package than the plugin module it loads, and a hard code-level
|
|
226
|
+
# inject then deadlocks the tree at boot ("pending (waiting for
|
|
227
|
+
# service: tuiWorkspaces)"). Without the row the TUI degrades to a
|
|
228
|
+
# local-only workspace runtime instead. (The comment sits below the
|
|
229
|
+
# inject line: verify-workspace-attachment.mjs asserts the
|
|
230
|
+
# name→inject distance.)
|
|
221
231
|
config:
|
|
222
232
|
provider: deepseek-official
|
|
223
233
|
# Alt-screen fullscreen(CC 同款):应用内鼠标选区(选择即复制, OSC 52 +
|
package/lib/types/commands.js
CHANGED
|
@@ -31,7 +31,7 @@ export const LOCAL_COMMANDS = [
|
|
|
31
31
|
{ name: 'agents', description: 'Show subagents of this session' },
|
|
32
32
|
// Model / display
|
|
33
33
|
{ name: 'activity', description: 'Switch the working-activity indicator preset' },
|
|
34
|
-
{ name: 'preset', description: 'Switch the agent preset (
|
|
34
|
+
{ name: 'preset', description: 'Switch the agent preset (including Liangshen mode)' },
|
|
35
35
|
{ name: 'theme', description: 'Switch the color theme (auto, built-in or custom)' },
|
|
36
36
|
{ name: 'lang', description: 'Switch the UI language (en / zh)' },
|
|
37
37
|
{ name: 'model', description: 'Show the active model' },
|
|
@@ -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,EAA8B,MAAM,2BAA2B,CAAA;AAwDpF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,
|
|
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,EAA8B,MAAM,2BAA2B,CAAA;AAwDpF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAgEhF"}
|
|
@@ -45,7 +45,12 @@ function BranchPrefix({ last }) {
|
|
|
45
45
|
*/
|
|
46
46
|
export function GoalTodoPanel({ channel }) {
|
|
47
47
|
const goal = channel.goal;
|
|
48
|
-
const
|
|
48
|
+
const allTodos = channel.todos ?? [];
|
|
49
|
+
// Completed rows are useful progress while a turn is running, but become
|
|
50
|
+
// stale footer noise once the agent is idle. Keep unfinished work visible.
|
|
51
|
+
const todos = channel.working
|
|
52
|
+
? allTodos
|
|
53
|
+
: allTodos.filter(todo => todo.status !== 'completed');
|
|
49
54
|
if (goal === undefined && todos.length === 0)
|
|
50
55
|
return null;
|
|
51
56
|
const visible = todos.slice(0, MAX_TODOS);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HistorySearchDialog.d.ts","sourceRoot":"","sources":["../../../src/components/HistorySearchDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,YAAY,EACZ,OAAO,EACP,UAAU,GACX,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"HistorySearchDialog.d.ts","sourceRoot":"","sources":["../../../src/components/HistorySearchDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,YAAY,EACZ,OAAO,EACP,UAAU,GACX,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;CACnB,GAAG,KAAK,CAAC,SAAS,CAuDlB"}
|
|
@@ -26,7 +26,11 @@ export function HistorySearchDialog({ query, cursorOffset, matches, focusIndex,
|
|
|
26
26
|
const { start, end } = listWindow(matches.map(() => 2), focusIndex, Math.max(terminalRows - 18, 2), 1);
|
|
27
27
|
return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, color: "permission", children: t('history-search-title') }), _jsx(SearchBox, { query: query, cursorOffset: cursorOffset, isFocused: true, isTerminalFocused: isTerminalFocused, placeholder: t('history-search-placeholder') }), matches.length === 0 ? (_jsx(Text, { dimColor: true, children: t('history-search-empty') })) : (matches.slice(start, end).map((entry, index) => {
|
|
28
28
|
const absoluteIndex = start + index;
|
|
29
|
-
return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex,
|
|
29
|
+
return (_jsx(ListItem, { isFocused: absoluteIndex === focusIndex,
|
|
30
|
+
// The SearchBox owns the native-cursor declaration while this
|
|
31
|
+
// dialog is open — result rows must not park the cursor on
|
|
32
|
+
// themselves, or IME preedit lands on a list row.
|
|
33
|
+
declareCursor: false, description: formatRelativeAge(entry.ts), showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < matches.length, children: entry.text }, historyEntryId(entry)));
|
|
30
34
|
})), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('hint-history-search') }) })] }) }));
|
|
31
35
|
}
|
|
32
36
|
/** Relative age like CC's formatRelativeTimeAgo ("now" / "5m ago" / …), localized. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadedContextPanel.d.ts","sourceRoot":"","sources":["../../../src/components/LoadedContextPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAA;AA+BlF;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,aAAa,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"LoadedContextPanel.d.ts","sourceRoot":"","sources":["../../../src/components/LoadedContextPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAA;AA+BlF;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,aAAa,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,MAAM,IAAI,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,CAoElB"}
|
|
@@ -26,5 +26,5 @@ export function LoadedContextPanel({ context, open, onToggle, }) {
|
|
|
26
26
|
const summary = summarizeLoadedContext(context);
|
|
27
27
|
if (summary === '')
|
|
28
28
|
return null;
|
|
29
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
|
|
29
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsx(Box, { paddingX: 1, backgroundColor: open ? 'userMessageBackground' : undefined, children: _jsxs(Text, { wrap: "truncate", bold: open, children: [open ? '▼' : '▶', " ", t('context-loaded'), " \u00B7 ", summary, _jsxs(Text, { dimColor: true, children: [" \uFF08Ctrl+T", open ? t('context-panel-collapse') : t('context-panel-expand'), "\uFF09"] })] }) }), open && (_jsxs(Box, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [context.sections.length > 0 && (_jsx(Group, { title: t('context-panel-sections', { n: context.sections.length }), children: context.sections.map(section => (_jsx(Entry, { entry: section }, section.name))) })), context.files.length > 0 && (_jsx(Group, { title: t('context-panel-files', { n: context.files.length }), children: context.files.map(file => (_jsx(Text, { dimColor: true, children: file.displayPath }, file.displayPath))) })), context.contexts.length > 0 && (_jsx(Group, { title: t('context-panel-runtime', { n: context.contexts.length }), children: context.contexts.map(entry => (_jsx(Entry, { entry: entry }, entry.name))) })), context.skills.length > 0 && (_jsx(Group, { title: t('context-panel-skills', { n: context.skills.length }), children: context.skills.map(skill => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: skill.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: skill.description })] }, skill.name))) })), context.tools.length > 0 && (_jsx(Group, { title: t('context-panel-tools', { n: context.tools.length }), children: context.tools.map(tool => (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, dimColor: true, children: tool.name }), _jsx(Text, { dimColor: true, wrap: "wrap", children: truncateContextText(tool.description, 160) })] }, tool.name))) }))] }))] }));
|
|
30
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAA8B,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAyC,MAAM,2BAA2B,CAAA;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAuC/C,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,WAAW,EACX,WAAW,EACX,eAAsB,EACtB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB;2EACuE;IACvE,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAA;IAC/D,oDAAoD;IACpD,YAAY,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IACrC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;8EAC0E;IAC1E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAA8B,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3E,OAAO,KAAK,EAAE,OAAO,EAAyC,MAAM,2BAA2B,CAAA;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAuC/C,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,WAAW,EACX,WAAW,EACX,eAAsB,EACtB,cAAc,EACd,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;IACxB,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB;2EACuE;IACvE,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAA;IAC/D,oDAAoD;IACpD,YAAY,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IACrC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;8EAC0E;IAC1E,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,qBAwPA;AAuQD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,MAAM,EACN,GAAG,GACJ,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B,GAAG,EAAE,MAAM,CAAA;CACZ,GAAG,KAAK,CAAC,SAAS,CAMlB"}
|
|
@@ -158,8 +158,12 @@ export function MessageList({ rows, expanded, expandedRows, selectedId, onToggle
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
|
+
const lastUnseenReportRef = React.useRef(-1);
|
|
161
162
|
React.useEffect(() => {
|
|
162
|
-
|
|
163
|
+
if (unseenCount !== lastUnseenReportRef.current) {
|
|
164
|
+
lastUnseenReportRef.current = unseenCount;
|
|
165
|
+
onUnseenCount?.(unseenCount);
|
|
166
|
+
}
|
|
163
167
|
});
|
|
164
168
|
// Post-commit: measure mounted rows, derive the content-space base from
|
|
165
169
|
// the first mounted row's Yoga top, and clamp render-time scrollTop to the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/components/PromptInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAkDxD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,OAAO,CAAA;IAClB,KAAK,IAAI,IAAI,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,IAAI,IAAI,CAAA;IACpB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACrD,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,IAAI,IAAI,CAAA;IACvB,8EAA8E;IAC9E,eAAe,CAAC,IAAI,IAAI,CAAA;IACxB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,cAAc,EACd,eAAe,EACf,aAAa,GACd,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"PromptInput.d.ts","sourceRoot":"","sources":["../../../src/components/PromptInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AASzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAA;AAkDxD;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,IAAI,OAAO,CAAA;IAClB,KAAK,IAAI,IAAI,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAA;IAChB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAA;IACjB,YAAY,IAAI,IAAI,CAAA;IACpB;;;OAGG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACrD,6EAA6E;IAC7E,eAAe,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,cAAc,CAAC,IAAI,IAAI,CAAA;IACvB,8EAA8E;IAC9E,eAAe,CAAC,IAAI,IAAI,CAAA;IACxB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,cAAc,EACd,eAAe,EACf,aAAa,GACd,EAAE,gBAAgB,qBAg5BlB"}
|
|
@@ -103,6 +103,8 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
103
103
|
controllerRef.current = {
|
|
104
104
|
hasText: () => value.length > 0,
|
|
105
105
|
clear: () => {
|
|
106
|
+
valueRef.current = '';
|
|
107
|
+
cursorRef.current = 0;
|
|
106
108
|
setValue('');
|
|
107
109
|
setCursor(0);
|
|
108
110
|
},
|
|
@@ -120,6 +122,8 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
120
122
|
React.useEffect(() => {
|
|
121
123
|
if (fillText && fillText !== lastFill.current) {
|
|
122
124
|
lastFill.current = fillText;
|
|
125
|
+
valueRef.current = fillText;
|
|
126
|
+
cursorRef.current = fillText.length;
|
|
123
127
|
setValue(fillText);
|
|
124
128
|
setCursor(fillText.length);
|
|
125
129
|
onFillConsumed?.();
|
|
@@ -132,16 +136,6 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
132
136
|
const clipboardBusyRef = React.useRef(false);
|
|
133
137
|
/** True while the external editor owns the terminal (Ctrl+X round-trip). */
|
|
134
138
|
const editorBusyRef = React.useRef(false);
|
|
135
|
-
/**
|
|
136
|
-
* Latest committed input state. The Ctrl+V clipboard read resolves
|
|
137
|
-
* asynchronously; inserting against the render that STARTED the read
|
|
138
|
-
* would drop whatever the user typed while waiting. The continuation
|
|
139
|
-
* inserts against this mirror instead.
|
|
140
|
-
*/
|
|
141
|
-
const liveInputRef = React.useRef({ value: '', cursor: 0 });
|
|
142
|
-
React.useEffect(() => {
|
|
143
|
-
liveInputRef.current = { value, cursor };
|
|
144
|
-
});
|
|
145
139
|
/** Enter dedupe window: cmd pipelines can deliver one Enter as `\r`+`\n`. */
|
|
146
140
|
const lastEnterAtRef = React.useRef(0);
|
|
147
141
|
React.useEffect(() => {
|
|
@@ -193,6 +187,12 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
193
187
|
!helpOpen &&
|
|
194
188
|
!selectionActive &&
|
|
195
189
|
fileEscRef.current !== mention?.start;
|
|
190
|
+
const setInput = (next, cursorOffset = next.length) => {
|
|
191
|
+
valueRef.current = next;
|
|
192
|
+
cursorRef.current = Math.max(0, Math.min(cursorOffset, next.length));
|
|
193
|
+
setValue(next);
|
|
194
|
+
setCursor(cursorRef.current);
|
|
195
|
+
};
|
|
196
196
|
/**
|
|
197
197
|
* Accept the selected file suggestion: replace ONLY the mention token at
|
|
198
198
|
* the caret (prefix/suffix text survives), quoting whitespace paths. A
|
|
@@ -216,8 +216,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
216
216
|
if (history.current.length > HISTORY_LIMIT)
|
|
217
217
|
history.current.shift();
|
|
218
218
|
historyIndex.current = -1;
|
|
219
|
-
|
|
220
|
-
setCursor(0);
|
|
219
|
+
setInput('', 0);
|
|
221
220
|
setSelectedCommand(0);
|
|
222
221
|
appendHistory(trimmed);
|
|
223
222
|
channel.submit(trimmed);
|
|
@@ -243,8 +242,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
243
242
|
if (history.current.length > HISTORY_LIMIT)
|
|
244
243
|
history.current.shift();
|
|
245
244
|
historyIndex.current = -1;
|
|
246
|
-
|
|
247
|
-
setCursor(0);
|
|
245
|
+
setInput('', 0);
|
|
248
246
|
setSelectedCommand(0);
|
|
249
247
|
appendHistory(trimmed);
|
|
250
248
|
channel.steer(trimmed);
|
|
@@ -262,8 +260,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
262
260
|
if (history.current.length > HISTORY_LIMIT)
|
|
263
261
|
history.current.shift();
|
|
264
262
|
historyIndex.current = -1;
|
|
265
|
-
|
|
266
|
-
setCursor(0);
|
|
263
|
+
setInput('', 0);
|
|
267
264
|
setSelectedCommand(0);
|
|
268
265
|
appendHistory(trimmed);
|
|
269
266
|
channel.submit(trimmed);
|
|
@@ -282,8 +279,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
282
279
|
channel.notify(t('input-cannot-retract'), { color: 'warning', timeoutMs: 2500 });
|
|
283
280
|
return;
|
|
284
281
|
}
|
|
285
|
-
|
|
286
|
-
setCursor(item.text.length);
|
|
282
|
+
setInput(item.text);
|
|
287
283
|
setSelectedCommand(0);
|
|
288
284
|
setFileSelected(0);
|
|
289
285
|
channel.notify(t('input-retracted'), { timeoutMs: 2000 });
|
|
@@ -308,8 +304,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
308
304
|
if (history.current.length > HISTORY_LIMIT)
|
|
309
305
|
history.current.shift();
|
|
310
306
|
historyIndex.current = -1;
|
|
311
|
-
|
|
312
|
-
setCursor(0);
|
|
307
|
+
setInput('', 0);
|
|
313
308
|
setSelectedCommand(0);
|
|
314
309
|
setFileSelected(0);
|
|
315
310
|
appendHistory(trimmed);
|
|
@@ -336,19 +331,12 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
336
331
|
if (history.current.length > HISTORY_LIMIT)
|
|
337
332
|
history.current.shift();
|
|
338
333
|
historyIndex.current = -1;
|
|
339
|
-
|
|
340
|
-
setCursor(0);
|
|
334
|
+
setInput('', 0);
|
|
341
335
|
setSelectedCommand(0);
|
|
342
336
|
appendHistory(text.trim());
|
|
343
337
|
}
|
|
344
338
|
return handled;
|
|
345
339
|
};
|
|
346
|
-
const setInput = (next, cursorOffset = next.length) => {
|
|
347
|
-
valueRef.current = next;
|
|
348
|
-
cursorRef.current = Math.max(0, Math.min(cursorOffset, next.length));
|
|
349
|
-
setValue(next);
|
|
350
|
-
setCursor(cursorRef.current);
|
|
351
|
-
};
|
|
352
340
|
/** Clipboard reads are asynchronous; insert against the latest render so
|
|
353
341
|
* typing while PowerShell owns the clipboard never gets overwritten. */
|
|
354
342
|
const insertClipboardAtCaret = (text) => {
|
|
@@ -359,13 +347,13 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
359
347
|
setFileSelected(0);
|
|
360
348
|
};
|
|
361
349
|
/** Line index of the cursor; -1 when the cursor is at the very end. */
|
|
362
|
-
const cursorLine = () => {
|
|
363
|
-
const before =
|
|
350
|
+
const cursorLine = (text, cursorOffset) => {
|
|
351
|
+
const before = text.slice(0, cursorOffset);
|
|
364
352
|
return before.split('\n').length - 1;
|
|
365
353
|
};
|
|
366
354
|
/** Column of the cursor within its line. */
|
|
367
|
-
const cursorColumn = () => {
|
|
368
|
-
const before =
|
|
355
|
+
const cursorColumn = (text, cursorOffset) => {
|
|
356
|
+
const before = text.slice(0, cursorOffset);
|
|
369
357
|
const line = before.split('\n').pop() ?? '';
|
|
370
358
|
return line.length;
|
|
371
359
|
};
|
|
@@ -377,13 +365,17 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
377
365
|
// setValue can never overwrite fresh typing (and vice versa).
|
|
378
366
|
if (editorBusyRef.current)
|
|
379
367
|
return;
|
|
368
|
+
// App deliberately dispatches every parsed key from one stdin read in a
|
|
369
|
+
// single React update. Read the synchronous mirrors so each event sees
|
|
370
|
+
// the text/caret produced by the preceding event in that batch.
|
|
371
|
+
const value = valueRef.current;
|
|
372
|
+
const cursor = cursorRef.current;
|
|
380
373
|
/** Insert text at the caret (typing, paste) and dismiss overlays. */
|
|
381
374
|
const insertAtCaret = (text) => {
|
|
382
375
|
if (helpOpen)
|
|
383
376
|
onToggleHelp();
|
|
384
377
|
const next = value.slice(0, cursor) + text + value.slice(cursor);
|
|
385
|
-
|
|
386
|
-
setCursor(cursor + text.length);
|
|
378
|
+
setInput(next, cursor + text.length);
|
|
387
379
|
setSelectedCommand(0);
|
|
388
380
|
setFileSelected(0);
|
|
389
381
|
};
|
|
@@ -441,10 +433,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
441
433
|
// Insert against the LIVE input state: the read above resolved
|
|
442
434
|
// asynchronously and the user may have typed while waiting.
|
|
443
435
|
const text = formatClipboardInsert(content);
|
|
444
|
-
|
|
445
|
-
const caret = Math.min(live.cursor, live.value.length);
|
|
446
|
-
setValue(live.value.slice(0, caret) + text + live.value.slice(caret));
|
|
447
|
-
setCursor(caret + text.length);
|
|
436
|
+
insertClipboardAtCaret(text);
|
|
448
437
|
})
|
|
449
438
|
.catch(() => {
|
|
450
439
|
channel.notify(t('input-clipboard-read-failed'), { color: 'warning' });
|
|
@@ -469,8 +458,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
469
458
|
try {
|
|
470
459
|
const outcome = await editInExternalEditor(value);
|
|
471
460
|
if (outcome.kind === 'edited') {
|
|
472
|
-
|
|
473
|
-
setCursor(outcome.text.length);
|
|
461
|
+
setInput(outcome.text);
|
|
474
462
|
setSelectedCommand(0);
|
|
475
463
|
setFileSelected(0);
|
|
476
464
|
}
|
|
@@ -575,8 +563,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
575
563
|
// ink/terminal.ts); Option+Enter (ESC CR) is the fallback on terminals
|
|
576
564
|
// that can't report shift — e.g. macOS Terminal.app (issue #110).
|
|
577
565
|
const next = value.slice(0, cursor) + '\n' + value.slice(cursor);
|
|
578
|
-
|
|
579
|
-
setCursor(cursor + 1);
|
|
566
|
+
setInput(next, cursor + 1);
|
|
580
567
|
setSelectedCommand(0);
|
|
581
568
|
return;
|
|
582
569
|
}
|
|
@@ -620,13 +607,13 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
620
607
|
setFileSelected(index => index <= 0 ? fileMatches.length - 1 : index - 1);
|
|
621
608
|
return;
|
|
622
609
|
}
|
|
623
|
-
const line = cursorLine();
|
|
610
|
+
const line = cursorLine(value, cursor);
|
|
624
611
|
if (line > 0) {
|
|
625
612
|
// Move to the previous line, clamping to its length.
|
|
626
613
|
const upToLineStart = value.lastIndexOf('\n', cursor - 1);
|
|
627
614
|
const prevLineStart = upToLineStart === -1 ? 0 : value.lastIndexOf('\n', upToLineStart - 1) + 1;
|
|
628
615
|
const prevLine = value.slice(prevLineStart, upToLineStart);
|
|
629
|
-
|
|
616
|
+
setInput(value, prevLineStart + Math.min(cursorColumn(value, cursor), prevLine.length));
|
|
630
617
|
return;
|
|
631
618
|
}
|
|
632
619
|
if (overlayOpen) {
|
|
@@ -639,8 +626,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
639
626
|
? history.current.length - 1
|
|
640
627
|
: Math.max(0, historyIndex.current - 1);
|
|
641
628
|
const entry = history.current[historyIndex.current] ?? '';
|
|
642
|
-
|
|
643
|
-
setCursor(entry.length);
|
|
629
|
+
setInput(entry);
|
|
644
630
|
return;
|
|
645
631
|
}
|
|
646
632
|
if (key.downArrow) {
|
|
@@ -648,13 +634,13 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
648
634
|
setFileSelected(index => index >= fileMatches.length - 1 ? 0 : index + 1);
|
|
649
635
|
return;
|
|
650
636
|
}
|
|
651
|
-
const line = cursorLine();
|
|
637
|
+
const line = cursorLine(value, cursor);
|
|
652
638
|
const lines = value.split('\n');
|
|
653
639
|
if (line < lines.length - 1) {
|
|
654
640
|
const nextLineStart = value.indexOf('\n', cursor) + 1;
|
|
655
641
|
const nextLineEnd = value.indexOf('\n', nextLineStart);
|
|
656
642
|
const nextLine = value.slice(nextLineStart, nextLineEnd === -1 ? value.length : nextLineEnd);
|
|
657
|
-
|
|
643
|
+
setInput(value, nextLineStart + Math.min(cursorColumn(value, cursor), nextLine.length));
|
|
658
644
|
return;
|
|
659
645
|
}
|
|
660
646
|
if (overlayOpen) {
|
|
@@ -667,76 +653,73 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
667
653
|
const entry = historyIndex.current >= history.current.length
|
|
668
654
|
? ''
|
|
669
655
|
: (history.current[historyIndex.current] ?? '');
|
|
670
|
-
|
|
671
|
-
setCursor(entry.length);
|
|
656
|
+
setInput(entry);
|
|
672
657
|
return;
|
|
673
658
|
}
|
|
674
659
|
if (isMod(key) && key.leftArrow) {
|
|
675
660
|
// Jump to the previous word boundary (readline alt+b). Must precede the
|
|
676
661
|
// bare-arrow arms: Ctrl+Left arrives as leftArrow + ctrl.
|
|
677
|
-
|
|
662
|
+
setInput(value, wordBoundaryLeft(value, cursor));
|
|
678
663
|
return;
|
|
679
664
|
}
|
|
680
665
|
if (isMod(key) && key.rightArrow) {
|
|
681
666
|
// Jump to the next word boundary (readline alt+f).
|
|
682
|
-
|
|
667
|
+
setInput(value, wordBoundaryRight(value, cursor));
|
|
683
668
|
return;
|
|
684
669
|
}
|
|
685
670
|
if (key.leftArrow) {
|
|
686
|
-
|
|
671
|
+
setInput(value, Math.max(0, cursor - 1));
|
|
687
672
|
return;
|
|
688
673
|
}
|
|
689
674
|
if (key.rightArrow) {
|
|
690
|
-
|
|
675
|
+
setInput(value, Math.min(value.length, cursor + 1));
|
|
691
676
|
return;
|
|
692
677
|
}
|
|
693
678
|
if (key.backspace) {
|
|
694
679
|
if (cursor === 0)
|
|
695
680
|
return;
|
|
696
|
-
|
|
697
|
-
setCursor(cursor - 1);
|
|
681
|
+
setInput(value.slice(0, cursor - 1) + value.slice(cursor), cursor - 1);
|
|
698
682
|
return;
|
|
699
683
|
}
|
|
700
684
|
if (key.delete) {
|
|
701
685
|
if (cursor >= value.length)
|
|
702
686
|
return;
|
|
703
|
-
|
|
687
|
+
setInput(value.slice(0, cursor) + value.slice(cursor + 1), cursor);
|
|
704
688
|
return;
|
|
705
689
|
}
|
|
706
690
|
if (key.home) {
|
|
707
691
|
// Start of the current line.
|
|
708
692
|
const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
|
|
709
|
-
|
|
693
|
+
setInput(value, lineStart);
|
|
710
694
|
return;
|
|
711
695
|
}
|
|
712
696
|
if (key.end) {
|
|
713
697
|
// End of the current line.
|
|
714
698
|
const nextLine = value.indexOf('\n', cursor);
|
|
715
|
-
|
|
699
|
+
setInput(value, nextLine === -1 ? value.length : nextLine);
|
|
716
700
|
return;
|
|
717
701
|
}
|
|
718
702
|
if (isMod(key) && input === 'a') {
|
|
719
703
|
const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
|
|
720
|
-
|
|
704
|
+
setInput(value, lineStart);
|
|
721
705
|
return;
|
|
722
706
|
}
|
|
723
707
|
if (isMod(key) && input === 'e') {
|
|
724
708
|
const nextLine = value.indexOf('\n', cursor);
|
|
725
|
-
|
|
709
|
+
setInput(value, nextLine === -1 ? value.length : nextLine);
|
|
726
710
|
return;
|
|
727
711
|
}
|
|
728
712
|
if (isMod(key) && input === 'u') {
|
|
729
713
|
// Delete to start of line.
|
|
730
714
|
const lineStart = value.lastIndexOf('\n', cursor - 1) + 1;
|
|
731
|
-
|
|
732
|
-
setCursor(lineStart);
|
|
715
|
+
setInput(value.slice(0, lineStart) + value.slice(cursor), lineStart);
|
|
733
716
|
return;
|
|
734
717
|
}
|
|
735
718
|
if (isMod(key) && input === 'k') {
|
|
736
719
|
// Delete to end of line.
|
|
737
720
|
const nextLine = value.indexOf('\n', cursor);
|
|
738
721
|
const end = nextLine === -1 ? value.length : nextLine;
|
|
739
|
-
|
|
722
|
+
setInput(value.slice(0, cursor) + value.slice(end), cursor);
|
|
740
723
|
return;
|
|
741
724
|
}
|
|
742
725
|
if (isMod(key) && input === 'w') {
|
|
@@ -749,8 +732,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
749
732
|
let start = end;
|
|
750
733
|
while (start > 0 && !/\s/.test(before[start - 1]))
|
|
751
734
|
start--;
|
|
752
|
-
|
|
753
|
-
setCursor(start);
|
|
735
|
+
setInput(value.slice(0, start) + value.slice(cursor), start);
|
|
754
736
|
return;
|
|
755
737
|
}
|
|
756
738
|
if (key.escape) {
|
|
@@ -761,8 +743,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
761
743
|
// A single Esc closes the open command menu first (CC/pi behavior);
|
|
762
744
|
// the double-tap-clear semantics only apply to ordinary input.
|
|
763
745
|
if (overlayOpen) {
|
|
764
|
-
|
|
765
|
-
setCursor(0);
|
|
746
|
+
setInput('', 0);
|
|
766
747
|
setSelectedCommand(0);
|
|
767
748
|
setFileSelected(0);
|
|
768
749
|
return;
|
|
@@ -786,8 +767,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
786
767
|
// A single Esc clears the current input (if any); the double-tap
|
|
787
768
|
// path below handles rewind/clear on an already-empty input.
|
|
788
769
|
if (value.length > 0) {
|
|
789
|
-
|
|
790
|
-
setCursor(0);
|
|
770
|
+
setInput('', 0);
|
|
791
771
|
setSelectedCommand(0);
|
|
792
772
|
setFileSelected(0);
|
|
793
773
|
return;
|
|
@@ -803,8 +783,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
803
783
|
onRewindRequest?.();
|
|
804
784
|
}
|
|
805
785
|
else {
|
|
806
|
-
|
|
807
|
-
setCursor(0);
|
|
786
|
+
setInput('', 0);
|
|
808
787
|
}
|
|
809
788
|
return;
|
|
810
789
|
}
|
|
@@ -826,8 +805,7 @@ export function PromptInput({ channel, helpOpen, onToggleHelp, onRunCommand, sel
|
|
|
826
805
|
if (helpOpen)
|
|
827
806
|
onToggleHelp();
|
|
828
807
|
const next = value.slice(0, cursor) + input + value.slice(cursor);
|
|
829
|
-
|
|
830
|
-
setCursor(cursor + input.length);
|
|
808
|
+
setInput(next, cursor + input.length);
|
|
831
809
|
setSelectedCommand(0);
|
|
832
810
|
setFileSelected(0);
|
|
833
811
|
}
|
|
@@ -6,6 +6,10 @@ import React from 'react';
|
|
|
6
6
|
* placeholder is right-aligned (dimmed) — kept off the caret's cell so the
|
|
7
7
|
* terminal-painted IME preedit (pinyin) can never be overlaid on it during
|
|
8
8
|
* CJK composition.
|
|
9
|
+
*
|
|
10
|
+
* The query row is strictly single-line: an overlong query is windowed
|
|
11
|
+
* around the caret (horizontal scroll) instead of wrapping, so the native
|
|
12
|
+
* cursor declaration below stays exact for any query length.
|
|
9
13
|
*/
|
|
10
14
|
export declare function SearchBox({ query, placeholder, isFocused, isTerminalFocused, prefix, width, cursorOffset, borderless, }: {
|
|
11
15
|
query: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchBox.d.ts","sourceRoot":"","sources":["../../../src/components/SearchBox.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"SearchBox.d.ts","sourceRoot":"","sources":["../../../src/components/SearchBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyD,MAAM,OAAO,CAAA;AAyD7E;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,WAAuB,EACvB,SAAS,EACT,iBAAiB,EACjB,MAAY,EACZ,KAAK,EACL,YAAY,EACZ,UAAkB,GACnB,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,OAAO,CAAA;IAClB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,GAAG,KAAK,CAAC,SAAS,CA0GlB"}
|