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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`Full Terminal Tool Confirmation Snapshot > renders tool confirmation box in the frame of the entire terminal 1`] = `
" > Can you edit InputPrompt.tsx for me?
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀

╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│ ? Edit packages/.../InputPrompt.tsx: return kittyProtocolSupporte... => return kittyProto… │
Expand Down
176 changes: 2 additions & 174 deletions packages/cli/src/ui/components/InputPrompt.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,8 @@ import * as clipboardUtils from '../utils/clipboardUtils.js';
import { useKittyKeyboardProtocol } from '../hooks/useKittyKeyboardProtocol.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import stripAnsi from 'strip-ansi';
import chalk from 'chalk';
import { StreamingState } from '../types.js';
import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js';
import type { UIState } from '../contexts/UIStateContext.js';
import { isLowColorDepth } from '../utils/terminalUtils.js';
import { cpLen } from '../utils/textUtils.js';
import { defaultKeyMatchers, Command } from '../key/keyMatchers.js';
import { useKeypress, type Key } from '../hooks/useKeypress.js';
Expand All @@ -78,9 +75,6 @@ vi.mock('../hooks/useReverseSearchCompletion.js');
vi.mock('clipboardy');
vi.mock('../utils/clipboardUtils.js');
vi.mock('../hooks/useKittyKeyboardProtocol.js');
vi.mock('../utils/terminalUtils.js', () => ({
isLowColorDepth: vi.fn(() => false),
}));

// Mock ink BEFORE importing components that use it to intercept terminalCursorPosition
vi.mock('ink', async (importOriginal) => {
Expand Down Expand Up @@ -1914,172 +1908,6 @@ describe('InputPrompt', () => {
unmount();
});

describe('Background Color Styles', () => {
beforeEach(() => {
vi.mocked(isLowColorDepth).mockReturnValue(false);
});

afterEach(() => {
vi.restoreAllMocks();
});

it('should render with background color by default', async () => {
const { stdout, unmount } = await renderWithProviders(
<TestInputPrompt {...props} />,
);

await waitFor(() => {
const frame = stdout.lastFrameRaw();
expect(frame).toContain('▀');
expect(frame).toContain('▄');
});
unmount();
});

it.each([
{ color: 'black', name: 'black' },
{ color: '#000000', name: '#000000' },
{ color: '#000', name: '#000' },
{ color: 'white', name: 'white' },
{ color: '#ffffff', name: '#ffffff' },
{ color: '#fff', name: '#fff' },
])(
'should render with safe grey background but NO side borders in 8-bit mode when background is $name',
async ({ color }) => {
vi.mocked(isLowColorDepth).mockReturnValue(true);

const { stdout, unmount } = await renderWithProviders(
<TestInputPrompt {...props} />,
{
uiState: {
terminalBackgroundColor: color,
} as Partial<UIState>,
},
);

const isWhite =
color === 'white' || color === '#ffffff' || color === '#fff';
const expectedBgColor = isWhite ? '#eeeeee' : '#1c1c1c';

await waitFor(() => {
const frame = stdout.lastFrameRaw();

// Use chalk to get the expected background color escape sequence
const bgCheck = chalk.bgHex(expectedBgColor)(' ');
const bgCode = bgCheck.substring(0, bgCheck.indexOf(' '));

// Background color code should be present
expect(frame).toContain(bgCode);
// Background characters should be rendered
expect(frame).toContain('▀');
expect(frame).toContain('▄');
// Side borders should STILL be removed
expect(frame).not.toContain('│');
});

unmount();
},
);

it('should NOT render with background color but SHOULD render horizontal lines when color depth is < 24 and background is NOT black', async () => {
vi.mocked(isLowColorDepth).mockReturnValue(true);

const { stdout, unmount } = await renderWithProviders(
<TestInputPrompt {...props} />,
{
uiState: {
terminalBackgroundColor: '#333333',
} as Partial<UIState>,
},
);

await waitFor(() => {
const frame = stdout.lastFrameRaw();
expect(frame).not.toContain('▀');
expect(frame).not.toContain('▄');
// It SHOULD have horizontal fallback lines
expect(frame).toContain('─');
// It SHOULD NOT have vertical side borders (standard Box borders have │)
expect(frame).not.toContain('│');
});
unmount();
});
it('should handle 4-bit color mode (16 colors) as low color depth', async () => {
vi.mocked(isLowColorDepth).mockReturnValue(true);

const { stdout, unmount } = await renderWithProviders(
<TestInputPrompt {...props} />,
{
uiState: {
terminalBackgroundColor: 'black',
} as Partial<UIState>,
},
);

await waitFor(() => {
const frame = stdout.lastFrameRaw();

expect(frame).toContain('▀');

expect(frame).not.toContain('│');
});

unmount();
});

it('should render horizontal lines (but NO background) in 8-bit mode when background is blue', async () => {
vi.mocked(isLowColorDepth).mockReturnValue(true);

const { stdout, unmount } = await renderWithProviders(
<TestInputPrompt {...props} />,

{
uiState: {
terminalBackgroundColor: 'blue',
} as Partial<UIState>,
},
);

await waitFor(() => {
const frame = stdout.lastFrameRaw();

// Should NOT have background characters

expect(frame).not.toContain('▀');

expect(frame).not.toContain('▄');

// Should HAVE horizontal lines from the fallback Box borders

// Box style "round" uses these for top/bottom

expect(frame).toContain('─');

// Should NOT have vertical side borders

expect(frame).not.toContain('│');
});

unmount();
});

it('should render with plain borders when useBackgroundColor is false', async () => {
props.config.getUseBackgroundColor = () => false;
const { stdout, unmount } = await renderWithProviders(
<TestInputPrompt {...props} />,
);

await waitFor(() => {
const frame = stdout.lastFrameRaw();
expect(frame).not.toContain('▀');
expect(frame).not.toContain('▄');
// Check for Box borders (round style uses unicode box chars)
expect(frame).toMatch(/[─│┐└┘┌]/);
});
unmount();
});
});

describe('cursor-based completion trigger', () => {
it.each([
{
Expand Down Expand Up @@ -4007,9 +3835,9 @@ describe('InputPrompt', () => {
expect(stdout.lastFrame()).toContain('hello world');
});

// With plain borders: 1(border) + 1(padding) + 2(prompt) = 4 offset (x=4, col=5)
// With plain borders offset
await act(async () => {
stdin.write(`\x1b[<0;5;2M`); // Click at col 5, row 2
stdin.write(`\x1b[<0;4;2M`); // Click at col 4, row 2
});

await waitFor(() => {
Expand Down
40 changes: 8 additions & 32 deletions packages/cli/src/ui/components/InputPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ import {
import { parseSlashCommand } from '../../utils/commands.js';
import * as path from 'node:path';
import { SCREEN_READER_USER_PREFIX } from '../textConstants.js';
import { getSafeLowColorBackground } from '../themes/color-utils.js';
import { isLowColorDepth } from '../utils/terminalUtils.js';
import { useShellFocusState } from '../contexts/ShellFocusContext.js';
import { useUIState } from '../contexts/UIStateContext.js';
import { useInputState } from '../contexts/InputContext.js';
Expand Down Expand Up @@ -1645,21 +1643,6 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
);

const useBackgroundColor = config.getUseBackgroundColor();
const isLowColor = isLowColorDepth();
const terminalBg = theme.background.primary || 'black';

// We should fallback to lines if the background color is disabled OR if it is
// enabled but we are in a low color depth terminal where we don't have a safe
// background color to use.
const useLineFallback = useMemo(() => {
if (!useBackgroundColor) {
return true;
}
if (isLowColor) {
return !getSafeLowColorBackground(terminalBg);
}
return false;
}, [useBackgroundColor, isLowColor, terminalBg]);

const prevCursorRef = useRef(buffer.visualCursor);
const prevTextRef = useRef(buffer.text);
Expand Down Expand Up @@ -1698,8 +1681,11 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
}
}, [buffer.visualCursor, buffer.text, focus]);

const listBackgroundColor =
useLineFallback || !useBackgroundColor ? undefined : theme.background.input;
const listBackgroundColor = !useBackgroundColor
? undefined
: theme.background.input;

const useLineFallback = !!process.env['NO_COLOR'];

useEffect(() => {
if (onSuggestionsVisibilityChange) {
Expand Down Expand Up @@ -1762,7 +1748,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
return (
<>
{suggestionsPosition === 'above' && suggestionsNode}
{useLineFallback ? (
{useLineFallback || !useBackgroundColor ? (
<Box
borderStyle="round"
borderTop={true}
Expand All @@ -1781,17 +1767,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
backgroundOpacity={1}
useBackgroundColor={useBackgroundColor}
>
<Box
flexGrow={1}
flexDirection="row"
paddingX={1}
borderColor={borderColor}
borderStyle={useLineFallback ? 'round' : undefined}
borderTop={false}
borderBottom={false}
borderLeft={!useBackgroundColor}
borderRight={!useBackgroundColor}
>
<Box flexGrow={1} flexDirection="row" paddingX={1}>
<Text
color={statusColor ?? theme.text.accent}
aria-label={statusText || undefined}
Expand Down Expand Up @@ -1880,7 +1856,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
</Box>
</Box>
</HalfLinePaddedBox>
{useLineFallback ? (
{useLineFallback || !useBackgroundColor ? (
<Box
borderStyle="round"
borderTop={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ Tips for getting started:
2. /help for more information
3. Ask coding questions, edit code or run commands
4. Be specific for the best results
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
> Hello Gemini
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
> Hello Gemini
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
✦ Hello User!
"
`;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading