Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 15 additions & 25 deletions src/components/LogoV2/WelcomeV2.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Box flexDirection="row" gap={2} marginY={1} paddingX={1} alignItems="center">
<Box flexDirection="column">
{VERBOO_LOGO.map((line, index) => (
<Text color="claude" key={index}>
{line}
</Text>
))}
<Box flexDirection="column" marginY={1} paddingX={1}>
<Box flexDirection="row">
<Text color="claude">╭</Text>
<Text> 👻 </Text>
<Text bold color="claude">
Verboo Code
</Text>
<Text dimColor> v{version}</Text>
</Box>
<Box flexDirection="row">
<Text color="claude">│</Text>
<Text dimColor> Build, debug, and ship from your terminal.</Text>
</Box>
<Box flexDirection="column">
<Box flexDirection="row" gap={1}>
<Text bold color="claude">
Verboo Code
</Text>
<Text dimColor>v{version}</Text>
</Box>
<Text dimColor>Build, debug, and ship from your terminal.</Text>
<Box flexDirection="row">
<Text color="claude">╰</Text>
<Text dimColor> Describe what you need to get started.</Text>
</Box>
</Box>
)
Expand Down
7 changes: 3 additions & 4 deletions src/components/PromptInput/PromptInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2282,7 +2282,7 @@ function PromptInput({
<Text dimColor>Waiting for permission…</Text>
</Box>}
<PromptInputStashNotice hasStash={stashedPrompt !== undefined} />
<Box marginLeft={2} height={1} overflow="hidden">
<Box paddingX={2} height={1} overflow="hidden" justifyContent="flex-end">
{mode === 'prompt' && !exitMessage.show && !isPasting ? <ContextWindowDisplay messages={messages} permissionMode={effectiveToolPermissionContext.mode} /> : <Text> </Text>}
</Box>
{swarmBanner ? <>
Expand Down Expand Up @@ -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
Expand Down
11 changes: 3 additions & 8 deletions src/components/PromptInput/PromptInputFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -61,11 +57,10 @@ function ContextWindowDisplayInner({ messages, permissionMode }: {

return (
<Box flexDirection="row" gap={1}>
<Text dimColor>context</Text>
<Text color={contextColor} dimColor={contextColor === undefined}>{pct}%</Text>
<Text color={contextColor} dimColor={contextColor === undefined}>{bar}</Text>
<Text dimColor>
{inputK}/{windowK}
</Text>
<Text dimColor>·</Text>
<Text dimColor>{inputK} / {windowK}</Text>
{showTokenRate && <Text dimColor>·</Text>}
{showTokenRate && <Text dimColor={!isGenerating} color={rateColor}>{rateValue} tok/s</Text>}
</Box>
Expand Down
8 changes: 4 additions & 4 deletions src/components/PromptInput/PromptInputFooterLeftSide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() ? <Text color={getModeColor(currentMode)} key="mode">
{permissionModeSymbol(currentMode)}{' '}
{permissionModeTitle(currentMode).toLowerCase()} on
{currentMode === 'bypassPermissions' || currentMode === 'dontAsk' ? '⚠' : permissionModeSymbol(currentMode)}{' '}
{permissionModeTitle(currentMode).toLowerCase()}
{shouldShowModeHint && <Text dimColor>
{' '}
<KeyboardShortcutHint shortcut={modeCycleShortcut} action="cycle" parens />
{' · '}
<KeyboardShortcutHint shortcut={modeCycleShortcut} action="change" />
</Text>}
</Text> : null;

Expand Down
4 changes: 2 additions & 2 deletions src/components/PromptInput/usePromptInputPlaceholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions src/components/StartupScreen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
{
Expand All @@ -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)
})
})
80 changes: 9 additions & 71 deletions src/components/StartupScreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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('')

Expand Down