@askdkc/dsh-cli 0.11.4 → 0.11.6

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 CHANGED
@@ -109,10 +109,32 @@ pnpm dsh --profile dsh-cli
109
109
  ```
110
110
 
111
111
  Replace `~/DIR/TO/deepseek-harness` with your checkout path. The tarball name
112
- comes from the package's current name, `@askdkc/dsh-cli`, and version. To
113
- install the optional `dsh-cli` shell command, run
114
- `npm install -g --legacy-peer-deps "$TARBALL"` after creating the tarball.
115
- The existing `dsh-tui` and `dst` commands remain compatibility aliases.
112
+ comes from the package's current name, `@askdkc/dsh-cli`, and version. The
113
+ first interactive `dsh --profile dsh-cli` launch registers `dsh-cli` in
114
+ `~/.local/bin` (Windows: `%LOCALAPPDATA%\dsh-cli\bin`) and adds that directory
115
+ to the user's PATH when needed. Open a new shell before using the command.
116
+ Registration supports zsh, bash, fish and Windows; an existing unrelated
117
+ `dsh-cli` command is left alone. Set `DSH_TUI_AUTO_REGISTER_CLI=0` to disable
118
+ it. The managed PATH block goes in `${ZDOTDIR:-$HOME}/.zshrc` for zsh;
119
+ `.bashrc` and the active login file for bash; or
120
+ `${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d/dsh-cli.fish` for fish. To undo
121
+ it, remove the managed command and that block; delete the fish file only if
122
+ it contains nothing else (Windows: remove the command and user PATH entry). The
123
+ existing `dsh-tui` and `dst` commands remain compatibility aliases.
124
+
125
+ A Harness source checkout does not put `dsh` on PATH. When the TUI first starts
126
+ through `pnpm dsh --profile dsh-cli` in a Harness checkout, registration also
127
+ records that checkout as `DSH_TUI_DSH_ROOT` in the managed shell startup block
128
+ (`.zshrc` for zsh). Open a new shell, then launch from any project directory:
129
+
130
+ ```sh
131
+ cd /absolute/path/to/your-project
132
+ dsh-cli
133
+ ```
134
+
135
+ The launcher uses the checkout's built `apps/cli/lib/bin.js` and preserves the
136
+ project directory. Rebuild Harness after changing its source. An explicit
137
+ `DSH_TUI_DSH_ROOT` value overrides the registered default.
116
138
  A locally packed fork should be updated by rebuilding and reinstalling the
117
139
  archive, rather than by using the registry-backed `/update` command.
118
140
 
@@ -191,7 +213,10 @@ Runtime path, module boundaries, performance notes and persistence locations: [A
191
213
  ## Known Limitations
192
214
 
193
215
  - Injected plugin context has no standalone display; it counts into the context segments.
194
- - `/model` switches by forking the session; the old session stays in `/resume`.
216
+ - `/model` filters by space-separated, case-insensitive keywords in any order.
217
+ At the provider level it searches every model; inside a provider or Recents
218
+ it searches that list. Names and IDs of models and providers are matched.
219
+ Switching forks the session; the old session stays in `/resume`.
195
220
  - `Ctrl+V` needs platform clipboard tools; unsupported bitmap formats are rejected.
196
221
  - A background session lives inside this process and stops when the TUI exits.
197
222
  - `/thinking` is not persisted; `/compact` is unavailable under the `minimal` preset; `/update` needs a `dsh --profile` launch and is refused while a turn is running.
package/bin/dsh-tui.js CHANGED
@@ -97,7 +97,7 @@ const shellOpt = isWin ? { shell: true } : {}
97
97
  // DEP0190(issue #148):shell:true + 非空参数数组触发语法级弃用告警——
98
98
  // 转义后拼进命令字符串(空参数数组不触发),非 Windows 保持数组直传。
99
99
  const cmd = (command, args) =>
100
- isWin ? [`${command} ${shellQuote(args).join(' ')}`, []] : [command, args]
100
+ isWin ? [`${shellQuote([command])[0]} ${shellQuote(args).join(' ')}`, []] : [command, args]
101
101
 
102
102
  // 内联 semver(解析 + 严格大于):启动器可能在依赖不完整的环境里被执行
103
103
  // (迁移、半损坏安装、测试沙箱),零外部依赖是自保底线。覆盖 semver 的
@@ -136,8 +136,12 @@ const isVersionNewer = (a, b) => {
136
136
  const lang = process.env.DSH_TUI_LANG === 'en' ? 'en' : 'zh'
137
137
  const MSG = {
138
138
  noDsh: {
139
- en: '[dsh-cli] dsh CLI not found. Install the official client first:\n npm install -g @deepseek-ai/dsh',
140
- zh: '[dsh-cli] 未检测到 dsh CLI。请先安装官方客户端:\n npm install -g @deepseek-ai/dsh',
139
+ en: '[dsh-cli] dsh CLI not found. Install it or set DSH_TUI_DSH_ROOT to a built Harness checkout:\n npm install -g @deepseek-ai/dsh',
140
+ zh: '[dsh-cli] 未检测到 dsh CLI。可安装官方客户端,或将 DSH_TUI_DSH_ROOT 指向已构建的 Harness 源码目录:\n npm install -g @deepseek-ai/dsh',
141
+ },
142
+ invalidDshRoot: {
143
+ en: root => `[dsh-cli] DSH_TUI_DSH_ROOT must be an absolute, built DeepSeek Harness checkout: ${root}`,
144
+ zh: root => `[dsh-cli] DSH_TUI_DSH_ROOT 必须指向已构建的 DeepSeek Harness 源码绝对路径:${root}`,
141
145
  },
142
146
  noPnpm: {
143
147
  en: '[dsh-cli] The first-time setup needs pnpm (dsh plugin delegates installs to it):\n npm install -g pnpm (or via corepack: corepack enable pnpm)',
@@ -541,6 +545,19 @@ if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
541
545
  console.log(msg('helpText'))
542
546
  process.exit(0)
543
547
  }
548
+ // A source checkout does not put `dsh` on PATH. An explicit Harness root lets
549
+ // the launcher run its built CLI while keeping the caller's project cwd.
550
+ const dshRoot = process.env.DSH_TUI_DSH_ROOT
551
+ let dshBin
552
+ if (dshRoot !== undefined) {
553
+ const candidate = join(dshRoot, 'apps', 'cli', 'lib', 'bin.js')
554
+ if (!isAbsolute(dshRoot) || readJson(join(dshRoot, 'apps', 'cli', 'package.json'))?.name !== '@deepseek-ai/dsh' || !existsSync(candidate)) {
555
+ console.error(msg('invalidDshRoot')(dshRoot))
556
+ process.exit(1)
557
+ }
558
+ dshBin = candidate
559
+ }
560
+ const dshCmd = args => dshBin === undefined ? cmd('dsh', args) : cmd(process.execPath, [dshBin, ...args])
544
561
  /**
545
562
  * Whether the DSH credential store declares a reference by this name.
546
563
  *
@@ -578,7 +595,7 @@ const runDoctorChecks = () => {
578
595
  lines.push(`dsh-cli doctor · ${PACKAGE} ${ownVersion ?? 'unknown'}`)
579
596
  report(true, 'node', `${process.version} · ${process.platform} ${process.arch}`)
580
597
  const probeVersion = command => {
581
- const probe = spawnSync(...cmd(command, ['--version']), { stdio: 'pipe', encoding: 'utf8', ...shellOpt })
598
+ const probe = spawnSync(...command(['--version']), { stdio: 'pipe', encoding: 'utf8', ...shellOpt })
582
599
  if (probe.error || probe.status !== 0) return undefined
583
600
  // 白名单校验:只回显版本号形状的首行。诊断输出的红线是绝不泄露密钥,
584
601
  // 而 PATH 上的 wrapper 理论上可以把任意环境变量 echo 进 --version——
@@ -586,14 +603,14 @@ const runDoctorChecks = () => {
586
603
  const line = String(probe.stdout ?? '').trim().split('\n')[0] ?? ''
587
604
  return /^v?\d[\w.+-]*$/.test(line) ? line : '(version unreadable)'
588
605
  }
589
- const dshVersion = probeVersion('dsh')
606
+ const dshVersion = probeVersion(dshCmd)
590
607
  if (dshVersion === undefined) {
591
608
  hardFailure = true
592
609
  report(false, 'dsh', L.dshMissing)
593
610
  } else {
594
611
  report(true, 'dsh', dshVersion)
595
612
  }
596
- const pnpmVersion = probeVersion('pnpm')
613
+ const pnpmVersion = probeVersion(args => cmd('pnpm', args))
597
614
  report(pnpmVersion !== undefined, 'pnpm', pnpmVersion ?? L.pnpmMissing)
598
615
  const profileVersion = readJson(installedPkgPath)?.version
599
616
  if (profileVersion === undefined) {
@@ -722,7 +739,7 @@ const forwardExit = child => {
722
739
  // 不从数值反推信号(spec §5.1)。
723
740
  const startDshSession = (dshArgs, profile = PROFILE, env = process.env) =>
724
741
  new Promise(resolve => {
725
- const child = spawn(...cmd('dsh', ['--profile', profile, ...dshArgs]), {
742
+ const child = spawn(...dshCmd(['--profile', profile, ...dshArgs]), {
726
743
  stdio: 'inherit',
727
744
  env,
728
745
  ...shellOpt,
@@ -1029,11 +1046,11 @@ const createRescueProfile = () => {
1029
1046
  }
1030
1047
  console.log(msg('safeRescueCleanup')(rescueProfileDir))
1031
1048
  }
1032
- const probe = spawnSync(...cmd('dsh', ['--version']), { stdio: 'pipe', ...shellOpt })
1049
+ const probe = spawnSync(...dshCmd(['--version']), { stdio: 'pipe', ...shellOpt })
1033
1050
  if (probe.error || probe.status !== 0) return { kind: 'failed', lines: [msg('safeRescueFailed')('dsh missing')] }
1034
1051
  console.log(msg('safeRescueCreating'))
1035
1052
  const runAdd = extraArgs => spawnSync(
1036
- ...cmd('dsh', ['plugin', '--profile', RESCUE_PROFILE, 'add', ...extraArgs, `${PACKAGE}@${installVersion}`]),
1053
+ ...dshCmd(['plugin', '--profile', RESCUE_PROFILE, 'add', ...extraArgs, `${PACKAGE}@${installVersion}`]),
1037
1054
  { stdio: ['inherit', 'pipe', 'pipe'], env: rescueEnv(), ...shellOpt },
1038
1055
  )
1039
1056
  let add = runAdd([])
@@ -1099,7 +1116,7 @@ const profileReady = () => {
1099
1116
  }
1100
1117
  }
1101
1118
  const bootstrapProfile = () => {
1102
- const probe = spawnSync(...cmd('dsh', ['--version']), { stdio: 'pipe', ...shellOpt })
1119
+ const probe = spawnSync(...dshCmd(['--version']), { stdio: 'pipe', ...shellOpt })
1103
1120
  if (probe.error || probe.status !== 0) {
1104
1121
  console.error(msg('noDsh'))
1105
1122
  process.exit(1)
@@ -1111,7 +1128,7 @@ const bootstrapProfile = () => {
1111
1128
  }
1112
1129
  console.log(msg('bootstrapStart'))
1113
1130
  const runAdd = (extraArgs, capture) => spawnSync(
1114
- ...cmd('dsh', ['plugin', '--profile', PROFILE, 'add', ...extraArgs, `${PACKAGE}@${installVersion}`]),
1131
+ ...dshCmd(['plugin', '--profile', PROFILE, 'add', ...extraArgs, `${PACKAGE}@${installVersion}`]),
1115
1132
  { stdio: capture ? ['inherit', 'pipe', 'pipe'] : 'inherit', ...shellOpt },
1116
1133
  )
1117
1134
  let add = runAdd([], true)
@@ -1225,7 +1242,7 @@ if (subcommand === 'safe') {
1225
1242
  if (subcommand === 'update') {
1226
1243
  if (!profileReady()) bootstrapProfile()
1227
1244
  {
1228
- const probe = spawnSync(...cmd('dsh', ['--version']), { stdio: 'pipe', ...shellOpt })
1245
+ const probe = spawnSync(...dshCmd(['--version']), { stdio: 'pipe', ...shellOpt })
1229
1246
  if (probe.error || probe.status !== 0) {
1230
1247
  console.error(msg('noDsh'))
1231
1248
  process.exit(1)
@@ -1291,7 +1308,7 @@ if (!runningInsideProfile && ownVersion !== undefined && process.env.DSH_TUI_NO_
1291
1308
  // ─── profile 副本(或源码运行):完整启动逻辑 ─────────────────────────────
1292
1309
  // dsh CLI 预检(缺失时给安装指引,先于一切 profile 逻辑)。
1293
1310
  {
1294
- const probe = spawnSync(...cmd('dsh', ['--version']), { stdio: 'pipe', ...shellOpt })
1311
+ const probe = spawnSync(...dshCmd(['--version']), { stdio: 'pipe', ...shellOpt })
1295
1312
  if (probe.error || probe.status !== 0) {
1296
1313
  console.error(msg('noDsh'))
1297
1314
  process.exit(1)
@@ -24,6 +24,9 @@ export declare function ModelPicker(props: {
24
24
  /** Current route key — its group row carries the ✓ marker. */
25
25
  currentProvider: string;
26
26
  onPick?: (index: number) => void;
27
+ query?: string;
28
+ cursor?: number;
29
+ emptyReason?: 'loading' | 'empty' | 'no-match' | 'error';
27
30
  } | {
28
31
  /** Second level (or single-group fast path): one provider's models —
29
32
  * or the mixed-provider recents list (`showProviderPrefix`). */
@@ -38,5 +41,8 @@ export declare function ModelPicker(props: {
38
41
  /** `provider/model` of the current model — its row carries the ✓ marker. */
39
42
  currentModel: string;
40
43
  onPick?: (index: number) => void;
44
+ query?: string;
45
+ cursor?: number;
46
+ emptyReason?: 'loading' | 'empty' | 'no-match' | 'error';
41
47
  }): React.ReactNode;
42
48
  //# sourceMappingURL=ModelPicker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ModelPicker.d.ts","sourceRoot":"","sources":["../../../src/components/ModelPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAQtD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAC7B;IACA,+DAA+D;IAC/D,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GACC;IACA;qEACiE;IACjE,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;IAC/B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAA;IACjB,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC,GAAG,KAAK,CAAC,SAAS,CA+DpB"}
1
+ {"version":3,"file":"ModelPicker.d.ts","sourceRoot":"","sources":["../../../src/components/ModelPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAUtD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAC7B;IACA,+DAA+D;IAC/D,MAAM,EAAE,SAAS,aAAa,EAAE,CAAA;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,8DAA8D;IAC9D,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAA;CACzD,GACC;IACA;qEACiE;IACjE,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;IAC/B,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAA;IACjB,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAA;CACzD,GAAG,KAAK,CAAC,SAAS,CA4EpB"}
@@ -7,6 +7,8 @@ import { ListItem } from './design-system/ListItem.js';
7
7
  import { HintLine } from './design-system/HintLine.js';
8
8
  import { listWindow } from './listWindow.js';
9
9
  import { useOverlayListRows } from './OverlayAbove.js';
10
+ import { SearchBox } from './SearchBox.js';
11
+ import { useTerminalFocus } from '../ink/hooks/use-terminal-focus.js';
10
12
  /**
11
13
  * Model picker: a permission-colored Pane with
12
14
  * the rows as Select entries (❯ focus pointer, ✓ on the active row,
@@ -25,27 +27,28 @@ import { useOverlayListRows } from './OverlayAbove.js';
25
27
  */
26
28
  export function ModelPicker(props) {
27
29
  const inGroups = 'groups' in props;
30
+ const terminalFocused = useTerminalFocus();
28
31
  // Captured before the map: union narrowing does not survive into closures.
29
32
  const onPick = props.onPick;
30
33
  // 焦点窗口化按行预算:ListItem 带 description 时占 2 行(正文+描述,均
31
34
  // truncate 成单行),只数项数会把焦点裁出浮层(二次审查实证)。
32
35
  // 预算来自最近一层 OverlayAbove 的有效高度(已钳到输入簇上方的真实空间——
33
36
  // 按 terminalRows 预算在短会话 + 高终端下窗口高过浮层、顶部整行被裁、
34
- // 焦点行不可见,#493/#698),减去本面板框架行:Pane 2 + 标题 2 + 页脚 1
35
- // + 挂载包裹 marginTop 1 = 6。
37
+ // 焦点行不可见,#493/#698),减去本面板框架行:Pane 2 + 标题 2 +
38
+ // 搜索栏 1 + 结果数 1 + 页脚 1 + 挂载包裹 marginTop 1 = 8。
36
39
  const rowHeights = inGroups
37
40
  ? props.groups.map(() => 2)
38
41
  : props.models.map(m => (m.description ? 2 : 1));
39
42
  const rows = inGroups ? props.groups : props.models;
40
- const listRows = useOverlayListRows(6);
43
+ const listRows = useOverlayListRows(8);
41
44
  const { start, end } = listWindow(rowHeights, props.focusIndex, listRows);
42
45
  const hint = inGroups
43
46
  ? t('hint-model-groups')
44
47
  : props.showBack ? t('hint-model-back') : t('hint-confirm-exit');
45
- return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: inGroups || props.groupLabel === undefined ? t('picker-title-model') : props.groupLabel }) }), rows.slice(start, end).map((row, index) => {
48
+ return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: inGroups || props.groupLabel === undefined ? t('picker-title-model') : props.groupLabel }) }), _jsx(SearchBox, { query: props.query ?? '', cursorOffset: props.cursor ?? 0, isFocused: true, isTerminalFocused: terminalFocused, borderless: true, placeholder: t('picker-model-search') }), _jsx(Text, { dimColor: true, children: t('picker-model-results', { count: rows.length }) }), rows.length === 0 && _jsx(Text, { dimColor: true, children: t(props.emptyReason === 'loading' ? 'model-loading' : props.emptyReason === 'error' ? 'picker-model-error' : props.emptyReason === 'no-match' ? 'picker-model-no-match' : 'picker-model-empty') }), rows.slice(start, end).map((row, index) => {
46
49
  const absoluteIndex = start + index;
47
- return inGroups ? (_jsx(ListItem, { isFocused: absoluteIndex === props.focusIndex, isSelected: row.provider === props.currentProvider, description: t('picker-group-count', { count: row.count }), showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: row.label === RECENTS_LABEL_PLACEHOLDER && row.provider === RECENTS_GROUP_PROVIDER
50
+ return inGroups ? (_jsx(ListItem, { isFocused: absoluteIndex === props.focusIndex, declareCursor: false, isSelected: row.provider === props.currentProvider, description: t('picker-group-count', { count: row.count }), showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: row.label === RECENTS_LABEL_PLACEHOLDER && row.provider === RECENTS_GROUP_PROVIDER
48
51
  ? t('picker-group-recent')
49
- : row.label }, row.provider)) : (_jsx(ListItem, { isFocused: absoluteIndex === props.focusIndex, isSelected: `${row.provider}/${row.id}` === props.currentModel, description: row.description, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: props.showProviderPrefix === true ? `${row.provider} / ${row.name}` : row.name }, `${row.provider}/${row.id}`));
52
+ : row.label }, row.provider)) : (_jsx(ListItem, { isFocused: absoluteIndex === props.focusIndex, declareCursor: false, isSelected: `${row.provider}/${row.id}` === props.currentModel, description: row.description, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: props.showProviderPrefix === true ? `${row.provider} / ${row.name}` : row.name }, `${row.provider}/${row.id}`));
50
53
  })] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: hint }) })] }));
51
54
  }
@@ -0,0 +1,19 @@
1
+ export interface CliRegistrationOptions {
2
+ home?: string;
3
+ dshHome?: string;
4
+ path?: string;
5
+ shell?: string;
6
+ platform?: NodeJS.Platform;
7
+ zdotdir?: string;
8
+ xdgConfigHome?: string;
9
+ localAppData?: string;
10
+ cliEntry?: string;
11
+ dshRoot?: string;
12
+ windowsUserPath?: {
13
+ read(): string;
14
+ write(value: string): void;
15
+ };
16
+ }
17
+ /** Register a user-owned launcher for the existing dsh-cli profile. No DSH state is changed. */
18
+ export declare function ensureCliRegistered(options?: CliRegistrationOptions): string;
19
+ //# sourceMappingURL=cli-registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-registration.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/cli-registration.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAA;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE;QAAE,IAAI,IAAI,MAAM,CAAC;QAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CACjE;AAqFD,gGAAgG;AAChG,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CAgFhF"}
@@ -0,0 +1,191 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
5
+ const begin = '# BEGIN dsh-cli managed PATH';
6
+ const end = '# END dsh-cli managed PATH';
7
+ const marker = '# dsh-cli managed launcher';
8
+ function pathExists(path) {
9
+ try {
10
+ lstatSync(path);
11
+ return true;
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ function quoted(value) {
18
+ return `'${value.replace(/'/g, `'\\''`)}'`;
19
+ }
20
+ function atomicWrite(path, content, mode) {
21
+ const temporary = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
22
+ try {
23
+ writeFileSync(temporary, content, { flag: 'wx', mode });
24
+ renameSync(temporary, path);
25
+ }
26
+ finally {
27
+ if (existsSync(temporary))
28
+ rmSync(temporary);
29
+ }
30
+ }
31
+ function updateOwnedFile(path, content, mode) {
32
+ const target = pathExists(path) && lstatSync(path).isSymbolicLink() ? realpathSync(path) : path;
33
+ if (pathExists(target)) {
34
+ const current = readFileSync(target, 'utf8');
35
+ if (current === content)
36
+ return false;
37
+ if (!current.startsWith(`#!/bin/sh\n${marker}`) && !current.startsWith('@echo off\r\nrem dsh-cli managed launcher\r\n')) {
38
+ throw new Error(`${path} already belongs to another command`);
39
+ }
40
+ }
41
+ mkdirSync(dirname(target), { recursive: true });
42
+ atomicWrite(target, content, mode);
43
+ return true;
44
+ }
45
+ function updateShellFile(path, line) {
46
+ const target = pathExists(path) && lstatSync(path).isSymbolicLink() ? realpathSync(path) : path;
47
+ const current = pathExists(target) ? readFileSync(target, 'utf8') : '';
48
+ const first = current.indexOf(begin);
49
+ const last = current.indexOf(end);
50
+ if ((first < 0) !== (last < 0) || (first >= 0 && (last < first || current.indexOf(begin, first + begin.length) >= 0 || current.indexOf(end, last + end.length) >= 0))) {
51
+ throw new Error(`malformed dsh-cli PATH block in ${path}`);
52
+ }
53
+ const block = `${begin}\n${line}\n${end}`;
54
+ const next = first < 0
55
+ ? `${current}${current !== '' && !current.endsWith('\n') ? '\n' : ''}${block}\n`
56
+ : current.slice(0, first) + block + current.slice(last + end.length);
57
+ if (next === current)
58
+ return false;
59
+ mkdirSync(dirname(target), { recursive: true });
60
+ atomicWrite(target, next, pathExists(target) ? statSync(target).mode : 0o600);
61
+ return true;
62
+ }
63
+ function posixShellFiles(home, shell, zdotdir, xdg) {
64
+ switch (basename(shell)) {
65
+ case 'zsh': return [join(zdotdir || home, '.zshrc')];
66
+ case 'fish': return [join(xdg || join(home, '.config'), 'fish', 'conf.d', 'dsh-cli.fish')];
67
+ case 'bash': {
68
+ const login = ['.bash_profile', '.bash_login', '.profile'].map(name => join(home, name));
69
+ return [join(home, '.bashrc'), login.find(existsSync) ?? login[0]];
70
+ }
71
+ default: throw new Error(`unsupported shell ${shell}; add ~/.local/bin to PATH manually`);
72
+ }
73
+ }
74
+ /** Resolve a Harness source entry, never the caller's project directory. */
75
+ function harnessSourceRoot(cliEntry) {
76
+ if (cliEntry === undefined)
77
+ return undefined;
78
+ try {
79
+ const entry = realpathSync(cliEntry);
80
+ const root = resolve(dirname(entry), '../../..');
81
+ if (entry !== join(root, 'apps', 'cli', 'src', 'bin.ts')
82
+ && entry !== join(root, 'apps', 'cli', 'lib', 'bin.js'))
83
+ return undefined;
84
+ const pkg = JSON.parse(readFileSync(join(root, 'apps', 'cli', 'package.json'), 'utf8'));
85
+ return pkg.name === '@deepseek-ai/dsh' ? root : undefined;
86
+ }
87
+ catch {
88
+ return undefined;
89
+ }
90
+ }
91
+ function configuredHarnessRoot(root) {
92
+ if (root === undefined || !isAbsolute(root))
93
+ return undefined;
94
+ try {
95
+ const pkg = JSON.parse(readFileSync(join(root, 'apps', 'cli', 'package.json'), 'utf8'));
96
+ return pkg.name === '@deepseek-ai/dsh' ? realpathSync(root) : undefined;
97
+ }
98
+ catch {
99
+ return undefined;
100
+ }
101
+ }
102
+ /** Register a user-owned launcher for the existing dsh-cli profile. No DSH state is changed. */
103
+ export function ensureCliRegistered(options = {}) {
104
+ const home = options.home ?? homedir();
105
+ const dshHome = options.dshHome ?? process.env.DSH_HOME ?? join(home, '.dsh');
106
+ const platform = options.platform ?? process.platform;
107
+ const shell = options.shell ?? process.env.SHELL ?? '';
108
+ const pathValue = options.path ?? process.env.PATH ?? '';
109
+ const sourceRoot = harnessSourceRoot(options.cliEntry ?? process.argv[1])
110
+ ?? configuredHarnessRoot(options.dshRoot ?? process.env.DSH_TUI_DSH_ROOT);
111
+ const binDir = platform === 'win32'
112
+ ? join(options.localAppData ?? process.env.LOCALAPPDATA ?? join(home, 'AppData', 'Local'), 'dsh-cli', 'bin')
113
+ : join(home, '.local', 'bin');
114
+ const command = join(binDir, platform === 'win32' ? 'dsh-cli.cmd' : 'dsh-cli');
115
+ const profilePackage = join(dshHome, 'profiles', 'dsh-cli', 'node_modules', '@askdkc', 'dsh-cli');
116
+ try {
117
+ if (JSON.parse(readFileSync(join(profilePackage, 'package.json'), 'utf8')).name !== '@askdkc/dsh-cli') {
118
+ throw new Error('unexpected package identity');
119
+ }
120
+ }
121
+ catch {
122
+ throw new Error(`dsh-cli profile package is missing or unreadable at ${profilePackage}`);
123
+ }
124
+ const currentCommand = pathValue.split(platform === 'win32' ? ';' : ':')
125
+ .map(dir => join(dir, platform === 'win32' ? 'dsh-cli.cmd' : 'dsh-cli'))
126
+ .find(candidate => candidate !== command && pathExists(candidate) && !candidate.includes(`${join('node_modules', '.bin')}`));
127
+ if (currentCommand !== undefined)
128
+ return `Existing dsh-cli command at ${currentCommand} was kept; automatic registration skipped.`;
129
+ mkdirSync(binDir, { recursive: true });
130
+ const lock = join(binDir, '.dsh-cli-registration.lock');
131
+ let handle;
132
+ try {
133
+ handle = openSync(lock, 'wx');
134
+ }
135
+ catch {
136
+ if (pathExists(lock) && Date.now() - statSync(lock).mtimeMs > 120000) {
137
+ rmSync(lock, { force: true });
138
+ handle = openSync(lock, 'wx');
139
+ }
140
+ else {
141
+ throw new Error(`registration is already running (${lock}); retry on next TUI start`);
142
+ }
143
+ }
144
+ try {
145
+ const target = join('profiles', 'dsh-cli', 'node_modules', '@askdkc', 'dsh-cli', 'bin', 'dsh-tui.js');
146
+ const content = platform === 'win32'
147
+ ? `@echo off\r\nrem dsh-cli managed launcher\r\nsetlocal DisableDelayedExpansion\r\nif not defined DSH_HOME set "DSH_HOME=${dshHome.replace(/%/g, '%%')}"\r\nnode "%DSH_HOME%\\${target.replace(/\//g, '\\')}" %*\r\nexit /b %ERRORLEVEL%\r\n`
148
+ : `#!/bin/sh\n${marker}\nDEFAULT_DSH_HOME=${quoted(dshHome)}\n: "\${DSH_HOME:=$DEFAULT_DSH_HOME}"\nexport DSH_HOME\nexec node "$DSH_HOME/${target}" "$@"\n`;
149
+ let keepExistingDefault = false;
150
+ if (pathExists(command)) {
151
+ const existing = readFileSync(command, 'utf8');
152
+ keepExistingDefault = existing.startsWith(platform === 'win32' ? '@echo off\r\nrem dsh-cli managed launcher\r\n' : `#!/bin/sh\n${marker}\n`)
153
+ && existing !== content;
154
+ }
155
+ const changed = keepExistingDefault ? false : updateOwnedFile(command, content, 0o755);
156
+ if (platform === 'win32') {
157
+ const userPath = options.windowsUserPath ?? {
158
+ read: () => execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', '[Environment]::GetEnvironmentVariable("Path", "User")'], { encoding: 'utf8', timeout: 5000 }).trim(),
159
+ write: (value) => {
160
+ execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', '[Environment]::SetEnvironmentVariable("Path", $env:DSH_CLI_USER_PATH, "User")'], {
161
+ env: { ...process.env, DSH_CLI_USER_PATH: value }, timeout: 5000,
162
+ });
163
+ },
164
+ };
165
+ const current = userPath.read();
166
+ if (!current.split(';').some(part => part.toLowerCase() === binDir.toLowerCase())) {
167
+ const next = current ? `${current};${binDir}` : binDir;
168
+ userPath.write(next);
169
+ }
170
+ }
171
+ else {
172
+ const files = posixShellFiles(home, shell, options.zdotdir ?? process.env.ZDOTDIR, options.xdgConfigHome ?? process.env.XDG_CONFIG_HOME);
173
+ const pathLine = basename(shell) === 'fish'
174
+ ? `contains -- ${quoted(binDir)} $PATH; or set -gx PATH ${quoted(binDir)} $PATH`
175
+ : `case ":$PATH:" in *:${quoted(binDir)}:*) ;; *) export PATH=${quoted(binDir)}:"$PATH" ;; esac`;
176
+ const sourceLine = sourceRoot === undefined ? '' : basename(shell) === 'fish'
177
+ ? `\nset -q DSH_TUI_DSH_ROOT; or set -gx DSH_TUI_DSH_ROOT ${quoted(sourceRoot)}`
178
+ : `\nif [ -z "\${DSH_TUI_DSH_ROOT:-}" ]; then export DSH_TUI_DSH_ROOT=${quoted(sourceRoot)}; fi`;
179
+ const line = pathLine + sourceLine;
180
+ for (const file of files)
181
+ updateShellFile(file, line);
182
+ }
183
+ return keepExistingDefault
184
+ ? `Kept ${command} with its original DSH_HOME default. Open a new shell to use dsh-cli; set DSH_HOME explicitly to use another installation.`
185
+ : changed ? `Registered ${command}. Open a new shell to use dsh-cli.` : `dsh-cli registration is current at ${command}.`;
186
+ }
187
+ finally {
188
+ closeSync(handle);
189
+ rmSync(lock, { force: true });
190
+ }
191
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,EAA+D,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAwDtH,OAAO,EAAE,MAAM,EAAkC,MAAM,UAAU,CAAA;AAgCjE;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,CAcxF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,yBAAyB,GAAG,eAAe,CAAA;AAErF,wBAAgB,kBAAkB,CAChC,WAAW,UAAgC,EAC3C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,WAAW,CASb;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,WAAW,GAAE,OAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgnDzH;AAmMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV;AAgBD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,GAAG,SAAS,EACxD,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,IAAI,EAAE,MAAM,IAAI,GACf,OAAO,CAAC,IAAI,CAAC,CAmFf;AA4HD,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAIrE"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACnC,OAAO,EAA+D,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAyDtH,OAAO,EAAE,MAAM,EAAkC,MAAM,UAAU,CAAA;AAgCjE;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,CAcxF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,yBAAyB,GAAG,eAAe,CAAA;AAErF,wBAAgB,kBAAkB,CAChC,WAAW,UAAgC,EAC3C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,WAAW,CASb;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,WAAW,GAAE,OAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA4nDzH;AAmMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV;AAgBD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,GAAG,SAAS,EACxD,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,IAAI,EAAE,MAAM,IAAI,GACf,OAAO,CAAC,IAAI,CAAC,CAmFf;AA4HD,gFAAgF;AAChF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAIrE"}
@@ -46,6 +46,7 @@ import { reserveMount } from '../sessionMounts.js';
46
46
  import { getHostDialogStore } from './dialogs.js';
