@machina.ai/cell-cli 1.11.0-rc1 → 1.13.0-rc1

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 (538) hide show
  1. package/dist/package.json +12 -10
  2. package/dist/src/commands/extensions/disable.d.ts +1 -1
  3. package/dist/src/commands/extensions/disable.js +15 -7
  4. package/dist/src/commands/extensions/disable.js.map +1 -1
  5. package/dist/src/commands/extensions/enable.d.ts +1 -1
  6. package/dist/src/commands/extensions/enable.js +15 -7
  7. package/dist/src/commands/extensions/enable.js.map +1 -1
  8. package/dist/src/commands/extensions/install.js +14 -3
  9. package/dist/src/commands/extensions/install.js.map +1 -1
  10. package/dist/src/commands/extensions/install.test.js +39 -19
  11. package/dist/src/commands/extensions/install.test.js.map +1 -1
  12. package/dist/src/commands/extensions/link.js +14 -3
  13. package/dist/src/commands/extensions/link.js.map +1 -1
  14. package/dist/src/commands/extensions/list.js +13 -4
  15. package/dist/src/commands/extensions/list.js.map +1 -1
  16. package/dist/src/commands/extensions/uninstall.js +13 -2
  17. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  18. package/dist/src/commands/extensions/update.js +18 -13
  19. package/dist/src/commands/extensions/update.js.map +1 -1
  20. package/dist/src/commands/extensions/validate.d.ts +12 -0
  21. package/dist/src/commands/extensions/validate.js +83 -0
  22. package/dist/src/commands/extensions/validate.js.map +1 -0
  23. package/dist/src/commands/extensions/validate.test.js +93 -0
  24. package/dist/src/commands/extensions/validate.test.js.map +1 -0
  25. package/dist/src/commands/extensions.js +3 -0
  26. package/dist/src/commands/extensions.js.map +1 -1
  27. package/dist/src/commands/mcp/add.test.js +3 -0
  28. package/dist/src/commands/mcp/add.test.js.map +1 -1
  29. package/dist/src/commands/mcp/list.js +10 -3
  30. package/dist/src/commands/mcp/list.js.map +1 -1
  31. package/dist/src/commands/mcp/list.test.js +37 -27
  32. package/dist/src/commands/mcp/list.test.js.map +1 -1
  33. package/dist/src/config/auth.js +0 -5
  34. package/dist/src/config/auth.js.map +1 -1
  35. package/dist/src/config/config.d.ts +6 -3
  36. package/dist/src/config/config.js +65 -80
  37. package/dist/src/config/config.js.map +1 -1
  38. package/dist/src/config/config.test.js +235 -212
  39. package/dist/src/config/config.test.js.map +1 -1
  40. package/dist/src/config/extension-manager.d.ts +63 -0
  41. package/dist/src/config/extension-manager.js +450 -0
  42. package/dist/src/config/extension-manager.js.map +1 -0
  43. package/dist/src/config/extension.d.ts +4 -51
  44. package/dist/src/config/extension.js +1 -535
  45. package/dist/src/config/extension.js.map +1 -1
  46. package/dist/src/config/extension.test.js +525 -201
  47. package/dist/src/config/extension.test.js.map +1 -1
  48. package/dist/src/config/extensions/consent.d.ts +38 -0
  49. package/dist/src/config/extensions/consent.js +123 -0
  50. package/dist/src/config/extensions/consent.js.map +1 -0
  51. package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
  52. package/dist/src/config/extensions/extensionEnablement.js +4 -3
  53. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  54. package/dist/src/config/extensions/extensionEnablement.test.js +10 -10
  55. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  56. package/dist/src/config/extensions/extensionSettings.d.ts +15 -0
  57. package/dist/src/config/extensions/extensionSettings.js +113 -0
  58. package/dist/src/config/extensions/extensionSettings.js.map +1 -0
  59. package/dist/src/config/extensions/extensionSettings.test.d.ts +6 -0
  60. package/dist/src/config/extensions/extensionSettings.test.js +254 -0
  61. package/dist/src/config/extensions/extensionSettings.test.js.map +1 -0
  62. package/dist/src/config/extensions/github.d.ts +2 -2
  63. package/dist/src/config/extensions/github.js +5 -10
  64. package/dist/src/config/extensions/github.js.map +1 -1
  65. package/dist/src/config/extensions/github.test.js +153 -167
  66. package/dist/src/config/extensions/github.test.js.map +1 -1
  67. package/dist/src/config/extensions/github_fetch.d.ts +1 -1
  68. package/dist/src/config/extensions/github_fetch.js +13 -1
  69. package/dist/src/config/extensions/github_fetch.js.map +1 -1
  70. package/dist/src/config/extensions/github_fetch.test.d.ts +6 -0
  71. package/dist/src/config/extensions/github_fetch.test.js +169 -0
  72. package/dist/src/config/extensions/github_fetch.test.js.map +1 -0
  73. package/dist/src/config/extensions/storage.d.ts +14 -0
  74. package/dist/src/config/extensions/storage.js +32 -0
  75. package/dist/src/config/extensions/storage.js.map +1 -0
  76. package/dist/src/config/extensions/update.d.ts +4 -4
  77. package/dist/src/config/extensions/update.js +39 -39
  78. package/dist/src/config/extensions/update.js.map +1 -1
  79. package/dist/src/config/extensions/update.test.js +72 -74
  80. package/dist/src/config/extensions/update.test.js.map +1 -1
  81. package/dist/src/config/extensions/variableSchema.d.ts +0 -6
  82. package/dist/src/config/extensions/variableSchema.js.map +1 -1
  83. package/dist/src/config/extensions/variables.d.ts +4 -0
  84. package/dist/src/config/extensions/variables.js +6 -0
  85. package/dist/src/config/extensions/variables.js.map +1 -1
  86. package/dist/src/config/keyBindings.d.ts +3 -0
  87. package/dist/src/config/keyBindings.js +30 -8
  88. package/dist/src/config/keyBindings.js.map +1 -1
  89. package/dist/src/config/keyBindings.test.js +17 -0
  90. package/dist/src/config/keyBindings.test.js.map +1 -1
  91. package/dist/src/config/policies/read-only.toml +56 -0
  92. package/dist/src/config/policies/write.toml +63 -0
  93. package/dist/src/config/policies/yolo.toml +31 -0
  94. package/dist/src/config/policy-engine.integration.test.js +41 -38
  95. package/dist/src/config/policy-engine.integration.test.js.map +1 -1
  96. package/dist/src/config/policy.d.ts +2 -2
  97. package/dist/src/config/policy.js +10 -148
  98. package/dist/src/config/policy.js.map +1 -1
  99. package/dist/src/config/sandboxConfig.d.ts +1 -1
  100. package/dist/src/config/sandboxConfig.js +6 -3
  101. package/dist/src/config/sandboxConfig.js.map +1 -1
  102. package/dist/src/config/settings.d.ts +2 -1
  103. package/dist/src/config/settings.js +58 -18
  104. package/dist/src/config/settings.js.map +1 -1
  105. package/dist/src/config/settings.test.js +128 -69
  106. package/dist/src/config/settings.test.js.map +1 -1
  107. package/dist/src/config/settingsSchema.d.ts +170 -28
  108. package/dist/src/config/settingsSchema.js +418 -27
  109. package/dist/src/config/settingsSchema.js.map +1 -1
  110. package/dist/src/config/settingsSchema.test.js +42 -1
  111. package/dist/src/config/settingsSchema.test.js.map +1 -1
  112. package/dist/src/config/trustedFolders.d.ts +1 -1
  113. package/dist/src/config/trustedFolders.js +4 -2
  114. package/dist/src/config/trustedFolders.js.map +1 -1
  115. package/dist/src/core/initializer.js +2 -1
  116. package/dist/src/core/initializer.js.map +1 -1
  117. package/dist/src/gemini.d.ts +1 -1
  118. package/dist/src/gemini.js +46 -16
  119. package/dist/src/gemini.js.map +1 -1
  120. package/dist/src/gemini.test.js +88 -30
  121. package/dist/src/gemini.test.js.map +1 -1
  122. package/dist/src/generated/git-commit.d.ts +2 -2
  123. package/dist/src/generated/git-commit.js +2 -2
  124. package/dist/src/nonInteractiveCli.d.ts +9 -1
  125. package/dist/src/nonInteractiveCli.js +114 -7
  126. package/dist/src/nonInteractiveCli.js.map +1 -1
  127. package/dist/src/nonInteractiveCli.test.js +355 -112
  128. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  129. package/dist/src/services/BuiltinCommandLoader.js +4 -0
  130. package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
  131. package/dist/src/services/BuiltinCommandLoader.test.js +22 -0
  132. package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
  133. package/dist/src/services/FeedbackService.js +2 -2
  134. package/dist/src/services/FeedbackService.js.map +1 -1
  135. package/dist/src/services/McpPromptLoader.js +2 -2
  136. package/dist/src/services/McpPromptLoader.js.map +1 -1
  137. package/dist/src/services/McpPromptLoader.test.js +4 -2
  138. package/dist/src/services/McpPromptLoader.test.js.map +1 -1
  139. package/dist/src/test-utils/async.d.ts +9 -0
  140. package/dist/src/test-utils/async.js +29 -0
  141. package/dist/src/test-utils/async.js.map +1 -0
  142. package/dist/src/test-utils/createExtension.d.ts +3 -1
  143. package/dist/src/test-utils/createExtension.js +3 -3
  144. package/dist/src/test-utils/createExtension.js.map +1 -1
  145. package/dist/src/test-utils/render.d.ts +16 -2
  146. package/dist/src/test-utils/render.js +66 -4
  147. package/dist/src/test-utils/render.js.map +1 -1
  148. package/dist/src/test-utils/render.test.d.ts +6 -0
  149. package/dist/src/test-utils/render.test.js +79 -0
  150. package/dist/src/test-utils/render.test.js.map +1 -0
  151. package/dist/src/ui/App.test.js +1 -1
  152. package/dist/src/ui/App.test.js.map +1 -1
  153. package/dist/src/ui/AppContainer.js +181 -65
  154. package/dist/src/ui/AppContainer.js.map +1 -1
  155. package/dist/src/ui/AppContainer.test.js +505 -147
  156. package/dist/src/ui/AppContainer.test.js.map +1 -1
  157. package/dist/src/ui/IdeIntegrationNudge.js +1 -1
  158. package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
  159. package/dist/src/ui/auth/ApiAuthDialog.d.ts +14 -0
  160. package/dist/src/ui/auth/ApiAuthDialog.js +26 -0
  161. package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -0
  162. package/dist/src/ui/auth/ApiAuthDialog.test.d.ts +6 -0
  163. package/dist/src/ui/auth/ApiAuthDialog.test.js +91 -0
  164. package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -0
  165. package/dist/src/ui/auth/AuthDialog.js +7 -3
  166. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  167. package/dist/src/ui/auth/useAuth.d.ts +2 -0
  168. package/dist/src/ui/auth/useAuth.js +31 -2
  169. package/dist/src/ui/auth/useAuth.js.map +1 -1
  170. package/dist/src/ui/colors.js +3 -0
  171. package/dist/src/ui/colors.js.map +1 -1
  172. package/dist/src/ui/commands/directoryCommand.js +1 -1
  173. package/dist/src/ui/commands/directoryCommand.js.map +1 -1
  174. package/dist/src/ui/commands/extensionsCommand.js +64 -11
  175. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  176. package/dist/src/ui/commands/extensionsCommand.test.js +72 -1
  177. package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
  178. package/dist/src/ui/commands/mcpCommand.js +14 -14
  179. package/dist/src/ui/commands/mcpCommand.js.map +1 -1
  180. package/dist/src/ui/commands/mcpCommand.test.js +4 -0
  181. package/dist/src/ui/commands/mcpCommand.test.js.map +1 -1
  182. package/dist/src/ui/commands/memoryCommand.js +1 -1
  183. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  184. package/dist/src/ui/commands/memoryCommand.test.js +3 -1
  185. package/dist/src/ui/commands/memoryCommand.test.js.map +1 -1
  186. package/dist/src/ui/commands/policiesCommand.d.ts +7 -0
  187. package/dist/src/ui/commands/policiesCommand.js +59 -0
  188. package/dist/src/ui/commands/policiesCommand.js.map +1 -0
  189. package/dist/src/ui/commands/policiesCommand.test.d.ts +6 -0
  190. package/dist/src/ui/commands/policiesCommand.test.js +83 -0
  191. package/dist/src/ui/commands/policiesCommand.test.js.map +1 -0
  192. package/dist/src/ui/components/AnsiOutput.test.js +1 -1
  193. package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
  194. package/dist/src/ui/components/AsciiArt.d.ts +3 -3
  195. package/dist/src/ui/components/AsciiArt.js +3 -3
  196. package/dist/src/ui/components/Composer.js +1 -1
  197. package/dist/src/ui/components/Composer.js.map +1 -1
  198. package/dist/src/ui/components/Composer.test.js +5 -2
  199. package/dist/src/ui/components/Composer.test.js.map +1 -1
  200. package/dist/src/ui/components/ConfigInitDisplay.js +4 -6
  201. package/dist/src/ui/components/ConfigInitDisplay.js.map +1 -1
  202. package/dist/src/ui/components/ConsentPrompt.test.js +18 -8
  203. package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -1
  204. package/dist/src/ui/components/ConsoleSummaryDisplay.js +1 -1
  205. package/dist/src/ui/components/ConsoleSummaryDisplay.js.map +1 -1
  206. package/dist/src/ui/components/ContextSummaryDisplay.test.js +11 -6
  207. package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -1
  208. package/dist/src/ui/components/DetailedMessagesDisplay.js +1 -1
  209. package/dist/src/ui/components/DetailedMessagesDisplay.js.map +1 -1
  210. package/dist/src/ui/components/DialogManager.js +4 -0
  211. package/dist/src/ui/components/DialogManager.js.map +1 -1
  212. package/dist/src/ui/components/FolderTrustDialog.test.js +2 -1
  213. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  214. package/dist/src/ui/components/Footer.js +4 -3
  215. package/dist/src/ui/components/Footer.js.map +1 -1
  216. package/dist/src/ui/components/Footer.test.js +83 -0
  217. package/dist/src/ui/components/Footer.test.js.map +1 -1
  218. package/dist/src/ui/components/Header.test.js +13 -5
  219. package/dist/src/ui/components/Header.test.js.map +1 -1
  220. package/dist/src/ui/components/Help.test.js +5 -4
  221. package/dist/src/ui/components/Help.test.js.map +1 -1
  222. package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
  223. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  224. package/dist/src/ui/components/InputPrompt.js +27 -8
  225. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  226. package/dist/src/ui/components/InputPrompt.test.js +776 -727
  227. package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  228. package/dist/src/ui/components/LoadingIndicator.js +2 -2
  229. package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
  230. package/dist/src/ui/components/LoadingIndicator.test.js +28 -15
  231. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  232. package/dist/src/ui/components/LoopDetectionConfirmation.js +1 -1
  233. package/dist/src/ui/components/LoopDetectionConfirmation.js.map +1 -1
  234. package/dist/src/ui/components/LoopDetectionConfirmation.test.js +2 -2
  235. package/dist/src/ui/components/LoopDetectionConfirmation.test.js.map +1 -1
  236. package/dist/src/ui/components/MainContent.js +15 -4
  237. package/dist/src/ui/components/MainContent.js.map +1 -1
  238. package/dist/src/ui/components/ModelDialog.js +1 -1
  239. package/dist/src/ui/components/ModelDialog.js.map +1 -1
  240. package/dist/src/ui/components/ModelDialog.test.js +23 -13
  241. package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
  242. package/dist/src/ui/components/ModelStatsDisplay.test.js +1 -1
  243. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -1
  244. package/dist/src/ui/components/Notifications.js +38 -5
  245. package/dist/src/ui/components/Notifications.js.map +1 -1
  246. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +2 -2
  247. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  248. package/dist/src/ui/components/PrepareLabel.test.js +14 -8
  249. package/dist/src/ui/components/PrepareLabel.test.js.map +1 -1
  250. package/dist/src/ui/components/ProQuotaDialog.test.js +14 -6
  251. package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
  252. package/dist/src/ui/components/QueuedMessageDisplay.test.js +11 -6
  253. package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
  254. package/dist/src/ui/components/SessionSummaryDisplay.test.js +1 -1
  255. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -1
  256. package/dist/src/ui/components/SettingsDialog.js +32 -25
  257. package/dist/src/ui/components/SettingsDialog.js.map +1 -1
  258. package/dist/src/ui/components/SettingsDialog.test.js +428 -532
  259. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  260. package/dist/src/ui/components/ShellConfirmationDialog.js +1 -1
  261. package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
  262. package/dist/src/ui/components/ShellConfirmationDialog.test.js +2 -2
  263. package/dist/src/ui/components/ShellConfirmationDialog.test.js.map +1 -1
  264. package/dist/src/ui/components/StatsDisplay.test.js +1 -1
  265. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -1
  266. package/dist/src/ui/components/SuggestionsDisplay.js +1 -1
  267. package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
  268. package/dist/src/ui/components/ThemeDialog.test.js +2 -2
  269. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  270. package/dist/src/ui/components/ToolStatsDisplay.test.js +1 -1
  271. package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -1
  272. package/dist/src/ui/components/messages/CompressionMessage.test.js +25 -17
  273. package/dist/src/ui/components/messages/CompressionMessage.test.js.map +1 -1
  274. package/dist/src/ui/components/messages/DiffRenderer.test.js +1 -1
  275. package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
  276. package/dist/src/ui/components/messages/InfoMessage.js +1 -1
  277. package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
  278. package/dist/src/ui/components/messages/Todo.js +27 -5
  279. package/dist/src/ui/components/messages/Todo.js.map +1 -1
  280. package/dist/src/ui/components/messages/Todo.test.js +20 -8
  281. package/dist/src/ui/components/messages/Todo.test.js.map +1 -1
  282. package/dist/src/ui/components/messages/ToolConfirmationMessage.js +1 -1
  283. package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
  284. package/dist/src/ui/components/messages/ToolGroupMessage.test.js +29 -15
  285. package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
  286. package/dist/src/ui/components/messages/WarningMessage.js +2 -2
  287. package/dist/src/ui/components/messages/WarningMessage.js.map +1 -1
  288. package/dist/src/ui/components/shared/BaseSelectionList.test.js +1 -1
  289. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  290. package/dist/src/ui/components/shared/MaxSizedBox.test.js +43 -22
  291. package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -1
  292. package/dist/src/ui/components/shared/TextInput.d.ts +15 -0
  293. package/dist/src/ui/components/shared/TextInput.js +38 -0
  294. package/dist/src/ui/components/shared/TextInput.js.map +1 -0
  295. package/dist/src/ui/components/shared/TextInput.test.d.ts +6 -0
  296. package/dist/src/ui/components/shared/TextInput.test.js +242 -0
  297. package/dist/src/ui/components/shared/TextInput.test.js.map +1 -0
  298. package/dist/src/ui/components/shared/text-buffer.d.ts +9 -2
  299. package/dist/src/ui/components/shared/text-buffer.js +51 -13
  300. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  301. package/dist/src/ui/components/shared/text-buffer.test.js +385 -202
  302. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
  303. package/dist/src/ui/components/views/ChatList.test.js +7 -4
  304. package/dist/src/ui/components/views/ChatList.test.js.map +1 -1
  305. package/dist/src/ui/components/views/ExtensionsList.d.ts +7 -1
  306. package/dist/src/ui/components/views/ExtensionsList.js +9 -11
  307. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
  308. package/dist/src/ui/components/views/ExtensionsList.test.js +43 -22
  309. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
  310. package/dist/src/ui/components/views/McpStatus.test.js +23 -12
  311. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
  312. package/dist/src/ui/contexts/KeypressContext.d.ts +3 -2
  313. package/dist/src/ui/contexts/KeypressContext.js +610 -540
  314. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  315. package/dist/src/ui/contexts/KeypressContext.test.js +438 -718
  316. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  317. package/dist/src/ui/contexts/MouseContext.d.ts +21 -0
  318. package/dist/src/ui/contexts/MouseContext.js +89 -0
  319. package/dist/src/ui/contexts/MouseContext.js.map +1 -0
  320. package/dist/src/ui/contexts/MouseContext.test.d.ts +6 -0
  321. package/dist/src/ui/contexts/MouseContext.test.js +164 -0
  322. package/dist/src/ui/contexts/MouseContext.test.js.map +1 -0
  323. package/dist/src/ui/contexts/SessionContext.test.js +35 -17
  324. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
  325. package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
  326. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  327. package/dist/src/ui/contexts/UIStateContext.d.ts +2 -0
  328. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  329. package/dist/src/ui/hooks/atCommandProcessor.js +31 -9
  330. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  331. package/dist/src/ui/hooks/atCommandProcessor.test.js +163 -64
  332. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  333. package/dist/src/ui/hooks/shellCommandProcessor.test.js +64 -35
  334. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  335. package/dist/src/ui/hooks/slashCommandProcessor.test.js +193 -165
  336. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
  337. package/dist/src/ui/hooks/useAtCompletion.test.js +16 -5
  338. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -1
  339. package/dist/src/ui/hooks/useAutoAcceptIndicator.js +10 -0
  340. package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
  341. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +32 -1
  342. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  343. package/dist/src/ui/hooks/useCommandCompletion.test.js +66 -64
  344. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -1
  345. package/dist/src/ui/hooks/useConsoleMessages.test.js +26 -9
  346. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -1
  347. package/dist/src/ui/hooks/useEditorSettings.test.js +40 -34
  348. package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
  349. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +14 -5
  350. package/dist/src/ui/hooks/useExtensionUpdates.js +18 -13
  351. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  352. package/dist/src/ui/hooks/useExtensionUpdates.test.js +49 -44
  353. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  354. package/dist/src/ui/hooks/useFlickerDetector.test.js +9 -5
  355. package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -1
  356. package/dist/src/ui/hooks/useFocus.test.js +25 -9
  357. package/dist/src/ui/hooks/useFocus.test.js.map +1 -1
  358. package/dist/src/ui/hooks/useFolderTrust.test.js +46 -22
  359. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
  360. package/dist/src/ui/hooks/useGeminiStream.js +56 -19
  361. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  362. package/dist/src/ui/hooks/useGeminiStream.test.js +260 -411
  363. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
  364. package/dist/src/ui/hooks/useGitBranchName.js +4 -0
  365. package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
  366. package/dist/src/ui/hooks/useGitBranchName.test.js +46 -34
  367. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  368. package/dist/src/ui/hooks/useHistoryManager.test.js +2 -1
  369. package/dist/src/ui/hooks/useHistoryManager.test.js.map +1 -1
  370. package/dist/src/ui/hooks/useIdeTrustListener.test.js +40 -9
  371. package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -1
  372. package/dist/src/ui/hooks/useInputHistory.test.js +2 -1
  373. package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -1
  374. package/dist/src/ui/hooks/useInputHistoryStore.test.js +2 -1
  375. package/dist/src/ui/hooks/useInputHistoryStore.test.js.map +1 -1
  376. package/dist/src/ui/hooks/useKeypress.test.js +103 -114
  377. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
  378. package/dist/src/ui/hooks/useLoadingIndicator.test.js +24 -6
  379. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  380. package/dist/src/ui/hooks/useMemoryMonitor.test.js +10 -5
  381. package/dist/src/ui/hooks/useMemoryMonitor.test.js.map +1 -1
  382. package/dist/src/ui/hooks/useMessageQueue.test.js +62 -45
  383. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  384. package/dist/src/ui/hooks/useModelCommand.test.js +21 -11
  385. package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
  386. package/dist/src/ui/hooks/useMouse.d.ts +17 -0
  387. package/dist/src/ui/hooks/useMouse.js +27 -0
  388. package/dist/src/ui/hooks/useMouse.js.map +1 -0
  389. package/dist/src/ui/hooks/useMouse.test.d.ts +6 -0
  390. package/dist/src/ui/hooks/useMouse.test.js +57 -0
  391. package/dist/src/ui/hooks/useMouse.test.js.map +1 -0
  392. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +2 -2
  393. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
  394. package/dist/src/ui/hooks/usePhraseCycler.js +1 -1
  395. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  396. package/dist/src/ui/hooks/usePhraseCycler.test.js +109 -106
  397. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
  398. package/dist/src/ui/hooks/usePrivacySettings.test.js +26 -6
  399. package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
  400. package/dist/src/ui/hooks/usePromptCompletion.js +2 -2
  401. package/dist/src/ui/hooks/usePromptCompletion.js.map +1 -1
  402. package/dist/src/ui/hooks/useQuotaAndFallback.js +13 -14
  403. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
  404. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +55 -48
  405. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
  406. package/dist/src/ui/hooks/useReactToolScheduler.d.ts +8 -1
  407. package/dist/src/ui/hooks/useReactToolScheduler.js +59 -34
  408. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  409. package/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +6 -0
  410. package/dist/src/ui/hooks/useReactToolScheduler.test.js +65 -0
  411. package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -0
  412. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js +2 -2
  413. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js.map +1 -1
  414. package/dist/src/ui/hooks/useSelectionList.js +5 -4
  415. package/dist/src/ui/hooks/useSelectionList.js.map +1 -1
  416. package/dist/src/ui/hooks/useSelectionList.test.js +272 -183
  417. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
  418. package/dist/src/ui/hooks/useShellHistory.test.js +52 -20
  419. package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
  420. package/dist/src/ui/hooks/useSlashCompletion.js +18 -7
  421. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  422. package/dist/src/ui/hooks/useSlashCompletion.test.js +275 -137
  423. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  424. package/dist/src/ui/hooks/useTimer.test.js +43 -14
  425. package/dist/src/ui/hooks/useTimer.test.js.map +1 -1
  426. package/dist/src/ui/hooks/useToolScheduler.test.js +226 -242
  427. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  428. package/dist/src/ui/hooks/vim.test.js +235 -355
  429. package/dist/src/ui/hooks/vim.test.js.map +1 -1
  430. package/dist/src/ui/keyMatchers.test.js +30 -3
  431. package/dist/src/ui/keyMatchers.test.js.map +1 -1
  432. package/dist/src/ui/state/extensions.d.ts +1 -0
  433. package/dist/src/ui/state/extensions.js +1 -0
  434. package/dist/src/ui/state/extensions.js.map +1 -1
  435. package/dist/src/ui/themes/ansi-light.js +1 -0
  436. package/dist/src/ui/themes/ansi-light.js.map +1 -1
  437. package/dist/src/ui/themes/ansi.js +1 -0
  438. package/dist/src/ui/themes/ansi.js.map +1 -1
  439. package/dist/src/ui/themes/atom-one-dark.js +2 -0
  440. package/dist/src/ui/themes/atom-one-dark.js.map +1 -1
  441. package/dist/src/ui/themes/ayu-light.js +2 -0
  442. package/dist/src/ui/themes/ayu-light.js.map +1 -1
  443. package/dist/src/ui/themes/ayu.js +2 -0
  444. package/dist/src/ui/themes/ayu.js.map +1 -1
  445. package/dist/src/ui/themes/color-utils.d.ts +1 -0
  446. package/dist/src/ui/themes/color-utils.js +6 -0
  447. package/dist/src/ui/themes/color-utils.js.map +1 -1
  448. package/dist/src/ui/themes/color-utils.test.js +13 -1
  449. package/dist/src/ui/themes/color-utils.test.js.map +1 -1
  450. package/dist/src/ui/themes/dracula.js +2 -0
  451. package/dist/src/ui/themes/dracula.js.map +1 -1
  452. package/dist/src/ui/themes/github-dark.js +2 -0
  453. package/dist/src/ui/themes/github-dark.js.map +1 -1
  454. package/dist/src/ui/themes/github-light.js +2 -0
  455. package/dist/src/ui/themes/github-light.js.map +1 -1
  456. package/dist/src/ui/themes/googlecode.js +2 -0
  457. package/dist/src/ui/themes/googlecode.js.map +1 -1
  458. package/dist/src/ui/themes/no-color.js +3 -0
  459. package/dist/src/ui/themes/no-color.js.map +1 -1
  460. package/dist/src/ui/themes/semantic-tokens.d.ts +2 -0
  461. package/dist/src/ui/themes/semantic-tokens.js +6 -0
  462. package/dist/src/ui/themes/semantic-tokens.js.map +1 -1
  463. package/dist/src/ui/themes/shades-of-purple.js +2 -0
  464. package/dist/src/ui/themes/shades-of-purple.js.map +1 -1
  465. package/dist/src/ui/themes/theme.d.ts +3 -0
  466. package/dist/src/ui/themes/theme.js +14 -3
  467. package/dist/src/ui/themes/theme.js.map +1 -1
  468. package/dist/src/ui/themes/theme.test.js +67 -1
  469. package/dist/src/ui/themes/theme.test.js.map +1 -1
  470. package/dist/src/ui/themes/xcode.js +2 -0
  471. package/dist/src/ui/themes/xcode.js.map +1 -1
  472. package/dist/src/ui/types.d.ts +3 -1
  473. package/dist/src/ui/types.js +2 -0
  474. package/dist/src/ui/types.js.map +1 -1
  475. package/dist/src/ui/utils/CodeColorizer.js +2 -1
  476. package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
  477. package/dist/src/ui/utils/InlineMarkdownRenderer.d.ts +1 -0
  478. package/dist/src/ui/utils/InlineMarkdownRenderer.js +11 -10
  479. package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
  480. package/dist/src/ui/utils/MarkdownDisplay.js +11 -9
  481. package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
  482. package/dist/src/ui/utils/clipboardUtils.js +2 -2
  483. package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
  484. package/dist/src/ui/utils/input.d.ts +17 -0
  485. package/dist/src/ui/utils/input.js +51 -0
  486. package/dist/src/ui/utils/input.js.map +1 -0
  487. package/dist/src/ui/utils/input.test.d.ts +6 -0
  488. package/dist/src/ui/utils/input.test.js +44 -0
  489. package/dist/src/ui/utils/input.test.js.map +1 -0
  490. package/dist/src/ui/utils/kittyProtocolDetector.js +13 -4
  491. package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
  492. package/dist/src/ui/utils/mouse.d.ts +31 -0
  493. package/dist/src/ui/utils/mouse.js +164 -0
  494. package/dist/src/ui/utils/mouse.js.map +1 -0
  495. package/dist/src/ui/utils/mouse.test.d.ts +6 -0
  496. package/dist/src/ui/utils/mouse.test.js +131 -0
  497. package/dist/src/ui/utils/mouse.test.js.map +1 -0
  498. package/dist/src/ui/utils/textOutput.d.ts +25 -0
  499. package/dist/src/ui/utils/textOutput.js +49 -0
  500. package/dist/src/ui/utils/textOutput.js.map +1 -0
  501. package/dist/src/ui/utils/textOutput.test.d.ts +6 -0
  502. package/dist/src/ui/utils/textOutput.test.js +79 -0
  503. package/dist/src/ui/utils/textOutput.test.js.map +1 -0
  504. package/dist/src/ui/utils/updateCheck.d.ts +7 -1
  505. package/dist/src/ui/utils/updateCheck.js +33 -29
  506. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  507. package/dist/src/ui/utils/updateCheck.test.js +24 -50
  508. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  509. package/dist/src/utils/commentJson.js +2 -2
  510. package/dist/src/utils/commentJson.js.map +1 -1
  511. package/dist/src/utils/commentJson.test.js +7 -6
  512. package/dist/src/utils/commentJson.test.js.map +1 -1
  513. package/dist/src/utils/envVarResolver.d.ts +2 -2
  514. package/dist/src/utils/envVarResolver.js +10 -7
  515. package/dist/src/utils/envVarResolver.js.map +1 -1
  516. package/dist/src/utils/events.d.ts +11 -2
  517. package/dist/src/utils/events.js +1 -0
  518. package/dist/src/utils/events.js.map +1 -1
  519. package/dist/src/utils/handleAutoUpdate.js +9 -3
  520. package/dist/src/utils/handleAutoUpdate.js.map +1 -1
  521. package/dist/src/utils/sandbox.js +16 -18
  522. package/dist/src/utils/sandbox.js.map +1 -1
  523. package/dist/src/utils/version.js +6 -2
  524. package/dist/src/utils/version.js.map +1 -1
  525. package/dist/src/zed-integration/acp.js +2 -1
  526. package/dist/src/zed-integration/acp.js.map +1 -1
  527. package/dist/src/zed-integration/schema.d.ts +4 -4
  528. package/dist/src/zed-integration/zedIntegration.d.ts +2 -2
  529. package/dist/src/zed-integration/zedIntegration.js +12 -19
  530. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  531. package/dist/tsconfig.tsbuildinfo +1 -1
  532. package/package.json +14 -14
  533. package/dist/src/config/policy.test.js +0 -360
  534. package/dist/src/config/policy.test.js.map +0 -1
  535. package/dist/src/utils/package.d.ts +0 -12
  536. package/dist/src/utils/package.js +0 -24
  537. package/dist/src/utils/package.js.map +0 -1
  538. /package/dist/src/{config/policy.test.d.ts → commands/extensions/validate.test.d.ts} +0 -0
