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,784 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { useCallback, useReducer, useEffect } from 'react';
8
+ import type { Key } from './useKeypress.js';
9
+ import type { TextBuffer } from '../components/shared/text-buffer.js';
10
+ import { useVimMode } from '../contexts/VimModeContext.js';
11
+
12
+ export type VimMode = 'NORMAL' | 'INSERT';
13
+
14
+ // Constants
15
+ const DIGIT_MULTIPLIER = 10;
16
+ const DEFAULT_COUNT = 1;
17
+ const DIGIT_1_TO_9 = /^[1-9]$/;
18
+
19
+ // Command types
20
+ const CMD_TYPES = {
21
+ DELETE_WORD_FORWARD: 'dw',
22
+ DELETE_WORD_BACKWARD: 'db',
23
+ DELETE_WORD_END: 'de',
24
+ CHANGE_WORD_FORWARD: 'cw',
25
+ CHANGE_WORD_BACKWARD: 'cb',
26
+ CHANGE_WORD_END: 'ce',
27
+ DELETE_CHAR: 'x',
28
+ DELETE_LINE: 'dd',
29
+ CHANGE_LINE: 'cc',
30
+ DELETE_TO_EOL: 'D',
31
+ CHANGE_TO_EOL: 'C',
32
+ CHANGE_MOVEMENT: {
33
+ LEFT: 'ch',
34
+ DOWN: 'cj',
35
+ UP: 'ck',
36
+ RIGHT: 'cl',
37
+ },
38
+ } as const;
39
+
40
+ // Helper function to clear pending state
41
+ const createClearPendingState = () => ({
42
+ count: 0,
43
+ pendingOperator: null as 'g' | 'd' | 'c' | null,
44
+ });
45
+
46
+ // State and action types for useReducer
47
+ type VimState = {
48
+ mode: VimMode;
49
+ count: number;
50
+ pendingOperator: 'g' | 'd' | 'c' | null;
51
+ lastCommand: { type: string; count: number } | null;
52
+ };
53
+
54
+ type VimAction =
55
+ | { type: 'SET_MODE'; mode: VimMode }
56
+ | { type: 'SET_COUNT'; count: number }
57
+ | { type: 'INCREMENT_COUNT'; digit: number }
58
+ | { type: 'CLEAR_COUNT' }
59
+ | { type: 'SET_PENDING_OPERATOR'; operator: 'g' | 'd' | 'c' | null }
60
+ | {
61
+ type: 'SET_LAST_COMMAND';
62
+ command: { type: string; count: number } | null;
63
+ }
64
+ | { type: 'CLEAR_PENDING_STATES' }
65
+ | { type: 'ESCAPE_TO_NORMAL' };
66
+
67
+ const initialVimState: VimState = {
68
+ mode: 'NORMAL',
69
+ count: 0,
70
+ pendingOperator: null,
71
+ lastCommand: null,
72
+ };
73
+
74
+ // Reducer function
75
+ const vimReducer = (state: VimState, action: VimAction): VimState => {
76
+ switch (action.type) {
77
+ case 'SET_MODE':
78
+ return { ...state, mode: action.mode };
79
+
80
+ case 'SET_COUNT':
81
+ return { ...state, count: action.count };
82
+
83
+ case 'INCREMENT_COUNT':
84
+ return { ...state, count: state.count * DIGIT_MULTIPLIER + action.digit };
85
+
86
+ case 'CLEAR_COUNT':
87
+ return { ...state, count: 0 };
88
+
89
+ case 'SET_PENDING_OPERATOR':
90
+ return { ...state, pendingOperator: action.operator };
91
+
92
+ case 'SET_LAST_COMMAND':
93
+ return { ...state, lastCommand: action.command };
94
+
95
+ case 'CLEAR_PENDING_STATES':
96
+ return {
97
+ ...state,
98
+ ...createClearPendingState(),
99
+ };
100
+
101
+ case 'ESCAPE_TO_NORMAL':
102
+ // Handle escape - clear all pending states (mode is updated via context)
103
+ return {
104
+ ...state,
105
+ ...createClearPendingState(),
106
+ };
107
+
108
+ default:
109
+ return state;
110
+ }
111
+ };
112
+
113
+ /**
114
+ * React hook that provides vim-style editing functionality for text input.
115
+ *
116
+ * Features:
117
+ * - Modal editing (INSERT/NORMAL modes)
118
+ * - Navigation: h,j,k,l,w,b,e,0,$,^,gg,G with count prefixes
119
+ * - Editing: x,a,i,o,O,A,I,d,c,D,C with count prefixes
120
+ * - Complex operations: dd,cc,dw,cw,db,cb,de,ce
121
+ * - Command repetition (.)
122
+ * - Settings persistence
123
+ *
124
+ * @param buffer - TextBuffer instance for text manipulation
125
+ * @param onSubmit - Optional callback for command submission
126
+ * @returns Object with vim state and input handler
127
+ */
128
+ export function useVim(buffer: TextBuffer, onSubmit?: (value: string) => void) {
129
+ const { vimEnabled, vimMode, setVimMode } = useVimMode();
130
+ const [state, dispatch] = useReducer(vimReducer, initialVimState);
131
+
132
+ // Sync vim mode from context to local state
133
+ useEffect(() => {
134
+ dispatch({ type: 'SET_MODE', mode: vimMode });
135
+ }, [vimMode]);
136
+
137
+ // Helper to update mode in both reducer and context
138
+ const updateMode = useCallback(
139
+ (mode: VimMode) => {
140
+ setVimMode(mode);
141
+ dispatch({ type: 'SET_MODE', mode });
142
+ },
143
+ [setVimMode],
144
+ );
145
+
146
+ // Helper functions using the reducer state
147
+ const getCurrentCount = useCallback(
148
+ () => state.count || DEFAULT_COUNT,
149
+ [state.count],
150
+ );
151
+
152
+ /** Executes common commands to eliminate duplication in dot (.) repeat command */
153
+ const executeCommand = useCallback(
154
+ (cmdType: string, count: number) => {
155
+ switch (cmdType) {
156
+ case CMD_TYPES.DELETE_WORD_FORWARD: {
157
+ buffer.vimDeleteWordForward(count);
158
+ break;
159
+ }
160
+
161
+ case CMD_TYPES.DELETE_WORD_BACKWARD: {
162
+ buffer.vimDeleteWordBackward(count);
163
+ break;
164
+ }
165
+
166
+ case CMD_TYPES.DELETE_WORD_END: {
167
+ buffer.vimDeleteWordEnd(count);
168
+ break;
169
+ }
170
+
171
+ case CMD_TYPES.CHANGE_WORD_FORWARD: {
172
+ buffer.vimChangeWordForward(count);
173
+ updateMode('INSERT');
174
+ break;
175
+ }
176
+
177
+ case CMD_TYPES.CHANGE_WORD_BACKWARD: {
178
+ buffer.vimChangeWordBackward(count);
179
+ updateMode('INSERT');
180
+ break;
181
+ }
182
+
183
+ case CMD_TYPES.CHANGE_WORD_END: {
184
+ buffer.vimChangeWordEnd(count);
185
+ updateMode('INSERT');
186
+ break;
187
+ }
188
+
189
+ case CMD_TYPES.DELETE_CHAR: {
190
+ buffer.vimDeleteChar(count);
191
+ break;
192
+ }
193
+
194
+ case CMD_TYPES.DELETE_LINE: {
195
+ buffer.vimDeleteLine(count);
196
+ break;
197
+ }
198
+
199
+ case CMD_TYPES.CHANGE_LINE: {
200
+ buffer.vimChangeLine(count);
201
+ updateMode('INSERT');
202
+ break;
203
+ }
204
+
205
+ case CMD_TYPES.CHANGE_MOVEMENT.LEFT:
206
+ case CMD_TYPES.CHANGE_MOVEMENT.DOWN:
207
+ case CMD_TYPES.CHANGE_MOVEMENT.UP:
208
+ case CMD_TYPES.CHANGE_MOVEMENT.RIGHT: {
209
+ const movementMap: Record<string, 'h' | 'j' | 'k' | 'l'> = {
210
+ [CMD_TYPES.CHANGE_MOVEMENT.LEFT]: 'h',
211
+ [CMD_TYPES.CHANGE_MOVEMENT.DOWN]: 'j',
212
+ [CMD_TYPES.CHANGE_MOVEMENT.UP]: 'k',
213
+ [CMD_TYPES.CHANGE_MOVEMENT.RIGHT]: 'l',
214
+ };
215
+ const movementType = movementMap[cmdType];
216
+ if (movementType) {
217
+ buffer.vimChangeMovement(movementType, count);
218
+ updateMode('INSERT');
219
+ }
220
+ break;
221
+ }
222
+
223
+ case CMD_TYPES.DELETE_TO_EOL: {
224
+ buffer.vimDeleteToEndOfLine();
225
+ break;
226
+ }
227
+
228
+ case CMD_TYPES.CHANGE_TO_EOL: {
229
+ buffer.vimChangeToEndOfLine();
230
+ updateMode('INSERT');
231
+ break;
232
+ }
233
+
234
+ default:
235
+ return false;
236
+ }
237
+ return true;
238
+ },
239
+ [buffer, updateMode],
240
+ );
241
+
242
+ /**
243
+ * Handles key input in INSERT mode
244
+ * @param normalizedKey - The normalized key input
245
+ * @returns boolean indicating if the key was handled
246
+ */
247
+ const handleInsertModeInput = useCallback(
248
+ (normalizedKey: Key): boolean => {
249
+ // Handle escape key immediately - switch to NORMAL mode on any escape
250
+ if (normalizedKey.name === 'escape') {
251
+ // Vim behavior: move cursor left when exiting insert mode (unless at beginning of line)
252
+ buffer.vimEscapeInsertMode();
253
+ dispatch({ type: 'ESCAPE_TO_NORMAL' });
254
+ updateMode('NORMAL');
255
+ return true;
256
+ }
257
+
258
+ // In INSERT mode, let InputPrompt handle completion keys and special commands
259
+ if (
260
+ normalizedKey.name === 'tab' ||
261
+ (normalizedKey.name === 'return' && !normalizedKey.ctrl) ||
262
+ normalizedKey.name === 'up' ||
263
+ normalizedKey.name === 'down' ||
264
+ (normalizedKey.ctrl && normalizedKey.name === 'r')
265
+ ) {
266
+ return false; // Let InputPrompt handle completion
267
+ }
268
+
269
+ // Let InputPrompt handle Ctrl+V for clipboard image pasting
270
+ if (normalizedKey.ctrl && normalizedKey.name === 'v') {
271
+ return false; // Let InputPrompt handle clipboard functionality
272
+ }
273
+
274
+ // Let InputPrompt handle shell commands
275
+ if (normalizedKey.sequence === '!' && buffer.text.length === 0) {
276
+ return false;
277
+ }
278
+
279
+ // Special handling for Enter key to allow command submission (lower priority than completion)
280
+ if (
281
+ normalizedKey.name === 'return' &&
282
+ !normalizedKey.ctrl &&
283
+ !normalizedKey.meta
284
+ ) {
285
+ if (buffer.text.trim() && onSubmit) {
286
+ // Handle command submission directly
287
+ const submittedValue = buffer.text;
288
+ buffer.setText('');
289
+ onSubmit(submittedValue);
290
+ return true;
291
+ }
292
+ return true; // Handled by vim (even if no onSubmit callback)
293
+ }
294
+
295
+ // useKeypress already provides the correct format for TextBuffer
296
+ buffer.handleInput(normalizedKey);
297
+ return true; // Handled by vim
298
+ },
299
+ [buffer, dispatch, updateMode, onSubmit],
300
+ );
301
+
302
+ /**
303
+ * Normalizes key input to ensure all required properties are present
304
+ * @param key - Raw key input
305
+ * @returns Normalized key with all properties
306
+ */
307
+ const normalizeKey = useCallback(
308
+ (key: Key): Key => ({
309
+ name: key.name || '',
310
+ sequence: key.sequence || '',
311
+ ctrl: key.ctrl || false,
312
+ meta: key.meta || false,
313
+ shift: key.shift || false,
314
+ paste: key.paste || false,
315
+ }),
316
+ [],
317
+ );
318
+
319
+ /**
320
+ * Handles change movement commands (ch, cj, ck, cl)
321
+ * @param movement - The movement direction
322
+ * @returns boolean indicating if command was handled
323
+ */
324
+ const handleChangeMovement = useCallback(
325
+ (movement: 'h' | 'j' | 'k' | 'l'): boolean => {
326
+ const count = getCurrentCount();
327
+ dispatch({ type: 'CLEAR_COUNT' });
328
+ buffer.vimChangeMovement(movement, count);
329
+ updateMode('INSERT');
330
+
331
+ const cmdTypeMap = {
332
+ h: CMD_TYPES.CHANGE_MOVEMENT.LEFT,
333
+ j: CMD_TYPES.CHANGE_MOVEMENT.DOWN,
334
+ k: CMD_TYPES.CHANGE_MOVEMENT.UP,
335
+ l: CMD_TYPES.CHANGE_MOVEMENT.RIGHT,
336
+ };
337
+
338
+ dispatch({
339
+ type: 'SET_LAST_COMMAND',
340
+ command: { type: cmdTypeMap[movement], count },
341
+ });
342
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: null });
343
+ return true;
344
+ },
345
+ [getCurrentCount, dispatch, buffer, updateMode],
346
+ );
347
+
348
+ /**
349
+ * Handles operator-motion commands (dw/cw, db/cb, de/ce)
350
+ * @param operator - The operator type ('d' for delete, 'c' for change)
351
+ * @param motion - The motion type ('w', 'b', 'e')
352
+ * @returns boolean indicating if command was handled
353
+ */
354
+ const handleOperatorMotion = useCallback(
355
+ (operator: 'd' | 'c', motion: 'w' | 'b' | 'e'): boolean => {
356
+ const count = getCurrentCount();
357
+
358
+ const commandMap = {
359
+ d: {
360
+ w: CMD_TYPES.DELETE_WORD_FORWARD,
361
+ b: CMD_TYPES.DELETE_WORD_BACKWARD,
362
+ e: CMD_TYPES.DELETE_WORD_END,
363
+ },
364
+ c: {
365
+ w: CMD_TYPES.CHANGE_WORD_FORWARD,
366
+ b: CMD_TYPES.CHANGE_WORD_BACKWARD,
367
+ e: CMD_TYPES.CHANGE_WORD_END,
368
+ },
369
+ };
370
+
371
+ const cmdType = commandMap[operator][motion];
372
+ executeCommand(cmdType, count);
373
+
374
+ dispatch({
375
+ type: 'SET_LAST_COMMAND',
376
+ command: { type: cmdType, count },
377
+ });
378
+ dispatch({ type: 'CLEAR_COUNT' });
379
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: null });
380
+
381
+ return true;
382
+ },
383
+ [getCurrentCount, executeCommand, dispatch],
384
+ );
385
+
386
+ const handleInput = useCallback(
387
+ (key: Key): boolean => {
388
+ if (!vimEnabled) {
389
+ return false; // Let InputPrompt handle it
390
+ }
391
+
392
+ let normalizedKey: Key;
393
+ try {
394
+ normalizedKey = normalizeKey(key);
395
+ } catch (error) {
396
+ // Handle malformed key inputs gracefully
397
+ console.warn('Malformed key input in vim mode:', key, error);
398
+ return false;
399
+ }
400
+
401
+ // Handle INSERT mode
402
+ if (state.mode === 'INSERT') {
403
+ return handleInsertModeInput(normalizedKey);
404
+ }
405
+
406
+ // Handle NORMAL mode
407
+ if (state.mode === 'NORMAL') {
408
+ // If in NORMAL mode, allow escape to pass through to other handlers
409
+ // if there's no pending operation.
410
+ if (normalizedKey.name === 'escape') {
411
+ if (state.pendingOperator) {
412
+ dispatch({ type: 'CLEAR_PENDING_STATES' });
413
+ return true; // Handled by vim
414
+ }
415
+ return false; // Pass through to other handlers
416
+ }
417
+
418
+ // Handle count input (numbers 1-9, and 0 if count > 0)
419
+ if (
420
+ DIGIT_1_TO_9.test(normalizedKey.sequence) ||
421
+ (normalizedKey.sequence === '0' && state.count > 0)
422
+ ) {
423
+ dispatch({
424
+ type: 'INCREMENT_COUNT',
425
+ digit: parseInt(normalizedKey.sequence, 10),
426
+ });
427
+ return true; // Handled by vim
428
+ }
429
+
430
+ const repeatCount = getCurrentCount();
431
+
432
+ switch (normalizedKey.sequence) {
433
+ case 'h': {
434
+ // Check if this is part of a change command (ch)
435
+ if (state.pendingOperator === 'c') {
436
+ return handleChangeMovement('h');
437
+ }
438
+
439
+ // Normal left movement
440
+ buffer.vimMoveLeft(repeatCount);
441
+ dispatch({ type: 'CLEAR_COUNT' });
442
+ return true;
443
+ }
444
+
445
+ case 'j': {
446
+ // Check if this is part of a change command (cj)
447
+ if (state.pendingOperator === 'c') {
448
+ return handleChangeMovement('j');
449
+ }
450
+
451
+ // Normal down movement
452
+ buffer.vimMoveDown(repeatCount);
453
+ dispatch({ type: 'CLEAR_COUNT' });
454
+ return true;
455
+ }
456
+
457
+ case 'k': {
458
+ // Check if this is part of a change command (ck)
459
+ if (state.pendingOperator === 'c') {
460
+ return handleChangeMovement('k');
461
+ }
462
+
463
+ // Normal up movement
464
+ buffer.vimMoveUp(repeatCount);
465
+ dispatch({ type: 'CLEAR_COUNT' });
466
+ return true;
467
+ }
468
+
469
+ case 'l': {
470
+ // Check if this is part of a change command (cl)
471
+ if (state.pendingOperator === 'c') {
472
+ return handleChangeMovement('l');
473
+ }
474
+
475
+ // Normal right movement
476
+ buffer.vimMoveRight(repeatCount);
477
+ dispatch({ type: 'CLEAR_COUNT' });
478
+ return true;
479
+ }
480
+
481
+ case 'w': {
482
+ // Check if this is part of a delete or change command (dw/cw)
483
+ if (state.pendingOperator === 'd') {
484
+ return handleOperatorMotion('d', 'w');
485
+ }
486
+ if (state.pendingOperator === 'c') {
487
+ return handleOperatorMotion('c', 'w');
488
+ }
489
+
490
+ // Normal word movement
491
+ buffer.vimMoveWordForward(repeatCount);
492
+ dispatch({ type: 'CLEAR_COUNT' });
493
+ return true;
494
+ }
495
+
496
+ case 'b': {
497
+ // Check if this is part of a delete or change command (db/cb)
498
+ if (state.pendingOperator === 'd') {
499
+ return handleOperatorMotion('d', 'b');
500
+ }
501
+ if (state.pendingOperator === 'c') {
502
+ return handleOperatorMotion('c', 'b');
503
+ }
504
+
505
+ // Normal backward word movement
506
+ buffer.vimMoveWordBackward(repeatCount);
507
+ dispatch({ type: 'CLEAR_COUNT' });
508
+ return true;
509
+ }
510
+
511
+ case 'e': {
512
+ // Check if this is part of a delete or change command (de/ce)
513
+ if (state.pendingOperator === 'd') {
514
+ return handleOperatorMotion('d', 'e');
515
+ }
516
+ if (state.pendingOperator === 'c') {
517
+ return handleOperatorMotion('c', 'e');
518
+ }
519
+
520
+ // Normal word end movement
521
+ buffer.vimMoveWordEnd(repeatCount);
522
+ dispatch({ type: 'CLEAR_COUNT' });
523
+ return true;
524
+ }
525
+
526
+ case 'x': {
527
+ // Delete character under cursor
528
+ buffer.vimDeleteChar(repeatCount);
529
+ dispatch({
530
+ type: 'SET_LAST_COMMAND',
531
+ command: { type: CMD_TYPES.DELETE_CHAR, count: repeatCount },
532
+ });
533
+ dispatch({ type: 'CLEAR_COUNT' });
534
+ return true;
535
+ }
536
+
537
+ case 'i': {
538
+ // Enter INSERT mode at current position
539
+ buffer.vimInsertAtCursor();
540
+ updateMode('INSERT');
541
+ dispatch({ type: 'CLEAR_COUNT' });
542
+ return true;
543
+ }
544
+
545
+ case 'a': {
546
+ // Enter INSERT mode after current position
547
+ buffer.vimAppendAtCursor();
548
+ updateMode('INSERT');
549
+ dispatch({ type: 'CLEAR_COUNT' });
550
+ return true;
551
+ }
552
+
553
+ case 'o': {
554
+ // Insert new line after current line and enter INSERT mode
555
+ buffer.vimOpenLineBelow();
556
+ updateMode('INSERT');
557
+ dispatch({ type: 'CLEAR_COUNT' });
558
+ return true;
559
+ }
560
+
561
+ case 'O': {
562
+ // Insert new line before current line and enter INSERT mode
563
+ buffer.vimOpenLineAbove();
564
+ updateMode('INSERT');
565
+ dispatch({ type: 'CLEAR_COUNT' });
566
+ return true;
567
+ }
568
+
569
+ case '0': {
570
+ // Move to start of line
571
+ buffer.vimMoveToLineStart();
572
+ dispatch({ type: 'CLEAR_COUNT' });
573
+ return true;
574
+ }
575
+
576
+ case '$': {
577
+ // Move to end of line
578
+ buffer.vimMoveToLineEnd();
579
+ dispatch({ type: 'CLEAR_COUNT' });
580
+ return true;
581
+ }
582
+
583
+ case '^': {
584
+ // Move to first non-whitespace character
585
+ buffer.vimMoveToFirstNonWhitespace();
586
+ dispatch({ type: 'CLEAR_COUNT' });
587
+ return true;
588
+ }
589
+
590
+ case 'g': {
591
+ if (state.pendingOperator === 'g') {
592
+ // Second 'g' - go to first line (gg command)
593
+ buffer.vimMoveToFirstLine();
594
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: null });
595
+ } else {
596
+ // First 'g' - wait for second g
597
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: 'g' });
598
+ }
599
+ dispatch({ type: 'CLEAR_COUNT' });
600
+ return true;
601
+ }
602
+
603
+ case 'G': {
604
+ if (state.count > 0) {
605
+ // Go to specific line number (1-based) when a count was provided
606
+ buffer.vimMoveToLine(state.count);
607
+ } else {
608
+ // Go to last line when no count was provided
609
+ buffer.vimMoveToLastLine();
610
+ }
611
+ dispatch({ type: 'CLEAR_COUNT' });
612
+ return true;
613
+ }
614
+
615
+ case 'I': {
616
+ // Enter INSERT mode at start of line (first non-whitespace)
617
+ buffer.vimInsertAtLineStart();
618
+ updateMode('INSERT');
619
+ dispatch({ type: 'CLEAR_COUNT' });
620
+ return true;
621
+ }
622
+
623
+ case 'A': {
624
+ // Enter INSERT mode at end of line
625
+ buffer.vimAppendAtLineEnd();
626
+ updateMode('INSERT');
627
+ dispatch({ type: 'CLEAR_COUNT' });
628
+ return true;
629
+ }
630
+
631
+ case 'd': {
632
+ if (state.pendingOperator === 'd') {
633
+ // Second 'd' - delete N lines (dd command)
634
+ const repeatCount = getCurrentCount();
635
+ executeCommand(CMD_TYPES.DELETE_LINE, repeatCount);
636
+ dispatch({
637
+ type: 'SET_LAST_COMMAND',
638
+ command: { type: CMD_TYPES.DELETE_LINE, count: repeatCount },
639
+ });
640
+ dispatch({ type: 'CLEAR_COUNT' });
641
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: null });
642
+ } else {
643
+ // First 'd' - wait for movement command
644
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: 'd' });
645
+ }
646
+ return true;
647
+ }
648
+
649
+ case 'c': {
650
+ if (state.pendingOperator === 'c') {
651
+ // Second 'c' - change N entire lines (cc command)
652
+ const repeatCount = getCurrentCount();
653
+ executeCommand(CMD_TYPES.CHANGE_LINE, repeatCount);
654
+ dispatch({
655
+ type: 'SET_LAST_COMMAND',
656
+ command: { type: CMD_TYPES.CHANGE_LINE, count: repeatCount },
657
+ });
658
+ dispatch({ type: 'CLEAR_COUNT' });
659
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: null });
660
+ } else {
661
+ // First 'c' - wait for movement command
662
+ dispatch({ type: 'SET_PENDING_OPERATOR', operator: 'c' });
663
+ }
664
+ return true;
665
+ }
666
+
667
+ case 'D': {
668
+ // Delete from cursor to end of line (equivalent to d$)
669
+ executeCommand(CMD_TYPES.DELETE_TO_EOL, 1);
670
+ dispatch({
671
+ type: 'SET_LAST_COMMAND',
672
+ command: { type: CMD_TYPES.DELETE_TO_EOL, count: 1 },
673
+ });
674
+ dispatch({ type: 'CLEAR_COUNT' });
675
+ return true;
676
+ }
677
+
678
+ case 'C': {
679
+ // Change from cursor to end of line (equivalent to c$)
680
+ executeCommand(CMD_TYPES.CHANGE_TO_EOL, 1);
681
+ dispatch({
682
+ type: 'SET_LAST_COMMAND',
683
+ command: { type: CMD_TYPES.CHANGE_TO_EOL, count: 1 },
684
+ });
685
+ dispatch({ type: 'CLEAR_COUNT' });
686
+ return true;
687
+ }
688
+
689
+ case '.': {
690
+ // Repeat last command
691
+ if (state.lastCommand) {
692
+ const cmdData = state.lastCommand;
693
+
694
+ // All repeatable commands are now handled by executeCommand
695
+ executeCommand(cmdData.type, cmdData.count);
696
+ }
697
+
698
+ dispatch({ type: 'CLEAR_COUNT' });
699
+ return true;
700
+ }
701
+
702
+ default: {
703
+ // Check for arrow keys (they have different sequences but known names)
704
+ if (normalizedKey.name === 'left') {
705
+ // Left arrow - same as 'h'
706
+ if (state.pendingOperator === 'c') {
707
+ return handleChangeMovement('h');
708
+ }
709
+
710
+ // Normal left movement (same as 'h')
711
+ buffer.vimMoveLeft(repeatCount);
712
+ dispatch({ type: 'CLEAR_COUNT' });
713
+ return true;
714
+ }
715
+
716
+ if (normalizedKey.name === 'down') {
717
+ // Down arrow - same as 'j'
718
+ if (state.pendingOperator === 'c') {
719
+ return handleChangeMovement('j');
720
+ }
721
+
722
+ // Normal down movement (same as 'j')
723
+ buffer.vimMoveDown(repeatCount);
724
+ dispatch({ type: 'CLEAR_COUNT' });
725
+ return true;
726
+ }
727
+
728
+ if (normalizedKey.name === 'up') {
729
+ // Up arrow - same as 'k'
730
+ if (state.pendingOperator === 'c') {
731
+ return handleChangeMovement('k');
732
+ }
733
+
734
+ // Normal up movement (same as 'k')
735
+ buffer.vimMoveUp(repeatCount);
736
+ dispatch({ type: 'CLEAR_COUNT' });
737
+ return true;
738
+ }
739
+
740
+ if (normalizedKey.name === 'right') {
741
+ // Right arrow - same as 'l'
742
+ if (state.pendingOperator === 'c') {
743
+ return handleChangeMovement('l');
744
+ }
745
+
746
+ // Normal right movement (same as 'l')
747
+ buffer.vimMoveRight(repeatCount);
748
+ dispatch({ type: 'CLEAR_COUNT' });
749
+ return true;
750
+ }
751
+
752
+ // Unknown command, clear count and pending states
753
+ dispatch({ type: 'CLEAR_PENDING_STATES' });
754
+ return true; // Still handled by vim to prevent other handlers
755
+ }
756
+ }
757
+ }
758
+
759
+ return false; // Not handled by vim
760
+ },
761
+ [
762
+ vimEnabled,
763
+ normalizeKey,
764
+ handleInsertModeInput,
765
+ state.mode,
766
+ state.count,
767
+ state.pendingOperator,
768
+ state.lastCommand,
769
+ dispatch,
770
+ getCurrentCount,
771
+ handleChangeMovement,
772
+ handleOperatorMotion,
773
+ buffer,
774
+ executeCommand,
775
+ updateMode,
776
+ ],
777
+ );
778
+
779
+ return {
780
+ mode: state.mode,
781
+ vimModeEnabled: vimEnabled,
782
+ handleInput, // Expose the input handler for InputPrompt to use
783
+ };
784
+ }