@effect-tui/react 0.1.3 → 0.1.4

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 +53 -0
  27. package/dist/src/components/TextInput.d.ts.map +1 -0
  28. package/dist/src/components/TextInput.js +210 -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 +8 -8
  113. package/dist/src/hosts/canvas.d.ts.map +1 -1
  114. package/dist/src/hosts/canvas.js +13 -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 +22 -0
  260. package/dist/src/renderer/modes/InlineRenderer.d.ts.map +1 -0
  261. package/dist/src/renderer/modes/InlineRenderer.js +154 -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 +213 -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 +285 -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 +137 -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 +178 -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 +391 -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,247 @@ 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
+ state.invalidateBuffers();
102
+ container.staticDirty = false;
225
103
  }
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();
104
+ // For inline mode, measure content unconstrained to handle overflow
105
+ let actualContentHeight = frameHeight;
106
+ if (mode === "inline") {
107
+ const size = root.measure(frameWidth, Number.MAX_SAFE_INTEGER);
108
+ actualContentHeight = size.h;
109
+ }
110
+ // Buffer height: content height for inline (to capture all content), terminal height for fullscreen
111
+ const bufferHeight = mode === "inline" ? Math.max(actualContentHeight, frameHeight) : frameHeight;
112
+ // Ensure buffers exist
113
+ state.ensureBuffers(frameWidth, bufferHeight);
114
+ if (!state.nextBuffer)
115
+ return;
116
+ // Build frame (clear, layout, render)
117
+ const timings = frameBuilder.build(root, state.nextBuffer, state.palette, frameWidth, bufferHeight);
118
+ // Generate output
119
+ const t = Prof.startPhase();
120
+ const diffStartMs = performance.now();
121
+ const { output: modeOutput, contentHeight } = renderMode.generateOutput({
122
+ nextBuffer: state.nextBuffer,
123
+ prevBuffer: state.prevBuffer,
124
+ palette: state.palette,
125
+ frameWidth,
126
+ frameHeight,
127
+ contentHeight: actualContentHeight,
128
+ enableDiff,
129
+ stdout,
130
+ });
131
+ // Combine static + dynamic output for atomic write
132
+ let output = staticOutput + modeOutput + state.palette.sgr(0);
133
+ contentH = contentHeight;
134
+ const diffAnsiMs = performance.now() - diffStartMs;
135
+ Prof.endPhase("diff+ansi", t);
136
+ // Write output (single atomic write prevents visual glitches)
137
+ const writeT = Prof.startPhase();
138
+ const writeStart = performance.now();
233
139
  stdout.write(output);
234
- writeMs = performance.now() - t0;
140
+ const writeMs = performance.now() - writeStart;
141
+ Prof.endPhase("write", writeT);
142
+ Prof.endFrame(frameStart);
143
+ const frameMs = performance.now() - frameStartMs;
144
+ // Swap buffers
145
+ state.swapBuffers();
146
+ // Build stats
147
+ const stats = {
148
+ mode,
149
+ width: state.width,
150
+ height: state.height,
151
+ contentHeight: contentH,
152
+ bytes: Buffer.byteLength(output, "utf8"),
153
+ frameMs,
154
+ phases: {
155
+ clear: timings.clear,
156
+ layout: timings.layout,
157
+ render: timings.render,
158
+ diffAnsi: diffAnsiMs,
159
+ write: writeMs,
160
+ },
161
+ timestamp: performance.now(),
162
+ };
163
+ if (debugHook)
164
+ debugHook(stats);
165
+ if (events.hasFrameHandlers)
166
+ events.dispatchFrame(stats);
235
167
  }
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);
168
+ catch (err) {
169
+ console.error("[effect-tui] Render error:", err);
170
+ state.markDirty();
264
171
  }
265
172
  };
173
+ // Build renderer object
266
174
  const renderer = {
267
175
  get width() {
268
- return width;
176
+ return state.width;
269
177
  },
270
178
  get height() {
271
- return height;
179
+ return state.height;
272
180
  },
273
181
  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);
182
+ state.markDirty();
291
183
  },
