@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
@@ -0,0 +1,129 @@
1
+ // useConsole hook - manages console visibility and toggle keybinding
2
+ // Uses ` (backtick) for toggle, ~ (tilde) for screenshot
3
+ // Auto-show on error
4
+
5
+ import { useState, useEffect, useCallback, useMemo } from "react"
6
+ import { getConsoleCapture, type LogEntry } from "./ConsoleCapture.js"
7
+ import { useKeyboard } from "../hooks/use-keyboard.js"
8
+
9
+ // ─────────────────────────────────────────────────────────────
10
+ // Types
11
+ // ─────────────────────────────────────────────────────────────
12
+
13
+ export interface UseConsoleOptions {
14
+ /** Auto-show console on console.error (default: true) */
15
+ autoShowOnError?: boolean
16
+ /** Start with console visible (default: false) */
17
+ initiallyVisible?: boolean
18
+ }
19
+
20
+ export interface UseConsoleReturn {
21
+ /** Whether the console is currently visible */
22
+ visible: boolean
23
+ /** Set visibility directly */
24
+ setVisible: (visible: boolean) => void
25
+ /** Toggle visibility */
26
+ toggle: () => void
27
+ /** Current log entries */
28
+ entries: LogEntry[]
29
+ /** Clear all log entries */
30
+ clear: () => void
31
+ }
32
+
33
+ // ─────────────────────────────────────────────────────────────
34
+ // Hook
35
+ // ─────────────────────────────────────────────────────────────
36
+
37
+ /**
38
+ * Hook for managing the debug console.
39
+ *
40
+ * Features:
41
+ * - ` (backtick) to toggle visibility
42
+ * - ~ (tilde) to take screenshot
43
+ * - Auto-show on console.error (configurable)
44
+ * - Access to log entries and clear function
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * function App() {
49
+ * const { visible } = useConsole({ autoShowOnError: true })
50
+ *
51
+ * return (
52
+ * <zstack>
53
+ * <MainContent />
54
+ * {visible && <ConsolePopover position="bottom" />}
55
+ * </zstack>
56
+ * )
57
+ * }
58
+ * ```
59
+ */
60
+ export function useConsole(options: UseConsoleOptions = {}): UseConsoleReturn {
61
+ const { autoShowOnError = true, initiallyVisible = false } = options
62
+
63
+ const capture = useMemo(() => getConsoleCapture(), [])
64
+
65
+ const [visible, setVisible] = useState(initiallyVisible)
66
+ const [entries, setEntries] = useState<LogEntry[]>(() => capture.getEntries())
67
+
68
+ // Activate capture on mount
69
+ useEffect(() => {
70
+ if (!capture.isActive) {
71
+ capture.activate()
72
+ }
73
+
74
+ return () => {
75
+ // Don't deactivate on unmount - other components may still use it
76
+ // The singleton handles cleanup on process exit
77
+ }
78
+ }, [capture])
79
+
80
+ // Subscribe to entries and errors
81
+ useEffect(() => {
82
+ const handleEntry = (_entry: LogEntry) => {
83
+ setEntries(capture.getEntries())
84
+ }
85
+
86
+ const handleError = (_entry: LogEntry) => {
87
+ if (autoShowOnError) {
88
+ setVisible(true)
89
+ }
90
+ }
91
+
92
+ capture.on("entry", handleEntry)
93
+ capture.on("error", handleError)
94
+
95
+ return () => {
96
+ capture.off("entry", handleEntry)
97
+ capture.off("error", handleError)
98
+ }
99
+ }, [capture, autoShowOnError])
100
+
101
+ // Toggle function
102
+ const toggle = useCallback(() => {
103
+ setVisible((v) => !v)
104
+ }, [])
105
+
106
+ // Clear function
107
+ const clear = useCallback(() => {
108
+ capture.clear()
109
+ setEntries([])
110
+ }, [capture])
111
+
112
+ // Keyboard handler for ` toggle
113
+ // No useCallback needed - useKeyboard uses refs internally
114
+ useKeyboard((key) => {
115
+ // ` (backtick) - toggle console
116
+ if (key.name === "char" && key.text === "`") {
117
+ toggle()
118
+ key.preventDefault?.()
119
+ }
120
+ })
121
+
122
+ return {
123
+ visible,
124
+ setVisible,
125
+ toggle,
126
+ entries,
127
+ clear,
128
+ }
129
+ }
@@ -0,0 +1,557 @@
1
+ // Integrated debug overlay for all TUI apps
2
+ // Provides console log viewer with Ctrl+Shift+D toggle and Ctrl+Shift+S screenshot
3
+
4
+ import type { CellBuffer, Palette, KeyMsg, MouseMsg } from "@effect-tui/core"
5
+ import { getConsoleCapture, type LogLevel } from "../console/ConsoleCapture.js"
6
+ import { copyToClipboardSync, copyToClipboard } from "../console/clipboard.js"
7
+
8
+ // ─────────────────────────────────────────────────────────────
9
+ // Types
10
+ // ─────────────────────────────────────────────────────────────
11
+
12
+ export interface DebugOverlayOptions {
13
+ /** Initial height as percentage of terminal (default: 30) */
14
+ heightPercent?: number
15
+ /** Minimum height percent (default: 10) */
16
+ minHeightPercent?: number
17
+ /** Maximum height percent (default: 80) */
18
+ maxHeightPercent?: number
19
+ /** Show file locations in log entries (default: true) */
20
+ showLocations?: boolean
21
+ }
22
+
23
+ interface SelectionPoint {
24
+ line: number
25
+ col: number
26
+ }
27
+
28
+ // ─────────────────────────────────────────────────────────────
29
+ // Color constants (8-bit palette indices for performance)
30
+ // ─────────────────────────────────────────────────────────────
31
+
32
+ const TITLE_BG = 236 // gray(4)
33
+ const TITLE_FG = 15 // white
34
+ const CONTENT_BG = 234 // gray(2)
35
+ const SELECTION_BG_R = 60
36
+ const SELECTION_BG_G = 90
37
+ const SELECTION_BG_B = 140
38
+
39
+ const LOG_COLORS: Record<LogLevel, number> = {
40
+ LOG: 15, // white
41
+ INFO: 14, // cyan
42
+ WARN: 11, // yellow
43
+ ERROR: 9, // red
44
+ DEBUG: 250, // gray(12)
45
+ }
46
+
47
+ // ─────────────────────────────────────────────────────────────
48
+ // Helpers
49
+ // ─────────────────────────────────────────────────────────────
50
+
51
+ function formatTimestamp(date: Date): string {
52
+ const h = date.getHours().toString().padStart(2, "0")
53
+ const m = date.getMinutes().toString().padStart(2, "0")
54
+ const s = date.getSeconds().toString().padStart(2, "0")
55
+ return `${h}:${m}:${s}`
56
+ }
57
+
58
+ function formatLocation(loc?: { file: string; line: number }): string {
59
+ if (!loc) return ""
60
+ const parts = loc.file.split("/")
61
+ const filename = parts[parts.length - 1]
62
+ return `${filename}:${loc.line}`
63
+ }
64
+
65
+ function wrapLine(text: string, maxWidth: number): string[] {
66
+ if (text.length <= maxWidth) return [text]
67
+
68
+ const lines: string[] = []
69
+ let remaining = text
70
+
71
+ while (remaining.length > 0) {
72
+ if (remaining.length <= maxWidth) {
73
+ lines.push(remaining)
74
+ break
75
+ }
76
+
77
+ let breakAt = maxWidth
78
+ const lastSpace = remaining.lastIndexOf(" ", maxWidth)
79
+ if (lastSpace > maxWidth * 0.5) {
80
+ breakAt = lastSpace
81
+ }
82
+
83
+ lines.push(remaining.slice(0, breakAt))
84
+ remaining = remaining.slice(breakAt).trimStart()
85
+ }
86
+
87
+ return lines
88
+ }
89
+
90
+ function normalizeSelection(anchor: SelectionPoint, head: SelectionPoint): [SelectionPoint, SelectionPoint] {
91
+ if (anchor.line < head.line || (anchor.line === head.line && anchor.col <= head.col)) {
92
+ return [anchor, head]
93
+ }
94
+ return [head, anchor]
95
+ }
96
+
97
+ // ─────────────────────────────────────────────────────────────
98
+ // Debug Overlay Class
99
+ // ─────────────────────────────────────────────────────────────
100
+
101
+ interface DisplayLine {
102
+ text: string
103
+ level: LogLevel
104
+ }
105
+
106
+ export class DebugOverlay {
107
+ private _visible = false
108
+ private sizePercent: number
109
+ private minSizePercent: number
110
+ private maxSizePercent: number
111
+ private showLocations: boolean
112
+
113
+ // Scroll state
114
+ private scrollOffset = 0
115
+ private wasAtEnd = true
116
+
117
+ // Selection state
118
+ private selectionAnchor: SelectionPoint | null = null
119
+ private selectionHead: SelectionPoint | null = null
120
+ private isSelecting = false
121
+
122
+ // Feedback message
123
+ private feedback: string | null = null
124
+ private feedbackTimeout: ReturnType<typeof setTimeout> | null = null
125
+
126
+ // Cached display lines
127
+ private displayLines: DisplayLine[] = []
128
+ private lastEntriesLength = 0
129
+ private lastWidth = 0
130
+
131
+ // Screenshot callback
132
+ private getScreenshot: (() => string) | null = null
133
+
134
+ constructor(options: DebugOverlayOptions = {}) {
135
+ this.sizePercent = options.heightPercent ?? 30
136
+ this.minSizePercent = options.minHeightPercent ?? 10
137
+ this.maxSizePercent = options.maxHeightPercent ?? 80
138
+ this.showLocations = options.showLocations ?? true
139
+ }
140
+
141
+ get visible(): boolean {
142
+ return this._visible
143
+ }
144
+
145
+ show(): void {
146
+ this._visible = true
147
+ }
148
+
149
+ hide(): void {
150
+ this._visible = false
151
+ }
152
+
153
+ toggle(): void {
154
+ this._visible = !this._visible
155
+ }
156
+
157
+ setScreenshotFn(fn: () => string): void {
158
+ this.getScreenshot = fn
159
+ }
160
+
161
+ // ─────────────────────────────────────────────────────────────
162
+ // Key handling - returns true if event was consumed
163
+ // ─────────────────────────────────────────────────────────────
164
+
165
+ handleKey(key: KeyMsg, width: number, height: number): boolean {
166
+ // Ctrl+Shift+D - toggle overlay (handled even when hidden)
167
+ if (key.ctrl && key.shift && key.name === "char" && key.text === "d") {
168
+ this.toggle()
169
+ return true
170
+ }
171
+
172
+ // Ctrl+Shift+S - screenshot (handled even when hidden)
173
+ if (key.ctrl && key.shift && key.name === "char" && key.text === "s") {
174
+ this.takeScreenshot()
175
+ return true
176
+ }
177
+
178
+ // Rest only applies when visible
179
+ if (!this._visible) return false
180
+
181
+ // Ctrl+Y or Ctrl+C - copy selection
182
+ if (key.ctrl && !key.shift && key.name === "char" && (key.text === "y" || key.text === "c")) {
183
+ this.copySelection()
184
+ return true
185
+ }
186
+
187
+ // Ctrl+S - save logs
188
+ if (key.ctrl && !key.shift && key.name === "char" && key.text === "s") {
189
+ this.saveLogs()
190
+ return true
191
+ }
192
+
193
+ // Size controls
194
+ if (key.name === "char" && (key.text === "+" || key.text === "=")) {
195
+ this.sizePercent = Math.min(this.maxSizePercent, this.sizePercent + 5)
196
+ return true
197
+ }
198
+ if (key.name === "char" && key.text === "-") {
199
+ this.sizePercent = Math.max(this.minSizePercent, this.sizePercent - 5)
200
+ return true
201
+ }
202
+
203
+ const viewportHeight = this.getViewportHeight(height)
204
+ const maxScroll = Math.max(0, this.displayLines.length - viewportHeight)
205
+
206
+ // Shift+up/down - jump to top/bottom
207
+ if (key.shift && key.name === "up") {
208
+ this.scrollOffset = 0
209
+ this.wasAtEnd = false
210
+ return true
211
+ }
212
+ if (key.shift && key.name === "down") {
213
+ this.scrollOffset = maxScroll
214
+ this.wasAtEnd = true
215
+ return true
216
+ }
217
+
218
+ // Regular up/down - scroll
219
+ if (key.name === "up") {
220
+ this.scrollOffset = Math.max(0, this.scrollOffset - 1)
221
+ this.wasAtEnd = this.scrollOffset >= maxScroll
222
+ return true
223
+ }
224
+ if (key.name === "down") {
225
+ this.scrollOffset = Math.min(maxScroll, this.scrollOffset + 1)
226
+ this.wasAtEnd = this.scrollOffset >= maxScroll
227
+ return true
228
+ }
229
+
230
+ // Home/End
231
+ if (key.name === "home") {
232
+ this.scrollOffset = 0
233
+ this.wasAtEnd = false
234
+ return true
235
+ }
236
+ if (key.name === "end") {
237
+ this.scrollOffset = maxScroll
238
+ this.wasAtEnd = true
239
+ return true
240
+ }
241
+
242
+ return false
243
+ }
244
+
245
+ // ─────────────────────────────────────────────────────────────
246
+ // Mouse handling - returns true if event was consumed
247
+ // ─────────────────────────────────────────────────────────────
248
+
249
+ handleMouse(mouse: MouseMsg, width: number, height: number): boolean {
250
+ if (!this._visible) return false
251
+
252
+ const popoverHeight = this.getPopoverHeight(height)
253
+ const contentStartY = height - popoverHeight + 1
254
+
255
+ // Check if mouse is in overlay area
256
+ if (mouse.y < height - popoverHeight || mouse.y >= height) return false
257
+
258
+ // Mouse wheel scroll
259
+ if (mouse.button === "scroll-up" || mouse.button === "scroll-down") {
260
+ const viewportHeight = this.getViewportHeight(height)
261
+ const maxScroll = Math.max(0, this.displayLines.length - viewportHeight)
262
+ const delta = mouse.button === "scroll-up" ? -3 : 3
263
+ this.scrollOffset = Math.max(0, Math.min(maxScroll, this.scrollOffset + delta))
264
+ this.wasAtEnd = this.scrollOffset >= maxScroll
265
+ return true
266
+ }
267
+
268
+ // Text selection
269
+ if (mouse.button === "left") {
270
+ const relativeY = mouse.y - contentStartY
271
+ const lineIndex = Math.floor(relativeY + this.scrollOffset)
272
+ const col = Math.max(0, mouse.x)
273
+
274
+ if (lineIndex < 0 || lineIndex >= this.displayLines.length) return true
275
+
276
+ if (mouse.action === "press") {
277
+ this.selectionAnchor = { line: lineIndex, col }
278
+ this.selectionHead = { line: lineIndex, col }
279
+ this.isSelecting = true
280
+ } else if (mouse.action === "drag" && this.isSelecting) {
281
+ this.selectionHead = { line: lineIndex, col }
282
+
283
+ // Auto-scroll at edges
284
+ const viewportHeight = this.getViewportHeight(height)
285
+ if (relativeY <= 1) {
286
+ this.scrollOffset = Math.max(0, this.scrollOffset - 1)
287
+ } else if (relativeY >= viewportHeight - 2) {
288
+ const maxScroll = Math.max(0, this.displayLines.length - viewportHeight)
289
+ this.scrollOffset = Math.min(maxScroll, this.scrollOffset + 1)
290
+ }
291
+ } else if (mouse.action === "release") {
292
+ this.isSelecting = false
293
+ }
294
+ return true
295
+ }
296
+
297
+ return true // Consume all mouse events in overlay area
298
+ }
299
+
300
+ // ─────────────────────────────────────────────────────────────
301
+ // Rendering
302
+ // ─────────────────────────────────────────────────────────────
303
+
304
+ render(buffer: CellBuffer, palette: Palette, width: number, height: number): void {
305
+ if (!this._visible) return
306
+
307
+ const popoverHeight = this.getPopoverHeight(height)
308
+ const viewportHeight = this.getViewportHeight(height)
309
+ const startY = height - popoverHeight
310
+
311
+ // Update display lines if needed
312
+ this.updateDisplayLines(width)
313
+
314
+ // Auto-scroll to end if sticky
315
+ if (this.wasAtEnd) {
316
+ this.scrollOffset = Math.max(0, this.displayLines.length - viewportHeight)
317
+ }
318
+
319
+ // Title bar styles
320
+ const titleStyle = palette.id({ fg: TITLE_FG, bg: TITLE_BG })
321
+ const hintStyle = palette.id({ fg: this.feedback ? 10 : 250, bg: TITLE_BG })
322
+
323
+ // Draw title bar
324
+ const titleText = this.feedback ?? "Console"
325
+ const hints = `[^Y copy] [^S save] [^⇧S screenshot] [+/- ${this.sizePercent}%]`
326
+
327
+ buffer.fillRect(0, startY, width, 1, 32, titleStyle)
328
+ buffer.drawText(1, startY, ` ${titleText}`, titleStyle)
329
+ buffer.drawText(width - hints.length - 1, startY, hints, hintStyle)
330
+
331
+ // Content area
332
+ const contentStyle = palette.id({ bg: CONTENT_BG })
333
+ buffer.fillRect(0, startY + 1, width, viewportHeight, 32, contentStyle)
334
+
335
+ // Draw log lines
336
+ const contentStartY = startY + 1
337
+ for (let i = 0; i < viewportHeight; i++) {
338
+ const lineIdx = this.scrollOffset + i
339
+ if (lineIdx >= this.displayLines.length) break
340
+
341
+ const line = this.displayLines[lineIdx]
342
+ const y = contentStartY + i
343
+ const paddedText = line.text.padEnd(width - 1, " ")
344
+
345
+ // Check for selection
346
+ const sel = this.getLineSelection(lineIdx, paddedText.length)
347
+
348
+ if (sel) {
349
+ // Render with selection highlight
350
+ const before = paddedText.slice(0, sel.startCol)
351
+ const selected = paddedText.slice(sel.startCol, sel.endCol)
352
+ const after = paddedText.slice(sel.endCol)
353
+
354
+ const normalStyle = palette.id({ fg: LOG_COLORS[line.level], bg: CONTENT_BG })
355
+ const selStyle = palette.id({ fg: 15, bg: { r: SELECTION_BG_R, g: SELECTION_BG_G, b: SELECTION_BG_B } })
356
+
357
+ let x = 0
358
+ if (before.length > 0) {
359
+ buffer.drawText(x, y, before, normalStyle)
360
+ x += before.length
361
+ }
362
+ buffer.drawText(x, y, selected, selStyle)
363
+ x += selected.length
364
+ if (after.length > 0) {
365
+ buffer.drawText(x, y, after, normalStyle)
366
+ }
367
+ } else {
368
+ const style = palette.id({ fg: LOG_COLORS[line.level], bg: CONTENT_BG })
369
+ buffer.drawText(0, y, paddedText, style)
370
+ }
371
+ }
372
+
373
+ // Draw scrollbar if needed
374
+ if (this.displayLines.length > viewportHeight) {
375
+ this.drawScrollbar(buffer, palette, width, contentStartY, viewportHeight)
376
+ }
377
+ }
378
+
379
+ // ─────────────────────────────────────────────────────────────
380
+ // Private helpers
381
+ // ─────────────────────────────────────────────────────────────
382
+
383
+ private getPopoverHeight(termHeight: number): number {
384
+ return Math.max(5, Math.floor(termHeight * (this.sizePercent / 100)))
385
+ }
386
+
387
+ private getViewportHeight(termHeight: number): number {
388
+ return this.getPopoverHeight(termHeight) - 1 // -1 for title bar
389
+ }
390
+
391
+ private updateDisplayLines(width: number): void {
392
+ const capture = getConsoleCapture()
393
+ const entries = capture.getEntries()
394
+
395
+ // Only rebuild if entries changed or width changed
396
+ if (entries.length === this.lastEntriesLength && width === this.lastWidth) {
397
+ return
398
+ }
399
+
400
+ this.lastEntriesLength = entries.length
401
+ this.lastWidth = width
402
+ this.displayLines = []
403
+
404
+ const availableWidth = width - 1 // Leave room for scrollbar
405
+
406
+ for (const entry of entries) {
407
+ const timestamp = formatTimestamp(entry.timestamp)
408
+ const location = this.showLocations ? formatLocation(entry.location) : ""
409
+
410
+ const prefix = `[${timestamp}] [${entry.level}] `
411
+ const suffix = location ? ` (${location})` : ""
412
+
413
+ const messageLines = entry.message.split("\n")
414
+
415
+ for (let j = 0; j < messageLines.length; j++) {
416
+ const msgLine = messageLines[j]
417
+ const isFirstLine = j === 0
418
+ const isLastLine = j === messageLines.length - 1
419
+
420
+ const linePrefix = isFirstLine ? prefix : " "
421
+ const lineSuffix = isLastLine ? suffix : ""
422
+
423
+ const fullLine = linePrefix + msgLine + lineSuffix
424
+ const wrappedLines = wrapLine(fullLine, availableWidth)
425
+
426
+ for (const wrappedLine of wrappedLines) {
427
+ this.displayLines.push({
428
+ text: wrappedLine,
429
+ level: entry.level,
430
+ })
431
+ }
432
+ }
433
+ }
434
+ }
435
+
436
+ private getLineSelection(
437
+ lineIndex: number,
438
+ lineLength: number,
439
+ ): { startCol: number; endCol: number } | null {
440
+ if (!this.selectionAnchor || !this.selectionHead) return null
441
+
442
+ const [start, end] = normalizeSelection(this.selectionAnchor, this.selectionHead)
443
+
444
+ if (lineIndex < start.line || lineIndex > end.line) return null
445
+
446
+ const startCol = lineIndex === start.line ? Math.min(start.col, lineLength) : 0
447
+ const endCol = lineIndex === end.line ? Math.min(end.col, lineLength) : lineLength
448
+
449
+ if (startCol >= endCol && lineIndex === start.line && lineIndex === end.line) return null
450
+
451
+ return { startCol, endCol }
452
+ }
453
+
454
+ private drawScrollbar(
455
+ buffer: CellBuffer,
456
+ palette: Palette,
457
+ width: number,
458
+ contentStartY: number,
459
+ viewportHeight: number,
460
+ ): void {
461
+ const maxScroll = this.displayLines.length - viewportHeight
462
+ const scrollRatio = maxScroll > 0 ? this.scrollOffset / maxScroll : 0
463
+ const thumbRatio = viewportHeight / this.displayLines.length
464
+ const thumbHeight = Math.max(1, Math.floor(viewportHeight * thumbRatio))
465
+ const thumbY = Math.floor((viewportHeight - thumbHeight) * scrollRatio)
466
+
467
+ const trackStyle = palette.id({ fg: 8 })
468
+ const thumbStyle = palette.id({ fg: 7 })
469
+
470
+ for (let i = 0; i < viewportHeight; i++) {
471
+ const isThumb = i >= thumbY && i < thumbY + thumbHeight
472
+ const char = isThumb ? "┃" : "│"
473
+ const style = isThumb ? thumbStyle : trackStyle
474
+ buffer.drawText(width - 1, contentStartY + i, char, style)
475
+ }
476
+ }
477
+
478
+ private getSelectedText(): string {
479
+ if (!this.selectionAnchor || !this.selectionHead) return ""
480
+
481
+ const [start, end] = normalizeSelection(this.selectionAnchor, this.selectionHead)
482
+ const lines: string[] = []
483
+
484
+ for (let i = start.line; i <= end.line && i < this.displayLines.length; i++) {
485
+ const lineText = this.displayLines[i]?.text ?? ""
486
+ const startCol = i === start.line ? Math.min(start.col, lineText.length) : 0
487
+ const endCol = i === end.line ? Math.min(end.col, lineText.length) : lineText.length
488
+ lines.push(lineText.slice(startCol, endCol))
489
+ }
490
+
491
+ return lines.join("\n")
492
+ }
493
+
494
+ private copySelection(): void {
495
+ const text = this.getSelectedText()
496
+ if (!text) return
497
+
498
+ const copied = copyToClipboardSync(text)
499
+ if (!copied) {
500
+ copyToClipboard(text)
501
+ }
502
+
503
+ this.showFeedback("Copied!")
504
+ }
505
+
506
+ private async saveLogs(): Promise<void> {
507
+ const allText = this.displayLines.map((l) => l.text).join("\n")
508
+ const tmpPath = `/tmp/console-logs-${Date.now()}.txt`
509
+
510
+ try {
511
+ await Bun.write(tmpPath, allText)
512
+ const copied = copyToClipboardSync(tmpPath)
513
+ if (!copied) {
514
+ await copyToClipboard(tmpPath)
515
+ }
516
+ this.showFeedback(`Saved: ${tmpPath}`)
517
+ } catch {
518
+ this.showFeedback("Save failed")
519
+ }
520
+ }
521
+
522
+ private async takeScreenshot(): Promise<void> {
523
+ if (!this.getScreenshot) {
524
+ this.showFeedback("Screenshot not available")
525
+ return
526
+ }
527
+
528
+ const screenshot = this.getScreenshot()
529
+ const tmpPath = `/tmp/tui-screenshot-${Date.now()}.txt`
530
+
531
+ try {
532
+ await Bun.write(tmpPath, screenshot)
533
+ const copied = copyToClipboardSync(tmpPath)
534
+ if (!copied) {
535
+ await copyToClipboard(tmpPath)
536
+ }
537
+ this.showFeedback(`Screenshot: ${tmpPath}`)
538
+ // Also show the console so user sees the feedback
539
+ if (!this._visible) {
540
+ this._visible = true
541
+ }
542
+ } catch {
543
+ this.showFeedback("Screenshot failed")
544
+ }
545
+ }
546
+
547
+ private showFeedback(message: string, durationMs = 2000): void {
548
+ if (this.feedbackTimeout) {
549
+ clearTimeout(this.feedbackTimeout)
550
+ }
551
+ this.feedback = message
552
+ this.feedbackTimeout = setTimeout(() => {
553
+ this.feedback = null
554
+ this.feedbackTimeout = null
555
+ }, durationMs)
556
+ }
557
+ }