@@ -5,205 +5,211 @@ import { createContext, useCallback, useContext, useEffect, useRef, } from 'reac
5
5
  import readline from 'node:readline';
6
6
  import { PassThrough } from 'node:stream';
7
7
  import { BACKSLASH_ENTER_DETECTION_WINDOW_MS, CHAR_CODE_ESC, KITTY_CTRL_C, KITTY_KEYCODE_BACKSPACE, KITTY_KEYCODE_ENTER, KITTY_KEYCODE_NUMPAD_ENTER, KITTY_KEYCODE_TAB, MAX_KITTY_SEQUENCE_LENGTH, KITTY_MODIFIER_BASE, KITTY_MODIFIER_EVENT_TYPES_OFFSET, MODIFIER_SHIFT_BIT, MODIFIER_ALT_BIT, MODIFIER_CTRL_BIT, } from '../utils/platformConstants.js';
8
+ import { ESC, couldBeMouseSequence } from '../utils/input.js';
8
9
  import { FOCUS_IN, FOCUS_OUT } from '../hooks/useFocus.js';
9
- const ESC = '\u001B';
10
- export const PASTE_MODE_PREFIX = `${ESC}[200~`;
11
- export const PASTE_MODE_SUFFIX = `${ESC}[201~`;
10
+ import { isIncompleteMouseSequence, parseMouseEvent } from '../utils/mouse.js';
11
+ export const PASTE_MODE_START = `${ESC}[200~`;
12
+ export const PASTE_MODE_END = `${ESC}[201~`;
12
13
  export const DRAG_COMPLETION_TIMEOUT_MS = 100; // Broadcast full path after 100ms if no more input
