mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
feat(ui): add solid background color option for input prompt (#16563)
Co-authored-by: Alexander Farber <farber72@outlook.de>
This commit is contained in:
@@ -57,8 +57,8 @@ they appear in the UI.
|
|||||||
| Show Line Numbers | `ui.showLineNumbers` | Show line numbers in the chat. | `true` |
|
| 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 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` |
|
| 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 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` |
|
| 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` |
|
| 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` |
|
| Screen Reader Mode | `ui.accessibility.screenReader` | Render output in plain-text to be more screen reader accessible | `false` |
|
||||||
|
|||||||
@@ -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.
|
- **Description:** Show the model name in the chat for each model turn.
|
||||||
- **Default:** `false`
|
- **Default:** `false`
|
||||||
|
|
||||||
- **`ui.useFullWidth`** (boolean):
|
|
||||||
- **Description:** Use the entire width of the terminal for output.
|
|
||||||
- **Default:** `true`
|
|
||||||
|
|
||||||
- **`ui.useAlternateBuffer`** (boolean):
|
- **`ui.useAlternateBuffer`** (boolean):
|
||||||
- **Description:** Use an alternate screen buffer for the UI, preserving shell
|
- **Description:** Use an alternate screen buffer for the UI, preserving shell
|
||||||
history.
|
history.
|
||||||
- **Default:** `false`
|
- **Default:** `false`
|
||||||
- **Requires restart:** Yes
|
- **Requires restart:** Yes
|
||||||
|
|
||||||
|
- **`ui.useBackgroundColor`** (boolean):
|
||||||
|
- **Description:** Whether to use background colors in the UI.
|
||||||
|
- **Default:** `true`
|
||||||
|
|
||||||
- **`ui.incrementalRendering`** (boolean):
|
- **`ui.incrementalRendering`** (boolean):
|
||||||
- **Description:** Enable incremental rendering for the UI. This option will
|
- **Description:** Enable incremental rendering for the UI. This option will
|
||||||
reduce flickering but may cause rendering artifacts. Only supported when
|
reduce flickering but may cause rendering artifacts. Only supported when
|
||||||
|
|||||||
Generated
+29
-6
@@ -11,7 +11,7 @@
|
|||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ink": "npm:@jrichman/ink@6.4.7",
|
"ink": "npm:@jrichman/ink@6.4.8",
|
||||||
"latest-version": "^9.0.0",
|
"latest-version": "^9.0.0",
|
||||||
"simple-git": "^3.28.0"
|
"simple-git": "^3.28.0"
|
||||||
},
|
},
|
||||||
@@ -2251,6 +2251,7 @@
|
|||||||
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
|
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-token": "^6.0.0",
|
"@octokit/auth-token": "^6.0.0",
|
||||||
"@octokit/graphql": "^9.0.2",
|
"@octokit/graphql": "^9.0.2",
|
||||||
@@ -2431,6 +2432,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
}
|
}
|
||||||
@@ -2464,6 +2466,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.0.1.tgz",
|
||||||
"integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==",
|
"integrity": "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||||
},
|
},
|
||||||
@@ -2832,6 +2835,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.0.1.tgz",
|
||||||
"integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==",
|
"integrity": "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/core": "2.0.1",
|
"@opentelemetry/core": "2.0.1",
|
||||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||||
@@ -2865,6 +2869,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.0.1.tgz",
|
||||||
"integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==",
|
"integrity": "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/core": "2.0.1",
|
"@opentelemetry/core": "2.0.1",
|
||||||
"@opentelemetry/resources": "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",
|
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.0.1.tgz",
|
||||||
"integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==",
|
"integrity": "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opentelemetry/core": "2.0.1",
|
"@opentelemetry/core": "2.0.1",
|
||||||
"@opentelemetry/resources": "2.0.1",
|
"@opentelemetry/resources": "2.0.1",
|
||||||
@@ -4122,6 +4128,7 @@
|
|||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -4399,6 +4406,7 @@
|
|||||||
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
|
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.35.0",
|
"@typescript-eslint/scope-manager": "8.35.0",
|
||||||
"@typescript-eslint/types": "8.35.0",
|
"@typescript-eslint/types": "8.35.0",
|
||||||
@@ -5391,6 +5399,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -8400,6 +8409,7 @@
|
|||||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.2.0",
|
"@eslint-community/eslint-utils": "^4.2.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -8940,6 +8950,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "^2.0.0",
|
"accepts": "^2.0.0",
|
||||||
"body-parser": "^2.2.1",
|
"body-parser": "^2.2.1",
|
||||||
@@ -10537,10 +10548,11 @@
|
|||||||
},
|
},
|
||||||
"node_modules/ink": {
|
"node_modules/ink": {
|
||||||
"name": "@jrichman/ink",
|
"name": "@jrichman/ink",
|
||||||
"version": "6.4.7",
|
"version": "6.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.8.tgz",
|
||||||
"integrity": "sha512-QHyxhNF5VonF5cRmdAJD/UPucB9nRx3FozWMjQrDGfBxfAL9lpyu72/MlFPgloS1TMTGsOt7YN6dTPPA6mh0Aw==",
|
"integrity": "sha512-v0thcXIKl9hqF/1w4HqA6MKxIcMoWSP3YtEZIAA+eeJngXpN5lGnMkb6rllB7FnOdwyEyYaFTcu1ZVr4/JZpWQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alcalzone/ansi-tokenize": "^0.2.1",
|
"@alcalzone/ansi-tokenize": "^0.2.1",
|
||||||
"ansi-escapes": "^7.0.0",
|
"ansi-escapes": "^7.0.0",
|
||||||
@@ -14299,6 +14311,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -14309,6 +14322,7 @@
|
|||||||
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"shell-quote": "^1.6.1",
|
"shell-quote": "^1.6.1",
|
||||||
"ws": "^7"
|
"ws": "^7"
|
||||||
@@ -16545,6 +16559,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -16768,7 +16783,8 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "0BSD"
|
"license": "0BSD",
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.20.3",
|
"version": "4.20.3",
|
||||||
@@ -16776,6 +16792,7 @@
|
|||||||
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "~0.25.0",
|
"esbuild": "~0.25.0",
|
||||||
"get-tsconfig": "^4.7.5"
|
"get-tsconfig": "^4.7.5"
|
||||||
@@ -16948,6 +16965,7 @@
|
|||||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -17155,6 +17173,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -17268,6 +17287,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -17280,6 +17300,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
||||||
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/chai": "^5.2.2",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/expect": "3.2.4",
|
"@vitest/expect": "3.2.4",
|
||||||
@@ -17984,6 +18005,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
@@ -18075,7 +18097,7 @@
|
|||||||
"fzf": "^0.5.2",
|
"fzf": "^0.5.2",
|
||||||
"glob": "^12.0.0",
|
"glob": "^12.0.0",
|
||||||
"highlight.js": "^11.11.1",
|
"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-gradient": "^3.0.0",
|
||||||
"ink-spinner": "^5.0.0",
|
"ink-spinner": "^5.0.0",
|
||||||
"latest-version": "^9.0.0",
|
"latest-version": "^9.0.0",
|
||||||
@@ -18278,6 +18300,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
|
|||||||
+2
-2
@@ -64,7 +64,7 @@
|
|||||||
"pre-commit": "node scripts/pre-commit.js"
|
"pre-commit": "node scripts/pre-commit.js"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"ink": "npm:@jrichman/ink@6.4.7",
|
"ink": "npm:@jrichman/ink@6.4.8",
|
||||||
"wrap-ansi": "9.0.2",
|
"wrap-ansi": "9.0.2",
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"wrap-ansi": "7.0.0"
|
"wrap-ansi": "7.0.0"
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ink": "npm:@jrichman/ink@6.4.7",
|
"ink": "npm:@jrichman/ink@6.4.8",
|
||||||
"latest-version": "^9.0.0",
|
"latest-version": "^9.0.0",
|
||||||
"simple-git": "^3.28.0"
|
"simple-git": "^3.28.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
"fzf": "^0.5.2",
|
"fzf": "^0.5.2",
|
||||||
"glob": "^12.0.0",
|
"glob": "^12.0.0",
|
||||||
"highlight.js": "^11.11.1",
|
"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-gradient": "^3.0.0",
|
||||||
"ink-spinner": "^5.0.0",
|
"ink-spinner": "^5.0.0",
|
||||||
"latest-version": "^9.0.0",
|
"latest-version": "^9.0.0",
|
||||||
|
|||||||
@@ -766,6 +766,7 @@ export async function loadCliConfig(
|
|||||||
folderTrust,
|
folderTrust,
|
||||||
interactive,
|
interactive,
|
||||||
trustedFolder,
|
trustedFolder,
|
||||||
|
useBackgroundColor: settings.ui?.useBackgroundColor,
|
||||||
useRipgrep: settings.tools?.useRipgrep,
|
useRipgrep: settings.tools?.useRipgrep,
|
||||||
enableInteractiveShell: settings.tools?.shell?.enableInteractiveShell,
|
enableInteractiveShell: settings.tools?.shell?.enableInteractiveShell,
|
||||||
shellToolInactivityTimeout: settings.tools?.shell?.inactivityTimeout,
|
shellToolInactivityTimeout: settings.tools?.shell?.inactivityTimeout,
|
||||||
|
|||||||
@@ -526,15 +526,6 @@ const SETTINGS_SCHEMA = {
|
|||||||
description: 'Show the model name in the chat for each model turn.',
|
description: 'Show the model name in the chat for each model turn.',
|
||||||
showInDialog: true,
|
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: {
|
useAlternateBuffer: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
label: 'Use Alternate Screen Buffer',
|
label: 'Use Alternate Screen Buffer',
|
||||||
@@ -545,6 +536,15 @@ const SETTINGS_SCHEMA = {
|
|||||||
'Use an alternate screen buffer for the UI, preserving shell history.',
|
'Use an alternate screen buffer for the UI, preserving shell history.',
|
||||||
showInDialog: true,
|
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: {
|
incrementalRendering: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
label: 'Incremental Rendering',
|
label: 'Incremental Rendering',
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { SettingsContext } from '../ui/contexts/SettingsContext.js';
|
|||||||
import { ShellFocusContext } from '../ui/contexts/ShellFocusContext.js';
|
import { ShellFocusContext } from '../ui/contexts/ShellFocusContext.js';
|
||||||
import { UIStateContext, type UIState } from '../ui/contexts/UIStateContext.js';
|
import { UIStateContext, type UIState } from '../ui/contexts/UIStateContext.js';
|
||||||
import { ConfigContext } from '../ui/contexts/ConfigContext.js';
|
import { ConfigContext } from '../ui/contexts/ConfigContext.js';
|
||||||
import { calculateMainAreaWidth } from '../ui/utils/ui-sizing.js';
|
|
||||||
import { VimModeProvider } from '../ui/contexts/VimModeContext.js';
|
import { VimModeProvider } from '../ui/contexts/VimModeContext.js';
|
||||||
import { MouseProvider } from '../ui/contexts/MouseContext.js';
|
import { MouseProvider } from '../ui/contexts/MouseContext.js';
|
||||||
import { ScrollProvider } from '../ui/contexts/ScrollProvider.js';
|
import { ScrollProvider } from '../ui/contexts/ScrollProvider.js';
|
||||||
@@ -38,6 +37,11 @@ vi.mock('../utils/persistentState.js', () => ({
|
|||||||
persistentState: persistentStateMock,
|
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
|
// Wrapper around ink-testing-library's render that ensures act() is called
|
||||||
export const render = (
|
export const render = (
|
||||||
tree: React.ReactElement,
|
tree: React.ReactElement,
|
||||||
@@ -147,7 +151,6 @@ export const createMockSettings = (
|
|||||||
const baseMockUiState = {
|
const baseMockUiState = {
|
||||||
renderMarkdown: true,
|
renderMarkdown: true,
|
||||||
streamingState: StreamingState.Idle,
|
streamingState: StreamingState.Idle,
|
||||||
mainAreaWidth: 100,
|
|
||||||
terminalWidth: 120,
|
terminalWidth: 120,
|
||||||
terminalHeight: 40,
|
terminalHeight: 40,
|
||||||
currentModel: 'gemini-pro',
|
currentModel: 'gemini-pro',
|
||||||
@@ -269,7 +272,7 @@ export const renderWithProviders = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainAreaWidth = calculateMainAreaWidth(terminalWidth, finalSettings);
|
const mainAreaWidth = terminalWidth;
|
||||||
|
|
||||||
const finalUiState = {
|
const finalUiState = {
|
||||||
...baseState,
|
...baseState,
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ vi.mock('../components/shared/text-buffer.js', () => ({
|
|||||||
|
|
||||||
vi.mock('../contexts/UIStateContext.js', () => ({
|
vi.mock('../contexts/UIStateContext.js', () => ({
|
||||||
useUIState: vi.fn(() => ({
|
useUIState: vi.fn(() => ({
|
||||||
mainAreaWidth: 80,
|
terminalWidth: 80,
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ export function ApiAuthDialog({
|
|||||||
error,
|
error,
|
||||||
defaultValue = '',
|
defaultValue = '',
|
||||||
}: ApiAuthDialogProps): React.JSX.Element {
|
}: ApiAuthDialogProps): React.JSX.Element {
|
||||||
const { mainAreaWidth } = useUIState();
|
const { terminalWidth } = useUIState();
|
||||||
const viewportWidth = mainAreaWidth - 8;
|
const viewportWidth = terminalWidth - 8;
|
||||||
|
|
||||||
const pendingPromise = useRef<{ cancel: () => void } | null>(null);
|
const pendingPromise = useRef<{ cancel: () => void } | null>(null);
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ interface AppHeaderProps {
|
|||||||
export const AppHeader = ({ version }: AppHeaderProps) => {
|
export const AppHeader = ({ version }: AppHeaderProps) => {
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
const config = useConfig();
|
const config = useConfig();
|
||||||
const { nightly, mainAreaWidth, bannerData, bannerVisible } = useUIState();
|
const { nightly, terminalWidth, bannerData, bannerVisible } = useUIState();
|
||||||
|
|
||||||
const { bannerText } = useBanner(bannerData, config);
|
const { bannerText } = useBanner(bannerData, config);
|
||||||
const { showTips } = useTips();
|
const { showTips } = useTips();
|
||||||
@@ -33,7 +33,7 @@ export const AppHeader = ({ version }: AppHeaderProps) => {
|
|||||||
<Header version={version} nightly={nightly} />
|
<Header version={version} nightly={nightly} />
|
||||||
{bannerVisible && bannerText && (
|
{bannerVisible && bannerText && (
|
||||||
<Banner
|
<Banner
|
||||||
width={mainAreaWidth}
|
width={terminalWidth}
|
||||||
bannerText={bannerText}
|
bannerText={bannerText}
|
||||||
isWarning={bannerData.warningText !== ''}
|
isWarning={bannerData.warningText !== ''}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
width={uiState.mainAreaWidth}
|
width={uiState.terminalWidth}
|
||||||
flexGrow={0}
|
flexGrow={0}
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
>
|
>
|
||||||
@@ -113,7 +113,7 @@ export const Composer = ({ isFocused = true }: { isFocused?: boolean }) => {
|
|||||||
maxHeight={
|
maxHeight={
|
||||||
uiState.constrainHeight ? debugConsoleMaxHeight : undefined
|
uiState.constrainHeight ? debugConsoleMaxHeight : undefined
|
||||||
}
|
}
|
||||||
width={uiState.mainAreaWidth}
|
width={uiState.terminalWidth}
|
||||||
hasFocus={uiState.showErrorDetails}
|
hasFocus={uiState.showErrorDetails}
|
||||||
/>
|
/>
|
||||||
<ShowMoreLines constrainHeight={uiState.constrainHeight} />
|
<ShowMoreLines constrainHeight={uiState.constrainHeight} />
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ describe('DialogManager', () => {
|
|||||||
constrainHeight: false,
|
constrainHeight: false,
|
||||||
terminalHeight: 24,
|
terminalHeight: 24,
|
||||||
staticExtraHeight: 0,
|
staticExtraHeight: 0,
|
||||||
mainAreaWidth: 80,
|
terminalWidth: 80,
|
||||||
confirmUpdateExtensionRequests: [],
|
confirmUpdateExtensionRequests: [],
|
||||||
showIdeRestartPrompt: false,
|
showIdeRestartPrompt: false,
|
||||||
proQuotaRequest: null,
|
proQuotaRequest: null,
|
||||||
|
|||||||
@@ -50,8 +50,12 @@ export const DialogManager = ({
|
|||||||
|
|
||||||
const uiState = useUIState();
|
const uiState = useUIState();
|
||||||
const uiActions = useUIActions();
|
const uiActions = useUIActions();
|
||||||
const { constrainHeight, terminalHeight, staticExtraHeight, mainAreaWidth } =
|
const {
|
||||||
uiState;
|
constrainHeight,
|
||||||
|
terminalHeight,
|
||||||
|
staticExtraHeight,
|
||||||
|
terminalWidth: uiTerminalWidth,
|
||||||
|
} = uiState;
|
||||||
|
|
||||||
if (uiState.adminSettingsChanged) {
|
if (uiState.adminSettingsChanged) {
|
||||||
return <AdminSettingsChangedDialog />;
|
return <AdminSettingsChangedDialog />;
|
||||||
@@ -147,7 +151,7 @@ export const DialogManager = ({
|
|||||||
availableTerminalHeight={
|
availableTerminalHeight={
|
||||||
constrainHeight ? terminalHeight - staticExtraHeight : undefined
|
constrainHeight ? terminalHeight - staticExtraHeight : undefined
|
||||||
}
|
}
|
||||||
terminalWidth={mainAreaWidth}
|
terminalWidth={uiTerminalWidth}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const Footer: React.FC = () => {
|
|||||||
promptTokenCount,
|
promptTokenCount,
|
||||||
nightly,
|
nightly,
|
||||||
isTrustedFolder,
|
isTrustedFolder,
|
||||||
mainAreaWidth,
|
terminalWidth,
|
||||||
} = {
|
} = {
|
||||||
model: uiState.currentModel,
|
model: uiState.currentModel,
|
||||||
targetDir: config.getTargetDir(),
|
targetDir: config.getTargetDir(),
|
||||||
@@ -55,7 +55,7 @@ export const Footer: React.FC = () => {
|
|||||||
promptTokenCount: uiState.sessionStats.lastPromptTokenCount,
|
promptTokenCount: uiState.sessionStats.lastPromptTokenCount,
|
||||||
nightly: uiState.nightly,
|
nightly: uiState.nightly,
|
||||||
isTrustedFolder: uiState.isTrustedFolder,
|
isTrustedFolder: uiState.isTrustedFolder,
|
||||||
mainAreaWidth: uiState.mainAreaWidth,
|
terminalWidth: uiState.terminalWidth,
|
||||||
};
|
};
|
||||||
|
|
||||||
const showMemoryUsage =
|
const showMemoryUsage =
|
||||||
@@ -65,7 +65,7 @@ export const Footer: React.FC = () => {
|
|||||||
const hideModelInfo = settings.merged.ui.footer.hideModelInfo;
|
const hideModelInfo = settings.merged.ui.footer.hideModelInfo;
|
||||||
const hideContextPercentage = settings.merged.ui.footer.hideContextPercentage;
|
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 displayPath = shortenPath(tildeifyPath(targetDir), pathLength);
|
||||||
|
|
||||||
const justifyContent = hideCWD && hideModelInfo ? 'center' : 'space-between';
|
const justifyContent = hideCWD && hideModelInfo ? 'center' : 'space-between';
|
||||||
@@ -76,7 +76,7 @@ export const Footer: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
justifyContent={justifyContent}
|
justifyContent={justifyContent}
|
||||||
width={mainAreaWidth}
|
width={terminalWidth}
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
paddingX={1}
|
paddingX={1}
|
||||||
@@ -134,7 +134,7 @@ export const Footer: React.FC = () => {
|
|||||||
) : (
|
) : (
|
||||||
<Text color={theme.status.error}>
|
<Text color={theme.status.error}>
|
||||||
no sandbox
|
no sandbox
|
||||||
{mainAreaWidth >= 100 && (
|
{terminalWidth >= 100 && (
|
||||||
<Text color={theme.text.secondary}> (see /docs)</Text>
|
<Text color={theme.text.secondary}> (see /docs)</Text>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -155,7 +155,7 @@ export const Footer: React.FC = () => {
|
|||||||
<ContextUsageDisplay
|
<ContextUsageDisplay
|
||||||
promptTokenCount={promptTokenCount}
|
promptTokenCount={promptTokenCount}
|
||||||
model={model}
|
model={model}
|
||||||
terminalWidth={mainAreaWidth}
|
terminalWidth={terminalWidth}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
|
|||||||
<UserMessage text={itemForDisplay.text} width={terminalWidth} />
|
<UserMessage text={itemForDisplay.text} width={terminalWidth} />
|
||||||
)}
|
)}
|
||||||
{itemForDisplay.type === 'user_shell' && (
|
{itemForDisplay.type === 'user_shell' && (
|
||||||
<UserShellMessage text={itemForDisplay.text} />
|
<UserShellMessage text={itemForDisplay.text} width={terminalWidth} />
|
||||||
)}
|
)}
|
||||||
{itemForDisplay.type === 'gemini' && (
|
{itemForDisplay.type === 'gemini' && (
|
||||||
<GeminiMessage
|
<GeminiMessage
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import stripAnsi from 'strip-ansi';
|
|||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import { StreamingState } from '../types.js';
|
import { StreamingState } from '../types.js';
|
||||||
import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js';
|
import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js';
|
||||||
|
import type { UIState } from '../contexts/UIStateContext.js';
|
||||||
|
import { isLowColorDepth } from '../utils/terminalUtils.js';
|
||||||
|
|
||||||
vi.mock('../hooks/useShellHistory.js');
|
vi.mock('../hooks/useShellHistory.js');
|
||||||
vi.mock('../hooks/useCommandCompletion.js');
|
vi.mock('../hooks/useCommandCompletion.js');
|
||||||
@@ -50,6 +52,9 @@ vi.mock('../hooks/useReverseSearchCompletion.js');
|
|||||||
vi.mock('clipboardy');
|
vi.mock('clipboardy');
|
||||||
vi.mock('../utils/clipboardUtils.js');
|
vi.mock('../utils/clipboardUtils.js');
|
||||||
vi.mock('../hooks/useKittyKeyboardProtocol.js');
|
vi.mock('../hooks/useKittyKeyboardProtocol.js');
|
||||||
|
vi.mock('../utils/terminalUtils.js', () => ({
|
||||||
|
isLowColorDepth: vi.fn(() => false),
|
||||||
|
}));
|
||||||
|
|
||||||
const mockSlashCommands: SlashCommand[] = [
|
const mockSlashCommands: SlashCommand[] = [
|
||||||
{
|
{
|
||||||
@@ -260,6 +265,8 @@ describe('InputPrompt', () => {
|
|||||||
getProjectRoot: () => path.join('test', 'project'),
|
getProjectRoot: () => path.join('test', 'project'),
|
||||||
getTargetDir: () => path.join('test', 'project', 'src'),
|
getTargetDir: () => path.join('test', 'project', 'src'),
|
||||||
getVimMode: () => false,
|
getVimMode: () => false,
|
||||||
|
getUseBackgroundColor: () => true,
|
||||||
|
getTerminalBackground: () => undefined,
|
||||||
getWorkspaceContext: () => ({
|
getWorkspaceContext: () => ({
|
||||||
getDirectories: () => ['/test/project/src'],
|
getDirectories: () => ['/test/project/src'],
|
||||||
}),
|
}),
|
||||||
@@ -1320,6 +1327,168 @@ describe('InputPrompt', () => {
|
|||||||
unmount();
|
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(
|
||||||
|
<InputPrompt {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
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(
|
||||||
|
<InputPrompt {...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.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(
|
||||||
|
<InputPrompt {...props} />,
|
||||||
|
{
|
||||||
|
uiState: {
|
||||||
|
terminalBackgroundColor: '#333333',
|
||||||
|
} as Partial<UIState>,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
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(
|
||||||
|
<InputPrompt {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
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(
|
||||||
|
<InputPrompt {...props} />,
|
||||||
|
|
||||||
|
{
|
||||||
|
uiState: {
|
||||||
|
terminalBackgroundColor: 'blue',
|
||||||
|
} as Partial<UIState>,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
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(
|
||||||
|
<InputPrompt {...props} />,
|
||||||
|
);
|
||||||
|
|
||||||
|
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', () => {
|
describe('cursor-based completion trigger', () => {
|
||||||
it.each([
|
it.each([
|
||||||
{
|
{
|
||||||
@@ -1564,11 +1733,11 @@ describe('InputPrompt', () => {
|
|||||||
mockBuffer.lines = [text];
|
mockBuffer.lines = [text];
|
||||||
mockBuffer.viewportVisualLines = [text];
|
mockBuffer.viewportVisualLines = [text];
|
||||||
mockBuffer.visualCursor = visualCursor as [number, number];
|
mockBuffer.visualCursor = visualCursor as [number, number];
|
||||||
|
props.config.getUseBackgroundColor = () => false;
|
||||||
|
|
||||||
const { stdout, unmount } = renderWithProviders(
|
const { stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const frame = stdout.lastFrame();
|
const frame = stdout.lastFrame();
|
||||||
expect(frame).toContain(expected);
|
expect(frame).toContain(expected);
|
||||||
@@ -1621,11 +1790,11 @@ describe('InputPrompt', () => {
|
|||||||
mockBuffer.visualToLogicalMap = visualToLogicalMap as Array<
|
mockBuffer.visualToLogicalMap = visualToLogicalMap as Array<
|
||||||
[number, number]
|
[number, number]
|
||||||
>;
|
>;
|
||||||
|
props.config.getUseBackgroundColor = () => false;
|
||||||
|
|
||||||
const { stdout, unmount } = renderWithProviders(
|
const { stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const frame = stdout.lastFrame();
|
const frame = stdout.lastFrame();
|
||||||
expect(frame).toContain(expected);
|
expect(frame).toContain(expected);
|
||||||
@@ -1645,11 +1814,11 @@ describe('InputPrompt', () => {
|
|||||||
[1, 0],
|
[1, 0],
|
||||||
[2, 0],
|
[2, 0],
|
||||||
];
|
];
|
||||||
|
props.config.getUseBackgroundColor = () => false;
|
||||||
|
|
||||||
const { stdout, unmount } = renderWithProviders(
|
const { stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const frame = stdout.lastFrame();
|
const frame = stdout.lastFrame();
|
||||||
const lines = frame!.split('\n');
|
const lines = frame!.split('\n');
|
||||||
@@ -1673,15 +1842,15 @@ describe('InputPrompt', () => {
|
|||||||
mockBuffer.visualCursor = [2, 5]; // cursor at the end of "world"
|
mockBuffer.visualCursor = [2, 5]; // cursor at the end of "world"
|
||||||
// Provide a visual-to-logical mapping for each visual line
|
// Provide a visual-to-logical mapping for each visual line
|
||||||
mockBuffer.visualToLogicalMap = [
|
mockBuffer.visualToLogicalMap = [
|
||||||
[0, 0], // 'hello' starts at col 0 of logical line 0
|
[0, 0],
|
||||||
[1, 0], // '' (blank) is logical line 1, col 0
|
[1, 0],
|
||||||
[2, 0], // 'world' is logical line 2, col 0
|
[2, 0],
|
||||||
];
|
];
|
||||||
|
props.config.getUseBackgroundColor = () => false;
|
||||||
|
|
||||||
const { stdout, unmount } = renderWithProviders(
|
const { stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const frame = stdout.lastFrame();
|
const frame = stdout.lastFrame();
|
||||||
// Check that all lines, including the empty one, are rendered.
|
// Check that all lines, including the empty one, are rendered.
|
||||||
@@ -2505,20 +2674,23 @@ describe('InputPrompt', () => {
|
|||||||
stdin.write('\x12');
|
stdin.write('\x12');
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
expect(stdout.lastFrame()).toContain('(r:)');
|
||||||
|
});
|
||||||
expect(stdout.lastFrame()).toMatchSnapshot(
|
expect(stdout.lastFrame()).toMatchSnapshot(
|
||||||
'command-search-render-collapsed-match',
|
'command-search-render-collapsed-match',
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
stdin.write('\u001B[C');
|
stdin.write('\u001B[C');
|
||||||
});
|
});
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
// 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(
|
expect(stdout.lastFrame()).toMatchSnapshot(
|
||||||
'command-search-render-expanded-match',
|
'command-search-render-expanded-match',
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2637,28 +2809,28 @@ describe('InputPrompt', () => {
|
|||||||
name: 'first line, first char',
|
name: 'first line, first char',
|
||||||
relX: 0,
|
relX: 0,
|
||||||
relY: 0,
|
relY: 0,
|
||||||
mouseCol: 5,
|
mouseCol: 4,
|
||||||
mouseRow: 2,
|
mouseRow: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'first line, middle char',
|
name: 'first line, middle char',
|
||||||
relX: 6,
|
relX: 6,
|
||||||
relY: 0,
|
relY: 0,
|
||||||
mouseCol: 11,
|
mouseCol: 10,
|
||||||
mouseRow: 2,
|
mouseRow: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'second line, first char',
|
name: 'second line, first char',
|
||||||
relX: 0,
|
relX: 0,
|
||||||
relY: 1,
|
relY: 1,
|
||||||
mouseCol: 5,
|
mouseCol: 4,
|
||||||
mouseRow: 3,
|
mouseRow: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'second line, end char',
|
name: 'second line, end char',
|
||||||
relX: 5,
|
relX: 5,
|
||||||
relY: 1,
|
relY: 1,
|
||||||
mouseCol: 10,
|
mouseCol: 9,
|
||||||
mouseRow: 3,
|
mouseRow: 3,
|
||||||
},
|
},
|
||||||
])(
|
])(
|
||||||
@@ -2685,7 +2857,7 @@ describe('InputPrompt', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Simulate left mouse press at calculated coordinates.
|
// 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 () => {
|
await act(async () => {
|
||||||
stdin.write(`\x1b[<0;${mouseCol};${mouseRow}M`);
|
stdin.write(`\x1b[<0;${mouseCol};${mouseRow}M`);
|
||||||
});
|
});
|
||||||
@@ -2727,6 +2899,37 @@ describe('InputPrompt', () => {
|
|||||||
|
|
||||||
unmount();
|
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(
|
||||||
|
<InputPrompt {...props} />,
|
||||||
|
{ 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', () => {
|
describe('queued message editing', () => {
|
||||||
@@ -2889,7 +3092,8 @@ describe('InputPrompt', () => {
|
|||||||
const { stdout, unmount } = renderWithProviders(
|
const { stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
|
await waitFor(() => expect(stdout.lastFrame()).toContain('!'));
|
||||||
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2898,7 +3102,8 @@ describe('InputPrompt', () => {
|
|||||||
const { stdout, unmount } = renderWithProviders(
|
const { stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
|
await waitFor(() => expect(stdout.lastFrame()).toContain('>'));
|
||||||
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2907,10 +3112,10 @@ describe('InputPrompt', () => {
|
|||||||
const { stdout, unmount } = renderWithProviders(
|
const { stdout, unmount } = renderWithProviders(
|
||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
await waitFor(() => expect(stdout.lastFrame()).toMatchSnapshot());
|
await waitFor(() => expect(stdout.lastFrame()).toContain('*'));
|
||||||
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not show inverted cursor when shell is focused', async () => {
|
it('should not show inverted cursor when shell is focused', async () => {
|
||||||
props.isEmbeddedShellFocused = true;
|
props.isEmbeddedShellFocused = true;
|
||||||
props.focus = false;
|
props.focus = false;
|
||||||
@@ -2919,8 +3124,8 @@ describe('InputPrompt', () => {
|
|||||||
);
|
);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(stdout.lastFrame()).not.toContain(`{chalk.inverse(' ')}`);
|
expect(stdout.lastFrame()).not.toContain(`{chalk.inverse(' ')}`);
|
||||||
expect(stdout.lastFrame()).toMatchSnapshot();
|
|
||||||
});
|
});
|
||||||
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -3022,8 +3227,9 @@ describe('InputPrompt', () => {
|
|||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(stdout.lastFrame()).toMatchSnapshot();
|
expect(stdout.lastFrame()).toContain('[Image');
|
||||||
});
|
});
|
||||||
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -3040,8 +3246,9 @@ describe('InputPrompt', () => {
|
|||||||
<InputPrompt {...props} />,
|
<InputPrompt {...props} />,
|
||||||
);
|
);
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(stdout.lastFrame()).toMatchSnapshot();
|
expect(stdout.lastFrame()).toContain('@/path/to/screenshots');
|
||||||
});
|
});
|
||||||
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import clipboardy from 'clipboardy';
|
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 { Box, Text, useStdout, type DOMElement } from 'ink';
|
||||||
import { SuggestionsDisplay, MAX_WIDTH } from './SuggestionsDisplay.js';
|
import { SuggestionsDisplay, MAX_WIDTH } from './SuggestionsDisplay.js';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { theme } from '../semantic-colors.js';
|
||||||
import { useInputHistory } from '../hooks/useInputHistory.js';
|
import { useInputHistory } from '../hooks/useInputHistory.js';
|
||||||
|
import { HalfLinePaddedBox } from './shared/HalfLinePaddedBox.js';
|
||||||
import type { TextBuffer } from './shared/text-buffer.js';
|
import type { TextBuffer } from './shared/text-buffer.js';
|
||||||
import {
|
import {
|
||||||
logicalPosToOffset,
|
logicalPosToOffset,
|
||||||
@@ -47,6 +48,9 @@ import {
|
|||||||
} from '../utils/commandUtils.js';
|
} from '../utils/commandUtils.js';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { SCREEN_READER_USER_PREFIX } from '../textConstants.js';
|
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 { useShellFocusState } from '../contexts/ShellFocusContext.js';
|
||||||
import { useUIState } from '../contexts/UIStateContext.js';
|
import { useUIState } from '../contexts/UIStateContext.js';
|
||||||
import { useSettings } from '../contexts/SettingsContext.js';
|
import { useSettings } from '../contexts/SettingsContext.js';
|
||||||
@@ -141,7 +145,8 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||||||
const kittyProtocol = useKittyKeyboardProtocol();
|
const kittyProtocol = useKittyKeyboardProtocol();
|
||||||
const isShellFocused = useShellFocusState();
|
const isShellFocused = useShellFocusState();
|
||||||
const { setEmbeddedShellFocused } = useUIActions();
|
const { setEmbeddedShellFocused } = useUIActions();
|
||||||
const { mainAreaWidth, activePtyId, history } = useUIState();
|
const { terminalWidth, activePtyId, history, terminalBackgroundColor } =
|
||||||
|
useUIState();
|
||||||
const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
|
const [justNavigatedHistory, setJustNavigatedHistory] = useState(false);
|
||||||
const escPressCount = useRef(0);
|
const escPressCount = useRef(0);
|
||||||
const [showEscapePrompt, setShowEscapePrompt] = useState(false);
|
const [showEscapePrompt, setShowEscapePrompt] = useState(false);
|
||||||
@@ -321,6 +326,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||||||
const allMessages = popAllMessages();
|
const allMessages = popAllMessages();
|
||||||
if (allMessages) {
|
if (allMessages) {
|
||||||
buffer.setText(allMessages);
|
buffer.setText(allMessages);
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// No queued messages, proceed with input history
|
// No queued messages, proceed with input history
|
||||||
inputHistory.navigateUp();
|
inputHistory.navigateUp();
|
||||||
@@ -1033,6 +1039,23 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||||||
const activeCompletion = getActiveCompletion();
|
const activeCompletion = getActiveCompletion();
|
||||||
const shouldShowSuggestions = activeCompletion.showSuggestions;
|
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(() => {
|
useEffect(() => {
|
||||||
if (onSuggestionsVisibilityChange) {
|
if (onSuggestionsVisibilityChange) {
|
||||||
onSuggestionsVisibilityChange(shouldShowSuggestions);
|
onSuggestionsVisibilityChange(shouldShowSuggestions);
|
||||||
@@ -1085,21 +1108,47 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||||||
</Box>
|
</Box>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
|
const borderColor =
|
||||||
|
isShellFocused && !isEmbeddedShellFocused
|
||||||
|
? (statusColor ?? theme.border.focused)
|
||||||
|
: theme.border.default;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{suggestionsPosition === 'above' && suggestionsNode}
|
{suggestionsPosition === 'above' && suggestionsNode}
|
||||||
|
{useLineFallback ? (
|
||||||
<Box
|
<Box
|
||||||
borderStyle="round"
|
borderStyle="round"
|
||||||
borderColor={
|
borderTop={true}
|
||||||
isShellFocused && !isEmbeddedShellFocused
|
borderBottom={false}
|
||||||
? (statusColor ?? theme.border.focused)
|
borderLeft={false}
|
||||||
: theme.border.default
|
borderRight={false}
|
||||||
}
|
borderColor={borderColor}
|
||||||
paddingX={1}
|
width={terminalWidth}
|
||||||
width={mainAreaWidth}
|
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
alignItems="flex-start"
|
alignItems="flex-start"
|
||||||
minHeight={3}
|
height={0}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<HalfLinePaddedBox
|
||||||
|
backgroundBaseColor={
|
||||||
|
isShellFocused && !isEmbeddedShellFocused
|
||||||
|
? theme.border.focused
|
||||||
|
: theme.border.default
|
||||||
|
}
|
||||||
|
backgroundOpacity={DEFAULT_BACKGROUND_OPACITY}
|
||||||
|
useBackgroundColor={useBackgroundColor}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
flexGrow={1}
|
||||||
|
flexDirection="row"
|
||||||
|
paddingX={1}
|
||||||
|
borderColor={borderColor}
|
||||||
|
borderStyle={useLineFallback ? 'round' : undefined}
|
||||||
|
borderTop={false}
|
||||||
|
borderBottom={false}
|
||||||
|
borderLeft={!useBackgroundColor}
|
||||||
|
borderRight={!useBackgroundColor}
|
||||||
>
|
>
|
||||||
<Text
|
<Text
|
||||||
color={statusColor ?? theme.text.accent}
|
color={statusColor ?? theme.text.accent}
|
||||||
@@ -1129,14 +1178,16 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||||||
showCursor ? (
|
showCursor ? (
|
||||||
<Text>
|
<Text>
|
||||||
{chalk.inverse(placeholder.slice(0, 1))}
|
{chalk.inverse(placeholder.slice(0, 1))}
|
||||||
<Text color={theme.text.secondary}>{placeholder.slice(1)}</Text>
|
<Text color={theme.text.secondary}>
|
||||||
|
{placeholder.slice(1)}
|
||||||
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<Text color={theme.text.secondary}>{placeholder}</Text>
|
<Text color={theme.text.secondary}>{placeholder}</Text>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
linesToRender
|
linesToRender
|
||||||
.map((lineText, visualIdxInRenderedSet) => {
|
.map((lineText: string, visualIdxInRenderedSet: number) => {
|
||||||
const absoluteVisualIdx =
|
const absoluteVisualIdx =
|
||||||
scrollVisualRow + visualIdxInRenderedSet;
|
scrollVisualRow + visualIdxInRenderedSet;
|
||||||
const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx];
|
const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx];
|
||||||
@@ -1277,6 +1328,21 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
</HalfLinePaddedBox>
|
||||||
|
{useLineFallback ? (
|
||||||
|
<Box
|
||||||
|
borderStyle="round"
|
||||||
|
borderTop={false}
|
||||||
|
borderBottom={true}
|
||||||
|
borderLeft={false}
|
||||||
|
borderRight={false}
|
||||||
|
borderColor={borderColor}
|
||||||
|
width={terminalWidth}
|
||||||
|
flexDirection="row"
|
||||||
|
alignItems="flex-start"
|
||||||
|
height={0}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
{suggestionsPosition === 'below' && suggestionsNode}
|
{suggestionsPosition === 'below' && suggestionsNode}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ export const MainContent = () => {
|
|||||||
return (
|
return (
|
||||||
<ScrollableList
|
<ScrollableList
|
||||||
hasFocus={!uiState.isEditorDialogOpen}
|
hasFocus={!uiState.isEditorDialogOpen}
|
||||||
|
width={uiState.terminalWidth}
|
||||||
data={virtualizedData}
|
data={virtualizedData}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
estimatedItemHeight={() => 100}
|
estimatedItemHeight={() => 100}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const mockSetVimMode = vi.fn();
|
|||||||
|
|
||||||
vi.mock('../contexts/UIStateContext.js', () => ({
|
vi.mock('../contexts/UIStateContext.js', () => ({
|
||||||
useUIState: () => ({
|
useUIState: () => ({
|
||||||
mainAreaWidth: 100, // Fixed width for consistent snapshots
|
terminalWidth: 100, // Fixed width for consistent snapshots
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
+11
-11
@@ -39,14 +39,14 @@ Tips for getting started:
|
|||||||
2. Be specific for the best results.
|
2. Be specific for the best results.
|
||||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||||
4. /help for more information.
|
4. /help for more information.
|
||||||
╭─────────────────────────────────────────────────────────────────────────────╮
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ ✓ tool1 Description for tool 1 │
|
│ ✓ tool1 Description for tool 1 │
|
||||||
│ │
|
│ │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────╯
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
||||||
╭─────────────────────────────────────────────────────────────────────────────╮
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ ✓ tool2 Description for tool 2 │
|
│ ✓ tool2 Description for tool 2 │
|
||||||
│ │
|
│ │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────╯"
|
╰──────────────────────────────────────────────────────────────────────────────╯"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`AlternateBufferQuittingDisplay > renders with empty history and no pending items > empty 1`] = `
|
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.
|
2. Be specific for the best results.
|
||||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||||
4. /help for more information.
|
4. /help for more information.
|
||||||
╭─────────────────────────────────────────────────────────────────────────────╮
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ ✓ tool1 Description for tool 1 │
|
│ ✓ tool1 Description for tool 1 │
|
||||||
│ │
|
│ │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────╯
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
||||||
╭─────────────────────────────────────────────────────────────────────────────╮
|
╭──────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ ✓ tool2 Description for tool 2 │
|
│ ✓ tool2 Description for tool 2 │
|
||||||
│ │
|
│ │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────╯"
|
╰──────────────────────────────────────────────────────────────────────────────╯"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`AlternateBufferQuittingDisplay > renders with pending items but no history > with_pending_no_history 1`] = `
|
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.
|
2. Be specific for the best results.
|
||||||
3. Create GEMINI.md files to customize your interactions with Gemini.
|
3. Create GEMINI.md files to customize your interactions with Gemini.
|
||||||
4. /help for more information.
|
4. /help for more information.
|
||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
> Hello Gemini
|
> Hello Gemini
|
||||||
|
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||||
✦ Hello User!"
|
✦ Hello User!"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -65,9 +65,9 @@ exports[`<AppHeader /> > should render the banner when previewFeatures is disabl
|
|||||||
███░ ░░█████████
|
███░ ░░█████████
|
||||||
░░░ ░░░░░░░░░
|
░░░ ░░░░░░░░░
|
||||||
|
|
||||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ This is the default banner │
|
│ This is the default banner │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
Tips for getting started:
|
Tips for getting started:
|
||||||
1. Ask questions, edit files, or run commands.
|
1. Ask questions, edit files, or run commands.
|
||||||
2. Be specific for the best results.
|
2. Be specific for the best results.
|
||||||
@@ -86,9 +86,9 @@ exports[`<AppHeader /> > should render the banner with default text 1`] = `
|
|||||||
███░ ░░█████████
|
███░ ░░█████████
|
||||||
░░░ ░░░░░░░░░
|
░░░ ░░░░░░░░░
|
||||||
|
|
||||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ This is the default banner │
|
│ This is the default banner │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
Tips for getting started:
|
Tips for getting started:
|
||||||
1. Ask questions, edit files, or run commands.
|
1. Ask questions, edit files, or run commands.
|
||||||
2. Be specific for the best results.
|
2. Be specific for the best results.
|
||||||
@@ -107,9 +107,9 @@ exports[`<AppHeader /> > should render the banner with warning text 1`] = `
|
|||||||
███░ ░░█████████
|
███░ ░░█████████
|
||||||
░░░ ░░░░░░░░░
|
░░░ ░░░░░░░░░
|
||||||
|
|
||||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
│ There are capacity issues │
|
│ There are capacity issues │
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||||
Tips for getting started:
|
Tips for getting started:
|
||||||
1. Ask questions, edit files, or run commands.
|
1. Ask questions, edit files, or run commands.
|
||||||
2. Be specific for the best results.
|
2. Be specific for the best results.
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
exports[`<Footer /> > 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 /> > 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 /> > 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 /> > 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 /> > 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 /> > 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 /> > footer configuration filtering (golden snapshots) > renders footer with all optional sections hidden (minimal footer) > footer-minimal 1`] = `""`;
|
exports[`<Footer /> > footer configuration filtering (golden snapshots) > renders footer with all optional sections hidden (minimal footer) > footer-minimal 1`] = `""`;
|
||||||
|
|
||||||
exports[`<Footer /> > 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 /> > 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)"`;
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// 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`] = `
|
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 →
|
||||||
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`] = `
|
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 ←
|
||||||
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
|
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
|
||||||
llllllllllllllllllllllllllllllllllllllllllllllllll"
|
llllllllllllllllllllllllllllllllllllllllllllllllll"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`InputPrompt > command search (Ctrl+R when not in shell) > renders match window and expanded view (snapshots) > command-search-render-collapsed-match 1`] = `
|
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"
|
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`] = `
|
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"
|
git commit -m "feat: add search" in src/app"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`InputPrompt > image path transformation snapshots > should snapshot collapsed image path 1`] = `
|
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`] = `
|
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`] = `
|
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`] = `
|
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`] = `
|
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`] = `
|
exports[`InputPrompt > snapshots > should render correctly when accepting edits 1`] = `
|
||||||
"╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
│ > Type your message or @path/to/file │
|
> Type your message or @path/to/file
|
||||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
|
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 { UserMessage } from './UserMessage.js';
|
||||||
import { describe, it, expect, vi } from 'vitest';
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
|
|
||||||
@@ -15,8 +15,9 @@ vi.mock('../../utils/commandUtils.js', () => ({
|
|||||||
|
|
||||||
describe('UserMessage', () => {
|
describe('UserMessage', () => {
|
||||||
it('renders normal user message with correct prefix', () => {
|
it('renders normal user message with correct prefix', () => {
|
||||||
const { lastFrame } = render(
|
const { lastFrame } = renderWithProviders(
|
||||||
<UserMessage text="Hello Gemini" width={80} />,
|
<UserMessage text="Hello Gemini" width={80} />,
|
||||||
|
{ width: 80 },
|
||||||
);
|
);
|
||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
|
|
||||||
@@ -24,7 +25,10 @@ describe('UserMessage', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders slash command message', () => {
|
it('renders slash command message', () => {
|
||||||
const { lastFrame } = render(<UserMessage text="/help" width={80} />);
|
const { lastFrame } = renderWithProviders(
|
||||||
|
<UserMessage text="/help" width={80} />,
|
||||||
|
{ width: 80 },
|
||||||
|
);
|
||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
|
|
||||||
expect(output).toMatchSnapshot();
|
expect(output).toMatchSnapshot();
|
||||||
@@ -32,7 +36,10 @@ describe('UserMessage', () => {
|
|||||||
|
|
||||||
it('renders multiline user message', () => {
|
it('renders multiline user message', () => {
|
||||||
const message = 'Line 1\nLine 2';
|
const message = 'Line 1\nLine 2';
|
||||||
const { lastFrame } = render(<UserMessage text={message} width={80} />);
|
const { lastFrame } = renderWithProviders(
|
||||||
|
<UserMessage text={message} width={80} />,
|
||||||
|
{ width: 80 },
|
||||||
|
);
|
||||||
const output = lastFrame();
|
const output = lastFrame();
|
||||||
|
|
||||||
expect(output).toMatchSnapshot();
|
expect(output).toMatchSnapshot();
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import { Text, Box } from 'ink';
|
|||||||
import { theme } from '../../semantic-colors.js';
|
import { theme } from '../../semantic-colors.js';
|
||||||
import { SCREEN_READER_USER_PREFIX } from '../../textConstants.js';
|
import { SCREEN_READER_USER_PREFIX } from '../../textConstants.js';
|
||||||
import { isSlashCommand as checkIsSlashCommand } from '../../utils/commandUtils.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 {
|
interface UserMessageProps {
|
||||||
text: string;
|
text: string;
|
||||||
@@ -19,19 +22,30 @@ export const UserMessage: React.FC<UserMessageProps> = ({ text, width }) => {
|
|||||||
const prefix = '> ';
|
const prefix = '> ';
|
||||||
const prefixWidth = prefix.length;
|
const prefixWidth = prefix.length;
|
||||||
const isSlashCommand = checkIsSlashCommand(text);
|
const isSlashCommand = checkIsSlashCommand(text);
|
||||||
|
const config = useConfig();
|
||||||
|
const useBackgroundColor = config.getUseBackgroundColor();
|
||||||
|
|
||||||
const textColor = isSlashCommand ? theme.text.accent : theme.text.secondary;
|
const textColor = isSlashCommand ? theme.text.accent : theme.text.secondary;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<HalfLinePaddedBox
|
||||||
|
backgroundBaseColor={theme.border.default}
|
||||||
|
backgroundOpacity={DEFAULT_BACKGROUND_OPACITY}
|
||||||
|
useBackgroundColor={useBackgroundColor}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
flexDirection="row"
|
flexDirection="row"
|
||||||
paddingY={0}
|
paddingY={0}
|
||||||
marginY={1}
|
marginY={useBackgroundColor ? 0 : 1}
|
||||||
|
paddingX={useBackgroundColor ? 1 : 0}
|
||||||
alignSelf="flex-start"
|
alignSelf="flex-start"
|
||||||
width={width}
|
width={width}
|
||||||
>
|
>
|
||||||
<Box width={prefixWidth} flexShrink={0}>
|
<Box width={prefixWidth} flexShrink={0}>
|
||||||
<Text color={theme.text.accent} aria-label={SCREEN_READER_USER_PREFIX}>
|
<Text
|
||||||
|
color={theme.text.accent}
|
||||||
|
aria-label={SCREEN_READER_USER_PREFIX}
|
||||||
|
>
|
||||||
{prefix}
|
{prefix}
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -41,5 +55,6 @@ export const UserMessage: React.FC<UserMessageProps> = ({ text, width }) => {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
</HalfLinePaddedBox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,19 +7,40 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../../semantic-colors.js';
|
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 {
|
interface UserShellMessageProps {
|
||||||
text: string;
|
text: string;
|
||||||
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const UserShellMessage: React.FC<UserShellMessageProps> = ({ text }) => {
|
export const UserShellMessage: React.FC<UserShellMessageProps> = ({
|
||||||
|
text,
|
||||||
|
width,
|
||||||
|
}) => {
|
||||||
|
const config = useConfig();
|
||||||
|
const useBackgroundColor = config.getUseBackgroundColor();
|
||||||
|
|
||||||
// Remove leading '!' if present, as App.tsx adds it for the processor.
|
// Remove leading '!' if present, as App.tsx adds it for the processor.
|
||||||
const commandToDisplay = text.startsWith('!') ? text.substring(1) : text;
|
const commandToDisplay = text.startsWith('!') ? text.substring(1) : text;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<HalfLinePaddedBox
|
||||||
|
backgroundBaseColor={theme.border.default}
|
||||||
|
backgroundOpacity={DEFAULT_BACKGROUND_OPACITY}
|
||||||
|
useBackgroundColor={useBackgroundColor}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
paddingY={0}
|
||||||
|
marginY={useBackgroundColor ? 0 : 1}
|
||||||
|
paddingX={useBackgroundColor ? 1 : 0}
|
||||||
|
width={width}
|
||||||
|
>
|
||||||
<Text color={theme.ui.symbol}>$ </Text>
|
<Text color={theme.ui.symbol}>$ </Text>
|
||||||
<Text color={theme.text.primary}>{commandToDisplay}</Text>
|
<Text color={theme.text.primary}>{commandToDisplay}</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
</HalfLinePaddedBox>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
exports[`UserMessage > renders multiline user message 1`] = `
|
exports[`UserMessage > renders multiline user message 1`] = `
|
||||||
"
|
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
> Line 1
|
> Line 1
|
||||||
Line 2
|
Line 2
|
||||||
"
|
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`UserMessage > renders normal user message with correct prefix 1`] = `
|
exports[`UserMessage > renders normal user message with correct prefix 1`] = `
|
||||||
"
|
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
> Hello Gemini
|
> Hello Gemini
|
||||||
"
|
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`UserMessage > renders slash command message 1`] = `
|
exports[`UserMessage > renders slash command message 1`] = `
|
||||||
"
|
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
> /help
|
> /help
|
||||||
"
|
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -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<HalfLinePaddedBoxProps> = (props) => {
|
||||||
|
if (props.useBackgroundColor === false) {
|
||||||
|
return <>{props.children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <HalfLinePaddedBoxInternal {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const HalfLinePaddedBoxInternal: React.FC<HalfLinePaddedBoxProps> = ({
|
||||||
|
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 (
|
||||||
|
<Box
|
||||||
|
width={terminalWidth}
|
||||||
|
flexDirection="column"
|
||||||
|
alignItems="stretch"
|
||||||
|
minHeight={1}
|
||||||
|
flexShrink={0}
|
||||||
|
backgroundColor={backgroundColor}
|
||||||
|
>
|
||||||
|
<Box width={terminalWidth} flexDirection="row">
|
||||||
|
<Text backgroundColor={backgroundColor} color={terminalBg}>
|
||||||
|
{'▀'.repeat(terminalWidth)}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
{children}
|
||||||
|
<Box width={terminalWidth} flexDirection="row">
|
||||||
|
<Text color={terminalBg} backgroundColor={backgroundColor}>
|
||||||
|
{'▄'.repeat(terminalWidth)}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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(
|
||||||
|
<MouseProvider mouseEventsEnabled={false}>
|
||||||
|
<KeypressProvider>
|
||||||
|
<ScrollProvider>
|
||||||
|
<Box width={100} height={20}>
|
||||||
|
<ScrollableList
|
||||||
|
data={items}
|
||||||
|
renderItem={({ item }) => <Text>{item.title}</Text>}
|
||||||
|
estimatedItemHeight={() => 1}
|
||||||
|
keyExtractor={(item) => item.id}
|
||||||
|
hasFocus={true}
|
||||||
|
width={50}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</ScrollProvider>
|
||||||
|
</KeypressProvider>
|
||||||
|
</MouseProvider>,
|
||||||
|
);
|
||||||
|
lastFrame = result.lastFrame;
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(lastFrame()).toContain('Item 1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type VirtualizedListProps<T> = {
|
|||||||
|
|
||||||
interface ScrollableListProps<T> extends VirtualizedListProps<T> {
|
interface ScrollableListProps<T> extends VirtualizedListProps<T> {
|
||||||
hasFocus: boolean;
|
hasFocus: boolean;
|
||||||
|
width?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ScrollableListRef<T> = VirtualizedListRef<T>;
|
export type ScrollableListRef<T> = VirtualizedListRef<T>;
|
||||||
@@ -45,7 +46,7 @@ function ScrollableList<T>(
|
|||||||
props: ScrollableListProps<T>,
|
props: ScrollableListProps<T>,
|
||||||
ref: React.Ref<ScrollableListRef<T>>,
|
ref: React.Ref<ScrollableListRef<T>>,
|
||||||
) {
|
) {
|
||||||
const { hasFocus } = props;
|
const { hasFocus, width } = props;
|
||||||
const virtualizedListRef = useRef<VirtualizedListRef<T>>(null);
|
const virtualizedListRef = useRef<VirtualizedListRef<T>>(null);
|
||||||
const containerRef = useRef<DOMElement>(null);
|
const containerRef = useRef<DOMElement>(null);
|
||||||
|
|
||||||
@@ -236,6 +237,7 @@ function ScrollableList<T>(
|
|||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
|
width={width}
|
||||||
>
|
>
|
||||||
<VirtualizedList
|
<VirtualizedList
|
||||||
ref={virtualizedListRef}
|
ref={virtualizedListRef}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ export const QUEUE_ERROR_DISPLAY_DURATION_MS = 3000;
|
|||||||
export const SHELL_ACTION_REQUIRED_TITLE_DELAY_MS = 30000;
|
export const SHELL_ACTION_REQUIRED_TITLE_DELAY_MS = 30000;
|
||||||
export const SHELL_SILENT_WORKING_TITLE_DELAY_MS = 120000;
|
export const SHELL_SILENT_WORKING_TITLE_DELAY_MS = 120000;
|
||||||
|
|
||||||
|
export const DEFAULT_BACKGROUND_OPACITY = 0.08;
|
||||||
|
|
||||||
export const KEYBOARD_SHORTCUTS_URL =
|
export const KEYBOARD_SHORTCUTS_URL =
|
||||||
'https://geminicli.com/docs/cli/keyboard-shortcuts/';
|
'https://geminicli.com/docs/cli/keyboard-shortcuts/';
|
||||||
export const LRU_BUFFER_PERF_CACHE_LIMIT = 20000;
|
export const LRU_BUFFER_PERF_CACHE_LIMIT = 20000;
|
||||||
|
|||||||
@@ -34,13 +34,10 @@ export const DefaultAppLayout: React.FC = () => {
|
|||||||
useFlickerDetector(rootUiRef, terminalHeight);
|
useFlickerDetector(rootUiRef, terminalHeight);
|
||||||
// If in alternate buffer mode, need to leave room to draw the scrollbar on
|
// If in alternate buffer mode, need to leave room to draw the scrollbar on
|
||||||
// the right side of the terminal.
|
// the right side of the terminal.
|
||||||
const width = isAlternateBuffer
|
|
||||||
? uiState.terminalWidth
|
|
||||||
: uiState.mainAreaWidth;
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
width={width}
|
width={uiState.terminalWidth}
|
||||||
height={isAlternateBuffer ? terminalHeight : undefined}
|
height={isAlternateBuffer ? terminalHeight : undefined}
|
||||||
paddingBottom={isAlternateBuffer ? 1 : undefined}
|
paddingBottom={isAlternateBuffer ? 1 : undefined}
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
@@ -55,6 +52,7 @@ export const DefaultAppLayout: React.FC = () => {
|
|||||||
ref={uiState.mainControlsRef}
|
ref={uiState.mainControlsRef}
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
flexGrow={0}
|
flexGrow={0}
|
||||||
|
width={uiState.terminalWidth}
|
||||||
>
|
>
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<CopyModeWarning />
|
<CopyModeWarning />
|
||||||
@@ -63,7 +61,7 @@ export const DefaultAppLayout: React.FC = () => {
|
|||||||
uiState.customDialog
|
uiState.customDialog
|
||||||
) : uiState.dialogsVisible ? (
|
) : uiState.dialogsVisible ? (
|
||||||
<DialogManager
|
<DialogManager
|
||||||
terminalWidth={uiState.mainAreaWidth}
|
terminalWidth={uiState.terminalWidth}
|
||||||
addItem={uiState.historyManager.addItem}
|
addItem={uiState.historyManager.addItem}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -233,6 +233,33 @@ export function resolveColor(colorValue: string): string | undefined {
|
|||||||
return 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(
|
export function interpolateColor(
|
||||||
color1: string,
|
color1: string,
|
||||||
color2: string,
|
color2: string,
|
||||||
|
|||||||
@@ -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,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -29,43 +29,19 @@ describe('ui-sizing', () => {
|
|||||||
|
|
||||||
describe('calculateMainAreaWidth', () => {
|
describe('calculateMainAreaWidth', () => {
|
||||||
it.each([
|
it.each([
|
||||||
// width, useFullWidth, alternateBuffer, expected
|
// expected, width, altBuffer
|
||||||
[80, true, false, 80],
|
[80, 80, false],
|
||||||
[100, true, false, 100],
|
[100, 100, false],
|
||||||
[80, true, true, 79], // -1 for alternate buffer
|
[79, 80, true],
|
||||||
[100, true, true, 99],
|
[99, 100, true],
|
||||||
|
|
||||||
// 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
|
|
||||||
])(
|
])(
|
||||||
'should return %i when width=%i, useFullWidth=%s, altBuffer=%s',
|
'should return %i when width=%i and altBuffer=%s',
|
||||||
(width, useFullWidth, altBuffer, expected) => {
|
(expected, width, altBuffer) => {
|
||||||
mocks.isAlternateBufferEnabled.mockReturnValue(altBuffer);
|
mocks.isAlternateBufferEnabled.mockReturnValue(altBuffer);
|
||||||
const settings = createSettings(useFullWidth);
|
const settings = createSettings();
|
||||||
|
|
||||||
expect(calculateMainAreaWidth(width, settings)).toBe(expected);
|
expect(calculateMainAreaWidth(width, settings)).toBe(expected);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
it('should match snapshot for interpolation range', () => {
|
|
||||||
mocks.isAlternateBufferEnabled.mockReturnValue(false);
|
|
||||||
const settings = createSettings(false);
|
|
||||||
|
|
||||||
const results: Record<number, number> = {};
|
|
||||||
// Test range from 80 to 132
|
|
||||||
for (let w = 80; w <= 132; w += 4) {
|
|
||||||
results[w] = calculateMainAreaWidth(w, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(results).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,34 +4,15 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { lerp } from '../../utils/math.js';
|
|
||||||
import { type LoadedSettings } from '../../config/settings.js';
|
import { type LoadedSettings } from '../../config/settings.js';
|
||||||
import { isAlternateBufferEnabled } from '../hooks/useAlternateBuffer.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 = (
|
export const calculateMainAreaWidth = (
|
||||||
terminalWidth: number,
|
terminalWidth: number,
|
||||||
settings: LoadedSettings,
|
settings: LoadedSettings,
|
||||||
): number => {
|
): number => {
|
||||||
if (settings.merged.ui.useFullWidth) {
|
|
||||||
if (isAlternateBufferEnabled(settings)) {
|
if (isAlternateBufferEnabled(settings)) {
|
||||||
return terminalWidth - 1;
|
return terminalWidth - 1;
|
||||||
}
|
}
|
||||||
return terminalWidth;
|
return terminalWidth;
|
||||||
}
|
|
||||||
return getMainAreaWidthInternal(terminalWidth);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -356,6 +356,7 @@ export interface ConfigParameters {
|
|||||||
compressionThreshold?: number;
|
compressionThreshold?: number;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
trustedFolder?: boolean;
|
trustedFolder?: boolean;
|
||||||
|
useBackgroundColor?: boolean;
|
||||||
useRipgrep?: boolean;
|
useRipgrep?: boolean;
|
||||||
enableInteractiveShell?: boolean;
|
enableInteractiveShell?: boolean;
|
||||||
skipNextSpeakerCheck?: boolean;
|
skipNextSpeakerCheck?: boolean;
|
||||||
@@ -500,6 +501,7 @@ export class Config {
|
|||||||
private readonly useRipgrep: boolean;
|
private readonly useRipgrep: boolean;
|
||||||
private readonly enableInteractiveShell: boolean;
|
private readonly enableInteractiveShell: boolean;
|
||||||
private readonly skipNextSpeakerCheck: boolean;
|
private readonly skipNextSpeakerCheck: boolean;
|
||||||
|
private readonly useBackgroundColor: boolean;
|
||||||
private shellExecutionConfig: ShellExecutionConfig;
|
private shellExecutionConfig: ShellExecutionConfig;
|
||||||
private readonly extensionManagement: boolean = true;
|
private readonly extensionManagement: boolean = true;
|
||||||
private readonly enablePromptCompletion: boolean = false;
|
private readonly enablePromptCompletion: boolean = false;
|
||||||
@@ -666,6 +668,7 @@ export class Config {
|
|||||||
this.ptyInfo = params.ptyInfo ?? 'child_process';
|
this.ptyInfo = params.ptyInfo ?? 'child_process';
|
||||||
this.trustedFolder = params.trustedFolder;
|
this.trustedFolder = params.trustedFolder;
|
||||||
this.useRipgrep = params.useRipgrep ?? true;
|
this.useRipgrep = params.useRipgrep ?? true;
|
||||||
|
this.useBackgroundColor = params.useBackgroundColor ?? true;
|
||||||
this.enableInteractiveShell = params.enableInteractiveShell ?? false;
|
this.enableInteractiveShell = params.enableInteractiveShell ?? false;
|
||||||
this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? true;
|
this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? true;
|
||||||
this.shellExecutionConfig = {
|
this.shellExecutionConfig = {
|
||||||
@@ -1823,6 +1826,10 @@ export class Config {
|
|||||||
return this.useRipgrep;
|
return this.useRipgrep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUseBackgroundColor(): boolean {
|
||||||
|
return this.useBackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
getEnableInteractiveShell(): boolean {
|
getEnableInteractiveShell(): boolean {
|
||||||
return this.enableInteractiveShell;
|
return this.enableInteractiveShell;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,13 +302,6 @@
|
|||||||
"default": false,
|
"default": false,
|
||||||
"type": "boolean"
|
"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": {
|
"useAlternateBuffer": {
|
||||||
"title": "Use Alternate Screen Buffer",
|
"title": "Use Alternate Screen Buffer",
|
||||||
"description": "Use an alternate screen buffer for the UI, preserving shell history.",
|
"description": "Use an alternate screen buffer for the UI, preserving shell history.",
|
||||||
@@ -316,6 +309,13 @@
|
|||||||
"default": false,
|
"default": false,
|
||||||
"type": "boolean"
|
"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": {
|
"incrementalRendering": {
|
||||||
"title": "Incremental Rendering",
|
"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.",
|
"description": "Enable incremental rendering for the UI. This option will reduce flickering but may cause rendering artifacts. Only supported when useAlternateBuffer is enabled.",
|
||||||
|
|||||||
Reference in New Issue
Block a user