nastech-tui 0.0.1

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 (424) hide show
  1. package/.prettierrc +11 -0
  2. package/README.md +346 -0
  3. package/eslint.config.mjs +111 -0
  4. package/package.json +51 -0
  5. package/packages/nastech-ink/ambient.d.ts +83 -0
  6. package/packages/nastech-ink/index.d.ts +40 -0
  7. package/packages/nastech-ink/index.js +1 -0
  8. package/packages/nastech-ink/nastech-ink/ambient.d.ts +83 -0
  9. package/packages/nastech-ink/nastech-ink/index.d.ts +40 -0
  10. package/packages/nastech-ink/nastech-ink/index.js +1 -0
  11. package/packages/nastech-ink/nastech-ink/package.json +54 -0
  12. package/packages/nastech-ink/nastech-ink/src/bootstrap/state.ts +9 -0
  13. package/packages/nastech-ink/nastech-ink/src/entry-exports.ts +32 -0
  14. package/packages/nastech-ink/nastech-ink/src/hooks/use-stderr.ts +15 -0
  15. package/packages/nastech-ink/nastech-ink/src/hooks/use-stdout.ts +15 -0
  16. package/packages/nastech-ink/nastech-ink/src/ink/Ansi.tsx +435 -0
  17. package/packages/nastech-ink/nastech-ink/src/ink/app-mouse.test.ts +123 -0
  18. package/packages/nastech-ink/nastech-ink/src/ink/bidi.ts +145 -0
  19. package/packages/nastech-ink/nastech-ink/src/ink/cache-eviction.ts +45 -0
  20. package/packages/nastech-ink/nastech-ink/src/ink/clearTerminal.ts +68 -0
  21. package/packages/nastech-ink/nastech-ink/src/ink/colorize.test.ts +60 -0
  22. package/packages/nastech-ink/nastech-ink/src/ink/colorize.ts +277 -0
  23. package/packages/nastech-ink/nastech-ink/src/ink/components/AlternateScreen.tsx +133 -0
  24. package/packages/nastech-ink/nastech-ink/src/ink/components/App.tsx +830 -0
  25. package/packages/nastech-ink/nastech-ink/src/ink/components/AppContext.ts +20 -0
  26. package/packages/nastech-ink/nastech-ink/src/ink/components/Box.tsx +294 -0
  27. package/packages/nastech-ink/nastech-ink/src/ink/components/Button.tsx +236 -0
  28. package/packages/nastech-ink/nastech-ink/src/ink/components/ClockContext.tsx +133 -0
  29. package/packages/nastech-ink/nastech-ink/src/ink/components/CursorAdvanceContext.ts +35 -0
  30. package/packages/nastech-ink/nastech-ink/src/ink/components/CursorDeclarationContext.ts +28 -0
  31. package/packages/nastech-ink/nastech-ink/src/ink/components/ErrorOverview.tsx +130 -0
  32. package/packages/nastech-ink/nastech-ink/src/ink/components/Link.tsx +38 -0
  33. package/packages/nastech-ink/nastech-ink/src/ink/components/Newline.tsx +43 -0
  34. package/packages/nastech-ink/nastech-ink/src/ink/components/NoSelect.tsx +73 -0
  35. package/packages/nastech-ink/nastech-ink/src/ink/components/RawAnsi.tsx +61 -0
  36. package/packages/nastech-ink/nastech-ink/src/ink/components/ScrollBox.tsx +290 -0
  37. package/packages/nastech-ink/nastech-ink/src/ink/components/Spacer.tsx +23 -0
  38. package/packages/nastech-ink/nastech-ink/src/ink/components/StdinContext.ts +25 -0
  39. package/packages/nastech-ink/nastech-ink/src/ink/components/TerminalFocusContext.tsx +63 -0
  40. package/packages/nastech-ink/nastech-ink/src/ink/components/TerminalSizeContext.tsx +7 -0
  41. package/packages/nastech-ink/nastech-ink/src/ink/components/Text.test.ts +38 -0
  42. package/packages/nastech-ink/nastech-ink/src/ink/components/Text.tsx +336 -0
  43. package/packages/nastech-ink/nastech-ink/src/ink/constants.ts +6 -0
  44. package/packages/nastech-ink/nastech-ink/src/ink/cursor.ts +5 -0
  45. package/packages/nastech-ink/nastech-ink/src/ink/devtools.ts +2 -0
  46. package/packages/nastech-ink/nastech-ink/src/ink/dom.ts +495 -0
  47. package/packages/nastech-ink/nastech-ink/src/ink/events/click-event.ts +38 -0
  48. package/packages/nastech-ink/nastech-ink/src/ink/events/cmd-shortcuts.test.ts +65 -0
  49. package/packages/nastech-ink/nastech-ink/src/ink/events/dispatcher.ts +242 -0
  50. package/packages/nastech-ink/nastech-ink/src/ink/events/emitter.ts +40 -0
  51. package/packages/nastech-ink/nastech-ink/src/ink/events/event-handlers.ts +84 -0
  52. package/packages/nastech-ink/nastech-ink/src/ink/events/event.ts +11 -0
  53. package/packages/nastech-ink/nastech-ink/src/ink/events/focus-event.ts +18 -0
  54. package/packages/nastech-ink/nastech-ink/src/ink/events/input-event.ts +176 -0
  55. package/packages/nastech-ink/nastech-ink/src/ink/events/keyboard-event.ts +57 -0
  56. package/packages/nastech-ink/nastech-ink/src/ink/events/mouse-event.ts +18 -0
  57. package/packages/nastech-ink/nastech-ink/src/ink/events/paste-event.ts +10 -0
  58. package/packages/nastech-ink/nastech-ink/src/ink/events/resize-event.ts +12 -0
  59. package/packages/nastech-ink/nastech-ink/src/ink/events/terminal-event.ts +107 -0
  60. package/packages/nastech-ink/nastech-ink/src/ink/events/terminal-focus-event.ts +19 -0
  61. package/packages/nastech-ink/nastech-ink/src/ink/focus.ts +219 -0
  62. package/packages/nastech-ink/nastech-ink/src/ink/frame.ts +124 -0
  63. package/packages/nastech-ink/nastech-ink/src/ink/get-max-width.ts +27 -0
  64. package/packages/nastech-ink/nastech-ink/src/ink/global.d.ts +1 -0
  65. package/packages/nastech-ink/nastech-ink/src/ink/hit-test.test.ts +38 -0
  66. package/packages/nastech-ink/nastech-ink/src/ink/hit-test.ts +224 -0
  67. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-animation-frame.ts +62 -0
  68. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-app.ts +9 -0
  69. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-cursor-advance.ts +33 -0
  70. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-declared-cursor.ts +75 -0
  71. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-external-process.ts +27 -0
  72. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-input.ts +95 -0
  73. package/packages/nastech-ink/nastech-ink/src/ink/hooks/use-interval.ts +71 -0
  74. package/packages/nastech-ink/package.json +57 -0
  75. package/packages/nastech-ink/src/bootstrap/state.ts +9 -0
  76. package/packages/nastech-ink/src/entry-exports.ts +32 -0
  77. package/packages/nastech-ink/src/hooks/use-stderr.ts +15 -0
  78. package/packages/nastech-ink/src/hooks/use-stdout.ts +15 -0
  79. package/packages/nastech-ink/src/ink/Ansi.tsx +435 -0
  80. package/packages/nastech-ink/src/ink/app-mouse.test.ts +123 -0
  81. package/packages/nastech-ink/src/ink/app-rawmode-mouse.test.ts +91 -0
  82. package/packages/nastech-ink/src/ink/bidi.ts +145 -0
  83. package/packages/nastech-ink/src/ink/cache-eviction.ts +45 -0
  84. package/packages/nastech-ink/src/ink/clearTerminal.ts +68 -0
  85. package/packages/nastech-ink/src/ink/colorize.test.ts +60 -0
  86. package/packages/nastech-ink/src/ink/colorize.ts +277 -0
  87. package/packages/nastech-ink/src/ink/components/AlternateScreen.tsx +133 -0
  88. package/packages/nastech-ink/src/ink/components/App.tsx +855 -0
  89. package/packages/nastech-ink/src/ink/components/AppContext.ts +20 -0
  90. package/packages/nastech-ink/src/ink/components/Box.tsx +294 -0
  91. package/packages/nastech-ink/src/ink/components/Button.tsx +236 -0
  92. package/packages/nastech-ink/src/ink/components/ClockContext.tsx +133 -0
  93. package/packages/nastech-ink/src/ink/components/CursorAdvanceContext.ts +35 -0
  94. package/packages/nastech-ink/src/ink/components/CursorDeclarationContext.ts +28 -0
  95. package/packages/nastech-ink/src/ink/components/ErrorOverview.tsx +130 -0
  96. package/packages/nastech-ink/src/ink/components/Link.tsx +38 -0
  97. package/packages/nastech-ink/src/ink/components/Newline.tsx +43 -0
  98. package/packages/nastech-ink/src/ink/components/NoSelect.tsx +73 -0
  99. package/packages/nastech-ink/src/ink/components/RawAnsi.tsx +61 -0
  100. package/packages/nastech-ink/src/ink/components/ScrollBox.tsx +290 -0
  101. package/packages/nastech-ink/src/ink/components/Spacer.tsx +23 -0
  102. package/packages/nastech-ink/src/ink/components/StdinContext.ts +25 -0
  103. package/packages/nastech-ink/src/ink/components/TerminalFocusContext.tsx +63 -0
  104. package/packages/nastech-ink/src/ink/components/TerminalSizeContext.tsx +7 -0
  105. package/packages/nastech-ink/src/ink/components/Text.test.ts +38 -0
  106. package/packages/nastech-ink/src/ink/components/Text.tsx +336 -0
  107. package/packages/nastech-ink/src/ink/constants.ts +6 -0
  108. package/packages/nastech-ink/src/ink/cursor.ts +5 -0
  109. package/packages/nastech-ink/src/ink/devtools.ts +2 -0
  110. package/packages/nastech-ink/src/ink/dom.ts +495 -0
  111. package/packages/nastech-ink/src/ink/events/click-event.ts +38 -0
  112. package/packages/nastech-ink/src/ink/events/cmd-shortcuts.test.ts +65 -0
  113. package/packages/nastech-ink/src/ink/events/dispatcher.ts +242 -0
  114. package/packages/nastech-ink/src/ink/events/emitter.ts +40 -0
  115. package/packages/nastech-ink/src/ink/events/event-handlers.ts +84 -0
  116. package/packages/nastech-ink/src/ink/events/event.ts +11 -0
  117. package/packages/nastech-ink/src/ink/events/focus-event.ts +18 -0
  118. package/packages/nastech-ink/src/ink/events/input-event.ts +176 -0
  119. package/packages/nastech-ink/src/ink/events/keyboard-event.ts +57 -0
  120. package/packages/nastech-ink/src/ink/events/mouse-event.ts +18 -0
  121. package/packages/nastech-ink/src/ink/events/paste-event.ts +10 -0
  122. package/packages/nastech-ink/src/ink/events/resize-event.ts +12 -0
  123. package/packages/nastech-ink/src/ink/events/terminal-event.ts +107 -0
  124. package/packages/nastech-ink/src/ink/events/terminal-focus-event.ts +19 -0
  125. package/packages/nastech-ink/src/ink/focus.ts +219 -0
  126. package/packages/nastech-ink/src/ink/frame.ts +124 -0
  127. package/packages/nastech-ink/src/ink/get-max-width.ts +27 -0
  128. package/packages/nastech-ink/src/ink/global.d.ts +1 -0
  129. package/packages/nastech-ink/src/ink/hit-test.test.ts +38 -0
  130. package/packages/nastech-ink/src/ink/hit-test.ts +224 -0
  131. package/packages/nastech-ink/src/ink/hooks/use-animation-frame.ts +62 -0
  132. package/packages/nastech-ink/src/ink/hooks/use-app.ts +9 -0
  133. package/packages/nastech-ink/src/ink/hooks/use-cursor-advance.ts +33 -0
  134. package/packages/nastech-ink/src/ink/hooks/use-declared-cursor.ts +75 -0
  135. package/packages/nastech-ink/src/ink/hooks/use-external-process.ts +27 -0
  136. package/packages/nastech-ink/src/ink/hooks/use-input.ts +95 -0
  137. package/packages/nastech-ink/src/ink/hooks/use-interval.ts +71 -0
  138. package/packages/nastech-ink/src/ink/hooks/use-search-highlight.ts +56 -0
  139. package/packages/nastech-ink/src/ink/hooks/use-selection.ts +101 -0
  140. package/packages/nastech-ink/src/ink/hooks/use-stdin.ts +9 -0
  141. package/packages/nastech-ink/src/ink/hooks/use-tab-status.ts +71 -0
  142. package/packages/nastech-ink/src/ink/hooks/use-terminal-focus.ts +18 -0
  143. package/packages/nastech-ink/src/ink/hooks/use-terminal-title.ts +34 -0
  144. package/packages/nastech-ink/src/ink/hooks/use-terminal-viewport.ts +100 -0
  145. package/packages/nastech-ink/src/ink/hyperlinkHover.ts +52 -0
  146. package/packages/nastech-ink/src/ink/ink-cursor-advance.test.ts +234 -0
  147. package/packages/nastech-ink/src/ink/ink-resize.test.ts +50 -0
  148. package/packages/nastech-ink/src/ink/ink.tsx +2705 -0
  149. package/packages/nastech-ink/src/ink/instances.ts +10 -0
  150. package/packages/nastech-ink/src/ink/layout/engine.ts +6 -0
  151. package/packages/nastech-ink/src/ink/layout/geometry.ts +98 -0
  152. package/packages/nastech-ink/src/ink/layout/node.ts +145 -0
  153. package/packages/nastech-ink/src/ink/layout/yoga.ts +313 -0
  154. package/packages/nastech-ink/src/ink/line-width-cache.ts +38 -0
  155. package/packages/nastech-ink/src/ink/log-update.test.ts +223 -0
  156. package/packages/nastech-ink/src/ink/log-update.ts +752 -0
  157. package/packages/nastech-ink/src/ink/lru.ts +14 -0
  158. package/packages/nastech-ink/src/ink/measure-element.ts +23 -0
  159. package/packages/nastech-ink/src/ink/measure-text.ts +50 -0
  160. package/packages/nastech-ink/src/ink/node-cache.ts +53 -0
  161. package/packages/nastech-ink/src/ink/optimizer.ts +99 -0
  162. package/packages/nastech-ink/src/ink/output.ts +845 -0
  163. package/packages/nastech-ink/src/ink/parse-keypress.test.ts +133 -0
  164. package/packages/nastech-ink/src/ink/parse-keypress.ts +848 -0
  165. package/packages/nastech-ink/src/ink/reconciler.ts +382 -0
  166. package/packages/nastech-ink/src/ink/render-border.ts +206 -0
  167. package/packages/nastech-ink/src/ink/render-node-to-output.ts +1582 -0
  168. package/packages/nastech-ink/src/ink/render-to-screen.ts +236 -0
  169. package/packages/nastech-ink/src/ink/renderer.ts +169 -0
  170. package/packages/nastech-ink/src/ink/root.ts +204 -0
  171. package/packages/nastech-ink/src/ink/screen.ts +1590 -0
  172. package/packages/nastech-ink/src/ink/searchHighlight.ts +91 -0
  173. package/packages/nastech-ink/src/ink/selection.test.ts +82 -0
  174. package/packages/nastech-ink/src/ink/selection.ts +1143 -0
  175. package/packages/nastech-ink/src/ink/squash-text-nodes.ts +74 -0
  176. package/packages/nastech-ink/src/ink/stringWidth.ts +341 -0
  177. package/packages/nastech-ink/src/ink/styles.ts +750 -0
  178. package/packages/nastech-ink/src/ink/supports-hyperlinks.ts +51 -0
  179. package/packages/nastech-ink/src/ink/tabstops.ts +44 -0
  180. package/packages/nastech-ink/src/ink/terminal-focus-state.ts +52 -0
  181. package/packages/nastech-ink/src/ink/terminal-querier.ts +222 -0
  182. package/packages/nastech-ink/src/ink/terminal.test.ts +15 -0
  183. package/packages/nastech-ink/src/ink/terminal.ts +299 -0
  184. package/packages/nastech-ink/src/ink/termio/ansi.ts +75 -0
  185. package/packages/nastech-ink/src/ink/termio/csi.ts +334 -0
  186. package/packages/nastech-ink/src/ink/termio/dec.ts +99 -0
  187. package/packages/nastech-ink/src/ink/termio/esc.ts +69 -0
  188. package/packages/nastech-ink/src/ink/termio/osc.test.ts +191 -0
  189. package/packages/nastech-ink/src/ink/termio/osc.ts +724 -0
  190. package/packages/nastech-ink/src/ink/termio/parser.ts +467 -0
  191. package/packages/nastech-ink/src/ink/termio/sgr.ts +362 -0
  192. package/packages/nastech-ink/src/ink/termio/tokenize.test.ts +185 -0
  193. package/packages/nastech-ink/src/ink/termio/tokenize.ts +350 -0
  194. package/packages/nastech-ink/src/ink/termio/types.ts +230 -0
  195. package/packages/nastech-ink/src/ink/termio.ts +42 -0
  196. package/packages/nastech-ink/src/ink/useTerminalNotification.ts +110 -0
  197. package/packages/nastech-ink/src/ink/warn.ts +15 -0
  198. package/packages/nastech-ink/src/ink/widest-line.ts +22 -0
  199. package/packages/nastech-ink/src/ink/wrap-text.test.ts +17 -0
  200. package/packages/nastech-ink/src/ink/wrap-text.ts +144 -0
  201. package/packages/nastech-ink/src/ink/wrapAnsi.ts +13 -0
  202. package/packages/nastech-ink/src/native-ts/yoga-layout/enums.ts +112 -0
  203. package/packages/nastech-ink/src/native-ts/yoga-layout/index.ts +2326 -0
  204. package/packages/nastech-ink/src/utils/debug.ts +6 -0
  205. package/packages/nastech-ink/src/utils/earlyInput.ts +131 -0
  206. package/packages/nastech-ink/src/utils/env.ts +66 -0
  207. package/packages/nastech-ink/src/utils/envUtils.ts +13 -0
  208. package/packages/nastech-ink/src/utils/execFileNoThrow.test.ts +146 -0
  209. package/packages/nastech-ink/src/utils/execFileNoThrow.ts +115 -0
  210. package/packages/nastech-ink/src/utils/fullscreen.ts +3 -0
  211. package/packages/nastech-ink/src/utils/intl.ts +87 -0
  212. package/packages/nastech-ink/src/utils/log.ts +7 -0
  213. package/packages/nastech-ink/src/utils/semver.ts +57 -0
  214. package/packages/nastech-ink/src/utils/sliceAnsi.ts +106 -0
  215. package/packages/nastech-ink/text-input.d.ts +2 -0
  216. package/packages/nastech-ink/text-input.js +1 -0
  217. package/scripts/build.mjs +61 -0
  218. package/scripts/profile-tui.mjs +121 -0
  219. package/src/__tests__/activeSessionSwitcher.test.ts +157 -0
  220. package/src/__tests__/appChromeStatusRule.test.tsx +84 -0
  221. package/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +73 -0
  222. package/src/__tests__/approvalAction.test.ts +50 -0
  223. package/src/__tests__/asCommandDispatch.test.ts +27 -0
  224. package/src/__tests__/blockLayout.test.ts +122 -0
  225. package/src/__tests__/clipboard.test.ts +369 -0
  226. package/src/__tests__/constants.test.ts +53 -0
  227. package/src/__tests__/createGatewayEventHandler.test.ts +1091 -0
  228. package/src/__tests__/createSlashHandler.test.ts +822 -0
  229. package/src/__tests__/creditsCommand.test.ts +144 -0
  230. package/src/__tests__/cursorDriftRegression.test.ts +114 -0
  231. package/src/__tests__/details.test.ts +115 -0
  232. package/src/__tests__/emoji.test.ts +64 -0
  233. package/src/__tests__/externalLink.test.ts +144 -0
  234. package/src/__tests__/forceTruecolor.test.ts +191 -0
  235. package/src/__tests__/gatewayClient.test.ts +394 -0
  236. package/src/__tests__/gatewayRecovery.test.ts +47 -0
  237. package/src/__tests__/markdown.test.ts +331 -0
  238. package/src/__tests__/mathUnicode.test.ts +293 -0
  239. package/src/__tests__/memoryMonitor.test.ts +102 -0
  240. package/src/__tests__/messageLine.test.ts +19 -0
  241. package/src/__tests__/messages.test.ts +92 -0
  242. package/src/__tests__/orchestratorPromptSession.test.ts +64 -0
  243. package/src/__tests__/osc52.test.ts +67 -0
  244. package/src/__tests__/parentLog.test.ts +75 -0
  245. package/src/__tests__/paths.test.ts +70 -0
  246. package/src/__tests__/platform.test.ts +556 -0
  247. package/src/__tests__/precisionWheel.test.ts +44 -0
  248. package/src/__tests__/prompt.test.ts +31 -0
  249. package/src/__tests__/providers.test.ts +65 -0
  250. package/src/__tests__/reasoning.test.ts +76 -0
  251. package/src/__tests__/rpc.test.ts +27 -0
  252. package/src/__tests__/scroll.test.ts +99 -0
  253. package/src/__tests__/slashParity.test.ts +123 -0
  254. package/src/__tests__/spawnHistoryStore.test.ts +46 -0
  255. package/src/__tests__/stateIsolation.test.ts +46 -0
  256. package/src/__tests__/statusBarTicker.test.ts +18 -0
  257. package/src/__tests__/statusRule.test.ts +32 -0
  258. package/src/__tests__/streamingMarkdown.test.ts +121 -0
  259. package/src/__tests__/subagentTree.test.ts +407 -0
  260. package/src/__tests__/syntax.test.ts +45 -0
  261. package/src/__tests__/terminalModes.test.ts +39 -0
  262. package/src/__tests__/terminalParity.test.ts +77 -0
  263. package/src/__tests__/terminalSetup.test.ts +386 -0
  264. package/src/__tests__/termux.test.ts +35 -0
  265. package/src/__tests__/termuxComposerLayout.test.ts +40 -0
  266. package/src/__tests__/text.test.ts +233 -0
  267. package/src/__tests__/textInputBurstInput.test.ts +40 -0
  268. package/src/__tests__/textInputCursorSourceOfTruth.test.ts +50 -0
  269. package/src/__tests__/textInputFastEcho.test.ts +200 -0
  270. package/src/__tests__/textInputLineNav.test.ts +55 -0
  271. package/src/__tests__/textInputPassThrough.test.ts +59 -0
  272. package/src/__tests__/textInputRightClick.test.ts +48 -0
  273. package/src/__tests__/textInputWrap.test.ts +151 -0
  274. package/src/__tests__/theme.test.ts +311 -0
  275. package/src/__tests__/turnControllerNotice.test.ts +43 -0
  276. package/src/__tests__/turnStore.test.ts +66 -0
  277. package/src/__tests__/useCompletion.test.ts +35 -0
  278. package/src/__tests__/useComposerState.test.ts +59 -0
  279. package/src/__tests__/useConfigSync.test.ts +460 -0
  280. package/src/__tests__/useInputHandlers.test.ts +77 -0
  281. package/src/__tests__/useQueue.test.ts +28 -0
  282. package/src/__tests__/useSessionLifecycle.test.ts +60 -0
  283. package/src/__tests__/useVirtualHistoryHeights.test.ts +39 -0
  284. package/src/__tests__/viewport.test.ts +58 -0
  285. package/src/__tests__/viewportStore.test.ts +85 -0
  286. package/src/__tests__/virtualHeights.test.ts +96 -0
  287. package/src/__tests__/virtualHistoryClamp.test.ts +19 -0
  288. package/src/__tests__/virtualHistoryOffsetCache.test.ts +282 -0
  289. package/src/__tests__/wheelAccel.test.ts +138 -0
  290. package/src/app/createGatewayEventHandler.ts +833 -0
  291. package/src/app/createSlashHandler.ts +130 -0
  292. package/src/app/delegationStore.ts +77 -0
  293. package/src/app/gatewayContext.tsx +19 -0
  294. package/src/app/gatewayRecovery.ts +35 -0
  295. package/src/app/inputSelectionStore.ts +15 -0
  296. package/src/app/interfaces.ts +394 -0
  297. package/src/app/overlayStore.ts +53 -0
  298. package/src/app/scroll.ts +71 -0
  299. package/src/app/setupHandoff.ts +54 -0
  300. package/src/app/slash/commands/core.ts +648 -0
  301. package/src/app/slash/commands/credits.ts +57 -0
  302. package/src/app/slash/commands/debug.ts +48 -0
  303. package/src/app/slash/commands/ops.ts +717 -0
  304. package/src/app/slash/commands/session.ts +554 -0
  305. package/src/app/slash/commands/setup.ts +20 -0
  306. package/src/app/slash/registry.ts +20 -0
  307. package/src/app/slash/types.ts +21 -0
  308. package/src/app/spawnHistoryStore.ts +159 -0
  309. package/src/app/turnController.ts +866 -0
  310. package/src/app/turnStore.ts +85 -0
  311. package/src/app/uiStore.ts +44 -0
  312. package/src/app/useComposerState.ts +367 -0
  313. package/src/app/useConfigSync.ts +288 -0
  314. package/src/app/useInputHandlers.ts +576 -0
  315. package/src/app/useLongRunToolCharms.ts +69 -0
  316. package/src/app/useMainApp.ts +1039 -0
  317. package/src/app/useSessionLifecycle.ts +366 -0
  318. package/src/app/useSubmission.ts +429 -0
  319. package/src/app.tsx +25 -0
  320. package/src/banner.ts +93 -0
  321. package/src/components/activeSessionSwitcher.tsx +635 -0
  322. package/src/components/agentsOverlay.tsx +1073 -0
  323. package/src/components/appChrome.tsx +554 -0
  324. package/src/components/appLayout.tsx +444 -0
  325. package/src/components/appOverlays.tsx +254 -0
  326. package/src/components/branding.tsx +466 -0
  327. package/src/components/fpsOverlay.tsx +30 -0
  328. package/src/components/helpHint.tsx +73 -0
  329. package/src/components/markdown.tsx +1119 -0
  330. package/src/components/maskedPrompt.tsx +34 -0
  331. package/src/components/messageLine.tsx +237 -0
  332. package/src/components/modelPicker.tsx +527 -0
  333. package/src/components/overlayControls.tsx +50 -0
  334. package/src/components/pluginsHub.tsx +238 -0
  335. package/src/components/prompts.tsx +276 -0
  336. package/src/components/queuedMessages.tsx +64 -0
  337. package/src/components/sessionPicker.tsx +227 -0
  338. package/src/components/skillsHub.tsx +308 -0
  339. package/src/components/streamingAssistant.tsx +110 -0
  340. package/src/components/streamingMarkdown.tsx +174 -0
  341. package/src/components/textInput.tsx +1340 -0
  342. package/src/components/themed.tsx +30 -0
  343. package/src/components/thinking.tsx +1224 -0
  344. package/src/components/todoPanel.tsx +93 -0
  345. package/src/config/env.ts +64 -0
  346. package/src/config/limits.ts +13 -0
  347. package/src/config/timing.ts +6 -0
  348. package/src/content/charms.ts +1 -0
  349. package/src/content/faces.ts +17 -0
  350. package/src/content/fortunes.ts +30 -0
  351. package/src/content/hotkeys.ts +37 -0
  352. package/src/content/placeholders.ts +13 -0
  353. package/src/content/setup.ts +17 -0
  354. package/src/content/verbs.ts +38 -0
  355. package/src/domain/blockLayout.ts +146 -0
  356. package/src/domain/details.ts +76 -0
  357. package/src/domain/messages.ts +91 -0
  358. package/src/domain/paths.ts +16 -0
  359. package/src/domain/providers.ts +11 -0
  360. package/src/domain/roles.ts +9 -0
  361. package/src/domain/slash.ts +10 -0
  362. package/src/domain/usage.ts +3 -0
  363. package/src/domain/viewport.ts +51 -0
  364. package/src/entry.tsx +104 -0
  365. package/src/gatewayClient.ts +730 -0
  366. package/src/gatewayTypes.ts +568 -0
  367. package/src/hooks/useCompletion.ts +112 -0
  368. package/src/hooks/useGitBranch.ts +72 -0
  369. package/src/hooks/useInputHistory.ts +11 -0
  370. package/src/hooks/useQueue.ts +76 -0
  371. package/src/hooks/useVirtualHistory.ts +554 -0
  372. package/src/lib/circularBuffer.ts +48 -0
  373. package/src/lib/clipboard.ts +182 -0
  374. package/src/lib/editor.test.ts +74 -0
  375. package/src/lib/editor.ts +47 -0
  376. package/src/lib/emoji.ts +55 -0
  377. package/src/lib/externalCli.ts +16 -0
  378. package/src/lib/externalLink.ts +435 -0
  379. package/src/lib/forceTruecolor.ts +60 -0
  380. package/src/lib/fpsStore.ts +51 -0
  381. package/src/lib/fuzzy.test.ts +109 -0
  382. package/src/lib/fuzzy.ts +177 -0
  383. package/src/lib/gracefulExit.ts +47 -0
  384. package/src/lib/history.ts +82 -0
  385. package/src/lib/inputMetrics.ts +203 -0
  386. package/src/lib/liveProgress.test.ts +116 -0
  387. package/src/lib/liveProgress.ts +79 -0
  388. package/src/lib/mathUnicode.ts +770 -0
  389. package/src/lib/memory.test.ts +155 -0
  390. package/src/lib/memory.ts +188 -0
  391. package/src/lib/memoryMonitor.ts +109 -0
  392. package/src/lib/messages.test.ts +29 -0
  393. package/src/lib/messages.ts +8 -0
  394. package/src/lib/openExternalUrl.test.ts +217 -0
  395. package/src/lib/openExternalUrl.ts +158 -0
  396. package/src/lib/osc52.ts +73 -0
  397. package/src/lib/parentLog.ts +57 -0
  398. package/src/lib/perfPane.tsx +107 -0
  399. package/src/lib/platform.ts +409 -0
  400. package/src/lib/precisionWheel.ts +48 -0
  401. package/src/lib/prompt.ts +35 -0
  402. package/src/lib/reasoning.ts +55 -0
  403. package/src/lib/rpc.ts +41 -0
  404. package/src/lib/subagentTree.ts +355 -0
  405. package/src/lib/syntax.ts +117 -0
  406. package/src/lib/terminalModes.ts +51 -0
  407. package/src/lib/terminalParity.ts +78 -0
  408. package/src/lib/terminalSetup.ts +444 -0
  409. package/src/lib/termux.ts +29 -0
  410. package/src/lib/text.test.ts +18 -0
  411. package/src/lib/text.ts +339 -0
  412. package/src/lib/todo.test.ts +21 -0
  413. package/src/lib/todo.ts +9 -0
  414. package/src/lib/viewportStore.ts +124 -0
  415. package/src/lib/virtualHeights.ts +145 -0
  416. package/src/lib/wheelAccel.ts +190 -0
  417. package/src/protocol/interpolation.ts +3 -0
  418. package/src/protocol/paste.ts +1 -0
  419. package/src/theme.ts +589 -0
  420. package/src/types/nastech-ink.d.ts +176 -0
  421. package/src/types.ts +212 -0
  422. package/tsconfig.build.json +9 -0
  423. package/tsconfig.json +19 -0
  424. package/vitest.config.ts +7 -0
