feat(claude-code): refine model mapping UI and defaults

- Add independent Fable model mapping
- Rename startup model to default model
- Default to the Opus mapping and preserve Subagent inheritance
- Replace 1M checkboxes with standard toggle switches
- Align form fields and simplify explanatory text
- Remove bulk role synchronization
- Update translations and regression tests
This commit is contained in:
lzt404 committed 2026-10-08 18:36:58 +08:00
1 parent b90445143c
commit 97ba1051a2
17 files changed
+206 -118

No files matched your search

+8 -16
View File
@@ -8,19 +8,19 @@ const os = require('node:os');
try {
const page = await browser.newPage({ viewport: { width: 1280, height: 900 } });
await page.goto('http://127.0.0.1:1421/tests/fixtures/agent-backups.html?client=claude-code&reset-selections');
const startup = page.getByRole('combobox', { name: '启动模型', exact: true });
const startup = page.getByRole('combobox', { name: '默认模型', exact: true });
const subagent = page.getByRole('combobox', { name: 'Subagent 模型', exact: true });
await startup.click();
await page.getByRole('option', { name: /^sonnet / }).click();
assert.equal(await startup.inputValue(), 'sonnet');
await page.locator('.claude-role-row .agent-model-trigger').first().click();
await page.getByRole('option', { name: /gpt-two/ }).click();
await page.getByText('跟随 sonnet → gpt-two', { exact: true }).waitFor();
await page.getByText('sonnet → gpt-two', { exact: true }).waitFor();
assert.equal(await startup.inputValue(), 'sonnet');
await startup.click();
await page.getByRole('option', { name: /^gpt-two / }).click();
await page.locator('.claude-code-context-settings summary').click();
const context = page.getByRole('checkbox', { name: '启动模型 1M', exact: true });
const context = page.getByRole('switch', { name: '默认模型 1M', exact: true });
await context.check();
assert.equal(await startup.inputValue(), 'gpt-two[1m]');
assert.equal(await page.getByRole('spinbutton', { name: '全局窗口覆盖', exact: true }).inputValue(), '1000000');
@@ -28,7 +28,7 @@ const os = require('node:os');
assert.equal(await page.getByRole('spinbutton', { name: '全局窗口覆盖', exact: true }).inputValue(), '200000');
assert.equal(await startup.inputValue(), 'gpt-two');
await page.locator('.claude-code-context-settings summary').click();
assert.equal(await page.locator('.claude-role-row input[type=checkbox]').count(), 3);
assert.equal(await page.locator('.claude-role-row input[type=checkbox]').count(), 4);
await subagent.fill('custom-worker');
await subagent.press('Enter');
assert.equal(await subagent.inputValue(), 'custom-worker');
@@ -49,22 +49,14 @@ const os = require('node:os');
await page.locator('.claude-code-model-error').waitFor({ state: 'hidden' });
assert.equal(await subagent.inputValue(), 'unsaved-worker');
await startup.click();
await page.getByRole('option', { name: '使用 Claude Code 默认模型', exact: true }).click();
await page.getByRole('option', { name: /^opus / }).click();
await subagent.click();
await page.getByRole('option', { name: '使用默认继承行为', exact: true }).click();
await page.getByRole('button', { name: '更新配置', exact: true }).click();
await page.waitForFunction(() => window.fixtureCalls.some(c => c.args?.claudeCodeModelMappings?.startupModel === '' && c.args?.claudeCodeModelMappings?.subagentModel === ''));
await page.getByRole('checkbox', { name: 'sonnet 1M', exact: true }).check();
await page.getByRole('button', { name: '将 Sonnet 应用到全部角色', exact: true }).click();
for (const role of ['sonnet', 'opus', 'haiku']) {
assert.equal(await page.getByRole('checkbox', { name: `${role} 1M`, exact: true }).isChecked(), true);
assert.match(await page.locator(`.claude-role-row[data-role="${role}"] .agent-model-trigger`).innerText(), /gpt-two/);
}
await page.waitForFunction(() => window.fixtureCalls.some(c => c.args?.claudeCodeModelMappings?.startupModel === 'opus' && c.args?.claudeCodeModelMappings?.subagentModel === ''));
await page.getByRole('switch', { name: 'fable 1M', exact: true }).check();
await page.getByRole('button', { name: '更新配置', exact: true }).click();
await page.waitForFunction(() => window.fixtureCalls.some(c => {
const m = c.args?.claudeCodeModelMappings;
return m && m.opus === 'gpt-two' && m.haiku === 'gpt-two' && m.opus1m && m.haiku1m && m.sonnet1m;
}));
await page.waitForFunction(() => window.fixtureCalls.some(c => c.args?.claudeCodeModelMappings?.fable1m === true));
for (const width of [1280, 760]) {
await page.setViewportSize({ width, height: 1000 });
await page.screenshot({ path: path.join(os.tmpdir(), `claude-code-models-${width}.png`), fullPage: true });
+1 -1
View File
@@ -1,7 +1,7 @@
import { expect, test } from 'bun:test';
import { claudeCodeModelPreview, isKnownClaudeCodeModel } from '../src/services/claudeCodeModels';
const routes = { opus: 'model-a', sonnet: 'model-b', haiku: 'model-c', opus1m: true, sonnet1m: false, haiku1m: false };
const routes = { fable: 'model-f', fable1m: false, opus: 'model-a', sonnet: 'model-b', haiku: 'model-c', opus1m: true, sonnet1m: false, haiku1m: false };
test('role selections follow changed mappings and role context flags', () => {
expect(claudeCodeModelPreview('opus', routes).model).toBe('model-a[1m]');
expect(claudeCodeModelPreview('sonnet', { ...routes, sonnet: 'model-d', sonnet1m: true }).model).toBe('model-d[1m]');