diff --git a/src/components/LogoV2/WelcomeV2.tsx b/src/components/LogoV2/WelcomeV2.tsx index 563d321d94..f65f69f852 100644 --- a/src/components/LogoV2/WelcomeV2.tsx +++ b/src/components/LogoV2/WelcomeV2.tsx @@ -1,36 +1,26 @@ import * as React from 'react' import { Box, Text } from 'src/ink.js' -const VERBOO_LOGO = [ - ' ▄▀▀▀▀▀▀▀▄ ', - '▄▀▀▀▀▀▀▀▀▀▀▀▄', - '▀▀▀ ▀▀▀▀▀ ▀▀▀', - '▀▀▀▀▀▀▀▀▀▀▀▀▀', - '▀▀▀▀▀▄▄▄▀▀▀▀▀', - ' ▀▀▀▀▀▀▀▀▀▀▀ ', - '▄▀▀ ▀▀▀▀▀ ▀▀▄', -] - export function WelcomeV2(): React.ReactElement { const version = MACRO.DISPLAY_VERSION ?? MACRO.VERSION return ( - - - {VERBOO_LOGO.map((line, index) => ( - - {line} - - ))} + + + + 👻 + + Verboo Code + + v{version} + + + + Build, debug, and ship from your terminal. - - - - Verboo Code - - v{version} - - Build, debug, and ship from your terminal. + + + Describe what you need to get started. ) diff --git a/src/components/PromptInput/PromptInput.tsx b/src/components/PromptInput/PromptInput.tsx index b2fb7404cc..46880a06c9 100644 --- a/src/components/PromptInput/PromptInput.tsx +++ b/src/components/PromptInput/PromptInput.tsx @@ -2282,7 +2282,7 @@ function PromptInput({ Waiting for permission… } - + {mode === 'prompt' && !exitMessage.show && !isPasting ? : } {swarmBanner ? <> @@ -2368,10 +2368,9 @@ function getInitialPasteId(messages: Message[]): number { } return maxId + 1; } -function buildPromptBorderText(mode: PromptInputMode): BorderTextOptions | undefined { - if (mode !== 'bash') return undefined; +function buildPromptBorderText(mode: PromptInputMode): BorderTextOptions { return { - content: ' shell ', + content: mode === 'bash' ? ' shell ' : ' prompt ', position: 'top', align: 'start', offset: 1 diff --git a/src/components/PromptInput/PromptInputFooter.tsx b/src/components/PromptInput/PromptInputFooter.tsx index 87ccb6c459..8e365768c6 100644 --- a/src/components/PromptInput/PromptInputFooter.tsx +++ b/src/components/PromptInput/PromptInputFooter.tsx @@ -32,8 +32,6 @@ import { PromptInputFooterLeftSide } from './PromptInputFooterLeftSide.js'; import { PromptInputFooterSuggestions, type SuggestionItem } from './PromptInputFooterSuggestions.js'; import { PromptInputHelpMenu } from './PromptInputHelpMenu.js'; -const BAR_WIDTH = 16; - /** * ContextWindowDisplay with memo to prevent re-renders on every keystroke. * Compares message identity (reference + count) to avoid deep diffs. @@ -50,8 +48,6 @@ function ContextWindowDisplayInner({ messages, permissionMode }: { const contextTokens = useMemo(() => tokenCountWithEstimation(messages), [messages]); const pct = useMemo(() => Math.min(100, Math.max(0, Math.round((contextTokens / windowSize) * 100))), [contextTokens, windowSize]); - const filled = Math.max(0, Math.min(BAR_WIDTH, Math.round((pct / 100) * BAR_WIDTH))); - const bar = '█'.repeat(filled) + '░'.repeat(BAR_WIDTH - filled); const contextColor = pct >= 90 ? 'red' : pct >= 70 ? 'yellow' : undefined; const rateValue = Math.round(avgRate10s); const showTokenRate = rateValue > 0; @@ -61,11 +57,10 @@ function ContextWindowDisplayInner({ messages, permissionMode }: { return ( + context {pct}% - {bar} - - {inputK}/{windowK} - + · + {inputK} / {windowK} {showTokenRate && ·} {showTokenRate && {rateValue} tok/s} diff --git a/src/components/PromptInput/PromptInputFooterLeftSide.tsx b/src/components/PromptInput/PromptInputFooterLeftSide.tsx index da3a97e44b..cb75f6af97 100644 --- a/src/components/PromptInput/PromptInputFooterLeftSide.tsx +++ b/src/components/PromptInput/PromptInputFooterLeftSide.tsx @@ -346,11 +346,11 @@ function ModeIndicator({ // Rendered before the tasks pill so a long pill label (e.g. ultraplan URL) // doesn't push the mode indicator off-screen. const modePart = currentMode && hasActiveMode && !getIsRemoteMode() ? - {permissionModeSymbol(currentMode)}{' '} - {permissionModeTitle(currentMode).toLowerCase()} on + {currentMode === 'bypassPermissions' || currentMode === 'dontAsk' ? '⚠' : permissionModeSymbol(currentMode)}{' '} + {permissionModeTitle(currentMode).toLowerCase()} {shouldShowModeHint && - {' '} - + {' · '} + } : null; diff --git a/src/components/PromptInput/usePromptInputPlaceholder.ts b/src/components/PromptInput/usePromptInputPlaceholder.ts index 99bfbfea9b..39579e6182 100644 --- a/src/components/PromptInput/usePromptInputPlaceholder.ts +++ b/src/components/PromptInput/usePromptInputPlaceholder.ts @@ -62,10 +62,10 @@ export function usePromptInputPlaceholder({ promptSuggestionEnabled && !proactiveModule?.isProactiveActive() ) { - return getExampleCommandFromCache() ?? 'Ask Verboo to build, fix, or explain…' + return getExampleCommandFromCache() ?? 'Describe a task, bug, or idea…' } - return 'Ask Verboo to build, fix, or explain…' + return 'Describe a task, bug, or idea…' }, [ input, queuedCommands, diff --git a/src/components/StartupScreen.test.ts b/src/components/StartupScreen.test.ts index 02447d7503..af00c2ace3 100644 --- a/src/components/StartupScreen.test.ts +++ b/src/components/StartupScreen.test.ts @@ -194,20 +194,20 @@ describe('renderStartupScreen', () => { isLocal: false, } - test('uses the original mascot identity in a wide terminal', () => { + test('uses the compact ghost identity in a wide terminal', () => { const output = renderStartupScreen(provider, '0.14.5', '~/project', 120) const plainOutput = stripAnsi(output) - expect(plainOutput).not.toContain('👻') + expect(plainOutput).toContain('👻') expect(plainOutput).toContain('Verboo Code') - expect(plainOutput).toContain('▄▀▀▀▀▀▀▀▄') + expect(plainOutput).not.toContain('▄▀▀▀▀▀▀▀▄') expect(plainOutput).not.toContain('Tokens ilimitados') expect(output).toContain('\x1b[0m') expect(output).not.toContain('undefined') expect(plainOutput.split('\n').every(line => line.length <= 120)).toBe(true) }) - test('keeps the original mascot in a narrow terminal when it fits', () => { + test('keeps the compact identity in a narrow terminal', () => { const output = stripAnsi( renderStartupScreen( { @@ -220,8 +220,8 @@ describe('renderStartupScreen', () => { ), ) - expect(output).not.toContain('👻') - expect(output).toContain('▄▀▀▀▀▀▀▀▄') + expect(output).toContain('👻') + expect(output).not.toContain('▄▀▀▀▀▀▀▀▄') expect(output.split('\n').every(line => line.length <= 70)).toBe(true) }) }) diff --git a/src/components/StartupScreen.ts b/src/components/StartupScreen.ts index 00f06da23f..e2ef1e152a 100644 --- a/src/components/StartupScreen.ts +++ b/src/components/StartupScreen.ts @@ -22,30 +22,6 @@ const ACCENT = [173, 52, 254] as const const DIMCOL = [120, 100, 140] as const const STARTUP_DEFAULT_COLUMNS = 80 -// Original Verboo mascot. Keep this code-native so it renders consistently in -// terminals without depending on the platform's emoji artwork. -const VERBOO_LOGO = [ - ' ▄▀▀▀▀▀▀▀▄ ', - '▄▀▀▀▀▀▀▀▀▀▀▀▄', - '▀▀▀ ▀▀▀▀▀ ▀▀▀', - '▀▀▀▀▀▀▀▀▀▀▀▀▀', - '▀▀▀▀▀▄▄▄▀▀▀▀▀', - ' ▀▀▀▀▀▀▀▀▀▀▀ ', - '▄▀▀ ▀▀▀▀▀ ▀▀▄', -] - -const VERBOO_LOGO_MASK = [ - ' 011111110 ', - '0111111111110', - '1110111110111', - '1111011101111', - '1111100011111', - ' 11111111111 ', - '110 01110 011', -] - -const STARTUP_LOGO_MIN_COLUMNS = 48 - // ─── Provider detection ─────────────────────────────────────────────────────── function resolveVerbooStartupModel(modelOverride?: string): string { @@ -193,58 +169,20 @@ export function renderStartupScreen( const out: string[] = [''] const bold = `${ESC}1m` const PURPLE = rgb(...ACCENT) - const PURPLE_FILL = `${PURPLE}${ESC}48;2;${ACCENT[0]};${ACCENT[1]};${ACCENT[2]}m` const DIMP = `${DIM}${rgb(...DIMCOL)}` const STATUS_C = p.isLocal ? rgb(130, 200, 140) : PURPLE const statusLabel = p.isLocal ? 'local' : 'cloud' const providerAndModel = p.name === 'Verboo' ? p.model : `${p.name} · ${p.model}` + const shownVersion = truncateStartupText(version, Math.max(1, columns - 22)) + const model = truncateStartupText( + providerAndModel, + Math.max(1, columns - statusLabel.length - 10), + ) + const cwd = truncateStartupText(displayCwd, Math.max(1, columns - 5)) - if (columns < STARTUP_LOGO_MIN_COLUMNS) { - const detailWidth = Math.max(1, columns - 4) - const shownVersion = truncateStartupText(version, Math.max(1, columns - 20)) - const model = truncateStartupText(providerAndModel, Math.max(1, detailWidth - statusLabel.length - 4)) - const cwd = truncateStartupText(displayCwd, detailWidth) - - out.push(` ${bold}${PURPLE}Verboo Code${RESET} ${DIMP}v${shownVersion}${RESET}`) - out.push(` ${STATUS_C}●${RESET} ${DIMP}${model} · ${statusLabel}${RESET}`) - out.push(` ${DIMP}${cwd}${RESET}`) - } else { - const maxLogoWidth = Math.max(...VERBOO_LOGO.map(line => line.trimEnd().length)) - const logoTextPadding = 2 - const rightTextWidth = Math.max(1, columns - 2 - maxLogoWidth - logoTextPadding) - const shownVersion = truncateStartupText(version, Math.max(1, rightTextWidth - 16)) - const model = truncateStartupText(providerAndModel, Math.max(1, rightTextWidth - statusLabel.length - 5)) - const cwd = truncateStartupText(displayCwd, rightTextWidth) - const hint = truncateStartupText('Type a request, or use /help for commands', rightTextWidth) - const rightColumn = [ - '', - `${bold}${PURPLE}Verboo Code${RESET} ${DIMP}v${shownVersion}${RESET}`, - `${STATUS_C}●${RESET} ${DIMP}${model} · ${statusLabel}${RESET}`, - `${DIMP}${cwd}${RESET}`, - '', - `${DIMP}${hint}${RESET}`, - '', - ] - - for (let i = 0; i < VERBOO_LOGO.length; i++) { - const line = (VERBOO_LOGO[i] ?? '').trimEnd() - const mask = VERBOO_LOGO_MASK[i] ?? '' - let painted = '' - - for (let j = 0; j < line.length; j++) { - const character = line[j] ?? '' - const isBlock = character === '▀' || character === '▄' - painted += isBlock && mask[j] === '1' - ? `${PURPLE_FILL}${character}${RESET}` - : isBlock - ? `${PURPLE}${character}${RESET}` - : character - } - - const gap = ' '.repeat(Math.max(0, maxLogoWidth - line.length + logoTextPadding)) - out.push(` ${painted}${gap}${rightColumn[i] ?? ''}`) - } - } + out.push(` ${PURPLE}╭${RESET} 👻 ${bold}${PURPLE}Verboo Code${RESET} ${DIMP}v${shownVersion}${RESET}`) + out.push(` ${PURPLE}│${RESET} ${STATUS_C}●${RESET} ${DIMP}${model} · ${statusLabel}${RESET}`) + out.push(` ${PURPLE}╰${RESET} ${DIMP}${cwd}${RESET}`) out.push('')