47
47
  import { getHostStatusStore } from './status.js';
48
48
  import { createActivityStore } from './activity-store.js';
49
+ import { ensureCliRegistered } from './cli-registration.js';
49
50
  import { getHostToastStore } from './toast.js';
50
51
  import { getHostShortcuts } from './shortcuts.js';
51
52
  import { getHostThemes } from './themes.js';
@@ -210,6 +211,21 @@ export async function apply(ctx, runtimeConfig, configOwner = ctx) {
210
211
  // render so every module resolves strings in the same language.
211
212
  const envLang = process.env.DSH_TUI_LANG;
212
213
  setLang(isLang(envLang) ? envLang : isLang(config.lang) ? config.lang : resolveStartupLang());
214
+ if (resolveDshProfileName() === 'dsh-cli'
215
+ && !isStandaloneRuntime()
216
+ && !process.env.CI
217
+ && process.env.DSH_TUI_AUTO_REGISTER_CLI !== '0') {
218
+ try {
219
+ const result = ensureCliRegistered();
220
+ if (result.startsWith('Registered ') || result.startsWith('Kept '))
221
+ ctx.logger.info(`dsh-tui: ${result}`);
222
+ else if (result.startsWith('Existing '))
223
+ ctx.logger.warn(`dsh-tui: ${result} Remove the conflicting command or adjust PATH, then restart the TUI.`);
224
+ }
225
+ catch (error) {
226
+ ctx.logger.warn(`dsh-tui: dsh-cli command registration failed: ${error instanceof Error ? error.message : String(error)}. Resolve the reported path or permission issue, then restart the TUI; set DSH_TUI_AUTO_REGISTER_CLI=0 to disable registration.`);
227
+ }
228
+ }
213
229
  // /update restart verification: the pre-update process stamps the version
214
230
  // it was leaving behind; if the freshly loaded one is not newer, the
215
231
  // package manager "succeeded" without actually moving the version (mirror
@@ -2884,6 +2884,26 @@ declare const dict: {
2884
2884
  readonly zh: "{{count}} 个模型";
2885
2885
  readonly en: "{{count}} models";
2886
2886
  };
2887
+ readonly 'picker-model-search': {
2888
+ readonly zh: "输入关键词筛选模型";
2889
+ readonly en: "Type keywords to filter models";
2890
+ };
2891
+ readonly 'picker-model-results': {
2892
+ readonly zh: "{{count}} 个结果";
2893
+ readonly en: "{{count}} results";
2894
+ };
2895
+ readonly 'picker-model-empty': {
2896
+ readonly zh: "没有可用模型";
2897
+ readonly en: "No models available";
2898
+ };
2899
+ readonly 'picker-model-no-match': {
2900
+ readonly zh: "没有匹配的模型";
2901
+ readonly en: "No matching models";
2902
+ };
2903
+ readonly 'picker-model-error': {
2904
+ readonly zh: "模型列表获取失败,请重新打开 /model";
2905
+ readonly en: "Could not load models; reopen /model to retry";
2906
+ };
2887
2907
  readonly 'hint-model-groups': {
2888
2908
  readonly zh: "**Enter** 查看模型 · Esc 退出";
2889
2909
  readonly en: "**Enter** to view models · Esc to exit";
@@ -2964,10 +2984,6 @@ declare const dict: {
2964
2984
  readonly zh: "正在加载模型";
2965
2985
  readonly en: "Loading models";
2966
2986
  };
2967
- readonly 'model-loading-subtitle': {
2968
- readonly zh: "正在查询 provider…";
2969
- readonly en: "Querying the provider…";
2970
- };
2971
2987
  readonly 'model-switching': {
2972
2988
  readonly zh: "正在切换模型到 {{name}}…";
2973
2989
  readonly en: "Switching model to {{name}}…";
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
package/lib/types/i18n.js CHANGED
@@ -900,6 +900,11 @@ const dict = {
900
900
  'picker-title-model': { zh: '模型', en: 'Model' },
901
901
  'picker-group-recent': { zh: '最近使用', en: 'Recently used' },
902
902
  'picker-group-count': { zh: '{{count}} 个模型', en: '{{count}} models' },
903
+ 'picker-model-search': { zh: '输入关键词筛选模型', en: 'Type keywords to filter models' },
904
+ 'picker-model-results': { zh: '{{count}} 个结果', en: '{{count}} results' },
905
+ 'picker-model-empty': { zh: '没有可用模型', en: 'No models available' },
906
+ 'picker-model-no-match': { zh: '没有匹配的模型', en: 'No matching models' },
907
+ 'picker-model-error': { zh: '模型列表获取失败,请重新打开 /model', en: 'Could not load models; reopen /model to retry' },
903
908
  'hint-model-groups': { zh: '**Enter** 查看模型 · Esc 退出', en: '**Enter** to view models · Esc to exit' },
904
909
  'hint-model-back': { zh: '**Enter** 切换模型 · Esc/⌫ 返回上级', en: '**Enter** to switch · Esc/⌫ to go back' },
905
910
  'picker-title-skills': { zh: '技能', en: 'Skills' },
@@ -920,7 +925,6 @@ const dict = {
920
925
  'picker-title-color': { zh: '会话强调色', en: 'Session accent color' },
921
926
  'picker-title-effort': { zh: '推理强度', en: 'Reasoning effort' },
922
927
  'model-loading': { zh: '正在加载模型', en: 'Loading models' },
923
- 'model-loading-subtitle': { zh: '正在查询 provider…', en: 'Querying the provider…' },
924
928
  'model-switching': { zh: '正在切换模型到 {{name}}…', en: 'Switching model to {{name}}…' },
925
929
  'model-switched': { zh: '模型已切换为 {{name}}', en: 'Model switched to {{name}}' },
926
930
  // ── components/RewindPicker.tsx ─────────────────────────────────────
@@ -31,6 +31,8 @@ export interface ModelGroupRow {
31
31
  /** How many of the listed models belong to this provider. */
32
32
  readonly count: number;
33
33
  }
34
+ /** Literal, case-insensitive AND matching across a model's identifying fields. */
35
+ export declare function filterModels(models: readonly LlmModelInfo[], query: string, providers: readonly LlmProviderInfo[]): readonly LlmModelInfo[];
34
36
  /**
35
37
  * The recent refs that the current catalog still lists, most-recent-first —
36
38
  * the second level of the recents group. Refs whose model vanished from the
@@ -1 +1 @@
1
- {"version":3,"file":"modelGroups.d.ts","sourceRoot":"","sources":["../../src/modelGroups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEpF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAA;AAEnD,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,eAAe,CAAA;AAErD,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,MAAM,EAAE,SAAS,YAAY,EAAE,GAC9B,SAAS,YAAY,EAAE,CAUzB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,aAAa,EAAE,SAAS,eAAe,EAAE,EACzC,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,GAC5B,SAAS,aAAa,EAAE,CAsB1B;AAED,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,GAC5B,kBAAkB,CAiBpB"}
1
+ {"version":3,"file":"modelGroups.d.ts","sourceRoot":"","sources":["../../src/modelGroups.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEpF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAA;AAEnD,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,eAAe,CAAA;AAErD,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,SAAS,YAAY,EAAE,CAQzB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,MAAM,EAAE,SAAS,YAAY,EAAE,GAC9B,SAAS,YAAY,EAAE,CAUzB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,aAAa,EAAE,SAAS,eAAe,EAAE,EACzC,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,GAC5B,SAAS,aAAa,EAAE,CAsB1B;AAED,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,GAC5B,kBAAkB,CAiBpB"}
@@ -16,6 +16,17 @@
16
16
  export const RECENTS_GROUP_PROVIDER = '__recents__';
17
17
  /** The recents row's raw label; renderers replace it with the localized one. */
18
18
  export const RECENTS_LABEL_PLACEHOLDER = '__recent__';
19
+ /** Literal, case-insensitive AND matching across a model's identifying fields. */
20
+ export function filterModels(models, query, providers) {
21
+ const terms = query.trim().toLowerCase().split(/\s+/u).filter(Boolean);
22
+ if (terms.length === 0)
23
+ return models;
24
+ return models.filter(model => {
25
+ const providerName = providers.find(provider => provider.id === model.provider)?.name ?? '';
26
+ const fields = [model.name, model.id, model.provider, providerName].map(value => value.toLowerCase());
27
+ return terms.every(term => fields.some(field => field.includes(term)));
28
+ });
29
+ }
19
30
  /**
20
31
  * The recent refs that the current catalog still lists, most-recent-first —
21
32
  * the second level of the recents group. Refs whose model vanished from the
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AA2BzB,OAAO,KAAK,EAAE,SAAS,IAAI,OAAO,EAAE,MAAM,iCAAiC,CAAA;AAE3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAwB,MAAM,0BAA0B,CAAA;AAC/E,OAAO,EAAE,aAAa,EAAe,MAAM,kCAAkC,CAAA;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAkB3D,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAEjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AA2NxE,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,MAAM,EACN,QAAQ,EACR,SAAS,EACT,UAAkB,EAClB,cAAc,EAAE,kBAAkB,EAClC,mBAAmB,EACnB,mBAAmB,EAAE,uBAAuB,EAC5C,WAAW,EACX,cAAc,GACf,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAA;IAC/D,aAAa,EAAE,aAAa,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAA;IACjC,yDAAyD;IACzD,eAAe,CAAC,EAAE,cAAc,CAAA;IAChC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,eAAe,CAAA;IACpC,uEAAuE;IACvE,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IAC9D;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CAC/D,4BAg+IA"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/screens/Chat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AA6BzB,OAAO,KAAK,EAAE,SAAS,IAAI,OAAO,EAAE,MAAM,iCAAiC,CAAA;AAE3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAwB,MAAM,0BAA0B,CAAA;AAC/E,OAAO,EAAE,aAAa,EAAe,MAAM,kCAAkC,CAAA;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAG5D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAkB3D,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAEjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAyNxE,wBAAgB,IAAI,CAAC,EACnB,OAAO,EACP,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,kBAAkB,EAClB,SAAS,EACT,MAAM,EACN,QAAQ,EACR,SAAS,EACT,UAAkB,EAClB,cAAc,EAAE,kBAAkB,EAClC,mBAAmB,EACnB,mBAAmB,EAAE,uBAAuB,EAC5C,WAAW,EACX,cAAc,GACf,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAA;IAC/D,aAAa,EAAE,aAAa,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,cAAc,CAAA;IACjC,yDAAyD;IACzD,eAAe,CAAC,EAAE,cAAc,CAAA;IAChC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,eAAe,CAAA;IACpC,uEAAuE;IACvE,SAAS,CAAC,EAAE,YAAY,CAAA;IACxB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;IAC9D;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CAC/D,4BAilJA"}
@@ -17,7 +17,8 @@ import { formatTokens } from '../terminal-utils/format.js';
17
17
  import { homeDir } from '../utils/paths.js';
18
18
  import { execFileNoThrow } from '../utils/execFileNoThrow.js';
19
19
  import { cleanRenderText, cleanScalarText } from '../dsh-adapter/sanitize.js';
20
- import { deriveModelGroups, modelPickerLanding, recentCatalogModels, RECENTS_GROUP_PROVIDER, } from '../modelGroups.js';
20
+ import { nextModelQueryBoundary, previousModelQueryBoundary } from './modelSearchInput.js';
21
+ import { deriveModelGroups, filterModels, modelPickerLanding, recentCatalogModels, RECENTS_GROUP_PROVIDER, } from '../modelGroups.js';
21
22
  import { readModelRecents, recordModelUse } from '../modelRecents.js';
22
23
  import { TuiDialogStore } from '../dsh-adapter/dialogs.js';
23
24
  import { TuiStatusStore } from '../dsh-adapter/status.js';
@@ -99,8 +100,6 @@ import { markHomeSeen } from '../homePrefs.js';
99
100
  import { extendTrajectory, projectWave } from '../dsh-adapter/trajectory/index.js';
100
101
  import { miniWakeWidth } from '../components/trajectory/MiniWake.js';
101
102
  import { readTrajectorySeen, writeTrajectorySeen } from '../trajectoryPrefs.js';
102
- import { LoadingState } from '../components/design-system/LoadingState.js';
103
- import { Pane } from '../components/design-system/Pane.js';
104
103
  import { loadHistory } from '../history.js';
105
104
  import { formatLoadedContextReport } from '../utils/loaded-context.js';
106
105
  import { NO_OVERLAY, chatOverlayReducer, dialogOverlayVisible, wrapIndex, } from './chatOverlay.js';
@@ -343,18 +342,10 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
343
342
  }
344
343
  }, [overlay]);
345
344
  const [models, setModels] = React.useState([]);
345
+ const [modelCatalogStatus, setModelCatalogStatus] = React.useState('loading');
346
+ const modelRequestRef = React.useRef(0);
346
347
  /** Provider display identities for the /model group level; refreshed alongside `models`. */
347
348
  const [providerInfos, setProviderInfos] = React.useState([]);
348
- React.useEffect(() => {
349
- const subscribe = channel.providerSetup?.()?.oauth?.onCredentialChange;
350
- if (subscribe === undefined)
351
- return;
352
- return subscribe(() => {
353
- channel.invalidateModelCompletion();
354
- void channel.listModels().then(setModels).catch(() => setModels([]));
355
- void channel.listProviders().then(setProviderInfos).catch(() => setProviderInfos([]));
356
- });
357
- }, [channel]);
358
349
  /** /model 最近使用分组:成功切换即记录(去重置顶,上限 10),重启保留。 */
359
350
  const [modelRecents, setModelRecents] = React.useState(() => readModelRecents());
360
351
  /** Two-level /model: the drilled-in provider route; undefined = group level.
@@ -379,6 +370,69 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
379
370
  return recentCatalogModels(modelRecents, models);
380
371
  return models.filter(model => model.provider === activeModelGroup);
381
372
  }, [models, modelRecents, activeModelGroup]);
373
+ const modelQuery = overlay.kind === 'model' ? (overlay.query ?? '') : '';
374
+ const modelSearchActive = modelQuery.trim() !== '';
375
+ const visibleModelRows = React.useMemo(() => filterModels(activeModelGroup === undefined ? models : groupModels, modelQuery, providerInfos), [activeModelGroup, groupModels, models, modelQuery, providerInfos]);
376
+ const visibleModelGroups = activeModelGroup === undefined && !modelSearchActive ? modelGroups : [];
377
+ const modelRowsAreGroups = activeModelGroup === undefined && !modelSearchActive;
378
+ const modelRowCount = modelRowsAreGroups ? visibleModelGroups.length : visibleModelRows.length;
379
+ const modelFocus = overlay.kind === 'model'
380
+ ? Math.max(0, Math.min(overlay.index, modelRowCount - 1))
381
+ : 0;
382
+ const modelFocusedRef = React.useRef(null);
383
+ React.useEffect(() => {
384
+ modelFocusedRef.current = overlay.kind === 'model' ? {
385
+ query: modelQuery,
386
+ group: activeModelGroup,
387
+ provider: modelRowsAreGroups ? undefined : visibleModelRows[modelFocus]?.provider,
388
+ id: modelRowsAreGroups ? undefined : visibleModelRows[modelFocus]?.id,
389
+ groupKey: modelRowsAreGroups ? visibleModelGroups[modelFocus]?.provider : undefined,
390
+ } : null;
391
+ }, [overlay, modelQuery, activeModelGroup, modelRowsAreGroups, visibleModelRows, visibleModelGroups, modelFocus]);
392
+ React.useEffect(() => {
393
+ const subscribe = channel.providerSetup?.()?.oauth?.onCredentialChange;
394
+ if (subscribe === undefined)
395
+ return;
396
+ return subscribe(() => {
397
+ channel.invalidateModelCompletion();
398
+ const request = ++modelRequestRef.current;
399
+ void channel.listModels().then(list => {
400
+ if (request !== modelRequestRef.current)
401
+ return;
402
+ const focused = modelFocusedRef.current;
403
+ if (focused !== null) {
404
+ const freshGroups = deriveModelGroups(list, providerInfos, modelRecents);
405
+ if (focused.group !== undefined && !freshGroups.some(group => group.provider === focused.group)) {
406
+ setModelGroup(undefined);
407
+ setModelPickerDirect(false);
408
+ dispatchOverlay({ type: 'model-edit', query: '', cursor: 0 });
409
+ dispatchOverlay({ type: 'set-index', kind: 'model', index: 0 });
410
+ }
411
+ else {
412
+ const scope = focused.group === undefined ? list
413
+ : focused.group === RECENTS_GROUP_PROVIDER ? recentCatalogModels(modelRecents, list)
414
+ : list.filter(model => model.provider === focused.group);
415
+ const rows = filterModels(scope, focused.query, providerInfos);
416
+ const index = focused.group === undefined && focused.query.trim() === ''
417
+ ? freshGroups.findIndex(group => group.provider === focused.groupKey)
418
+ : rows.findIndex(model => model.provider === focused.provider && model.id === focused.id);
419
+ dispatchOverlay({ type: 'set-index', kind: 'model', index: Math.max(0, index) });
420
+ }
421
+ }
422
+ setModels(list);
423
+ setModelCatalogStatus('ready');
424
+ }).catch(() => {
425
+ if (request === modelRequestRef.current) {
426
+ setModelCatalogStatus('error');
427
+ channel.notify(t('picker-model-error'), { color: 'warning' });
428
+ }
429
+ });
430
+ void channel.listProviders().then(list => {
431
+ if (request === modelRequestRef.current)
432
+ setProviderInfos(list);
433
+ }).catch(() => undefined);
434
+ });
435
+ }, [channel, providerInfos, modelRecents]);
382
436
  /** Switch + record: every successful switch feeds the /model recents group
383
437
  * (picker Enter/click, `/model provider/id`, the wizard's live switch,
384
438
  * and /reload's applied model all ride this one path). */
@@ -1809,16 +1863,51 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
1809
1863
  const landing = modelPickerLanding(models, channel.provider, channel.model, recentsNow);
1810
1864
  setModelGroup(landing.group);
1811
1865
  setModelPickerDirect(landing.group !== undefined);
1812
- dispatchOverlay({ type: 'open', overlay: { kind: 'model', index: landing.index } });
1866
+ dispatchOverlay({ type: 'open', overlay: { kind: 'model', index: landing.index, query: '', cursor: 0 } });
1813
1867
  }
1868
+ const modelRequest = ++modelRequestRef.current;
1869
+ setModelCatalogStatus('loading');
1814
1870
  void channel.listModels().then((list) => {
1871
+ if (modelRequest !== modelRequestRef.current)
1872
+ return;
1873
+ const focused = modelFocusedRef.current;
1874
+ if (models.length === 0 && (focused === null || (focused.group === undefined && focused.query === ''))) {
1875
+ const landing = modelPickerLanding(list, channel.provider, channel.model, recentsNow);
1876
+ setModelGroup(landing.group);
1877
+ setModelPickerDirect(landing.group !== undefined);
1878
+ dispatchOverlay({ type: 'set-index', kind: 'model', index: landing.index });
1879
+ }
1880
+ else if (focused !== null) {
1881
+ const freshGroups = deriveModelGroups(list, providerInfos, recentsNow);
1882
+ if (focused.group !== undefined && !freshGroups.some(group => group.provider === focused.group)) {
1883
+ setModelGroup(undefined);
1884
+ setModelPickerDirect(false);
1885
+ dispatchOverlay({ type: 'model-edit', query: '', cursor: 0 });
1886
+ dispatchOverlay({ type: 'set-index', kind: 'model', index: 0 });
1887
+ }
1888
+ else {
1889
+ const scope = focused.group === undefined ? list
1890
+ : focused.group === RECENTS_GROUP_PROVIDER ? recentCatalogModels(recentsNow, list)
1891
+ : list.filter(model => model.provider === focused.group);
1892
+ const rows = filterModels(scope, focused.query, providerInfos);
1893
+ const index = focused.group === undefined && focused.query.trim() === ''
1894
+ ? freshGroups.findIndex(group => group.provider === focused.groupKey)
1895
+ : rows.findIndex(model => model.provider === focused.provider && model.id === focused.id);
1896
+ dispatchOverlay({ type: 'set-index', kind: 'model', index: Math.max(0, index) });
1897
+ }
1898
+ }
1815
1899
  setModels(list);
1816
- const landing = modelPickerLanding(list, channel.provider, channel.model, recentsNow);
1817
- setModelGroup(landing.group);
1818
- setModelPickerDirect(landing.group !== undefined);
1819
- dispatchOverlay({ type: 'set-index', kind: 'model', index: landing.index });
1900
+ setModelCatalogStatus('ready');
1901
+ }).catch(() => {
1902
+ if (modelRequest === modelRequestRef.current) {
1903
+ setModelCatalogStatus('error');
1904
+ channel.notify(t('picker-model-error'), { color: 'warning' });
1905
+ }
1820
1906
  });
1821
- void channel.listProviders().then(setProviderInfos).catch(() => setProviderInfos([]));
1907
+ void channel.listProviders().then(list => {
1908
+ if (modelRequest === modelRequestRef.current)
1909
+ setProviderInfos(list);
1910
+ }).catch(() => undefined);
1822
1911
  return true;
1823
1912
  }
1824
1913
  case 'skills': {
@@ -3176,22 +3265,24 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
3176
3265
  return;
3177
3266
  }
3178
3267
  if (overlay.kind === 'model') {
3268
+ event.stopImmediatePropagation();
3179
3269
  // Two-level picker: group rows at the top (Enter drills in), one
3180
3270
  // provider's models below (Enter switches, the same live-fork path as
3181
3271
  // the flat picker always had). Esc/⌫ climbs one level and only closes
3182
3272
  // at the top; a single-group catalog never shows the group level, so
3183
3273
  // Esc there closes directly.
3184
- const rowCount = activeModelGroup === undefined ? modelGroups.length : groupModels.length;
3274
+ const rowCount = modelRowCount;
3275
+ const query = overlay.query ?? '';
3276
+ const cursor = overlay.cursor ?? query.length;
3277
+ const editModelQuery = (value, nextCursor) => dispatchOverlay({ type: 'model-edit', query: value, cursor: nextCursor });
3185
3278
  if (key.upArrow || key.downArrow) {
3186
3279
  dispatchOverlay({ type: 'move', delta: key.upArrow ? -1 : 1, count: rowCount });
3187
3280
  }
3188
3281
  else if (plainReturn) {
3189
- if (activeModelGroup === undefined) {
3190
- const group = modelGroups[overlay.index];
3191
- if (!group) {
3192
- dispatchOverlay({ type: 'close' });
3282
+ if (modelRowsAreGroups) {
3283
+ const group = visibleModelGroups[modelFocus];
3284
+ if (!group)
3193
3285
  return;
3194
- }
3195
3286
  setModelGroup(group.provider);
3196
3287
  // The recents group opens on its most-recent entry; a provider
3197
3288
  // group on its current model when it owns one, else its first row.
@@ -3203,7 +3294,7 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
3203
3294
  dispatchOverlay({ type: 'set-index', kind: 'model', index: landing.index });
3204
3295
  return;
3205
3296
  }
3206
- const model = groupModels[overlay.index];
3297
+ const model = visibleModelRows[modelFocus];
3207
3298
  // oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: out-of-range index on an empty list
3208
3299
  if (model) {
3209
3300
  // Enter switches the live model right away: the conversation is
@@ -3212,20 +3303,41 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
3212
3303
  dispatchOverlay({ type: 'close' });
3213
3304
  void switchModelRecorded(model.provider, model.id, model.name);
3214
3305
  }
3215
- else {
3216
- dispatchOverlay({ type: 'close' });
3217
- }
3218
3306
  }
3219
- else if (key.escape || key.backspace) {
3307
+ else if (key.backspace && cursor > 0) {
3308
+ const start = previousModelQueryBoundary(query, cursor);
3309
+ editModelQuery(query.slice(0, start) + query.slice(cursor), start);
3310
+ }
3311
+ else if (key.delete && cursor < query.length) {
3312
+ editModelQuery(query.slice(0, cursor) + query.slice(nextModelQueryBoundary(query, cursor)), cursor);
3313
+ }
3314
+ else if (key.leftArrow && cursor > 0) {
3315
+ editModelQuery(query, previousModelQueryBoundary(query, cursor));
3316
+ }
3317
+ else if (key.rightArrow && cursor < query.length) {
3318
+ editModelQuery(query, nextModelQueryBoundary(query, cursor));
3319
+ }
3320
+ else if (key.home) {
3321
+ editModelQuery(query, 0);
3322
+ }
3323
+ else if (key.end) {
3324
+ editModelQuery(query, query.length);
3325
+ }
3326
+ else if (key.escape || (key.backspace && query === '')) {
3220
3327
  if (activeModelGroup !== undefined && modelGroups.length > 1 && !modelPickerDirect) {
3221
3328
  setModelGroup(undefined);
3222
3329
  const groupIndex = Math.max(0, modelGroups.findIndex(group => group.provider === activeModelGroup));
3330
+ dispatchOverlay({ type: 'model-edit', query: '', cursor: 0 });
3223
3331
  dispatchOverlay({ type: 'set-index', kind: 'model', index: groupIndex });
3224
3332
  }
3225
3333
  else {
3226
3334
  dispatchOverlay({ type: 'close' });
3227
3335
  }
3228
3336
  }
3337
+ else if (input.length > 0 && !key.ctrl && !key.meta && !key.super) {
3338
+ const inserted = input.replace(/[\r\n\t]+/gu, ' ');
3339
+ editModelQuery(query.slice(0, cursor) + inserted + query.slice(cursor), cursor + inserted.length);
3340
+ }
3229
3341
  return;
3230
3342
  }
3231
3343
  if (overlay.kind === 'skills') {
@@ -4142,9 +4254,9 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
4142
4254
  return;
4143
4255
  dispatchOverlay({ type: 'set-index', kind: 'workspace-flow', index });
4144
4256
  runWorkspaceFlowAction(signal => choice.choose(signal));
4145
- } }) })), overlay.kind === 'model' && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: models.length === 0 ? (_jsx(ModelPickerLoading, {})) : activeModelGroup === undefined ? (_jsx(ModelPicker, { groups: modelGroups, focusIndex: overlay.index, currentProvider: channel.provider, onPick: (index) => {
4257
+ } }) })), overlay.kind === 'model' && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: modelRowsAreGroups ? (_jsx(ModelPicker, { groups: visibleModelGroups, query: modelQuery, cursor: overlay.cursor ?? modelQuery.length, emptyReason: modelCatalogStatus === 'loading' && models.length === 0 ? 'loading' : modelCatalogStatus === 'error' ? 'error' : 'empty', focusIndex: modelFocus, currentProvider: channel.provider, onPick: (index) => {
4146
4258
  // 点击分组行 = 进入该组(与 Enter 同一条路径)
4147
- const group = modelGroups[index];
4259
+ const group = visibleModelGroups[index];
4148
4260
  if (!group)
4149
4261
  return;
4150
4262
  setModelGroup(group.provider);
@@ -4154,11 +4266,11 @@ export function Chat({ channel, questionStore, approvalStore, extensionDialogs,
4154
4266
  }
4155
4267
  const landing = modelPickerLanding(models.filter(model => model.provider === group.provider), channel.provider, channel.model);
4156
4268
  dispatchOverlay({ type: 'set-index', kind: 'model', index: landing.index });
4157
- } })) : (_jsx(ModelPicker, { models: groupModels, groupLabel: activeModelGroup === RECENTS_GROUP_PROVIDER
4269
+ } })) : (_jsx(ModelPicker, { models: visibleModelRows, query: modelQuery, cursor: overlay.cursor ?? modelQuery.length, emptyReason: modelCatalogStatus === 'loading' && models.length === 0 ? 'loading' : modelCatalogStatus === 'error' ? 'error' : models.length === 0 ? 'empty' : 'no-match', groupLabel: activeModelGroup === RECENTS_GROUP_PROVIDER
4158
4270
  ? t('picker-group-recent')
4159
- : modelGroups.find(group => group.provider === activeModelGroup)?.label, showBack: modelGroups.length > 1 && !modelPickerDirect, showProviderPrefix: activeModelGroup === RECENTS_GROUP_PROVIDER, focusIndex: overlay.index, currentModel: `${channel.provider}/${channel.model}`, onPick: (index) => {
4271
+ : modelGroups.find(group => group.provider === activeModelGroup)?.label, showBack: modelGroups.length > 1 && !modelPickerDirect, showProviderPrefix: activeModelGroup === RECENTS_GROUP_PROVIDER || activeModelGroup === undefined, focusIndex: modelFocus, currentModel: `${channel.provider}/${channel.model}`, onPick: (index) => {
4160
4272
  // 点击行 = 应用该行模型(与 Enter 同一条路径)
4161
- const model = groupModels[index];
4273
+ const model = visibleModelRows[index];
4162
4274
  if (!model)
4163
4275
  return;
4164
4276
  dispatchOverlay({ type: 'close' });
@@ -4285,10 +4397,6 @@ function NewMessagesPill({ count, onClick, }) {
4285
4397
  ? t(count === 1 ? 'new-message' : 'new-messages', { n: count })
4286
4398
  : t('back-to-bottom'), ' '] }) }) }));
