From 6494367985bd0db2b90060efa80dad442276fb58 Mon Sep 17 00:00:00 2001 From: takecchi Date: Sun, 19 Jul 2026 16:34:37 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=98=E3=83=83=E3=83=80=E3=81=AB?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E4=B8=AD=E3=81=AE=E3=83=A2=E3=83=87=E3=83=AB?= =?UTF-8?q?=E5=90=8D=E3=82=92=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 起動バナーのカレントディレクトリ行の上に、設定されているモデル (config.model)を表示する。未設定時は「CLI 既定」/「CLI default」を表示。 - i18n banner グループに model/defaultModel を ja/en 両方追加 - config.model を index.tsx → App → SessionList → Banner へ配線 - Banner のレンダリング・順序を banner.spec.tsx で検証 --- src/app.tsx | 3 +++ src/core/i18n.ts | 8 ++++++++ src/index.tsx | 8 +++++++- src/ui/banner.spec.tsx | 34 ++++++++++++++++++++++++++++++++++ src/ui/banner.tsx | 12 +++++++----- src/ui/session-list.tsx | 10 ++++++++-- 6 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/ui/banner.spec.tsx diff --git a/src/app.tsx b/src/app.tsx index db823fc..7fd80f4 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -21,11 +21,13 @@ export type ExternalRunner = (args: { export const App: FC<{ manager: SessionManager; cwd?: string; + model?: string; messages?: Messages; runExternal?: ExternalRunner; }> = ({ manager, cwd, + model, // 既定は ja。index.tsx が解決済みカタログを注入する。 messages = catalogs.ja, runExternal, @@ -83,6 +85,7 @@ export const App: FC<{ onOpenExternal={runExternal ? openExternal : undefined} onQuit={quit} cwd={cwd} + model={model} /> diff --git a/src/core/i18n.ts b/src/core/i18n.ts index 633b303..08d24a1 100644 --- a/src/core/i18n.ts +++ b/src/core/i18n.ts @@ -71,6 +71,10 @@ export interface Messages { /** 起動バナー(banner.tsx) */ banner: { subtitle: string; + /** 使用中モデルの表示(設定 model。未設定は CLI 既定)。 */ + model: (name: string) => string; + /** model 未設定時に表示するプレースホルダ(CLI 既定)。 */ + defaultModel: string; }; /** 下部モード行(status-footer.tsx) */ footer: { @@ -128,6 +132,8 @@ const ja: Messages = { }, banner: { subtitle: '並列 Claude Code セッションを git worktree 上で実行', + model: (name) => `モデル: ${name}`, + defaultModel: 'CLI 既定', }, footer: { autoMode: '自動モード', @@ -184,6 +190,8 @@ const en: Messages = { }, banner: { subtitle: 'Parallel Claude Code sessions in git worktrees', + model: (name) => `model: ${name}`, + defaultModel: 'CLI default', }, footer: { autoMode: 'auto mode on', diff --git a/src/index.tsx b/src/index.tsx index e7f3a3f..fb497ad 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -129,7 +129,13 @@ async function main(): Promise { }; const { waitUntilExit } = render( - , + , { exitOnCtrlC: false }, ); await waitUntilExit(); diff --git a/src/ui/banner.spec.tsx b/src/ui/banner.spec.tsx new file mode 100644 index 0000000..6776a0d --- /dev/null +++ b/src/ui/banner.spec.tsx @@ -0,0 +1,34 @@ +import { render } from 'ink-testing-library'; +import { describe, expect, it } from 'vitest'; +import { messages } from '@/core'; +import { Banner } from './banner'; +import { MessagesProvider } from './i18n-context'; + +function renderBanner(props: Parameters[0], lang: 'ja' | 'en' = 'en') { + return render( + + + , + ); +} + +describe('Banner', () => { + it('設定されたモデル名をヘッダに表示する', () => { + const { lastFrame } = renderBanner({ sessionCount: 0, model: 'claude-opus-4-8' }, 'en'); + expect(lastFrame()).toContain('model: claude-opus-4-8'); + }); + + it('モデル未設定なら CLI 既定を表示する', () => { + const { lastFrame } = renderBanner({ sessionCount: 0 }, 'en'); + expect(lastFrame()).toContain('model: CLI default'); + }); + + it('モデル行は cwd 行の上に描画される', () => { + const { lastFrame } = renderBanner( + { sessionCount: 0, model: 'sonnet', cwd: '/tmp/repo' }, + 'en', + ); + const frame = lastFrame() ?? ''; + expect(frame.indexOf('model: sonnet')).toBeLessThan(frame.indexOf('/tmp/repo')); + }); +}); diff --git a/src/ui/banner.tsx b/src/ui/banner.tsx index 1bc439c..3eac625 100644 --- a/src/ui/banner.tsx +++ b/src/ui/banner.tsx @@ -45,11 +45,12 @@ const LOGO_ROWS = LOGO.map((line, row) => ({ * Borderless startup header echoing Claude Code's banner: the mascot on the left * and identity / subtitle / cwd on the right (vertically centered against it). */ -export const Banner: FC<{ cwd?: string; sessionCount: number; totalCostUsd?: number }> = ({ - cwd, - sessionCount, - totalCostUsd = 0, -}) => { +export const Banner: FC<{ + cwd?: string; + model?: string; + sessionCount: number; + totalCostUsd?: number; +}> = ({ cwd, model, sessionCount, totalCostUsd = 0 }) => { const m = useMessages(); return ( @@ -76,6 +77,7 @@ export const Banner: FC<{ cwd?: string; sessionCount: number; totalCostUsd?: num {m.banner.subtitle} + {m.banner.model(model ?? m.banner.defaultModel)} {cwd ? {cwd} : null} diff --git a/src/ui/session-list.tsx b/src/ui/session-list.tsx index b7917ab..4d02263 100644 --- a/src/ui/session-list.tsx +++ b/src/ui/session-list.tsx @@ -38,7 +38,8 @@ export const SessionList: FC<{ onOpenExternal?: OpenExternal; onQuit: () => void; cwd?: string; -}> = ({ manager, onOpenExternal, onQuit, cwd }) => { + model?: string; +}> = ({ manager, onOpenExternal, onQuit, cwd, model }) => { const m = useMessages(); const sessions = useSessions(manager); const mode = useRunMode(manager); @@ -246,7 +247,12 @@ export const SessionList: FC<{ return ( - + {/* flexGrow で残り高さを占め、入力欄とフッタを画面最下部へ押し下げる */}