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,355 @@
1
+ import type { SubagentAggregate, SubagentNode, SubagentProgress } from '../types.js'
2
+
3
+ const ROOT_KEY = '__root__'
4
+
5
+ /**
6
+ * Reconstruct the subagent spawn tree from a flat event-ordered list.
7
+ *
8
+ * Grouping is by `parentId`; a missing `parentId` (or one pointing at an
9
+ * unknown subagent) is treated as a top-level spawn of the current turn.
10
+ * Children within a parent are sorted by `depth` then `index` — same key
11
+ * used in `turnController.upsertSubagent`, so render order matches spawn
12
+ * order regardless of network reordering of gateway events.
13
+ *
14
+ * Older gateways omit `parentId`; every subagent is then a top-level node
15
+ * and the tree renders flat — matching pre-observability behaviour.
16
+ */
17
+ export function buildSubagentTree(items: readonly SubagentProgress[]): SubagentNode[] {
18
+ if (!items.length) {
19
+ return []
20
+ }
21
+
22
+ const byParent = new Map<string, SubagentProgress[]>()
23
+ const known = new Set<string>()
24
+
25
+ for (const item of items) {
26
+ known.add(item.id)
27
+ }
28
+
29
+ for (const item of items) {
30
+ const parentKey = item.parentId && known.has(item.parentId) ? item.parentId : ROOT_KEY
31
+ const bucket = byParent.get(parentKey) ?? []
32
+ bucket.push(item)
33
+ byParent.set(parentKey, bucket)
34
+ }
35
+
36
+ for (const bucket of byParent.values()) {
37
+ bucket.sort((a, b) => a.depth - b.depth || a.index - b.index)
38
+ }
39
+
40
+ const build = (item: SubagentProgress): SubagentNode => {
41
+ const kids = byParent.get(item.id) ?? []
42
+ const children = kids.map(build)
43
+
44
+ return { aggregate: aggregate(item, children), children, item }
45
+ }
46
+
47
+ return (byParent.get(ROOT_KEY) ?? []).map(build)
48
+ }
49
+
50
+ /**
51
+ * Roll up counts for a node's whole subtree. Kept pure so the live view
52
+ * and the post-hoc replay can share the same renderer unchanged.
53
+ *
54
+ * `hotness` = tools per second across the subtree — a crude proxy for
55
+ * "how much work is happening in this branch". Used to colour tree rails
56
+ * in the overlay / inline view so the eye spots the expensive branch.
57
+ */
58
+ export function aggregate(item: SubagentProgress, children: readonly SubagentNode[]): SubagentAggregate {
59
+ let totalTools = item.toolCount ?? 0
60
+ let totalDuration = item.durationSeconds ?? 0
61
+ let descendantCount = 0
62
+ let activeCount = isRunning(item) ? 1 : 0
63
+ let maxDepthFromHere = 0
64
+ let inputTokens = item.inputTokens ?? 0
65
+ let outputTokens = item.outputTokens ?? 0
66
+ let costUsd = item.costUsd ?? 0
67
+ let filesTouched = (item.filesRead?.length ?? 0) + (item.filesWritten?.length ?? 0)
68
+
69
+ for (const child of children) {
70
+ totalTools += child.aggregate.totalTools
71
+ totalDuration += child.aggregate.totalDuration
72
+ descendantCount += child.aggregate.descendantCount + 1
73
+ activeCount += child.aggregate.activeCount
74
+ maxDepthFromHere = Math.max(maxDepthFromHere, child.aggregate.maxDepthFromHere + 1)
75
+ inputTokens += child.aggregate.inputTokens
76
+ outputTokens += child.aggregate.outputTokens
77
+ costUsd += child.aggregate.costUsd
78
+ filesTouched += child.aggregate.filesTouched
79
+ }
80
+
81
+ const hotness = totalDuration > 0 ? totalTools / totalDuration : 0
82
+
83
+ return {
84
+ activeCount,
85
+ costUsd,
86
+ descendantCount,
87
+ filesTouched,
88
+ hotness,
89
+ inputTokens,
90
+ maxDepthFromHere,
91
+ outputTokens,
92
+ totalDuration,
93
+ totalTools
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Count of subagents at each depth level, indexed by depth (0 = top level).
99
+ * Drives the inline sparkline (`▁▃▇▅`) and the status-bar HUD.
100
+ */
101
+ export function widthByDepth(tree: readonly SubagentNode[]): number[] {
102
+ const widths: number[] = []
103
+
104
+ const walk = (nodes: readonly SubagentNode[], depth: number) => {
105
+ if (!nodes.length) {
106
+ return
107
+ }
108
+
109
+ widths[depth] = (widths[depth] ?? 0) + nodes.length
110
+
111
+ for (const node of nodes) {
112
+ walk(node.children, depth + 1)
113
+ }
114
+ }
115
+
116
+ walk(tree, 0)
117
+
118
+ return widths
119
+ }
120
+
121
+ /**
122
+ * Flat totals across the full tree — feeds the summary chip header.
123
+ */
124
+ export function treeTotals(tree: readonly SubagentNode[]): SubagentAggregate {
125
+ let totalTools = 0
126
+ let totalDuration = 0
127
+ let descendantCount = 0
128
+ let activeCount = 0
129
+ let maxDepthFromHere = 0
130
+ let inputTokens = 0
131
+ let outputTokens = 0
132
+ let costUsd = 0
133
+ let filesTouched = 0
134
+
135
+ for (const node of tree) {
136
+ totalTools += node.aggregate.totalTools
137
+ totalDuration += node.aggregate.totalDuration
138
+ descendantCount += node.aggregate.descendantCount + 1
139
+ activeCount += node.aggregate.activeCount
140
+ maxDepthFromHere = Math.max(maxDepthFromHere, node.aggregate.maxDepthFromHere + 1)
141
+ inputTokens += node.aggregate.inputTokens
142
+ outputTokens += node.aggregate.outputTokens
143
+ costUsd += node.aggregate.costUsd
144
+ filesTouched += node.aggregate.filesTouched
145
+ }
146
+
147
+ const hotness = totalDuration > 0 ? totalTools / totalDuration : 0
148
+
149
+ return {
150
+ activeCount,
151
+ costUsd,
152
+ descendantCount,
153
+ filesTouched,
154
+ hotness,
155
+ inputTokens,
156
+ maxDepthFromHere,
157
+ outputTokens,
158
+ totalDuration,
159
+ totalTools
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Flatten the tree into visit order — useful for keyboard navigation and
165
+ * for "kill subtree" walks that fire one RPC per descendant.
166
+ */
167
+ export function flattenTree(tree: readonly SubagentNode[]): SubagentNode[] {
168
+ const out: SubagentNode[] = []
169
+
170
+ const walk = (nodes: readonly SubagentNode[]) => {
171
+ for (const node of nodes) {
172
+ out.push(node)
173
+ walk(node.children)
174
+ }
175
+ }
176
+
177
+ walk(tree)
178
+
179
+ return out
180
+ }
181
+
182
+ /**
183
+ * Collect every descendant's id for a given node (excluding the node itself).
184
+ */
185
+ export function descendantIds(node: SubagentNode): string[] {
186
+ const ids: string[] = []
187
+
188
+ const walk = (children: readonly SubagentNode[]) => {
189
+ for (const child of children) {
190
+ ids.push(child.item.id)
191
+ walk(child.children)
192
+ }
193
+ }
194
+
195
+ walk(node.children)
196
+
197
+ return ids
198
+ }
199
+
200
+ export function isRunning(item: Pick<SubagentProgress, 'status'>): boolean {
201
+ return item.status === 'running' || item.status === 'queued'
202
+ }
203
+
204
+ const SPARK_RAMP = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'] as const
205
+
206
+ /**
207
+ * 8-step unicode bar sparkline from a positive-integer array. Zeroes render
208
+ * as spaces so a sparse tree doesn't read as equal activity at every depth.
209
+ */
210
+ export function sparkline(values: readonly number[]): string {
211
+ if (!values.length) {
212
+ return ''
213
+ }
214
+
215
+ const max = Math.max(...values)
216
+
217
+ if (max <= 0) {
218
+ return ' '.repeat(values.length)
219
+ }
220
+
221
+ return values
222
+ .map(v => {
223
+ if (v <= 0) {
224
+ return ' '
225
+ }
226
+
227
+ const idx = Math.min(SPARK_RAMP.length - 1, Math.max(0, Math.ceil((v / max) * (SPARK_RAMP.length - 1))))
228
+
229
+ return SPARK_RAMP[idx]
230
+ })
231
+ .join('')
232
+ }
233
+
234
+ /**
235
+ * Format totals into a compact one-line summary: `d2 · 7 agents · 124 tools · 2m 14s`
236
+ */
237
+ export function formatSummary(totals: SubagentAggregate): string {
238
+ const pieces = [`d${Math.max(0, totals.maxDepthFromHere)}`]
239
+ pieces.push(`${totals.descendantCount} agent${totals.descendantCount === 1 ? '' : 's'}`)
240
+
241
+ if (totals.totalTools > 0) {
242
+ pieces.push(`${totals.totalTools} tool${totals.totalTools === 1 ? '' : 's'}`)
243
+ }
244
+
245
+ if (totals.totalDuration > 0) {
246
+ pieces.push(fmtDuration(totals.totalDuration))
247
+ }
248
+
249
+ const tokens = totals.inputTokens + totals.outputTokens
250
+
251
+ if (tokens > 0) {
252
+ pieces.push(`${fmtTokens(tokens)} tok`)
253
+ }
254
+
255
+ if (totals.costUsd > 0) {
256
+ pieces.push(fmtCost(totals.costUsd))
257
+ }
258
+
259
+ if (totals.activeCount > 0) {
260
+ pieces.push(`⚡${totals.activeCount}`)
261
+ }
262
+
263
+ return pieces.join(' · ')
264
+ }
265
+
266
+ /** Compact dollar amount: `$0.02`, `$1.34`, `$12.4` — never > 5 chars beyond the `$`. */
267
+ export function fmtCost(usd: number): string {
268
+ if (!Number.isFinite(usd) || usd <= 0) {
269
+ return ''
270
+ }
271
+
272
+ if (usd < 0.01) {
273
+ return '<$0.01'
274
+ }
275
+
276
+ if (usd < 10) {
277
+ return `$${usd.toFixed(2)}`
278
+ }
279
+
280
+ return `$${usd.toFixed(1)}`
281
+ }
282
+
283
+ /** Compact token count: `12k`, `1.2k`, `542`. */
284
+ export function fmtTokens(n: number): string {
285
+ if (!Number.isFinite(n) || n <= 0) {
286
+ return '0'
287
+ }
288
+
289
+ if (n < 1000) {
290
+ return String(Math.round(n))
291
+ }
292
+
293
+ if (n < 10_000) {
294
+ return `${(n / 1000).toFixed(1)}k`
295
+ }
296
+
297
+ return `${Math.round(n / 1000)}k`
298
+ }
299
+
300
+ /**
301
+ * `Ns` / `Nm` / `Nm Ss` formatter for seconds. Shared with the agents
302
+ * overlay so the timeline + list + summary all speak the same dialect.
303
+ */
304
+ export function fmtDuration(seconds: number): string {
305
+ if (seconds < 60) {
306
+ return `${Math.max(0, Math.round(seconds))}s`
307
+ }
308
+
309
+ const m = Math.floor(seconds / 60)
310
+ const s = Math.round(seconds - m * 60)
311
+
312
+ return s === 0 ? `${m}m` : `${m}m ${s}s`
313
+ }
314
+
315
+ /**
316
+ * A subagent is top-level if it has no `parentId`, or its parent isn't in
317
+ * the same snapshot (orphaned by a pruned mid-flight root). Same rule
318
+ * `buildSubagentTree` uses — keep call sites consistent across the live
319
+ * view, disk label, and diff pane.
320
+ */
321
+ export function topLevelSubagents(items: readonly SubagentProgress[]): SubagentProgress[] {
322
+ const ids = new Set(items.map(s => s.id))
323
+
324
+ return items.filter(s => !s.parentId || !ids.has(s.parentId))
325
+ }
326
+
327
+ /**
328
+ * Normalize a node's hotness into a palette index 0..N-1 where N = buckets.
329
+ * Higher hotness = "hotter" colour. Normalized against the tree's peak hotness
330
+ * so a uniformly slow tree still shows gradient across its busiest branches.
331
+ */
332
+ export function hotnessBucket(hotness: number, peakHotness: number, buckets: number): number {
333
+ if (!Number.isFinite(hotness) || hotness <= 0 || peakHotness <= 0 || buckets <= 1) {
334
+ return 0
335
+ }
336
+
337
+ const ratio = Math.min(1, hotness / peakHotness)
338
+
339
+ return Math.min(buckets - 1, Math.max(0, Math.round(ratio * (buckets - 1))))
340
+ }
341
+
342
+ export function peakHotness(tree: readonly SubagentNode[]): number {
343
+ let peak = 0
344
+
345
+ const walk = (nodes: readonly SubagentNode[]) => {
346
+ for (const node of nodes) {
347
+ peak = Math.max(peak, node.aggregate.hotness)
348
+ walk(node.children)
349
+ }
350
+ }
351
+
352
+ walk(tree)
353
+
354
+ return peak
355
+ }
@@ -0,0 +1,117 @@
1
+ import type { Theme } from '../theme.js'
2
+
3
+ export type Token = [string, string]
4
+
5
+ interface LangSpec {
6
+ comment: null | string
7
+ keywords: Set<string>
8
+ }
9
+
10
+ const KW = (s: string) => new Set(s.split(/\s+/).filter(Boolean))
11
+
12
+ const TS = KW(`
13
+ abstract as async await break case catch class const continue debugger default delete do else enum export extends
14
+ false finally for from function get if implements import in instanceof interface is let new null of package private
15
+ protected public readonly return set static super switch this throw true try type typeof undefined var void while
16
+ with yield
17
+ `)
18
+
19
+ const PY = KW(`
20
+ False None True and as assert async await break class continue def del elif else except finally for from global if
21
+ import in is lambda nonlocal not or pass raise return try while with yield
22
+ `)
23
+
24
+ const SH = KW(`
25
+ if then else elif fi for in do done while until case esac function return break continue local export readonly
26
+ declare typeset
27
+ `)
28
+
29
+ const GO = KW(`
30
+ break case chan const continue default defer else fallthrough for func go goto if import interface map package range
31
+ return select struct switch type var nil true false
32
+ `)
33
+
34
+ const RUST = KW(`
35
+ as async await break const continue crate dyn else enum extern false fn for if impl in let loop match mod move mut
36
+ pub ref return self Self static struct super trait true type unsafe use where while yield
37
+ `)
38
+
39
+ const SQL = KW(`
40
+ select from where and or not in is null as by group order limit offset insert into values update set delete create
41
+ table drop alter add column primary key foreign references join left right inner outer on
42
+ `)
43
+
44
+ const LANGS: Record<string, LangSpec> = {
45
+ go: { comment: '//', keywords: GO },
46
+ json: { comment: null, keywords: KW('true false null') },
47
+ py: { comment: '#', keywords: PY },
48
+ rust: { comment: '//', keywords: RUST },
49
+ sh: { comment: '#', keywords: SH },
50
+ sql: { comment: '--', keywords: SQL },
51
+ ts: { comment: '//', keywords: TS },
52
+ yaml: { comment: '#', keywords: KW('true false null yes no on off') }
53
+ }
54
+
55
+ const ALIAS: Record<string, string> = {
56
+ bash: 'sh',
57
+ javascript: 'ts',
58
+ js: 'ts',
59
+ jsx: 'ts',
60
+ python: 'py',
61
+ rs: 'rust',
62
+ shell: 'sh',
63
+ tsx: 'ts',
64
+ typescript: 'ts',
65
+ yml: 'yaml',
66
+ zsh: 'sh'
67
+ }
68
+
69
+ const resolve = (lang: string): LangSpec | null => LANGS[ALIAS[lang] ?? lang] ?? null
70
+
71
+ export const isHighlightable = (lang: string): boolean => resolve(lang) !== null
72
+
73
+ const TOKEN_RE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`|\b\d+(?:\.\d+)?\b|[A-Za-z_$][\w$]*/g
74
+
75
+ export function highlightLine(line: string, lang: string, t: Theme): Token[] {
76
+ const spec = resolve(lang)
77
+
78
+ if (!spec) {
79
+ return [['', line]]
80
+ }
81
+
82
+ if (spec.comment && line.trimStart().startsWith(spec.comment)) {
83
+ return [[t.color.muted, line]]
84
+ }
85
+
86
+ const tokens: Token[] = []
87
+ let last = 0
88
+
89
+ for (const m of line.matchAll(TOKEN_RE)) {
90
+ const start = m.index ?? 0
91
+
92
+ if (start > last) {
93
+ tokens.push(['', line.slice(last, start)])
94
+ }
95
+
96
+ const tok = m[0]
97
+ const ch = tok[0]!
98
+
99
+ if (ch === '"' || ch === "'" || ch === '`') {
100
+ tokens.push([t.color.accent, tok])
101
+ } else if (ch >= '0' && ch <= '9') {
102
+ tokens.push([t.color.text, tok])
103
+ } else if (spec.keywords.has(tok)) {
104
+ tokens.push([t.color.border, tok])
105
+ } else {
106
+ tokens.push(['', tok])
107
+ }
108
+
109
+ last = start + tok.length
110
+ }
111
+
112
+ if (last < line.length) {
113
+ tokens.push(['', line.slice(last)])
114
+ }
115
+
116
+ return tokens
117
+ }
@@ -0,0 +1,51 @@
1
+ import { writeSync } from 'node:fs'
2
+
3
+ export const TERMINAL_MODE_RESET =
4
+ '\x1b[0\'z' + // DEC locator reporting
5
+ '\x1b[0\'{' + // selectable locator events
6
+ '\x1b[?2029l' + // passive mouse
7
+ '\x1b[?1016l' + // SGR-pixels mouse
8
+ '\x1b[?1015l' + // urxvt decimal mouse
9
+ '\x1b[?1006l' + // SGR mouse
10
+ '\x1b[?1005l' + // UTF-8 extended mouse
11
+ '\x1b[?1003l' + // any-motion mouse
12
+ '\x1b[?1002l' + // button-motion mouse
13
+ '\x1b[?1001l' + // highlight mouse
14
+ '\x1b[?1000l' + // click mouse
15
+ '\x1b[?9l' + // X10 mouse
16
+ '\x1b[?1004l' + // focus events
17
+ '\x1b[?2004l' + // bracketed paste
18
+ '\x1b[?1049l' + // alternate screen
19
+ '\x1b[<u' + // kitty keyboard
20
+ '\x1b[>4m' + // modifyOtherKeys
21
+ '\x1b[0m' + // attributes
22
+ '\x1b[?25h' // cursor visible
23
+
24
+ type ResettableStream = Pick<NodeJS.WriteStream, 'isTTY' | 'write'> & {
25
+ fd?: number
26
+ }
27
+
28
+ export function resetTerminalModes(stream: ResettableStream = process.stdout): boolean {
29
+ if (!stream.isTTY) {
30
+ return false
31
+ }
32
+
33
+ const fd = typeof stream.fd === 'number' ? stream.fd : stream === process.stdout ? 1 : undefined
34
+ if (fd !== undefined) {
35
+ try {
36
+ writeSync(fd, TERMINAL_MODE_RESET)
37
+
38
+ return true
39
+ } catch {
40
+ // Fall through to stream.write for mocked or unusual TTY streams.
41
+ }
42
+ }
43
+
44
+ try {
45
+ stream.write(TERMINAL_MODE_RESET)
46
+
47
+ return true
48
+ } catch {
49
+ return false
50
+ }
51
+ }
@@ -0,0 +1,78 @@
1
+ import {
2
+ detectVSCodeLikeTerminal,
3
+ type FileOps,
4
+ isRemoteShellSession,
5
+ shouldPromptForTerminalSetup
6
+ } from './terminalSetup.js'
7
+
8
+ export type MacTerminalHint = {
9
+ key: string
10
+ message: string
11
+ tone: 'info' | 'warn'
12
+ }
13
+
14
+ export type MacTerminalContext = {
15
+ isAppleTerminal: boolean
16
+ isRemote: boolean
17
+ isTmux: boolean
18
+ vscodeLike: null | 'cursor' | 'vscode' | 'windsurf'
19
+ }
20
+
21
+ export function detectMacTerminalContext(env: NodeJS.ProcessEnv = process.env): MacTerminalContext {
22
+ const termProgram = env['TERM_PROGRAM'] ?? ''
23
+
24
+ return {
25
+ isAppleTerminal: termProgram === 'Apple_Terminal' || !!env['TERM_SESSION_ID'],
26
+ isRemote: isRemoteShellSession(env),
27
+ isTmux: !!env['TMUX'],
28
+ vscodeLike: detectVSCodeLikeTerminal(env)
29
+ }
30
+ }
31
+
32
+ export async function terminalParityHints(
33
+ env: NodeJS.ProcessEnv = process.env,
34
+ options?: { fileOps?: Partial<FileOps>; homeDir?: string }
35
+ ): Promise<MacTerminalHint[]> {
36
+ const ctx = detectMacTerminalContext(env)
37
+ const hints: MacTerminalHint[] = []
38
+
39
+ if (
40
+ ctx.vscodeLike &&
41
+ (await shouldPromptForTerminalSetup({ env, fileOps: options?.fileOps, homeDir: options?.homeDir }))
42
+ ) {
43
+ hints.push({
44
+ key: 'ide-setup',
45
+ tone: 'info',
46
+ message: `Detected ${ctx.vscodeLike} terminal · run /terminal-setup for best Cmd+Enter / undo parity`
47
+ })
48
+ }
49
+
50
+ if (ctx.isAppleTerminal) {
51
+ hints.push({
52
+ key: 'apple-terminal',
53
+ tone: 'warn',
54
+ message:
55
+ 'Apple Terminal detected · use /paste for image-only clipboard fallback, and try Ctrl+A / Ctrl+E / Ctrl+U if Cmd+←/→/⌫ gets rewritten'
56
+ })
57
+ }
58
+
59
+ if (ctx.isTmux) {
60
+ hints.push({
61
+ key: 'tmux',
62
+ tone: 'warn',
63
+ message:
64
+ 'tmux detected · clipboard copy/paste uses passthrough when available; allow-passthrough improves OSC52 reliability'
65
+ })
66
+ }
67
+
68
+ if (ctx.isRemote) {
69
+ hints.push({
70
+ key: 'remote',
71
+ tone: 'warn',
72
+ message:
73
+ 'SSH session detected · text clipboard can bridge via OSC52, but image clipboard and local screenshot paths still depend on the machine running NasTech'
74
+ })
75
+ }
76
+
77
+ return hints
78
+ }