@@ -0,0 +1,409 @@
1
+ /** Platform-aware keybinding helpers.
2
+ *
3
+ * On macOS the "action" modifier is Cmd. Modern terminals that support kitty
4
+ * keyboard protocol report Cmd as `key.super`; legacy terminals often surface it
5
+ * as `key.meta`. Some macOS terminals also translate Cmd+Left/Right/Backspace
6
+ * into readline-style Ctrl+A/Ctrl+E/Ctrl+U before the app sees them.
7
+ * On other platforms the action modifier is Ctrl.
8
+ * Ctrl+C stays the interrupt key on macOS. On non-mac terminals it can also
9
+ * copy an active TUI selection, matching common terminal selection behavior.
10
+ */
11
+
12
+ export const isMac = process.platform === 'darwin'
13
+
14
+ /** True when the platform action-modifier is pressed (Cmd on macOS, Ctrl elsewhere). */
15
+ export const isActionMod = (key: { ctrl: boolean; meta: boolean; super?: boolean }): boolean =>
16
+ isMac ? key.meta || key.super === true : key.ctrl
17
+
18
+ /**
19
+ * Accept raw Ctrl+<letter> as an action shortcut on macOS, where `isActionMod`
20
+ * otherwise means Cmd. Two motivations:
21
+ * - Some macOS terminals rewrite Cmd navigation/deletion into readline control
22
+ * keys (Cmd+Left → Ctrl+A, Cmd+Right → Ctrl+E, Cmd+Backspace → Ctrl+U).
23
+ * - Ctrl+K (kill-to-end) and Ctrl+W (delete-word-back) are standard readline
24
+ * bindings that users expect to work regardless of platform, even though
25
+ * no terminal rewrites Cmd into them.
26
+ */
27
+ export const isMacActionFallback = (
28
+ key: { ctrl: boolean; meta: boolean; super?: boolean },
29
+ ch: string,
30
+ target: 'a' | 'e' | 'u' | 'k' | 'w'
31
+ ): boolean => isMac && key.ctrl && !key.meta && key.super !== true && ch.toLowerCase() === target
32
+
33
+ /** Match action-modifier + a single character (case-insensitive). */
34
+ export const isAction = (key: { ctrl: boolean; meta: boolean; super?: boolean }, ch: string, target: string): boolean =>
35
+ isActionMod(key) && ch.toLowerCase() === target
36
+
37
+ export const isRemoteShell = (env: NodeJS.ProcessEnv = process.env): boolean =>
38
+ Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.SSH_TTY)
39
+
40
+ export const isCopyShortcut = (
41
+ key: { ctrl: boolean; meta: boolean; super?: boolean },
42
+ ch: string,
43
+ env: NodeJS.ProcessEnv = process.env
44
+ ): boolean =>
45
+ ch.toLowerCase() === 'c' &&
46
+ (isAction(key, ch, 'c') ||
47
+ (isRemoteShell(env) && (key.meta || key.super === true)) ||
48
+ // VS Code/Cursor/Windsurf terminal setup forwards Cmd+C as a CSI-u
49
+ // sequence with the super bit plus a benign ctrl bit. Accept that shape
50
+ // even though raw Ctrl+C should remain interrupt on local macOS.
51
+ (isMac && key.ctrl && (key.meta || key.super === true)))
52
+
53
+ /**
54
+ * Voice recording toggle key — configurable via ``voice.record_key`` in
55
+ * ``config.yaml`` (default ``ctrl+b``).
56
+ *
57
+ * Documented in tips.py, the Python CLI prompt_toolkit handler, and the
58
+ * config.yaml default. The TUI honours the same config knob (#18994);
59
+ * when ``voice.record_key`` is e.g. ``ctrl+o`` the TUI binds Ctrl+O.
60
+ *
61
+ * Only the documented default (``ctrl+b``) additionally accepts the
62
+ * macOS action modifier (Cmd+B) — custom bindings like ``ctrl+o``
63
+ * require the literal Ctrl bit so Cmd+O can't steal the shortcut.
64
+ */
65
+ export type VoiceRecordKeyMod = 'alt' | 'ctrl' | 'super'
66
+
67
+ /** Named (multi-character) keys we support, matching the CLI's
68
+ * prompt_toolkit binding shape (``c-space``, ``c-enter``, etc.) so a
69
+ * config value like ``ctrl+space`` binds in both runtimes. */
70
+ export type VoiceRecordKeyNamed = 'backspace' | 'delete' | 'enter' | 'escape' | 'space' | 'tab'
71
+
72
+ export interface ParsedVoiceRecordKey {
73
+ /** Single character (``'b'``, ``'o'``) when ``named`` is undefined,
74
+ * otherwise the named-key token (``'space'``, ``'enter'``…). Kept as
75
+ * one field for back-compat with the v1 ``{ ch, mod, raw }`` shape. */
76
+ ch: string
77
+ mod: VoiceRecordKeyMod
78
+ named?: VoiceRecordKeyNamed
79
+ raw: string
80
+ }
81
+
82
+ export const DEFAULT_VOICE_RECORD_KEY: ParsedVoiceRecordKey = {
83
+ ch: 'b',
84
+ mod: 'ctrl',
85
+ raw: 'ctrl+b'
86
+ }
87
+
88
+ /** Modifier aliases.
89
+ *
90
+ * ``meta`` / ``cmd`` / ``command`` are intentionally absent.
91
+ * nastech-ink sets ``key.meta`` for plain Alt/Option on every platform
92
+ * AND for Cmd on some legacy macOS terminals (Terminal.app without
93
+ * kitty-protocol passthrough). Accepting any of those as a literal
94
+ * modifier would produce a display/binding mismatch — a config like
95
+ * ``cmd+b`` would render as ``Cmd+B`` but silently fire on Alt+B, or
96
+ * never fire at all on legacy terminals even though the UI advertises
97
+ * it (Copilot round-6 review on #19835). Users on modern kitty-style
98
+ * terminals (iTerm2 CSI-u, Ghostty, Kitty, WezTerm, Alacritty) spell
99
+ * the platform action modifier ``super`` / ``win``, which match the
100
+ * unambiguous ``key.super`` bit. macOS users on Terminal.app stick
101
+ * with the documented ``ctrl+b``.
102
+ *
103
+ * Cross-runtime parity: the ``ctrl`` / ``control`` / ``alt`` / ``option`` /
104
+ * ``opt`` spellings are normalized identically in the classic CLI
105
+ * (``nastech_cli/voice.py::normalize_voice_record_key_for_prompt_toolkit``)
106
+ * so one ``voice.record_key`` value binds the same shortcut in both
107
+ * runtimes (Copilot round-9 review on #19835). The ``super`` /
108
+ * ``win`` / ``windows`` spellings are TUI-only — prompt_toolkit has no
109
+ * super modifier, so the CLI falls back to the documented default and
110
+ * logs a warning at startup (Copilot round-11 review on #19835). */
111
+ const _MOD_ALIASES: Record<string, VoiceRecordKeyMod> = {
112
+ alt: 'alt',
113
+ control: 'ctrl',
114
+ ctrl: 'ctrl',
115
+ option: 'alt',
116
+ opt: 'alt',
117
+ super: 'super',
118
+ win: 'super',
119
+ windows: 'super'
120
+ }
121
+
122
+ /** Map config-string named tokens to the canonical name used at match time.
123
+ *
124
+ * Aliases mirror what prompt_toolkit accepts (``return`` ↔ ``enter``,
125
+ * ``esc`` ↔ ``escape``) so a config that round-trips through the CLI also
126
+ * binds in the TUI. */
127
+ const _NAMED_KEY_ALIASES: Record<string, VoiceRecordKeyNamed> = {
128
+ backspace: 'backspace',
129
+ bs: 'backspace',
130
+ del: 'delete',
131
+ delete: 'delete',
132
+ enter: 'enter',
133
+ esc: 'escape',
134
+ escape: 'escape',
135
+ ret: 'enter',
136
+ return: 'enter',
137
+ space: 'space',
138
+ spc: 'space',
139
+ tab: 'tab'
140
+ }
141
+
142
+ /** ``useInputHandlers()`` intercepts these unconditionally before the
143
+ * voice check runs, so a binding like ``ctrl+c`` (interrupt),
144
+ * ``ctrl+d`` (quit), or ``ctrl+l`` (clear screen) would be advertised
145
+ * in /voice status but never fire push-to-talk. Reject at parse time
146
+ * so the user gets the documented Ctrl+B instead of a dead shortcut
147
+ * (Copilot round-4 review on #19835).
148
+ *
149
+ * ``ctrl+x`` is intentionally NOT here — it's only claimed during
150
+ * queue-edit (``queueEditIdx !== null``), so the voice binding works
151
+ * for most of the session and matches CLI parity for ``ctrl+<letter>``
152
+ * bindings (Copilot round-8 review on #19835). */
153
+ const _RESERVED_CTRL_CHARS = new Set(['c', 'd', 'l'])
154
+
155
+ /** On macOS the action-modifier intercepts these editor chords via
156
+ * ``isCopyShortcut`` / ``isAction`` in ``useInputHandlers()``:
157
+ * - super+c → copy
158
+ * - super+d → exit
159
+ * - super+l → clear screen
160
+ * - super+v → paste (also claimed at the TextInput layer)
161
+ * On Linux/Windows those globals key off Ctrl instead of Super, so
162
+ * super+<letter> bindings don't collide. Gate the rejection to darwin
163
+ * at parse time so kitty/CSI-u ``super+<key>`` configs still work for
164
+ * non-mac users (Copilot round-8 review on #19835). */
165
+ const _RESERVED_SUPER_CHARS = new Set(['c', 'd', 'l', 'v'])
166
+
167
+ /** On macOS ``isActionMod`` accepts ``key.meta`` as the action
168
+ * modifier — but nastech-ink reports Alt as ``key.meta`` on many
169
+ * terminals. So on darwin a configured ``alt+c`` / ``alt+d`` / ``alt+l``
170
+ * gets swallowed by ``isCopyShortcut`` / ``isAction`` before the voice
171
+ * check runs. Block at parse time so /voice status doesn't advertise
172
+ * a shortcut that actually copies / quits / clears (Copilot round-12
173
+ * review on #19835). */
174
+ const _RESERVED_ALT_CHARS_MAC = new Set(['c', 'd', 'l'])
175
+
176
+ interface RuntimeKeyEvent {
177
+ alt?: boolean
178
+ backspace?: boolean
179
+ ctrl: boolean
180
+ delete?: boolean
181
+ escape?: boolean
182
+ meta: boolean
183
+ return?: boolean
184
+ shift?: boolean
185
+ super?: boolean
186
+ tab?: boolean
187
+ }
188
+
189
+ /** Match an ink ``key`` event against a parsed named key. The ink runtime
190
+ * sets one boolean per named key; ``space`` is a printable char so it
191
+ * arrives as ``ch === ' '`` rather than a dedicated ``key.space`` flag. */
192
+ const _matchesNamedKey = (
193
+ named: VoiceRecordKeyNamed,
194
+ key: RuntimeKeyEvent,
195
+ ch: string
196
+ ): boolean => {
197
+ switch (named) {
198
+ case 'backspace':
199
+ return key.backspace === true
200
+ case 'delete':
201
+ return key.delete === true
202
+ case 'enter':
203
+ return key.return === true
204
+ case 'escape':
205
+ return key.escape === true
206
+ case 'space':
207
+ return ch === ' '
208
+ case 'tab':
209
+ return key.tab === true
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Parse a config-string voice record key like ``ctrl+b`` / ``alt+r`` /
215
+ * ``ctrl+space`` into ``{mod, ch, named?}``. Accepts single characters
216
+ * AND the named tokens declared in ``_NAMED_KEY_ALIASES`` (``space``,
217
+ * ``enter``/``return``, ``tab``, ``escape``/``esc``, ``backspace``,
218
+ * ``delete``) — matching the keys prompt_toolkit accepts on the CLI
219
+ * side via the ``c-<name>`` rewrite in ``cli.py``.
220
+ *
221
+ * Accepts ``unknown`` because the source is raw YAML via
222
+ * ``config.get full`` — a hand-edited ``voice.record_key: 1`` or
223
+ * ``voice.record_key: true`` would otherwise crash ``.trim()`` on a
224
+ * non-string scalar (Copilot round-3 review on #19835). Non-string /
225
+ * empty / unrecognised values fall back to the documented Ctrl+B
226
+ * default so a typo never silently disables the shortcut.
227
+ */
228
+ export const parseVoiceRecordKey = (raw: unknown): ParsedVoiceRecordKey => {
229
+ if (typeof raw !== 'string') {
230
+ return DEFAULT_VOICE_RECORD_KEY
231
+ }
232
+
233
+ const lower = raw.trim().toLowerCase()
234
+
235
+ if (!lower) {
236
+ return DEFAULT_VOICE_RECORD_KEY
237
+ }
238
+
239
+ const parts = lower.split('+').map(p => p.trim()).filter(Boolean)
240
+
241
+ if (!parts.length) {
242
+ return DEFAULT_VOICE_RECORD_KEY
243
+ }
244
+
245
+ const last = parts[parts.length - 1]
246
+ const modCandidates = parts.slice(0, -1)
247
+
248
+ // Reject multi-modifier chords (``ctrl+alt+r``, ``cmd+ctrl+b``) rather
249
+ // than silently dropping the extra modifier — the previous
250
+ // single-token validator made a typo bind a different shortcut than
251
+ // the user configured (Copilot round-3 review on #19835). The classic
252
+ // CLI only supports single-modifier bindings via prompt_toolkit's
253
+ // ``c-x`` / ``a-x`` rewrite in ``cli.py``, so this matches CLI parity.
254
+ if (modCandidates.length > 1) {
255
+ return DEFAULT_VOICE_RECORD_KEY
256
+ }
257
+
258
+ // Require an explicit modifier. A bare ``o`` / ``space`` / ``escape``
259
+ // has no sensible mapping: the CLI's prompt_toolkit binds the raw
260
+ // key (no rewrite) so bare-char configs would silently diverge
261
+ // between the two runtimes (Copilot round-4 review on #19835).
262
+ // Fall back to the documented default.
263
+ if (modCandidates.length === 0) {
264
+ return DEFAULT_VOICE_RECORD_KEY
265
+ }
266
+
267
+ const norm = _MOD_ALIASES[modCandidates[0]]
268
+
269
+ // Unknown modifier token (e.g. bare ``meta+b`` which is ambiguous on
270
+ // the wire) falls back to the documented default rather than
271
+ // silently coercing to Ctrl and producing a misleading bind.
272
+ if (!norm) {
273
+ return DEFAULT_VOICE_RECORD_KEY
274
+ }
275
+
276
+ const mod = norm
277
+
278
+ // Block bindings the TUI input handler intercepts before the voice
279
+ // check — ``ctrl+c`` / ``ctrl+d`` / ``ctrl+l`` would never actually
280
+ // fire push-to-talk, so advertising them in /voice status is a lie.
281
+ if (mod === 'ctrl' && last.length === 1 && _RESERVED_CTRL_CHARS.has(last)) {
282
+ return DEFAULT_VOICE_RECORD_KEY
283
+ }
284
+
285
+ // Same for ``super+c`` / ``super+d`` / ``super+l`` / ``super+v`` on
286
+ // macOS only — those are copy / exit / clear / paste and get claimed
287
+ // by ``isCopyShortcut`` / ``isAction`` / the TextInput paste layer
288
+ // before voice has a chance to toggle. On Linux/Windows the TUI
289
+ // globals key off Ctrl (not Super), so kitty/CSI-u ``super+<letter>``
290
+ // bindings stay usable for non-mac users.
291
+ if (isMac && mod === 'super' && last.length === 1 && _RESERVED_SUPER_CHARS.has(last)) {
292
+ return DEFAULT_VOICE_RECORD_KEY
293
+ }
294
+
295
+ // On macOS nastech-ink reports Alt as ``key.meta``, which ``isActionMod``
296
+ // accepts as the mac action modifier. So ``alt+c`` / ``alt+d`` / ``alt+l``
297
+ // collide with copy / exit / clear in ``useInputHandlers()`` before the
298
+ // voice check. Reject at parse time on darwin only — non-mac ``alt+<letter>``
299
+ // bindings are still usable (Copilot round-12 review on #19835).
300
+ if (isMac && mod === 'alt' && last.length === 1 && _RESERVED_ALT_CHARS_MAC.has(last)) {
301
+ return DEFAULT_VOICE_RECORD_KEY
302
+ }
303
+
304
+ if (last.length === 1) {
305
+ return { ch: last, mod, raw: lower }
306
+ }
307
+
308
+ const named = _NAMED_KEY_ALIASES[last]
309
+
310
+ if (named) {
311
+ return { ch: named, mod, named, raw: lower }
312
+ }
313
+
314
+ // Unknown multi-character token (e.g. typo'd ``ctrl+spcae``) — fall back
315
+ // to the doc default rather than silently disabling the binding.
316
+ return DEFAULT_VOICE_RECORD_KEY
317
+ }
318
+
319
+ /** Render a parsed key back as ``Ctrl+B`` / ``Ctrl+Space`` for status text.
320
+ *
321
+ * Platform-aware for the ``super`` modifier: renders ``Cmd`` on macOS and
322
+ * ``Super`` elsewhere. Previously rendered ``Cmd`` universally, which told
323
+ * Linux/Windows users the wrong modifier to press (Copilot review, round
324
+ * 2 on #19835). */
325
+ export const formatVoiceRecordKey = (parsed: ParsedVoiceRecordKey): string => {
326
+ const modLabel =
327
+ parsed.mod === 'super' ? (isMac ? 'Cmd' : 'Super') : parsed.mod[0].toUpperCase() + parsed.mod.slice(1)
328
+ // Named tokens render in title case (Ctrl+Space, Ctrl+Enter); single
329
+ // chars render upper-case to match the existing Ctrl+B convention.
330
+ const keyLabel = parsed.named
331
+ ? parsed.named[0].toUpperCase() + parsed.named.slice(1)
332
+ : parsed.ch.toUpperCase()
333
+
334
+ return `${modLabel}+${keyLabel}`
335
+ }
336
+
337
+ /** Whether the parsed binding is the documented default (ctrl+b).
338
+ *
339
+ * Compare on the parsed spec rather than ``raw`` so semantically-equal
340
+ * aliases (``control+b``, ``ctrl + b``) still get the macOS Cmd+B
341
+ * muscle-memory fallback (Copilot review, round 2 on #19835). */
342
+ const _isDefaultVoiceKey = (parsed: ParsedVoiceRecordKey): boolean =>
343
+ parsed.mod === DEFAULT_VOICE_RECORD_KEY.mod &&
344
+ parsed.ch === DEFAULT_VOICE_RECORD_KEY.ch &&
345
+ parsed.named === DEFAULT_VOICE_RECORD_KEY.named
346
+
347
+ export const isVoiceToggleKey = (
348
+ key: RuntimeKeyEvent,
349
+ ch: string,
350
+ configured: ParsedVoiceRecordKey = DEFAULT_VOICE_RECORD_KEY
351
+ ): boolean => {
352
+ // Match the configured key first (single-char compare or named-key
353
+ // event-property check). Bail out before evaluating modifier shape
354
+ // so the wrong key never reaches the modifier guard.
355
+ if (configured.named) {
356
+ if (!_matchesNamedKey(configured.named, key, ch)) {
357
+ return false
358
+ }
359
+ } else if (ch.toLowerCase() !== configured.ch) {
360
+ return false
361
+ }
362
+
363
+ // The parser rejects multi-modifier configs (``ctrl+shift+b`` etc.),
364
+ // so at match time Shift must always be clear — otherwise
365
+ // ``ctrl+tab`` would also fire on Ctrl+Shift+Tab and ``alt+enter``
366
+ // on Alt+Shift+Enter, triggering a different chord than configured
367
+ // (Copilot round-5 review on #19835).
368
+ if (key.shift === true) {
369
+ return false
370
+ }
371
+
372
+ switch (configured.mod) {
373
+ case 'alt':
374
+ // Most terminals surface Alt as either ``alt`` or ``meta``; accept
375
+ // both so the binding works across xterm-style and kitty-style
376
+ // protocols. Guard against ctrl/super bits so a chord like
377
+ // Ctrl+Alt+<key> or Cmd+Alt+<key> doesn't spuriously fire the
378
+ // alt binding.
379
+ //
380
+ // Bare Escape on nastech-ink can arrive as ``key.meta=true`` on some
381
+ // terminals, so a configured ``alt+escape`` must not match that shape;
382
+ // require an explicit alt bit for escape chords (Copilot round-7
383
+ // follow-up on #19835).
384
+ return (key.alt === true || (key.meta && key.escape !== true)) && !key.ctrl && key.super !== true
385
+ case 'ctrl':
386
+ // Require the Ctrl bit AND a clear Alt/Super so a chord like
387
+ // Ctrl+Alt+<key> / Ctrl+Cmd+<key> doesn't spuriously match
388
+ // ``ctrl+<key>`` (Copilot round-6 review on #19835).
389
+ //
390
+ // The documented default (``ctrl+b``) additionally accepts the
391
+ // explicit ``key.super`` bit on macOS for Cmd+B muscle memory —
392
+ // but ONLY ``key.super`` (kitty-style), never ``key.meta``, since
393
+ // ``key.meta`` is nastech-ink's Alt signal and accepting it would
394
+ // fire the binding on Alt+B.
395
+ if (key.ctrl) {
396
+ return !key.alt && !key.meta && key.super !== true
397
+ }
398
+
399
+ return _isDefaultVoiceKey(configured) && isMac && key.super === true && !key.alt && !key.meta
400
+ case 'super':
401
+ // Require the explicit ``key.super`` bit (kitty-style protocol)
402
+ // AND clear Ctrl/Alt/Meta so Ctrl+Cmd+X or Alt+Cmd+X don't
403
+ // spuriously fire the super binding (Copilot round-6 review on
404
+ // #19835). Legacy-terminal users whose Cmd arrives as
405
+ // ``key.meta`` need a kitty-protocol terminal — see the
406
+ // _MOD_ALIASES doc-comment for the rationale.
407
+ return key.super === true && !key.ctrl && !key.alt && !key.meta
408
+ }
409
+ }
@@ -0,0 +1,48 @@
1
+ const PRECISION_WHEEL_FRAME_MS = 16
2
+ const PRECISION_WHEEL_STICKY_MS = 80
3
+
4
+ export type PrecisionWheelState = {
5
+ active: boolean
6
+ dir: 0 | -1 | 1
7
+ lastEventAtMs: number
8
+ lastScrollAtMs: number
9
+ }
10
+
11
+ export type PrecisionWheelStep = {
12
+ active: boolean
13
+ entered: boolean
14
+ rows: 0 | 1
15
+ }
16
+
17
+ export function initPrecisionWheel(): PrecisionWheelState {
18
+ return { active: false, dir: 0, lastEventAtMs: 0, lastScrollAtMs: 0 }
19
+ }
20
+
21
+ export function computePrecisionWheelStep(
22
+ state: PrecisionWheelState,
23
+ dir: -1 | 1,
24
+ hasModifier: boolean,
25
+ now: number
26
+ ): PrecisionWheelStep {
27
+ const active = hasModifier || now - state.lastEventAtMs < PRECISION_WHEEL_STICKY_MS
28
+
29
+ if (!active) {
30
+ state.active = false
31
+
32
+ return { active: false, entered: false, rows: 0 }
33
+ }
34
+
35
+ const entered = !state.active
36
+
37
+ state.active = true
38
+ state.lastEventAtMs = now
39
+
40
+ if (dir === state.dir && now - state.lastScrollAtMs < PRECISION_WHEEL_FRAME_MS) {
41
+ return { active: true, entered, rows: 0 }
42
+ }
43
+
44
+ state.dir = dir
45
+ state.lastScrollAtMs = now
46
+
47
+ return { active: true, entered, rows: 1 }
48
+ }
@@ -0,0 +1,35 @@
1
+ const TERMUX_SAFE_PROMPT = '>'
2
+
3
+ export function composerPromptText(
4
+ prompt: string,
5
+ profileName?: null | string,
6
+ shellMode = false,
7
+ termuxMode = false,
8
+ totalCols?: number
9
+ ): string {
10
+ if (shellMode) {
11
+ return '$'
12
+ }
13
+
14
+ if (termuxMode) {
15
+ // Termux fonts/terminal backends can render decorative prompt glyphs with
16
+ // ambiguous width; keep the live composer marker strictly single-cell ASCII
17
+ // so we never leave stale arrow artifacts while typing.
18
+ const basePrompt = TERMUX_SAFE_PROMPT
19
+
20
+ // On very wide panes we can still include profile context. On narrow/mobile
21
+ // panes this burns precious columns and increases wrap/clipping risk.
22
+ const wideEnoughForProfile = typeof totalCols === 'number' ? totalCols >= 90 : false
23
+ if (wideEnoughForProfile && profileName && !['default', 'custom'].includes(profileName)) {
24
+ return `${profileName} ${basePrompt}`
25
+ }
26
+
27
+ return basePrompt
28
+ }
29
+
30
+ if (profileName && !['default', 'custom'].includes(profileName)) {
31
+ return `${profileName} ${prompt}`
32
+ }
33
+
34
+ return prompt
35
+ }
@@ -0,0 +1,55 @@
1
+ const TAGS = ['think', 'reasoning', 'thinking', 'thought', 'REASONING_SCRATCHPAD'] as const
2
+
3
+ export interface SplitReasoning {
4
+ reasoning: string
5
+ text: string
6
+ }
7
+
8
+ export function splitReasoning(input: string): SplitReasoning {
9
+ let text = input
10
+ const reasoning: string[] = []
11
+
12
+ for (const tag of TAGS) {
13
+ const paired = new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>\\s*`, 'gi')
14
+ text = text.replace(paired, (_m, inner: string) => {
15
+ const trimmed = inner.trim()
16
+
17
+ if (trimmed) {
18
+ reasoning.push(trimmed)
19
+ }
20
+
21
+ return ''
22
+ })
23
+
24
+ // Anchor to start-of-input so a literal `<think>` mid-prose (model quoting
25
+ // the word, code blocks containing the tag, etc.) doesn't eat every
26
+ // paragraph after it. Real unclosed reasoning blocks always lead the
27
+ // message — that's how reasoning models stream. See test
28
+ // "does not strip trailing prose after a stray mid-text <think> mention".
29
+ const unclosed = new RegExp(`^\\s*<${tag}>([\\s\\S]*)$`, 'i')
30
+ text = text.replace(unclosed, (_m, inner: string) => {
31
+ const trimmed = inner.trim()
32
+
33
+ if (trimmed) {
34
+ reasoning.push(trimmed)
35
+ }
36
+
37
+ return ''
38
+ })
39
+ }
40
+
41
+ return {
42
+ reasoning: reasoning.join('\n\n').trim(),
43
+ text: text.trim()
44
+ }
45
+ }
46
+
47
+ export const hasReasoningTag = (input: string) => {
48
+ for (const tag of TAGS) {
49
+ if (input.includes(`<${tag}>`)) {
50
+ return true
51
+ }
52
+ }
53
+
54
+ return false
55
+ }
package/src/lib/rpc.ts ADDED
@@ -0,0 +1,41 @@
1
+ import type { CommandDispatchResponse } from '../gatewayTypes.js'
2
+
3
+ export type RpcResult = Record<string, any>
4
+
5
+ export const asRpcResult = <T extends RpcResult = RpcResult>(value: unknown): T | null =>
6
+ !value || typeof value !== 'object' || Array.isArray(value) ? null : (value as T)
7
+
8
+ export const asCommandDispatch = (value: unknown): CommandDispatchResponse | null => {
9
+ const o = asRpcResult(value)
10
+
11
+ if (!o || typeof o.type !== 'string') {
12
+ return null
13
+ }
14
+
15
+ const t = o.type
16
+
17
+ if (t === 'exec' || t === 'plugin') {
18
+ return { type: t, output: typeof o.output === 'string' ? o.output : undefined }
19
+ }
20
+
21
+ if (t === 'alias' && typeof o.target === 'string') {
22
+ return { type: 'alias', target: o.target }
23
+ }
24
+
25
+ if (t === 'skill' && typeof o.name === 'string') {
26
+ return { type: 'skill', name: o.name, message: typeof o.message === 'string' ? o.message : undefined }
27
+ }
28
+
29
+ if (t === 'send' && typeof o.message === 'string') {
30
+ return {
31
+ type: 'send',
32
+ message: o.message,
33
+ notice: typeof o.notice === 'string' ? o.notice : undefined,
34
+ }
35
+ }
36
+
37
+ return null
38
+ }
39
+
40
+ export const rpcErrorMessage = (err: unknown) =>
41
+ err instanceof Error && err.message ? err.message : typeof err === 'string' && err.trim() ? err : 'request failed'