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,466 @@
1
+ import { Box, Text, useStdout } from '@nastechai/ink'
2
+ import { useEffect, useState } from 'react'
3
+ import unicodeSpinners from 'unicode-animations'
4
+
5
+ import { artWidth, caduceus, CADUCEUS_WIDTH, logo, LOGO_WIDTH } from '../banner.js'
6
+ import { flat } from '../lib/text.js'
7
+ import type { Theme } from '../theme.js'
8
+ import type { PanelSection, SessionInfo } from '../types.js'
9
+
10
+ const LOADER_TICK_MS = 120
11
+
12
+ function InlineLoader({ label, t }: { label: string; t: Theme }) {
13
+ const [tick, setTick] = useState(0)
14
+ const spinner = unicodeSpinners.braille
15
+ const frame = spinner.frames[tick % spinner.frames.length] ?? '⠋'
16
+
17
+ useEffect(() => {
18
+ const id = setInterval(() => setTick(n => n + 1), Math.max(LOADER_TICK_MS, spinner.interval))
19
+
20
+ return () => clearInterval(id)
21
+ }, [spinner.interval])
22
+
23
+ return (
24
+ <Text color={t.color.muted} wrap="truncate">
25
+ <Text color={t.color.accent}>{frame}</Text> {label}
26
+ </Text>
27
+ )
28
+ }
29
+
30
+ export function ArtLines({ lines }: { lines: [string, string][] }) {
31
+ return (
32
+ <Box flexDirection="column" height={lines.length} opaque width={artWidth(lines)}>
33
+ {lines.map(([c, text], i) => (
34
+ <Text color={c} key={i} wrap="truncate-end">
35
+ {text}
36
+ </Text>
37
+ ))}
38
+ </Box>
39
+ )
40
+ }
41
+
42
+ // Responsive Banner: full art → compact rule → text → hidden.
43
+ //
44
+ // Terminals can't scale glyphs, so "responsive" means picking a layout that
45
+ // fits the available columns. Thresholds are picked so each tier reads
46
+ // comfortably without forcing wrap or truncation drift on box-drawing edges.
47
+ const TAG_FULL = 'NasTech · Messenger of the Digital Gods'
48
+ const TAG_MID = 'Messenger of the Digital Gods'
49
+ const TAG_TINY = 'NasTech'
50
+ const HIDE_BELOW = 34
51
+ const COMPACT_FROM = 58
52
+
53
+ const clip = (s: string, w: number) =>
54
+ w <= 0 ? '' : s.length > w ? `${s.slice(0, Math.max(0, w - 1))}…` : s
55
+
56
+ const centerIn = (s: string, w: number) => {
57
+ const f = clip(s, w)
58
+ const slack = Math.max(0, w - f.length)
59
+ const left = slack >> 1
60
+
61
+ return `${' '.repeat(left)}${f}${' '.repeat(slack - left)}`
62
+ }
63
+
64
+ const ruleIn = (label: string, w: number) => {
65
+ const f = clip(label, Math.max(1, w - 4))
66
+ const slack = Math.max(0, w - f.length - 2)
67
+ const left = slack >> 1
68
+
69
+ return `${'─'.repeat(left)} ${f} ${'─'.repeat(slack - left)}`
70
+ }
71
+
72
+ function CompactBanner({ cols, t }: { cols: number; t: Theme }) {
73
+ // -4 keeps a margin so exact-edge rows don't trip terminal pending-wrap.
74
+ const w = Math.max(28, cols - 4)
75
+
76
+ return (
77
+ <Box flexDirection="column" height={3} marginBottom={1} opaque width={w}>
78
+ <Text bold color={t.color.primary}>{ruleIn(t.brand.name, w)}</Text>
79
+ <Text color={t.color.muted}>{centerIn(TAG_FULL, w)}</Text>
80
+ <Text color={t.color.primary}>{'─'.repeat(w)}</Text>
81
+ </Box>
82
+ )
83
+ }
84
+
85
+ export function Banner({ maxWidth, t }: { maxWidth?: number; t: Theme }) {
86
+ const term = useStdout().stdout?.columns ?? 80
87
+ const cols = Math.max(1, Math.min(term, maxWidth ?? term))
88
+
89
+ if (cols < HIDE_BELOW) {
90
+ return null
91
+ }
92
+
93
+ const logoLines = logo(t.color, t.bannerLogo || undefined)
94
+ const logoW = t.bannerLogo ? artWidth(logoLines) : LOGO_WIDTH
95
+
96
+ if (cols >= logoW + 2) {
97
+ return (
98
+ <Box flexDirection="column" marginBottom={1}>
99
+ <ArtLines lines={logoLines} />
100
+ <Text color={t.color.muted} wrap="truncate-end">
101
+ {t.brand.icon} {TAG_FULL}
102
+ </Text>
103
+ </Box>
104
+ )
105
+ }
106
+
107
+ if (cols >= COMPACT_FROM) {
108
+ return <CompactBanner cols={cols} t={t} />
109
+ }
110
+
111
+ const name = cols >= 52 ? t.brand.name : (t.brand.name.split(' ')[0] ?? t.brand.name)
112
+ const tag = cols >= 64 ? TAG_FULL : cols >= 46 ? TAG_MID : TAG_TINY
113
+
114
+ return (
115
+ <Box flexDirection="column" marginBottom={1}>
116
+ <Text bold color={t.color.primary} wrap="truncate-end">{t.brand.icon} {name}</Text>
117
+ <Text color={t.color.muted} wrap="truncate-end">{t.brand.icon} {tag}</Text>
118
+ </Box>
119
+ )
120
+ }
121
+
122
+ // ── Collapsible helpers ──────────────────────────────────────────────
123
+
124
+ function CollapseToggle({
125
+ count,
126
+ open,
127
+ suffix,
128
+ t,
129
+ title,
130
+ onToggle
131
+ }: {
132
+ count?: number
133
+ open: boolean
134
+ suffix?: string
135
+ t: Theme
136
+ title: string
137
+ onToggle: () => void
138
+ }) {
139
+ return (
140
+ <Box onClick={onToggle}>
141
+ <Text color={t.color.accent}>{open ? '▾ ' : '▸ '}</Text>
142
+ <Text bold color={t.color.accent}>
143
+ {title}
144
+ </Text>
145
+ {typeof count === 'number' ? (
146
+ <Text color={t.color.muted}> ({count})</Text>
147
+ ) : null}
148
+ {suffix ? (
149
+ <Text color={t.color.muted}> {suffix}</Text>
150
+ ) : null}
151
+ </Box>
152
+ )
153
+ }
154
+
155
+ // ── SessionPanel ─────────────────────────────────────────────────────
156
+
157
+ const SKILLS_MAX = 8
158
+ const TOOLSETS_MAX = 8
159
+
160
+ export function SessionPanel({ info, maxWidth, sid, t }: SessionPanelProps) {
161
+ const term = useStdout().stdout?.columns ?? 100
162
+ const cols = Math.max(20, Math.min(term, maxWidth ?? term))
163
+ const heroLines = caduceus(t.color, t.bannerHero || undefined)
164
+ const leftW = Math.min((artWidth(heroLines) || CADUCEUS_WIDTH) + 4, Math.floor(cols * 0.4))
165
+ const wide = cols >= 90 && leftW + 40 < cols
166
+ const w = Math.max(20, wide ? cols - leftW - 14 : cols - 12)
167
+ const lineBudget = Math.max(12, w - 2)
168
+ const strip = (s: string) => (s.endsWith('_tools') ? s.slice(0, -6) : s)
169
+
170
+ // ── Local collapse state for each section ──
171
+ const [toolsOpen, setToolsOpen] = useState(true)
172
+ const [skillsOpen, setSkillsOpen] = useState(false)
173
+ const [systemOpen, setSystemOpen] = useState(false)
174
+ const [mcpOpen, setMcpOpen] = useState(false)
175
+
176
+ const truncLine = (pfx: string, items: string[]) => {
177
+ let line = ''
178
+ let shown = 0
179
+
180
+ for (const item of [...items].sort()) {
181
+ const next = line ? `${line}, ${item}` : item
182
+
183
+ if (pfx.length + next.length > lineBudget) {
184
+ return line ? `${line}, …+${items.length - shown}` : `${item}, …`
185
+ }
186
+
187
+ line = next
188
+ shown++
189
+ }
190
+
191
+ return line
192
+ }
193
+
194
+ // ── Collapsible skills section ──
195
+ const skillEntries = Object.entries(info.skills).sort()
196
+ const skillsTotal = flat(info.skills).length
197
+ const skillsCatCount = skillEntries.length
198
+
199
+ const skillsBody = () => {
200
+ if (info.lazy && skillEntries.length === 0) {
201
+ return <InlineLoader label="scanning skills" t={t} />
202
+ }
203
+
204
+ const shown = skillEntries.slice(0, SKILLS_MAX)
205
+ const overflow = skillEntries.length - SKILLS_MAX
206
+
207
+ return (
208
+ <>
209
+ {shown.map(([k, vs]) => (
210
+ <Text key={k} wrap="truncate">
211
+ <Text color={t.color.muted}>{strip(k)}: </Text>
212
+ <Text color={t.color.text}>{truncLine(strip(k) + ': ', vs)}</Text>
213
+ </Text>
214
+ ))}
215
+ {overflow > 0 && (
216
+ <Text color={t.color.muted}>(and {overflow} more categories…)</Text>
217
+ )}
218
+ </>
219
+ )
220
+ }
221
+
222
+ // ── Collapsible tools section ──
223
+ const toolEntries = Object.entries(info.tools).sort()
224
+ const toolsTotal = flat(info.tools).length
225
+
226
+ const toolsBody = () => {
227
+ const shown = toolEntries.slice(0, TOOLSETS_MAX)
228
+ const overflow = toolEntries.length - TOOLSETS_MAX
229
+
230
+ return (
231
+ <>
232
+ {shown.map(([k, vs]) => (
233
+ <Text key={k} wrap="truncate">
234
+ <Text color={t.color.muted}>{strip(k)}: </Text>
235
+ <Text color={t.color.text}>{truncLine(strip(k) + ': ', vs)}</Text>
236
+ </Text>
237
+ ))}
238
+ {overflow > 0 && (
239
+ <Text color={t.color.muted}>(and {overflow} more toolsets…)</Text>
240
+ )}
241
+ </>
242
+ )
243
+ }
244
+
245
+ // ── Collapsible MCP section ──
246
+ const mcpBody = () => (
247
+ <>
248
+ {(info.mcp_servers ?? []).map(s => (
249
+ <Text key={s.name} wrap="truncate">
250
+ <Text color={t.color.muted}>{` ${s.name} `}</Text>
251
+ <Text color={t.color.muted}>{`[${s.transport}]`}</Text>
252
+ <Text color={t.color.muted}>: </Text>
253
+ {s.connected ? (
254
+ <Text color={t.color.text}>
255
+ {s.tools} tool{s.tools === 1 ? '' : 's'}
256
+ </Text>
257
+ ) : (
258
+ <Text color={t.color.error}>failed</Text>
259
+ )}
260
+ </Text>
261
+ ))}
262
+ </>
263
+ )
264
+
265
+ // ── System prompt body ──
266
+ const sysPromptLen = (info.system_prompt ?? '').length
267
+
268
+ const systemBody = () => {
269
+ if (sysPromptLen === 0) {
270
+ return <Text color={t.color.muted}>No system prompt loaded.</Text>
271
+ }
272
+
273
+ return (
274
+ <Text color={t.color.muted}>
275
+ {info.system_prompt}
276
+ </Text>
277
+ )
278
+ }
279
+
280
+ return (
281
+ <Box borderColor={t.color.border} borderStyle="round" marginBottom={1} paddingX={2} paddingY={1}>
282
+ {wide && (
283
+ <Box flexDirection="column" marginRight={2} width={leftW}>
284
+ <ArtLines lines={heroLines} />
285
+ <Text />
286
+
287
+ <Text color={t.color.accent}>
288
+ {info.model.split('/').pop()}
289
+ <Text color={t.color.muted}> · NasTech</Text>
290
+ </Text>
291
+
292
+ <Text color={t.color.muted} wrap="truncate-end">
293
+ {info.cwd || process.cwd()}
294
+ </Text>
295
+
296
+ {sid && (
297
+ <Text>
298
+ <Text color={t.color.sessionLabel}>Session: </Text>
299
+ <Text color={t.color.sessionBorder}>{sid}</Text>
300
+ </Text>
301
+ )}
302
+ </Box>
303
+ )}
304
+
305
+ <Box flexDirection="column" width={w}>
306
+ {wide ? (
307
+ <Box justifyContent="center" marginBottom={1}>
308
+ <Text bold color={t.color.primary}>
309
+ {t.brand.name}
310
+ {info.version ? ` v${info.version}` : ''}
311
+ {info.release_date ? ` (${info.release_date})` : ''}
312
+ </Text>
313
+ </Box>
314
+ ) : (
315
+ // Narrow layout hides the hero column; surface model/cwd/session
316
+ // here so they aren't lost.
317
+ <Box flexDirection="column" marginBottom={1}>
318
+ <Text color={t.color.accent} wrap="truncate-end">
319
+ {info.model.split('/').pop()}
320
+ <Text color={t.color.muted}> · NasTech</Text>
321
+ </Text>
322
+ <Text color={t.color.muted} wrap="truncate-end">
323
+ {info.cwd || process.cwd()}
324
+ </Text>
325
+ {sid && (
326
+ <Text wrap="truncate-end">
327
+ <Text color={t.color.sessionLabel}>Session: </Text>
328
+ <Text color={t.color.sessionBorder}>{sid}</Text>
329
+ </Text>
330
+ )}
331
+ </Box>
332
+ )}
333
+
334
+ {/* ── Tools (expanded by default) ── */}
335
+ <Box flexDirection="column" marginTop={1}>
336
+ <CollapseToggle
337
+ onToggle={() => setToolsOpen(v => !v)}
338
+ open={toolsOpen}
339
+ t={t}
340
+ title="Available Tools"
341
+ />
342
+ {toolsOpen && toolsBody()}
343
+ </Box>
344
+
345
+ {/* ── Skills (collapsed by default) ── */}
346
+ <Box flexDirection="column" marginTop={1}>
347
+ <CollapseToggle
348
+ count={skillsTotal}
349
+ onToggle={() => setSkillsOpen(v => !v)}
350
+ open={skillsOpen}
351
+ suffix={skillsCatCount > 0 ? `in ${skillsCatCount} categor${skillsCatCount === 1 ? 'y' : 'ies'}` : undefined}
352
+ t={t}
353
+ title="Available Skills"
354
+ />
355
+ {skillsOpen && skillsBody()}
356
+ </Box>
357
+
358
+ {/* ── System Prompt (collapsed by default) ── */}
359
+ {sysPromptLen > 0 && (
360
+ <Box flexDirection="column" marginTop={1}>
361
+ <CollapseToggle
362
+ onToggle={() => setSystemOpen(v => !v)}
363
+ open={systemOpen}
364
+ suffix={`— ${sysPromptLen.toLocaleString()} chars`}
365
+ t={t}
366
+ title="System Prompt"
367
+ />
368
+ {systemOpen && systemBody()}
369
+ </Box>
370
+ )}
371
+
372
+ {/* ── MCP Servers (collapsed by default) ── */}
373
+ {info.mcp_servers && info.mcp_servers.length > 0 && (
374
+ <Box flexDirection="column" marginTop={1}>
375
+ <CollapseToggle
376
+ count={info.mcp_servers.length}
377
+ onToggle={() => setMcpOpen(v => !v)}
378
+ open={mcpOpen}
379
+ suffix="connected"
380
+ t={t}
381
+ title="MCP Servers"
382
+ />
383
+ {mcpOpen && mcpBody()}
384
+ </Box>
385
+ )}
386
+
387
+ <Text />
388
+
389
+ <Text color={t.color.text}>
390
+ {toolsTotal} tools{' · '}
391
+ {skillsTotal} skills
392
+ {info.mcp_servers?.length ? ` · ${info.mcp_servers.length} MCP` : ''}
393
+ {' · '}
394
+ <Text color={t.color.muted}>/help for commands</Text>
395
+ </Text>
396
+
397
+ {typeof info.update_behind === 'number' && info.update_behind > 0 && (
398
+ <Text bold color={t.color.warn}>
399
+ ! {info.update_behind} {info.update_behind === 1 ? 'commit' : 'commits'} behind
400
+ <Text bold={false} color={t.color.warn} dimColor>
401
+ {' '}
402
+ - run{' '}
403
+ </Text>
404
+ <Text bold color={t.color.warn}>
405
+ {info.update_command || 'nastech update'}
406
+ </Text>
407
+ <Text bold={false} color={t.color.warn} dimColor>
408
+ {' '}
409
+ to update
410
+ </Text>
411
+ </Text>
412
+ )}
413
+ </Box>
414
+ </Box>
415
+ )
416
+ }
417
+
418
+ export function Panel({ sections, t, title }: PanelProps) {
419
+ return (
420
+ <Box borderColor={t.color.border} borderStyle="round" flexDirection="column" paddingX={2} paddingY={1}>
421
+ <Box justifyContent="center" marginBottom={1}>
422
+ <Text bold color={t.color.primary}>
423
+ {title}
424
+ </Text>
425
+ </Box>
426
+
427
+ {sections.map((sec, si) => (
428
+ <Box flexDirection="column" key={si} marginTop={si > 0 ? 1 : 0}>
429
+ {sec.title && (
430
+ <Text bold color={t.color.accent}>
431
+ {sec.title}
432
+ </Text>
433
+ )}
434
+
435
+ {sec.rows?.map(([k, v], ri) => (
436
+ <Text key={ri} wrap="truncate">
437
+ <Text color={t.color.muted}>{k.padEnd(20)}</Text>
438
+ <Text color={t.color.text}>{v}</Text>
439
+ </Text>
440
+ ))}
441
+
442
+ {sec.items?.map((item, ii) => (
443
+ <Text color={t.color.text} key={ii} wrap="truncate">
444
+ {item}
445
+ </Text>
446
+ ))}
447
+
448
+ {sec.text && <Text color={t.color.muted}>{sec.text}</Text>}
449
+ </Box>
450
+ ))}
451
+ </Box>
452
+ )
453
+ }
454
+
455
+ interface PanelProps {
456
+ sections: PanelSection[]
457
+ t: Theme
458
+ title: string
459
+ }
460
+
461
+ interface SessionPanelProps {
462
+ info: SessionInfo
463
+ maxWidth?: number
464
+ sid?: string | null
465
+ t: Theme
466
+ }
@@ -0,0 +1,30 @@
1
+ // FPS counter overlay (NASTECH_TUI_FPS=1). Zero-cost when disabled.
2
+
3
+ import { Text } from '@nastechai/ink'
4
+ import { useStore } from '@nanostores/react'
5
+
6
+ import { SHOW_FPS } from '../config/env.js'
7
+ import { $fpsState } from '../lib/fpsStore.js'
8
+ import type { Theme } from '../theme.js'
9
+
10
+ const fpsColor = (fps: number, t: Theme) =>
11
+ fps >= 50 ? t.color.statusGood : fps >= 30 ? t.color.statusWarn : t.color.error
12
+
13
+ export function FpsOverlay({ t }: { t: Theme }) {
14
+ if (!SHOW_FPS) {
15
+ return null
16
+ }
17
+
18
+ return <FpsOverlayInner t={t} />
19
+ }
20
+
21
+ function FpsOverlayInner({ t }: { t: Theme }) {
22
+ const { fps, lastDurationMs, totalFrames } = useStore($fpsState)
23
+
24
+ // Zero-pad widths so digit churn doesn't jitter the corner.
25
+ return (
26
+ <Text color={fpsColor(fps, t)}>
27
+ {fps.toFixed(1).padStart(5)}fps · {lastDurationMs.toFixed(1).padStart(5)}ms · #{totalFrames}
28
+ </Text>
29
+ )
30
+ }
@@ -0,0 +1,73 @@
1
+ import { Box, Text } from '@nastechai/ink'
2
+
3
+ import { HOTKEYS } from '../content/hotkeys.js'
4
+ import type { Theme } from '../theme.js'
5
+
6
+ const COMMON_COMMANDS: [string, string][] = [
7
+ ['/help', 'full list of commands + hotkeys'],
8
+ ['/clear', 'start a new session'],
9
+ ['/resume', 'resume a prior session'],
10
+ ['/details', 'control transcript detail level'],
11
+ ['/copy', 'copy selection or last assistant message'],
12
+ ['/quit', 'exit nastech']
13
+ ]
14
+
15
+ const HOTKEY_PREVIEW = HOTKEYS.slice(0, 8)
16
+
17
+ export function HelpHint({ t }: { t: Theme }) {
18
+ const labelW = Math.max(
19
+ ...COMMON_COMMANDS.map(([k]) => k.length),
20
+ ...HOTKEY_PREVIEW.map(([k]) => k.length)
21
+ )
22
+
23
+ const pad = (s: string) => s + ' '.repeat(Math.max(0, labelW - s.length + 2))
24
+
25
+ return (
26
+ <Box alignItems="flex-start" bottom="100%" flexDirection="column" left={0} position="absolute" right={0}>
27
+ <Box
28
+ alignSelf="flex-start"
29
+ borderColor={t.color.primary}
30
+ borderStyle="round"
31
+ flexDirection="column"
32
+ marginBottom={1}
33
+ opaque
34
+ paddingX={1}
35
+ >
36
+ <Text>
37
+ <Text bold color={t.color.primary}>
38
+ ? quick help
39
+ </Text>
40
+ <Text color={t.color.muted}>
41
+ {' · type /help for the full panel · backspace to dismiss'}
42
+ </Text>
43
+ </Text>
44
+
45
+ <Box marginTop={1}>
46
+ <Text bold color={t.color.accent}>
47
+ Common commands
48
+ </Text>
49
+ </Box>
50
+
51
+ {COMMON_COMMANDS.map(([k, v]) => (
52
+ <Text key={k}>
53
+ <Text color={t.color.label}>{pad(k)}</Text>
54
+ <Text color={t.color.muted}>{v}</Text>
55
+ </Text>
56
+ ))}
57
+
58
+ <Box marginTop={1}>
59
+ <Text bold color={t.color.accent}>
60
+ Hotkeys
61
+ </Text>
62
+ </Box>
63
+
64
+ {HOTKEY_PREVIEW.map(([k, v]) => (
65
+ <Text key={k}>
66
+ <Text color={t.color.label}>{pad(k)}</Text>
67
+ <Text color={t.color.muted}>{v}</Text>
68
+ </Text>
69
+ ))}
70
+ </Box>
71
+ </Box>
72
+ )
73
+ }