184
+ onKey: (handler) => events.onKey(handler),
185
+ onMouse: (handler) => events.onMouse(handler),
186
+ onPaste: (handler) => events.onPaste(handler),
187
+ onResize: (handler) => events.onResize(handler),
188
+ onFrameStats: (handler) => events.onFrameStats(handler),
292
189
  stop() {
293
- running = false;
294
- if (loop) {
295
- clearInterval(loop);
296
- loop = null;
190
+ state.running = false;
191
+ if (state.loop) {
192
+ clearInterval(state.loop);
193
+ state.loop = null;
194
+ }
195
+ if (state.inputHandler) {
196
+ stdin.removeListener("data", state.inputHandler);
197
+ state.inputHandler = null;
198
+ }
199
+ if (state.resizeHandler) {
200
+ stdout.removeListener("resize", state.resizeHandler);
201
+ state.resizeHandler = null;
297
202
  }
298
- teardown();
203
+ terminal.teardown();
299
204
  },
300
205
  flush() {
301
206
  renderFrame();
302
207
  },
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
- }
208
+ getScreenshot() {
209
+ // Return the previous buffer as ANSI string (it has the last rendered frame)
210
+ if (state.prevBuffer) {
211
+ return bufferToString(state.prevBuffer, state.palette, state.width, state.height);
342
212
  }
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", () => {
213
+ return "";
214
+ },
215
+ dispatchKey(key) {
216
+ events.dispatchKey(key);
217
+ if (!manualMode)
218
+ renderFrame();
219
+ },
220
+ dispatchPaste(text) {
221
+ events.dispatchPaste(text);
222
+ if (!manualMode)
223
+ renderFrame();
224
+ },
225
+ dispatchResize(width, height) {
226
+ state.updateDimensions(width, height);
227
+ state.invalidateBuffers();
228
+ state.markDirty();
229
+ events.dispatchResize(width, height);
230
+ if (!manualMode)
231
+ renderFrame();
232
+ },
233
+ };
234
+ // Terminal setup
235
+ terminal.setup();
236
+ // Input handling
237
+ state.inputHandler = (data) => inputProcessor.process(data);
238
+ stdin.on("data", state.inputHandler);
239
+ // Resize handling
240
+ state.resizeHandler = () => {
378
241
  const newWidth = stdout.columns || 80;
379
242
  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)
243
+ renderMode.handleResize(newWidth, newHeight, state.lastWidth);
244
+ state.updateDimensions(newWidth, newHeight);
245
+ state.invalidateBuffers();
246
+ state.markDirty();
247
+ events.dispatchResize(newWidth, newHeight);
248
+ };
249
+ stdout.on("resize", state.resizeHandler);
250
+ // Render loop
406
251
  if (!manualMode) {
407
252
  const frameMs = 1000 / fps;
408
- loop = setInterval(() => {
409
- if (!running) {
410
- if (loop)
411
- clearInterval(loop);
412
- teardown();
253
+ state.loop = setInterval(() => {
254
+ if (!state.running) {
255
+ if (state.loop)
256
+ clearInterval(state.loop);
257
+ terminal.teardown();
413
258
  return;
414
259
  }
415
260
  renderFrame();
416
261
  }, frameMs);
417
262
  }
418
- // Store container reference for direct root access
419
263
  ;
420
264
  renderer._container = null;
421
265
  return renderer;
@@ -423,29 +267,19 @@ export function createRenderer(options) {
423
267
  export function createRoot(renderer) {
424
268
  const hostContext = {
425
269
  requestRender: () => renderer.requestRender(),
270
+ requestImmediateRender: () => renderer.flush(),
426
271
  };
427
272
  const container = {
428
273
  root: null,
429
274
  ctx: hostContext,
430
275
  };
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);
276
+ const fiberRoot = reconciler.createContainer(container, 0, null, false, null, "", (err) => console.error(err), (err) => console.error(err), (err) => console.error(err), () => { }, null);
441
277
  renderer._container = container;
442
- const reconcilerAny = reconciler;
443
- const runSync = reconcilerAny.flushSync?.bind(reconcilerAny) ?? ((fn) => fn());
444
278
  return {
445
279
  render(element, sync = false) {
446
280
  const wrapped = React.createElement(RendererContext.Provider, { value: renderer }, element);
447
281
  if (sync) {
448
- runSync(() => {
282
+ flushSync(() => {
449
283
  reconciler.updateContainer(wrapped, fiberRoot, null, null);
450
284
  });
451
285
  renderer.requestRender();
@@ -463,14 +297,9 @@ export function createRoot(renderer) {
463
297
  },
464
298
  };
465
299
  }
466
- /**
467
- * Render a React tree to the terminal in one call.
468
- * Returns helpers similar to Ink: rerender, unmount, waitUntilExit.
469
- */
470
300
  export function render(element, options) {
471
301
  const renderer = createRenderer(options);
472
302
  const root = createRoot(renderer);
473
- // Initial render (sync to avoid flicker before the loop runs)
474
303
  root.render(element, true);
475
304
  let resolved = false;
476
305
  let resolveExit = null;