fss-link 1.0.40 → 1.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/dist/commands/mcp/add.test.ts +122 -0
  2. package/dist/commands/mcp/add.ts +222 -0
  3. package/dist/commands/mcp/list.test.ts +154 -0
  4. package/dist/commands/mcp/list.ts +139 -0
  5. package/dist/commands/mcp/remove.test.ts +69 -0
  6. package/dist/commands/mcp/remove.ts +60 -0
  7. package/dist/commands/mcp.test.ts +55 -0
  8. package/dist/commands/mcp.ts +27 -0
  9. package/dist/config/apiValidation.test.ts +118 -0
  10. package/dist/config/auth.test.ts +79 -0
  11. package/dist/config/auth.ts +100 -0
  12. package/dist/config/config.integration.test.ts +407 -0
  13. package/dist/config/config.test.ts +1952 -0
  14. package/dist/config/config.ts +690 -0
  15. package/dist/config/database.test.ts +96 -0
  16. package/dist/config/database.ts +752 -0
  17. package/dist/config/extension.test.ts +236 -0
  18. package/dist/config/extension.ts +180 -0
  19. package/dist/config/keyBindings.test.ts +62 -0
  20. package/dist/config/keyBindings.ts +184 -0
  21. package/dist/config/modelManager.ts +275 -0
  22. package/dist/config/providerManager.ts +244 -0
  23. package/dist/config/providerPersistence.test.ts +377 -0
  24. package/dist/config/providerPersistence.ts +105 -0
  25. package/dist/config/sandboxConfig.ts +107 -0
  26. package/dist/config/settings.test.ts +1424 -0
  27. package/dist/config/settings.ts +517 -0
  28. package/dist/config/settingsSchema.test.ts +252 -0
  29. package/dist/config/settingsSchema.ts +728 -0
  30. package/dist/config/trustedFolders.test.ts +208 -0
  31. package/dist/config/trustedFolders.ts +167 -0
  32. package/dist/gemini.test.tsx +252 -0
  33. package/dist/gemini.tsx +357 -0
  34. package/dist/generated/git-commit.ts +10 -0
  35. package/dist/index.ts +21 -0
  36. package/dist/nonInteractiveCli.test.ts +276 -0
  37. package/dist/nonInteractiveCli.ts +143 -0
  38. package/dist/package.json +87 -87
  39. package/dist/patches/is-in-ci.ts +17 -0
  40. package/dist/services/BuiltinCommandLoader.test.ts +127 -0
  41. package/dist/services/BuiltinCommandLoader.ts +95 -0
  42. package/dist/services/CommandService.test.ts +352 -0
  43. package/dist/services/CommandService.ts +103 -0
  44. package/dist/services/FileCommandLoader.test.ts +1002 -0
  45. package/dist/services/FileCommandLoader.ts +289 -0
  46. package/dist/services/McpPromptLoader.ts +231 -0
  47. package/dist/services/SearchEngineConfigProvider.ts +100 -0
  48. package/dist/services/prompt-processors/argumentProcessor.test.ts +41 -0
  49. package/dist/services/prompt-processors/argumentProcessor.ts +23 -0
  50. package/dist/services/prompt-processors/shellProcessor.test.ts +709 -0
  51. package/dist/services/prompt-processors/shellProcessor.ts +248 -0
  52. package/dist/services/prompt-processors/types.ts +44 -0
  53. package/dist/services/types.ts +24 -0
  54. package/dist/src/config/apiValidation.test.d.ts +6 -0
  55. package/dist/src/config/apiValidation.test.js +99 -0
  56. package/dist/src/config/apiValidation.test.js.map +1 -0
  57. package/dist/src/config/database.d.ts +32 -0
  58. package/dist/src/config/database.js +281 -2
  59. package/dist/src/config/database.js.map +1 -1
  60. package/dist/src/config/database.test.d.ts +6 -0
  61. package/dist/src/config/database.test.js +80 -0
  62. package/dist/src/config/database.test.js.map +1 -0
  63. package/dist/src/config/providerManager.d.ts +74 -0
  64. package/dist/src/config/providerManager.js +203 -0
  65. package/dist/src/config/providerManager.js.map +1 -0
  66. package/dist/src/config/providerPersistence.d.ts +75 -0
  67. package/dist/src/config/providerPersistence.js +55 -0
  68. package/dist/src/config/providerPersistence.js.map +1 -0
  69. package/dist/src/config/providerPersistence.test.d.ts +6 -0
  70. package/dist/src/config/providerPersistence.test.js +283 -0
  71. package/dist/src/config/providerPersistence.test.js.map +1 -0
  72. package/dist/src/config/settingsSchema.d.ts +9 -0
  73. package/dist/src/config/settingsSchema.js +9 -0
  74. package/dist/src/config/settingsSchema.js.map +1 -1
  75. package/dist/src/generated/git-commit.d.ts +1 -1
  76. package/dist/src/generated/git-commit.js +1 -1
  77. package/dist/src/services/BuiltinCommandLoader.js +2 -0
  78. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  79. package/dist/src/ui/App.js +14 -2
  80. package/dist/src/ui/App.js.map +1 -1
  81. package/dist/src/ui/commands/contextCommand.d.ts +7 -0
  82. package/dist/src/ui/commands/contextCommand.js +115 -0
  83. package/dist/src/ui/commands/contextCommand.js.map +1 -0
  84. package/dist/src/ui/components/ContextUsageDisplay.d.ts +3 -1
  85. package/dist/src/ui/components/ContextUsageDisplay.js +43 -3
  86. package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
  87. package/dist/src/ui/components/Footer.d.ts +1 -0
  88. package/dist/src/ui/components/Footer.js +2 -2
  89. package/dist/src/ui/components/Footer.js.map +1 -1
  90. package/dist/src/ui/components/GeminiKeyDialog.d.ts +11 -0
  91. package/dist/src/ui/components/GeminiKeyDialog.js +156 -0
  92. package/dist/src/ui/components/GeminiKeyDialog.js.map +1 -0
  93. package/dist/src/ui/components/OpenAIEndpointDialog.d.ts +19 -0
  94. package/dist/src/ui/components/OpenAIEndpointDialog.js +163 -0
  95. package/dist/src/ui/components/OpenAIEndpointDialog.js.map +1 -0
  96. package/dist/src/ui/components/WelcomeBackDialog.d.ts +36 -0
  97. package/dist/src/ui/components/WelcomeBackDialog.js +109 -0
  98. package/dist/src/ui/components/WelcomeBackDialog.js.map +1 -0
  99. package/dist/src/ui/hooks/useWelcomeBack.d.ts +52 -0
  100. package/dist/src/ui/hooks/useWelcomeBack.js +214 -0
  101. package/dist/src/ui/hooks/useWelcomeBack.js.map +1 -0
  102. package/dist/src/zed-integration/schema.d.ts +1516 -1516
  103. package/dist/test-setup.ts +12 -0
  104. package/dist/test-utils/customMatchers.ts +65 -0
  105. package/dist/test-utils/mockCommandContext.test.ts +62 -0
  106. package/dist/test-utils/mockCommandContext.ts +105 -0
  107. package/dist/test-utils/render.tsx +18 -0
  108. package/dist/tsconfig.tsbuildinfo +1 -1
  109. package/dist/ui/App.test.tsx +2181 -0
  110. package/dist/ui/App.tsx +1344 -0
  111. package/dist/ui/IdeIntegrationNudge.tsx +98 -0
  112. package/dist/ui/__snapshots__/App.test.tsx.snap +124 -0
  113. package/dist/ui/colors.ts +56 -0
  114. package/dist/ui/commands/aboutCommand.test.ts +153 -0
  115. package/dist/ui/commands/aboutCommand.ts +49 -0
  116. package/dist/ui/commands/authCommand.test.ts +36 -0
  117. package/dist/ui/commands/authCommand.ts +17 -0
  118. package/dist/ui/commands/bugCommand.test.ts +114 -0
  119. package/dist/ui/commands/bugCommand.ts +92 -0
  120. package/dist/ui/commands/chatCommand.test.ts +414 -0
  121. package/dist/ui/commands/chatCommand.ts +280 -0
  122. package/dist/ui/commands/clearCommand.test.ts +100 -0
  123. package/dist/ui/commands/clearCommand.ts +29 -0
  124. package/dist/ui/commands/compressCommand.test.ts +129 -0
  125. package/dist/ui/commands/compressCommand.ts +78 -0
  126. package/dist/ui/commands/contextCommand.ts +132 -0
  127. package/dist/ui/commands/copyCommand.test.ts +296 -0
  128. package/dist/ui/commands/copyCommand.ts +67 -0
  129. package/dist/ui/commands/corgiCommand.test.ts +34 -0
  130. package/dist/ui/commands/corgiCommand.ts +16 -0
  131. package/dist/ui/commands/directoryCommand.test.tsx +185 -0
  132. package/dist/ui/commands/directoryCommand.tsx +179 -0
  133. package/dist/ui/commands/docsCommand.test.ts +99 -0
  134. package/dist/ui/commands/docsCommand.ts +42 -0
  135. package/dist/ui/commands/editorCommand.test.ts +30 -0
  136. package/dist/ui/commands/editorCommand.ts +21 -0
  137. package/dist/ui/commands/extensionsCommand.test.ts +67 -0
  138. package/dist/ui/commands/extensionsCommand.ts +46 -0
  139. package/dist/ui/commands/helpCommand.test.ts +52 -0
  140. package/dist/ui/commands/helpCommand.ts +23 -0
  141. package/dist/ui/commands/ideCommand.test.ts +255 -0
  142. package/dist/ui/commands/ideCommand.ts +283 -0
  143. package/dist/ui/commands/initCommand.test.ts +127 -0
  144. package/dist/ui/commands/initCommand.ts +117 -0
  145. package/dist/ui/commands/mcpCommand.test.ts +1057 -0
  146. package/dist/ui/commands/mcpCommand.ts +531 -0
  147. package/dist/ui/commands/memoryCommand.test.ts +344 -0
  148. package/dist/ui/commands/memoryCommand.ts +305 -0
  149. package/dist/ui/commands/privacyCommand.test.ts +38 -0
  150. package/dist/ui/commands/privacyCommand.ts +17 -0
  151. package/dist/ui/commands/quitCommand.test.ts +55 -0
  152. package/dist/ui/commands/quitCommand.ts +36 -0
  153. package/dist/ui/commands/restoreCommand.test.ts +250 -0
  154. package/dist/ui/commands/restoreCommand.ts +157 -0
  155. package/dist/ui/commands/searchEngineSetupCommand.ts +18 -0
  156. package/dist/ui/commands/settingsCommand.test.ts +36 -0
  157. package/dist/ui/commands/settingsCommand.ts +17 -0
  158. package/dist/ui/commands/setupGithubCommand.test.ts +238 -0
  159. package/dist/ui/commands/setupGithubCommand.ts +212 -0
  160. package/dist/ui/commands/speakCommand.ts +175 -0
  161. package/dist/ui/commands/statsCommand.test.ts +78 -0
  162. package/dist/ui/commands/statsCommand.ts +70 -0
  163. package/dist/ui/commands/terminalSetupCommand.test.ts +85 -0
  164. package/dist/ui/commands/terminalSetupCommand.ts +45 -0
  165. package/dist/ui/commands/themeCommand.test.ts +38 -0
  166. package/dist/ui/commands/themeCommand.ts +17 -0
  167. package/dist/ui/commands/toolsCommand.test.ts +105 -0
  168. package/dist/ui/commands/toolsCommand.ts +71 -0
  169. package/dist/ui/commands/ttsCommand.ts +143 -0
  170. package/dist/ui/commands/types.ts +204 -0
  171. package/dist/ui/commands/vimCommand.ts +25 -0
  172. package/dist/ui/commands/voiceCommand.ts +125 -0
  173. package/dist/ui/components/AboutBox.tsx +133 -0
  174. package/dist/ui/components/AsciiArt.ts +54 -0
  175. package/dist/ui/components/AuthDialog.test.tsx +334 -0
  176. package/dist/ui/components/AuthDialog.tsx +289 -0
  177. package/dist/ui/components/AuthInProgress.tsx +62 -0
  178. package/dist/ui/components/AutoAcceptIndicator.tsx +47 -0
  179. package/dist/ui/components/ConsoleSummaryDisplay.tsx +35 -0
  180. package/dist/ui/components/ContextSummaryDisplay.test.tsx +85 -0
  181. package/dist/ui/components/ContextSummaryDisplay.tsx +120 -0
  182. package/dist/ui/components/ContextUsageDisplay.tsx +77 -0
  183. package/dist/ui/components/DebugProfiler.tsx +36 -0
  184. package/dist/ui/components/DetailedMessagesDisplay.tsx +82 -0
  185. package/dist/ui/components/EditorSettingsDialog.tsx +172 -0
  186. package/dist/ui/components/FolderTrustDialog.test.tsx +36 -0
  187. package/dist/ui/components/FolderTrustDialog.tsx +74 -0
  188. package/dist/ui/components/Footer.test.tsx +159 -0
  189. package/dist/ui/components/Footer.tsx +158 -0
  190. package/dist/ui/components/GeminiKeyDialog.tsx +252 -0
  191. package/dist/ui/components/GeminiRespondingSpinner.tsx +34 -0
  192. package/dist/ui/components/Header.test.tsx +44 -0
  193. package/dist/ui/components/Header.tsx +70 -0
  194. package/dist/ui/components/Help.tsx +174 -0
  195. package/dist/ui/components/HistoryItemDisplay.test.tsx +125 -0
  196. package/dist/ui/components/HistoryItemDisplay.tsx +98 -0
  197. package/dist/ui/components/InputPrompt.test.tsx +1467 -0
  198. package/dist/ui/components/InputPrompt.tsx +641 -0
  199. package/dist/ui/components/LMStudioModelPrompt.tsx +215 -0
  200. package/dist/ui/components/LoadingIndicator.test.tsx +296 -0
  201. package/dist/ui/components/LoadingIndicator.tsx +82 -0
  202. package/dist/ui/components/MemoryUsageDisplay.tsx +36 -0
  203. package/dist/ui/components/ModelStatsDisplay.test.tsx +252 -0
  204. package/dist/ui/components/ModelStatsDisplay.tsx +197 -0
  205. package/dist/ui/components/OllamaModelPrompt.tsx +206 -0
  206. package/dist/ui/components/OpenAIEndpointDialog.tsx +261 -0
  207. package/dist/ui/components/OpenAIKeyPrompt.test.tsx +64 -0
  208. package/dist/ui/components/OpenAIKeyPrompt.tsx +197 -0
  209. package/dist/ui/components/PrepareLabel.tsx +48 -0
  210. package/dist/ui/components/SearchEngineConfigDialog.tsx +280 -0
  211. package/dist/ui/components/SessionSummaryDisplay.test.tsx +75 -0
  212. package/dist/ui/components/SessionSummaryDisplay.tsx +18 -0
  213. package/dist/ui/components/SettingsDialog.test.tsx +865 -0
  214. package/dist/ui/components/SettingsDialog.tsx +753 -0
  215. package/dist/ui/components/ShellConfirmationDialog.test.tsx +53 -0
  216. package/dist/ui/components/ShellConfirmationDialog.tsx +103 -0
  217. package/dist/ui/components/ShellModeIndicator.tsx +18 -0
  218. package/dist/ui/components/ShowMoreLines.tsx +40 -0
  219. package/dist/ui/components/StatsDisplay.test.tsx +401 -0
  220. package/dist/ui/components/StatsDisplay.tsx +273 -0
  221. package/dist/ui/components/SuggestionsDisplay.tsx +102 -0
  222. package/dist/ui/components/ThemeDialog.tsx +310 -0
  223. package/dist/ui/components/Tips.tsx +45 -0
  224. package/dist/ui/components/TodoDisplay.test.tsx +97 -0
  225. package/dist/ui/components/TodoDisplay.tsx +72 -0
  226. package/dist/ui/components/ToolStatsDisplay.test.tsx +180 -0
  227. package/dist/ui/components/ToolStatsDisplay.tsx +208 -0
  228. package/dist/ui/components/UpdateNotification.tsx +23 -0
  229. package/dist/ui/components/WelcomeBackDialog.tsx +290 -0
  230. package/dist/ui/components/__snapshots__/IDEContextDetailDisplay.test.tsx.snap +24 -0
  231. package/dist/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap +121 -0
  232. package/dist/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap +30 -0
  233. package/dist/ui/components/__snapshots__/ShellConfirmationDialog.test.tsx.snap +21 -0
  234. package/dist/ui/components/__snapshots__/StatsDisplay.test.tsx.snap +264 -0
  235. package/dist/ui/components/__snapshots__/ToolStatsDisplay.test.tsx.snap +91 -0
  236. package/dist/ui/components/messages/CompressionMessage.tsx +49 -0
  237. package/dist/ui/components/messages/DiffRenderer.test.tsx +365 -0
  238. package/dist/ui/components/messages/DiffRenderer.tsx +358 -0
  239. package/dist/ui/components/messages/ErrorMessage.tsx +31 -0
  240. package/dist/ui/components/messages/GeminiMessage.tsx +43 -0
  241. package/dist/ui/components/messages/GeminiMessageContent.tsx +43 -0
  242. package/dist/ui/components/messages/InfoMessage.tsx +32 -0
  243. package/dist/ui/components/messages/ToolConfirmationMessage.test.tsx +58 -0
  244. package/dist/ui/components/messages/ToolConfirmationMessage.tsx +297 -0
  245. package/dist/ui/components/messages/ToolGroupMessage.tsx +126 -0
  246. package/dist/ui/components/messages/ToolMessage.test.tsx +183 -0
  247. package/dist/ui/components/messages/ToolMessage.tsx +296 -0
  248. package/dist/ui/components/messages/UserMessage.tsx +43 -0
  249. package/dist/ui/components/messages/UserShellMessage.tsx +25 -0
  250. package/dist/ui/components/shared/MaxSizedBox.test.tsx +425 -0
  251. package/dist/ui/components/shared/MaxSizedBox.tsx +624 -0
  252. package/dist/ui/components/shared/RadioButtonSelect.test.tsx +181 -0
  253. package/dist/ui/components/shared/RadioButtonSelect.tsx +234 -0
  254. package/dist/ui/components/shared/__snapshots__/RadioButtonSelect.test.tsx.snap +47 -0
  255. package/dist/ui/components/shared/text-buffer.test.ts +1728 -0
  256. package/dist/ui/components/shared/text-buffer.ts +2227 -0
  257. package/dist/ui/components/shared/vim-buffer-actions.test.ts +1119 -0
  258. package/dist/ui/components/shared/vim-buffer-actions.ts +814 -0
  259. package/dist/ui/constants.ts +17 -0
  260. package/dist/ui/contexts/KeypressContext.test.tsx +391 -0
  261. package/dist/ui/contexts/KeypressContext.tsx +440 -0
  262. package/dist/ui/contexts/OverflowContext.tsx +87 -0
  263. package/dist/ui/contexts/SessionContext.test.tsx +132 -0
  264. package/dist/ui/contexts/SessionContext.tsx +143 -0
  265. package/dist/ui/contexts/SettingsContext.tsx +20 -0
  266. package/dist/ui/contexts/StreamingContext.tsx +22 -0
  267. package/dist/ui/contexts/VimModeContext.tsx +79 -0
  268. package/dist/ui/editors/editorSettingsManager.ts +66 -0
  269. package/dist/ui/hooks/atCommandProcessor.test.ts +1102 -0
  270. package/dist/ui/hooks/atCommandProcessor.ts +485 -0
  271. package/dist/ui/hooks/shellCommandProcessor.test.ts +481 -0
  272. package/dist/ui/hooks/shellCommandProcessor.ts +314 -0
  273. package/dist/ui/hooks/slashCommandProcessor.test.ts +1044 -0
  274. package/dist/ui/hooks/slashCommandProcessor.ts +595 -0
  275. package/dist/ui/hooks/useAtCompletion.test.ts +497 -0
  276. package/dist/ui/hooks/useAtCompletion.ts +244 -0
  277. package/dist/ui/hooks/useAuthCommand.ts +129 -0
  278. package/dist/ui/hooks/useAutoAcceptIndicator.test.ts +300 -0
  279. package/dist/ui/hooks/useAutoAcceptIndicator.ts +52 -0
  280. package/dist/ui/hooks/useBracketedPaste.ts +37 -0
  281. package/dist/ui/hooks/useCommandCompletion.test.ts +518 -0
  282. package/dist/ui/hooks/useCommandCompletion.tsx +238 -0
  283. package/dist/ui/hooks/useCompletion.ts +128 -0
  284. package/dist/ui/hooks/useConsoleMessages.test.ts +147 -0
  285. package/dist/ui/hooks/useConsoleMessages.ts +110 -0
  286. package/dist/ui/hooks/useEditorSettings.test.ts +283 -0
  287. package/dist/ui/hooks/useEditorSettings.ts +75 -0
  288. package/dist/ui/hooks/useFocus.test.ts +119 -0
  289. package/dist/ui/hooks/useFocus.ts +48 -0
  290. package/dist/ui/hooks/useFolderTrust.test.ts +159 -0
  291. package/dist/ui/hooks/useFolderTrust.ts +72 -0
  292. package/dist/ui/hooks/useGeminiStream.test.tsx +1998 -0
  293. package/dist/ui/hooks/useGeminiStream.ts +1017 -0
  294. package/dist/ui/hooks/useGitBranchName.test.ts +280 -0
  295. package/dist/ui/hooks/useGitBranchName.ts +79 -0
  296. package/dist/ui/hooks/useHistoryManager.test.ts +202 -0
  297. package/dist/ui/hooks/useHistoryManager.ts +111 -0
  298. package/dist/ui/hooks/useInputHistory.test.ts +261 -0
  299. package/dist/ui/hooks/useInputHistory.ts +111 -0
  300. package/dist/ui/hooks/useKeypress.test.ts +280 -0
  301. package/dist/ui/hooks/useKeypress.ts +39 -0
  302. package/dist/ui/hooks/useKittyKeyboardProtocol.ts +31 -0
  303. package/dist/ui/hooks/useLoadingIndicator.test.ts +139 -0
  304. package/dist/ui/hooks/useLoadingIndicator.ts +57 -0
  305. package/dist/ui/hooks/useLogger.ts +32 -0
  306. package/dist/ui/hooks/useMessageQueue.test.ts +226 -0
  307. package/dist/ui/hooks/useMessageQueue.ts +69 -0
  308. package/dist/ui/hooks/usePhraseCycler.test.ts +145 -0
  309. package/dist/ui/hooks/usePhraseCycler.ts +198 -0
  310. package/dist/ui/hooks/usePrivacySettings.test.ts +242 -0
  311. package/dist/ui/hooks/usePrivacySettings.ts +150 -0
  312. package/dist/ui/hooks/useReactToolScheduler.ts +309 -0
  313. package/dist/ui/hooks/useRefreshMemoryCommand.ts +7 -0
  314. package/dist/ui/hooks/useReverseSearchCompletion.test.tsx +260 -0
  315. package/dist/ui/hooks/useReverseSearchCompletion.tsx +95 -0
  316. package/dist/ui/hooks/useSettingsCommand.ts +25 -0
  317. package/dist/ui/hooks/useShellHistory.test.ts +219 -0
  318. package/dist/ui/hooks/useShellHistory.ts +133 -0
  319. package/dist/ui/hooks/useShowMemoryCommand.ts +75 -0
  320. package/dist/ui/hooks/useSlashCompletion.test.ts +434 -0
  321. package/dist/ui/hooks/useSlashCompletion.ts +187 -0
  322. package/dist/ui/hooks/useStateAndRef.ts +36 -0
  323. package/dist/ui/hooks/useTerminalSize.ts +32 -0
  324. package/dist/ui/hooks/useThemeCommand.ts +110 -0
  325. package/dist/ui/hooks/useTimer.test.ts +120 -0
  326. package/dist/ui/hooks/useTimer.ts +65 -0
  327. package/dist/ui/hooks/useToolScheduler.test.ts +1123 -0
  328. package/dist/ui/hooks/useWelcomeBack.ts +253 -0
  329. package/dist/ui/hooks/vim.test.ts +1691 -0
  330. package/dist/ui/hooks/vim.ts +784 -0
  331. package/dist/ui/keyMatchers.test.ts +337 -0
  332. package/dist/ui/keyMatchers.ts +105 -0
  333. package/dist/ui/privacy/CloudFreePrivacyNotice.tsx +117 -0
  334. package/dist/ui/privacy/CloudPaidPrivacyNotice.tsx +59 -0
  335. package/dist/ui/privacy/GeminiPrivacyNotice.tsx +62 -0
  336. package/dist/ui/privacy/PrivacyNotice.tsx +42 -0
  337. package/dist/ui/semantic-colors.ts +26 -0
  338. package/dist/ui/themes/ansi-light.ts +150 -0
  339. package/dist/ui/themes/ansi.ts +159 -0
  340. package/dist/ui/themes/atom-one-dark.ts +147 -0
  341. package/dist/ui/themes/ayu-light.ts +139 -0
  342. package/dist/ui/themes/ayu.ts +113 -0
  343. package/dist/ui/themes/color-utils.test.ts +221 -0
  344. package/dist/ui/themes/color-utils.ts +231 -0
  345. package/dist/ui/themes/default-light.ts +108 -0
  346. package/dist/ui/themes/default.ts +151 -0
  347. package/dist/ui/themes/dracula.ts +124 -0
  348. package/dist/ui/themes/fss-code-dark.ts +156 -0
  349. package/dist/ui/themes/fss-dark.ts +113 -0
  350. package/dist/ui/themes/fss-light.ts +139 -0
  351. package/dist/ui/themes/github-dark.ts +147 -0
  352. package/dist/ui/themes/github-light.ts +149 -0
  353. package/dist/ui/themes/googlecode.ts +146 -0
  354. package/dist/ui/themes/no-color.ts +125 -0
  355. package/dist/ui/themes/qwen-dark.ts +118 -0
  356. package/dist/ui/themes/qwen-light.ts +144 -0
  357. package/dist/ui/themes/semantic-tokens.ts +127 -0
  358. package/dist/ui/themes/shades-of-purple.ts +352 -0
  359. package/dist/ui/themes/theme-manager.test.ts +99 -0
  360. package/dist/ui/themes/theme-manager.ts +257 -0
  361. package/dist/ui/themes/theme.test.ts +97 -0
  362. package/dist/ui/themes/theme.ts +451 -0
  363. package/dist/ui/themes/xcode.ts +154 -0
  364. package/dist/ui/types.ts +255 -0
  365. package/dist/ui/utils/CodeColorizer.tsx +217 -0
  366. package/dist/ui/utils/ConsolePatcher.ts +71 -0
  367. package/dist/ui/utils/InlineMarkdownRenderer.tsx +173 -0
  368. package/dist/ui/utils/MarkdownDisplay.test.tsx +244 -0
  369. package/dist/ui/utils/MarkdownDisplay.tsx +415 -0
  370. package/dist/ui/utils/TableRenderer.tsx +159 -0
  371. package/dist/ui/utils/__snapshots__/MarkdownDisplay.test.tsx.snap +93 -0
  372. package/dist/ui/utils/clipboardUtils.test.ts +76 -0
  373. package/dist/ui/utils/clipboardUtils.ts +149 -0
  374. package/dist/ui/utils/commandUtils.test.ts +384 -0
  375. package/dist/ui/utils/commandUtils.ts +106 -0
  376. package/dist/ui/utils/computeStats.test.ts +292 -0
  377. package/dist/ui/utils/computeStats.ts +86 -0
  378. package/dist/ui/utils/displayUtils.test.ts +58 -0
  379. package/dist/ui/utils/displayUtils.ts +32 -0
  380. package/dist/ui/utils/formatters.test.ts +72 -0
  381. package/dist/ui/utils/formatters.ts +63 -0
  382. package/dist/ui/utils/isNarrowWidth.ts +9 -0
  383. package/dist/ui/utils/kittyProtocolDetector.ts +105 -0
  384. package/dist/ui/utils/markdownUtilities.test.ts +50 -0
  385. package/dist/ui/utils/markdownUtilities.ts +125 -0
  386. package/dist/ui/utils/platformConstants.ts +52 -0
  387. package/dist/ui/utils/terminalSetup.ts +342 -0
  388. package/dist/ui/utils/textUtils.ts +40 -0
  389. package/dist/ui/utils/updateCheck.test.ts +163 -0
  390. package/dist/ui/utils/updateCheck.ts +100 -0
  391. package/dist/utils/checks.ts +28 -0
  392. package/dist/utils/cleanup.test.ts +68 -0
  393. package/dist/utils/cleanup.ts +36 -0
  394. package/dist/utils/dialogScopeUtils.ts +64 -0
  395. package/dist/utils/events.ts +14 -0
  396. package/dist/utils/gitUtils.test.ts +149 -0
  397. package/dist/utils/gitUtils.ts +116 -0
  398. package/dist/utils/handleAutoUpdate.test.ts +272 -0
  399. package/dist/utils/handleAutoUpdate.ts +145 -0
  400. package/dist/utils/installationInfo.test.ts +315 -0
  401. package/dist/utils/installationInfo.ts +176 -0
  402. package/dist/utils/package.ts +38 -0
  403. package/dist/utils/readStdin.ts +51 -0
  404. package/dist/utils/resolvePath.ts +21 -0
  405. package/dist/utils/sandbox-macos-permissive-closed.sb +32 -0
  406. package/dist/utils/sandbox-macos-permissive-open.sb +25 -0
  407. package/dist/utils/sandbox-macos-permissive-proxied.sb +37 -0
  408. package/dist/utils/sandbox-macos-restrictive-closed.sb +93 -0
  409. package/dist/utils/sandbox-macos-restrictive-open.sb +96 -0
  410. package/dist/utils/sandbox-macos-restrictive-proxied.sb +98 -0
  411. package/dist/utils/sandbox.ts +962 -0
  412. package/dist/utils/settingsUtils.test.ts +797 -0
  413. package/dist/utils/settingsUtils.ts +489 -0
  414. package/dist/utils/spawnWrapper.ts +9 -0
  415. package/dist/utils/startupWarnings.test.ts +83 -0
  416. package/dist/utils/startupWarnings.ts +40 -0
  417. package/dist/utils/updateEventEmitter.ts +13 -0
  418. package/dist/utils/userStartupWarnings.test.ts +87 -0
  419. package/dist/utils/userStartupWarnings.ts +69 -0
  420. package/dist/utils/version.ts +12 -0
  421. package/dist/validateNonInterActiveAuth.test.ts +260 -0
  422. package/dist/validateNonInterActiveAuth.ts +51 -0
  423. package/dist/vitest.config.ts +37 -0
  424. package/dist/zed-integration/acp.ts +366 -0
  425. package/dist/zed-integration/fileSystemService.ts +47 -0
  426. package/dist/zed-integration/schema.ts +466 -0
  427. package/dist/zed-integration/zedIntegration.ts +944 -0
  428. package/package.json +2 -2
