diff --git a/docs/cli/settings.md b/docs/cli/settings.md
index ea7d5c9f8d..83b92fb3c8 100644
--- a/docs/cli/settings.md
+++ b/docs/cli/settings.md
@@ -57,8 +57,8 @@ they appear in the UI.
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `true` |
| Show Citations | `ui.showCitations` | Show citations for generated text in the chat. | `false` |
| Show Model Info In Chat | `ui.showModelInfoInChat` | Show the model name in the chat for each model turn. | `false` |
-| Use Full Width | `ui.useFullWidth` | Use the entire width of the terminal for output. | `true` |
| Use Alternate Screen Buffer | `ui.useAlternateBuffer` | Use an alternate screen buffer for the UI, preserving shell history. | `false` |
+| Use Background Color | `ui.useBackgroundColor` | Whether to use background colors in the UI. | `true` |
| Incremental Rendering | `ui.incrementalRendering` | Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled. | `true` |
| Enable Loading Phrases | `ui.accessibility.enableLoadingPhrases` | Enable loading phrases during operations. | `true` |
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible | `false` |
diff --git a/docs/get-started/configuration.md b/docs/get-started/configuration.md
index 9dc13a10d2..d7885df084 100644
--- a/docs/get-started/configuration.md
+++ b/docs/get-started/configuration.md
@@ -244,16 +244,16 @@ their corresponding top-level category object in your `settings.json` file.
- **Description:** Show the model name in the chat for each model turn.
- **Default:** `false`
-- **`ui.useFullWidth`** (boolean):
- - **Description:** Use the entire width of the terminal for output.
- - **Default:** `true`
-
- **`ui.useAlternateBuffer`** (boolean):
- **Description:** Use an alternate screen buffer for the UI, preserving shell
history.
- **Default:** `false`
- **Requires restart:** Yes
+- **`ui.useBackgroundColor`** (boolean):
+ - **Description:** Whether to use background colors in the UI.
+ - **Default:** `true`
+
- **`ui.incrementalRendering`** (boolean):
- **Description:** Enable incremental rendering for the UI. This option will
reduce flickering but may cause rendering artifacts. Only supported when
diff --git a/package-lock.json b/package-lock.json
index f89bac2f1c..6da192364b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
"packages/*"
],
"dependencies": {
- "ink": "npm:@jrichman/ink@6.4.7",
+ "ink": "npm:@jrichman/ink@6.4.8",
"latest-version": "^9.0.0",
"simple-git": "^3.28.0"
},
@@ -2251,6 +2251,7 @@
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@octokit/auth-token": "^6.0.0",
"@octokit/graphql": "^9.0.2",
@@ -2431,6 +2432,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
+ "peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -2464,6 +2466,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz",
"integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0"
},
@@ -2832,6 +2835,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz",
"integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@opentelemetry/core": "2.0.1",
"@opentelemetry/semantic-conventions": "^1.29.0"
@@ -2865,6 +2869,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz",
"integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@opentelemetry/core": "2.0.1",
"@opentelemetry/resources": "2.0.1"
@@ -2917,6 +2922,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz",
"integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"@opentelemetry/core": "2.0.1",
"@opentelemetry/resources": "2.0.1",
@@ -4122,6 +4128,7 @@
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"devOptional": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -4399,6 +4406,7 @@
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.35.0",
"@typescript-eslint/types": "8.35.0",
@@ -5391,6 +5399,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -8400,6 +8409,7 @@
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
"dev": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -8940,6 +8950,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -10537,10 +10548,11 @@
},
"node_modules/ink": {
"name": "@jrichman/ink",
- "version": "6.4.7",
- "resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.7.tgz",
- "integrity": "sha512-QHyxhNF5VonF5cRmdAJD/UPucB9nRx3FozWMjQrDGfBxfAL9lpyu72/MlFPgloS1TMTGsOt7YN6dTPPA6mh0Aw==",
+ "version": "6.4.8",
+ "resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.8.tgz",
+ "integrity": "sha512-v0thcXIKl9hqF/1w4HqA6MKxIcMoWSP3YtEZIAA+eeJngXpN5lGnMkb6rllB7FnOdwyEyYaFTcu1ZVr4/JZpWQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@alcalzone/ansi-tokenize": "^0.2.1",
"ansi-escapes": "^7.0.0",
@@ -14299,6 +14311,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -14309,6 +14322,7 @@
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
"devOptional": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"shell-quote": "^1.6.1",
"ws": "^7"
@@ -16545,6 +16559,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -16768,7 +16783,8 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
- "license": "0BSD"
+ "license": "0BSD",
+ "peer": true
},
"node_modules/tsx": {
"version": "4.20.3",
@@ -16776,6 +16792,7 @@
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
"devOptional": true,
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5"
@@ -16948,6 +16965,7 @@
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"devOptional": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -17155,6 +17173,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -17268,6 +17287,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -17280,6 +17300,7 @@
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
@@ -17984,6 +18005,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
+ "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
@@ -18075,7 +18097,7 @@
"fzf": "^0.5.2",
"glob": "^12.0.0",
"highlight.js": "^11.11.1",
- "ink": "npm:@jrichman/ink@6.4.7",
+ "ink": "npm:@jrichman/ink@6.4.8",
"ink-gradient": "^3.0.0",
"ink-spinner": "^5.0.0",
"latest-version": "^9.0.0",
@@ -18278,6 +18300,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
diff --git a/package.json b/package.json
index 08c7a7ccd6..4a570180ff 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,7 @@
"pre-commit": "node scripts/pre-commit.js"
},
"overrides": {
- "ink": "npm:@jrichman/ink@6.4.7",
+ "ink": "npm:@jrichman/ink@6.4.8",
"wrap-ansi": "9.0.2",
"cliui": {
"wrap-ansi": "7.0.0"
@@ -124,7 +124,7 @@
"yargs": "^17.7.2"
},
"dependencies": {
- "ink": "npm:@jrichman/ink@6.4.7",
+ "ink": "npm:@jrichman/ink@6.4.8",
"latest-version": "^9.0.0",
"simple-git": "^3.28.0"
},
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 9eccec9e67..e4159590b0 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -46,7 +46,7 @@
"fzf": "^0.5.2",
"glob": "^12.0.0",
"highlight.js": "^11.11.1",
- "ink": "npm:@jrichman/ink@6.4.7",
+ "ink": "npm:@jrichman/ink@6.4.8",
"ink-gradient": "^3.0.0",
"ink-spinner": "^5.0.0",
"latest-version": "^9.0.0",
diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts
index 9b00f0ea33..1a622f7d0c 100755
--- a/packages/cli/src/config/config.ts
+++ b/packages/cli/src/config/config.ts
@@ -766,6 +766,7 @@ export async function loadCliConfig(
folderTrust,
interactive,
trustedFolder,
+ useBackgroundColor: settings.ui?.useBackgroundColor,
useRipgrep: settings.tools?.useRipgrep,
enableInteractiveShell: settings.tools?.shell?.enableInteractiveShell,
shellToolInactivityTimeout: settings.tools?.shell?.inactivityTimeout,
diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts
index fbd72cec36..6a881fdeff 100644
--- a/packages/cli/src/config/settingsSchema.ts
+++ b/packages/cli/src/config/settingsSchema.ts
@@ -526,15 +526,6 @@ const SETTINGS_SCHEMA = {
description: 'Show the model name in the chat for each model turn.',
showInDialog: true,
},
- useFullWidth: {
- type: 'boolean',
- label: 'Use Full Width',
- category: 'UI',
- requiresRestart: false,
- default: true,
- description: 'Use the entire width of the terminal for output.',
- showInDialog: true,
- },
useAlternateBuffer: {
type: 'boolean',
label: 'Use Alternate Screen Buffer',
@@ -545,6 +536,15 @@ const SETTINGS_SCHEMA = {
'Use an alternate screen buffer for the UI, preserving shell history.',
showInDialog: true,
},
+ useBackgroundColor: {
+ type: 'boolean',
+ label: 'Use Background Color',
+ category: 'UI',
+ requiresRestart: false,
+ default: true,
+ description: 'Whether to use background colors in the UI.',
+ showInDialog: true,
+ },
incrementalRendering: {
type: 'boolean',
label: 'Incremental Rendering',
diff --git a/packages/cli/src/test-utils/render.tsx b/packages/cli/src/test-utils/render.tsx
index 717aa668d1..85c3e0f305 100644
--- a/packages/cli/src/test-utils/render.tsx
+++ b/packages/cli/src/test-utils/render.tsx
@@ -16,7 +16,6 @@ import { SettingsContext } from '../ui/contexts/SettingsContext.js';
import { ShellFocusContext } from '../ui/contexts/ShellFocusContext.js';
import { UIStateContext, type UIState } from '../ui/contexts/UIStateContext.js';
import { ConfigContext } from '../ui/contexts/ConfigContext.js';
-import { calculateMainAreaWidth } from '../ui/utils/ui-sizing.js';
import { VimModeProvider } from '../ui/contexts/VimModeContext.js';
import { MouseProvider } from '../ui/contexts/MouseContext.js';
import { ScrollProvider } from '../ui/contexts/ScrollProvider.js';
@@ -38,6 +37,11 @@ vi.mock('../utils/persistentState.js', () => ({
persistentState: persistentStateMock,
}));
+vi.mock('../ui/utils/terminalUtils.js', () => ({
+ isLowColorDepth: vi.fn(() => false),
+ getColorDepth: vi.fn(() => 24),
+}));
+
// Wrapper around ink-testing-library's render that ensures act() is called
export const render = (
tree: React.ReactElement,
@@ -147,7 +151,6 @@ export const createMockSettings = (
const baseMockUiState = {
renderMarkdown: true,
streamingState: StreamingState.Idle,
- mainAreaWidth: 100,
terminalWidth: 120,
terminalHeight: 40,
currentModel: 'gemini-pro',
@@ -269,7 +272,7 @@ export const renderWithProviders = (
});
}
- const mainAreaWidth = calculateMainAreaWidth(terminalWidth, finalSettings);
+ const mainAreaWidth = terminalWidth;
const finalUiState = {
...baseState,
diff --git a/packages/cli/src/ui/auth/ApiAuthDialog.test.tsx b/packages/cli/src/ui/auth/ApiAuthDialog.test.tsx
index ea67bdcf6c..ddcf301268 100644
--- a/packages/cli/src/ui/auth/ApiAuthDialog.test.tsx
+++ b/packages/cli/src/ui/auth/ApiAuthDialog.test.tsx
@@ -34,7 +34,7 @@ vi.mock('../components/shared/text-buffer.js', () => ({
vi.mock('../contexts/UIStateContext.js', () => ({
useUIState: vi.fn(() => ({
- mainAreaWidth: 80,
+ terminalWidth: 80,
})),
}));
diff --git a/packages/cli/src/ui/auth/ApiAuthDialog.tsx b/packages/cli/src/ui/auth/ApiAuthDialog.tsx
index 6345599634..f76fb90edb 100644
--- a/packages/cli/src/ui/auth/ApiAuthDialog.tsx
+++ b/packages/cli/src/ui/auth/ApiAuthDialog.tsx
@@ -28,8 +28,8 @@ export function ApiAuthDialog({
error,
defaultValue = '',
}: ApiAuthDialogProps): React.JSX.Element {
- const { mainAreaWidth } = useUIState();
- const viewportWidth = mainAreaWidth - 8;
+ const { terminalWidth } = useUIState();
+ const viewportWidth = terminalWidth - 8;
const pendingPromise = useRef<{ cancel: () => void } | null>(null);
diff --git a/packages/cli/src/ui/components/AppHeader.tsx b/packages/cli/src/ui/components/AppHeader.tsx
index 5efe1ed81f..77042c6e3a 100644
--- a/packages/cli/src/ui/components/AppHeader.tsx
+++ b/packages/cli/src/ui/components/AppHeader.tsx
@@ -21,7 +21,7 @@ interface AppHeaderProps {
export const AppHeader = ({ version }: AppHeaderProps) => {
const settings = useSettings();
const config = useConfig();
- const { nightly, mainAreaWidth, bannerData, bannerVisible } = useUIState();
+ const { nightly, terminalWidth, bannerData, bannerVisible } = useUIState();
const { bannerText } = useBanner(bannerData, config);
const { showTips } = useTips();
@@ -33,7 +33,7 @@ export const AppHeader = ({ version }: AppHeaderProps) => {
{bannerVisible && bannerText && (
diff --git a/packages/cli/src/ui/components/Composer.tsx b/packages/cli/src/ui/components/Composer.tsx
index de3ecebd19..8f6c807de7 100644
--- a/packages/cli/src/ui/components/Composer.tsx
+++ b/packages/cli/src/ui/components/Composer.tsx
@@ -50,7 +50,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
return (
@@ -113,7 +113,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
maxHeight={
uiState.constrainHeight ? debugConsoleMaxHeight : undefined
}
- width={uiState.mainAreaWidth}
+ width={uiState.terminalWidth}
hasFocus={uiState.showErrorDetails}
/>
diff --git a/packages/cli/src/ui/components/DialogManager.test.tsx b/packages/cli/src/ui/components/DialogManager.test.tsx
index 5ac33794cc..85cc050b21 100644
--- a/packages/cli/src/ui/components/DialogManager.test.tsx
+++ b/packages/cli/src/ui/components/DialogManager.test.tsx
@@ -72,7 +72,7 @@ describe('DialogManager', () => {
constrainHeight: false,
terminalHeight: 24,
staticExtraHeight: 0,
- mainAreaWidth: 80,
+ terminalWidth: 80,
confirmUpdateExtensionRequests: [],
showIdeRestartPrompt: false,
proQuotaRequest: null,
diff --git a/packages/cli/src/ui/components/DialogManager.tsx b/packages/cli/src/ui/components/DialogManager.tsx
index 305f2333f1..b8bf51a81e 100644
--- a/packages/cli/src/ui/components/DialogManager.tsx
+++ b/packages/cli/src/ui/components/DialogManager.tsx
@@ -50,8 +50,12 @@ export const DialogManager = ({
const uiState = useUIState();
const uiActions = useUIActions();
- const { constrainHeight, terminalHeight, staticExtraHeight, mainAreaWidth } =
- uiState;
+ const {
+ constrainHeight,
+ terminalHeight,
+ staticExtraHeight,
+ terminalWidth: uiTerminalWidth,
+ } = uiState;
if (uiState.adminSettingsChanged) {
return ;
@@ -147,7 +151,7 @@ export const DialogManager = ({
availableTerminalHeight={
constrainHeight ? terminalHeight - staticExtraHeight : undefined
}
- terminalWidth={mainAreaWidth}
+ terminalWidth={uiTerminalWidth}
/>
);
diff --git a/packages/cli/src/ui/components/Footer.tsx b/packages/cli/src/ui/components/Footer.tsx
index 44bab56f45..c488568e7d 100644
--- a/packages/cli/src/ui/components/Footer.tsx
+++ b/packages/cli/src/ui/components/Footer.tsx
@@ -42,7 +42,7 @@ export const Footer: React.FC = () => {
promptTokenCount,
nightly,
isTrustedFolder,
- mainAreaWidth,
+ terminalWidth,
} = {
model: uiState.currentModel,
targetDir: config.getTargetDir(),
@@ -55,7 +55,7 @@ export const Footer: React.FC = () => {
promptTokenCount: uiState.sessionStats.lastPromptTokenCount,
nightly: uiState.nightly,
isTrustedFolder: uiState.isTrustedFolder,
- mainAreaWidth: uiState.mainAreaWidth,
+ terminalWidth: uiState.terminalWidth,
};
const showMemoryUsage =
@@ -65,7 +65,7 @@ export const Footer: React.FC = () => {
const hideModelInfo = settings.merged.ui.footer.hideModelInfo;
const hideContextPercentage = settings.merged.ui.footer.hideContextPercentage;
- const pathLength = Math.max(20, Math.floor(mainAreaWidth * 0.25));
+ const pathLength = Math.max(20, Math.floor(terminalWidth * 0.25));
const displayPath = shortenPath(tildeifyPath(targetDir), pathLength);
const justifyContent = hideCWD && hideModelInfo ? 'center' : 'space-between';
@@ -76,7 +76,7 @@ export const Footer: React.FC = () => {
return (
{
) : (
no sandbox
- {mainAreaWidth >= 100 && (
+ {terminalWidth >= 100 && (
(see /docs)
)}
@@ -155,7 +155,7 @@ export const Footer: React.FC = () => {
>
)}
diff --git a/packages/cli/src/ui/components/HistoryItemDisplay.tsx b/packages/cli/src/ui/components/HistoryItemDisplay.tsx
index 7a72dc6120..3814e603d8 100644
--- a/packages/cli/src/ui/components/HistoryItemDisplay.tsx
+++ b/packages/cli/src/ui/components/HistoryItemDisplay.tsx
@@ -67,7 +67,7 @@ export const HistoryItemDisplay: React.FC = ({
)}
{itemForDisplay.type === 'user_shell' && (
-
+
)}
{itemForDisplay.type === 'gemini' && (
({
+ isLowColorDepth: vi.fn(() => false),
+}));
const mockSlashCommands: SlashCommand[] = [
{
@@ -260,6 +265,8 @@ describe('InputPrompt', () => {
getProjectRoot: () => path.join('test', 'project'),
getTargetDir: () => path.join('test', 'project', 'src'),
getVimMode: () => false,
+ getUseBackgroundColor: () => true,
+ getTerminalBackground: () => undefined,
getWorkspaceContext: () => ({
getDirectories: () => ['/test/project/src'],
}),
@@ -1320,6 +1327,168 @@ 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 } = renderWithProviders(
+ ,
+ );
+
+ await waitFor(() => {
+ const frame = stdout.lastFrame();
+ expect(frame).toContain('▀');
+ expect(frame).toContain('▄');
+ });
+ unmount();
+ });
+
+ it.each([
+ { color: 'black', name: 'black' },
+ { color: '#000000', name: '#000000' },
+ { color: '#000', name: '#000' },
+ { color: undefined, name: 'default (black)' },
+ { 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 } = renderWithProviders(
+ ,
+ {
+ uiState: {
+ terminalBackgroundColor: color,
+ } as Partial,
+ },
+ );
+
+ const isWhite =
+ color === 'white' || color === '#ffffff' || color === '#fff';
+ const expectedBgColor = isWhite ? '#eeeeee' : '#1c1c1c';
+
+ await waitFor(() => {
+ const frame = stdout.lastFrame();
+
+ // 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 } = renderWithProviders(
+ ,
+ {
+ uiState: {
+ terminalBackgroundColor: '#333333',
+ } as Partial,
+ },
+ );
+
+ await waitFor(() => {
+ const frame = stdout.lastFrame();
+ 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 } = renderWithProviders(
+ ,
+ );
+
+ await waitFor(() => {
+ const frame = stdout.lastFrame();
+
+ 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 } = renderWithProviders(
+ ,
+
+ {
+ uiState: {
+ terminalBackgroundColor: 'blue',
+ } as Partial,
+ },
+ );
+
+ await waitFor(() => {
+ const frame = stdout.lastFrame();
+
+ // 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 } = renderWithProviders(
+ ,
+ );
+
+ await waitFor(() => {
+ const frame = stdout.lastFrame();
+ 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([
{
@@ -1564,11 +1733,11 @@ describe('InputPrompt', () => {
mockBuffer.lines = [text];
mockBuffer.viewportVisualLines = [text];
mockBuffer.visualCursor = visualCursor as [number, number];
+ props.config.getUseBackgroundColor = () => false;
const { stdout, unmount } = renderWithProviders(
,
);
-
await waitFor(() => {
const frame = stdout.lastFrame();
expect(frame).toContain(expected);
@@ -1621,11 +1790,11 @@ describe('InputPrompt', () => {
mockBuffer.visualToLogicalMap = visualToLogicalMap as Array<
[number, number]
>;
+ props.config.getUseBackgroundColor = () => false;
const { stdout, unmount } = renderWithProviders(
,
);
-
await waitFor(() => {
const frame = stdout.lastFrame();
expect(frame).toContain(expected);
@@ -1645,11 +1814,11 @@ describe('InputPrompt', () => {
[1, 0],
[2, 0],
];
+ props.config.getUseBackgroundColor = () => false;
const { stdout, unmount } = renderWithProviders(
,
);
-
await waitFor(() => {
const frame = stdout.lastFrame();
const lines = frame!.split('\n');
@@ -1673,15 +1842,15 @@ describe('InputPrompt', () => {
mockBuffer.visualCursor = [2, 5]; // cursor at the end of "world"
// Provide a visual-to-logical mapping for each visual line
mockBuffer.visualToLogicalMap = [
- [0, 0], // 'hello' starts at col 0 of logical line 0
- [1, 0], // '' (blank) is logical line 1, col 0
- [2, 0], // 'world' is logical line 2, col 0
+ [0, 0],
+ [1, 0],
+ [2, 0],
];
+ props.config.getUseBackgroundColor = () => false;
const { stdout, unmount } = renderWithProviders(
,
);
-
await waitFor(() => {
const frame = stdout.lastFrame();
// Check that all lines, including the empty one, are rendered.
@@ -2505,20 +2674,23 @@ describe('InputPrompt', () => {
stdin.write('\x12');
});
await waitFor(() => {
- expect(stdout.lastFrame()).toMatchSnapshot(
- 'command-search-render-collapsed-match',
- );
+ expect(stdout.lastFrame()).toContain('(r:)');
});
+ expect(stdout.lastFrame()).toMatchSnapshot(
+ 'command-search-render-collapsed-match',
+ );
await act(async () => {
stdin.write('\u001B[C');
});
await waitFor(() => {
- expect(stdout.lastFrame()).toMatchSnapshot(
- 'command-search-render-expanded-match',
- );
+ // Just wait for any update to ensure it is stable.
+ // We could also wait for specific text if we knew it.
+ expect(stdout.lastFrame()).toContain('(r:)');
});
-
+ expect(stdout.lastFrame()).toMatchSnapshot(
+ 'command-search-render-expanded-match',
+ );
unmount();
});
@@ -2637,28 +2809,28 @@ describe('InputPrompt', () => {
name: 'first line, first char',
relX: 0,
relY: 0,
- mouseCol: 5,
+ mouseCol: 4,
mouseRow: 2,
},
{
name: 'first line, middle char',
relX: 6,
relY: 0,
- mouseCol: 11,
+ mouseCol: 10,
mouseRow: 2,
},
{
name: 'second line, first char',
relX: 0,
relY: 1,
- mouseCol: 5,
+ mouseCol: 4,
mouseRow: 3,
},
{
name: 'second line, end char',
relX: 5,
relY: 1,
- mouseCol: 10,
+ mouseCol: 9,
mouseRow: 3,
},
])(
@@ -2685,7 +2857,7 @@ describe('InputPrompt', () => {
});
// Simulate left mouse press at calculated coordinates.
- // Assumes inner box is at x=4, y=1 based on border(1)+padding(1)+prompt(2) and border-top(1).
+ // Without left border: inner box is at x=3, y=1 based on padding(1)+prompt(2) and border-top(1).
await act(async () => {
stdin.write(`\x1b[<0;${mouseCol};${mouseRow}M`);
});
@@ -2727,6 +2899,37 @@ describe('InputPrompt', () => {
unmount();
});
+
+ it('should move cursor on mouse click with plain borders', async () => {
+ props.config.getUseBackgroundColor = () => false;
+ props.buffer.text = 'hello world';
+ props.buffer.lines = ['hello world'];
+ props.buffer.viewportVisualLines = ['hello world'];
+ props.buffer.visualToLogicalMap = [[0, 0]];
+ props.buffer.visualCursor = [0, 11];
+ props.buffer.visualScrollRow = 0;
+
+ const { stdin, stdout, unmount } = renderWithProviders(
+ ,
+ { mouseEventsEnabled: true, uiActions },
+ );
+
+ // Wait for initial render
+ await waitFor(() => {
+ expect(stdout.lastFrame()).toContain('hello world');
+ });
+
+ // With plain borders: 1(border) + 1(padding) + 2(prompt) = 4 offset (x=4, col=5)
+ await act(async () => {
+ stdin.write(`\x1b[<0;5;2M`); // Click at col 5, row 2
+ });
+
+ await waitFor(() => {
+ expect(props.buffer.moveToVisualPosition).toHaveBeenCalledWith(0, 0);
+ });
+
+ unmount();
+ });
});
describe('queued message editing', () => {
@@ -2889,7 +3092,8 @@ describe('InputPrompt', () => {
const { stdout, unmount } = renderWithProviders(
,
);
- await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
+ await waitFor(() => expect(stdout.lastFrame()).toContain('!'));
+ expect(stdout.lastFrame()).toMatchSnapshot();
unmount();
});
@@ -2898,7 +3102,8 @@ describe('InputPrompt', () => {
const { stdout, unmount } = renderWithProviders(
,
);
- await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
+ await waitFor(() => expect(stdout.lastFrame()).toContain('>'));
+ expect(stdout.lastFrame()).toMatchSnapshot();
unmount();
});
@@ -2907,10 +3112,10 @@ describe('InputPrompt', () => {
const { stdout, unmount } = renderWithProviders(
,
);
- await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
+ await waitFor(() => expect(stdout.lastFrame()).toContain('*'));
+ expect(stdout.lastFrame()).toMatchSnapshot();
unmount();
});
-
it('should not show inverted cursor when shell is focused', async () => {
props.isEmbeddedShellFocused = true;
props.focus = false;
@@ -2919,8 +3124,8 @@ describe('InputPrompt', () => {
);
await waitFor(() => {
expect(stdout.lastFrame()).not.toContain(`{chalk.inverse(' ')}`);
- expect(stdout.lastFrame()).toMatchSnapshot();
});
+ expect(stdout.lastFrame()).toMatchSnapshot();
unmount();
});
});
@@ -3022,8 +3227,9 @@ describe('InputPrompt', () => {
,
);
await waitFor(() => {
- expect(stdout.lastFrame()).toMatchSnapshot();
+ expect(stdout.lastFrame()).toContain('[Image');
});
+ expect(stdout.lastFrame()).toMatchSnapshot();
unmount();
});
@@ -3040,8 +3246,9 @@ describe('InputPrompt', () => {
,
);
await waitFor(() => {
- expect(stdout.lastFrame()).toMatchSnapshot();
+ expect(stdout.lastFrame()).toContain('@/path/to/screenshots');
});
+ expect(stdout.lastFrame()).toMatchSnapshot();
unmount();
});
});
diff --git a/packages/cli/src/ui/components/InputPrompt.tsx b/packages/cli/src/ui/components/InputPrompt.tsx
index c1c3644f20..e0199b8630 100644
--- a/packages/cli/src/ui/components/InputPrompt.tsx
+++ b/packages/cli/src/ui/components/InputPrompt.tsx
@@ -6,11 +6,12 @@
import type React from 'react';
import clipboardy from 'clipboardy';
-import { useCallback, useEffect, useState, useRef } from 'react';
+import { useCallback, useEffect, useState, useRef, useMemo } from 'react';
import { Box, Text, useStdout, type DOMElement } from 'ink';
import { SuggestionsDisplay, MAX_WIDTH } from './SuggestionsDisplay.js';
import { theme } from '../semantic-colors.js';
import { useInputHistory } from '../hooks/useInputHistory.js';
+import { HalfLinePaddedBox } from './shared/HalfLinePaddedBox.js';
import type { TextBuffer } from './shared/text-buffer.js';
import {
logicalPosToOffset,
@@ -47,6 +48,9 @@ import {
} from '../utils/commandUtils.js';
import * as path from 'node:path';
import { SCREEN_READER_USER_PREFIX } from '../textConstants.js';
+import { DEFAULT_BACKGROUND_OPACITY } from '../constants.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 { useSettings } from '../contexts/SettingsContext.js';
@@ -141,7 +145,8 @@ export const InputPrompt: React.FC = ({
const kittyProtocol = useKittyKeyboardProtocol();
const isShellFocused = useShellFocusState();
const { setEmbeddedShellFocused } = useUIActions();
- const { mainAreaWidth, activePtyId, history } = useUIState();
+ const { terminalWidth, activePtyId, history, terminalBackgroundColor } =
+ useUIState();
const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
const escPressCount = useRef(0);
const [showEscapePrompt, setShowEscapePrompt] = useState(false);
@@ -321,6 +326,7 @@ export const InputPrompt: React.FC = ({
const allMessages = popAllMessages();
if (allMessages) {
buffer.setText(allMessages);
+ return true;
} else {
// No queued messages, proceed with input history
inputHistory.navigateUp();
@@ -1033,6 +1039,23 @@ export const InputPrompt: React.FC = ({
const activeCompletion = getActiveCompletion();
const shouldShowSuggestions = activeCompletion.showSuggestions;
+ const useBackgroundColor = config.getUseBackgroundColor();
+ const isLowColor = isLowColorDepth();
+ const terminalBg = terminalBackgroundColor || '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]);
+
useEffect(() => {
if (onSuggestionsVisibilityChange) {
onSuggestionsVisibilityChange(shouldShowSuggestions);
@@ -1085,198 +1108,241 @@ export const InputPrompt: React.FC = ({
) : null;
+ const borderColor =
+ isShellFocused && !isEmbeddedShellFocused
+ ? (statusColor ?? theme.border.focused)
+ : theme.border.default;
+
return (
<>
{suggestionsPosition === 'above' && suggestionsNode}
-
+ ) : null}
+
-
- {shellModeActive ? (
- reverseSearchActive ? (
-
- (r:){' '}
-
+
+ {shellModeActive ? (
+ reverseSearchActive ? (
+
+ (r:){' '}
+
+ ) : (
+ '!'
+ )
+ ) : commandSearchActive ? (
+ (r:)
+ ) : showYoloStyling ? (
+ '*'
) : (
- '!'
- )
- ) : commandSearchActive ? (
- (r:)
- ) : showYoloStyling ? (
- '*'
- ) : (
- '>'
- )}{' '}
-
-
- {buffer.text.length === 0 && placeholder ? (
- showCursor ? (
-
- {chalk.inverse(placeholder.slice(0, 1))}
- {placeholder.slice(1)}
-
+ '>'
+ )}{' '}
+
+
+ {buffer.text.length === 0 && placeholder ? (
+ showCursor ? (
+
+ {chalk.inverse(placeholder.slice(0, 1))}
+
+ {placeholder.slice(1)}
+
+
+ ) : (
+ {placeholder}
+ )
) : (
- {placeholder}
- )
- ) : (
- linesToRender
- .map((lineText, visualIdxInRenderedSet) => {
- const absoluteVisualIdx =
- scrollVisualRow + visualIdxInRenderedSet;
- const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx];
- const cursorVisualRow =
- cursorVisualRowAbsolute - scrollVisualRow;
- const isOnCursorLine =
- focus && visualIdxInRenderedSet === cursorVisualRow;
+ linesToRender
+ .map((lineText: string, visualIdxInRenderedSet: number) => {
+ const absoluteVisualIdx =
+ scrollVisualRow + visualIdxInRenderedSet;
+ const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx];
+ const cursorVisualRow =
+ cursorVisualRowAbsolute - scrollVisualRow;
+ const isOnCursorLine =
+ focus && visualIdxInRenderedSet === cursorVisualRow;
- const renderedLine: React.ReactNode[] = [];
+ const renderedLine: React.ReactNode[] = [];
- const [logicalLineIdx] = mapEntry;
- const logicalLine = buffer.lines[logicalLineIdx] || '';
- const transformations =
- buffer.transformationsByLine[logicalLineIdx] ?? [];
- const tokens = parseInputForHighlighting(
- logicalLine,
- logicalLineIdx,
- transformations,
- ...(focus && buffer.cursor[0] === logicalLineIdx
- ? [buffer.cursor[1]]
- : []),
- );
- const startColInTransformed =
- buffer.visualToTransformedMap[absoluteVisualIdx] ?? 0;
- const visualStartCol = startColInTransformed;
- const visualEndCol = visualStartCol + cpLen(lineText);
- const segments = parseSegmentsFromTokens(
- tokens,
- visualStartCol,
- visualEndCol,
- );
- let charCount = 0;
- segments.forEach((seg, segIdx) => {
- const segLen = cpLen(seg.text);
- let display = seg.text;
+ const [logicalLineIdx] = mapEntry;
+ const logicalLine = buffer.lines[logicalLineIdx] || '';
+ const transformations =
+ buffer.transformationsByLine[logicalLineIdx] ?? [];
+ const tokens = parseInputForHighlighting(
+ logicalLine,
+ logicalLineIdx,
+ transformations,
+ ...(focus && buffer.cursor[0] === logicalLineIdx
+ ? [buffer.cursor[1]]
+ : []),
+ );
+ const startColInTransformed =
+ buffer.visualToTransformedMap[absoluteVisualIdx] ?? 0;
+ const visualStartCol = startColInTransformed;
+ const visualEndCol = visualStartCol + cpLen(lineText);
+ const segments = parseSegmentsFromTokens(
+ tokens,
+ visualStartCol,
+ visualEndCol,
+ );
+ let charCount = 0;
+ segments.forEach((seg, segIdx) => {
+ const segLen = cpLen(seg.text);
+ let display = seg.text;
- if (isOnCursorLine) {
- const relativeVisualColForHighlight =
- cursorVisualColAbsolute;
- const segStart = charCount;
- const segEnd = segStart + segLen;
- if (
- relativeVisualColForHighlight >= segStart &&
- relativeVisualColForHighlight < segEnd
- ) {
- const charToHighlight = cpSlice(
- display,
- relativeVisualColForHighlight - segStart,
- relativeVisualColForHighlight - segStart + 1,
- );
- const highlighted = showCursor
- ? chalk.inverse(charToHighlight)
- : charToHighlight;
- display =
- cpSlice(
+ if (isOnCursorLine) {
+ const relativeVisualColForHighlight =
+ cursorVisualColAbsolute;
+ const segStart = charCount;
+ const segEnd = segStart + segLen;
+ if (
+ relativeVisualColForHighlight >= segStart &&
+ relativeVisualColForHighlight < segEnd
+ ) {
+ const charToHighlight = cpSlice(
display,
- 0,
relativeVisualColForHighlight - segStart,
- ) +
- highlighted +
- cpSlice(
- display,
relativeVisualColForHighlight - segStart + 1,
);
+ const highlighted = showCursor
+ ? chalk.inverse(charToHighlight)
+ : charToHighlight;
+ display =
+ cpSlice(
+ display,
+ 0,
+ relativeVisualColForHighlight - segStart,
+ ) +
+ highlighted +
+ cpSlice(
+ display,
+ relativeVisualColForHighlight - segStart + 1,
+ );
+ }
+ charCount = segEnd;
+ } else {
+ // Advance the running counter even when not on cursor line
+ charCount += segLen;
}
- charCount = segEnd;
- } else {
- // Advance the running counter even when not on cursor line
- charCount += segLen;
- }
- const color =
- seg.type === 'command' ||
- seg.type === 'file' ||
- seg.type === 'paste'
- ? theme.text.accent
- : theme.text.primary;
+ const color =
+ seg.type === 'command' ||
+ seg.type === 'file' ||
+ seg.type === 'paste'
+ ? theme.text.accent
+ : theme.text.primary;
- renderedLine.push(
-
- {display}
- ,
- );
- });
-
- const currentLineGhost = isOnCursorLine ? inlineGhost : '';
- if (
- isOnCursorLine &&
- cursorVisualColAbsolute === cpLen(lineText)
- ) {
- if (!currentLineGhost) {
renderedLine.push(
-
- {showCursor ? chalk.inverse(' ') : ' '}
+
+ {display}
,
);
+ });
+
+ const currentLineGhost = isOnCursorLine ? inlineGhost : '';
+ if (
+ isOnCursorLine &&
+ cursorVisualColAbsolute === cpLen(lineText)
+ ) {
+ if (!currentLineGhost) {
+ renderedLine.push(
+
+ {showCursor ? chalk.inverse(' ') : ' '}
+ ,
+ );
+ }
}
- }
- const showCursorBeforeGhost =
- focus &&
- isOnCursorLine &&
- cursorVisualColAbsolute === cpLen(lineText) &&
- currentLineGhost;
+ const showCursorBeforeGhost =
+ focus &&
+ isOnCursorLine &&
+ cursorVisualColAbsolute === cpLen(lineText) &&
+ currentLineGhost;
- return (
-
-
- {renderedLine}
- {showCursorBeforeGhost &&
- (showCursor ? chalk.inverse(' ') : ' ')}
- {currentLineGhost && (
-
- {currentLineGhost}
-
- )}
-
-
- );
- })
- .concat(
- additionalLines.map((ghostLine, index) => {
- const padding = Math.max(
- 0,
- inputWidth - stringWidth(ghostLine),
- );
return (
-
- {ghostLine}
- {' '.repeat(padding)}
-
+
+
+ {renderedLine}
+ {showCursorBeforeGhost &&
+ (showCursor ? chalk.inverse(' ') : ' ')}
+ {currentLineGhost && (
+
+ {currentLineGhost}
+
+ )}
+
+
);
- }),
- )
- )}
+ })
+ .concat(
+ additionalLines.map((ghostLine, index) => {
+ const padding = Math.max(
+ 0,
+ inputWidth - stringWidth(ghostLine),
+ );
+ return (
+
+ {ghostLine}
+ {' '.repeat(padding)}
+
+ );
+ }),
+ )
+ )}
+
-
+
+ {useLineFallback ? (
+
+ ) : null}
{suggestionsPosition === 'below' && suggestionsNode}
>
);
diff --git a/packages/cli/src/ui/components/MainContent.tsx b/packages/cli/src/ui/components/MainContent.tsx
index 7f3982eec0..5239ec040a 100644
--- a/packages/cli/src/ui/components/MainContent.tsx
+++ b/packages/cli/src/ui/components/MainContent.tsx
@@ -129,6 +129,7 @@ export const MainContent = () => {
return (
100}
diff --git a/packages/cli/src/ui/components/SettingsDialog.test.tsx b/packages/cli/src/ui/components/SettingsDialog.test.tsx
index c58910628f..9bc8f05298 100644
--- a/packages/cli/src/ui/components/SettingsDialog.test.tsx
+++ b/packages/cli/src/ui/components/SettingsDialog.test.tsx
@@ -43,7 +43,7 @@ const mockSetVimMode = vi.fn();
vi.mock('../contexts/UIStateContext.js', () => ({
useUIState: () => ({
- mainAreaWidth: 100, // Fixed width for consistent snapshots
+ terminalWidth: 100, // Fixed width for consistent snapshots
}),
}));
diff --git a/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap
index fa02687659..c112a83117 100644
--- a/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/AlternateBufferQuittingDisplay.test.tsx.snap
@@ -39,14 +39,14 @@ Tips for getting started:
2. Be specific for the best results.
3. Create GEMINI.md files to customize your interactions with Gemini.
4. /help for more information.
-╭─────────────────────────────────────────────────────────────────────────────╮
-│ ✓ tool1 Description for tool 1 │
-│ │
-╰─────────────────────────────────────────────────────────────────────────────╯
-╭─────────────────────────────────────────────────────────────────────────────╮
-│ ✓ tool2 Description for tool 2 │
-│ │
-╰─────────────────────────────────────────────────────────────────────────────╯"
+╭──────────────────────────────────────────────────────────────────────────────╮
+│ ✓ tool1 Description for tool 1 │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+╭──────────────────────────────────────────────────────────────────────────────╮
+│ ✓ tool2 Description for tool 2 │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`AlternateBufferQuittingDisplay > renders with empty history and no pending items > empty 1`] = `
@@ -83,14 +83,14 @@ Tips for getting started:
2. Be specific for the best results.
3. Create GEMINI.md files to customize your interactions with Gemini.
4. /help for more information.
-╭─────────────────────────────────────────────────────────────────────────────╮
-│ ✓ tool1 Description for tool 1 │
-│ │
-╰─────────────────────────────────────────────────────────────────────────────╯
-╭─────────────────────────────────────────────────────────────────────────────╮
-│ ✓ tool2 Description for tool 2 │
-│ │
-╰─────────────────────────────────────────────────────────────────────────────╯"
+╭──────────────────────────────────────────────────────────────────────────────╮
+│ ✓ tool1 Description for tool 1 │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯
+╭──────────────────────────────────────────────────────────────────────────────╮
+│ ✓ tool2 Description for tool 2 │
+│ │
+╰──────────────────────────────────────────────────────────────────────────────╯"
`;
exports[`AlternateBufferQuittingDisplay > renders with pending items but no history > with_pending_no_history 1`] = `
@@ -127,8 +127,8 @@ Tips for getting started:
2. Be specific for the best results.
3. Create GEMINI.md files to customize your interactions with Gemini.
4. /help for more information.
-
-> Hello Gemini
-
+▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > Hello Gemini
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
✦ Hello User!"
`;
diff --git a/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap
index 6da8b523f2..bb28344103 100644
--- a/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/AppHeader.test.tsx.snap
@@ -65,9 +65,9 @@ exports[` > should render the banner when previewFeatures is disabl
███░ ░░█████████
░░░ ░░░░░░░░░
-╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ This is the default banner │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
+│ This is the default banner │
+╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -86,9 +86,9 @@ exports[` > should render the banner with default text 1`] = `
███░ ░░█████████
░░░ ░░░░░░░░░
-╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ This is the default banner │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
+│ This is the default banner │
+╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
@@ -107,9 +107,9 @@ exports[` > should render the banner with warning text 1`] = `
███░ ░░█████████
░░░ ░░░░░░░░░
-╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ There are capacity issues │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
+│ There are capacity issues │
+╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Tips for getting started:
1. Ask questions, edit files, or run commands.
2. Be specific for the best results.
diff --git a/packages/cli/src/ui/components/__snapshots__/Footer.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/Footer.test.tsx.snap
index 495446eb0a..4c870387ae 100644
--- a/packages/cli/src/ui/components/__snapshots__/Footer.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/Footer.test.tsx.snap
@@ -1,11 +1,11 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-exports[` > footer configuration filtering (golden snapshots) > renders complete footer in narrow terminal (baseline narrow) > complete-footer-narrow 1`] = `" ...s/to/make/it/long no sandbox gemini-pro /model (100%)"`;
+exports[` > footer configuration filtering (golden snapshots) > renders complete footer in narrow terminal (baseline narrow) > complete-footer-narrow 1`] = `" ...s/to/make/it/long no sandbox gemini-pro /model (100%)"`;
-exports[` > footer configuration filtering (golden snapshots) > renders complete footer with all sections visible (baseline) > complete-footer-wide 1`] = `" ...irectories/to/make/it/long no sandbox (see /docs) gemini-pro /model (100% context left)"`;
+exports[` > footer configuration filtering (golden snapshots) > renders complete footer with all sections visible (baseline) > complete-footer-wide 1`] = `" ...directories/to/make/it/long no sandbox (see /docs) gemini-pro /model (100% context left)"`;
-exports[` > footer configuration filtering (golden snapshots) > renders footer with CWD and model info hidden to test alignment (only sandbox visible) > footer-only-sandbox 1`] = `" no sandbox (see /docs)"`;
+exports[` > footer configuration filtering (golden snapshots) > renders footer with CWD and model info hidden to test alignment (only sandbox visible) > footer-only-sandbox 1`] = `" no sandbox (see /docs)"`;
exports[` > footer configuration filtering (golden snapshots) > renders footer with all optional sections hidden (minimal footer) > footer-minimal 1`] = `""`;
-exports[` > footer configuration filtering (golden snapshots) > renders footer with only model info hidden (partial filtering) > footer-no-model 1`] = `" ...irectories/to/make/it/long no sandbox (see /docs)"`;
+exports[` > footer configuration filtering (golden snapshots) > renders footer with only model info hidden (partial filtering) > footer-no-model 1`] = `" ...directories/to/make/it/long no sandbox (see /docs)"`;
diff --git a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
index 9766c88b7d..e7a5ba5156 100644
--- a/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
+++ b/packages/cli/src/ui/components/__snapshots__/InputPrompt.test.tsx.snap
@@ -1,69 +1,69 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and collapses long suggestion via Right/Left arrows > command-search-render-collapsed-match 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ (r:) Type your message or @path/to/file │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ (r:) Type your message or @path/to/file
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll →
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
..."
`;
exports[`InputPrompt > command search (Ctrl+R when not in shell) > expands and collapses long suggestion via Right/Left arrows > command-search-render-expanded-match 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ (r:) Type your message or @path/to/file │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ (r:) Type your message or @path/to/file
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll ←
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
llllllllllllllllllllllllllllllllllllllllllllllllll"
`;
exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match window and expanded view (snapshots) > command-search-render-collapsed-match 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ (r:) commit │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ (r:) commit
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
git commit -m "feat: add search" in src/app"
`;
exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match window and expanded view (snapshots) > command-search-render-expanded-match 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ (r:) commit │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ (r:) commit
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
git commit -m "feat: add search" in src/app"
`;
exports[`InputPrompt > image path transformation snapshots > should snapshot collapsed image path 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ > [Image ...reenshot2x.png] │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > [Image ...reenshot2x.png]
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
exports[`InputPrompt > image path transformation snapshots > should snapshot expanded image path when cursor is on it 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ > @/path/to/screenshots/screenshot2x.png │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > @/path/to/screenshots/screenshot2x.png
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
exports[`InputPrompt > snapshots > should not show inverted cursor when shell is focused 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ > Type your message or @path/to/file │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > Type your message or @path/to/file
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
exports[`InputPrompt > snapshots > should render correctly in shell mode 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ ! Type your message or @path/to/file │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ ! Type your message or @path/to/file
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
exports[`InputPrompt > snapshots > should render correctly in yolo mode 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ * Type your message or @path/to/file │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ * Type your message or @path/to/file
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
exports[`InputPrompt > snapshots > should render correctly when accepting edits 1`] = `
-"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
-│ > Type your message or @path/to/file │
-╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > Type your message or @path/to/file
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
diff --git a/packages/cli/src/ui/components/messages/UserMessage.test.tsx b/packages/cli/src/ui/components/messages/UserMessage.test.tsx
index 2f130c5469..91b03c969a 100644
--- a/packages/cli/src/ui/components/messages/UserMessage.test.tsx
+++ b/packages/cli/src/ui/components/messages/UserMessage.test.tsx
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { render } from '../../../test-utils/render.js';
+import { renderWithProviders } from '../../../test-utils/render.js';
import { UserMessage } from './UserMessage.js';
import { describe, it, expect, vi } from 'vitest';
@@ -15,8 +15,9 @@ vi.mock('../../utils/commandUtils.js', () => ({
describe('UserMessage', () => {
it('renders normal user message with correct prefix', () => {
- const { lastFrame } = render(
+ const { lastFrame } = renderWithProviders(
,
+ { width: 80 },
);
const output = lastFrame();
@@ -24,7 +25,10 @@ describe('UserMessage', () => {
});
it('renders slash command message', () => {
- const { lastFrame } = render();
+ const { lastFrame } = renderWithProviders(
+ ,
+ { width: 80 },
+ );
const output = lastFrame();
expect(output).toMatchSnapshot();
@@ -32,7 +36,10 @@ describe('UserMessage', () => {
it('renders multiline user message', () => {
const message = 'Line 1\nLine 2';
- const { lastFrame } = render();
+ const { lastFrame } = renderWithProviders(
+ ,
+ { width: 80 },
+ );
const output = lastFrame();
expect(output).toMatchSnapshot();
diff --git a/packages/cli/src/ui/components/messages/UserMessage.tsx b/packages/cli/src/ui/components/messages/UserMessage.tsx
index 51f94a0601..5f26bf771c 100644
--- a/packages/cli/src/ui/components/messages/UserMessage.tsx
+++ b/packages/cli/src/ui/components/messages/UserMessage.tsx
@@ -9,6 +9,9 @@ import { Text, Box } from 'ink';
import { theme } from '../../semantic-colors.js';
import { SCREEN_READER_USER_PREFIX } from '../../textConstants.js';
import { isSlashCommand as checkIsSlashCommand } from '../../utils/commandUtils.js';
+import { HalfLinePaddedBox } from '../shared/HalfLinePaddedBox.js';
+import { DEFAULT_BACKGROUND_OPACITY } from '../../constants.js';
+import { useConfig } from '../../contexts/ConfigContext.js';
interface UserMessageProps {
text: string;
@@ -19,27 +22,39 @@ export const UserMessage: React.FC = ({ text, width }) => {
const prefix = '> ';
const prefixWidth = prefix.length;
const isSlashCommand = checkIsSlashCommand(text);
+ const config = useConfig();
+ const useBackgroundColor = config.getUseBackgroundColor();
const textColor = isSlashCommand ? theme.text.accent : theme.text.secondary;
return (
-
-
-
- {prefix}
-
+
+
+
+ {prefix}
+
+
+
+
+ {text}
+
+
-
-
- {text}
-
-
-
+
);
};
diff --git a/packages/cli/src/ui/components/messages/UserShellMessage.tsx b/packages/cli/src/ui/components/messages/UserShellMessage.tsx
index 98baa32595..ca86e29b8c 100644
--- a/packages/cli/src/ui/components/messages/UserShellMessage.tsx
+++ b/packages/cli/src/ui/components/messages/UserShellMessage.tsx
@@ -7,19 +7,40 @@
import type React from 'react';
import { Box, Text } from 'ink';
import { theme } from '../../semantic-colors.js';
+import { HalfLinePaddedBox } from '../shared/HalfLinePaddedBox.js';
+import { DEFAULT_BACKGROUND_OPACITY } from '../../constants.js';
+import { useConfig } from '../../contexts/ConfigContext.js';
interface UserShellMessageProps {
text: string;
+ width: number;
}
-export const UserShellMessage: React.FC = ({ text }) => {
+export const UserShellMessage: React.FC = ({
+ text,
+ width,
+}) => {
+ const config = useConfig();
+ const useBackgroundColor = config.getUseBackgroundColor();
+
// Remove leading '!' if present, as App.tsx adds it for the processor.
const commandToDisplay = text.startsWith('!') ? text.substring(1) : text;
return (
-
- $
- {commandToDisplay}
-
+
+
+ $
+ {commandToDisplay}
+
+
);
};
diff --git a/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap b/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap
index 1d1e950bb3..b298c864c1 100644
--- a/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap
+++ b/packages/cli/src/ui/components/messages/__snapshots__/UserMessage.test.tsx.snap
@@ -1,20 +1,20 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`UserMessage > renders multiline user message 1`] = `
-"
-> Line 1
- Line 2
-"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > Line 1
+ Line 2
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
exports[`UserMessage > renders normal user message with correct prefix 1`] = `
-"
-> Hello Gemini
-"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > Hello Gemini
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
exports[`UserMessage > renders slash command message 1`] = `
-"
-> /help
-"
+"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
+ > /help
+▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
`;
diff --git a/packages/cli/src/ui/components/shared/HalfLinePaddedBox.tsx b/packages/cli/src/ui/components/shared/HalfLinePaddedBox.tsx
new file mode 100644
index 0000000000..8c4a089bd0
--- /dev/null
+++ b/packages/cli/src/ui/components/shared/HalfLinePaddedBox.tsx
@@ -0,0 +1,102 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import type React from 'react';
+import { useMemo } from 'react';
+import { Box, Text } from 'ink';
+import { useUIState } from '../../contexts/UIStateContext.js';
+import {
+ interpolateColor,
+ resolveColor,
+ getSafeLowColorBackground,
+} from '../../themes/color-utils.js';
+import { isLowColorDepth } from '../../utils/terminalUtils.js';
+
+export interface HalfLinePaddedBoxProps {
+ /**
+ * The base color to blend with the terminal background.
+ */
+ backgroundBaseColor: string;
+
+ /**
+ * The opacity (0-1) for blending the backgroundBaseColor onto the terminal background.
+ */
+ backgroundOpacity: number;
+
+ /**
+ * Whether to render the solid background color.
+ */
+ useBackgroundColor?: boolean;
+
+ children: React.ReactNode;
+}
+
+/**
+ * A container component that renders a solid background with half-line padding
+ * at the top and bottom using block characters (▀/▄).
+ */
+export const HalfLinePaddedBox: React.FC = (props) => {
+ if (props.useBackgroundColor === false) {
+ return <>{props.children}>;
+ }
+
+ return ;
+};
+
+const HalfLinePaddedBoxInternal: React.FC = ({
+ backgroundBaseColor,
+ backgroundOpacity,
+ children,
+}) => {
+ const { terminalWidth, terminalBackgroundColor } = useUIState();
+ const terminalBg = terminalBackgroundColor || 'black';
+
+ const isLowColor = isLowColorDepth();
+
+ const backgroundColor = useMemo(() => {
+ // Interpolated background colors often look bad in 256-color terminals
+ if (isLowColor) {
+ return getSafeLowColorBackground(terminalBg);
+ }
+
+ const resolvedBase =
+ resolveColor(backgroundBaseColor) || backgroundBaseColor;
+ const resolvedTerminalBg = resolveColor(terminalBg) || terminalBg;
+
+ return interpolateColor(
+ resolvedTerminalBg,
+ resolvedBase,
+ backgroundOpacity,
+ );
+ }, [backgroundBaseColor, backgroundOpacity, terminalBg, isLowColor]);
+
+ if (!backgroundColor) {
+ return <>{children}>;
+ }
+
+ return (
+
+
+
+ {'▀'.repeat(terminalWidth)}
+
+
+ {children}
+
+
+ {'▄'.repeat(terminalWidth)}
+
+
+
+ );
+};
diff --git a/packages/cli/src/ui/components/shared/ScrollableList.test.tsx b/packages/cli/src/ui/components/shared/ScrollableList.test.tsx
index c27fe24511..b899894cc4 100644
--- a/packages/cli/src/ui/components/shared/ScrollableList.test.tsx
+++ b/packages/cli/src/ui/components/shared/ScrollableList.test.tsx
@@ -374,4 +374,37 @@ describe('ScrollableList Demo Behavior', () => {
});
});
});
+
+ describe('Width Prop', () => {
+ it('should apply the width prop to the container', async () => {
+ const items = [{ id: '1', title: 'Item 1' }];
+ let lastFrame: () => string | undefined;
+
+ await act(async () => {
+ const result = render(
+
+
+
+
+ {item.title}}
+ estimatedItemHeight={() => 1}
+ keyExtractor={(item) => item.id}
+ hasFocus={true}
+ width={50}
+ />
+
+
+
+ ,
+ );
+ lastFrame = result.lastFrame;
+ });
+
+ await waitFor(() => {
+ expect(lastFrame()).toContain('Item 1');
+ });
+ });
+ });
});
diff --git a/packages/cli/src/ui/components/shared/ScrollableList.tsx b/packages/cli/src/ui/components/shared/ScrollableList.tsx
index cfd314fb51..3b38749632 100644
--- a/packages/cli/src/ui/components/shared/ScrollableList.tsx
+++ b/packages/cli/src/ui/components/shared/ScrollableList.tsx
@@ -37,6 +37,7 @@ type VirtualizedListProps = {
interface ScrollableListProps extends VirtualizedListProps {
hasFocus: boolean;
+ width?: string | number;
}
export type ScrollableListRef = VirtualizedListRef;
@@ -45,7 +46,7 @@ function ScrollableList(
props: ScrollableListProps,
ref: React.Ref>,
) {
- const { hasFocus } = props;
+ const { hasFocus, width } = props;
const virtualizedListRef = useRef>(null);
const containerRef = useRef(null);
@@ -236,6 +237,7 @@ function ScrollableList(
flexGrow={1}
flexDirection="column"
overflow="hidden"
+ width={width}
>
{
useFlickerDetector(rootUiRef, terminalHeight);
// If in alternate buffer mode, need to leave room to draw the scrollbar on
// the right side of the terminal.
- const width = isAlternateBuffer
- ? uiState.terminalWidth
- : uiState.mainAreaWidth;
return (
{
ref={uiState.mainControlsRef}
flexShrink={0}
flexGrow={0}
+ width={uiState.terminalWidth}
>
@@ -63,7 +61,7 @@ export const DefaultAppLayout: React.FC = () => {
uiState.customDialog
) : uiState.dialogsVisible ? (
) : (
diff --git a/packages/cli/src/ui/themes/color-utils.ts b/packages/cli/src/ui/themes/color-utils.ts
index b9b438de96..31d04cc8c5 100644
--- a/packages/cli/src/ui/themes/color-utils.ts
+++ b/packages/cli/src/ui/themes/color-utils.ts
@@ -233,6 +233,33 @@ export function resolveColor(colorValue: string): string | undefined {
return undefined;
}
+/**
+ * Returns a "safe" background color to use in low-color terminals if the
+ * terminal background is a standard black or white.
+ * Returns undefined if no safe background color is available for the given
+ * terminal background.
+ */
+export function getSafeLowColorBackground(
+ terminalBg: string,
+): string | undefined {
+ const resolvedTerminalBg = resolveColor(terminalBg) || terminalBg;
+ if (
+ resolvedTerminalBg === 'black' ||
+ resolvedTerminalBg === '#000000' ||
+ resolvedTerminalBg === '#000'
+ ) {
+ return '#1c1c1c';
+ }
+ if (
+ resolvedTerminalBg === 'white' ||
+ resolvedTerminalBg === '#ffffff' ||
+ resolvedTerminalBg === '#fff'
+ ) {
+ return '#eeeeee';
+ }
+ return undefined;
+}
+
export function interpolateColor(
color1: string,
color2: string,
diff --git a/packages/cli/src/ui/utils/__snapshots__/ui-sizing.test.ts.snap b/packages/cli/src/ui/utils/__snapshots__/ui-sizing.test.ts.snap
deleted file mode 100644
index b166d30701..0000000000
--- a/packages/cli/src/ui/utils/__snapshots__/ui-sizing.test.ts.snap
+++ /dev/null
@@ -1,20 +0,0 @@
-// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
-
-exports[`ui-sizing > calculateMainAreaWidth > should match snapshot for interpolation range 1`] = `
-{
- "100": 95,
- "104": 98,
- "108": 101,
- "112": 104,
- "116": 107,
- "120": 110,
- "124": 113,
- "128": 116,
- "132": 119,
- "80": 78,
- "84": 82,
- "88": 85,
- "92": 88,
- "96": 92,
-}
-`;
diff --git a/packages/cli/src/ui/utils/terminalUtils.ts b/packages/cli/src/ui/utils/terminalUtils.ts
new file mode 100644
index 0000000000..b1506c2817
--- /dev/null
+++ b/packages/cli/src/ui/utils/terminalUtils.ts
@@ -0,0 +1,22 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import process from 'node:process';
+
+/**
+ * Returns the color depth of the current terminal.
+ * Returns 24 (TrueColor) if unknown or not a TTY.
+ */
+export function getColorDepth(): number {
+ return process.stdout.getColorDepth ? process.stdout.getColorDepth() : 24;
+}
+
+/**
+ * Returns true if the terminal has low color depth (less than 24-bit).
+ */
+export function isLowColorDepth(): boolean {
+ return getColorDepth() < 24;
+}
diff --git a/packages/cli/src/ui/utils/ui-sizing.test.ts b/packages/cli/src/ui/utils/ui-sizing.test.ts
index 7611abbaa3..dc3b21e862 100644
--- a/packages/cli/src/ui/utils/ui-sizing.test.ts
+++ b/packages/cli/src/ui/utils/ui-sizing.test.ts
@@ -29,43 +29,19 @@ describe('ui-sizing', () => {
describe('calculateMainAreaWidth', () => {
it.each([
- // width, useFullWidth, alternateBuffer, expected
- [80, true, false, 80],
- [100, true, false, 100],
- [80, true, true, 79], // -1 for alternate buffer
- [100, true, true, 99],
-
- // Default behavior (useFullWidth true)
- [100, true, false, 100],
-
- // useFullWidth: false (Smart sizing)
- [80, false, false, 78], // 98% of 80
- [132, false, false, 119], // 90% of 132
- [200, false, false, 180], // 90% of 200 (>= 132)
-
- // Interpolation check
- [106, false, false, 100], // Approx middle
+ // expected, width, altBuffer
+ [80, 80, false],
+ [100, 100, false],
+ [79, 80, true],
+ [99, 100, true],
])(
- 'should return %i when width=%i, useFullWidth=%s, altBuffer=%s',
- (width, useFullWidth, altBuffer, expected) => {
+ 'should return %i when width=%i and altBuffer=%s',
+ (expected, width, altBuffer) => {
mocks.isAlternateBufferEnabled.mockReturnValue(altBuffer);
- const settings = createSettings(useFullWidth);
+ const settings = createSettings();
expect(calculateMainAreaWidth(width, settings)).toBe(expected);
},
);
-
- it('should match snapshot for interpolation range', () => {
- mocks.isAlternateBufferEnabled.mockReturnValue(false);
- const settings = createSettings(false);
-
- const results: Record = {};
- // Test range from 80 to 132
- for (let w = 80; w <= 132; w += 4) {
- results[w] = calculateMainAreaWidth(w, settings);
- }
-
- expect(results).toMatchSnapshot();
- });
});
});
diff --git a/packages/cli/src/ui/utils/ui-sizing.ts b/packages/cli/src/ui/utils/ui-sizing.ts
index bb9ef1b6f6..d8b7f8e73f 100644
--- a/packages/cli/src/ui/utils/ui-sizing.ts
+++ b/packages/cli/src/ui/utils/ui-sizing.ts
@@ -4,34 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { lerp } from '../../utils/math.js';
import { type LoadedSettings } from '../../config/settings.js';
import { isAlternateBufferEnabled } from '../hooks/useAlternateBuffer.js';
-const getMainAreaWidthInternal = (terminalWidth: number): number => {
- if (terminalWidth <= 80) {
- return Math.round(0.98 * terminalWidth);
- }
- if (terminalWidth >= 132) {
- return Math.round(0.9 * terminalWidth);
- }
-
- // Linearly interpolate between 80 columns (98%) and 132 columns (90%).
- const t = (terminalWidth - 80) / (132 - 80);
- const percentage = lerp(98, 90, t);
-
- return Math.round(percentage * terminalWidth * 0.01);
-};
-
export const calculateMainAreaWidth = (
terminalWidth: number,
settings: LoadedSettings,
): number => {
- if (settings.merged.ui.useFullWidth) {
- if (isAlternateBufferEnabled(settings)) {
- return terminalWidth - 1;
- }
- return terminalWidth;
+ if (isAlternateBufferEnabled(settings)) {
+ return terminalWidth - 1;
}
- return getMainAreaWidthInternal(terminalWidth);
+ return terminalWidth;
};
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index e65abff562..6da474cbcf 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -356,6 +356,7 @@ export interface ConfigParameters {
compressionThreshold?: number;
interactive?: boolean;
trustedFolder?: boolean;
+ useBackgroundColor?: boolean;
useRipgrep?: boolean;
enableInteractiveShell?: boolean;
skipNextSpeakerCheck?: boolean;
@@ -500,6 +501,7 @@ export class Config {
private readonly useRipgrep: boolean;
private readonly enableInteractiveShell: boolean;
private readonly skipNextSpeakerCheck: boolean;
+ private readonly useBackgroundColor: boolean;
private shellExecutionConfig: ShellExecutionConfig;
private readonly extensionManagement: boolean = true;
private readonly enablePromptCompletion: boolean = false;
@@ -666,6 +668,7 @@ export class Config {
this.ptyInfo = params.ptyInfo ?? 'child_process';
this.trustedFolder = params.trustedFolder;
this.useRipgrep = params.useRipgrep ?? true;
+ this.useBackgroundColor = params.useBackgroundColor ?? true;
this.enableInteractiveShell = params.enableInteractiveShell ?? false;
this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? true;
this.shellExecutionConfig = {
@@ -1823,6 +1826,10 @@ export class Config {
return this.useRipgrep;
}
+ getUseBackgroundColor(): boolean {
+ return this.useBackgroundColor;
+ }
+
getEnableInteractiveShell(): boolean {
return this.enableInteractiveShell;
}
diff --git a/schemas/settings.schema.json b/schemas/settings.schema.json
index e0dedc461b..bd0b406eaf 100644
--- a/schemas/settings.schema.json
+++ b/schemas/settings.schema.json
@@ -302,13 +302,6 @@
"default": false,
"type": "boolean"
},
- "useFullWidth": {
- "title": "Use Full Width",
- "description": "Use the entire width of the terminal for output.",
- "markdownDescription": "Use the entire width of the terminal for output.\n\n- Category: `UI`\n- Requires restart: `no`\n- Default: `true`",
- "default": true,
- "type": "boolean"
- },
"useAlternateBuffer": {
"title": "Use Alternate Screen Buffer",
"description": "Use an alternate screen buffer for the UI, preserving shell history.",
@@ -316,6 +309,13 @@
"default": false,
"type": "boolean"
},
+ "useBackgroundColor": {
+ "title": "Use Background Color",
+ "description": "Whether to use background colors in the UI.",
+ "markdownDescription": "Whether to use background colors in the UI.\n\n- Category: `UI`\n- Requires restart: `no`\n- Default: `true`",
+ "default": true,
+ "type": "boolean"
+ },
"incrementalRendering": {
"title": "Incremental Rendering",
"description": "Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled.",