Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ Full reference: [Interaction and commands](docs/interaction.en.md).

`/resume` · `/home` · `/agentview` · `/bg` · `⌸` open the same session manager: workspace rail, live state, filter, ★ pins. Also `/model` `/new` `/compact` `/export` `/btw` `/tree` `/fork` `/rewind` `/settings` `/setup` `/status` `/cost` `/jobs` `/skills` `/mcp` `/provider` `/auth` `/login` `/update`.

`/model` shows models and reasoning effort on one page. DSH has provider tabs with **Recently used** first, moving the current model to the top; `Tab` / `Shift+Tab` select the next / previous provider. Codex and Claude open their model catalogs directly, without provider or recents tabs. `↑/↓` select a model, `←/→` adjust its effort, `Enter` applies the model and any explicit effort draft, and `Esc` cancels. Changing only the model preserves the backend's preference handling. Mouse users can click the available tabs, models, effort levels, and select/cancel hints; the wheel moves through models.
`/model` shows models and reasoning effort on one page. DSH has provider tabs with **Recently used** first, moving the current model to the top; `Tab` / `Shift+Tab` select the next / previous provider. Codex and Claude open their model catalogs directly, without provider or recents tabs. `↑/↓` select a model, `←/→` adjust its effort, `Enter` applies the model and any explicit effort draft, and `Esc` cancels. Changing only the model preserves the backend's preference handling. Mouse users can click the available tabs, models, effort levels, and select/cancel hints; clicking an effort level immediately applies the focused model and that effort and closes the picker. The wheel moves through models.

