fss-link 1.0.43 → 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 (419) 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/generated/git-commit.d.ts +1 -1
  73. package/dist/src/generated/git-commit.js +1 -1
  74. package/dist/src/services/BuiltinCommandLoader.js +2 -0
  75. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  76. package/dist/src/ui/App.js +1 -1
  77. package/dist/src/ui/App.js.map +1 -1
  78. package/dist/src/ui/commands/contextCommand.d.ts +7 -0
  79. package/dist/src/ui/commands/contextCommand.js +115 -0
  80. package/dist/src/ui/commands/contextCommand.js.map +1 -0
  81. package/dist/src/ui/components/ContextUsageDisplay.d.ts +3 -1
  82. package/dist/src/ui/components/ContextUsageDisplay.js +43 -3
  83. package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
  84. package/dist/src/ui/components/Footer.d.ts +1 -0
  85. package/dist/src/ui/components/Footer.js +2 -2
  86. package/dist/src/ui/components/Footer.js.map +1 -1
  87. package/dist/src/ui/components/GeminiKeyDialog.d.ts +11 -0
  88. package/dist/src/ui/components/GeminiKeyDialog.js +156 -0
  89. package/dist/src/ui/components/GeminiKeyDialog.js.map +1 -0
  90. package/dist/src/ui/components/OpenAIEndpointDialog.d.ts +19 -0
  91. package/dist/src/ui/components/OpenAIEndpointDialog.js +163 -0
  92. package/dist/src/ui/components/OpenAIEndpointDialog.js.map +1 -0
  93. package/dist/src/zed-integration/schema.d.ts +1516 -1516
  94. package/dist/test-setup.ts +12 -0
  95. package/dist/test-utils/customMatchers.ts +65 -0
  96. package/dist/test-utils/mockCommandContext.test.ts +62 -0
  97. package/dist/test-utils/mockCommandContext.ts +105 -0
  98. package/dist/test-utils/render.tsx +18 -0
  99. package/dist/tsconfig.tsbuildinfo +1 -1
  100. package/dist/ui/App.test.tsx +2181 -0
  101. package/dist/ui/App.tsx +1344 -0
  102. package/dist/ui/IdeIntegrationNudge.tsx +98 -0
  103. package/dist/ui/__snapshots__/App.test.tsx.snap +124 -0
  104. package/dist/ui/colors.ts +56 -0
  105. package/dist/ui/commands/aboutCommand.test.ts +153 -0
  106. package/dist/ui/commands/aboutCommand.ts +49 -0
  107. package/dist/ui/commands/authCommand.test.ts +36 -0
  108. package/dist/ui/commands/authCommand.ts +17 -0
  109. package/dist/ui/commands/bugCommand.test.ts +114 -0
  110. package/dist/ui/commands/bugCommand.ts +92 -0
  111. package/dist/ui/commands/chatCommand.test.ts +414 -0
  112. package/dist/ui/commands/chatCommand.ts +280 -0
  113. package/dist/ui/commands/clearCommand.test.ts +100 -0
  114. package/dist/ui/commands/clearCommand.ts +29 -0
  115. package/dist/ui/commands/compressCommand.test.ts +129 -0
  116. package/dist/ui/commands/compressCommand.ts +78 -0
  117. package/dist/ui/commands/contextCommand.ts +132 -0
  118. package/dist/ui/commands/copyCommand.test.ts +296 -0
  119. package/dist/ui/commands/copyCommand.ts +67 -0
  120. package/dist/ui/commands/corgiCommand.test.ts +34 -0
  121. package/dist/ui/commands/corgiCommand.ts +16 -0
  122. package/dist/ui/commands/directoryCommand.test.tsx +185 -0
  123. package/dist/ui/commands/directoryCommand.tsx +179 -0
  124. package/dist/ui/commands/docsCommand.test.ts +99 -0
  125. package/dist/ui/commands/docsCommand.ts +42 -0
  126. package/dist/ui/commands/editorCommand.test.ts +30 -0
  127. package/dist/ui/commands/editorCommand.ts +21 -0
  128. package/dist/ui/commands/extensionsCommand.test.ts +67 -0
  129. package/dist/ui/commands/extensionsCommand.ts +46 -0
  130. package/dist/ui/commands/helpCommand.test.ts +52 -0
  131. package/dist/ui/commands/helpCommand.ts +23 -0
  132. package/dist/ui/commands/ideCommand.test.ts +255 -0
  133. package/dist/ui/commands/ideCommand.ts +283 -0
  134. package/dist/ui/commands/initCommand.test.ts +127 -0
  135. package/dist/ui/commands/initCommand.ts +117 -0
  136. package/dist/ui/commands/mcpCommand.test.ts +1057 -0
  137. package/dist/ui/commands/mcpCommand.ts +531 -0
  138. package/dist/ui/commands/memoryCommand.test.ts +344 -0
  139. package/dist/ui/commands/memoryCommand.ts +305 -0
  140. package/dist/ui/commands/privacyCommand.test.ts +38 -0
  141. package/dist/ui/commands/privacyCommand.ts +17 -0
  142. package/dist/ui/commands/quitCommand.test.ts +55 -0
  143. package/dist/ui/commands/quitCommand.ts +36 -0
  144. package/dist/ui/commands/restoreCommand.test.ts +250 -0
  145. package/dist/ui/commands/restoreCommand.ts +157 -0
  146. package/dist/ui/commands/searchEngineSetupCommand.ts +18 -0
  147. package/dist/ui/commands/settingsCommand.test.ts +36 -0
  148. package/dist/ui/commands/settingsCommand.ts +17 -0
  149. package/dist/ui/commands/setupGithubCommand.test.ts +238 -0
  150. package/dist/ui/commands/setupGithubCommand.ts +212 -0
  151. package/dist/ui/commands/speakCommand.ts +175 -0
  152. package/dist/ui/commands/statsCommand.test.ts +78 -0
  153. package/dist/ui/commands/statsCommand.ts +70 -0
  154. package/dist/ui/commands/terminalSetupCommand.test.ts +85 -0
  155. package/dist/ui/commands/terminalSetupCommand.ts +45 -0
  156. package/dist/ui/commands/themeCommand.test.ts +38 -0
  157. package/dist/ui/commands/themeCommand.ts +17 -0
  158. package/dist/ui/commands/toolsCommand.test.ts +105 -0
  159. package/dist/ui/commands/toolsCommand.ts +71 -0
  160. package/dist/ui/commands/ttsCommand.ts +143 -0
  161. package/dist/ui/commands/types.ts +204 -0
  162. package/dist/ui/commands/vimCommand.ts +25 -0
  163. package/dist/ui/commands/voiceCommand.ts +125 -0
  164. package/dist/ui/components/AboutBox.tsx +133 -0
  165. package/dist/ui/components/AsciiArt.ts +54 -0
  166. package/dist/ui/components/AuthDialog.test.tsx +334 -0
  167. package/dist/ui/components/AuthDialog.tsx +289 -0
  168. package/dist/ui/components/AuthInProgress.tsx +62 -0
  169. package/dist/ui/components/AutoAcceptIndicator.tsx +47 -0
  170. package/dist/ui/components/ConsoleSummaryDisplay.tsx +35 -0
  171. package/dist/ui/components/ContextSummaryDisplay.test.tsx +85 -0
  172. package/dist/ui/components/ContextSummaryDisplay.tsx +120 -0
  173. package/dist/ui/components/ContextUsageDisplay.tsx +77 -0
  174. package/dist/ui/components/DebugProfiler.tsx +36 -0
  175. package/dist/ui/components/DetailedMessagesDisplay.tsx +82 -0
  176. package/dist/ui/components/EditorSettingsDialog.tsx +172 -0
  177. package/dist/ui/components/FolderTrustDialog.test.tsx +36 -0
  178. package/dist/ui/components/FolderTrustDialog.tsx +74 -0
  179. package/dist/ui/components/Footer.test.tsx +159 -0
  180. package/dist/ui/components/Footer.tsx +158 -0
  181. package/dist/ui/components/GeminiKeyDialog.tsx +252 -0
  182. package/dist/ui/components/GeminiRespondingSpinner.tsx +34 -0
  183. package/dist/ui/components/Header.test.tsx +44 -0
  184. package/dist/ui/components/Header.tsx +70 -0
  185. package/dist/ui/components/Help.tsx +174 -0
  186. package/dist/ui/components/HistoryItemDisplay.test.tsx +125 -0
  187. package/dist/ui/components/HistoryItemDisplay.tsx +98 -0
  188. package/dist/ui/components/InputPrompt.test.tsx +1467 -0
  189. package/dist/ui/components/InputPrompt.tsx +641 -0
  190. package/dist/ui/components/LMStudioModelPrompt.tsx +215 -0
  191. package/dist/ui/components/LoadingIndicator.test.tsx +296 -0
  192. package/dist/ui/components/LoadingIndicator.tsx +82 -0
  193. package/dist/ui/components/MemoryUsageDisplay.tsx +36 -0
  194. package/dist/ui/components/ModelStatsDisplay.test.tsx +252 -0
  195. package/dist/ui/components/ModelStatsDisplay.tsx +197 -0
  196. package/dist/ui/components/OllamaModelPrompt.tsx +206 -0
  197. package/dist/ui/components/OpenAIEndpointDialog.tsx +261 -0
  198. package/dist/ui/components/OpenAIKeyPrompt.test.tsx +64 -0
  199. package/dist/ui/components/OpenAIKeyPrompt.tsx +197 -0
  200. package/dist/ui/components/PrepareLabel.tsx +48 -0
  201. package/dist/ui/components/SearchEngineConfigDialog.tsx +280 -0
  202. package/dist/ui/components/SessionSummaryDisplay.test.tsx +75 -0
  203. package/dist/ui/components/SessionSummaryDisplay.tsx +18 -0
  204. package/dist/ui/components/SettingsDialog.test.tsx +865 -0
  205. package/dist/ui/components/SettingsDialog.tsx +753 -0
  206. package/dist/ui/components/ShellConfirmationDialog.test.tsx +53 -0
  207. package/dist/ui/components/ShellConfirmationDialog.tsx +103 -0
  208. package/dist/ui/components/ShellModeIndicator.tsx +18 -0
  209. package/dist/ui/components/ShowMoreLines.tsx +40 -0
  210. package/dist/ui/components/StatsDisplay.test.tsx +401 -0
  211. package/dist/ui/components/StatsDisplay.tsx +273 -0
  212. package/dist/ui/components/SuggestionsDisplay.tsx +102 -0
  213. package/dist/ui/components/ThemeDialog.tsx +310 -0
  214. package/dist/ui/components/Tips.tsx +45 -0
  215. package/dist/ui/components/TodoDisplay.test.tsx +97 -0
  216. package/dist/ui/components/TodoDisplay.tsx +72 -0
  217. package/dist/ui/components/ToolStatsDisplay.test.tsx +180 -0
  218. package/dist/ui/components/ToolStatsDisplay.tsx +208 -0
  219. package/dist/ui/components/UpdateNotification.tsx +23 -0
  220. package/dist/ui/components/WelcomeBackDialog.tsx +290 -0
  221. package/dist/ui/components/__snapshots__/IDEContextDetailDisplay.test.tsx.snap +24 -0
  222. package/dist/ui/components/__snapshots__/ModelStatsDisplay.test.tsx.snap +121 -0
  223. package/dist/ui/components/__snapshots__/SessionSummaryDisplay.test.tsx.snap +30 -0
  224. package/dist/ui/components/__snapshots__/ShellConfirmationDialog.test.tsx.snap +21 -0
  225. package/dist/ui/components/__snapshots__/StatsDisplay.test.tsx.snap +264 -0
  226. package/dist/ui/components/__snapshots__/ToolStatsDisplay.test.tsx.snap +91 -0
  227. package/dist/ui/components/messages/CompressionMessage.tsx +49 -0
  228. package/dist/ui/components/messages/DiffRenderer.test.tsx +365 -0
  229. package/dist/ui/components/messages/DiffRenderer.tsx +358 -0
  230. package/dist/ui/components/messages/ErrorMessage.tsx +31 -0
  231. package/dist/ui/components/messages/GeminiMessage.tsx +43 -0
  232. package/dist/ui/components/messages/GeminiMessageContent.tsx +43 -0
  233. package/dist/ui/components/messages/InfoMessage.tsx +32 -0
  234. package/dist/ui/components/messages/ToolConfirmationMessage.test.tsx +58 -0
  235. package/dist/ui/components/messages/ToolConfirmationMessage.tsx +297 -0
  236. package/dist/ui/components/messages/ToolGroupMessage.tsx +126 -0
  237. package/dist/ui/components/messages/ToolMessage.test.tsx +183 -0
  238. package/dist/ui/components/messages/ToolMessage.tsx +296 -0
  239. package/dist/ui/components/messages/UserMessage.tsx +43 -0
  240. package/dist/ui/components/messages/UserShellMessage.tsx +25 -0
  241. package/dist/ui/components/shared/MaxSizedBox.test.tsx +425 -0
  242. package/dist/ui/components/shared/MaxSizedBox.tsx +624 -0
  243. package/dist/ui/components/shared/RadioButtonSelect.test.tsx +181 -0
  244. package/dist/ui/components/shared/RadioButtonSelect.tsx +234 -0
  245. package/dist/ui/components/shared/__snapshots__/RadioButtonSelect.test.tsx.snap +47 -0
  246. package/dist/ui/components/shared/text-buffer.test.ts +1728 -0
  247. package/dist/ui/components/shared/text-buffer.ts +2227 -0
  248. package/dist/ui/components/shared/vim-buffer-actions.test.ts +1119 -0
  249. package/dist/ui/components/shared/vim-buffer-actions.ts +814 -0
  250. package/dist/ui/constants.ts +17 -0
  251. package/dist/ui/contexts/KeypressContext.test.tsx +391 -0
  252. package/dist/ui/contexts/KeypressContext.tsx +440 -0
  253. package/dist/ui/contexts/OverflowContext.tsx +87 -0
  254. package/dist/ui/contexts/SessionContext.test.tsx +132 -0
  255. package/dist/ui/contexts/SessionContext.tsx +143 -0
  256. package/dist/ui/contexts/SettingsContext.tsx +20 -0
  257. package/dist/ui/contexts/StreamingContext.tsx +22 -0
  258. package/dist/ui/contexts/VimModeContext.tsx +79 -0
  259. package/dist/ui/editors/editorSettingsManager.ts +66 -0
  260. package/dist/ui/hooks/atCommandProcessor.test.ts +1102 -0
  261. package/dist/ui/hooks/atCommandProcessor.ts +485 -0
  262. package/dist/ui/hooks/shellCommandProcessor.test.ts +481 -0
  263. package/dist/ui/hooks/shellCommandProcessor.ts +314 -0
  264. package/dist/ui/hooks/slashCommandProcessor.test.ts +1044 -0
  265. package/dist/ui/hooks/slashCommandProcessor.ts +595 -0
  266. package/dist/ui/hooks/useAtCompletion.test.ts +497 -0
  267. package/dist/ui/hooks/useAtCompletion.ts +244 -0
  268. package/dist/ui/hooks/useAuthCommand.ts +129 -0
  269. package/dist/ui/hooks/useAutoAcceptIndicator.test.ts +300 -0
  270. package/dist/ui/hooks/useAutoAcceptIndicator.ts +52 -0
  271. package/dist/ui/hooks/useBracketedPaste.ts +37 -0
  272. package/dist/ui/hooks/useCommandCompletion.test.ts +518 -0
  273. package/dist/ui/hooks/useCommandCompletion.tsx +238 -0
  274. package/dist/ui/hooks/useCompletion.ts +128 -0
  275. package/dist/ui/hooks/useConsoleMessages.test.ts +147 -0
  276. package/dist/ui/hooks/useConsoleMessages.ts +110 -0
  277. package/dist/ui/hooks/useEditorSettings.test.ts +283 -0
  278. package/dist/ui/hooks/useEditorSettings.ts +75 -0
  279. package/dist/ui/hooks/useFocus.test.ts +119 -0
  280. package/dist/ui/hooks/useFocus.ts +48 -0
  281. package/dist/ui/hooks/useFolderTrust.test.ts +159 -0
  282. package/dist/ui/hooks/useFolderTrust.ts +72 -0
  283. package/dist/ui/hooks/useGeminiStream.test.tsx +1998 -0
  284. package/dist/ui/hooks/useGeminiStream.ts +1017 -0
  285. package/dist/ui/hooks/useGitBranchName.test.ts +280 -0
  286. package/dist/ui/hooks/useGitBranchName.ts +79 -0
  287. package/dist/ui/hooks/useHistoryManager.test.ts +202 -0
  288. package/dist/ui/hooks/useHistoryManager.ts +111 -0
  289. package/dist/ui/hooks/useInputHistory.test.ts +261 -0
  290. package/dist/ui/hooks/useInputHistory.ts +111 -0
  291. package/dist/ui/hooks/useKeypress.test.ts +280 -0
  292. package/dist/ui/hooks/useKeypress.ts +39 -0
  293. package/dist/ui/hooks/useKittyKeyboardProtocol.ts +31 -0
  294. package/dist/ui/hooks/useLoadingIndicator.test.ts +139 -0
  295. package/dist/ui/hooks/useLoadingIndicator.ts +57 -0
  296. package/dist/ui/hooks/useLogger.ts +32 -0
  297. package/dist/ui/hooks/useMessageQueue.test.ts +226 -0
  298. package/dist/ui/hooks/useMessageQueue.ts +69 -0
  299. package/dist/ui/hooks/usePhraseCycler.test.ts +145 -0
  300. package/dist/ui/hooks/usePhraseCycler.ts +198 -0
  301. package/dist/ui/hooks/usePrivacySettings.test.ts +242 -0
  302. package/dist/ui/hooks/usePrivacySettings.ts +150 -0
  303. package/dist/ui/hooks/useReactToolScheduler.ts +309 -0
  304. package/dist/ui/hooks/useRefreshMemoryCommand.ts +7 -0
  305. package/dist/ui/hooks/useReverseSearchCompletion.test.tsx +260 -0
  306. package/dist/ui/hooks/useReverseSearchCompletion.tsx +95 -0
  307. package/dist/ui/hooks/useSettingsCommand.ts +25 -0
  308. package/dist/ui/hooks/useShellHistory.test.ts +219 -0
  309. package/dist/ui/hooks/useShellHistory.ts +133 -0
  310. package/dist/ui/hooks/useShowMemoryCommand.ts +75 -0
  311. package/dist/ui/hooks/useSlashCompletion.test.ts +434 -0
  312. package/dist/ui/hooks/useSlashCompletion.ts +187 -0
  313. package/dist/ui/hooks/useStateAndRef.ts +36 -0
  314. package/dist/ui/hooks/useTerminalSize.ts +32 -0
  315. package/dist/ui/hooks/useThemeCommand.ts +110 -0
  316. package/dist/ui/hooks/useTimer.test.ts +120 -0
  317. package/dist/ui/hooks/useTimer.ts +65 -0
  318. package/dist/ui/hooks/useToolScheduler.test.ts +1123 -0
  319. package/dist/ui/hooks/useWelcomeBack.ts +253 -0
  320. package/dist/ui/hooks/vim.test.ts +1691 -0
  321. package/dist/ui/hooks/vim.ts +784 -0
  322. package/dist/ui/keyMatchers.test.ts +337 -0
  323. package/dist/ui/keyMatchers.ts +105 -0
  324. package/dist/ui/privacy/CloudFreePrivacyNotice.tsx +117 -0
  325. package/dist/ui/privacy/CloudPaidPrivacyNotice.tsx +59 -0
  326. package/dist/ui/privacy/GeminiPrivacyNotice.tsx +62 -0
  327. package/dist/ui/privacy/PrivacyNotice.tsx +42 -0
  328. package/dist/ui/semantic-colors.ts +26 -0
  329. package/dist/ui/themes/ansi-light.ts +150 -0
  330. package/dist/ui/themes/ansi.ts +159 -0
  331. package/dist/ui/themes/atom-one-dark.ts +147 -0
  332. package/dist/ui/themes/ayu-light.ts +139 -0
  333. package/dist/ui/themes/ayu.ts +113 -0
  334. package/dist/ui/themes/color-utils.test.ts +221 -0
  335. package/dist/ui/themes/color-utils.ts +231 -0
  336. package/dist/ui/themes/default-light.ts +108 -0
  337. package/dist/ui/themes/default.ts +151 -0
  338. package/dist/ui/themes/dracula.ts +124 -0
  339. package/dist/ui/themes/fss-code-dark.ts +156 -0
  340. package/dist/ui/themes/fss-dark.ts +113 -0
  341. package/dist/ui/themes/fss-light.ts +139 -0
  342. package/dist/ui/themes/github-dark.ts +147 -0
  343. package/dist/ui/themes/github-light.ts +149 -0
  344. package/dist/ui/themes/googlecode.ts +146 -0
  345. package/dist/ui/themes/no-color.ts +125 -0
  346. package/dist/ui/themes/qwen-dark.ts +118 -0
  347. package/dist/ui/themes/qwen-light.ts +144 -0
  348. package/dist/ui/themes/semantic-tokens.ts +127 -0
  349. package/dist/ui/themes/shades-of-purple.ts +352 -0
  350. package/dist/ui/themes/theme-manager.test.ts +99 -0
  351. package/dist/ui/themes/theme-manager.ts +257 -0
  352. package/dist/ui/themes/theme.test.ts +97 -0
  353. package/dist/ui/themes/theme.ts +451 -0
  354. package/dist/ui/themes/xcode.ts +154 -0
  355. package/dist/ui/types.ts +255 -0
  356. package/dist/ui/utils/CodeColorizer.tsx +217 -0
  357. package/dist/ui/utils/ConsolePatcher.ts +71 -0
  358. package/dist/ui/utils/InlineMarkdownRenderer.tsx +173 -0
  359. package/dist/ui/utils/MarkdownDisplay.test.tsx +244 -0
  360. package/dist/ui/utils/MarkdownDisplay.tsx +415 -0
  361. package/dist/ui/utils/TableRenderer.tsx +159 -0
  362. package/dist/ui/utils/__snapshots__/MarkdownDisplay.test.tsx.snap +93 -0
  363. package/dist/ui/utils/clipboardUtils.test.ts +76 -0
  364. package/dist/ui/utils/clipboardUtils.ts +149 -0
  365. package/dist/ui/utils/commandUtils.test.ts +384 -0
  366. package/dist/ui/utils/commandUtils.ts +106 -0
  367. package/dist/ui/utils/computeStats.test.ts +292 -0
  368. package/dist/ui/utils/computeStats.ts +86 -0
  369. package/dist/ui/utils/displayUtils.test.ts +58 -0
  370. package/dist/ui/utils/displayUtils.ts +32 -0
  371. package/dist/ui/utils/formatters.test.ts +72 -0
  372. package/dist/ui/utils/formatters.ts +63 -0
  373. package/dist/ui/utils/isNarrowWidth.ts +9 -0
  374. package/dist/ui/utils/kittyProtocolDetector.ts +105 -0
  375. package/dist/ui/utils/markdownUtilities.test.ts +50 -0
  376. package/dist/ui/utils/markdownUtilities.ts +125 -0
  377. package/dist/ui/utils/platformConstants.ts +52 -0
  378. package/dist/ui/utils/terminalSetup.ts +342 -0
  379. package/dist/ui/utils/textUtils.ts +40 -0
  380. package/dist/ui/utils/updateCheck.test.ts +163 -0
  381. package/dist/ui/utils/updateCheck.ts +100 -0
  382. package/dist/utils/checks.ts +28 -0
  383. package/dist/utils/cleanup.test.ts +68 -0
  384. package/dist/utils/cleanup.ts +36 -0
  385. package/dist/utils/dialogScopeUtils.ts +64 -0
  386. package/dist/utils/events.ts +14 -0
  387. package/dist/utils/gitUtils.test.ts +149 -0
  388. package/dist/utils/gitUtils.ts +116 -0
  389. package/dist/utils/handleAutoUpdate.test.ts +272 -0
  390. package/dist/utils/handleAutoUpdate.ts +145 -0
  391. package/dist/utils/installationInfo.test.ts +315 -0
  392. package/dist/utils/installationInfo.ts +176 -0
  393. package/dist/utils/package.ts +38 -0
  394. package/dist/utils/readStdin.ts +51 -0
  395. package/dist/utils/resolvePath.ts +21 -0
  396. package/dist/utils/sandbox-macos-permissive-closed.sb +32 -0
  397. package/dist/utils/sandbox-macos-permissive-open.sb +25 -0
  398. package/dist/utils/sandbox-macos-permissive-proxied.sb +37 -0
  399. package/dist/utils/sandbox-macos-restrictive-closed.sb +93 -0
  400. package/dist/utils/sandbox-macos-restrictive-open.sb +96 -0
  401. package/dist/utils/sandbox-macos-restrictive-proxied.sb +98 -0
  402. package/dist/utils/sandbox.ts +962 -0
  403. package/dist/utils/settingsUtils.test.ts +797 -0
  404. package/dist/utils/settingsUtils.ts +489 -0
  405. package/dist/utils/spawnWrapper.ts +9 -0
  406. package/dist/utils/startupWarnings.test.ts +83 -0
  407. package/dist/utils/startupWarnings.ts +40 -0
  408. package/dist/utils/updateEventEmitter.ts +13 -0
  409. package/dist/utils/userStartupWarnings.test.ts +87 -0
  410. package/dist/utils/userStartupWarnings.ts +69 -0
  411. package/dist/utils/version.ts +12 -0
  412. package/dist/validateNonInterActiveAuth.test.ts +260 -0
  413. package/dist/validateNonInterActiveAuth.ts +51 -0
  414. package/dist/vitest.config.ts +37 -0
  415. package/dist/zed-integration/acp.ts +366 -0
  416. package/dist/zed-integration/fileSystemService.ts +47 -0
  417. package/dist/zed-integration/schema.ts +466 -0
  418. package/dist/zed-integration/zedIntegration.ts +944 -0
  419. package/package.json +2 -2