@@ -0,0 +1,753 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import React, { useState, useEffect } from 'react';
8
+ import { Box, Text } from 'ink';
9
+ import { Colors } from '../colors.js';
10
+ import {
11
+ LoadedSettings,
12
+ SettingScope,
13
+ Settings,
14
+ } from '../../config/settings.js';
15
+ import {
16
+ getScopeItems,
17
+ getScopeMessageForSetting,
18
+ } from '../../utils/dialogScopeUtils.js';
19
+ import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
20
+ import {
21
+ getDialogSettingKeys,
22
+ getSettingValue,
23
+ setPendingSettingValue,
24
+ getDisplayValue,
25
+ hasRestartRequiredSettings,
26
+ saveModifiedSettings,
27
+ getSettingDefinition,
28
+ isDefaultValue,
29
+ requiresRestart,
30
+ getRestartRequiredFromModified,
31
+ getDefaultValue,
32
+ setPendingSettingValueAny,
33
+ getNestedValue,
34
+ } from '../../utils/settingsUtils.js';
35
+ import { useVimMode } from '../contexts/VimModeContext.js';
36
+ import { useKeypress } from '../hooks/useKeypress.js';
37
+ import chalk from 'chalk';
38
+ import { cpSlice, cpLen } from '../utils/textUtils.js';
39
+
40
+ interface SettingsDialogProps {
41
+ settings: LoadedSettings;
42
+ onSelect: (settingName: string | undefined, scope: SettingScope) => void;
43
+ onRestartRequest?: () => void;
44
+ }
45
+
46
+ const maxItemsToShow = 8;
47
+
48
+ export function SettingsDialog({
49
+ settings,
50
+ onSelect,
51
+ onRestartRequest,
52
+ }: SettingsDialogProps): React.JSX.Element {
53
+ // Get vim mode context to sync vim mode changes
54
+ const { vimEnabled, toggleVimEnabled } = useVimMode();
55
+
56
+ // Focus state: 'settings' or 'scope'
57
+ const [focusSection, setFocusSection] = useState<'settings' | 'scope'>(
58
+ 'settings',
59
+ );
60
+ // Scope selector state (User by default)
61
+ const [selectedScope, setSelectedScope] = useState<SettingScope>(
62
+ SettingScope.User,
63
+ );
64
+ // Active indices
65
+ const [activeSettingIndex, setActiveSettingIndex] = useState(0);
66
+ // Scroll offset for settings
67
+ const [scrollOffset, setScrollOffset] = useState(0);
68
+ const [showRestartPrompt, setShowRestartPrompt] = useState(false);
69
+
70
+ // Local pending settings state for the selected scope
71
+ const [pendingSettings, setPendingSettings] = useState<Settings>(() =>
72
+ // Deep clone to avoid mutation
73
+ structuredClone(settings.forScope(selectedScope).settings),
74
+ );
75
+
76
+ // Track which settings have been modified by the user
77
+ const [modifiedSettings, setModifiedSettings] = useState<Set<string>>(
78
+ new Set(),
79
+ );
80
+
81
+ // Preserve pending changes across scope switches (boolean and number values only)
82
+ type PendingValue = boolean | number;
83
+ const [globalPendingChanges, setGlobalPendingChanges] = useState<
84
+ Map<string, PendingValue>
85
+ >(new Map());
86
+
87
+ // Track restart-required settings across scope changes
88
+ const [_restartRequiredSettings, setRestartRequiredSettings] = useState<
89
+ Set<string>
90
+ >(new Set());
91
+
92
+ useEffect(() => {
93
+ // Base settings for selected scope
94
+ let updated = structuredClone(settings.forScope(selectedScope).settings);
95
+ // Overlay globally pending (unsaved) changes so user sees their modifications in any scope
96
+ const newModified = new Set<string>();
97
+ const newRestartRequired = new Set<string>();
98
+ for (const [key, value] of globalPendingChanges.entries()) {
99
+ const def = getSettingDefinition(key);
100
+ if (def?.type === 'boolean' && typeof value === 'boolean') {
101
+ updated = setPendingSettingValue(key, value, updated);
102
+ } else if (def?.type === 'number' && typeof value === 'number') {
103
+ updated = setPendingSettingValueAny(key, value, updated);
104
+ }
105
+ newModified.add(key);
106
+ if (requiresRestart(key)) newRestartRequired.add(key);
107
+ }
108
+ setPendingSettings(updated);
109
+ setModifiedSettings(newModified);
110
+ setRestartRequiredSettings(newRestartRequired);
111
+ setShowRestartPrompt(newRestartRequired.size > 0);
112
+ }, [selectedScope, settings, globalPendingChanges]);
113
+
114
+ const generateSettingsItems = () => {
115
+ const settingKeys = getDialogSettingKeys();
116
+
117
+ return settingKeys.map((key: string) => {
118
+ const definition = getSettingDefinition(key);
119
+
120
+ return {
121
+ label: definition?.label || key,
122
+ value: key,
123
+ type: definition?.type,
124
+ toggle: () => {
125
+ if (definition?.type !== 'boolean') {
126
+ // For non-boolean (e.g., number) items, toggle will be handled via edit mode.
127
+ return;
128
+ }
129
+ const currentValue = getSettingValue(key, pendingSettings, {});
130
+ const newValue = !currentValue;
131
+
132
+ setPendingSettings((prev) =>
133
+ setPendingSettingValue(key, newValue, prev),
134
+ );
135
+
136
+ if (!requiresRestart(key)) {
137
+ const immediateSettings = new Set([key]);
138
+ const immediateSettingsObject = setPendingSettingValueAny(
139
+ key,
140
+ newValue,
141
+ {} as Settings,
142
+ );
143
+
144
+ console.log(
145
+ `[DEBUG SettingsDialog] Saving ${key} immediately with value:`,
146
+ newValue,
147
+ );
148
+ saveModifiedSettings(
149
+ immediateSettings,
150
+ immediateSettingsObject,
151
+ settings,
152
+ selectedScope,
153
+ );
154
+
155
+ // Special handling for vim mode to sync with VimModeContext
156
+ if (key === 'vimMode' && newValue !== vimEnabled) {
157
+ // Call toggleVimEnabled to sync the VimModeContext local state
158
+ toggleVimEnabled().catch((error) => {
159
+ console.error('Failed to toggle vim mode:', error);
160
+ });
161
+ }
162
+
163
+ // Remove from modifiedSettings since it's now saved
164
+ setModifiedSettings((prev) => {
165
+ const updated = new Set(prev);
166
+ updated.delete(key);
167
+ return updated;
168
+ });
169
+
170
+ // Also remove from restart-required settings if it was there
171
+ setRestartRequiredSettings((prev) => {
172
+ const updated = new Set(prev);
173
+ updated.delete(key);
174
+ return updated;
175
+ });
176
+
177
+ // Remove from global pending changes if present
178
+ setGlobalPendingChanges((prev) => {
179
+ if (!prev.has(key)) return prev;
180
+ const next = new Map(prev);
181
+ next.delete(key);
182
+ return next;
183
+ });
184
+
185
+ // Refresh pending settings from the saved state
186
+ setPendingSettings(
187
+ structuredClone(settings.forScope(selectedScope).settings),
188
+ );
189
+ } else {
190
+ // For restart-required settings, track as modified
191
+ setModifiedSettings((prev) => {
192
+ const updated = new Set(prev).add(key);
193
+ const needsRestart = hasRestartRequiredSettings(updated);
194
+ console.log(
195
+ `[DEBUG SettingsDialog] Modified settings:`,
196
+ Array.from(updated),
197
+ 'Needs restart:',
198
+ needsRestart,
199
+ );
200
+ if (needsRestart) {
201
+ setShowRestartPrompt(true);
202
+ setRestartRequiredSettings((prevRestart) =>
203
+ new Set(prevRestart).add(key),
204
+ );
205
+ }
206
+ return updated;
207
+ });
208
+
209
+ // Add/update pending change globally so it persists across scopes
210
+ setGlobalPendingChanges((prev) => {
211
+ const next = new Map(prev);
212
+ next.set(key, newValue as PendingValue);
213
+ return next;
214
+ });
215
+ }
216
+ },
217
+ };
218
+ });
219
+ };
220
+
221
+ const items = generateSettingsItems();
222
+
223
+ // Number edit state
224
+ const [editingKey, setEditingKey] = useState<string | null>(null);
225
+ const [editBuffer, setEditBuffer] = useState<string>('');
226
+ const [editCursorPos, setEditCursorPos] = useState<number>(0); // Cursor position within edit buffer
227
+ const [cursorVisible, setCursorVisible] = useState<boolean>(true);
228
+
229
+ useEffect(() => {
230
+ if (!editingKey) {
231
+ setCursorVisible(true);
232
+ return;
233
+ }
234
+ const id = setInterval(() => setCursorVisible((v) => !v), 500);
235
+ return () => clearInterval(id);
236
+ }, [editingKey]);
237
+
238
+ const startEditingNumber = (key: string, initial?: string) => {
239
+ setEditingKey(key);
240
+ const initialValue = initial ?? '';
241
+ setEditBuffer(initialValue);
242
+ setEditCursorPos(cpLen(initialValue)); // Position cursor at end of initial value
243
+ };
244
+
245
+ const commitNumberEdit = (key: string) => {
246
+ if (editBuffer.trim() === '') {
247
+ // Nothing entered; cancel edit
248
+ setEditingKey(null);
249
+ setEditBuffer('');
250
+ setEditCursorPos(0);
251
+ return;
252
+ }
253
+ const parsed = Number(editBuffer.trim());
254
+ if (Number.isNaN(parsed)) {
255
+ // Invalid number; cancel edit
256
+ setEditingKey(null);
257
+ setEditBuffer('');
258
+ setEditCursorPos(0);
259
+ return;
260
+ }
261
+
262
+ // Update pending
263
+ setPendingSettings((prev) => setPendingSettingValueAny(key, parsed, prev));
264
+
265
+ if (!requiresRestart(key)) {
266
+ const immediateSettings = new Set([key]);
267
+ const immediateSettingsObject = setPendingSettingValueAny(
268
+ key,
269
+ parsed,
270
+ {} as Settings,
271
+ );
272
+ saveModifiedSettings(
273
+ immediateSettings,
274
+ immediateSettingsObject,
275
+ settings,
276
+ selectedScope,
277
+ );
278
+
279
+ // Remove from modified sets if present
280
+ setModifiedSettings((prev) => {
281
+ const updated = new Set(prev);
282
+ updated.delete(key);
283
+ return updated;
284
+ });
285
+ setRestartRequiredSettings((prev) => {
286
+ const updated = new Set(prev);
287
+ updated.delete(key);
288
+ return updated;
289
+ });
290
+
291
+ // Remove from global pending since it's immediately saved
292
+ setGlobalPendingChanges((prev) => {
293
+ if (!prev.has(key)) return prev;
294
+ const next = new Map(prev);
295
+ next.delete(key);
296
+ return next;
297
+ });
298
+ } else {
299
+ // Mark as modified and needing restart
300
+ setModifiedSettings((prev) => {
301
+ const updated = new Set(prev).add(key);
302
+ const needsRestart = hasRestartRequiredSettings(updated);
303
+ if (needsRestart) {
304
+ setShowRestartPrompt(true);
305
+ setRestartRequiredSettings((prevRestart) =>
306
+ new Set(prevRestart).add(key),
307
+ );
308
+ }
309
+ return updated;
310
+ });
311
+
312
+ // Record pending change globally for persistence across scopes
313
+ setGlobalPendingChanges((prev) => {
314
+ const next = new Map(prev);
315
+ next.set(key, parsed as PendingValue);
316
+ return next;
317
+ });
318
+ }
319
+
320
+ setEditingKey(null);
321
+ setEditBuffer('');
322
+ setEditCursorPos(0);
323
+ };
324
+
325
+ // Scope selector items
326
+ const scopeItems = getScopeItems();
327
+
328
+ const handleScopeHighlight = (scope: SettingScope) => {
329
+ setSelectedScope(scope);
330
+ };
331
+
332
+ const handleScopeSelect = (scope: SettingScope) => {
333
+ handleScopeHighlight(scope);
334
+ setFocusSection('settings');
335
+ };
336
+
337
+ // Scroll logic for settings
338
+ const visibleItems = items.slice(scrollOffset, scrollOffset + maxItemsToShow);
339
+ // Always show arrows for consistent UI and to indicate circular navigation
340
+ const showScrollUp = true;
341
+ const showScrollDown = true;
342
+
343
+ useKeypress(
344
+ (key) => {
345
+ const { name, ctrl } = key;
346
+ if (name === 'tab') {
347
+ setFocusSection((prev) => (prev === 'settings' ? 'scope' : 'settings'));
348
+ }
349
+ if (focusSection === 'settings') {
350
+ // If editing a number, capture numeric input and control keys
351
+ if (editingKey) {
352
+ if (key.paste && key.sequence) {
353
+ const pasted = key.sequence.replace(/[^0-9\-+.]/g, '');
354
+ if (pasted) {
355
+ setEditBuffer((b) => {
356
+ const before = cpSlice(b, 0, editCursorPos);
357
+ const after = cpSlice(b, editCursorPos);
358
+ return before + pasted + after;
359
+ });
360
+ setEditCursorPos((pos) => pos + cpLen(pasted));
361
+ }
362
+ return;
363
+ }
364
+ if (name === 'backspace' || name === 'delete') {
365
+ if (name === 'backspace' && editCursorPos > 0) {
366
+ setEditBuffer((b) => {
367
+ const before = cpSlice(b, 0, editCursorPos - 1);
368
+ const after = cpSlice(b, editCursorPos);
369
+ return before + after;
370
+ });
371
+ setEditCursorPos((pos) => pos - 1);
372
+ } else if (name === 'delete' && editCursorPos < cpLen(editBuffer)) {
373
+ setEditBuffer((b) => {
374
+ const before = cpSlice(b, 0, editCursorPos);
375
+ const after = cpSlice(b, editCursorPos + 1);
376
+ return before + after;
377
+ });
378
+ // Cursor position stays the same for delete
379
+ }
380
+ return;
381
+ }
382
+ if (name === 'escape') {
383
+ commitNumberEdit(editingKey);
384
+ return;
385
+ }
386
+ if (name === 'return') {
387
+ commitNumberEdit(editingKey);
388
+ return;
389
+ }
390
+ // Allow digits, minus, plus, and dot
391
+ const ch = key.sequence;
392
+ if (/[0-9\-+.]/.test(ch)) {
393
+ setEditBuffer((currentBuffer) => {
394
+ const beforeCursor = cpSlice(currentBuffer, 0, editCursorPos);
395
+ const afterCursor = cpSlice(currentBuffer, editCursorPos);
396
+ return beforeCursor + ch + afterCursor;
397
+ });
398
+ setEditCursorPos((pos) => pos + 1);
399
+ return;
400
+ }
401
+ // Arrow key navigation
402
+ if (name === 'left') {
403
+ setEditCursorPos((pos) => Math.max(0, pos - 1));
404
+ return;
405
+ }
406
+ if (name === 'right') {
407
+ setEditCursorPos((pos) => Math.min(cpLen(editBuffer), pos + 1));
408
+ return;
409
+ }
410
+ // Home and End keys
411
+ if (name === 'home') {
412
+ setEditCursorPos(0);
413
+ return;
414
+ }
415
+ if (name === 'end') {
416
+ setEditCursorPos(cpLen(editBuffer));
417
+ return;
418
+ }
419
+ // Block other keys while editing
420
+ return;
421
+ }
422
+ if (name === 'up' || name === 'k') {
423
+ // If editing, commit first
424
+ if (editingKey) {
425
+ commitNumberEdit(editingKey);
426
+ }
427
+ const newIndex =
428
+ activeSettingIndex > 0 ? activeSettingIndex - 1 : items.length - 1;
429
+ setActiveSettingIndex(newIndex);
430
+ // Adjust scroll offset for wrap-around
431
+ if (newIndex === items.length - 1) {
432
+ setScrollOffset(Math.max(0, items.length - maxItemsToShow));
433
+ } else if (newIndex < scrollOffset) {
434
+ setScrollOffset(newIndex);
435
+ }
436
+ } else if (name === 'down' || name === 'j') {
437
+ // If editing, commit first
438
+ if (editingKey) {
439
+ commitNumberEdit(editingKey);
440
+ }
441
+ const newIndex =
442
+ activeSettingIndex < items.length - 1 ? activeSettingIndex + 1 : 0;
443
+ setActiveSettingIndex(newIndex);
444
+ // Adjust scroll offset for wrap-around
445
+ if (newIndex === 0) {
446
+ setScrollOffset(0);
447
+ } else if (newIndex >= scrollOffset + maxItemsToShow) {
448
+ setScrollOffset(newIndex - maxItemsToShow + 1);
449
+ }
450
+ } else if (name === 'return' || name === 'space') {
451
+ const currentItem = items[activeSettingIndex];
452
+ if (currentItem?.type === 'number') {
453
+ startEditingNumber(currentItem.value);
454
+ } else {
455
+ currentItem?.toggle();
456
+ }
457
+ } else if (/^[0-9]$/.test(key.sequence || '') && !editingKey) {
458
+ const currentItem = items[activeSettingIndex];
459
+ if (currentItem?.type === 'number') {
460
+ startEditingNumber(currentItem.value, key.sequence);
461
+ }
462
+ } else if (ctrl && (name === 'c' || name === 'l')) {
463
+ // Ctrl+C or Ctrl+L: Clear current setting and reset to default
464
+ const currentSetting = items[activeSettingIndex];
465
+ if (currentSetting) {
466
+ const defaultValue = getDefaultValue(currentSetting.value);
467
+ const defType = currentSetting.type;
468
+ if (defType === 'boolean') {
469
+ const booleanDefaultValue =
470
+ typeof defaultValue === 'boolean' ? defaultValue : false;
471
+ setPendingSettings((prev) =>
472
+ setPendingSettingValue(
473
+ currentSetting.value,
474
+ booleanDefaultValue,
475
+ prev,
476
+ ),
477
+ );
478
+ } else if (defType === 'number') {
479
+ if (typeof defaultValue === 'number') {
480
+ setPendingSettings((prev) =>
481
+ setPendingSettingValueAny(
482
+ currentSetting.value,
483
+ defaultValue,
484
+ prev,
485
+ ),
486
+ );
487
+ }
488
+ }
489
+
490
+ // Remove from modified settings since it's now at default
491
+ setModifiedSettings((prev) => {
492
+ const updated = new Set(prev);
493
+ updated.delete(currentSetting.value);
494
+ return updated;
495
+ });
496
+
497
+ // Remove from restart-required settings if it was there
498
+ setRestartRequiredSettings((prev) => {
499
+ const updated = new Set(prev);
500
+ updated.delete(currentSetting.value);
501
+ return updated;
502
+ });
503
+
504
+ // If this setting doesn't require restart, save it immediately
505
+ if (!requiresRestart(currentSetting.value)) {
506
+ const immediateSettings = new Set([currentSetting.value]);
507
+ const toSaveValue =
508
+ currentSetting.type === 'boolean'
509
+ ? typeof defaultValue === 'boolean'
510
+ ? defaultValue
511
+ : false
512
+ : typeof defaultValue === 'number'
513
+ ? defaultValue
514
+ : undefined;
515
+ const immediateSettingsObject =
516
+ toSaveValue !== undefined
517
+ ? setPendingSettingValueAny(
518
+ currentSetting.value,
519
+ toSaveValue,
520
+ {} as Settings,
521
+ )
522
+ : ({} as Settings);
523
+
524
+ saveModifiedSettings(
525
+ immediateSettings,
526
+ immediateSettingsObject,
527
+ settings,
528
+ selectedScope,
529
+ );
530
+
531
+ // Remove from global pending changes if present
532
+ setGlobalPendingChanges((prev) => {
533
+ if (!prev.has(currentSetting.value)) return prev;
534
+ const next = new Map(prev);
535
+ next.delete(currentSetting.value);
536
+ return next;
537
+ });
538
+ } else {
539
+ // Track default reset as a pending change if restart required
540
+ if (
541
+ (currentSetting.type === 'boolean' &&
542
+ typeof defaultValue === 'boolean') ||
543
+ (currentSetting.type === 'number' &&
544
+ typeof defaultValue === 'number')
545
+ ) {
546
+ setGlobalPendingChanges((prev) => {
547
+ const next = new Map(prev);
548
+ next.set(currentSetting.value, defaultValue as PendingValue);
549
+ return next;
550
+ });
551
+ }
552
+ }
553
+ }
554
+ }
555
+ }
556
+ if (showRestartPrompt && name === 'r') {
557
+ // Only save settings that require restart (non-restart settings were already saved immediately)
558
+ const restartRequiredSettings =
559
+ getRestartRequiredFromModified(modifiedSettings);
560
+ const restartRequiredSet = new Set(restartRequiredSettings);
561
+
562
+ if (restartRequiredSet.size > 0) {
563
+ saveModifiedSettings(
564
+ restartRequiredSet,
565
+ pendingSettings,
566
+ settings,
567
+ selectedScope,
568
+ );
569
+
570
+ // Remove saved keys from global pending changes
571
+ setGlobalPendingChanges((prev) => {
572
+ if (prev.size === 0) return prev;
573
+ const next = new Map(prev);
574
+ for (const key of restartRequiredSet) {
575
+ next.delete(key);
576
+ }
577
+ return next;
578
+ });
579
+ }
580
+
581
+ setShowRestartPrompt(false);
582
+ setRestartRequiredSettings(new Set()); // Clear restart-required settings
583
+ if (onRestartRequest) onRestartRequest();
584
+ }
585
+ if (name === 'escape') {
586
+ if (editingKey) {
587
+ commitNumberEdit(editingKey);
588
+ } else {
589
+ onSelect(undefined, selectedScope);
590
+ }
591
+ }
592
+ },
593
+ { isActive: true },
594
+ );
595
+
596
+ return (
597
+ <Box
598
+ borderStyle="round"
599
+ borderColor={Colors.Gray}
600
+ flexDirection="row"
601
+ padding={1}
602
+ width="100%"
603
+ height="100%"
604
+ >
605
+ <Box flexDirection="column" flexGrow={1}>
606
+ <Text bold color={Colors.AccentBlue}>
607
+ Settings
608
+ </Text>
609
+ <Box height={1} />
610
+ {showScrollUp && <Text color={Colors.Gray}>▲</Text>}
611
+ {visibleItems.map((item, idx) => {
612
+ const isActive =
613
+ focusSection === 'settings' &&
614
+ activeSettingIndex === idx + scrollOffset;
615
+
616
+ const scopeSettings = settings.forScope(selectedScope).settings;
617
+ const mergedSettings = settings.merged;
618
+
619
+ let displayValue: string;
620
+ if (editingKey === item.value) {
621
+ // Show edit buffer with advanced cursor highlighting
622
+ if (cursorVisible && editCursorPos < cpLen(editBuffer)) {
623
+ // Cursor is in the middle or at start of text
624
+ const beforeCursor = cpSlice(editBuffer, 0, editCursorPos);
625
+ const atCursor = cpSlice(
626
+ editBuffer,
627
+ editCursorPos,
628
+ editCursorPos + 1,
629
+ );
630
+ const afterCursor = cpSlice(editBuffer, editCursorPos + 1);
631
+ displayValue =
632
+ beforeCursor + chalk.inverse(atCursor) + afterCursor;
633
+ } else if (cursorVisible && editCursorPos >= cpLen(editBuffer)) {
634
+ // Cursor is at the end - show inverted space
635
+ displayValue = editBuffer + chalk.inverse(' ');
636
+ } else {
637
+ // Cursor not visible
638
+ displayValue = editBuffer;
639
+ }
640
+ } else if (item.type === 'number') {
641
+ // For numbers, get the actual current value from pending settings
642
+ const path = item.value.split('.');
643
+ const currentValue = getNestedValue(pendingSettings, path);
644
+
645
+ const defaultValue = getDefaultValue(item.value);
646
+
647
+ if (currentValue !== undefined && currentValue !== null) {
648
+ displayValue = String(currentValue);
649
+ } else {
650
+ displayValue =
651
+ defaultValue !== undefined && defaultValue !== null
652
+ ? String(defaultValue)
653
+ : '';
654
+ }
655
+
656
+ // Add * if value differs from default OR if currently being modified
657
+ const isModified = modifiedSettings.has(item.value);
658
+ const effectiveCurrentValue =
659
+ currentValue !== undefined && currentValue !== null
660
+ ? currentValue
661
+ : defaultValue;
662
+ const isDifferentFromDefault =
663
+ effectiveCurrentValue !== defaultValue;
664
+
665
+ if (isDifferentFromDefault || isModified) {
666
+ displayValue += '*';
667
+ }
668
+ } else {
669
+ // For booleans and other types, use existing logic
670
+ displayValue = getDisplayValue(
671
+ item.value,
672
+ scopeSettings,
673
+ mergedSettings,
674
+ modifiedSettings,
675
+ pendingSettings,
676
+ );
677
+ }
678
+ const shouldBeGreyedOut = isDefaultValue(item.value, scopeSettings);
679
+
680
+ // Generate scope message for this setting
681
+ const scopeMessage = getScopeMessageForSetting(
682
+ item.value,
683
+ selectedScope,
684
+ settings,
685
+ );
686
+
687
+ return (
688
+ <React.Fragment key={item.value}>
689
+ <Box flexDirection="row" alignItems="center">
690
+ <Box minWidth={2} flexShrink={0}>
691
+ <Text color={isActive ? Colors.AccentGreen : Colors.Gray}>
692
+ {isActive ? '●' : ''}
693
+ </Text>
694
+ </Box>
695
+ <Box minWidth={50}>
696
+ <Text
697
+ color={isActive ? Colors.AccentGreen : Colors.Foreground}
698
+ >
699
+ {item.label}
700
+ {scopeMessage && (
701
+ <Text color={Colors.Gray}> {scopeMessage}</Text>
702
+ )}
703
+ </Text>
704
+ </Box>
705
+ <Box minWidth={3} />
706
+ <Text
707
+ color={
708
+ isActive
709
+ ? Colors.AccentGreen
710
+ : shouldBeGreyedOut
711
+ ? Colors.Gray
712
+ : Colors.Foreground
713
+ }
714
+ >
715
+ {displayValue}
716
+ </Text>
717
+ </Box>
718
+ <Box height={1} />
719
+ </React.Fragment>
720
+ );
721
+ })}
722
+ {showScrollDown && <Text color={Colors.Gray}>▼</Text>}
723
+
724
+ <Box height={1} />
725
+
726
+ <Box marginTop={1} flexDirection="column">
727
+ <Text bold={focusSection === 'scope'} wrap="truncate">
728
+ {focusSection === 'scope' ? '> ' : ' '}Apply To
729
+ </Text>
730
+ <RadioButtonSelect
731
+ items={scopeItems}
732
+ initialIndex={0}
733
+ onSelect={handleScopeSelect}
734
+ onHighlight={handleScopeHighlight}
735
+ isFocused={focusSection === 'scope'}
736
+ showNumbers={focusSection === 'scope'}
737
+ />
738
+ </Box>
739
+
740
+ <Box height={1} />
741
+ <Text color={Colors.Gray}>
742
+ (Use Enter to select, Tab to change focus)
743
+ </Text>
744
+ {showRestartPrompt && (
745
+ <Text color={Colors.AccentYellow}>
746
+ To see changes, Gemini CLI must be restarted. Press r to exit and
747
+ apply changes now.
748
+ </Text>
749
+ )}
750
+ </Box>
751
+ </Box>
752
+ );
753
+ }