diff --git a/src/widgets/Model.ts b/src/widgets/Model.ts index 94b2b90d..fa85e714 100644 --- a/src/widgets/Model.ts +++ b/src/widgets/Model.ts @@ -26,7 +26,8 @@ export class ModelWidget implements Widget { : (model?.display_name ?? model?.id); if (modelDisplayName) { - return item.rawValue ? modelDisplayName : `Model: ${modelDisplayName}`; + const shortName = modelDisplayName.replace(/\s*\(.*\)$/, ''); + return item.rawValue ? shortName : `Model: ${shortName}`; } return null; } diff --git a/src/widgets/__tests__/Model.test.ts b/src/widgets/__tests__/Model.test.ts new file mode 100644 index 00000000..a4234567 --- /dev/null +++ b/src/widgets/__tests__/Model.test.ts @@ -0,0 +1,69 @@ +import { + describe, + expect, + it +} from 'vitest'; + +import type { + RenderContext, + WidgetItem +} from '../../types'; +import { DEFAULT_SETTINGS } from '../../types/Settings'; +import { ModelWidget } from '../Model'; + +const ITEM: WidgetItem = { id: 'model', type: 'model' }; +const RAW_ITEM: WidgetItem = { id: 'model', type: 'model', rawValue: true }; + +function makeContext(overrides: Partial = {}): RenderContext { + return { ...overrides }; +} + +describe('ModelWidget', () => { + describe('render()', () => { + it('strips parenthetical suffix from display_name', () => { + const ctx = makeContext({ data: { model: { id: 'claude-opus-4-6[1m]', display_name: 'Opus 4.6 (1M context)' } } }); + expect(new ModelWidget().render(RAW_ITEM, ctx, DEFAULT_SETTINGS)).toBe('Opus 4.6'); + }); + + it('strips parenthetical from Sonnet display_name', () => { + const ctx = makeContext({ data: { model: { id: 'claude-sonnet-4-6', display_name: 'Sonnet 4.6 (200K context)' } } }); + expect(new ModelWidget().render(RAW_ITEM, ctx, DEFAULT_SETTINGS)).toBe('Sonnet 4.6'); + }); + + it('leaves name unchanged when no parenthetical', () => { + const ctx = makeContext({ data: { model: { id: 'claude-sonnet-4-6', display_name: 'Sonnet 4.6' } } }); + expect(new ModelWidget().render(RAW_ITEM, ctx, DEFAULT_SETTINGS)).toBe('Sonnet 4.6'); + }); + + it('handles model as string (legacy)', () => { + const ctx = makeContext({ data: { model: 'Claude Opus 4.6 (1M context)' } }); + expect(new ModelWidget().render(RAW_ITEM, ctx, DEFAULT_SETTINGS)).toBe('Claude Opus 4.6'); + }); + + it('includes Model: prefix when rawValue is false', () => { + const ctx = makeContext({ data: { model: { id: 'claude-opus-4-6[1m]', display_name: 'Opus 4.6 (1M context)' } } }); + expect(new ModelWidget().render(ITEM, ctx, DEFAULT_SETTINGS)).toBe('Model: Opus 4.6'); + }); + + it('returns null when model is absent', () => { + const ctx = makeContext({ data: {} }); + expect(new ModelWidget().render(ITEM, ctx, DEFAULT_SETTINGS)).toBeNull(); + }); + + it('returns null when context.data is absent', () => { + const ctx = makeContext(); + expect(new ModelWidget().render(ITEM, ctx, DEFAULT_SETTINGS)).toBeNull(); + }); + + it('returns preview text in preview mode', () => { + const ctx = makeContext({ isPreview: true }); + expect(new ModelWidget().render(ITEM, ctx, DEFAULT_SETTINGS)).toBe('Model: Claude'); + expect(new ModelWidget().render(RAW_ITEM, ctx, DEFAULT_SETTINGS)).toBe('Claude'); + }); + + it('falls back to model id when display_name is absent', () => { + const ctx = makeContext({ data: { model: { id: 'claude-opus-4-6[1m]' } } }); + expect(new ModelWidget().render(RAW_ITEM, ctx, DEFAULT_SETTINGS)).toBe('claude-opus-4-6[1m]'); + }); + }); +}); \ No newline at end of file