13
14
  export const KITTY_SEQUENCE_TIMEOUT_MS = 50; // Flush incomplete kitty sequences after 50ms
15
+ export const PASTE_CODE_TIMEOUT_MS = 50; // Flush incomplete paste code after 50ms
14
16
  export const SINGLE_QUOTE = "'";
15
17
  export const DOUBLE_QUOTE = '"';
16
- const ALT_KEY_CHARACTER_MAP = {
17
- '\u00E5': 'a',
18
- '\u222B': 'b',
19
- '\u00E7': 'c',
20
- '\u2202': 'd',
21
- '\u00B4': 'e',
22
- '\u0192': 'f',
23
- '\u00A9': 'g',
24
- '\u02D9': 'h',
25
- '\u02C6': 'i',
26
- '\u2206': 'j',
27
- '\u02DA': 'k',
28
- '\u00AC': 'l',
29
- '\u00B5': 'm',
30
- '\u02DC': 'n',
31
- '\u00F8': 'o',
32
- '\u03C0': 'p',
33
- '\u0153': 'q',
34
- '\u00AE': 'r',
35
- '\u00DF': 's',
36
- '\u2020': 't',
37
- '\u00A8': 'u',
38
- '\u221A': 'v',
39
- '\u2211': 'w',
40
- '\u2248': 'x',
41
- '\u00A5': 'y',
42
- '\u03A9': 'z',
18
+ // On Mac, hitting alt+char will yield funny characters.
19
+ // Remap these three since we listen for them.
20
+ const MAC_ALT_KEY_CHARACTER_MAP = {
21
+ '\u222B': 'b', // "∫" back one word
22
+ '\u0192': 'f', // "ƒ" forward one word
23
+ '\u00B5': 'm', // "µ" toggle markup view
43
24
  };
44
- const KeypressContext = createContext(undefined);
45
- export function useKeypressContext() {
46
- const context = useContext(KeypressContext);
47
- if (!context) {
48
- throw new Error('useKeypressContext must be used within a KeypressProvider');
49
- }
50
- return context;
25
+ /**
26
+ * Maps symbols from parameterized functional keys `\x1b[1;1<letter>`
27
+ * to their corresponding key names (e.g., 'up', 'f1').
28
+ */
29
+ const LEGACY_FUNC_TO_NAME = {
30
+ A: 'up',
31
+ B: 'down',
32
+ C: 'right',
33
+ D: 'left',
34
+ H: 'home',
35
+ F: 'end',
36
+ P: 'f1',
37
+ Q: 'f2',
38
+ R: 'f3',
39
+ S: 'f4',
40
+ };
41
+ /**
42
+ * Maps key codes from tilde-coded functional keys `\x1b[<code>~`
43
+ * to their corresponding key names.
44
+ */
45
+ const TILDE_KEYCODE_TO_NAME = {
46
+ 1: 'home',
47
+ 2: 'insert',
48
+ 3: 'delete',
49
+ 4: 'end',
50
+ 5: 'pageup',
51
+ 6: 'pagedown',
52
+ 11: 'f1',
53
+ 12: 'f2',
54
+ 13: 'f3',
55
+ 14: 'f4',
56
+ 15: 'f5',
57
+ 17: 'f6', // skipping 16 is intentional
58
+ 18: 'f7',
59
+ 19: 'f8',
60
+ 20: 'f9',
61
+ 21: 'f10',
62
+ 23: 'f11', // skipping 22 is intentional
63
+ 24: 'f12',
64
+ };
65
+ /**
66
+ * Check if a buffer could potentially be a valid kitty sequence or its prefix.
67
+ */
68
+ function couldBeKittySequence(buffer) {
69
+ // Kitty sequences always start with ESC[.
70
+ if (buffer.length === 0)
71
+ return true;
72
+ if (buffer === ESC || buffer === `${ESC}[`)
73
+ return true;
74
+ if (!buffer.startsWith(`${ESC}[`))
75
+ return false;
76
+ if (couldBeMouseSequence(buffer))
77
+ return true;
78
+ // Check for known kitty sequence patterns:
79
+ // 1. ESC[<digit> - could be CSI-u or tilde-coded
80
+ // 2. ESC[1;<digit> - parameterized functional
81
+ // 3. ESC[<letter> - legacy functional keys
82
+ // 4. ESC[Z - reverse tab
83
+ const afterCSI = buffer.slice(2);
84
+ // Check if it starts with a digit (could be CSI-u or parameterized)
85
+ if (/^\d/.test(afterCSI))
86
+ return true;
87
+ // Check for known single-letter sequences
88
+ if (/^[ABCDHFPQRSZ]/.test(afterCSI))
89
+ return true;
90
+ // Check for 1; pattern (parameterized sequences)
91
+ if (/^1;\d/.test(afterCSI))
92
+ return true;
93
+ // Anything else starting with ESC[ that doesn't match our patterns
94
+ // is likely not a kitty sequence we handle
95
+ return false;
51
96
  }
52
- export function KeypressProvider({ children, kittyProtocolEnabled, config, debugKeystrokeLogging, }) {
53
- const { stdin, setRawMode } = useStdin();
54
- const subscribers = useRef(new Set()).current;
55
- const isDraggingRef = useRef(false);
56
- const dragBufferRef = useRef('');
57
- const draggingTimerRef = useRef(null);
58
- const subscribe = useCallback((handler) => {
59
- subscribers.add(handler);
60
- }, [subscribers]);
61
- const unsubscribe = useCallback((handler) => {
62
- subscribers.delete(handler);
63
- }, [subscribers]);
64
- useEffect(() => {
65
- const clearDraggingTimer = () => {
66
- if (draggingTimerRef.current) {
67
- clearTimeout(draggingTimerRef.current);
68
- draggingTimerRef.current = null;
69
- }
97
+ /**
98
+ * Parses a single complete kitty/parameterized/legacy sequence from the start
99
+ * of the buffer.
100
+ *
101
+ * This enables peel-and-continue parsing for batched input, allowing us to
102
+ * "peel off" one complete event when multiple sequences arrive in a single
103
+ * chunk, preventing buffer overflow and fragmentation.
104
+ *
105
+ * @param buffer - The input buffer string to parse.
106
+ * @returns The parsed Key and the number of characters consumed, or null if
107
+ * no complete sequence is found at the start of the buffer.
108
+ */
109
+ function parseKittyPrefix(buffer) {
110
+ // In older terminals ESC [ Z was used as Cursor Backward Tabulation (CBT)
111
+ // In newer terminals the same functionality of key combination for moving
112
+ // backward through focusable elements is Shift+Tab, hence we will
113
+ // map ESC [ Z to Shift+Tab
114
+ // 0) Reverse Tab (legacy): ESC [ Z
115
+ // Treat as Shift+Tab for UI purposes.
116
+ // Regex parts:
117
+ // ^ - start of buffer
118
+ // ESC [ - CSI introducer
119
+ // Z - legacy reverse tab
120
+ const revTabLegacy = new RegExp(`^${ESC}\\[Z`);
121
+ let m = buffer.match(revTabLegacy);
122
+ if (m) {
123
+ return {
124
+ key: {
125
+ name: 'tab',
126
+ ctrl: false,
127
+ meta: false,
128
+ shift: true,
129
+ paste: false,
130
+ sequence: buffer.slice(0, m[0].length),
131
+ kittyProtocol: false,
132
+ },
133
+ length: m[0].length,
70
134
  };
71
- const wasRaw = stdin.isRaw;
72
- if (wasRaw === false) {
73
- setRawMode(true);
135
+ }
136
+ // 1) Reverse Tab (parameterized): ESC [ 1 ; <mods> Z
137
+ // Parameterized reverse Tab: ESC [ 1 ; <mods> Z
138
+ const revTabParam = new RegExp(`^${ESC}\\[1;(\\d+)Z`);
139
+ m = buffer.match(revTabParam);
140
+ if (m) {
141
+ let mods = parseInt(m[1], 10);
142
+ if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
143
+ mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
74
144
  }
75
- const keypressStream = new PassThrough();
76
- let usePassthrough = false;
77
- const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10);
78
- if (nodeMajorVersion < 20 ||
79
- process.env['PASTE_WORKAROUND'] === '1' ||
80
- process.env['PASTE_WORKAROUND'] === 'true') {
81
- usePassthrough = true;
145
+ const bits = mods - KITTY_MODIFIER_BASE;
146
+ const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
147
+ const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
148
+ return {
149
+ key: {
150
+ name: 'tab',
151
+ ctrl,
152
+ meta: alt,
153
+ // Reverse tab implies Shift behavior; force shift regardless of mods
154
+ shift: true,
155
+ paste: false,
156
+ sequence: buffer.slice(0, m[0].length),
157
+ kittyProtocol: true,
158
+ },
159
+ length: m[0].length,
160
+ };
161
+ }
162
+ // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
163
+ // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
164
+ // Arrows, Home/End, F1–F4 with modifiers encoded in <mods>.
165
+ const arrowPrefix = new RegExp(`^${ESC}\\[1;(\\d+)([ABCDHFPQSR])`);
166
+ m = buffer.match(arrowPrefix);
167
+ if (m) {
168
+ let mods = parseInt(m[1], 10);
169
+ if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
170
+ mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
82
171
  }
83
- let isPaste = false;
84
- let pasteBuffer = Buffer.alloc(0);
85
- let kittySequenceBuffer = '';
86
- let kittySequenceTimeout = null;
87
- let backslashTimeout = null;
88
- let waitingForEnterAfterBackslash = false;
89
- // Check if a buffer could potentially be a valid kitty sequence or its prefix
90
- const couldBeKittySequence = (buffer) => {
91
- // Kitty sequences always start with ESC[.
92
- if (buffer.length === 0)
93
- return true;
94
- if (buffer === ESC || buffer === `${ESC}[`)
95
- return true;
96
- if (!buffer.startsWith(`${ESC}[`))
97
- return false;
98
- // Check for known kitty sequence patterns:
99
- // 1. ESC[<digit> - could be CSI-u or tilde-coded
100
- // 2. ESC[1;<digit> - parameterized functional
101
- // 3. ESC[<letter> - legacy functional keys
102
- // 4. ESC[Z - reverse tab
103
- const afterCSI = buffer.slice(2);
104
- // Check if it starts with a digit (could be CSI-u or parameterized)
105
- if (/^\d/.test(afterCSI))
106
- return true;
107
- // Check for known single-letter sequences
108
- if (/^[ABCDHFPQRSZ]/.test(afterCSI))
109
- return true;
110
- // Check for 1; pattern (parameterized sequences)
111
- if (/^1;\d/.test(afterCSI))
112
- return true;
113
- // Anything else starting with ESC[ that doesn't match our patterns
114
- // is likely not a kitty sequence we handle
115
- return false;
172
+ const bits = mods - KITTY_MODIFIER_BASE;
173
+ const shift = (bits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
174
+ const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
175
+ const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
176
+ const sym = m[2];
177
+ const name = LEGACY_FUNC_TO_NAME[sym] || '';
178
+ if (!name)
179
+ return null;
180
+ return {
181
+ key: {
182
+ name,
183
+ ctrl,
184
+ meta: alt,
185
+ shift,
186
+ paste: false,
187
+ sequence: buffer.slice(0, m[0].length),
188
+ kittyProtocol: true,
189
+ },
190
+ length: m[0].length,
116
191
  };
117
- // Parse a single complete kitty sequence from the start (prefix) of the
118
- // buffer and return both the Key and the number of characters consumed.
119
- // This lets us "peel off" one complete event when multiple sequences arrive
120
- // in a single chunk, preventing buffer overflow and fragmentation.
121
- // Parse a single complete kitty/parameterized/legacy sequence from the start
122
- // of the buffer and return both the parsed Key and the number of characters
123
- // consumed. This enables peel-and-continue parsing for batched input.
124
- const parseKittyPrefix = (buffer) => {
125
- // In older terminals ESC [ Z was used as Cursor Backward Tabulation (CBT)
126
- // In newer terminals the same functionality of key combination for moving
127
- // backward through focusable elements is Shift+Tab, hence we will
128
- // map ESC [ Z to Shift+Tab
129
- // 0) Reverse Tab (legacy): ESC [ Z
130
- // Treat as Shift+Tab for UI purposes.
131
- // Regex parts:
132
- // ^ - start of buffer
133
- // ESC [ - CSI introducer
134
- // Z - legacy reverse tab
135
- const revTabLegacy = new RegExp(`^${ESC}\\[Z`);
136
- let m = buffer.match(revTabLegacy);
137
- if (m) {
138
- return {
139
- key: {
140
- name: 'tab',
141
- ctrl: false,
142
- meta: false,
143
- shift: true,
144
- paste: false,
145
- sequence: buffer.slice(0, m[0].length),
146
- kittyProtocol: true,
147
- },
148
- length: m[0].length,
149
- };
150
- }
151
- // 1) Reverse Tab (parameterized): ESC [ 1 ; <mods> Z
152
- // Parameterized reverse Tab: ESC [ 1 ; <mods> Z
153
- const revTabParam = new RegExp(`^${ESC}\\[1;(\\d+)Z`);
154
- m = buffer.match(revTabParam);
155
- if (m) {
156
- let mods = parseInt(m[1], 10);
157
- if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
158
- mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
159
- }
160
- const bits = mods - KITTY_MODIFIER_BASE;
161
- const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
162
- const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
163
- return {
164
- key: {
165
- name: 'tab',
166
- ctrl,
167
- meta: alt,
168
- // Reverse tab implies Shift behavior; force shift regardless of mods
169
- shift: true,
170
- paste: false,
171
- sequence: buffer.slice(0, m[0].length),
172
- kittyProtocol: true,
173
- },
174
- length: m[0].length,
175
- };
176
- }
177
- // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
178
- // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
179
- // Arrows, Home/End, F1–F4 with modifiers encoded in <mods>.
180
- const arrowPrefix = new RegExp(`^${ESC}\\[1;(\\d+)([ABCDHFPQSR])`);
181
- m = buffer.match(arrowPrefix);
182
- if (m) {
183
- let mods = parseInt(m[1], 10);
184
- if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
185
- mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
186
- }
187
- const bits = mods - KITTY_MODIFIER_BASE;
188
- const shift = (bits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
189
- const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
190
- const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
191
- const sym = m[2];
192
- const symbolToName = {
193
- A: 'up',
194
- B: 'down',
195
- C: 'right',
196
- D: 'left',
197
- H: 'home',
198
- F: 'end',
199
- P: 'f1',
200
- Q: 'f2',
201
- R: 'f3',
202
- S: 'f4',
203
- };
204
- const name = symbolToName[sym] || '';
205
- if (!name)
206
- return null;
192
+ }
193
+ // 3) CSI-u form: ESC [ <code> ; <mods> (u|~)
194
+ // 3) CSI-u and tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
195
+ // 'u' terminator: Kitty CSI-u; '~' terminator: tilde-coded function keys.
196
+ const csiUPrefix = new RegExp(`^${ESC}\\[(\\d+)(;(\\d+))?([u~])`);
197
+ m = buffer.match(csiUPrefix);
198
+ if (m) {
199
+ const keyCode = parseInt(m[1], 10);
200
+ let modifiers = m[3] ? parseInt(m[3], 10) : KITTY_MODIFIER_BASE;
201
+ if (modifiers >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
202
+ modifiers -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
203
+ }
204
+ const modifierBits = modifiers - KITTY_MODIFIER_BASE;
205
+ const shift = (modifierBits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
206
+ const alt = (modifierBits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
207
+ const ctrl = (modifierBits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
208
+ const terminator = m[4];
209
+ // Tilde-coded functional keys (Delete, Insert, PageUp/Down, Home/End)
210
+ if (terminator === '~') {
211
+ const name = TILDE_KEYCODE_TO_NAME[keyCode];
212
+ if (name) {
207
213
  return {
208
214
  key: {
209
215
  name,
@@ -212,147 +218,210 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
212
218
  shift,
213
219
  paste: false,
214
220
  sequence: buffer.slice(0, m[0].length),
215
- kittyProtocol: true,
221
+ kittyProtocol: false,
216
222
  },
217
223
  length: m[0].length,
218
224
  };
219
225
  }
220
- // 3) CSI-u form: ESC [ <code> ; <mods> (u|~)
221
- // 3) CSI-u and tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
222
- // 'u' terminator: Kitty CSI-u; '~' terminator: tilde-coded function keys.
223
- const csiUPrefix = new RegExp(`^${ESC}\\[(\\d+)(;(\\d+))?([u~])`);
224
- m = buffer.match(csiUPrefix);
225
- if (m) {
226
- const keyCode = parseInt(m[1], 10);
227
- let modifiers = m[3] ? parseInt(m[3], 10) : KITTY_MODIFIER_BASE;
228
- if (modifiers >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
229
- modifiers -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
230
- }
231
- const modifierBits = modifiers - KITTY_MODIFIER_BASE;
232
- const shift = (modifierBits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
233
- const alt = (modifierBits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
234
- const ctrl = (modifierBits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
235
- const terminator = m[4];
236
- // Tilde-coded functional keys (Delete, Insert, PageUp/Down, Home/End)
237
- if (terminator === '~') {
238
- let name = null;
239
- switch (keyCode) {
240
- case 1:
241
- name = 'home';
242
- break;
243
- case 2:
244
- name = 'insert';
245
- break;
246
- case 3:
247
- name = 'delete';
248
- break;
249
- case 4:
250
- name = 'end';
251
- break;
252
- case 5:
253
- name = 'pageup';
254
- break;
255
- case 6:
256
- name = 'pagedown';
257
- break;
258
- default:
259
- break;
260
- }
261
- if (name) {
262
- return {
263
- key: {
264
- name,
265
- ctrl,
266
- meta: alt,
267
- shift,
268
- paste: false,
269
- sequence: buffer.slice(0, m[0].length),
270
- kittyProtocol: true,
271
- },
272
- length: m[0].length,
273
- };
274
- }
275
- }
276
- const kittyKeyCodeToName = {
277
- [CHAR_CODE_ESC]: 'escape',
278
- [KITTY_KEYCODE_TAB]: 'tab',
279
- [KITTY_KEYCODE_BACKSPACE]: 'backspace',
280
- [KITTY_KEYCODE_ENTER]: 'return',
281
- [KITTY_KEYCODE_NUMPAD_ENTER]: 'return',
282
- };
283
- const name = kittyKeyCodeToName[keyCode];
284
- if (name) {
285
- return {
286
- key: {
287
- name,
288
- ctrl,
289
- meta: alt,
290
- shift,
291
- paste: false,
292
- sequence: buffer.slice(0, m[0].length),
293
- kittyProtocol: true,
294
- },
295
- length: m[0].length,
296
- };
297
- }
298
- // Ctrl+letters and Alt+letters
299
- if ((ctrl || alt) &&
300
- keyCode >= 'a'.charCodeAt(0) &&
301
- keyCode <= 'z'.charCodeAt(0)) {
302
- const letter = String.fromCharCode(keyCode);
303
- return {
304
- key: {
305
- name: letter,
306
- ctrl,
307
- meta: alt,
308
- shift,
309
- paste: false,
310
- sequence: buffer.slice(0, m[0].length),
311
- kittyProtocol: true,
312
- },
313
- length: m[0].length,
314
- };
226
+ }
227
+ const kittyKeyCodeToName = {
228
+ [CHAR_CODE_ESC]: 'escape',
229
+ [KITTY_KEYCODE_TAB]: 'tab',
230
+ [KITTY_KEYCODE_BACKSPACE]: 'backspace',
231
+ [KITTY_KEYCODE_ENTER]: 'return',
232
+ [KITTY_KEYCODE_NUMPAD_ENTER]: 'return',
233
+ };
234
+ const name = kittyKeyCodeToName[keyCode];
235
+ if (name) {
236
+ return {
237
+ key: {
238
+ name,
239
+ ctrl,
240
+ meta: alt,
241
+ shift,
242
+ paste: false,
243
+ sequence: buffer.slice(0, m[0].length),
244
+ kittyProtocol: true,
245
+ },
246
+ length: m[0].length,
247
+ };
248
+ }
249
+ // Ctrl+letters and Alt+letters
250
+ if ((ctrl || alt) &&
251
+ keyCode >= 'a'.charCodeAt(0) &&
252
+ keyCode <= 'z'.charCodeAt(0)) {
253
+ const letter = String.fromCharCode(keyCode);
254
+ return {
255
+ key: {
256
+ name: letter,
257
+ ctrl,
258
+ meta: alt,
259
+ shift,
260
+ paste: false,
261
+ sequence: buffer.slice(0, m[0].length),
262
+ kittyProtocol: true,
263
+ },
264
+ length: m[0].length,
265
+ };
266
+ }
267
+ }
268
+ // 4) Legacy function keys (no parameters): ESC [ (A|B|C|D|H|F)
269
+ // Arrows + Home/End without modifiers.
270
+ const legacyFuncKey = new RegExp(`^${ESC}\\[([ABCDHF])`);
271
+ m = buffer.match(legacyFuncKey);
272
+ if (m) {
273
+ const sym = m[1];
274
+ const name = LEGACY_FUNC_TO_NAME[sym];
275
+ return {
276
+ key: {
277
+ name,
278
+ ctrl: false,
279
+ meta: false,
280
+ shift: false,
281
+ paste: false,
282
+ sequence: buffer.slice(0, m[0].length),
283
+ kittyProtocol: false,
284
+ },
285
+ length: m[0].length,
286
+ };
287
+ }
288
+ return null;
289
+ }
290
+ /**
291
+ * Returns the first index before which we are certain there is no paste marker.
292
+ */
293
+ function earliestPossiblePasteMarker(data) {
294
+ // Check data for full start-paste or end-paste markers.
295
+ const startIndex = data.indexOf(PASTE_MODE_START);
296
+ const endIndex = data.indexOf(PASTE_MODE_END);
297
+ if (startIndex !== -1 && endIndex !== -1) {
298
+ return Math.min(startIndex, endIndex);
299
+ }
300
+ else if (startIndex !== -1) {
301
+ return startIndex;
302
+ }
303
+ else if (endIndex !== -1) {
304
+ return endIndex;
305
+ }
306
+ // data contains no full start-paste or end-paste.
307
+ // Check if data ends with a prefix of start-paste or end-paste.
308
+ const codeLength = PASTE_MODE_START.length;
309
+ for (let i = Math.min(data.length, codeLength - 1); i > 0; i--) {
310
+ const candidate = data.slice(data.length - i);
311
+ if (PASTE_MODE_START.indexOf(candidate) === 0 ||
312
+ PASTE_MODE_END.indexOf(candidate) === 0) {
313
+ return data.length - i;
314
+ }
315
+ }
316
+ return data.length;
317
+ }
318
+ /**
319
+ * A generator that takes in data chunks and spits out paste-start and
320
+ * paste-end keypresses. All non-paste marker data is passed to passthrough.
321
+ */
322
+ function* pasteMarkerParser(passthrough, keypressHandler) {
323
+ while (true) {
324
+ let data = yield;
325
+ if (data.length === 0) {
326
+ continue; // we timed out
327
+ }
328
+ while (true) {
329
+ const index = earliestPossiblePasteMarker(data);
330
+ if (index === data.length) {
331
+ // no possible paste markers were found
332
+ passthrough.write(data);
333
+ break;
334
+ }
335
+ if (index > 0) {
336
+ // snip off and send the part that doesn't have a paste marker
337
+ passthrough.write(data.slice(0, index));
338
+ data = data.slice(index);
339
+ }
340
+ // data starts with a possible paste marker
341
+ const codeLength = PASTE_MODE_START.length;
342
+ if (data.length < codeLength) {
343
+ // we have a prefix. Concat the next data and try again.
344
+ const newData = yield;
345
+ if (newData.length === 0) {
346
+ // we timed out. Just dump what we have and start over.
347
+ passthrough.write(data);
348
+ break;
315
349
  }
350
+ data += newData;
316
351
  }
317
- // 4) Legacy function keys (no parameters): ESC [ (A|B|C|D|H|F)
318
- // Arrows + Home/End without modifiers.
319
- const legacyFuncKey = new RegExp(`^${ESC}\\[([ABCDHF])`);
320
- m = buffer.match(legacyFuncKey);
321
- if (m) {
322
- const sym = m[1];
323
- const nameMap = {
324
- A: 'up',
325
- B: 'down',
326
- C: 'right',
327
- D: 'left',
328
- H: 'home',
329
- F: 'end',
330
- };
331
- const name = nameMap[sym];
332
- return {
333
- key: {
334
- name,
335
- ctrl: false,
336
- meta: false,
337
- shift: false,
338
- paste: false,
339
- sequence: buffer.slice(0, m[0].length),
340
- kittyProtocol: true,
341
- },
342
- length: m[0].length,
343
- };
352
+ else if (data.startsWith(PASTE_MODE_START)) {
353
+ keypressHandler(undefined, {
354
+ name: 'paste-start',
355
+ ctrl: false,
356
+ meta: false,
357
+ shift: false,
358
+ paste: false,
359
+ sequence: '',
360
+ });
361
+ data = data.slice(PASTE_MODE_START.length);
344
362
  }
345
- return null;
346
- };
347
- const broadcast = (key) => {
348
- for (const handler of subscribers) {
349
- handler(key);
363
+ else if (data.startsWith(PASTE_MODE_END)) {
364
+ keypressHandler(undefined, {
365
+ name: 'paste-end',
366
+ ctrl: false,
367
+ meta: false,
368
+ shift: false,
369
+ paste: false,
370
+ sequence: '',
371
+ });
372
+ data = data.slice(PASTE_MODE_END.length);
373
+ }
374
+ else {
375
+ // This should never happen.
376
+ passthrough.write(data);
377
+ break;
378
+ }
379
+ }
380
+ }
381
+ }
382
+ const KeypressContext = createContext(undefined);
383
+ export function useKeypressContext() {
384
+ const context = useContext(KeypressContext);
385
+ if (!context) {
386
+ throw new Error('useKeypressContext must be used within a KeypressProvider');
387
+ }
388
+ return context;
389
+ }
390
+ function shouldUsePassthrough() {
391
+ return process.env['PASTE_WORKAROUND'] !== 'false';
392
+ }
393
+ export function KeypressProvider({ children, kittyProtocolEnabled, config, debugKeystrokeLogging, }) {
394
+ const { stdin, setRawMode } = useStdin();
395
+ const subscribers = useRef(new Set()).current;
396
+ const subscribe = useCallback((handler) => subscribers.add(handler), [subscribers]);
397
+ const unsubscribe = useCallback((handler) => subscribers.delete(handler), [subscribers]);
398
+ const broadcast = useCallback((key) => subscribers.forEach((handler) => handler(key)), [subscribers]);
399
+ useEffect(() => {
400
+ const wasRaw = stdin.isRaw;
401
+ if (wasRaw === false) {
402
+ setRawMode(true);
403
+ }
404
+ const keypressStream = shouldUsePassthrough() ? new PassThrough() : null;
405
+ // If non-null that means we are in paste mode
406
+ let pasteBuffer = null;
407
+ // Used to turn "\" quickly followed by a "enter" into a shift enter
408
+ let backslashTimeout = null;
409
+ // Buffers incomplete sequences (Kitty or Mouse) and timer to flush it
410
+ let inputBuffer = '';
411
+ let inputTimeout = null;
412
+ // Used to detect filename drag-and-drops.
413
+ let dragBuffer = '';
414
+ let draggingTimer = null;
415
+ const clearDraggingTimer = () => {
416
+ if (draggingTimer) {
417
+ clearTimeout(draggingTimer);
418
+ draggingTimer = null;
350
419
  }
351
420
  };
352
- const flushKittyBufferOnInterrupt = (reason) => {
353
- if (kittySequenceBuffer) {
421
+ const flushInputBufferOnInterrupt = (reason) => {
422
+ if (inputBuffer) {
354
423
  if (debugKeystrokeLogging) {
355
- debugLogger.log(`[DEBUG] Kitty sequence flushed due to ${reason}:`, JSON.stringify(kittySequenceBuffer));
424
+ debugLogger.log(`[DEBUG] Input sequence flushed due to ${reason}:`, JSON.stringify(inputBuffer));
356
425
  }
357
426
  broadcast({
358
427
  name: '',
@@ -360,76 +429,73 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
360
429
  meta: false,
361
430
  shift: false,
362
431
  paste: false,
363
- sequence: kittySequenceBuffer,
432
+ sequence: inputBuffer,
364
433
  });
365
- kittySequenceBuffer = '';
434
+ inputBuffer = '';
366
435
  }
367
- if (kittySequenceTimeout) {
368
- clearTimeout(kittySequenceTimeout);
369
- kittySequenceTimeout = null;
436
+ if (inputTimeout) {
437
+ clearTimeout(inputTimeout);
438
+ inputTimeout = null;
370
439
  }
371
440
  };
372
441
  const handleKeypress = (_, key) => {
373
442
  if (key.sequence === FOCUS_IN || key.sequence === FOCUS_OUT) {
374
- flushKittyBufferOnInterrupt('focus event');
443
+ flushInputBufferOnInterrupt('focus event');
375
444
  return;
376
445
  }
377
446
  if (key.name === 'paste-start') {
378
- flushKittyBufferOnInterrupt('paste start');
379
- isPaste = true;
447
+ flushInputBufferOnInterrupt('paste start');
448
+ pasteBuffer = Buffer.alloc(0);
380
449
  return;
381
450
  }
382
451
  if (key.name === 'paste-end') {
383
- isPaste = false;
384
- broadcast({
385
- name: '',
386
- ctrl: false,
387
- meta: false,
388
- shift: false,
389
- paste: true,
390
- sequence: pasteBuffer.toString(),
391
- });
392
- pasteBuffer = Buffer.alloc(0);
452
+ if (pasteBuffer !== null) {
453
+ broadcast({
454
+ name: '',
455
+ ctrl: false,
456
+ meta: false,
457
+ shift: false,
458
+ paste: true,
459
+ sequence: pasteBuffer.toString(),
460
+ });
461
+ }
462
+ pasteBuffer = null;
393
463
  return;
394
464
  }
395
- if (isPaste) {
465
+ if (pasteBuffer !== null) {
396
466
  pasteBuffer = Buffer.concat([pasteBuffer, Buffer.from(key.sequence)]);
397
467
  return;
398
468
  }
399
469
  if (key.sequence === SINGLE_QUOTE ||
400
470
  key.sequence === DOUBLE_QUOTE ||
401
- isDraggingRef.current) {
402
- isDraggingRef.current = true;
403
- dragBufferRef.current += key.sequence;
471
+ draggingTimer !== null) {
472
+ dragBuffer += key.sequence;
404
473
  clearDraggingTimer();
405
- draggingTimerRef.current = setTimeout(() => {
406
- isDraggingRef.current = false;
407
- const seq = dragBufferRef.current;
408
- dragBufferRef.current = '';
474
+ draggingTimer = setTimeout(() => {
475
+ draggingTimer = null;
476
+ const seq = dragBuffer;
477
+ dragBuffer = '';
409
478
  if (seq) {
410
479
  broadcast({ ...key, name: '', paste: true, sequence: seq });
411
480
  }
412
481
  }, DRAG_COMPLETION_TIMEOUT_MS);
413
482
  return;
414
483
  }
415
- const mappedLetter = ALT_KEY_CHARACTER_MAP[key.sequence];
416
- if (mappedLetter && !key.meta) {
484
+ const mappedLetter = MAC_ALT_KEY_CHARACTER_MAP[key.sequence];
485
+ if (process.platform === 'darwin' && mappedLetter && !key.meta) {
417
486
  broadcast({
418
487
  name: mappedLetter,
419
488
  ctrl: false,
420
489
  meta: true,
421
490
  shift: false,
422
- paste: isPaste,
491
+ paste: pasteBuffer !== null,
423
492
  sequence: key.sequence,
424
493
  });
425
494
  return;
426
495
  }
427
- if (key.name === 'return' && waitingForEnterAfterBackslash) {
428
- if (backslashTimeout) {
429
- clearTimeout(backslashTimeout);
430
- backslashTimeout = null;
431
- }
432
- waitingForEnterAfterBackslash = false;
496
+ if (key.name === 'return' && backslashTimeout !== null) {
497
+ clearTimeout(backslashTimeout);
498
+ backslashTimeout = null;
433
499
  broadcast({
434
500
  ...key,
435
501
  shift: true,
@@ -439,20 +505,15 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
439
505
  }
440
506
  if (key.sequence === '\\' && !key.name) {
441
507
  // Corrected escaping for backslash
442
- waitingForEnterAfterBackslash = true;
443
508
  backslashTimeout = setTimeout(() => {
444
- waitingForEnterAfterBackslash = false;
445
509
  backslashTimeout = null;
446
510
  broadcast(key);
447
511
  }, BACKSLASH_ENTER_DETECTION_WINDOW_MS);
448
512
  return;
449
513
  }
450
- if (waitingForEnterAfterBackslash && key.name !== 'return') {
451
- if (backslashTimeout) {
452
- clearTimeout(backslashTimeout);
453
- backslashTimeout = null;
454
- }
455
- waitingForEnterAfterBackslash = false;
514
+ if (backslashTimeout !== null && key.name !== 'return') {
515
+ clearTimeout(backslashTimeout);
516
+ backslashTimeout = null;
456
517
  broadcast({
457
518
  name: '',
458
519
  sequence: '\\',
@@ -468,13 +529,13 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
468
529
  }
469
530
  if ((key.ctrl && key.name === 'c') ||
470
531
  key.sequence === `${ESC}${KITTY_CTRL_C}`) {
471
- if (kittySequenceBuffer && debugKeystrokeLogging) {
472
- debugLogger.log('[DEBUG] Kitty buffer cleared on Ctrl+C:', kittySequenceBuffer);
532
+ if (inputBuffer && debugKeystrokeLogging) {
533
+ debugLogger.log('[DEBUG] Input buffer cleared on Ctrl+C:', inputBuffer);
473
534
  }
474
- kittySequenceBuffer = '';
475
- if (kittySequenceTimeout) {
476
- clearTimeout(kittySequenceTimeout);
477
- kittySequenceTimeout = null;
535
+ inputBuffer = '';
536
+ if (inputTimeout) {
537
+ clearTimeout(inputTimeout);
538
+ inputTimeout = null;
478
539
  }
479
540
  if (key.sequence === `${ESC}${KITTY_CTRL_C}`) {
480
541
  broadcast({
@@ -492,196 +553,209 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
492
553
  }
493
554
  return;
494
555
  }
495
- if (kittyProtocolEnabled) {
496
- // Clear any pending timeout when new input arrives
497
- if (kittySequenceTimeout) {
498
- clearTimeout(kittySequenceTimeout);
499
- kittySequenceTimeout = null;
556
+ // Clear any pending timeout when new input arrives
557
+ if (inputTimeout) {
558
+ clearTimeout(inputTimeout);
559
+ inputTimeout = null;
560
+ }
561
+ // Always check if this could start a sequence we need to buffer (Kitty or Mouse)
562
+ // Other ESC sequences (like Alt+Key which is ESC+Key) should be let through if readline parsed them.
563
+ const shouldBuffer = couldBeKittySequence(key.sequence);
564
+ const isExcluded = [
565
+ PASTE_MODE_START,
566
+ PASTE_MODE_END,
567
+ FOCUS_IN,
568
+ FOCUS_OUT,
569
+ ].some((prefix) => key.sequence.startsWith(prefix));
570
+ if (inputBuffer || (shouldBuffer && !isExcluded)) {
571
+ inputBuffer += key.sequence;
572
+ if (debugKeystrokeLogging && !couldBeMouseSequence(inputBuffer)) {
573
+ debugLogger.log('[DEBUG] Input buffer accumulating:', JSON.stringify(inputBuffer));
500
574
  }
501
- // Check if this could start a kitty sequence
502
- const startsWithEsc = key.sequence.startsWith(ESC);
503
- const isExcluded = [
504
- PASTE_MODE_PREFIX,
505
- PASTE_MODE_SUFFIX,
506
- FOCUS_IN,
507
- FOCUS_OUT,
508
- ].some((prefix) => key.sequence.startsWith(prefix));
509
- if (kittySequenceBuffer || (startsWithEsc && !isExcluded)) {
510
- kittySequenceBuffer += key.sequence;
511
- if (debugKeystrokeLogging) {
512
- debugLogger.log('[DEBUG] Kitty buffer accumulating:', JSON.stringify(kittySequenceBuffer));
513
- }
514
- // Try immediate parsing
515
- let remainingBuffer = kittySequenceBuffer;
516
- let parsedAny = false;
517
- while (remainingBuffer) {
518
- const parsed = parseKittyPrefix(remainingBuffer);
519
- if (parsed) {
575
+ // Try immediate parsing
576
+ let remainingBuffer = inputBuffer;
577
+ let parsedAny = false;
578
+ while (remainingBuffer) {
579
+ const parsed = parseKittyPrefix(remainingBuffer);
580
+ if (parsed) {
581
+ // If kitty protocol is disabled, only allow legacy/standard sequences.
582
+ // parseKittyPrefix returns true for kittyProtocol if it's a modern kitty sequence.
583
+ if (kittyProtocolEnabled || !parsed.key.kittyProtocol) {
520
584
  if (debugKeystrokeLogging) {
521
585
  const parsedSequence = remainingBuffer.slice(0, parsed.length);
522
- debugLogger.log('[DEBUG] Kitty sequence parsed successfully:', JSON.stringify(parsedSequence));
586
+ debugLogger.log('[DEBUG] Sequence parsed successfully:', JSON.stringify(parsedSequence));
523
587
  }
524
588
  broadcast(parsed.key);
525
589
  remainingBuffer = remainingBuffer.slice(parsed.length);
526
590
  parsedAny = true;
591
+ continue;
527
592
  }
528
- else {
529
- // If we can't parse a sequence at the start, check if there's
530
- // another ESC later in the buffer. If so, the data before it
531
- // is garbage/incomplete and should be dropped so we can
532
- // process the next sequence.
533
- const nextEscIndex = remainingBuffer.indexOf(ESC, 1);
534
- if (nextEscIndex !== -1) {
535
- const garbage = remainingBuffer.slice(0, nextEscIndex);
536
- if (debugKeystrokeLogging) {
537
- debugLogger.log('[DEBUG] Dropping incomplete sequence before next ESC:', JSON.stringify(garbage));
538
- }
539
- // Drop garbage and continue parsing from next ESC
540
- remainingBuffer = remainingBuffer.slice(nextEscIndex);
541
- // We made progress, so we can continue the loop to parse the next sequence
542
- continue;
543
- }
544
- // Check if buffer could become a valid kitty sequence
545
- const couldBeValid = couldBeKittySequence(remainingBuffer);
546
- if (!couldBeValid) {
547
- // Not a kitty sequence - flush as regular input immediately
548
- if (debugKeystrokeLogging) {
549
- debugLogger.log('[DEBUG] Not a kitty sequence, flushing:', JSON.stringify(remainingBuffer));
550
- }
551
- broadcast({
552
- name: '',
553
- ctrl: false,
554
- meta: false,
555
- shift: false,
556
- paste: false,
557
- sequence: remainingBuffer,
558
- });
559
- remainingBuffer = '';
560
- parsedAny = true;
593
+ }
594
+ const mouseParsed = parseMouseEvent(remainingBuffer);
595
+ if (mouseParsed) {
596
+ // These are handled by the separate mouse sequence parser.
597
+ // All we need to do is make sure we don't get confused by these
598
+ // sequences.
599
+ remainingBuffer = remainingBuffer.slice(mouseParsed.length);
600
+ parsedAny = true;
601
+ continue;
602
+ }
603
+ // If we can't parse a sequence at the start, check if there's
604
+ // another ESC later in the buffer. If so, the data before it
605
+ // is garbage/incomplete and should be dropped so we can
606
+ // process the next sequence.
607
+ const nextEscIndex = remainingBuffer.indexOf(ESC, 1);
608
+ if (nextEscIndex !== -1) {
609
+ const garbage = remainingBuffer.slice(0, nextEscIndex);
610
+ // Special case: if garbage is exactly ESC, it's likely a rapid ESC press.
611
+ if (garbage === ESC) {
612
+ if (debugKeystrokeLogging) {
613
+ debugLogger.log('[DEBUG] Flushing rapid ESC before next ESC:', JSON.stringify(garbage));
561
614
  }
562
- else if (remainingBuffer.length > MAX_KITTY_SEQUENCE_LENGTH) {
563
- // Buffer overflow - log and clear
564
- if (debugKeystrokeLogging) {
565
- debugLogger.log('[DEBUG] Kitty buffer overflow, clearing:', JSON.stringify(remainingBuffer));
566
- }
567
- if (config) {
568
- const event = new KittySequenceOverflowEvent(remainingBuffer.length, remainingBuffer);
569
- logKittySequenceOverflow(config, event);
570
- }
571
- // Flush as regular input
572
- broadcast({
573
- name: '',
574
- ctrl: false,
575
- meta: false,
576
- shift: false,
577
- paste: false,
578
- sequence: remainingBuffer,
579
- });
580
- remainingBuffer = '';
581
- parsedAny = true;
615
+ broadcast({
616
+ name: 'escape',
617
+ ctrl: false,
618
+ meta: true,
619
+ shift: false,
620
+ paste: false,
621
+ sequence: garbage,
622
+ });
623
+ }
624
+ else {
625
+ if (debugKeystrokeLogging) {
626
+ debugLogger.log('[DEBUG] Dropping incomplete sequence before next ESC:', JSON.stringify(garbage));
582
627
  }
583
- else {
584
- if (config?.getDebugMode() || debugKeystrokeLogging) {
585
- debugLogger.warn('Kitty sequence buffer has content:', JSON.stringify(kittySequenceBuffer));
586
- }
587
- // Could be valid but incomplete - set timeout
588
- kittySequenceTimeout = setTimeout(() => {
589
- if (kittySequenceBuffer) {
590
- if (debugKeystrokeLogging) {
591
- debugLogger.log('[DEBUG] Kitty sequence timeout, flushing:', JSON.stringify(kittySequenceBuffer));
592
- }
593
- broadcast({
594
- name: '',
595
- ctrl: false,
596
- meta: false,
597
- shift: false,
598
- paste: false,
599
- sequence: kittySequenceBuffer,
600
- });
601
- kittySequenceBuffer = '';
628
+ }
629
+ // Continue parsing from next ESC
630
+ remainingBuffer = remainingBuffer.slice(nextEscIndex);
631
+ // We made progress, so we can continue the loop to parse the next sequence
632
+ continue;
633
+ }
634
+ // Check if buffer could become a valid sequence
635
+ const couldBeValidKitty = kittyProtocolEnabled && couldBeKittySequence(remainingBuffer);
636
+ const isMouse = isIncompleteMouseSequence(remainingBuffer);
637
+ const couldBeValid = couldBeValidKitty || isMouse;
638
+ if (!couldBeValid) {
639
+ // Not a valid sequence - flush as regular input immediately
640
+ if (debugKeystrokeLogging) {
641
+ debugLogger.log('[DEBUG] Not a valid sequence, flushing:', JSON.stringify(remainingBuffer));
642
+ }
643
+ broadcast({
644
+ name: '',
645
+ ctrl: false,
646
+ meta: false,
647
+ shift: false,
648
+ paste: false,
649
+ sequence: remainingBuffer,
650
+ });
651
+ remainingBuffer = '';
652
+ parsedAny = true;
653
+ }
654
+ else if (remainingBuffer.length > MAX_KITTY_SEQUENCE_LENGTH) {
655
+ // Buffer overflow - log and clear
656
+ if (debugKeystrokeLogging) {
657
+ debugLogger.log('[DEBUG] Input buffer overflow, clearing:', JSON.stringify(remainingBuffer));
658
+ }
659
+ if (config && kittyProtocolEnabled) {
660
+ const event = new KittySequenceOverflowEvent(remainingBuffer.length, remainingBuffer);
661
+ logKittySequenceOverflow(config, event);
662
+ }
663
+ // Flush as regular input
664
+ broadcast({
665
+ name: '',
666
+ ctrl: false,
667
+ meta: false,
668
+ shift: false,
669
+ paste: false,
670
+ sequence: remainingBuffer,
671
+ });
672
+ remainingBuffer = '';
673
+ parsedAny = true;
674
+ }
675
+ else {
676
+ if ((config?.getDebugMode() || debugKeystrokeLogging) &&
677
+ !couldBeMouseSequence(inputBuffer)) {
678
+ debugLogger.warn('Input sequence buffer has content:', JSON.stringify(inputBuffer));
679
+ }
680
+ // Could be valid but incomplete - set timeout
681
+ // Only set timeout if it's NOT a mouse sequence.
682
+ // Mouse sequences might be slow (e.g. over network) and we don't want to
683
+ // flush them as garbage keypresses.
684
+ // However, if it's just ESC or ESC[, it might be a user typing slowly,
685
+ // so we should still timeout in that case.
686
+ const isAmbiguousPrefix = remainingBuffer === ESC || remainingBuffer === `${ESC}[`;
687
+ if (!isMouse || isAmbiguousPrefix) {
688
+ inputTimeout = setTimeout(() => {
689
+ if (inputBuffer) {
690
+ if (debugKeystrokeLogging) {
691
+ debugLogger.log('[DEBUG] Input sequence timeout, flushing:', JSON.stringify(inputBuffer));
602
692
  }
603
- kittySequenceTimeout = null;
604
- }, KITTY_SEQUENCE_TIMEOUT_MS);
605
- break;
693
+ const isEscape = inputBuffer === ESC;
694
+ broadcast({
695
+ name: isEscape ? 'escape' : '',
696
+ ctrl: false,
697
+ meta: isEscape,
698
+ shift: false,
699
+ paste: false,
700
+ sequence: inputBuffer,
701
+ });
702
+ inputBuffer = '';
703
+ }
704
+ inputTimeout = null;
705
+ }, KITTY_SEQUENCE_TIMEOUT_MS);
706
+ }
707
+ else {
708
+ // It IS a mouse sequence and it's long enough to be unambiguously NOT just a user hitting ESC slowly.
709
+ // We just wait for more data.
710
+ if (inputTimeout) {
711
+ clearTimeout(inputTimeout);
712
+ inputTimeout = null;
606
713
  }
607
714
  }
715
+ break;
608
716
  }
609
- kittySequenceBuffer = remainingBuffer;
610
- if (parsedAny || kittySequenceBuffer)
611
- return;
612
717
  }
718
+ inputBuffer = remainingBuffer;
719
+ if (parsedAny || inputBuffer)
720
+ return;
613
721
  }
614
722
  if (key.name === 'return' && key.sequence === `${ESC}\r`) {
615
723
  key.meta = true;
616
724
  }
617
- broadcast({ ...key, paste: isPaste });
618
- };
619
- const handleRawKeypress = (data) => {
620
- const pasteModePrefixBuffer = Buffer.from(PASTE_MODE_PREFIX);
621
- const pasteModeSuffixBuffer = Buffer.from(PASTE_MODE_SUFFIX);
622
- let pos = 0;
623
- while (pos < data.length) {
624
- const prefixPos = data.indexOf(pasteModePrefixBuffer, pos);
625
- const suffixPos = data.indexOf(pasteModeSuffixBuffer, pos);
626
- const isPrefixNext = prefixPos !== -1 && (suffixPos === -1 || prefixPos < suffixPos);
627
- const isSuffixNext = suffixPos !== -1 && (prefixPos === -1 || suffixPos < prefixPos);
628
- let nextMarkerPos = -1;
629
- let markerLength = 0;
630
- if (isPrefixNext) {
631
- nextMarkerPos = prefixPos;
632
- }
633
- else if (isSuffixNext) {
634
- nextMarkerPos = suffixPos;
635
- }
636
- markerLength = pasteModeSuffixBuffer.length;
637
- if (nextMarkerPos === -1) {
638
- keypressStream.write(data.slice(pos));
639
- return;
640
- }
641
- const nextData = data.slice(pos, nextMarkerPos);
642
- if (nextData.length > 0) {
643
- keypressStream.write(nextData);
644
- }
645
- const createPasteKeyEvent = (name) => ({
646
- name,
647
- ctrl: false,
648
- meta: false,
649
- shift: false,
650
- paste: false,
651
- sequence: '',
652
- });
653
- if (isPrefixNext) {
654
- handleKeypress(undefined, createPasteKeyEvent('paste-start'));
655
- }
656
- else if (isSuffixNext) {
657
- handleKeypress(undefined, createPasteKeyEvent('paste-end'));
658
- }
659
- pos = nextMarkerPos + markerLength;
660
- }
725
+ broadcast({ ...key, paste: pasteBuffer !== null });
661
726
  };
727
+ let cleanup = () => { };
662
728
  let rl;
663
- if (usePassthrough) {
729
+ if (keypressStream !== null) {
664
730
  rl = readline.createInterface({
665
731
  input: keypressStream,
666
732
  escapeCodeTimeout: 0,
667
733
  });
668
734
  readline.emitKeypressEvents(keypressStream, rl);
735
+ const parser = pasteMarkerParser(keypressStream, handleKeypress);
736
+ parser.next(); // prime the generator so it starts listening.
737
+ let timeoutId;
738
+ const handleRawKeypress = (data) => {
739
+ clearTimeout(timeoutId);
740
+ parser.next(data);
741
+ timeoutId = setTimeout(() => parser.next(''), PASTE_CODE_TIMEOUT_MS);
742
+ };
669
743
  keypressStream.on('keypress', handleKeypress);
744
+ process.stdin.setEncoding('utf8'); // so handleRawKeypress gets strings
670
745
  stdin.on('data', handleRawKeypress);
746
+ cleanup = () => {
747
+ keypressStream.removeListener('keypress', handleKeypress);
748
+ stdin.removeListener('data', handleRawKeypress);
749
+ };
671
750
  }
672
751
  else {
673
752
  rl = readline.createInterface({ input: stdin, escapeCodeTimeout: 0 });
674
753
  readline.emitKeypressEvents(stdin, rl);
675
754
  stdin.on('keypress', handleKeypress);
755
+ cleanup = () => stdin.removeListener('keypress', handleKeypress);
676
756
  }
677
757
  return () => {
678
- if (usePassthrough) {
679
- keypressStream.removeListener('keypress', handleKeypress);
680
- stdin.removeListener('data', handleRawKeypress);
681
- }
682
- else {
683
- stdin.removeListener('keypress', handleKeypress);
684
- }
758
+ cleanup();
685
759
  rl.close();
686
760
  // Restore the terminal to its original state.
687
761
  if (wasRaw === false) {
@@ -691,24 +765,24 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
691
765
  clearTimeout(backslashTimeout);
692
766
  backslashTimeout = null;
693
767
  }
694
- if (kittySequenceTimeout) {
695
- clearTimeout(kittySequenceTimeout);
696
- kittySequenceTimeout = null;
768
+ if (inputTimeout) {
769
+ clearTimeout(inputTimeout);
770
+ inputTimeout = null;
697
771
  }
698
772
  // Flush any pending kitty sequence data to avoid data loss on exit.
699
- if (kittySequenceBuffer) {
773
+ if (inputBuffer) {
700
774
  broadcast({
701
775
  name: '',
702
776
  ctrl: false,
703
777
  meta: false,
704
778
  shift: false,
705
779
  paste: false,
706
- sequence: kittySequenceBuffer,
780
+ sequence: inputBuffer,
707
781
  });
708
- kittySequenceBuffer = '';
782
+ inputBuffer = '';
709
783
  }
710
784
  // Flush any pending paste data to avoid data loss on exit.
711
- if (isPaste) {
785
+ if (pasteBuffer !== null) {
712
786
  broadcast({
713
787
  name: '',
714
788
  ctrl: false,
@@ -717,23 +791,19 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
717
791
  paste: true,
718
792
  sequence: pasteBuffer.toString(),
719
793
  });
720
- pasteBuffer = Buffer.alloc(0);
721
- }
722
- if (draggingTimerRef.current) {
723
- clearTimeout(draggingTimerRef.current);
724
- draggingTimerRef.current = null;
794
+ pasteBuffer = null;
725
795
  }
726
- if (isDraggingRef.current && dragBufferRef.current) {
796
+ clearDraggingTimer();
797
+ if (dragBuffer) {
727
798
  broadcast({
728
799
  name: '',
729
800
  ctrl: false,
730
801
  meta: false,
731
802
  shift: false,
732
803
  paste: true,
733
- sequence: dragBufferRef.current,
804
+ sequence: dragBuffer,
734
805
  });
735
- isDraggingRef.current = false;
736
- dragBufferRef.current = '';
806
+ dragBuffer = '';
737
807
  }
738
808
  };
739
809
  }, [
@@ -741,8 +811,8 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
741
811
  setRawMode,
742
812
  kittyProtocolEnabled,
743
813
  config,
744
- subscribers,
745
814
  debugKeystrokeLogging,
815
+ broadcast,
746
816
  ]);
747
817
  return (_jsx(KeypressContext.Provider, { value: { subscribe, unsubscribe }, children: children }));
748
818
  }