diff --git a/src/cli/tui/components/Panel.tsx b/src/cli/tui/components/Panel.tsx index de2caa8c0..77fb40ee9 100644 --- a/src/cli/tui/components/Panel.tsx +++ b/src/cli/tui/components/Panel.tsx @@ -16,7 +16,7 @@ export interface PanelProps { fullWidth?: boolean; } -export function Panel({ title, children, borderColor, height, flexGrow, flexBasis, fullWidth = false }: PanelProps) { +export function Panel({ title, children, borderColor, height, flexGrow, flexBasis, fullWidth = true }: PanelProps) { const { contentWidth } = useLayout(); return ( diff --git a/src/cli/tui/components/__tests__/Panel.test.tsx b/src/cli/tui/components/__tests__/Panel.test.tsx index 5d3a5eec4..89e9b6958 100644 --- a/src/cli/tui/components/__tests__/Panel.test.tsx +++ b/src/cli/tui/components/__tests__/Panel.test.tsx @@ -2,20 +2,12 @@ import { Panel } from '../Panel.js'; import { Text } from 'ink'; import { render } from 'ink-testing-library'; import React from 'react'; -import { afterEach, describe, expect, it, vi } from 'vitest'; - -const { mockContentWidth } = vi.hoisted(() => ({ - mockContentWidth: { value: 60 }, -})); +import { describe, expect, it, vi } from 'vitest'; vi.mock('../../context/index.js', () => ({ - useLayout: () => ({ contentWidth: mockContentWidth.value }), + useLayout: () => ({ contentWidth: 80 }), })); -afterEach(() => { - mockContentWidth.value = 60; -}); - describe('Panel', () => { it('renders children content inside a border', () => { const { lastFrame } = render( @@ -41,23 +33,14 @@ describe('Panel', () => { expect(frame.indexOf('Settings')).toBeLessThan(frame.indexOf('body')); }); - it('adapts to different content widths from context', () => { - mockContentWidth.value = 30; - const { lastFrame: narrow } = render( - - test - - ); - - mockContentWidth.value = 100; - const { lastFrame: wide } = render( + it('defaults to full width', () => { + const { lastFrame } = render( test ); - - const narrowTopLine = narrow()!.split('\n')[0]!; - const wideTopLine = wide()!.split('\n')[0]!; - expect(narrowTopLine.length).toBeLessThan(wideTopLine.length); + const frame = lastFrame()!; + const topLine = frame.split('\n')[0]!; + expect(topLine.length).toBeGreaterThan(80); }); }); diff --git a/src/cli/tui/context/LayoutContext.tsx b/src/cli/tui/context/LayoutContext.tsx index e55a7366d..7acf7d006 100644 --- a/src/cli/tui/context/LayoutContext.tsx +++ b/src/cli/tui/context/LayoutContext.tsx @@ -1,16 +1,14 @@ import { useStdout } from 'ink'; import React, { type ReactNode, createContext, useContext } from 'react'; -/** Maximum content width cap */ -const MAX_CONTENT_WIDTH = 60; +const DEFAULT_WIDTH = 80; interface LayoutContextValue { - /** Global content width: min(terminalWidth, MAX_CONTENT_WIDTH) */ contentWidth: number; } const LayoutContext = createContext({ - contentWidth: MAX_CONTENT_WIDTH, + contentWidth: DEFAULT_WIDTH, }); // eslint-disable-next-line react-refresh/only-export-components @@ -18,35 +16,13 @@ export function useLayout(): LayoutContextValue { return useContext(LayoutContext); } -/** - * Build the logo dynamically based on width. - * The logo has fixed text " >_ AgentCore" on left and version on right, - * with padding in between to fill the width. - */ -// eslint-disable-next-line react-refresh/only-export-components -export function buildLogo(width: number, version?: string): string { - const left = '│ >_ AgentCore'; - const right = version ? `v${version} │` : '│'; - // -2 for the border chars already in left/right - const innerWidth = width - 2; - const paddingNeeded = innerWidth - (left.length - 1) - (right.length - 1); - const padding = ' '.repeat(Math.max(0, paddingNeeded)); - - const topBorder = '┌' + '─'.repeat(innerWidth) + '┐'; - const bottomBorder = '└' + '─'.repeat(innerWidth) + '┘'; - const middle = left + padding + right; - - return `\n${topBorder}\n${middle}\n${bottomBorder}`; -} - interface LayoutProviderProps { children: ReactNode; } export function LayoutProvider({ children }: LayoutProviderProps) { const { stdout } = useStdout(); - const terminalWidth = stdout?.columns ?? MAX_CONTENT_WIDTH; - const contentWidth = Math.min(terminalWidth, MAX_CONTENT_WIDTH); + const contentWidth = stdout?.columns ?? DEFAULT_WIDTH; return {children}; } diff --git a/src/cli/tui/context/__tests__/LayoutContext.test.ts b/src/cli/tui/context/__tests__/LayoutContext.test.ts deleted file mode 100644 index 862039b20..000000000 --- a/src/cli/tui/context/__tests__/LayoutContext.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { buildLogo } from '../LayoutContext.js'; -import { describe, expect, it } from 'vitest'; - -describe('buildLogo', () => { - it('builds logo with correct width', () => { - const logo = buildLogo(40); - - expect(logo).toContain('>_ AgentCore'); - expect(logo).toContain('┌'); - expect(logo).toContain('┐'); - expect(logo).toContain('└'); - expect(logo).toContain('┘'); - }); - - it('includes version when provided', () => { - const logo = buildLogo(50, '1.2.3'); - - expect(logo).toContain('>_ AgentCore'); - expect(logo).toContain('v1.2.3'); - }); - - it('does not include version when not provided', () => { - const logo = buildLogo(40); - - expect(logo).not.toContain('v'); - }); - - it('handles narrow width without crashing', () => { - const logo = buildLogo(20); - - expect(logo).toContain('>_ AgentCore'); - }); -}); diff --git a/src/cli/tui/context/index.ts b/src/cli/tui/context/index.ts index 05fe7f29e..60f44748a 100644 --- a/src/cli/tui/context/index.ts +++ b/src/cli/tui/context/index.ts @@ -1 +1 @@ -export { LayoutProvider, useLayout, buildLogo } from './LayoutContext'; +export { LayoutProvider, useLayout } from './LayoutContext'; diff --git a/src/cli/tui/screens/home/CommandListScreen.tsx b/src/cli/tui/screens/home/CommandListScreen.tsx index ad2f22c7c..b4c65f932 100644 --- a/src/cli/tui/screens/home/CommandListScreen.tsx +++ b/src/cli/tui/screens/home/CommandListScreen.tsx @@ -1,4 +1,3 @@ -import { buildLogo, useLayout } from '../../context'; import type { CommandMeta } from '../../utils/commands'; import { Box, Text, useApp, useStdout } from 'ink'; import React, { useEffect } from 'react'; @@ -18,11 +17,9 @@ interface CommandListScreenProps { */ export function CommandListScreen({ commands }: CommandListScreenProps) { const { exit } = useApp(); - const { contentWidth } = useLayout(); const { stdout } = useStdout(); const terminalWidth = stdout?.columns ?? 80; const maxDescWidth = Math.max(20, terminalWidth - 18); - const logo = buildLogo(contentWidth); // Exit after render useEffect(() => { @@ -34,7 +31,9 @@ export function CommandListScreen({ commands }: CommandListScreenProps) { return ( - {logo} + + {'>_ AgentCore'} + Usage: diff --git a/src/cli/tui/screens/home/HelpScreen.tsx b/src/cli/tui/screens/home/HelpScreen.tsx index 71b8d56e8..e9f3c23be 100644 --- a/src/cli/tui/screens/home/HelpScreen.tsx +++ b/src/cli/tui/screens/home/HelpScreen.tsx @@ -1,5 +1,4 @@ import { Cursor, ScreenLayout } from '../../components'; -import { useLayout } from '../../context'; import { HINTS } from '../../copy'; import { useTextInput } from '../../hooks'; import type { CommandMeta } from '../../utils/commands'; @@ -84,10 +83,8 @@ function HelpDisplay({ interactiveCount, notice, }: HelpDisplayProps) { - const { contentWidth } = useLayout(); const { stdout } = useStdout(); const terminalWidth = stdout?.columns ?? 80; - const bottomDivider = '─'.repeat(contentWidth); const allItems = [...interactiveItems, ...cliOnlyItems]; const maxLabelLen = getMaxLabelLen(allItems); @@ -131,8 +128,16 @@ function HelpDisplay({ {showCliSection && ( <> - - CLI only {'─'.repeat(Math.max(0, contentWidth - 11))} + + CLI only {cliOnlyItems.map((item, idx) => ( {notice}} - - {bottomDivider} + {hintText} diff --git a/src/cli/tui/screens/home/HomeScreen.tsx b/src/cli/tui/screens/home/HomeScreen.tsx index c9d1d521c..7c67222fa 100644 --- a/src/cli/tui/screens/home/HomeScreen.tsx +++ b/src/cli/tui/screens/home/HomeScreen.tsx @@ -1,6 +1,5 @@ import { findConfigRoot } from '../../../../lib'; import { Cursor, ScreenLayout } from '../../components'; -import { buildLogo, useLayout } from '../../context'; import { HINTS } from '../../copy'; import { Box, Text, useApp, useInput } from 'ink'; import React from 'react'; @@ -53,10 +52,7 @@ interface HomeScreenProps { export function HomeScreen({ cwd: _cwd, version, onShowHelp, onSelectCreate }: HomeScreenProps) { const { exit } = useApp(); - const { contentWidth } = useLayout(); const showQuickStart = !hasProject(); - const logo = buildLogo(contentWidth, version); - const divider = '─'.repeat(contentWidth); useInput((input, key) => { if (key.escape) { @@ -82,8 +78,11 @@ export function HomeScreen({ cwd: _cwd, version, onShowHelp, onSelectCreate }: H return ( - {/* Logo with version - always at top */} - {logo} + {/* Logo with version */} + + {'>_ AgentCore'} + {version && v{version}} + {/* Input - directly under logo */} @@ -97,8 +96,15 @@ export function HomeScreen({ cwd: _cwd, version, onShowHelp, onSelectCreate }: H {showQuickStart ? : } {/* Divider and hint at bottom */} - - {divider} + {HINTS.HOME}