diff --git a/packages/presentation/i18n/src/locales/en.ts b/packages/presentation/i18n/src/locales/en.ts
index 63950cb6..4f58424f 100644
--- a/packages/presentation/i18n/src/locales/en.ts
+++ b/packages/presentation/i18n/src/locales/en.ts
@@ -332,8 +332,9 @@ export const en = {
modelDefault: 'Default',
effortDefault: 'Default',
effortShort: 'Def.',
- useDefaultModel: 'Use default model',
- useDefaultEffort: 'Use default effort',
+ model: 'Model',
+ effort: 'Effort',
+ resetToDefault: 'Reset to default',
add: 'Add',
attach: 'Images',
attachmentPickerTitle: 'Attach images',
@@ -347,7 +348,6 @@ export const en = {
attachmentUnsupportedAgent: "This agent doesn't support image attachments yet",
attachmentReadFailed: 'Failed to read the file',
approvalTitle: 'How should {agent} actions be approved?',
- reasoning: 'Reasoning',
provider: 'Provider',
},
mode: {
diff --git a/packages/presentation/i18n/src/locales/zh-cn.ts b/packages/presentation/i18n/src/locales/zh-cn.ts
index e078cbd2..59cf01fd 100644
--- a/packages/presentation/i18n/src/locales/zh-cn.ts
+++ b/packages/presentation/i18n/src/locales/zh-cn.ts
@@ -322,8 +322,9 @@ export const zhCN = {
modelDefault: '默认',
effortDefault: '默认',
effortShort: '默认',
- useDefaultModel: '使用默认模型',
- useDefaultEffort: '使用默认推理强度',
+ model: '模型',
+ effort: '推理强度',
+ resetToDefault: '恢复默认设置',
add: '添加',
attach: '图片',
attachmentPickerTitle: '添加图片',
@@ -337,7 +338,6 @@ export const zhCN = {
attachmentUnsupportedAgent: '当前 agent 暂不支持图片附件',
attachmentReadFailed: '读取文件失败',
approvalTitle: '如何审批 {agent} 的操作?',
- reasoning: '推理',
provider: '提供方',
},
mode: {
diff --git a/packages/presentation/ui/src/chat/agent-icon.tsx b/packages/presentation/ui/src/chat/agent-icon.tsx
index 57471be4..8d97c406 100644
--- a/packages/presentation/ui/src/chat/agent-icon.tsx
+++ b/packages/presentation/ui/src/chat/agent-icon.tsx
@@ -1,7 +1,7 @@
///
import type { AgentKind } from '@linkcode/schema';
+import ClaudeColorGlyph from '~icons/lobe-icons/claude-color';
import ClaudeCodeGlyph from '~icons/lobe-icons/claudecode';
-import ClaudeCodeColorGlyph from '~icons/lobe-icons/claudecode-color';
import CodexGlyph from '~icons/lobe-icons/codex';
import CodexColorGlyph from '~icons/lobe-icons/codex-color';
import OpenCodeGlyph from '~icons/lobe-icons/opencode';
@@ -16,8 +16,8 @@ const AGENT_GLYPHS: Partial> = {
opencode: OpenCodeGlyph,
};
-const AGENT_COLOR_GLYPHS: Partial> = {
- 'claude-code': ClaudeCodeColorGlyph,
+const AGENT_COLOR_GLYPHS: Partial> = {
+ 'claude-code': ClaudeColorGlyph,
codex: CodexColorGlyph,
};
diff --git a/packages/presentation/ui/src/shell/__tests__/new-session-surface.test.tsx b/packages/presentation/ui/src/shell/__tests__/new-session-surface.test.tsx
index e8197975..5f7cb78f 100644
--- a/packages/presentation/ui/src/shell/__tests__/new-session-surface.test.tsx
+++ b/packages/presentation/ui/src/shell/__tests__/new-session-surface.test.tsx
@@ -54,6 +54,12 @@ const RE_PI_WIDE = /Pi Wide/;
const RE_HIGH_EFFORT = /High/;
const RE_LOW_EFFORT = /Low/;
const RE_GPT_56_SOL = /GPT-5.6-Sol/;
+const RE_PROVIDER_CLAUDE_CODE_MENU = /provider.*Claude Code/;
+const RE_MODEL_SONNET_5_MENU = /model.*Sonnet 5/;
+const RE_MODEL_GPT_56_SOL_MENU = /model.*GPT-5\.6-Sol/;
+const RE_MODEL_DEFAULT_MENU = /model.*modelDefault/;
+const RE_MODEL_PI_SONNET_MENU = /model.*Pi Sonnet/;
+const RE_EFFORT_DEFAULT_MENU = /effort.*effortDefault/;
const RE_APPROVAL_DEFAULT = /Default/;
const RE_ACCEPT_EDITS = /Accept edits/;
const RE_PROJECT_WORKSPACE = /app/;
@@ -274,7 +280,7 @@ describe('NewSessionSurface', () => {
);
});
- it('names the model selector with its agent, model, and reasoning effort', () => {
+ it('names the model selector with its agent, model, and effort', () => {
render(
{
expect(
screen.getByRole('button', {
- name: 'Claude Code, Sonnet 5, reasoning: effortDefault',
+ name: 'Claude Code, Sonnet 5, effort: effortDefault',
}),
).toBeTruthy();
});
@@ -571,7 +577,16 @@ describe('NewSessionSurface', () => {
);
await user.click(screen.getByRole('button', { name: RE_SONNET_5 }));
- await user.click(await screen.findByText('High'));
+ const providerItem = await screen.findByRole('menuitem', {
+ name: RE_PROVIDER_CLAUDE_CODE_MENU,
+ });
+ expect(screen.getByRole('menuitem', { name: RE_MODEL_SONNET_5_MENU })).toBeTruthy();
+ providerItem.focus();
+ await user.keyboard('{ArrowRight}');
+ expect(await screen.findByRole('menuitemradio', { name: 'Claude Code' })).toBeTruthy();
+ await user.keyboard('{ArrowLeft}');
+ await user.click(screen.getByRole('menuitem', { name: RE_EFFORT_DEFAULT_MENU }));
+ fireEvent.click(await screen.findByRole('menuitemradio', { name: 'High' }));
typeInComposer('hello');
await pressInComposer('Enter');
@@ -717,7 +732,7 @@ describe('NewSessionSurface', () => {
);
await user.click(screen.getByRole('button', { name: RE_GPT_56_SOL }));
- await user.click(await screen.findByRole('menuitem', { name: 'GPT-5.6-Sol' }));
+ await user.click(await screen.findByRole('menuitem', { name: RE_MODEL_GPT_56_SOL_MENU }));
fireEvent.click(await screen.findByRole('menuitemradio', { name: 'GPT-5.6-Luna' }));
typeInComposer('use Luna');
await pressInComposer('Enter');
@@ -775,11 +790,11 @@ describe('NewSessionSurface', () => {
);
await user.click(screen.getByRole('button', { name: RE_OPUS_4_8 }));
- await user.click(await screen.findByRole('menuitem', { name: 'useDefaultModel' }));
+ await user.click(await screen.findByRole('menuitem', { name: 'resetToDefault' }));
expect(screen.getByRole('button', { name: RE_CONFIGURED_CLAUDE_MODEL })).toBeTruthy();
-
await user.click(screen.getByRole('button', { name: RE_CONFIGURED_CLAUDE_MODEL }));
- await user.click(await screen.findByRole('menuitem', { name: 'useDefaultEffort' }));
+ expect(screen.queryByRole('menuitem', { name: 'resetToDefault' })).toBeNull();
+ await user.keyboard('{Escape}');
typeInComposer('use provider defaults');
await pressInComposer('Enter');
@@ -808,7 +823,7 @@ describe('NewSessionSurface', () => {
);
await user.click(screen.getByRole('button', { name: RE_SONNET_5 }));
- await user.click(await screen.findByRole('menuitem', { name: 'Sonnet 5' }));
+ await user.click(await screen.findByRole('menuitem', { name: RE_MODEL_SONNET_5_MENU }));
fireEvent.click(await screen.findByRole('menuitemradio', { name: 'Opus 5' }));
typeInComposer('hello');
await pressInComposer('Enter');
@@ -1048,10 +1063,11 @@ describe('NewSessionSurface', () => {
);
await user.click(screen.getByRole('button', { name: RE_MODEL_DEFAULT }));
- await user.click(await screen.findByRole('menuitem', { name: RE_MODEL_DEFAULT }));
+ await user.click(await screen.findByRole('menuitem', { name: RE_MODEL_DEFAULT_MENU }));
fireEvent.click(await screen.findByRole('menuitemradio', { name: 'Pi Sonnet' }));
await user.click(screen.getByRole('button', { name: RE_PI_SONNET }));
- await user.click(await screen.findByText('High'));
+ await user.click(await screen.findByRole('menuitem', { name: RE_EFFORT_DEFAULT_MENU }));
+ fireEvent.click(await screen.findByRole('menuitemradio', { name: 'High' }));
await user.click(screen.getByRole('button', { name: RE_APPROVAL_DEFAULT }));
await user.click(await screen.findByRole('menuitemradio', { name: RE_ACCEPT_EDITS }));
typeInComposer('catalog choices');
@@ -1068,7 +1084,7 @@ describe('NewSessionSurface', () => {
onSubmit.mockClear();
await user.click(screen.getByRole('button', { name: RE_PI_SONNET }));
- await user.click(await screen.findByRole('menuitem', { name: 'Pi Sonnet' }));
+ await user.click(await screen.findByRole('menuitem', { name: RE_MODEL_PI_SONNET_MENU }));
fireEvent.click(await screen.findByRole('menuitemradio', { name: 'Pi Basic' }));
typeInComposer('no stale effort');
await pressInComposer('Enter');
diff --git a/packages/presentation/ui/src/shell/composer-controls.tsx b/packages/presentation/ui/src/shell/composer-controls.tsx
index 7c96b5dc..7860235a 100644
--- a/packages/presentation/ui/src/shell/composer-controls.tsx
+++ b/packages/presentation/ui/src/shell/composer-controls.tsx
@@ -17,10 +17,10 @@ import {
} from 'coss-ui/components/menu';
import { Separator } from 'coss-ui/components/separator';
import {
- BrainCircuitIcon,
ChevronDownIcon,
ListTodoIcon,
PlusIcon,
+ RotateCcwIcon,
ShieldIcon,
SlidersHorizontalIcon,
TargetIcon,
@@ -211,7 +211,6 @@ function RuntimeCueBadge({ cue }: { cue?: AgentRuntimeCue }): React.ReactNode {
);
}
-/** Codex-style model trigger: [provider glyph] model + effort, opening reasoning/model/provider menus. */
export function ModelSelectorMenu({
disabled,
provider,
@@ -264,7 +263,7 @@ export function ModelSelectorMenu({
const selectorLabels: string[] = [];
if (provider) selectorLabels.push(AGENT_LABELS[provider]);
if (showsModel) selectorLabels.push(modelLabel);
- if (hasEfforts) selectorLabels.push(`${t('reasoning')}: ${effortLabel}`);
+ if (hasEfforts) selectorLabels.push(`${t('effort')}: ${effortLabel}`);
return (
);