Short terminals keep the focused model and select/cancel actions visible, hiding descriptions and neighboring models first, then the effort row when only two rows remain.
Shortcuts sit below the title and effort levels have their own section; DSH, Claude, and Codex use the terminal background while covering the text underneath.
Expand Down
2 changes: 1 addition & 1 deletion README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ OpenAI 条款约束。完整操作与当前边界:[Codex 后端](docs/codex-ba

`/resume` · `/home` · `/agentview` · `/bg` · `⌸` 打开同一个会话管理界面:工作区栏、实时状态、筛选、★ 固定。另有 `/model` `/new` `/compact` `/export` `/btw` `/tree` `/fork` `/rewind` `/settings` `/setup` `/status` `/cost` `/jobs` `/skills` `/mcp` `/provider` `/auth` `/login` `/update`。

`/model` 同页选择模型与推理等级。DSH 顶部显示提供商标签,**最近使用**排在第一位,打开时将当前模型去重置顶;`Tab` / `Shift+Tab` 切换下一个 / 上一个提供商。Codex 与 Claude 直接展示模型目录,不显示提供商或最近使用标签。`↑/↓` 选择模型,`←/→` 调整推理等级,`Enter` 应用模型及显式调整的推理草稿,`Esc` 取消;只换模型时沿用后端既有偏好处理。鼠标可点击可用的标签、模型、档位及选择/取消提示,滚轮切换模型。
`/model` 同页选择模型与推理等级。DSH 顶部显示提供商标签,**最近使用**排在第一位,打开时将当前模型去重置顶;`Tab` / `Shift+Tab` 切换下一个 / 上一个提供商。Codex 与 Claude 直接展示模型目录,不显示提供商或最近使用标签。`↑/↓` 选择模型,`←/→` 调整推理等级,`Enter` 应用模型及显式调整的推理草稿,`Esc` 取消;只换模型时沿用后端既有偏好处理。鼠标可点击可用的标签、模型、档位及选择/取消提示;点击推理档位会立即应用当前聚焦模型与该强度,并关闭选择器。滚轮切换模型。

短终端优先保留聚焦模型与选择/取消入口,先隐藏描述和邻项,仅剩两行时再隐藏推理行。
操作提示位于标题下,推理档位独立成区;DSH、Claude、Codex 均沿用终端背景,并遮住底层文字。
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ Full-screen view of the whole session timeline (doesn't pollute scrollback); key
- `/model`: selector. **Switching = fork the session** (history kept, only routing changes, the old session stays in `/resume`);
persisted to `~/.dsh-tui/model.json` and reused by the next launch (a `provider`/`model`
pair in cordis.yml/profile is only the first-run deployment default).
- DSH provider tabs start with **Recently used**, and `Tab` / `Shift+Tab` switch providers; Codex and Claude show their catalogs directly without these tabs. All backends use `↑/↓` to select a model, `←/→` to adjust its effort, `Enter` to apply both, and `Esc` to cancel the draft. Click the available tabs, models, effort levels, or select/cancel hints; the wheel moves model focus.
- DSH provider tabs start with **Recently used**, and `Tab` / `Shift+Tab` switch providers; Codex and Claude show their catalogs directly without these tabs. All backends use `↑/↓` to select a model, `←/→` to adjust its effort, `Enter` to apply both, and `Esc` to cancel the draft. Click the available tabs, models, effort levels, or select/cancel hints; clicking an effort level immediately applies the focused model and that effort and closes the picker. The wheel moves model focus.
- Switching is rejected mid-turn.
- `/preset` options: `standard` (default full features), `ptc`, `minimal` (the kernel's Minimal preset: one persistent-shell tool only, no compaction, no plan mode — and therefore no compaction and no tool-result pruning, so a long session can hit the context limit and `/compact` plus questions are unavailable; Help and `/` completion mark the command, and entering the preset says so once),
`cordis`, `liangshen` (Liangshen mode).
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ dsh-TUI 不预装通用技能;`/skills` 浏览 DSH 发现的技能,可直调
- `/model`:选择器。**切换 = fork 会话续聊**(历史保留、仅换路由,旧会话留在 `/resume`);
持久化 `~/.dsh-tui/model.json`,下次启动直接沿用它(cordis.yml/profile 中的
`provider`/`model` 只是首次启动的部署默认值)。
- DSH 的顶部标签以「最近使用」开头,再列各提供商,`Tab` / `Shift+Tab` 切换提供商;Codex 与 Claude 直接显示模型目录,不显示这些标签。所有后端均用 `↑/↓` 选模型、`←/→` 调推理等级、`Enter` 一并应用、`Esc` 取消草稿,支持点击可用的标签、模型、档位与选择/取消提示,以及滚轮移动模型焦点。
- DSH 的顶部标签以「最近使用」开头,再列各提供商,`Tab` / `Shift+Tab` 切换提供商;Codex 与 Claude 直接显示模型目录,不显示这些标签。所有后端均用 `↑/↓` 选模型、`←/→` 调推理等级、`Enter` 一并应用、`Esc` 取消草稿,支持点击可用的标签、模型、档位与选择/取消提示,以及滚轮移动模型焦点。点击推理档位会立即应用当前聚焦模型与该强度,并关闭选择器。
- 回合运行中切换会被拒绝。
- `/preset` 可选:`standard`(默认全功能)、`ptc`、`minimal`(内核「极简模式」:只暴露一个持久 shell 工具,无 compaction、无计划模式;也因此没有压缩、工具结果不剪枝——长会话可能撞上下文上限,`/compact` 与问卷不可用,Help 与 `/` 补全会标注,进入该预设时提示一次)、
`cordis`、`liangshen`(梁神模式)。
Expand Down
2 changes: 1 addition & 1 deletion guide/dsh-tui-guide/user-guide.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ Full-screen view of the whole session timeline (doesn't pollute scrollback); key
- `/model`: selector. **Switching = fork the session** (history kept, only routing changes, the old session stays in `/resume`);
persisted to `~/.dsh-tui/model.json` and reused by the next launch (a `provider`/`model`
pair in cordis.yml/profile is only the first-run deployment default).
- DSH provider tabs start with **Recently used**, and `Tab` / `Shift+Tab` switch providers; Codex and Claude show their catalogs directly without these tabs. All backends use `↑/↓` to select a model, `←/→` to adjust its effort, `Enter` to apply both, and `Esc` to cancel the draft. Click the available tabs, models, effort levels, or select/cancel hints; the wheel moves model focus.
- DSH provider tabs start with **Recently used**, and `Tab` / `Shift+Tab` switch providers; Codex and Claude show their catalogs directly without these tabs. All backends use `↑/↓` to select a model, `←/→` to adjust its effort, `Enter` to apply both, and `Esc` to cancel the draft. Click the available tabs, models, effort levels, or select/cancel hints; clicking an effort level immediately applies the focused model and that effort and closes the picker. The wheel moves model focus.
- Switching is rejected mid-turn.
- `/preset` options: `standard` (default full features), `ptc`, `minimal` (the kernel's Minimal preset: one persistent-shell tool only, no compaction, no plan mode — and therefore no compaction and no tool-result pruning, so a long session can hit the context limit and `/compact` plus questions are unavailable; Help and `/` completion mark the command, and entering the preset says so once),
`cordis`, `liangshen` (Liangshen mode).
Expand Down
2 changes: 1 addition & 1 deletion guide/dsh-tui-guide/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ dsh-TUI 不预装通用技能;`/skills` 浏览 DSH 发现的技能,可直调
- `/model`:选择器。**切换 = fork 会话续聊**(历史保留、仅换路由,旧会话留在 `/resume`);
持久化 `~/.dsh-tui/model.json`,下次启动直接沿用它(cordis.yml/profile 中的
`provider`/`model` 只是首次启动的部署默认值)。
- DSH 的顶部标签以「最近使用」开头,再列各提供商,`Tab` / `Shift+Tab` 切换提供商;Codex 与 Claude 直接显示模型目录,不显示这些标签。所有后端均用 `↑/↓` 选模型、`←/→` 调推理等级、`Enter` 一并应用、`Esc` 取消草稿,支持点击可用的标签、模型、档位与选择/取消提示,以及滚轮移动模型焦点。
- DSH 的顶部标签以「最近使用」开头,再列各提供商,`Tab` / `Shift+Tab` 切换提供商;Codex 与 Claude 直接显示模型目录,不显示这些标签。所有后端均用 `↑/↓` 选模型、`←/→` 调推理等级、`Enter` 一并应用、`Esc` 取消草稿,支持点击可用的标签、模型、档位与选择/取消提示,以及滚轮移动模型焦点。点击推理档位会立即应用当前聚焦模型与该强度,并关闭选择器。
- 回合运行中切换会被拒绝。
- `/preset` 可选:`standard`(默认全功能)、`ptc`、`minimal`(内核「极简模式」:只暴露一个持久 shell 工具,无 compaction、无计划模式;也因此没有压缩、工具结果不剪枝——长会话可能撞上下文上限,`/compact` 与问卷不可用,Help 与 `/` 补全会标注,进入该预设时提示一次)、
`cordis`、`liangshen`(梁神模式)。
Expand Down
21 changes: 15 additions & 6 deletions scripts/verify-model-picker-ui.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* /model through the real Chat/channel/rendering path, with a fake LLM catalog:
* recents-first tabs, current-model promotion, forward/backward wrapping, independent model/effort drafts,
* same-batch navigation/confirmation, cancellation, mouse picks and wheel,
* same-batch navigation/confirmation, cancellation, single-click effort confirmation and wheel,
* focus windowing and resize in inline/fullscreen at 100 and 36 columns;
* header shortcuts, readable effort colors and the original terminal background.
* Claude/Codex use a flat catalog, focus the current model after loading,
Expand Down Expand Up @@ -329,10 +329,19 @@ async function scenario(fullscreen: boolean, columns: number): Promise<void> {
if (fullscreen) {
await click('Beta 01')
await check('mouse focuses a model', () => focused('Beta 01') && inverse('OFF'))
assert.deepEqual(switches, ['alpha/a1'], 'clicking a model only updates its draft')
await click('MAX')
await check('mouse selects effort draft', () => inverse('MAX'))
await click('选择')
await check('mouse applies model and effort', () => channel.model === 'b1' && channel.reasoningEffort === 'max' && hit('最近使用') === undefined)
await check('one effort click applies the focused model and effort and closes the picker', () => channel.model === 'b1' && channel.reasoningEffort === 'max' && hit('最近使用') === undefined)
assert.deepEqual(switches, ['alpha/a1', 'beta/b1'])
assert.deepEqual(effortPicks, ['high', 'max'])
assert.equal(JSON.parse(readFileSync(join(prefsDir, 'effort.json'), 'utf8')).effort, 'max')
await open('Beta 01', 'MAX')
await click('beta / Beta 01')
await check('clicking the current model keeps the picker open', () => focused('Beta 01') && inverse('MAX'))
await click('MAX')
await check('clicking the selected effort also confirms and closes', () => hit('最近使用') === undefined && switches.length === 3 && effortPicks.length === 3)
assert.deepEqual(switches, ['alpha/a1', 'beta/b1', 'beta/b1'])
assert.deepEqual(effortPicks, ['high', 'max', 'max'])
stdin.write('/model')
await check('mouse follow-up command', () => hit('/model') !== undefined)
stdin.write('\r')
Expand Down Expand Up @@ -441,9 +450,9 @@ async function backendSurface(backendId: 'claude' | 'codex', fullscreen: boolean
await click('Model 01')
assert.ok(await settled(() => focused('Model 01') && panelUsesDefaultBackground(term)), `${label}: mouse selects a model`)
await click('HIGH')
await click('选择')
assert.ok(await settled(() => !text().includes('推理强度') && picks.length === 2 && effortPicks.at(-1) === 'high'), `${label}: mouse applies model and reasoning`)
assert.ok(await settled(() => !text().includes('推理强度') && picks.length === 2 && effortPicks.at(-1) === 'high'), `${label}: one effort click applies model and reasoning and closes the picker`)
assert.deepEqual(picks, ['m0', 'm1'])
assert.deepEqual(effortPicks, ['high'])
} else {
assert.deepEqual(effortPicks, [], `${label}: no reasoning changes without an explicit draft`)
}
Expand Down
10 changes: 8 additions & 2 deletions src/components/ModelPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export function ModelPicker({
muted={false}
cursor={caretZone === 'effort'}
onPick={onEffort}
onConfirm={onConfirm}
/>
</>
)}
Expand All @@ -197,14 +198,16 @@ export function ModelPicker({
}

/** Keep the active cell visible when a provider or effort strip exceeds its width. */
function PickerTabs({ labels, focusIndex, width, muted = true, cursor = false, onPick }: {
function PickerTabs({ labels, focusIndex, width, muted = true, cursor = false, onPick, onConfirm }: {
labels: readonly string[]
focusIndex: number
width: number
muted?: boolean
/** This strip owns the native caret — its region was the last one used. */
cursor?: boolean
onPick(index: number): void
/** Confirm a clicked label; overflow arrows only navigate. */
onConfirm?(): void
}): React.ReactNode {
const singleLines = labels.map(label => label.replace(/[\r\n]+/g, ' '))
const allFit = singleLines.reduce((sum, label) => sum + stringWidth(label) + 2, 0)
Expand All @@ -225,7 +228,10 @@ function PickerTabs({ labels, focusIndex, width, muted = true, cursor = false, o
isFocused={start + index === focusIndex}
muted={muted}
cursor={cursor}
onPick={() => onPick(start + index)}
onPick={() => {
onPick(start + index)
onConfirm?.()
}}
/>
))}
{end < cells.length ? <Box onClick={event => { event.stopImmediatePropagation(); onPick(end) }}><Text dimColor>›</Text></Box> : null}
Expand Down
Loading