4287
4399
  }
4288
- /** /model while the provider catalog is still loading. */
4289
- function ModelPickerLoading() {
4290
- return (_jsx(Pane, { color: "permission", children: _jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, color: "permission", children: t('picker-title-model') }), _jsx(LoadingState, { message: t('model-loading'), bold: true, subtitle: t('model-loading-subtitle') })] }) }));
4291
- }
4292
4400
  /**
4293
4401
  * The `/` incsearch bar: a
4294
4402
  * single row above the prompt input with the query, a block cursor, and the
@@ -63,6 +63,8 @@ export type ChatOverlay = {
63
63
  } | {
64
64
  kind: 'model';
65
65
  index: number;
66
+ query?: string;
67
+ cursor?: number;
66
68
  } | {
67
69
  kind: 'skills';
68
70
  index: number;
@@ -204,6 +206,10 @@ export type ChatOverlayAction =
204
206
  query?: string;
205
207
  cursor?: number;
206
208
  focus?: number;
209
+ } | {
210
+ type: 'model-edit';
211
+ query: string;
212
+ cursor: number;
207
213
  }
208
214
  /** Workspace flow: an action is running (keys except Esc are swallowed). */
209
215
  | {
@@ -1 +1 @@
1
- {"version":3,"file":"chatOverlay.d.ts","sourceRoot":"","sources":["../../../src/screens/chatOverlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAA;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAA;AACvE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAEjE,mEAAmE;AACnE,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,yBAAyB,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,CAAA;AAE1F,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACzC;IACE,IAAI,EAAE,gBAAgB,CAAA;IACtB,IAAI,EAAE,oBAAoB,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAA;CACjC,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,iBAAiB,CAAA;CAAE,GAC3B;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,wBAAwB,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjE;IACE,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,OAAO,CAAA;CACd,GAID;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;AAClB;;;;;;GAMG;GACD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE;AACvE;;;;;;;GAOG;GACD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,eAAe,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAC9D,OAAO,CAAC,EAAE,SAAS;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvF,eAAO,MAAM,UAAU,EAAE,WAA8B,CAAA;AAEvD,MAAM,MAAM,iBAAiB;AAC3B,mEAAmE;AACjE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE;AACxC,gFAAgF;GAC9E;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE;AACnB,8EAA8E;GAC5E;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;CAAE;AACvC;oEACoE;GAClE;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE;AACjD;;;;;;GAMG;GACD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,SAAS,WAAW,CAAC,MAAM,CAAC,EAAE,CAAA;CAAE;AACjF;;;;;GAKG;GACD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAChD;;;oDAGoD;GAClD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,gBAAgB,GAAG,QAAQ,GAAG,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AACzI,wEAAwE;GACtE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAC3E,4EAA4E;GAC1E;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AACtC,2EAA2E;GACzE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAA;CAAE;AAC1D,0DAA0D;GACxD;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAC5D,sEAAsE;GACpE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE;AACxC;;wEAEwE;GACtE;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAA;CAAE;AACvF,0EAA0E;GACxE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAA;AAE3B;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAqF7F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE;IACL,oBAAoB,EAAE,MAAM,CAAA;IAC5B,iBAAiB,EAAE,MAAM,CAAA;IACzB,iBAAiB,EAAE,MAAM,CAAA;CAC1B,GACA,OAAO,CAmBT"}
1
+ {"version":3,"file":"chatOverlay.d.ts","sourceRoot":"","sources":["../../../src/screens/chatOverlay.ts"],"names":[],"mappings":"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"}
@@ -69,6 +69,10 @@ export function chatOverlayReducer(state, action) {
69
69
  }
70
70
  case 'set-index':
71
71
  return state.kind === action.kind ? { ...state, index: action.index } : state;
72
+ case 'model-edit':
73
+ return state.kind === 'model'
74
+ ? { ...state, query: action.query, cursor: action.cursor, index: action.query === (state.query ?? '') ? state.index : 0 }
75
+ : state;
72
76
  case 'history-edit':
73
77
  return state.kind === 'history'
74
78
  ? {
@@ -0,0 +1,4 @@
1
+ /** UTF-16 offsets used by SearchBox, stepped at complete Unicode characters. */
2
+ export declare function previousModelQueryBoundary(query: string, cursor: number): number;
3
+ export declare function nextModelQueryBoundary(query: string, cursor: number): number;
4
+ //# sourceMappingURL=modelSearchInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelSearchInput.d.ts","sourceRoot":"","sources":["../../../src/screens/modelSearchInput.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAOhF;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAM5E"}
@@ -0,0 +1,19 @@
1
+ import { getGraphemeSegmenter } from '../utils/intl.js';
2
+ /** UTF-16 offsets used by SearchBox, stepped at complete Unicode characters. */
3
+ export function previousModelQueryBoundary(query, cursor) {
4
+ let previous = 0;
5
+ for (const { index } of getGraphemeSegmenter().segment(query)) {
6
+ if (index >= cursor)
7
+ break;
8
+ previous = index;
9
+ }
10
+ return previous;
11
+ }
12
+ export function nextModelQueryBoundary(query, cursor) {
13
+ for (const { index, segment } of getGraphemeSegmenter().segment(query)) {
14
+ const end = index + segment.length;
15
+ if (end > cursor)
16
+ return end;
17
+ }
18
+ return query.length;
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askdkc/dsh-cli",
3
- "version": "0.11.4",
3
+ "version": "0.11.6",
4
4
  "description": "Interactive terminal interface for DeepSeek Harness agents, sessions and tools.",
5
5
  "keywords": [
6
6
  "dsh",