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,273 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import React from 'react';
8
+ import { Box, Text } from 'ink';
9
+ import Gradient from 'ink-gradient';
10
+ import { theme } from '../semantic-colors.js';
11
+ import { formatDuration } from '../utils/formatters.js';
12
+ import { useSessionStats, ModelMetrics } from '../contexts/SessionContext.js';
13
+ import {
14
+ getStatusColor,
15
+ TOOL_SUCCESS_RATE_HIGH,
16
+ TOOL_SUCCESS_RATE_MEDIUM,
17
+ USER_AGREEMENT_RATE_HIGH,
18
+ USER_AGREEMENT_RATE_MEDIUM,
19
+ } from '../utils/displayUtils.js';
20
+ import { computeSessionStats } from '../utils/computeStats.js';
21
+
22
+ // A more flexible and powerful StatRow component
23
+ interface StatRowProps {
24
+ title: string;
25
+ children: React.ReactNode; // Use children to allow for complex, colored values
26
+ }
27
+
28
+ const StatRow: React.FC<StatRowProps> = ({ title, children }) => (
29
+ <Box>
30
+ {/* Fixed width for the label creates a clean "gutter" for alignment */}
31
+ <Box width={28}>
32
+ <Text color={theme.text.link}>{title}</Text>
33
+ </Box>
34
+ {children}
35
+ </Box>
36
+ );
37
+
38
+ // A SubStatRow for indented, secondary information
39
+ interface SubStatRowProps {
40
+ title: string;
41
+ children: React.ReactNode;
42
+ }
43
+
44
+ const SubStatRow: React.FC<SubStatRowProps> = ({ title, children }) => (
45
+ <Box paddingLeft={2}>
46
+ {/* Adjust width for the "» " prefix */}
47
+ <Box width={26}>
48
+ <Text>» {title}</Text>
49
+ </Box>
50
+ {children}
51
+ </Box>
52
+ );
53
+
54
+ // A Section component to group related stats
55
+ interface SectionProps {
56
+ title: string;
57
+ children: React.ReactNode;
58
+ }
59
+
60
+ const Section: React.FC<SectionProps> = ({ title, children }) => (
61
+ <Box flexDirection="column" width="100%" marginBottom={1}>
62
+ <Text bold>{title}</Text>
63
+ {children}
64
+ </Box>
65
+ );
66
+
67
+ const ModelUsageTable: React.FC<{
68
+ models: Record<string, ModelMetrics>;
69
+ totalCachedTokens: number;
70
+ cacheEfficiency: number;
71
+ }> = ({ models, totalCachedTokens, cacheEfficiency }) => {
72
+ const nameWidth = 25;
73
+ const requestsWidth = 8;
74
+ const inputTokensWidth = 15;
75
+ const outputTokensWidth = 15;
76
+
77
+ return (
78
+ <Box flexDirection="column" marginTop={1}>
79
+ {/* Header */}
80
+ <Box>
81
+ <Box width={nameWidth}>
82
+ <Text bold>Model Usage</Text>
83
+ </Box>
84
+ <Box width={requestsWidth} justifyContent="flex-end">
85
+ <Text bold>Reqs</Text>
86
+ </Box>
87
+ <Box width={inputTokensWidth} justifyContent="flex-end">
88
+ <Text bold>Input Tokens</Text>
89
+ </Box>
90
+ <Box width={outputTokensWidth} justifyContent="flex-end">
91
+ <Text bold>Output Tokens</Text>
92
+ </Box>
93
+ </Box>
94
+ {/* Divider */}
95
+ <Box
96
+ borderStyle="round"
97
+ borderBottom={true}
98
+ borderTop={false}
99
+ borderLeft={false}
100
+ borderRight={false}
101
+ width={nameWidth + requestsWidth + inputTokensWidth + outputTokensWidth}
102
+ ></Box>
103
+
104
+ {/* Rows */}
105
+ {Object.entries(models).map(([name, modelMetrics]) => (
106
+ <Box key={name}>
107
+ <Box width={nameWidth}>
108
+ <Text>{name.replace('-001', '')}</Text>
109
+ </Box>
110
+ <Box width={requestsWidth} justifyContent="flex-end">
111
+ <Text>{modelMetrics.api.totalRequests}</Text>
112
+ </Box>
113
+ <Box width={inputTokensWidth} justifyContent="flex-end">
114
+ <Text color={theme.status.warning}>
115
+ {modelMetrics.tokens.prompt.toLocaleString()}
116
+ </Text>
117
+ </Box>
118
+ <Box width={outputTokensWidth} justifyContent="flex-end">
119
+ <Text color={theme.status.warning}>
120
+ {modelMetrics.tokens.candidates.toLocaleString()}
121
+ </Text>
122
+ </Box>
123
+ </Box>
124
+ ))}
125
+ {cacheEfficiency > 0 && (
126
+ <Box flexDirection="column" marginTop={1}>
127
+ <Text>
128
+ <Text color={theme.status.success}>Savings Highlight:</Text>{' '}
129
+ {totalCachedTokens.toLocaleString()} ({cacheEfficiency.toFixed(1)}
130
+ %) of input tokens were served from the cache, reducing costs.
131
+ </Text>
132
+ <Box height={1} />
133
+ <Text color={theme.text.secondary}>
134
+ » Tip: For a full token breakdown, run `/stats model`.
135
+ </Text>
136
+ </Box>
137
+ )}
138
+ </Box>
139
+ );
140
+ };
141
+
142
+ interface StatsDisplayProps {
143
+ duration: string;
144
+ title?: string;
145
+ }
146
+
147
+ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
148
+ duration,
149
+ title,
150
+ }) => {
151
+ const { stats } = useSessionStats();
152
+ const { metrics } = stats;
153
+ const { models, tools, files } = metrics;
154
+ const computed = computeSessionStats(metrics);
155
+
156
+ const successThresholds = {
157
+ green: TOOL_SUCCESS_RATE_HIGH,
158
+ yellow: TOOL_SUCCESS_RATE_MEDIUM,
159
+ };
160
+ const agreementThresholds = {
161
+ green: USER_AGREEMENT_RATE_HIGH,
162
+ yellow: USER_AGREEMENT_RATE_MEDIUM,
163
+ };
164
+ const successColor = getStatusColor(computed.successRate, successThresholds);
165
+ const agreementColor = getStatusColor(
166
+ computed.agreementRate,
167
+ agreementThresholds,
168
+ );
169
+
170
+ const renderTitle = () => {
171
+ if (title) {
172
+ return theme.ui.gradient && theme.ui.gradient.length > 0 ? (
173
+ <Gradient colors={theme.ui.gradient}>
174
+ <Text bold>{title}</Text>
175
+ </Gradient>
176
+ ) : (
177
+ <Text bold color={theme.text.accent}>
178
+ {title}
179
+ </Text>
180
+ );
181
+ }
182
+ return (
183
+ <Text bold color={theme.text.accent}>
184
+ Session Stats
185
+ </Text>
186
+ );
187
+ };
188
+
189
+ return (
190
+ <Box
191
+ borderStyle="round"
192
+ borderColor={theme.border.default}
193
+ flexDirection="column"
194
+ paddingY={1}
195
+ paddingX={2}
196
+ >
197
+ {renderTitle()}
198
+ <Box height={1} />
199
+
200
+ <Section title="Interaction Summary">
201
+ <StatRow title="Session ID:">
202
+ <Text>{stats.sessionId}</Text>
203
+ </StatRow>
204
+ <StatRow title="Tool Calls:">
205
+ <Text>
206
+ {tools.totalCalls} ({' '}
207
+ <Text color={theme.status.success}>✔ {tools.totalSuccess}</Text>{' '}
208
+ <Text color={theme.status.error}>✖ {tools.totalFail}</Text> )
209
+ </Text>
210
+ </StatRow>
211
+ <StatRow title="Success Rate:">
212
+ <Text color={successColor}>{computed.successRate.toFixed(1)}%</Text>
213
+ </StatRow>
214
+ {computed.totalDecisions > 0 && (
215
+ <StatRow title="User Agreement:">
216
+ <Text color={agreementColor}>
217
+ {computed.agreementRate.toFixed(1)}%{' '}
218
+ <Text color={theme.text.secondary}>
219
+ ({computed.totalDecisions} reviewed)
220
+ </Text>
221
+ </Text>
222
+ </StatRow>
223
+ )}
224
+ {files &&
225
+ (files.totalLinesAdded > 0 || files.totalLinesRemoved > 0) && (
226
+ <StatRow title="Code Changes:">
227
+ <Text>
228
+ <Text color={theme.status.success}>
229
+ +{files.totalLinesAdded}
230
+ </Text>{' '}
231
+ <Text color={theme.status.error}>
232
+ -{files.totalLinesRemoved}
233
+ </Text>
234
+ </Text>
235
+ </StatRow>
236
+ )}
237
+ </Section>
238
+
239
+ <Section title="Performance">
240
+ <StatRow title="Wall Time:">
241
+ <Text>{duration}</Text>
242
+ </StatRow>
243
+ <StatRow title="Agent Active:">
244
+ <Text>{formatDuration(computed.agentActiveTime)}</Text>
245
+ </StatRow>
246
+ <SubStatRow title="API Time:">
247
+ <Text>
248
+ {formatDuration(computed.totalApiTime)}{' '}
249
+ <Text color={theme.text.secondary}>
250
+ ({computed.apiTimePercent.toFixed(1)}%)
251
+ </Text>
252
+ </Text>
253
+ </SubStatRow>
254
+ <SubStatRow title="Tool Time:">
255
+ <Text>
256
+ {formatDuration(computed.totalToolTime)}{' '}
257
+ <Text color={theme.text.secondary}>
258
+ ({computed.toolTimePercent.toFixed(1)}%)
259
+ </Text>
260
+ </Text>
261
+ </SubStatRow>
262
+ </Section>
263
+
264
+ {Object.keys(models).length > 0 && (
265
+ <ModelUsageTable
266
+ models={models}
267
+ totalCachedTokens={computed.totalCachedTokens}
268
+ cacheEfficiency={computed.cacheEfficiency}
269
+ />
270
+ )}
271
+ </Box>
272
+ );
273
+ };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { Box, Text } from 'ink';
8
+ import { Colors } from '../colors.js';
9
+ import { PrepareLabel } from './PrepareLabel.js';
10
+ export interface Suggestion {
11
+ label: string;
12
+ value: string;
13
+ description?: string;
14
+ matchedIndex?: number;
15
+ }
16
+ interface SuggestionsDisplayProps {
17
+ suggestions: Suggestion[];
18
+ activeIndex: number;
19
+ isLoading: boolean;
20
+ width: number;
21
+ scrollOffset: number;
22
+ userInput: string;
23
+ }
24
+
25
+ export const MAX_SUGGESTIONS_TO_SHOW = 8;
26
+
27
+ export function SuggestionsDisplay({
28
+ suggestions,
29
+ activeIndex,
30
+ isLoading,
31
+ width,
32
+ scrollOffset,
33
+ userInput,
34
+ }: SuggestionsDisplayProps) {
35
+ if (isLoading) {
36
+ return (
37
+ <Box paddingX={1} width={width}>
38
+ <Text color="gray">Loading suggestions...</Text>
39
+ </Box>
40
+ );
41
+ }
42
+
43
+ if (suggestions.length === 0) {
44
+ return null; // Don't render anything if there are no suggestions
45
+ }
46
+
47
+ // Calculate the visible slice based on scrollOffset
48
+ const startIndex = scrollOffset;
49
+ const endIndex = Math.min(
50
+ scrollOffset + MAX_SUGGESTIONS_TO_SHOW,
51
+ suggestions.length,
52
+ );
53
+ const visibleSuggestions = suggestions.slice(startIndex, endIndex);
54
+
55
+ return (
56
+ <Box flexDirection="column" paddingX={1} width={width}>
57
+ {scrollOffset > 0 && <Text color={Colors.Foreground}>▲</Text>}
58
+
59
+ {visibleSuggestions.map((suggestion, index) => {
60
+ const originalIndex = startIndex + index;
61
+ const isActive = originalIndex === activeIndex;
62
+ const textColor = isActive ? Colors.AccentPurple : Colors.Gray;
63
+ const labelElement = (
64
+ <PrepareLabel
65
+ label={suggestion.label}
66
+ matchedIndex={suggestion.matchedIndex}
67
+ userInput={userInput}
68
+ textColor={textColor}
69
+ />
70
+ );
71
+
72
+ return (
73
+ <Box key={`${suggestion.value}-${originalIndex}`} width={width}>
74
+ <Box flexDirection="row">
75
+ {userInput.startsWith('/') ? (
76
+ // only use box model for (/) command mode
77
+ <Box width={20} flexShrink={0}>
78
+ {labelElement}
79
+ </Box>
80
+ ) : (
81
+ labelElement
82
+ )}
83
+ {suggestion.description ? (
84
+ <Box flexGrow={1}>
85
+ <Text color={textColor} wrap="truncate">
86
+ {suggestion.description}
87
+ </Text>
88
+ </Box>
89
+ ) : null}
90
+ </Box>
91
+ </Box>
92
+ );
93
+ })}
94
+ {endIndex < suggestions.length && <Text color="gray">▼</Text>}
95
+ {suggestions.length > MAX_SUGGESTIONS_TO_SHOW && (
96
+ <Text color="gray">
97
+ ({activeIndex + 1}/{suggestions.length})
98
+ </Text>
99
+ )}
100
+ </Box>
101
+ );
102
+ }
@@ -0,0 +1,310 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import React, { useCallback, useState } from 'react';
8
+ import { Box, Text } from 'ink';
9
+ import { Colors } from '../colors.js';
10
+ import { themeManager, DEFAULT_THEME } from '../themes/theme-manager.js';
11
+ import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
12
+ import { DiffRenderer } from './messages/DiffRenderer.js';
13
+ import { colorizeCode } from '../utils/CodeColorizer.js';
14
+ import { LoadedSettings, SettingScope } from '../../config/settings.js';
15
+ import {
16
+ getScopeItems,
17
+ getScopeMessageForSetting,
18
+ } from '../../utils/dialogScopeUtils.js';
19
+ import { useKeypress } from '../hooks/useKeypress.js';
20
+
21
+ interface ThemeDialogProps {
22
+ /** Callback function when a theme is selected */
23
+ onSelect: (themeName: string | undefined, scope: SettingScope) => void;
24
+
25
+ /** Callback function when a theme is highlighted */
26
+ onHighlight: (themeName: string | undefined) => void;
27
+ /** The settings object */
28
+ settings: LoadedSettings;
29
+ availableTerminalHeight?: number;
30
+ terminalWidth: number;
31
+ }
32
+
33
+ export function ThemeDialog({
34
+ onSelect,
35
+ onHighlight,
36
+ settings,
37
+ availableTerminalHeight,
38
+ terminalWidth,
39
+ }: ThemeDialogProps): React.JSX.Element {
40
+ const [selectedScope, setSelectedScope] = useState<SettingScope>(
41
+ SettingScope.User,
42
+ );
43
+
44
+ // Track the currently highlighted theme name
45
+ const [highlightedThemeName, setHighlightedThemeName] = useState<
46
+ string | undefined
47
+ >(settings.merged.theme || DEFAULT_THEME.name);
48
+
49
+ // Generate theme items filtered by selected scope
50
+ const customThemes =
51
+ selectedScope === SettingScope.User
52
+ ? settings.user.settings.customThemes || {}
53
+ : settings.merged.customThemes || {};
54
+ const builtInThemes = themeManager
55
+ .getAvailableThemes()
56
+ .filter((theme) => theme.type !== 'custom');
57
+ const customThemeNames = Object.keys(customThemes);
58
+ const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
59
+ // Generate theme items
60
+ const themeItems = [
61
+ ...builtInThemes.map((theme) => ({
62
+ label: theme.name,
63
+ value: theme.name,
64
+ themeNameDisplay: theme.name,
65
+ themeTypeDisplay: capitalize(theme.type),
66
+ })),
67
+ ...customThemeNames.map((name) => ({
68
+ label: name,
69
+ value: name,
70
+ themeNameDisplay: name,
71
+ themeTypeDisplay: 'Custom',
72
+ })),
73
+ ];
74
+ const [selectInputKey, setSelectInputKey] = useState(Date.now());
75
+
76
+ // Find the index of the selected theme, but only if it exists in the list
77
+ const selectedThemeName = settings.merged.theme || DEFAULT_THEME.name;
78
+ const initialThemeIndex = themeItems.findIndex(
79
+ (item) => item.value === selectedThemeName,
80
+ );
81
+ // If not found, fall back to the first theme
82
+ const safeInitialThemeIndex = initialThemeIndex >= 0 ? initialThemeIndex : 0;
83
+
84
+ const scopeItems = getScopeItems();
85
+
86
+ const handleThemeSelect = useCallback(
87
+ (themeName: string) => {
88
+ onSelect(themeName, selectedScope);
89
+ },
90
+ [onSelect, selectedScope],
91
+ );
92
+
93
+ const handleThemeHighlight = (themeName: string) => {
94
+ setHighlightedThemeName(themeName);
95
+ onHighlight(themeName);
96
+ };
97
+
98
+ const handleScopeHighlight = useCallback((scope: SettingScope) => {
99
+ setSelectedScope(scope);
100
+ setSelectInputKey(Date.now());
101
+ }, []);
102
+
103
+ const handleScopeSelect = useCallback(
104
+ (scope: SettingScope) => {
105
+ handleScopeHighlight(scope);
106
+ setFocusedSection('theme'); // Reset focus to theme section
107
+ },
108
+ [handleScopeHighlight],
109
+ );
110
+
111
+ const [focusedSection, setFocusedSection] = useState<'theme' | 'scope'>(
112
+ 'theme',
113
+ );
114
+
115
+ useKeypress(
116
+ (key) => {
117
+ if (key.name === 'tab') {
118
+ setFocusedSection((prev) => (prev === 'theme' ? 'scope' : 'theme'));
119
+ }
120
+ if (key.name === 'escape') {
121
+ onSelect(undefined, selectedScope);
122
+ }
123
+ },
124
+ { isActive: true },
125
+ );
126
+
127
+ // Generate scope message for theme setting
128
+ const otherScopeModifiedMessage = getScopeMessageForSetting(
129
+ 'theme',
130
+ selectedScope,
131
+ settings,
132
+ );
133
+
134
+ // Constants for calculating preview pane layout.
135
+ // These values are based on the JSX structure below.
136
+ const PREVIEW_PANE_WIDTH_PERCENTAGE = 0.55;
137
+ // A safety margin to prevent text from touching the border.
138
+ // This is a complete hack unrelated to the 0.9 used in App.tsx
139
+ const PREVIEW_PANE_WIDTH_SAFETY_MARGIN = 0.9;
140
+ // Combined horizontal padding from the dialog and preview pane.
141
+ const TOTAL_HORIZONTAL_PADDING = 4;
142
+ const colorizeCodeWidth = Math.max(
143
+ Math.floor(
144
+ (terminalWidth - TOTAL_HORIZONTAL_PADDING) *
145
+ PREVIEW_PANE_WIDTH_PERCENTAGE *
146
+ PREVIEW_PANE_WIDTH_SAFETY_MARGIN,
147
+ ),
148
+ 1,
149
+ );
150
+
151
+ const DIALOG_PADDING = 2;
152
+ const selectThemeHeight = themeItems.length + 1;
153
+ const SCOPE_SELECTION_HEIGHT = 4; // Height for the scope selection section + margin.
154
+ const SPACE_BETWEEN_THEME_SELECTION_AND_APPLY_TO = 1;
155
+ const TAB_TO_SELECT_HEIGHT = 2;
156
+ availableTerminalHeight = availableTerminalHeight ?? Number.MAX_SAFE_INTEGER;
157
+ availableTerminalHeight -= 2; // Top and bottom borders.
158
+ availableTerminalHeight -= TAB_TO_SELECT_HEIGHT;
159
+
160
+ let totalLeftHandSideHeight =
161
+ DIALOG_PADDING +
162
+ selectThemeHeight +
163
+ SCOPE_SELECTION_HEIGHT +
164
+ SPACE_BETWEEN_THEME_SELECTION_AND_APPLY_TO;
165
+
166
+ let showScopeSelection = true;
167
+ let includePadding = true;
168
+
169
+ // Remove content from the LHS that can be omitted if it exceeds the available height.
170
+ if (totalLeftHandSideHeight > availableTerminalHeight) {
171
+ includePadding = false;
172
+ totalLeftHandSideHeight -= DIALOG_PADDING;
173
+ }
174
+
175
+ if (totalLeftHandSideHeight > availableTerminalHeight) {
176
+ // First, try hiding the scope selection
177
+ totalLeftHandSideHeight -= SCOPE_SELECTION_HEIGHT;
178
+ showScopeSelection = false;
179
+ }
180
+
181
+ // Don't focus the scope selection if it is hidden due to height constraints.
182
+ const currentFocusedSection = !showScopeSelection ? 'theme' : focusedSection;
183
+
184
+ // Vertical space taken by elements other than the two code blocks in the preview pane.
185
+ // Includes "Preview" title, borders, and margin between blocks.
186
+ const PREVIEW_PANE_FIXED_VERTICAL_SPACE = 8;
187
+
188
+ // The right column doesn't need to ever be shorter than the left column.
189
+ availableTerminalHeight = Math.max(
190
+ availableTerminalHeight,
191
+ totalLeftHandSideHeight,
192
+ );
193
+ const availableTerminalHeightCodeBlock =
194
+ availableTerminalHeight -
195
+ PREVIEW_PANE_FIXED_VERTICAL_SPACE -
196
+ (includePadding ? 2 : 0) * 2;
197
+
198
+ // Subtract margin between code blocks from available height.
199
+ const availableHeightForPanes = Math.max(
200
+ 0,
201
+ availableTerminalHeightCodeBlock - 1,
202
+ );
203
+
204
+ // The code block is slightly longer than the diff, so give it more space.
205
+ const codeBlockHeight = Math.ceil(availableHeightForPanes * 0.6);
206
+ const diffHeight = Math.floor(availableHeightForPanes * 0.4);
207
+ return (
208
+ <Box
209
+ borderStyle="round"
210
+ borderColor={Colors.Gray}
211
+ flexDirection="column"
212
+ paddingTop={includePadding ? 1 : 0}
213
+ paddingBottom={includePadding ? 1 : 0}
214
+ paddingLeft={1}
215
+ paddingRight={1}
216
+ width="100%"
217
+ >
218
+ <Box flexDirection="row">
219
+ {/* Left Column: Selection */}
220
+ <Box flexDirection="column" width="45%" paddingRight={2}>
221
+ <Text bold={currentFocusedSection === 'theme'} wrap="truncate">
222
+ {currentFocusedSection === 'theme' ? '> ' : ' '}Select Theme{' '}
223
+ <Text color={Colors.Gray}>{otherScopeModifiedMessage}</Text>
224
+ </Text>
225
+ <RadioButtonSelect
226
+ key={selectInputKey}
227
+ items={themeItems}
228
+ initialIndex={safeInitialThemeIndex}
229
+ onSelect={handleThemeSelect}
230
+ onHighlight={handleThemeHighlight}
231
+ isFocused={currentFocusedSection === 'theme'}
232
+ maxItemsToShow={8}
233
+ showScrollArrows={true}
234
+ showNumbers={currentFocusedSection === 'theme'}
235
+ />
236
+
237
+ {/* Scope Selection */}
238
+ {showScopeSelection && (
239
+ <Box marginTop={1} flexDirection="column">
240
+ <Text bold={currentFocusedSection === 'scope'} wrap="truncate">
241
+ {currentFocusedSection === 'scope' ? '> ' : ' '}Apply To
242
+ </Text>
243
+ <RadioButtonSelect
244
+ items={scopeItems}
245
+ initialIndex={0} // Default to User Settings
246
+ onSelect={handleScopeSelect}
247
+ onHighlight={handleScopeHighlight}
248
+ isFocused={currentFocusedSection === 'scope'}
249
+ showNumbers={currentFocusedSection === 'scope'}
250
+ />
251
+ </Box>
252
+ )}
253
+ </Box>
254
+
255
+ {/* Right Column: Preview */}
256
+ <Box flexDirection="column" width="55%" paddingLeft={2}>
257
+ <Text bold>Preview</Text>
258
+ {/* Get the Theme object for the highlighted theme, fall back to default if not found */}
259
+ {(() => {
260
+ const previewTheme =
261
+ themeManager.getTheme(
262
+ highlightedThemeName || DEFAULT_THEME.name,
263
+ ) || DEFAULT_THEME;
264
+ return (
265
+ <Box
266
+ borderStyle="single"
267
+ borderColor={Colors.Gray}
268
+ paddingTop={includePadding ? 1 : 0}
269
+ paddingBottom={includePadding ? 1 : 0}
270
+ paddingLeft={1}
271
+ paddingRight={1}
272
+ flexDirection="column"
273
+ >
274
+ {colorizeCode(
275
+ `# function
276
+ def fibonacci(n):
277
+ a, b = 0, 1
278
+ for _ in range(n):
279
+ a, b = b, a + b
280
+ return a`,
281
+ 'python',
282
+ codeBlockHeight,
283
+ colorizeCodeWidth,
284
+ )}
285
+ <Box marginTop={1} />
286
+ <DiffRenderer
287
+ diffContent={`--- a/util.py
288
+ +++ b/util.py
289
+ @@ -1,2 +1,2 @@
290
+ - print("Hello, " + name)
291
+ + print(f"Hello, {name}!")
292
+ `}
293
+ availableTerminalHeight={diffHeight}
294
+ terminalWidth={colorizeCodeWidth}
295
+ theme={previewTheme}
296
+ />
297
+ </Box>
298
+ );
299
+ })()}
300
+ </Box>
301
+ </Box>
302
+ <Box marginTop={1}>
303
+ <Text color={Colors.Gray} wrap="truncate">
304
+ (Use Enter to select
305
+ {showScopeSelection ? ', Tab to change focus' : ''})
306
+ </Text>
307
+ </Box>
308
+ </Box>
309
+ );
310
+ }