@@ -0,0 +1,215 @@
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, useInput } from 'ink';
9
+ import { Colors } from '../colors.js';
10
+
11
+ interface LMStudioModel {
12
+ id: string;
13
+ object: string;
14
+ isRecommended: boolean;
15
+ }
16
+
17
+ interface LMStudioModelPromptProps {
18
+ onSubmit: (apiKey: string, baseUrl: string, model: string) => void;
19
+ onCancel: () => void;
20
+ }
21
+
22
+ // Hardcoded recommended models with stars - based on quality and performance
23
+ const RECOMMENDED_MODELS = new Set([
24
+ 'qwen3-coder-4b',
25
+ 'qwen3-coder-30b',
26
+ 'qwen3-30b-a3b-2507',
27
+ 'qwen3-4b-2507',
28
+ 'llama-3.1-8b',
29
+ 'llama-3.1-70b',
30
+ 'codellama-7b',
31
+ 'codellama-13b',
32
+ 'mistral-7b',
33
+ 'deepseek-coder-6.7b',
34
+ 'codegemma-7b'
35
+ ]);
36
+
37
+ function normalizeModelName(modelId: string): string {
38
+ // Extract base name from LM Studio model IDs for recommendation matching
39
+ return modelId.toLowerCase()
40
+ .replace(/[-_]/g, '-')
41
+ .replace(/\.(gguf|bin|safetensors)$/, '')
42
+ .split('/').pop() || modelId;
43
+ }
44
+
45
+ export function LMStudioModelPrompt({
46
+ onSubmit,
47
+ onCancel,
48
+ }: LMStudioModelPromptProps): React.JSX.Element {
49
+ const [availableModels, setAvailableModels] = useState<LMStudioModel[]>([]);
50
+ const [selectedModelIndex, setSelectedModelIndex] = useState(0);
51
+ const [loading, setLoading] = useState(true);
52
+ const [error, setError] = useState<string | null>(null);
53
+
54
+ // Fetch available models from LM Studio on component mount
55
+ useEffect(() => {
56
+ const fetchModels = async () => {
57
+ try {
58
+ const response = await fetch('http://localhost:1234/v1/models');
59
+ if (!response.ok) {
60
+ throw new Error('LM Studio server not accessible');
61
+ }
62
+
63
+ const data = await response.json();
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ const models: LMStudioModel[] = data.data?.map((model: any) => ({
66
+ id: model.id,
67
+ object: model.object || 'model',
68
+ isRecommended: RECOMMENDED_MODELS.has(normalizeModelName(model.id))
69
+ })) || [];
70
+
71
+ // Sort: recommended models first, then alphabetically
72
+ models.sort((a, b) => {
73
+ if (a.isRecommended && !b.isRecommended) return -1;
74
+ if (!a.isRecommended && b.isRecommended) return 1;
75
+ return a.id.localeCompare(b.id);
76
+ });
77
+
78
+ setAvailableModels(models);
79
+ setLoading(false);
80
+ } catch (err) {
81
+ setError(err instanceof Error ? err.message : 'Failed to fetch models');
82
+ setLoading(false);
83
+ }
84
+ };
85
+
86
+ fetchModels();
87
+ }, []);
88
+
89
+ useInput((input, key) => {
90
+ if (loading) return;
91
+
92
+ if (key.escape) {
93
+ onCancel();
94
+ return;
95
+ }
96
+
97
+ if (input.includes('\n') || input.includes('\r')) {
98
+ if (availableModels.length > 0) {
99
+ const selectedModel = availableModels[selectedModelIndex];
100
+ // Use dummy API key "1" - required for persistence but not used by LM Studio
101
+ onSubmit('1', 'http://localhost:1234/v1', selectedModel.id);
102
+ }
103
+ return;
104
+ }
105
+
106
+ // Handle arrow keys for model selection
107
+ if (key.upArrow && selectedModelIndex > 0) {
108
+ setSelectedModelIndex(selectedModelIndex - 1);
109
+ }
110
+
111
+ if (key.downArrow && selectedModelIndex < availableModels.length - 1) {
112
+ setSelectedModelIndex(selectedModelIndex + 1);
113
+ }
114
+ });
115
+
116
+ if (loading) {
117
+ return (
118
+ <Box
119
+ borderStyle="round"
120
+ borderColor={Colors.AccentBlue}
121
+ flexDirection="column"
122
+ padding={1}
123
+ width="100%"
124
+ >
125
+ <Text bold color={Colors.AccentBlue}>
126
+ LM Studio Model Selection
127
+ </Text>
128
+ <Box marginTop={1}>
129
+ <Text>Loading available models from LM Studio...</Text>
130
+ </Box>
131
+ </Box>
132
+ );
133
+ }
134
+
135
+ if (error) {
136
+ return (
137
+ <Box
138
+ borderStyle="round"
139
+ borderColor={Colors.AccentRed}
140
+ flexDirection="column"
141
+ padding={1}
142
+ width="100%"
143
+ >
144
+ <Text bold color={Colors.AccentRed}>
145
+ LM Studio Connection Error
146
+ </Text>
147
+ <Box marginTop={1}>
148
+ <Text>{error}</Text>
149
+ </Box>
150
+ <Box marginTop={1}>
151
+ <Text color={Colors.Gray}>
152
+ Make sure LM Studio is running with a model loaded
153
+ </Text>
154
+ </Box>
155
+ <Box marginTop={1}>
156
+ <Text color={Colors.Gray}>Press Esc to cancel</Text>
157
+ </Box>
158
+ </Box>
159
+ );
160
+ }
161
+
162
+ return (
163
+ <Box
164
+ borderStyle="round"
165
+ borderColor={Colors.AccentBlue}
166
+ flexDirection="column"
167
+ padding={1}
168
+ width="100%"
169
+ >
170
+ <Text bold color={Colors.AccentBlue}>
171
+ Select LM Studio Model
172
+ </Text>
173
+ <Box marginTop={1}>
174
+ <Text>Choose from loaded models in your LM Studio server:</Text>
175
+ </Box>
176
+
177
+ <Box marginTop={1} flexDirection="column">
178
+ {availableModels.slice(0, 10).map((model, index) => (
179
+ <Box key={model.id} flexDirection="row">
180
+ <Text color={index === selectedModelIndex ? Colors.AccentBlue : Colors.Gray}>
181
+ {index === selectedModelIndex ? '> ' : ' '}
182
+ {model.isRecommended ? '⭐ ' : ' '}
183
+ {model.id}
184
+ </Text>
185
+ </Box>
186
+ ))}
187
+ {availableModels.length > 10 && (
188
+ <Text color={Colors.Gray}>
189
+ ... and {availableModels.length - 10} more models
190
+ </Text>
191
+ )}
192
+ </Box>
193
+
194
+ {availableModels.length === 0 && (
195
+ <Box marginTop={1}>
196
+ <Text color={Colors.AccentYellow}>
197
+ No models found. Load a model in LM Studio first.
198
+ </Text>
199
+ </Box>
200
+ )}
201
+
202
+ <Box marginTop={1}>
203
+ <Text color={Colors.Gray}>
204
+ ⭐ = Recommended for coding • Press ↑↓ to select, Enter to confirm, Esc to cancel
205
+ </Text>
206
+ </Box>
207
+
208
+ <Box marginTop={1}>
209
+ <Text color={Colors.AccentBlue}>
210
+ Endpoint: http://localhost:1234/v1
211
+ </Text>
212
+ </Box>
213
+ </Box>
214
+ );
215
+ }
@@ -0,0 +1,296 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import React from 'react';
8
+ import { render } from 'ink-testing-library';
9
+ import { Text } from 'ink';
10
+ import { LoadingIndicator } from './LoadingIndicator.js';
11
+ import { StreamingContext } from '../contexts/StreamingContext.js';
12
+ import { StreamingState } from '../types.js';
13
+ import { vi } from 'vitest';
14
+ import * as useTerminalSize from '../hooks/useTerminalSize.js';
15
+
16
+ // Mock GeminiRespondingSpinner
17
+ vi.mock('./GeminiRespondingSpinner.js', () => ({
18
+ GeminiRespondingSpinner: ({
19
+ nonRespondingDisplay,
20
+ }: {
21
+ nonRespondingDisplay?: string;
22
+ }) => {
23
+ const streamingState = React.useContext(StreamingContext)!;
24
+ if (streamingState === StreamingState.Responding) {
25
+ return <Text>MockRespondingSpinner</Text>;
26
+ } else if (nonRespondingDisplay) {
27
+ return <Text>{nonRespondingDisplay}</Text>;
28
+ }
29
+ return null;
30
+ },
31
+ }));
32
+
33
+ vi.mock('../hooks/useTerminalSize.js', () => ({
34
+ useTerminalSize: vi.fn(),
35
+ }));
36
+
37
+ const useTerminalSizeMock = vi.mocked(useTerminalSize.useTerminalSize);
38
+
39
+ const renderWithContext = (
40
+ ui: React.ReactElement,
41
+ streamingStateValue: StreamingState,
42
+ width = 120,
43
+ ) => {
44
+ useTerminalSizeMock.mockReturnValue({ columns: width, rows: 24 });
45
+ const contextValue: StreamingState = streamingStateValue;
46
+ return render(
47
+ <StreamingContext.Provider value={contextValue}>
48
+ {ui}
49
+ </StreamingContext.Provider>,
50
+ );
51
+ };
52
+
53
+ describe('<LoadingIndicator />', () => {
54
+ const defaultProps = {
55
+ currentLoadingPhrase: 'Loading...',
56
+ elapsedTime: 5,
57
+ };
58
+
59
+ it('should not render when streamingState is Idle', () => {
60
+ const { lastFrame } = renderWithContext(
61
+ <LoadingIndicator {...defaultProps} />,
62
+ StreamingState.Idle,
63
+ );
64
+ expect(lastFrame()).toBe('');
65
+ });
66
+
67
+ it('should render spinner, phrase, and time when streamingState is Responding', () => {
68
+ const { lastFrame } = renderWithContext(
69
+ <LoadingIndicator {...defaultProps} />,
70
+ StreamingState.Responding,
71
+ );
72
+ const output = lastFrame();
73
+ expect(output).toContain('MockRespondingSpinner');
74
+ expect(output).toContain('Loading...');
75
+ expect(output).toContain('(esc to cancel, 5s)');
76
+ });
77
+
78
+ it('should render spinner (static), phrase but no time/cancel when streamingState is WaitingForConfirmation', () => {
79
+ const props = {
80
+ currentLoadingPhrase: 'Confirm action',
81
+ elapsedTime: 10,
82
+ };
83
+ const { lastFrame } = renderWithContext(
84
+ <LoadingIndicator {...props} />,
85
+ StreamingState.WaitingForConfirmation,
86
+ );
87
+ const output = lastFrame();
88
+ expect(output).toContain('⠏'); // Static char for WaitingForConfirmation
89
+ expect(output).toContain('Confirm action');
90
+ expect(output).not.toContain('(esc to cancel)');
91
+ expect(output).not.toContain(', 10s');
92
+ });
93
+
94
+ it('should display the currentLoadingPhrase correctly', () => {
95
+ const props = {
96
+ currentLoadingPhrase: 'Processing data...',
97
+ elapsedTime: 3,
98
+ };
99
+ const { lastFrame } = renderWithContext(
100
+ <LoadingIndicator {...props} />,
101
+ StreamingState.Responding,
102
+ );
103
+ expect(lastFrame()).toContain('Processing data...');
104
+ });
105
+
106
+ it('should display the elapsedTime correctly when Responding', () => {
107
+ const props = {
108
+ currentLoadingPhrase: 'Working...',
109
+ elapsedTime: 60,
110
+ };
111
+ const { lastFrame } = renderWithContext(
112
+ <LoadingIndicator {...props} />,
113
+ StreamingState.Responding,
114
+ );
115
+ expect(lastFrame()).toContain('(esc to cancel, 1m)');
116
+ });
117
+
118
+ it('should display the elapsedTime correctly in human-readable format', () => {
119
+ const props = {
120
+ currentLoadingPhrase: 'Working...',
121
+ elapsedTime: 125,
122
+ };
123
+ const { lastFrame } = renderWithContext(
124
+ <LoadingIndicator {...props} />,
125
+ StreamingState.Responding,
126
+ );
127
+ expect(lastFrame()).toContain('(esc to cancel, 2m 5s)');
128
+ });
129
+
130
+ it('should render rightContent when provided', () => {
131
+ const rightContent = <Text>Extra Info</Text>;
132
+ const { lastFrame } = renderWithContext(
133
+ <LoadingIndicator {...defaultProps} rightContent={rightContent} />,
134
+ StreamingState.Responding,
135
+ );
136
+ expect(lastFrame()).toContain('Extra Info');
137
+ });
138
+
139
+ it('should transition correctly between states using rerender', () => {
140
+ const { lastFrame, rerender } = renderWithContext(
141
+ <LoadingIndicator {...defaultProps} />,
142
+ StreamingState.Idle,
143
+ );
144
+ expect(lastFrame()).toBe(''); // Initial: Idle
145
+
146
+ // Transition to Responding
147
+ rerender(
148
+ <StreamingContext.Provider value={StreamingState.Responding}>
149
+ <LoadingIndicator
150
+ currentLoadingPhrase="Now Responding"
151
+ elapsedTime={2}
152
+ />
153
+ </StreamingContext.Provider>,
154
+ );
155
+ let output = lastFrame();
156
+ expect(output).toContain('MockRespondingSpinner');
157
+ expect(output).toContain('Now Responding');
158
+ expect(output).toContain('(esc to cancel, 2s)');
159
+
160
+ // Transition to WaitingForConfirmation
161
+ rerender(
162
+ <StreamingContext.Provider value={StreamingState.WaitingForConfirmation}>
163
+ <LoadingIndicator
164
+ currentLoadingPhrase="Please Confirm"
165
+ elapsedTime={15}
166
+ />
167
+ </StreamingContext.Provider>,
168
+ );
169
+ output = lastFrame();
170
+ expect(output).toContain('⠏');
171
+ expect(output).toContain('Please Confirm');
172
+ expect(output).not.toContain('(esc to cancel)');
173
+ expect(output).not.toContain(', 15s');
174
+
175
+ // Transition back to Idle
176
+ rerender(
177
+ <StreamingContext.Provider value={StreamingState.Idle}>
178
+ <LoadingIndicator {...defaultProps} />
179
+ </StreamingContext.Provider>,
180
+ );
181
+ expect(lastFrame()).toBe('');
182
+ });
183
+
184
+ it('should display fallback phrase if thought is empty', () => {
185
+ const props = {
186
+ thought: null,
187
+ currentLoadingPhrase: 'Loading...',
188
+ elapsedTime: 5,
189
+ };
190
+ const { lastFrame } = renderWithContext(
191
+ <LoadingIndicator {...props} />,
192
+ StreamingState.Responding,
193
+ );
194
+ const output = lastFrame();
195
+ expect(output).toContain('Loading...');
196
+ });
197
+
198
+ it('should display the subject of a thought', () => {
199
+ const props = {
200
+ thought: {
201
+ subject: 'Thinking about something...',
202
+ description: 'and other stuff.',
203
+ },
204
+ elapsedTime: 5,
205
+ };
206
+ const { lastFrame } = renderWithContext(
207
+ <LoadingIndicator {...props} />,
208
+ StreamingState.Responding,
209
+ );
210
+ const output = lastFrame();
211
+ expect(output).toBeDefined();
212
+ if (output) {
213
+ expect(output).toContain('Thinking about something...');
214
+ expect(output).not.toContain('and other stuff.');
215
+ }
216
+ });
217
+
218
+ it('should prioritize thought.subject over currentLoadingPhrase', () => {
219
+ const props = {
220
+ thought: {
221
+ subject: 'This should be displayed',
222
+ description: 'A description',
223
+ },
224
+ currentLoadingPhrase: 'This should not be displayed',
225
+ elapsedTime: 5,
226
+ };
227
+ const { lastFrame } = renderWithContext(
228
+ <LoadingIndicator {...props} />,
229
+ StreamingState.Responding,
230
+ );
231
+ const output = lastFrame();
232
+ expect(output).toContain('This should be displayed');
233
+ expect(output).not.toContain('This should not be displayed');
234
+ });
235
+
236
+ describe('responsive layout', () => {
237
+ it('should render on a single line on a wide terminal', () => {
238
+ const { lastFrame } = renderWithContext(
239
+ <LoadingIndicator
240
+ {...defaultProps}
241
+ rightContent={<Text>Right</Text>}
242
+ />,
243
+ StreamingState.Responding,
244
+ 120,
245
+ );
246
+ const output = lastFrame();
247
+ // Check for single line output
248
+ expect(output?.includes('\n')).toBe(false);
249
+ expect(output).toContain('Loading...');
250
+ expect(output).toContain('(esc to cancel, 5s)');
251
+ expect(output).toContain('Right');
252
+ });
253
+
254
+ it('should render on multiple lines on a narrow terminal', () => {
255
+ const { lastFrame } = renderWithContext(
256
+ <LoadingIndicator
257
+ {...defaultProps}
258
+ rightContent={<Text>Right</Text>}
259
+ />,
260
+ StreamingState.Responding,
261
+ 79,
262
+ );
263
+ const output = lastFrame();
264
+ const lines = output?.split('\n');
265
+ // Expecting 3 lines:
266
+ // 1. Spinner + Primary Text
267
+ // 2. Cancel + Timer
268
+ // 3. Right Content
269
+ expect(lines).toHaveLength(3);
270
+ if (lines) {
271
+ expect(lines[0]).toContain('Loading...');
272
+ expect(lines[0]).not.toContain('(esc to cancel, 5s)');
273
+ expect(lines[1]).toContain('(esc to cancel, 5s)');
274
+ expect(lines[2]).toContain('Right');
275
+ }
276
+ });
277
+
278
+ it('should use wide layout at 80 columns', () => {
279
+ const { lastFrame } = renderWithContext(
280
+ <LoadingIndicator {...defaultProps} />,
281
+ StreamingState.Responding,
282
+ 80,
283
+ );
284
+ expect(lastFrame()?.includes('\n')).toBe(false);
285
+ });
286
+
287
+ it('should use narrow layout at 79 columns', () => {
288
+ const { lastFrame } = renderWithContext(
289
+ <LoadingIndicator {...defaultProps} />,
290
+ StreamingState.Responding,
291
+ 79,
292
+ );
293
+ expect(lastFrame()?.includes('\n')).toBe(true);
294
+ });
295
+ });
296
+ });
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { ThoughtSummary } from 'fss-link-core';
8
+ import React from 'react';
9
+ import { Box, Text } from 'ink';
10
+ import { Colors } from '../colors.js';
11
+ import { useStreamingContext } from '../contexts/StreamingContext.js';
12
+ import { StreamingState } from '../types.js';
13
+ import { GeminiRespondingSpinner } from './GeminiRespondingSpinner.js';
14
+ import { formatDuration } from '../utils/formatters.js';
15
+ import { useTerminalSize } from '../hooks/useTerminalSize.js';
16
+ import { isNarrowWidth } from '../utils/isNarrowWidth.js';
17
+
18
+ interface LoadingIndicatorProps {
19
+ currentLoadingPhrase?: string;
20
+ elapsedTime: number;
21
+ rightContent?: React.ReactNode;
22
+ thought?: ThoughtSummary | null;
23
+ }
24
+
25
+ export const LoadingIndicator: React.FC<LoadingIndicatorProps> = ({
26
+ currentLoadingPhrase,
27
+ elapsedTime,
28
+ rightContent,
29
+ thought,
30
+ }) => {
31
+ const streamingState = useStreamingContext();
32
+ const { columns: terminalWidth } = useTerminalSize();
33
+ const isNarrow = isNarrowWidth(terminalWidth);
34
+
35
+ if (streamingState === StreamingState.Idle) {
36
+ return null;
37
+ }
38
+
39
+ const primaryText = thought?.subject || currentLoadingPhrase;
40
+
41
+ const cancelAndTimerContent =
42
+ streamingState !== StreamingState.WaitingForConfirmation
43
+ ? `(esc to cancel, ${elapsedTime < 60 ? `${elapsedTime}s` : formatDuration(elapsedTime * 1000)})`
44
+ : null;
45
+
46
+ return (
47
+ <Box paddingLeft={0} flexDirection="column">
48
+ {/* Main loading line */}
49
+ <Box
50
+ width="100%"
51
+ flexDirection={isNarrow ? 'column' : 'row'}
52
+ alignItems={isNarrow ? 'flex-start' : 'center'}
53
+ >
54
+ <Box>
55
+ <Box marginRight={1}>
56
+ <GeminiRespondingSpinner
57
+ nonRespondingDisplay={
58
+ streamingState === StreamingState.WaitingForConfirmation
59
+ ? '⠏'
60
+ : ''
61
+ }
62
+ />
63
+ </Box>
64
+ {primaryText && (
65
+ <Text color={Colors.AccentPurple}>{primaryText}</Text>
66
+ )}
67
+ {!isNarrow && cancelAndTimerContent && (
68
+ <Text color={Colors.Gray}> {cancelAndTimerContent}</Text>
69
+ )}
70
+ </Box>
71
+ {!isNarrow && <Box flexGrow={1}>{/* Spacer */}</Box>}
72
+ {!isNarrow && rightContent && <Box>{rightContent}</Box>}
73
+ </Box>
74
+ {isNarrow && cancelAndTimerContent && (
75
+ <Box>
76
+ <Text color={Colors.Gray}>{cancelAndTimerContent}</Text>
77
+ </Box>
78
+ )}
79
+ {isNarrow && rightContent && <Box>{rightContent}</Box>}
80
+ </Box>
81
+ );
82
+ };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import React, { useEffect, useState } from 'react';
8
+ import { Box, Text } from 'ink';
9
+ import { Colors } from '../colors.js';
10
+ import process from 'node:process';
11
+ import { formatMemoryUsage } from '../utils/formatters.js';
12
+
13
+ export const MemoryUsageDisplay: React.FC = () => {
14
+ const [memoryUsage, setMemoryUsage] = useState<string>('');
15
+ const [memoryUsageColor, setMemoryUsageColor] = useState<string>(Colors.Gray);
16
+
17
+ useEffect(() => {
18
+ const updateMemory = () => {
19
+ const usage = process.memoryUsage().rss;
20
+ setMemoryUsage(formatMemoryUsage(usage));
21
+ setMemoryUsageColor(
22
+ usage >= 2 * 1024 * 1024 * 1024 ? Colors.AccentRed : Colors.Gray,
23
+ );
24
+ };
25
+ const intervalId = setInterval(updateMemory, 2000);
26
+ updateMemory(); // Initial update
27
+ return () => clearInterval(intervalId);
28
+ }, []);
29
+
30
+ return (
31
+ <Box>
32
+ <Text color={Colors.Gray}>| </Text>
33
+ <Text color={memoryUsageColor}>{memoryUsage}</Text>
34
+ </Box>
35
+ );
36
+ };