@effect-tui/react 0.1.3 → 0.1.5

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 (442) hide show
  1. package/dist/jsx-runtime.d.ts +13 -0
  2. package/dist/jsx-runtime.d.ts.map +1 -1
  3. package/dist/jsx-runtime.js.map +1 -1
  4. package/dist/src/codeblock.d.ts.map +1 -1
  5. package/dist/src/codeblock.js.map +1 -1
  6. package/dist/src/components/Divider.d.ts +18 -0
  7. package/dist/src/components/Divider.d.ts.map +1 -0
  8. package/dist/src/components/Divider.js +17 -0
  9. package/dist/src/components/Divider.js.map +1 -0
  10. package/dist/src/components/Markdown.d.ts +66 -0
  11. package/dist/src/components/Markdown.d.ts.map +1 -0
  12. package/dist/src/components/Markdown.js +226 -0
  13. package/dist/src/components/Markdown.js.map +1 -0
  14. package/dist/src/components/MultilineTextInput.d.ts +65 -0
  15. package/dist/src/components/MultilineTextInput.d.ts.map +1 -0
  16. package/dist/src/components/MultilineTextInput.js +607 -0
  17. package/dist/src/components/MultilineTextInput.js.map +1 -0
  18. package/dist/src/components/Overlay.d.ts +46 -0
  19. package/dist/src/components/Overlay.d.ts.map +1 -0
  20. package/dist/src/components/Overlay.js +11 -0
  21. package/dist/src/components/Overlay.js.map +1 -0
  22. package/dist/src/components/Static.d.ts +44 -0
  23. package/dist/src/components/Static.d.ts.map +1 -0
  24. package/dist/src/components/Static.js +53 -0
  25. package/dist/src/components/Static.js.map +1 -0
  26. package/dist/src/components/TextInput.d.ts +55 -0
  27. package/dist/src/components/TextInput.d.ts.map +1 -0
  28. package/dist/src/components/TextInput.js +277 -0
  29. package/dist/src/components/TextInput.js.map +1 -0
  30. package/dist/src/components/index.d.ts +7 -0
  31. package/dist/src/components/index.d.ts.map +1 -0
  32. package/dist/src/components/index.js +7 -0
  33. package/dist/src/components/index.js.map +1 -0
  34. package/dist/src/components/text-editing.d.ts +62 -0
  35. package/dist/src/components/text-editing.d.ts.map +1 -0
  36. package/dist/src/components/text-editing.js +385 -0
  37. package/dist/src/components/text-editing.js.map +1 -0
  38. package/dist/src/console/ConsoleCapture.d.ts +36 -0
  39. package/dist/src/console/ConsoleCapture.d.ts.map +1 -0
  40. package/dist/src/console/ConsoleCapture.js +210 -0
  41. package/dist/src/console/ConsoleCapture.js.map +1 -0
  42. package/dist/src/console/ConsolePopover.d.ts +18 -0
  43. package/dist/src/console/ConsolePopover.d.ts.map +1 -0
  44. package/dist/src/console/ConsolePopover.js +324 -0
  45. package/dist/src/console/ConsolePopover.js.map +1 -0
  46. package/dist/src/console/clipboard.d.ts +10 -0
  47. package/dist/src/console/clipboard.d.ts.map +1 -0
  48. package/dist/src/console/clipboard.js +74 -0
  49. package/dist/src/console/clipboard.js.map +1 -0
  50. package/dist/src/console/index.d.ts +5 -0
  51. package/dist/src/console/index.d.ts.map +1 -0
  52. package/dist/src/console/index.js +33 -0
  53. package/dist/src/console/index.js.map +1 -0
  54. package/dist/src/console/useConsole.d.ts +44 -0
  55. package/dist/src/console/useConsole.d.ts.map +1 -0
  56. package/dist/src/console/useConsole.js +91 -0
  57. package/dist/src/console/useConsole.js.map +1 -0
  58. package/dist/src/debug/DebugOverlay.d.ts +49 -0
  59. package/dist/src/debug/DebugOverlay.d.ts.map +1 -0
  60. package/dist/src/debug/DebugOverlay.js +438 -0
  61. package/dist/src/debug/DebugOverlay.js.map +1 -0
  62. package/dist/src/debug/DiagnosticsPanel.d.ts.map +1 -1
  63. package/dist/src/debug/DiagnosticsPanel.js.map +1 -1
  64. package/dist/src/dev/Toast.d.ts +19 -0
  65. package/dist/src/dev/Toast.d.ts.map +1 -0
  66. package/dist/src/dev/Toast.js +72 -0
  67. package/dist/src/dev/Toast.js.map +1 -0
  68. package/dist/src/dev/index.d.ts +2 -0
  69. package/dist/src/dev/index.d.ts.map +1 -0
  70. package/dist/src/dev/index.js +3 -0
  71. package/dist/src/dev/index.js.map +1 -0
  72. package/dist/src/dev.d.ts +114 -0
  73. package/dist/src/dev.d.ts.map +1 -0
  74. package/dist/src/dev.js +373 -0
  75. package/dist/src/dev.js.map +1 -0
  76. package/dist/src/highlight.d.ts +3 -3
  77. package/dist/src/highlight.d.ts.map +1 -1
  78. package/dist/src/highlight.js.map +1 -1
  79. package/dist/src/hmr-plugin.d.ts +2 -0
  80. package/dist/src/hmr-plugin.d.ts.map +1 -0
  81. package/dist/src/hmr-plugin.js +53 -0
  82. package/dist/src/hmr-plugin.js.map +1 -0
  83. package/dist/src/hooks/index.d.ts +4 -0
  84. package/dist/src/hooks/index.d.ts.map +1 -1
  85. package/dist/src/hooks/index.js +2 -0
  86. package/dist/src/hooks/index.js.map +1 -1
  87. package/dist/src/hooks/use-keyboard.d.ts +11 -0
  88. package/dist/src/hooks/use-keyboard.d.ts.map +1 -1
  89. package/dist/src/hooks/use-keyboard.js +22 -4
  90. package/dist/src/hooks/use-keyboard.js.map +1 -1
  91. package/dist/src/hooks/use-mouse.d.ts +24 -0
  92. package/dist/src/hooks/use-mouse.d.ts.map +1 -0
  93. package/dist/src/hooks/use-mouse.js +41 -0
  94. package/dist/src/hooks/use-mouse.js.map +1 -0
  95. package/dist/src/hooks/use-paste.d.ts +11 -0
  96. package/dist/src/hooks/use-paste.d.ts.map +1 -1
  97. package/dist/src/hooks/use-paste.js +17 -3
  98. package/dist/src/hooks/use-paste.js.map +1 -1
  99. package/dist/src/hooks/use-scroll.d.ts +79 -0
  100. package/dist/src/hooks/use-scroll.d.ts.map +1 -0
  101. package/dist/src/hooks/use-scroll.js +239 -0
  102. package/dist/src/hooks/use-scroll.js.map +1 -0
  103. package/dist/src/hooks/useFrameStats.js.map +1 -1
  104. package/dist/src/hosts/base.d.ts +62 -1
  105. package/dist/src/hosts/base.d.ts.map +1 -1
  106. package/dist/src/hosts/base.js +118 -5
  107. package/dist/src/hosts/base.js.map +1 -1
  108. package/dist/src/hosts/box.d.ts +7 -7
  109. package/dist/src/hosts/box.d.ts.map +1 -1
  110. package/dist/src/hosts/box.js +30 -23
  111. package/dist/src/hosts/box.js.map +1 -1
  112. package/dist/src/hosts/canvas.d.ts +16 -8
  113. package/dist/src/hosts/canvas.d.ts.map +1 -1
  114. package/dist/src/hosts/canvas.js +27 -22
  115. package/dist/src/hosts/canvas.js.map +1 -1
  116. package/dist/src/hosts/codeblock.d.ts +7 -7
  117. package/dist/src/hosts/codeblock.d.ts.map +1 -1
  118. package/dist/src/hosts/codeblock.js +11 -20
  119. package/dist/src/hosts/codeblock.js.map +1 -1
  120. package/dist/src/hosts/flex-container.d.ts +45 -0
  121. package/dist/src/hosts/flex-container.d.ts.map +1 -0
  122. package/dist/src/hosts/flex-container.js +90 -0
  123. package/dist/src/hosts/flex-container.js.map +1 -0
  124. package/dist/src/hosts/hstack.d.ts +6 -11
  125. package/dist/src/hosts/hstack.d.ts.map +1 -1
  126. package/dist/src/hosts/hstack.js +6 -41
  127. package/dist/src/hosts/hstack.js.map +1 -1
  128. package/dist/src/hosts/index.d.ts +4 -0
  129. package/dist/src/hosts/index.d.ts.map +1 -1
  130. package/dist/src/hosts/index.js +10 -0
  131. package/dist/src/hosts/index.js.map +1 -1
  132. package/dist/src/hosts/overlay-item.d.ts +32 -0
  133. package/dist/src/hosts/overlay-item.d.ts.map +1 -0
  134. package/dist/src/hosts/overlay-item.js +54 -0
  135. package/dist/src/hosts/overlay-item.js.map +1 -0
  136. package/dist/src/hosts/overlay.d.ts +30 -0
  137. package/dist/src/hosts/overlay.d.ts.map +1 -0
  138. package/dist/src/hosts/overlay.js +105 -0
  139. package/dist/src/hosts/overlay.js.map +1 -0
  140. package/dist/src/hosts/scroll.d.ts +56 -0
  141. package/dist/src/hosts/scroll.d.ts.map +1 -0
  142. package/dist/src/hosts/scroll.js +204 -0
  143. package/dist/src/hosts/scroll.js.map +1 -0
  144. package/dist/src/hosts/single-child.d.ts +16 -0
  145. package/dist/src/hosts/single-child.d.ts.map +1 -0
  146. package/dist/src/hosts/single-child.js +45 -0
  147. package/dist/src/hosts/single-child.js.map +1 -0
  148. package/dist/src/hosts/spacer.d.ts.map +1 -1
  149. package/dist/src/hosts/spacer.js +7 -3
  150. package/dist/src/hosts/spacer.js.map +1 -1
  151. package/dist/src/hosts/text.d.ts +9 -6
  152. package/dist/src/hosts/text.d.ts.map +1 -1
  153. package/dist/src/hosts/text.js +49 -22
  154. package/dist/src/hosts/text.js.map +1 -1
  155. package/dist/src/hosts/vstack.d.ts +6 -11
  156. package/dist/src/hosts/vstack.d.ts.map +1 -1
  157. package/dist/src/hosts/vstack.js +6 -41
  158. package/dist/src/hosts/vstack.js.map +1 -1
  159. package/dist/src/hosts/zstack.d.ts.map +1 -1
  160. package/dist/src/hosts/zstack.js +16 -5
  161. package/dist/src/hosts/zstack.js.map +1 -1
  162. package/dist/src/index.d.ts +9 -2
  163. package/dist/src/index.d.ts.map +1 -1
  164. package/dist/src/index.js +10 -2
  165. package/dist/src/index.js.map +1 -1
  166. package/dist/src/inline/index.d.ts.map +1 -1
  167. package/dist/src/inline/index.js.map +1 -1
  168. package/dist/src/motion/color-motion-value.d.ts.map +1 -1
  169. package/dist/src/motion/color-motion-value.js.map +1 -1
  170. package/dist/src/motion/color.d.ts +1 -29
  171. package/dist/src/motion/color.d.ts.map +1 -1
  172. package/dist/src/motion/color.js +2 -170
  173. package/dist/src/motion/color.js.map +1 -1
  174. package/dist/src/motion/color.test.js.map +1 -1
  175. package/dist/src/motion/event-emitter.d.ts.map +1 -1
  176. package/dist/src/motion/event-emitter.js.map +1 -1
  177. package/dist/src/motion/frame.js.map +1 -1
  178. package/dist/src/motion/hooks.d.ts.map +1 -1
  179. package/dist/src/motion/hooks.js +8 -3
  180. package/dist/src/motion/hooks.js.map +1 -1
  181. package/dist/src/motion/index.d.ts.map +1 -1
  182. package/dist/src/motion/index.js.map +1 -1
  183. package/dist/src/motion/motion-value.d.ts.map +1 -1
  184. package/dist/src/motion/motion-value.js.map +1 -1
  185. package/dist/src/motion/motion-value.test.js.map +1 -1
  186. package/dist/src/motion/spring-math.d.ts +6 -1
  187. package/dist/src/motion/spring-math.d.ts.map +1 -1
  188. package/dist/src/motion/spring-math.js +6 -1
  189. package/dist/src/motion/spring-math.js.map +1 -1
  190. package/dist/src/motion/types.d.ts.map +1 -1
  191. package/dist/src/motion/types.js.map +1 -1
  192. package/dist/src/profiler.js.map +1 -1
  193. package/dist/src/reconciler/host-config.d.ts +5 -5
  194. package/dist/src/reconciler/host-config.d.ts.map +1 -1
  195. package/dist/src/reconciler/host-config.js +43 -51
  196. package/dist/src/reconciler/host-config.js.map +1 -1
  197. package/dist/src/reconciler/noop-methods.d.ts +29 -0
  198. package/dist/src/reconciler/noop-methods.d.ts.map +1 -0
  199. package/dist/src/reconciler/noop-methods.js +43 -0
  200. package/dist/src/reconciler/noop-methods.js.map +1 -0
  201. package/dist/src/reconciler/types.d.ts +68 -14
  202. package/dist/src/reconciler/types.d.ts.map +1 -1
  203. package/dist/src/remote/Procedures.d.ts +22 -0
  204. package/dist/src/remote/Procedures.d.ts.map +1 -0
  205. package/dist/src/remote/Procedures.js +42 -0
  206. package/dist/src/remote/Procedures.js.map +1 -0
  207. package/dist/src/remote/Router.d.ts +20 -0
  208. package/dist/src/remote/Router.d.ts.map +1 -0
  209. package/dist/src/remote/Router.js +26 -0
  210. package/dist/src/remote/Router.js.map +1 -0
  211. package/dist/src/remote/Server.d.ts +6 -0
  212. package/dist/src/remote/Server.d.ts.map +1 -0
  213. package/dist/src/remote/Server.js +53 -0
  214. package/dist/src/remote/Server.js.map +1 -0
  215. package/dist/src/remote/index.d.ts +18 -0
  216. package/dist/src/remote/index.d.ts.map +1 -0
  217. package/dist/src/remote/index.js +74 -0
  218. package/dist/src/remote/index.js.map +1 -0
  219. package/dist/src/renderer/core/FrameBuilder.d.ts +18 -0
  220. package/dist/src/renderer/core/FrameBuilder.d.ts.map +1 -0
  221. package/dist/src/renderer/core/FrameBuilder.js +38 -0
  222. package/dist/src/renderer/core/FrameBuilder.js.map +1 -0
  223. package/dist/src/renderer/core/RendererState.d.ts +41 -0
  224. package/dist/src/renderer/core/RendererState.d.ts.map +1 -0
  225. package/dist/src/renderer/core/RendererState.js +70 -0
  226. package/dist/src/renderer/core/RendererState.js.map +1 -0
  227. package/dist/src/renderer/core/index.d.ts +3 -0
  228. package/dist/src/renderer/core/index.d.ts.map +1 -0
  229. package/dist/src/renderer/core/index.js +3 -0
  230. package/dist/src/renderer/core/index.js.map +1 -0
  231. package/dist/src/renderer/input/InputProcessor.d.ts +25 -0
  232. package/dist/src/renderer/input/InputProcessor.d.ts.map +1 -0
  233. package/dist/src/renderer/input/InputProcessor.js +81 -0
  234. package/dist/src/renderer/input/InputProcessor.js.map +1 -0
  235. package/dist/src/renderer/input/index.d.ts +2 -0
  236. package/dist/src/renderer/input/index.d.ts.map +1 -0
  237. package/dist/src/renderer/input/index.js +2 -0
  238. package/dist/src/renderer/input/index.js.map +1 -0
  239. package/dist/src/renderer/lifecycle/EventBus.d.ts +41 -0
  240. package/dist/src/renderer/lifecycle/EventBus.d.ts.map +1 -0
  241. package/dist/src/renderer/lifecycle/EventBus.js +78 -0
  242. package/dist/src/renderer/lifecycle/EventBus.js.map +1 -0
  243. package/dist/src/renderer/lifecycle/ResizeManager.d.ts +34 -0
  244. package/dist/src/renderer/lifecycle/ResizeManager.d.ts.map +1 -0
  245. package/dist/src/renderer/lifecycle/ResizeManager.js +47 -0
  246. package/dist/src/renderer/lifecycle/ResizeManager.js.map +1 -0
  247. package/dist/src/renderer/lifecycle/TerminalSetup.d.ts +36 -0
  248. package/dist/src/renderer/lifecycle/TerminalSetup.d.ts.map +1 -0
  249. package/dist/src/renderer/lifecycle/TerminalSetup.js +82 -0
  250. package/dist/src/renderer/lifecycle/TerminalSetup.js.map +1 -0
  251. package/dist/src/renderer/lifecycle/index.d.ts +4 -0
  252. package/dist/src/renderer/lifecycle/index.d.ts.map +1 -0
  253. package/dist/src/renderer/lifecycle/index.js +4 -0
  254. package/dist/src/renderer/lifecycle/index.js.map +1 -0
  255. package/dist/src/renderer/modes/FullscreenRenderer.d.ts +12 -0
  256. package/dist/src/renderer/modes/FullscreenRenderer.d.ts.map +1 -0
  257. package/dist/src/renderer/modes/FullscreenRenderer.js +52 -0
  258. package/dist/src/renderer/modes/FullscreenRenderer.js.map +1 -0
  259. package/dist/src/renderer/modes/InlineRenderer.d.ts +25 -0
  260. package/dist/src/renderer/modes/InlineRenderer.d.ts.map +1 -0
  261. package/dist/src/renderer/modes/InlineRenderer.js +161 -0
  262. package/dist/src/renderer/modes/InlineRenderer.js.map +1 -0
  263. package/dist/src/renderer/modes/RendererMode.d.ts +42 -0
  264. package/dist/src/renderer/modes/RendererMode.d.ts.map +1 -0
  265. package/dist/src/renderer/modes/RendererMode.js +2 -0
  266. package/dist/src/renderer/modes/RendererMode.js.map +1 -0
  267. package/dist/src/renderer/modes/StaticContentRenderer.d.ts +25 -0
  268. package/dist/src/renderer/modes/StaticContentRenderer.d.ts.map +1 -0
  269. package/dist/src/renderer/modes/StaticContentRenderer.js +47 -0
  270. package/dist/src/renderer/modes/StaticContentRenderer.js.map +1 -0
  271. package/dist/src/renderer/modes/index.d.ts +5 -0
  272. package/dist/src/renderer/modes/index.d.ts.map +1 -0
  273. package/dist/src/renderer/modes/index.js +4 -0
  274. package/dist/src/renderer/modes/index.js.map +1 -0
  275. package/dist/src/renderer-context.d.ts +9 -0
  276. package/dist/src/renderer-context.d.ts.map +1 -0
  277. package/dist/src/renderer-context.js +22 -0
  278. package/dist/src/renderer-context.js.map +1 -0
  279. package/dist/src/renderer-types.d.ts +103 -0
  280. package/dist/src/renderer-types.d.ts.map +1 -0
  281. package/dist/src/renderer-types.js +2 -0
  282. package/dist/src/renderer-types.js.map +1 -0
  283. package/dist/src/renderer.d.ts +4 -86
  284. package/dist/src/renderer.d.ts.map +1 -1
  285. package/dist/src/renderer.js +214 -384
  286. package/dist/src/renderer.js.map +1 -1
  287. package/dist/src/test/index.d.ts.map +1 -1
  288. package/dist/src/test/index.js.map +1 -1
  289. package/dist/src/test/mock-streams.d.ts.map +1 -1
  290. package/dist/src/test/mock-streams.js.map +1 -1
  291. package/dist/src/test/render-tui.d.ts.map +1 -1
  292. package/dist/src/test/render-tui.js +2 -5
  293. package/dist/src/test/render-tui.js.map +1 -1
  294. package/dist/src/trace/SpanTree.d.ts.map +1 -1
  295. package/dist/src/trace/SpanTree.js +21 -11
  296. package/dist/src/trace/SpanTree.js.map +1 -1
  297. package/dist/src/trace/format-value.d.ts +15 -0
  298. package/dist/src/trace/format-value.d.ts.map +1 -0
  299. package/dist/src/trace/format-value.js +77 -0
  300. package/dist/src/trace/format-value.js.map +1 -0
  301. package/dist/src/trace/index.d.ts.map +1 -1
  302. package/dist/src/trace/index.js.map +1 -1
  303. package/dist/src/trace/location.js +1 -1
  304. package/dist/src/trace/location.js.map +1 -1
  305. package/dist/src/trace/span-processor.d.ts.map +1 -1
  306. package/dist/src/trace/span-processor.js.map +1 -1
  307. package/dist/src/trace/span-state.d.ts +19 -2
  308. package/dist/src/trace/span-state.d.ts.map +1 -1
  309. package/dist/src/trace/span-state.js +62 -31
  310. package/dist/src/trace/span-state.js.map +1 -1
  311. package/dist/src/trace/tui-logger.js.map +1 -1
  312. package/dist/src/utils/border.d.ts +1 -1
  313. package/dist/src/utils/border.d.ts.map +1 -1
  314. package/dist/src/utils/border.js +6 -0
  315. package/dist/src/utils/border.js.map +1 -1
  316. package/dist/src/utils/flex-layout.d.ts +2 -1
  317. package/dist/src/utils/flex-layout.d.ts.map +1 -1
  318. package/dist/src/utils/flex-layout.js +22 -33
  319. package/dist/src/utils/flex-layout.js.map +1 -1
  320. package/dist/src/utils/index.d.ts +1 -1
  321. package/dist/src/utils/index.d.ts.map +1 -1
  322. package/dist/src/utils/index.js +1 -1
  323. package/dist/src/utils/index.js.map +1 -1
  324. package/dist/src/utils/padding.d.ts.map +1 -1
  325. package/dist/src/utils/padding.js.map +1 -1
  326. package/dist/src/utils/styles.d.ts +20 -1
  327. package/dist/src/utils/styles.d.ts.map +1 -1
  328. package/dist/src/utils/styles.js +36 -1
  329. package/dist/src/utils/styles.js.map +1 -1
  330. package/dist/src/visualize/index.d.ts +8 -19
  331. package/dist/src/visualize/index.d.ts.map +1 -1
  332. package/dist/src/visualize/index.js +11 -25
  333. package/dist/src/visualize/index.js.map +1 -1
  334. package/dist/tsconfig.tsbuildinfo +1 -1
  335. package/jsx-dev-runtime.ts +5 -0
  336. package/jsx-runtime.ts +54 -0
  337. package/package.json +124 -92
  338. package/src/codeblock.tsx +34 -34
  339. package/src/components/Divider.tsx +23 -0
  340. package/src/components/Markdown.tsx +380 -0
  341. package/src/components/MultilineTextInput.tsx +749 -0
  342. package/src/components/Overlay.tsx +56 -0
  343. package/src/components/Static.tsx +68 -0
  344. package/src/components/TextInput.tsx +356 -0
  345. package/src/components/index.ts +6 -0
  346. package/src/components/text-editing.ts +464 -0
  347. package/src/console/ConsoleCapture.ts +272 -0
  348. package/src/console/ConsolePopover.tsx +487 -0
  349. package/src/console/clipboard.ts +81 -0
  350. package/src/console/index.ts +42 -0
  351. package/src/console/useConsole.ts +129 -0
  352. package/src/debug/DebugOverlay.ts +557 -0
  353. package/src/debug/DiagnosticsPanel.tsx +27 -27
  354. package/src/dev/Toast.tsx +117 -0
  355. package/src/dev/index.ts +2 -0
  356. package/src/dev.tsx +489 -0
  357. package/src/highlight.ts +46 -46
  358. package/src/hmr-plugin.ts +61 -0
  359. package/src/hooks/index.ts +4 -0
  360. package/src/hooks/use-keyboard.ts +44 -24
  361. package/src/hooks/use-mouse.ts +51 -0
  362. package/src/hooks/use-paste.ts +21 -6
  363. package/src/hooks/use-scroll.ts +386 -0
  364. package/src/hooks/useFrameStats.ts +17 -17
  365. package/src/hosts/base.ts +180 -59
  366. package/src/hosts/box.ts +117 -94
  367. package/src/hosts/canvas.ts +170 -141
  368. package/src/hosts/codeblock.ts +117 -133
  369. package/src/hosts/flex-container.ts +124 -0
  370. package/src/hosts/hstack.ts +11 -59
  371. package/src/hosts/index.ts +24 -14
  372. package/src/hosts/overlay-item.ts +72 -0
  373. package/src/hosts/overlay.ts +125 -0
  374. package/src/hosts/scroll.ts +255 -0
  375. package/src/hosts/single-child.ts +52 -0
  376. package/src/hosts/spacer.ts +30 -26
  377. package/src/hosts/text.ts +198 -164
  378. package/src/hosts/vstack.ts +11 -59
  379. package/src/hosts/zstack.ts +79 -67
  380. package/src/index.ts +44 -19
  381. package/src/inline/index.tsx +123 -123
  382. package/src/motion/color-motion-value.ts +67 -67
  383. package/src/motion/color.test.ts +107 -107
  384. package/src/motion/color.ts +9 -190
  385. package/src/motion/event-emitter.ts +20 -20
  386. package/src/motion/frame.ts +35 -35
  387. package/src/motion/hooks.ts +144 -139
  388. package/src/motion/index.ts +10 -10
  389. package/src/motion/motion-value.test.ts +207 -207
  390. package/src/motion/motion-value.ts +112 -112
  391. package/src/motion/spring-math.ts +88 -83
  392. package/src/motion/types.ts +25 -25
  393. package/src/profiler.ts +50 -50
  394. package/src/reconciler/host-config.ts +152 -174
  395. package/src/reconciler/noop-methods.ts +55 -0
  396. package/src/reconciler/types.ts +112 -46
  397. package/src/remote/Procedures.ts +52 -0
  398. package/src/remote/Router.ts +58 -0
  399. package/src/remote/Server.ts +76 -0
  400. package/src/remote/index.ts +90 -0
  401. package/src/renderer/core/FrameBuilder.ts +49 -0
  402. package/src/renderer/core/RendererState.ts +80 -0
  403. package/src/renderer/core/index.ts +2 -0
  404. package/src/renderer/input/InputProcessor.ts +94 -0
  405. package/src/renderer/input/index.ts +1 -0
  406. package/src/renderer/lifecycle/EventBus.ts +90 -0
  407. package/src/renderer/lifecycle/ResizeManager.ts +65 -0
  408. package/src/renderer/lifecycle/TerminalSetup.ts +105 -0
  409. package/src/renderer/lifecycle/index.ts +3 -0
  410. package/src/renderer/modes/FullscreenRenderer.ts +53 -0
  411. package/src/renderer/modes/InlineRenderer.ts +186 -0
  412. package/src/renderer/modes/RendererMode.ts +46 -0
  413. package/src/renderer/modes/StaticContentRenderer.ts +56 -0
  414. package/src/renderer/modes/index.ts +4 -0
  415. package/src/renderer-context.ts +27 -0
  416. package/src/renderer-types.ts +109 -0
  417. package/src/renderer.ts +392 -642
  418. package/src/test/index.ts +5 -5
  419. package/src/test/mock-streams.ts +115 -115
  420. package/src/test/render-tui.ts +84 -87
  421. package/src/utils/border.ts +79 -73
  422. package/src/utils/flex-layout.ts +80 -93
  423. package/src/utils/index.ts +1 -1
  424. package/src/utils/padding.ts +27 -27
  425. package/src/utils/styles.ts +50 -7
  426. package/src/visualize/index.tsx +225 -240
  427. package/dist/src/output.d.ts +0 -47
  428. package/dist/src/output.d.ts.map +0 -1
  429. package/dist/src/output.js +0 -125
  430. package/dist/src/output.js.map +0 -1
  431. package/dist/src/terminal.d.ts +0 -37
  432. package/dist/src/terminal.d.ts.map +0 -1
  433. package/dist/src/terminal.js +0 -65
  434. package/dist/src/terminal.js.map +0 -1
  435. package/src/output.ts +0 -156
  436. package/src/terminal.ts +0 -67
  437. package/src/trace/SpanTree.tsx +0 -195
  438. package/src/trace/index.tsx +0 -205
  439. package/src/trace/location.ts +0 -90
  440. package/src/trace/span-processor.ts +0 -65
  441. package/src/trace/span-state.ts +0 -286
  442. package/src/trace/tui-logger.ts +0 -72
@@ -1,31 +1,16 @@
1
- import React, { createContext, useContext, useState, useEffect } from "react";
1
+ import React, {} from "react";
2
2
  import { performance } from "node:perf_hooks";
3
- import { CellBuffer, Palette, decodeKeys } from "@effect-tui/core";
4
- import { reconciler } from "./reconciler/host-config.js";
5
- import { ANSI, Terminal } from "./terminal.js";
3
+ import { ANSI, bufferToString } from "@effect-tui/core";
4
+ import { reconciler, flushSync } from "./reconciler/host-config.js";
6
5
  import * as Prof from "./profiler.js";
7
6
  import { DEFAULT_FPS } from "./constants.js";
8
- import { emitRowWithReset, rowChanged, rowContentWidth, findChangeWindow, contentHeight, } from "./output.js";
9
- // Context for accessing renderer in components
10
- export const RendererContext = createContext(null);
11
- export function useRenderer() {
12
- const renderer = useContext(RendererContext);
13
- if (!renderer) {
14
- throw new Error("useRenderer must be used within a TUI renderer");
15
- }
16
- return renderer;
17
- }
18
- /** Hook that returns terminal size and re-renders on resize */
19
- export function useTerminalSize() {
20
- const renderer = useRenderer();
21
- const [size, setSize] = useState({ width: renderer.width, height: renderer.height });
22
- useEffect(() => {
23
- return renderer.onResize((width, height) => {
24
- setSize({ width, height });
25
- });
26
- }, [renderer]);
27
- return size;
28
- }
7
+ import { RendererContext } from "./renderer-context.js";
8
+ // Extracted modules
9
+ import { RendererState, FrameBuilder } from "./renderer/core/index.js";
10
+ import { InputProcessor } from "./renderer/input/index.js";
11
+ import { EventBus, TerminalSetup } from "./renderer/lifecycle/index.js";
12
+ import { FullscreenRenderer, InlineRenderer, StaticContentRenderer } from "./renderer/modes/index.js";
13
+ export { RendererContext, useRenderer, useTerminalSize } from "./renderer-context.js";
29
14
  export function createRenderer(options) {
30
15
  const fps = options?.fps ?? DEFAULT_FPS;
31
16
  const stdout = options?.stdout ?? process.stdout;
@@ -34,388 +19,248 @@ export function createRenderer(options) {
34
19
  const exitOnCtrlC = options?.exitOnCtrlC ?? true;
35
20
  const manualMode = options?.manualMode ?? false;
36
21
  const enableDiff = options?.diff ?? !manualMode;
37
- const enableDiffInline = options?.diffInline ?? false;
38
22
  const skipTerminalSetup = options?.skipTerminalSetup ?? false;
39
23
  const enablePaste = options?.enablePaste ?? true;
24
+ const enableMouse = options?.enableMouse ?? mode === "fullscreen";
40
25
  const debugHook = options?.debug?.onFrame;
41
- const PASTE_START = "\x1b[200~";
42
- const PASTE_END = "\x1b[201~";
43
- const PASTE_ENABLE = "\x1b[?2004h";
44
- const PASTE_DISABLE = "\x1b[?2004l";
45
- let width = stdout.columns || 80;
46
- let height = stdout.rows || 24;
47
- let lastWidth = width; // Track for shrink detection (like Ink)
48
- let dirty = true;
49
- let running = true;
50
- let previousHeight = 0; // For inline mode: track how many lines were rendered
51
- const printedWidths = new Map(); // track rightmost printed col per row for inline diff
52
- const keyHandlers = new Set();
53
- const pasteHandlers = new Set();
54
- const resizeHandlers = new Set();
55
- const frameHandlers = new Set();
56
- let pasteActive = false;
57
- let pasteBuffer = "";
58
- const palette = new Palette();
59
- let prevBuffer = null;
60
- let nextBuffer = null;
61
- let loop = null;
62
- const teardown = () => {
63
- if (skipTerminalSetup)
64
- return;
65
- if (mode === "fullscreen") {
66
- stdout.write(Terminal.exitFullscreen);
67
- }
68
- else {
69
- stdout.write("\r\n");
70
- }
71
- if (enablePaste) {
72
- stdout.write(PASTE_DISABLE);
73
- }
74
- stdout.write(Terminal.showCursor);
75
- if (stdin.isTTY && stdin.setRawMode) {
76
- stdin.setRawMode(false);
77
- }
78
- };
79
- // The actual render logic, extracted for manual flushing
26
+ // Initialize state
27
+ const state = new RendererState(stdout.columns || 80, stdout.rows || 24);
28
+ const events = new EventBus();
29
+ const frameBuilder = new FrameBuilder();
30
+ // Terminal setup/teardown
31
+ const terminal = new TerminalSetup(stdout, stdin, {
32
+ mode,
33
+ enablePaste,
34
+ enableMouse,
35
+ skipTerminalSetup,
36
+ });
37
+ // Render mode (fullscreen or inline)
38
+ const renderMode = mode === "fullscreen" ? new FullscreenRenderer() : new InlineRenderer();
39
+ // Static content renderer (inline mode only)
40
+ const staticRenderer = mode === "inline" ? new StaticContentRenderer(stdout, state.palette) : null;
41
+ // Input processing
42
+ const inputProcessor = new InputProcessor({
43
+ exitOnCtrlC,
44
+ dispatchKey: (key) => {
45
+ events.dispatchKey(key);
46
+ return key.defaultPrevented ?? false;
47
+ },
48
+ dispatchMouse: (mouse) => events.dispatchMouse(mouse),
49
+ dispatchPaste: (text) => events.dispatchPaste(text),
50
+ flushSync: (fn) => flushSync(fn) ?? undefined,
51
+ onInputProcessed: () => {
52
+ if (!manualMode)
53
+ renderFrame();
54
+ },
55
+ });
56
+ // The render frame logic
80
57
  const renderFrame = () => {
81
58
  const frameStart = Prof.startFrame();
82
59
  const frameStartMs = performance.now();
83
- let clearMs = 0;
84
- let layoutMs = 0;
85
- let renderMs = 0;
86
- let diffAnsiMs = 0;
87
- let writeMs = 0;
88
- let contentH = height;
60
+ const frameWidth = state.width;
61
+ const frameHeight = state.height;
62
+ let contentH = frameHeight;
89
63
  const container = renderer._container;
90
64
  const root = container?.root ?? null;
91
- if (!dirty || !root)
65
+ // Must render if dirty OR if static content needs flushing
66
+ if ((!state.dirty && !container?.staticDirty) || !root)
92
67
  return;
93
- dirty = false;
94
- // Ensure buffers exist and are correct size
95
- if (!prevBuffer || !nextBuffer || prevBuffer.w !== width || prevBuffer.h !== height) {
96
- prevBuffer = new CellBuffer(width, height);
97
- nextBuffer = new CellBuffer(width, height);
98
- prevBuffer.clear(0);
99
- nextBuffer.clear(0);
100
- }
101
- // Clear next buffer
102
- let t = Prof.startPhase();
103
- {
104
- const t0 = performance.now();
105
- nextBuffer.clear(0);
106
- clearMs = performance.now() - t0;
107
- }
108
- Prof.endPhase("clear", t);
109
- // Layout
110
- t = Prof.startPhase();
111
- {
112
- const t0 = performance.now();
113
- root.measure(width, height);
114
- root.layout({ x: 0, y: 0, w: width, h: height });
115
- layoutMs = performance.now() - t0;
116
- }
117
- Prof.endPhase("layout", t);
118
- // Render
119
- t = Prof.startPhase();
120
- {
121
- const t0 = performance.now();
122
- root.render(nextBuffer, palette);
123
- renderMs = performance.now() - t0;
124
- }
125
- Prof.endPhase("render", t);
126
- // Output based on mode
127
- t = Prof.startPhase();
128
- const diffStartMs = performance.now();
129
- let output = "";
130
- if (mode === "fullscreen") {
131
- // Fullscreen: optionally diff per line for minimal writes
132
- if (enableDiff && prevBuffer) {
133
- for (let y = 0; y < height; y++) {
134
- if (!rowChanged(prevBuffer, nextBuffer, y, width))
135
- continue;
136
- output += ANSI.cursor.to(1, y + 1);
137
- // Clear the row using default style to avoid carrying stale backgrounds.
138
- output += palette.sgr(0) + ANSI.line.clear;
139
- output += emitRowWithReset(nextBuffer, palette, y, width);
140
- }
141
- }
142
- else {
143
- // Full redraw (tests/manual mode)
144
- stdout.write(ANSI.cursor.to(1, 1) + palette.sgr(0));
145
- for (let y = 0; y < height; y++) {
146
- output += emitRowWithReset(nextBuffer, palette, y, width);
147
- if (y < height - 1)
148
- output += "\r\n";
149
- }
150
- }
151
- }
152
- else {
153
- // ============================================================
154
- // INLINE MODE RENDERING
155
- // ============================================================
156
- // Renders in-place without alternate buffer. Key insights from Ink:
157
- // - Track previousHeight to know how many lines to erase
158
- // - Trim trailing spaces to prevent wrap on terminal shrink
159
- // - Safety valve: clear terminal if content >= terminal height
160
- // ============================================================
161
- const newHeight = contentHeight(nextBuffer, width, height);
162
- contentH = newHeight;
163
- // Safety valve: if previous content would overflow, clear terminal first
164
- if (previousHeight >= height) {
165
- stdout.write(ANSI.screen.clear + ANSI.cursor.to(1, 1));
166
- previousHeight = 0;
167
- }
168
- // Move cursor up to start of previous output (eraseLines pattern)
169
- if (previousHeight > 0) {
170
- output += ANSI.cursor.up(previousHeight);
171
- output += ANSI.cursor.startOfLine;
172
- }
173
- if (enableDiffInline && prevBuffer) {
174
- // Diff-based inline rendering: only update changed regions
175
- const rowsToProcess = Math.max(newHeight, previousHeight);
176
- for (let y = 0; y < rowsToProcess; y++) {
177
- // Rows beyond newHeight: clear if previously printed
178
- if (y >= newHeight) {
179
- if ((printedWidths.get(y) ?? 0) > 0) {
180
- output += ANSI.cursor.to(y + 1, 1) + palette.sgr(0) + ANSI.line.clear;
181
- printedWidths.set(y, 0);
182
- }
183
- continue;
184
- }
185
- const change = findChangeWindow(prevBuffer, nextBuffer, y, width);
186
- const newW = rowContentWidth(nextBuffer, y, width);
187
- const prevW = printedWidths.get(y) ?? 0;
188
- if (!change) {
189
- // No change; maybe need to clear tail if content shrunk
190
- if (prevW > newW) {
191
- output += ANSI.cursor.to(y + 1, newW + 1) + palette.sgr(0) + ANSI.line.clearToEnd;
192
- printedWidths.set(y, newW);
193
- }
194
- continue;
68
+ state.dirty = false;
69
+ try {
70
+ // Handle full rerender on resize (Ink-style: clear everything + replay static)
71
+ if (mode === "inline" && staticRenderer) {
72
+ const inlineMode = renderMode;
73
+ if (inlineMode.needsFullRerender()) {
74
+ // Clear screen + scrollback + cursor home
75
+ stdout.write(ANSI.screen.clear + ANSI.screen.clearScrollback + ANSI.cursor.home);
76
+ // Replay all cached static content
77
+ const cachedStatic = staticRenderer.getCachedOutput();
78
+ if (cachedStatic) {
79
+ stdout.write(cachedStatic);
195
80
  }
196
- // Emit changed region [left..right]
197
- output += ANSI.cursor.to(y + 1, change.left + 1);
198
- output += emitRowWithReset(nextBuffer, palette, y, width, change.left, change.right + 1);
199
- // Clear tail if shrunk
200
- const effectiveW = Math.max(newW, change.right + 1);
201
- if (prevW > effectiveW) {
202
- output += ANSI.cursor.to(y + 1, effectiveW + 1) + ANSI.line.clearToEnd;
203
- }
204
- printedWidths.set(y, effectiveW);
81
+ // Reset state
82
+ inlineMode.clearFullRerenderFlag();
83
+ state.invalidateBuffers();
205
84
  }
206
- previousHeight = newHeight;
207
85
  }
208
- else {
209
- // Full redraw inline: clear and emit each line, trimming trailing spaces
210
- for (let y = 0; y < newHeight; y++) {
211
- output += palette.sgr(0) + ANSI.line.clear;
212
- const trimmedWidth = rowContentWidth(nextBuffer, y, width);
213
- output += emitRowWithReset(nextBuffer, palette, y, width, 0, trimmedWidth);
214
- output += "\r\n";
86
+ // Handle static content: clear dynamic area, append static, then fresh dynamic render
87
+ // Note: IL (insert lines) won't work here because inline mode uses relative positioning
88
+ // and IL would desync the screen state from our buffer tracking.
89
+ let staticOutput = "";
90
+ if (mode === "inline" && container?.staticDirty && container?.staticRoot && staticRenderer) {
91
+ const inlineMode = renderMode;
92
+ const prevHeight = inlineMode.getPreviousHeight();
93
+ // Step 1: Clear the dynamic area (move up + clear to end of screen)
94
+ if (prevHeight > 0) {
95
+ staticOutput += ANSI.cursor.up(prevHeight) + ANSI.cursor.startOfLine + ANSI.screen.clearToEnd;
215
96
  }
216
- // Clear any extra lines if content shrank
217
- for (let y = newHeight; y < previousHeight; y++) {
218
- output += palette.sgr(0) + ANSI.line.clear + "\r\n";
219
- }
220
- // Move cursor back up to end of content
221
- if (previousHeight > newHeight) {
222
- output += ANSI.cursor.up(previousHeight - newHeight);
223
- }
224
- previousHeight = newHeight;
97
+ // Step 2: Append static content (cursor ends at bottom of static)
98
+ staticOutput += staticRenderer.render(container.staticRoot, frameWidth);
99
+ // Step 3: Reset previousHeight to 0 (we cleared dynamic, starting fresh)
100
+ inlineMode.reset();
101
+ inlineMode.forceFullOutputOnce(); // Force full output to resync cursor tracking after static
102
+ state.invalidateBuffers();
103
+ container.staticDirty = false;
225
104
  }
226
- }
227
- output += palette.sgr(0); // Reset style
228
- diffAnsiMs = performance.now() - diffStartMs;
229
- Prof.endPhase("diff+ansi", t);
230
- t = Prof.startPhase();
231
- {
232
- const t0 = performance.now();
105
+ // For inline mode, measure content unconstrained to handle overflow
106
+ let actualContentHeight = frameHeight;
107
+ if (mode === "inline") {
108
+ const size = root.measure(frameWidth, Number.MAX_SAFE_INTEGER);
109
+ actualContentHeight = size.h;
110
+ }
111
+ // Buffer height: content height for inline (to capture all content), terminal height for fullscreen
112
+ const bufferHeight = mode === "inline" ? Math.max(actualContentHeight, frameHeight) : frameHeight;
113
+ // Ensure buffers exist
114
+ state.ensureBuffers(frameWidth, bufferHeight);
115
+ if (!state.nextBuffer)
116
+ return;
117
+ // Build frame (clear, layout, render)
118
+ const timings = frameBuilder.build(root, state.nextBuffer, state.palette, frameWidth, bufferHeight);
119
+ // Generate output
120
+ const t = Prof.startPhase();
121
+ const diffStartMs = performance.now();
122
+ const { output: modeOutput, contentHeight } = renderMode.generateOutput({
123
+ nextBuffer: state.nextBuffer,
124
+ prevBuffer: state.prevBuffer,
125
+ palette: state.palette,
126
+ frameWidth,
127
+ frameHeight,
128
+ contentHeight: actualContentHeight,
129
+ enableDiff,
130
+ stdout,
131
+ });
132
+ // Combine static + dynamic output for atomic write
133
+ let output = staticOutput + modeOutput + state.palette.sgr(0);
134
+ contentH = contentHeight;
135
+ const diffAnsiMs = performance.now() - diffStartMs;
136
+ Prof.endPhase("diff+ansi", t);
137
+ // Write output (single atomic write prevents visual glitches)
138
+ const writeT = Prof.startPhase();
139
+ const writeStart = performance.now();
233
140
  stdout.write(output);
234
- writeMs = performance.now() - t0;
141
+ const writeMs = performance.now() - writeStart;
142
+ Prof.endPhase("write", writeT);
143
+ Prof.endFrame(frameStart);
144
+ const frameMs = performance.now() - frameStartMs;
145
+ // Swap buffers
146
+ state.swapBuffers();
147
+ // Build stats
148
+ const stats = {
149
+ mode,
150
+ width: state.width,
151
+ height: state.height,
152
+ contentHeight: contentH,
153
+ bytes: Buffer.byteLength(output, "utf8"),
154
+ frameMs,
155
+ phases: {
156
+ clear: timings.clear,
157
+ layout: timings.layout,
158
+ render: timings.render,
159
+ diffAnsi: diffAnsiMs,
160
+ write: writeMs,
161
+ },
162
+ timestamp: performance.now(),
163
+ };
164
+ if (debugHook)
165
+ debugHook(stats);
166
+ if (events.hasFrameHandlers)
167
+ events.dispatchFrame(stats);
235
168
  }
236
- Prof.endPhase("write", t);
237
- Prof.endFrame(frameStart);
238
- const frameMs = performance.now() - frameStartMs;
239
- // Swap buffers
240
- const tmp = prevBuffer;
241
- prevBuffer = nextBuffer;
242
- nextBuffer = tmp;
243
- const stats = {
244
- mode,
245
- width,
246
- height,
247
- contentHeight: contentH,
248
- bytes: Buffer.byteLength(output, "utf8"),
249
- frameMs,
250
- phases: {
251
- clear: clearMs,
252
- layout: layoutMs,
253
- render: renderMs,
254
- diffAnsi: diffAnsiMs,
255
- write: writeMs,
256
- },
257
- timestamp: performance.now(),
258
- };
259
- if (debugHook)
260
- debugHook(stats);
261
- if (frameHandlers.size > 0) {
262
- for (const handler of frameHandlers)
263
- handler(stats);
169
+ catch (err) {
170
+ console.error("[effect-tui] Render error:", err);
171
+ state.markDirty();
264
172
  }
265
173
  };
174
+ // Build renderer object
266
175
  const renderer = {
267
176
  get width() {
268
- return width;
177
+ return state.width;
269
178
  },
270
179
  get height() {
271
- return height;
180
+ return state.height;
272
181
  },
273
182
  requestRender() {
274
- dirty = true;
275
- },
276
- onKey(handler) {
277
- keyHandlers.add(handler);
278
- return () => keyHandlers.delete(handler);
279
- },
280
- onPaste(handler) {
281
- pasteHandlers.add(handler);
282
- return () => pasteHandlers.delete(handler);
283
- },
284
- onResize(handler) {
285
- resizeHandlers.add(handler);
286
- return () => resizeHandlers.delete(handler);
287
- },
288
- onFrameStats(handler) {
289
- frameHandlers.add(handler);
290
- return () => frameHandlers.delete(handler);
183
+ state.markDirty();
291
184
  },
185
+ onKey: (handler) => events.onKey(handler),
186
+ onMouse: (handler) => events.onMouse(handler),
187
+ onPaste: (handler) => events.onPaste(handler),
188
+ onResize: (handler) => events.onResize(handler),
189
+ onFrameStats: (handler) => events.onFrameStats(handler),
292
190
  stop() {
293
- running = false;
294
- if (loop) {
295
- clearInterval(loop);
296
- loop = null;
191
+ state.running = false;
192
+ if (state.loop) {
193
+ clearInterval(state.loop);
194
+ state.loop = null;
195
+ }
196
+ if (state.inputHandler) {
197
+ stdin.removeListener("data", state.inputHandler);
198
+ state.inputHandler = null;
199
+ }
200
+ if (state.resizeHandler) {
201
+ stdout.removeListener("resize", state.resizeHandler);
202
+ state.resizeHandler = null;
297
203
  }
298
- teardown();
204
+ terminal.teardown();
299
205
  },
300
206
  flush() {
301
207
  renderFrame();
302
208
  },
303
- };
304
- // Terminal setup (skip for testing)
305
- if (!skipTerminalSetup) {
306
- if (mode === "fullscreen") {
307
- stdout.write(Terminal.enterFullscreen);
308
- }
309
- // Hide cursor during rendering (both modes)
310
- stdout.write(Terminal.hideCursor);
311
- if (enablePaste)
312
- stdout.write(PASTE_ENABLE);
313
- if (stdin.isTTY && stdin.setRawMode) {
314
- stdin.setRawMode(true);
315
- stdin.resume?.();
316
- }
317
- }
318
- // Handle keyboard input (and bracketed paste)
319
- stdin.on("data", (data) => {
320
- let chunk = data.toString("utf8");
321
- const emitKeys = (str) => {
322
- if (!str)
323
- return;
324
- const keys = decodeKeys(Buffer.from(str, "utf8"));
325
- for (const key of keys) {
326
- const wrapped = {
327
- ...key,
328
- defaultPrevented: false,
329
- preventDefault() {
330
- wrapped.defaultPrevented = true;
331
- },
332
- };
333
- for (const handler of keyHandlers) {
334
- if (wrapped.defaultPrevented)
335
- break;
336
- handler(wrapped);
337
- }
338
- // Default Ctrl+C handling - exit unless user called preventDefault()
339
- if (exitOnCtrlC && !wrapped.defaultPrevented && key.ctrl && key.text === "c") {
340
- process.exit(0);
341
- }
209
+ getScreenshot() {
210
+ // Return the previous buffer as ANSI string (it has the last rendered frame)
211
+ if (state.prevBuffer) {
212
+ return bufferToString(state.prevBuffer, state.palette, state.width, state.height);
342
213
  }
343
- };
344
- while (chunk.length > 0) {
345
- if (pasteActive) {
346
- const endIdx = chunk.indexOf(PASTE_END);
347
- if (endIdx >= 0) {
348
- pasteBuffer += chunk.slice(0, endIdx);
349
- pasteHandlers.forEach((h) => h(pasteBuffer));
350
- pasteBuffer = "";
351
- pasteActive = false;
352
- chunk = chunk.slice(endIdx + PASTE_END.length);
353
- continue;
354
- }
355
- else {
356
- pasteBuffer += chunk;
357
- chunk = "";
358
- break;
359
- }
360
- }
361
- const startIdx = chunk.indexOf(PASTE_START);
362
- if (startIdx >= 0) {
363
- // Emit any keys before the paste start
364
- emitKeys(chunk.slice(0, startIdx));
365
- pasteActive = true;
366
- pasteBuffer = "";
367
- chunk = chunk.slice(startIdx + PASTE_START.length);
368
- continue;
369
- }
370
- // No paste markers; treat as normal keys
371
- emitKeys(chunk);
372
- chunk = "";
373
- }
374
- dirty = true;
375
- });
376
- // Handle resize - render synchronously like Ink does
377
- stdout.on("resize", () => {
214
+ return "";
215
+ },
216
+ dispatchKey(key) {
217
+ events.dispatchKey(key);
218
+ if (!manualMode)
219
+ renderFrame();
220
+ },
221
+ dispatchPaste(text) {
222
+ events.dispatchPaste(text);
223
+ if (!manualMode)
224
+ renderFrame();
225
+ },
226
+ dispatchResize(width, height) {
227
+ state.updateDimensions(width, height);
228
+ state.invalidateBuffers();
229
+ state.markDirty();
230
+ events.dispatchResize(width, height);
231
+ if (!manualMode)
232
+ renderFrame();
233
+ },
234
+ };
235
+ // Terminal setup
236
+ terminal.setup();
237
+ // Input handling
238
+ state.inputHandler = (data) => inputProcessor.process(data);
239
+ stdin.on("data", state.inputHandler);
240
+ // Resize handling
241
+ state.resizeHandler = () => {
378
242
  const newWidth = stdout.columns || 80;
379
243
  const newHeight = stdout.rows || 24;
380
- // Fullscreen: clear entire screen on resize to prevent artifacts from terminal reflow
381
- // The terminal may leave stale content that our diff-based rendering doesn't see
382
- if (mode === "fullscreen") {
383
- stdout.write(ANSI.screen.clear + ANSI.cursor.to(1, 1));
384
- }
385
- else if (mode === "inline" && newWidth < lastWidth && previousHeight > 0) {
386
- // Inline: on width shrink, previous content may have wrapped to MORE lines
387
- // than previousHeight. Clear from content start to END OF SCREEN to catch all.
388
- stdout.write(ANSI.cursor.up(previousHeight) + ANSI.cursor.startOfLine);
389
- stdout.write(ANSI.screen.clearToEnd);
390
- previousHeight = 0;
391
- }
392
- width = newWidth;
393
- height = newHeight;
394
- lastWidth = newWidth;
395
- prevBuffer = null;
396
- nextBuffer = null;
397
- dirty = true;
398
- // Notify resize handlers
399
- for (const handler of resizeHandlers) {
400
- handler(newWidth, newHeight);
401
- }
402
- // Render immediately on resize (like Ink) to prevent cursor corruption
403
- renderFrame();
404
- });
405
- // Automatic render loop (skip in manual mode)
244
+ renderMode.handleResize(newWidth, newHeight, state.lastWidth);
245
+ state.updateDimensions(newWidth, newHeight);
246
+ state.invalidateBuffers();
247
+ state.markDirty();
248
+ events.dispatchResize(newWidth, newHeight);
249
+ };
250
+ stdout.on("resize", state.resizeHandler);
251
+ // Render loop
406
252
  if (!manualMode) {
407
253
  const frameMs = 1000 / fps;
408
- loop = setInterval(() => {
409
- if (!running) {
410
- if (loop)
411
- clearInterval(loop);
412
- teardown();
254
+ state.loop = setInterval(() => {
255
+ if (!state.running) {
256
+ if (state.loop)
257
+ clearInterval(state.loop);
258
+ terminal.teardown();
413
259
  return;
414
260
  }
415
261
  renderFrame();
416
262
  }, frameMs);
417
263
  }
418
- // Store container reference for direct root access
419
264
  ;
420
265
  renderer._container = null;
421
266
  return renderer;
@@ -423,29 +268,19 @@ export function createRenderer(options) {
423
268
  export function createRoot(renderer) {
424
269
  const hostContext = {
425
270
  requestRender: () => renderer.requestRender(),
271
+ requestImmediateRender: () => renderer.flush(),
426
272
  };
427
273
  const container = {
428
274
  root: null,
429
275
  ctx: hostContext,
430
276
  };
431
- const fiberRoot = reconciler.createContainer(container, 0, // LegacyRoot
432
- null, // hydrationCallbacks
433
- false, // isStrictMode
434
- null, // concurrentUpdatesByDefaultOverride
435
- "", // identifierPrefix
436
- (err) => console.error(err), // onUncaughtError
437
- (err) => console.error(err), // onCaughtError
438
- (err) => console.error(err), // onRecoverableError
439
- () => { }, // onDefaultTransitionIndicator
440
- null);
277
+ const fiberRoot = reconciler.createContainer(container, 0, null, false, null, "", (err) => console.error(err), (err) => console.error(err), (err) => console.error(err), () => { }, null);
441
278
  renderer._container = container;
442
- const reconcilerAny = reconciler;
443
- const runSync = reconcilerAny.flushSync?.bind(reconcilerAny) ?? ((fn) => fn());
444
279
  return {
445
280
  render(element, sync = false) {
446
281
  const wrapped = React.createElement(RendererContext.Provider, { value: renderer }, element);
447
282
  if (sync) {
448
- runSync(() => {
283
+ flushSync(() => {
449
284
  reconciler.updateContainer(wrapped, fiberRoot, null, null);
450
285
  });
451
286
  renderer.requestRender();
@@ -463,14 +298,9 @@ export function createRoot(renderer) {
463
298
  },
464
299
  };
465
300
  }
466
- /**
467
- * Render a React tree to the terminal in one call.
468
- * Returns helpers similar to Ink: rerender, unmount, waitUntilExit.
469
- */
470
301
  export function render(element, options) {
471
302
  const renderer = createRenderer(options);
472
303
  const root = createRoot(renderer);
473
- // Initial render (sync to avoid flicker before the loop runs)
474
304
  root.render(element, true);
475
305
  let resolved = false;
476
306
  let resolveExit = null;