@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.
- package/dist/jsx-runtime.d.ts +13 -0
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/src/codeblock.d.ts.map +1 -1
- package/dist/src/codeblock.js.map +1 -1
- package/dist/src/components/Divider.d.ts +18 -0
- package/dist/src/components/Divider.d.ts.map +1 -0
- package/dist/src/components/Divider.js +17 -0
- package/dist/src/components/Divider.js.map +1 -0
- package/dist/src/components/Markdown.d.ts +66 -0
- package/dist/src/components/Markdown.d.ts.map +1 -0
- package/dist/src/components/Markdown.js +226 -0
- package/dist/src/components/Markdown.js.map +1 -0
- package/dist/src/components/MultilineTextInput.d.ts +65 -0
- package/dist/src/components/MultilineTextInput.d.ts.map +1 -0
- package/dist/src/components/MultilineTextInput.js +607 -0
- package/dist/src/components/MultilineTextInput.js.map +1 -0
- package/dist/src/components/Overlay.d.ts +46 -0
- package/dist/src/components/Overlay.d.ts.map +1 -0
- package/dist/src/components/Overlay.js +11 -0
- package/dist/src/components/Overlay.js.map +1 -0
- package/dist/src/components/Static.d.ts +44 -0
- package/dist/src/components/Static.d.ts.map +1 -0
- package/dist/src/components/Static.js +53 -0
- package/dist/src/components/Static.js.map +1 -0
- package/dist/src/components/TextInput.d.ts +53 -0
- package/dist/src/components/TextInput.d.ts.map +1 -0
- package/dist/src/components/TextInput.js +210 -0
- package/dist/src/components/TextInput.js.map +1 -0
- package/dist/src/components/index.d.ts +7 -0
- package/dist/src/components/index.d.ts.map +1 -0
- package/dist/src/components/index.js +7 -0
- package/dist/src/components/index.js.map +1 -0
- package/dist/src/components/text-editing.d.ts +62 -0
- package/dist/src/components/text-editing.d.ts.map +1 -0
- package/dist/src/components/text-editing.js +385 -0
- package/dist/src/components/text-editing.js.map +1 -0
- package/dist/src/console/ConsoleCapture.d.ts +36 -0
- package/dist/src/console/ConsoleCapture.d.ts.map +1 -0
- package/dist/src/console/ConsoleCapture.js +210 -0
- package/dist/src/console/ConsoleCapture.js.map +1 -0
- package/dist/src/console/ConsolePopover.d.ts +18 -0
- package/dist/src/console/ConsolePopover.d.ts.map +1 -0
- package/dist/src/console/ConsolePopover.js +324 -0
- package/dist/src/console/ConsolePopover.js.map +1 -0
- package/dist/src/console/clipboard.d.ts +10 -0
- package/dist/src/console/clipboard.d.ts.map +1 -0
- package/dist/src/console/clipboard.js +74 -0
- package/dist/src/console/clipboard.js.map +1 -0
- package/dist/src/console/index.d.ts +5 -0
- package/dist/src/console/index.d.ts.map +1 -0
- package/dist/src/console/index.js +33 -0
- package/dist/src/console/index.js.map +1 -0
- package/dist/src/console/useConsole.d.ts +44 -0
- package/dist/src/console/useConsole.d.ts.map +1 -0
- package/dist/src/console/useConsole.js +91 -0
- package/dist/src/console/useConsole.js.map +1 -0
- package/dist/src/debug/DebugOverlay.d.ts +49 -0
- package/dist/src/debug/DebugOverlay.d.ts.map +1 -0
- package/dist/src/debug/DebugOverlay.js +438 -0
- package/dist/src/debug/DebugOverlay.js.map +1 -0
- package/dist/src/debug/DiagnosticsPanel.d.ts.map +1 -1
- package/dist/src/debug/DiagnosticsPanel.js.map +1 -1
- package/dist/src/dev/Toast.d.ts +19 -0
- package/dist/src/dev/Toast.d.ts.map +1 -0
- package/dist/src/dev/Toast.js +72 -0
- package/dist/src/dev/Toast.js.map +1 -0
- package/dist/src/dev/index.d.ts +2 -0
- package/dist/src/dev/index.d.ts.map +1 -0
- package/dist/src/dev/index.js +3 -0
- package/dist/src/dev/index.js.map +1 -0
- package/dist/src/dev.d.ts +114 -0
- package/dist/src/dev.d.ts.map +1 -0
- package/dist/src/dev.js +373 -0
- package/dist/src/dev.js.map +1 -0
- package/dist/src/highlight.d.ts +3 -3
- package/dist/src/highlight.d.ts.map +1 -1
- package/dist/src/highlight.js.map +1 -1
- package/dist/src/hmr-plugin.d.ts +2 -0
- package/dist/src/hmr-plugin.d.ts.map +1 -0
- package/dist/src/hmr-plugin.js +53 -0
- package/dist/src/hmr-plugin.js.map +1 -0
- package/dist/src/hooks/index.d.ts +4 -0
- package/dist/src/hooks/index.d.ts.map +1 -1
- package/dist/src/hooks/index.js +2 -0
- package/dist/src/hooks/index.js.map +1 -1
- package/dist/src/hooks/use-keyboard.d.ts +11 -0
- package/dist/src/hooks/use-keyboard.d.ts.map +1 -1
- package/dist/src/hooks/use-keyboard.js +22 -4
- package/dist/src/hooks/use-keyboard.js.map +1 -1
- package/dist/src/hooks/use-mouse.d.ts +24 -0
- package/dist/src/hooks/use-mouse.d.ts.map +1 -0
- package/dist/src/hooks/use-mouse.js +41 -0
- package/dist/src/hooks/use-mouse.js.map +1 -0
- package/dist/src/hooks/use-paste.d.ts +11 -0
- package/dist/src/hooks/use-paste.d.ts.map +1 -1
- package/dist/src/hooks/use-paste.js +17 -3
- package/dist/src/hooks/use-paste.js.map +1 -1
- package/dist/src/hooks/use-scroll.d.ts +79 -0
- package/dist/src/hooks/use-scroll.d.ts.map +1 -0
- package/dist/src/hooks/use-scroll.js +239 -0
- package/dist/src/hooks/use-scroll.js.map +1 -0
- package/dist/src/hooks/useFrameStats.js.map +1 -1
- package/dist/src/hosts/base.d.ts +62 -1
- package/dist/src/hosts/base.d.ts.map +1 -1
- package/dist/src/hosts/base.js +118 -5
- package/dist/src/hosts/base.js.map +1 -1
- package/dist/src/hosts/box.d.ts +7 -7
- package/dist/src/hosts/box.d.ts.map +1 -1
- package/dist/src/hosts/box.js +30 -23
- package/dist/src/hosts/box.js.map +1 -1
- package/dist/src/hosts/canvas.d.ts +8 -8
- package/dist/src/hosts/canvas.d.ts.map +1 -1
- package/dist/src/hosts/canvas.js +13 -22
- package/dist/src/hosts/canvas.js.map +1 -1
- package/dist/src/hosts/codeblock.d.ts +7 -7
- package/dist/src/hosts/codeblock.d.ts.map +1 -1
- package/dist/src/hosts/codeblock.js +11 -20
- package/dist/src/hosts/codeblock.js.map +1 -1
- package/dist/src/hosts/flex-container.d.ts +45 -0
- package/dist/src/hosts/flex-container.d.ts.map +1 -0
- package/dist/src/hosts/flex-container.js +90 -0
- package/dist/src/hosts/flex-container.js.map +1 -0
- package/dist/src/hosts/hstack.d.ts +6 -11
- package/dist/src/hosts/hstack.d.ts.map +1 -1
- package/dist/src/hosts/hstack.js +6 -41
- package/dist/src/hosts/hstack.js.map +1 -1
- package/dist/src/hosts/index.d.ts +4 -0
- package/dist/src/hosts/index.d.ts.map +1 -1
- package/dist/src/hosts/index.js +10 -0
- package/dist/src/hosts/index.js.map +1 -1
- package/dist/src/hosts/overlay-item.d.ts +32 -0
- package/dist/src/hosts/overlay-item.d.ts.map +1 -0
- package/dist/src/hosts/overlay-item.js +54 -0
- package/dist/src/hosts/overlay-item.js.map +1 -0
- package/dist/src/hosts/overlay.d.ts +30 -0
- package/dist/src/hosts/overlay.d.ts.map +1 -0
- package/dist/src/hosts/overlay.js +105 -0
- package/dist/src/hosts/overlay.js.map +1 -0
- package/dist/src/hosts/scroll.d.ts +56 -0
- package/dist/src/hosts/scroll.d.ts.map +1 -0
- package/dist/src/hosts/scroll.js +204 -0
- package/dist/src/hosts/scroll.js.map +1 -0
- package/dist/src/hosts/single-child.d.ts +16 -0
- package/dist/src/hosts/single-child.d.ts.map +1 -0
- package/dist/src/hosts/single-child.js +45 -0
- package/dist/src/hosts/single-child.js.map +1 -0
- package/dist/src/hosts/spacer.d.ts.map +1 -1
- package/dist/src/hosts/spacer.js +7 -3
- package/dist/src/hosts/spacer.js.map +1 -1
- package/dist/src/hosts/text.d.ts +9 -6
- package/dist/src/hosts/text.d.ts.map +1 -1
- package/dist/src/hosts/text.js +49 -22
- package/dist/src/hosts/text.js.map +1 -1
- package/dist/src/hosts/vstack.d.ts +6 -11
- package/dist/src/hosts/vstack.d.ts.map +1 -1
- package/dist/src/hosts/vstack.js +6 -41
- package/dist/src/hosts/vstack.js.map +1 -1
- package/dist/src/hosts/zstack.d.ts.map +1 -1
- package/dist/src/hosts/zstack.js +16 -5
- package/dist/src/hosts/zstack.js.map +1 -1
- package/dist/src/index.d.ts +9 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +10 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/inline/index.d.ts.map +1 -1
- package/dist/src/inline/index.js.map +1 -1
- package/dist/src/motion/color-motion-value.d.ts.map +1 -1
- package/dist/src/motion/color-motion-value.js.map +1 -1
- package/dist/src/motion/color.d.ts +1 -29
- package/dist/src/motion/color.d.ts.map +1 -1
- package/dist/src/motion/color.js +2 -170
- package/dist/src/motion/color.js.map +1 -1
- package/dist/src/motion/color.test.js.map +1 -1
- package/dist/src/motion/event-emitter.d.ts.map +1 -1
- package/dist/src/motion/event-emitter.js.map +1 -1
- package/dist/src/motion/frame.js.map +1 -1
- package/dist/src/motion/hooks.d.ts.map +1 -1
- package/dist/src/motion/hooks.js +8 -3
- package/dist/src/motion/hooks.js.map +1 -1
- package/dist/src/motion/index.d.ts.map +1 -1
- package/dist/src/motion/index.js.map +1 -1
- package/dist/src/motion/motion-value.d.ts.map +1 -1
- package/dist/src/motion/motion-value.js.map +1 -1
- package/dist/src/motion/motion-value.test.js.map +1 -1
- package/dist/src/motion/spring-math.d.ts +6 -1
- package/dist/src/motion/spring-math.d.ts.map +1 -1
- package/dist/src/motion/spring-math.js +6 -1
- package/dist/src/motion/spring-math.js.map +1 -1
- package/dist/src/motion/types.d.ts.map +1 -1
- package/dist/src/motion/types.js.map +1 -1
- package/dist/src/profiler.js.map +1 -1
- package/dist/src/reconciler/host-config.d.ts +5 -5
- package/dist/src/reconciler/host-config.d.ts.map +1 -1
- package/dist/src/reconciler/host-config.js +43 -51
- package/dist/src/reconciler/host-config.js.map +1 -1
- package/dist/src/reconciler/noop-methods.d.ts +29 -0
- package/dist/src/reconciler/noop-methods.d.ts.map +1 -0
- package/dist/src/reconciler/noop-methods.js +43 -0
- package/dist/src/reconciler/noop-methods.js.map +1 -0
- package/dist/src/reconciler/types.d.ts +68 -14
- package/dist/src/reconciler/types.d.ts.map +1 -1
- package/dist/src/remote/Procedures.d.ts +22 -0
- package/dist/src/remote/Procedures.d.ts.map +1 -0
- package/dist/src/remote/Procedures.js +42 -0
- package/dist/src/remote/Procedures.js.map +1 -0
- package/dist/src/remote/Router.d.ts +20 -0
- package/dist/src/remote/Router.d.ts.map +1 -0
- package/dist/src/remote/Router.js +26 -0
- package/dist/src/remote/Router.js.map +1 -0
- package/dist/src/remote/Server.d.ts +6 -0
- package/dist/src/remote/Server.d.ts.map +1 -0
- package/dist/src/remote/Server.js +53 -0
- package/dist/src/remote/Server.js.map +1 -0
- package/dist/src/remote/index.d.ts +18 -0
- package/dist/src/remote/index.d.ts.map +1 -0
- package/dist/src/remote/index.js +74 -0
- package/dist/src/remote/index.js.map +1 -0
- package/dist/src/renderer/core/FrameBuilder.d.ts +18 -0
- package/dist/src/renderer/core/FrameBuilder.d.ts.map +1 -0
- package/dist/src/renderer/core/FrameBuilder.js +38 -0
- package/dist/src/renderer/core/FrameBuilder.js.map +1 -0
- package/dist/src/renderer/core/RendererState.d.ts +41 -0
- package/dist/src/renderer/core/RendererState.d.ts.map +1 -0
- package/dist/src/renderer/core/RendererState.js +70 -0
- package/dist/src/renderer/core/RendererState.js.map +1 -0
- package/dist/src/renderer/core/index.d.ts +3 -0
- package/dist/src/renderer/core/index.d.ts.map +1 -0
- package/dist/src/renderer/core/index.js +3 -0
- package/dist/src/renderer/core/index.js.map +1 -0
- package/dist/src/renderer/input/InputProcessor.d.ts +25 -0
- package/dist/src/renderer/input/InputProcessor.d.ts.map +1 -0
- package/dist/src/renderer/input/InputProcessor.js +81 -0
- package/dist/src/renderer/input/InputProcessor.js.map +1 -0
- package/dist/src/renderer/input/index.d.ts +2 -0
- package/dist/src/renderer/input/index.d.ts.map +1 -0
- package/dist/src/renderer/input/index.js +2 -0
- package/dist/src/renderer/input/index.js.map +1 -0
- package/dist/src/renderer/lifecycle/EventBus.d.ts +41 -0
- package/dist/src/renderer/lifecycle/EventBus.d.ts.map +1 -0
- package/dist/src/renderer/lifecycle/EventBus.js +78 -0
- package/dist/src/renderer/lifecycle/EventBus.js.map +1 -0
- package/dist/src/renderer/lifecycle/ResizeManager.d.ts +34 -0
- package/dist/src/renderer/lifecycle/ResizeManager.d.ts.map +1 -0
- package/dist/src/renderer/lifecycle/ResizeManager.js +47 -0
- package/dist/src/renderer/lifecycle/ResizeManager.js.map +1 -0
- package/dist/src/renderer/lifecycle/TerminalSetup.d.ts +36 -0
- package/dist/src/renderer/lifecycle/TerminalSetup.d.ts.map +1 -0
- package/dist/src/renderer/lifecycle/TerminalSetup.js +82 -0
- package/dist/src/renderer/lifecycle/TerminalSetup.js.map +1 -0
- package/dist/src/renderer/lifecycle/index.d.ts +4 -0
- package/dist/src/renderer/lifecycle/index.d.ts.map +1 -0
- package/dist/src/renderer/lifecycle/index.js +4 -0
- package/dist/src/renderer/lifecycle/index.js.map +1 -0
- package/dist/src/renderer/modes/FullscreenRenderer.d.ts +12 -0
- package/dist/src/renderer/modes/FullscreenRenderer.d.ts.map +1 -0
- package/dist/src/renderer/modes/FullscreenRenderer.js +52 -0
- package/dist/src/renderer/modes/FullscreenRenderer.js.map +1 -0
- package/dist/src/renderer/modes/InlineRenderer.d.ts +22 -0
- package/dist/src/renderer/modes/InlineRenderer.d.ts.map +1 -0
- package/dist/src/renderer/modes/InlineRenderer.js +154 -0
- package/dist/src/renderer/modes/InlineRenderer.js.map +1 -0
- package/dist/src/renderer/modes/RendererMode.d.ts +42 -0
- package/dist/src/renderer/modes/RendererMode.d.ts.map +1 -0
- package/dist/src/renderer/modes/RendererMode.js +2 -0
- package/dist/src/renderer/modes/RendererMode.js.map +1 -0
- package/dist/src/renderer/modes/StaticContentRenderer.d.ts +25 -0
- package/dist/src/renderer/modes/StaticContentRenderer.d.ts.map +1 -0
- package/dist/src/renderer/modes/StaticContentRenderer.js +47 -0
- package/dist/src/renderer/modes/StaticContentRenderer.js.map +1 -0
- package/dist/src/renderer/modes/index.d.ts +5 -0
- package/dist/src/renderer/modes/index.d.ts.map +1 -0
- package/dist/src/renderer/modes/index.js +4 -0
- package/dist/src/renderer/modes/index.js.map +1 -0
- package/dist/src/renderer-context.d.ts +9 -0
- package/dist/src/renderer-context.d.ts.map +1 -0
- package/dist/src/renderer-context.js +22 -0
- package/dist/src/renderer-context.js.map +1 -0
- package/dist/src/renderer-types.d.ts +103 -0
- package/dist/src/renderer-types.d.ts.map +1 -0
- package/dist/src/renderer-types.js +2 -0
- package/dist/src/renderer-types.js.map +1 -0
- package/dist/src/renderer.d.ts +4 -86
- package/dist/src/renderer.d.ts.map +1 -1
- package/dist/src/renderer.js +213 -384
- package/dist/src/renderer.js.map +1 -1
- package/dist/src/test/index.d.ts.map +1 -1
- package/dist/src/test/index.js.map +1 -1
- package/dist/src/test/mock-streams.d.ts.map +1 -1
- package/dist/src/test/mock-streams.js.map +1 -1
- package/dist/src/test/render-tui.d.ts.map +1 -1
- package/dist/src/test/render-tui.js +2 -5
- package/dist/src/test/render-tui.js.map +1 -1
- package/dist/src/trace/SpanTree.d.ts.map +1 -1
- package/dist/src/trace/SpanTree.js +21 -11
- package/dist/src/trace/SpanTree.js.map +1 -1
- package/dist/src/trace/format-value.d.ts +15 -0
- package/dist/src/trace/format-value.d.ts.map +1 -0
- package/dist/src/trace/format-value.js +77 -0
- package/dist/src/trace/format-value.js.map +1 -0
- package/dist/src/trace/index.d.ts.map +1 -1
- package/dist/src/trace/index.js.map +1 -1
- package/dist/src/trace/location.js +1 -1
- package/dist/src/trace/location.js.map +1 -1
- package/dist/src/trace/span-processor.d.ts.map +1 -1
- package/dist/src/trace/span-processor.js.map +1 -1
- package/dist/src/trace/span-state.d.ts +19 -2
- package/dist/src/trace/span-state.d.ts.map +1 -1
- package/dist/src/trace/span-state.js +62 -31
- package/dist/src/trace/span-state.js.map +1 -1
- package/dist/src/trace/tui-logger.js.map +1 -1
- package/dist/src/utils/border.d.ts +1 -1
- package/dist/src/utils/border.d.ts.map +1 -1
- package/dist/src/utils/border.js +6 -0
- package/dist/src/utils/border.js.map +1 -1
- package/dist/src/utils/flex-layout.d.ts +2 -1
- package/dist/src/utils/flex-layout.d.ts.map +1 -1
- package/dist/src/utils/flex-layout.js +22 -33
- package/dist/src/utils/flex-layout.js.map +1 -1
- package/dist/src/utils/index.d.ts +1 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +1 -1
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/padding.d.ts.map +1 -1
- package/dist/src/utils/padding.js.map +1 -1
- package/dist/src/utils/styles.d.ts +20 -1
- package/dist/src/utils/styles.d.ts.map +1 -1
- package/dist/src/utils/styles.js +36 -1
- package/dist/src/utils/styles.js.map +1 -1
- package/dist/src/visualize/index.d.ts +8 -19
- package/dist/src/visualize/index.d.ts.map +1 -1
- package/dist/src/visualize/index.js +11 -25
- package/dist/src/visualize/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jsx-dev-runtime.ts +5 -0
- package/jsx-runtime.ts +54 -0
- package/package.json +124 -92
- package/src/codeblock.tsx +34 -34
- package/src/components/Divider.tsx +23 -0
- package/src/components/Markdown.tsx +380 -0
- package/src/components/MultilineTextInput.tsx +749 -0
- package/src/components/Overlay.tsx +56 -0
- package/src/components/Static.tsx +68 -0
- package/src/components/TextInput.tsx +285 -0
- package/src/components/index.ts +6 -0
- package/src/components/text-editing.ts +464 -0
- package/src/console/ConsoleCapture.ts +272 -0
- package/src/console/ConsolePopover.tsx +487 -0
- package/src/console/clipboard.ts +81 -0
- package/src/console/index.ts +42 -0
- package/src/console/useConsole.ts +129 -0
- package/src/debug/DebugOverlay.ts +557 -0
- package/src/debug/DiagnosticsPanel.tsx +27 -27
- package/src/dev/Toast.tsx +117 -0
- package/src/dev/index.ts +2 -0
- package/src/dev.tsx +489 -0
- package/src/highlight.ts +46 -46
- package/src/hmr-plugin.ts +61 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/use-keyboard.ts +44 -24
- package/src/hooks/use-mouse.ts +51 -0
- package/src/hooks/use-paste.ts +21 -6
- package/src/hooks/use-scroll.ts +386 -0
- package/src/hooks/useFrameStats.ts +17 -17
- package/src/hosts/base.ts +180 -59
- package/src/hosts/box.ts +117 -94
- package/src/hosts/canvas.ts +137 -141
- package/src/hosts/codeblock.ts +117 -133
- package/src/hosts/flex-container.ts +124 -0
- package/src/hosts/hstack.ts +11 -59
- package/src/hosts/index.ts +24 -14
- package/src/hosts/overlay-item.ts +72 -0
- package/src/hosts/overlay.ts +125 -0
- package/src/hosts/scroll.ts +255 -0
- package/src/hosts/single-child.ts +52 -0
- package/src/hosts/spacer.ts +30 -26
- package/src/hosts/text.ts +198 -164
- package/src/hosts/vstack.ts +11 -59
- package/src/hosts/zstack.ts +79 -67
- package/src/index.ts +44 -19
- package/src/inline/index.tsx +123 -123
- package/src/motion/color-motion-value.ts +67 -67
- package/src/motion/color.test.ts +107 -107
- package/src/motion/color.ts +9 -190
- package/src/motion/event-emitter.ts +20 -20
- package/src/motion/frame.ts +35 -35
- package/src/motion/hooks.ts +144 -139
- package/src/motion/index.ts +10 -10
- package/src/motion/motion-value.test.ts +207 -207
- package/src/motion/motion-value.ts +112 -112
- package/src/motion/spring-math.ts +88 -83
- package/src/motion/types.ts +25 -25
- package/src/profiler.ts +50 -50
- package/src/reconciler/host-config.ts +152 -174
- package/src/reconciler/noop-methods.ts +55 -0
- package/src/reconciler/types.ts +112 -46
- package/src/remote/Procedures.ts +52 -0
- package/src/remote/Router.ts +58 -0
- package/src/remote/Server.ts +76 -0
- package/src/remote/index.ts +90 -0
- package/src/renderer/core/FrameBuilder.ts +49 -0
- package/src/renderer/core/RendererState.ts +80 -0
- package/src/renderer/core/index.ts +2 -0
- package/src/renderer/input/InputProcessor.ts +94 -0
- package/src/renderer/input/index.ts +1 -0
- package/src/renderer/lifecycle/EventBus.ts +90 -0
- package/src/renderer/lifecycle/ResizeManager.ts +65 -0
- package/src/renderer/lifecycle/TerminalSetup.ts +105 -0
- package/src/renderer/lifecycle/index.ts +3 -0
- package/src/renderer/modes/FullscreenRenderer.ts +53 -0
- package/src/renderer/modes/InlineRenderer.ts +178 -0
- package/src/renderer/modes/RendererMode.ts +46 -0
- package/src/renderer/modes/StaticContentRenderer.ts +56 -0
- package/src/renderer/modes/index.ts +4 -0
- package/src/renderer-context.ts +27 -0
- package/src/renderer-types.ts +109 -0
- package/src/renderer.ts +391 -642
- package/src/test/index.ts +5 -5
- package/src/test/mock-streams.ts +115 -115
- package/src/test/render-tui.ts +84 -87
- package/src/utils/border.ts +79 -73
- package/src/utils/flex-layout.ts +80 -93
- package/src/utils/index.ts +1 -1
- package/src/utils/padding.ts +27 -27
- package/src/utils/styles.ts +50 -7
- package/src/visualize/index.tsx +225 -240
- package/dist/src/output.d.ts +0 -47
- package/dist/src/output.d.ts.map +0 -1
- package/dist/src/output.js +0 -125
- package/dist/src/output.js.map +0 -1
- package/dist/src/terminal.d.ts +0 -37
- package/dist/src/terminal.d.ts.map +0 -1
- package/dist/src/terminal.js +0 -65
- package/dist/src/terminal.js.map +0 -1
- package/src/output.ts +0 -156
- package/src/terminal.ts +0 -67
- package/src/trace/SpanTree.tsx +0 -195
- package/src/trace/index.tsx +0 -205
- package/src/trace/location.ts +0 -90
- package/src/trace/span-processor.ts +0 -65
- package/src/trace/span-state.ts +0 -286
- package/src/trace/tui-logger.ts +0 -72
package/src/renderer.ts
CHANGED
|
@@ -1,662 +1,411 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { type ReactNode } from "react"
|
|
2
2
|
import { performance } from "node:perf_hooks"
|
|
3
|
-
import {
|
|
4
|
-
import { reconciler,
|
|
3
|
+
import { ANSI, type KeyMsg, type MouseMsg, bufferToString } from "@effect-tui/core"
|
|
4
|
+
import { reconciler, flushSync } from "./reconciler/host-config.js"
|
|
5
5
|
import type { HostContext } from "./reconciler/types.js"
|
|
6
|
-
import { ANSI, Terminal } from "./terminal.js"
|
|
7
6
|
import * as Prof from "./profiler.js"
|
|
8
7
|
import { DEFAULT_FPS } from "./constants.js"
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
resume?(): void
|
|
30
|
-
on(event: string, cb: (data: Buffer) => void): void
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface TuiRenderer {
|
|
34
|
-
/** Terminal width */
|
|
35
|
-
width: number
|
|
36
|
-
/** Terminal height */
|
|
37
|
-
height: number
|
|
38
|
-
/** Request a re-render */
|
|
39
|
-
requestRender(): void
|
|
40
|
-
/** Subscribe to per-frame stats (if enabled). */
|
|
41
|
-
onFrameStats?(handler: (stats: FrameStats) => void): () => void
|
|
42
|
-
/** Subscribe to keyboard events */
|
|
43
|
-
onKey(handler: (key: KeyMsg) => void): () => void
|
|
44
|
-
/** Subscribe to paste events (bracketed paste mode). */
|
|
45
|
-
onPaste?(handler: (text: string) => void): () => void
|
|
46
|
-
/** Subscribe to resize events */
|
|
47
|
-
onResize(handler: (width: number, height: number) => void): () => void
|
|
48
|
-
/** Stop the renderer */
|
|
49
|
-
stop(): void
|
|
50
|
-
/** Manually trigger one render frame (only in manualMode) */
|
|
51
|
-
flush(): void
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Internal renderer type with container reference */
|
|
55
|
-
interface TuiRendererInternal extends TuiRenderer {
|
|
56
|
-
_container: Container | null
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Context for accessing renderer in components
|
|
60
|
-
export const RendererContext = createContext<TuiRenderer | null>(null)
|
|
61
|
-
|
|
62
|
-
export function useRenderer(): TuiRenderer {
|
|
63
|
-
const renderer = useContext(RendererContext)
|
|
64
|
-
if (!renderer) {
|
|
65
|
-
throw new Error("useRenderer must be used within a TUI renderer")
|
|
66
|
-
}
|
|
67
|
-
return renderer
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Hook that returns terminal size and re-renders on resize */
|
|
71
|
-
export function useTerminalSize(): { width: number; height: number } {
|
|
72
|
-
const renderer = useRenderer()
|
|
73
|
-
const [size, setSize] = useState({ width: renderer.width, height: renderer.height })
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
return renderer.onResize((width, height) => {
|
|
77
|
-
setSize({ width, height })
|
|
78
|
-
})
|
|
79
|
-
}, [renderer])
|
|
80
|
-
|
|
81
|
-
return size
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface RendererOptions {
|
|
85
|
-
fps?: number
|
|
86
|
-
stdout?: NodeJS.WriteStream | TuiWriteStream
|
|
87
|
-
stdin?: NodeJS.ReadStream | TuiReadStream
|
|
88
|
-
/** Render mode: "fullscreen" uses alternate buffer, "inline" renders in-place */
|
|
89
|
-
mode?: "fullscreen" | "inline"
|
|
90
|
-
/** Exit the process on Ctrl+C unless preventDefault was called. Defaults to true. */
|
|
91
|
-
exitOnCtrlC?: boolean
|
|
92
|
-
/** Enable diffed rendering (per-line). Defaults to true in runtime, false in manualMode (tests). */
|
|
93
|
-
diff?: boolean
|
|
94
|
-
/** Enable diffed rendering for inline mode (off by default; eraseLines baseline). */
|
|
95
|
-
diffInline?: boolean
|
|
96
|
-
/** Skip automatic render loop. Call flush() manually to render frames. */
|
|
97
|
-
manualMode?: boolean
|
|
98
|
-
/** Skip fullscreen/raw mode setup (for testing) */
|
|
99
|
-
skipTerminalSetup?: boolean
|
|
100
|
-
/** Enable bracketed paste (default true). */
|
|
101
|
-
enablePaste?: boolean
|
|
102
|
-
/** Optional per-frame diagnostics hook. Called after each frame is written. */
|
|
103
|
-
debug?: {
|
|
104
|
-
onFrame?: (stats: FrameStats) => void
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface FrameStats {
|
|
109
|
-
mode: "fullscreen" | "inline"
|
|
110
|
-
width: number
|
|
111
|
-
height: number
|
|
112
|
-
contentHeight: number
|
|
113
|
-
bytes: number
|
|
114
|
-
frameMs: number
|
|
115
|
-
phases: {
|
|
116
|
-
clear: number
|
|
117
|
-
layout: number
|
|
118
|
-
render: number
|
|
119
|
-
diffAnsi: number
|
|
120
|
-
write: number
|
|
121
|
-
}
|
|
122
|
-
timestamp: number
|
|
123
|
-
}
|
|
8
|
+
import type {
|
|
9
|
+
TuiWriteStream,
|
|
10
|
+
TuiReadStream,
|
|
11
|
+
TuiRenderer,
|
|
12
|
+
TuiRendererInternal,
|
|
13
|
+
Container,
|
|
14
|
+
RendererOptions,
|
|
15
|
+
FrameStats,
|
|
16
|
+
} from "./renderer-types.js"
|
|
17
|
+
import { RendererContext } from "./renderer-context.js"
|
|
18
|
+
|
|
19
|
+
// Extracted modules
|
|
20
|
+
import { RendererState, FrameBuilder } from "./renderer/core/index.js"
|
|
21
|
+
import { InputProcessor } from "./renderer/input/index.js"
|
|
22
|
+
import { EventBus, TerminalSetup } from "./renderer/lifecycle/index.js"
|
|
23
|
+
import { FullscreenRenderer, InlineRenderer, StaticContentRenderer } from "./renderer/modes/index.js"
|
|
24
|
+
|
|
25
|
+
// Re-export types and context for backwards compatibility
|
|
26
|
+
export type { TuiWriteStream, TuiReadStream, TuiRenderer, RendererOptions, FrameStats } from "./renderer-types.js"
|
|
27
|
+
export { RendererContext, useRenderer, useTerminalSize } from "./renderer-context.js"
|
|
124
28
|
|
|
125
29
|
export function createRenderer(options?: RendererOptions): TuiRenderer {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
onResize(handler: (width: number, height: number) => void) {
|
|
401
|
-
resizeHandlers.add(handler)
|
|
402
|
-
return () => resizeHandlers.delete(handler)
|
|
403
|
-
},
|
|
404
|
-
onFrameStats(handler: (stats: FrameStats) => void) {
|
|
405
|
-
frameHandlers.add(handler)
|
|
406
|
-
return () => frameHandlers.delete(handler)
|
|
407
|
-
},
|
|
408
|
-
stop() {
|
|
409
|
-
running = false
|
|
410
|
-
if (loop) {
|
|
411
|
-
clearInterval(loop)
|
|
412
|
-
loop = null
|
|
413
|
-
}
|
|
414
|
-
teardown()
|
|
415
|
-
},
|
|
416
|
-
flush() {
|
|
417
|
-
renderFrame()
|
|
418
|
-
},
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Terminal setup (skip for testing)
|
|
422
|
-
if (!skipTerminalSetup) {
|
|
423
|
-
if (mode === "fullscreen") {
|
|
424
|
-
stdout.write(Terminal.enterFullscreen)
|
|
425
|
-
}
|
|
426
|
-
// Hide cursor during rendering (both modes)
|
|
427
|
-
stdout.write(Terminal.hideCursor)
|
|
428
|
-
if (enablePaste) stdout.write(PASTE_ENABLE)
|
|
429
|
-
|
|
430
|
-
if (stdin.isTTY && stdin.setRawMode) {
|
|
431
|
-
stdin.setRawMode(true)
|
|
432
|
-
stdin.resume?.()
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Handle keyboard input (and bracketed paste)
|
|
437
|
-
stdin.on("data", (data: Buffer) => {
|
|
438
|
-
let chunk = data.toString("utf8")
|
|
439
|
-
|
|
440
|
-
const emitKeys = (str: string) => {
|
|
441
|
-
if (!str) return
|
|
442
|
-
const keys = decodeKeys(Buffer.from(str, "utf8"))
|
|
443
|
-
for (const key of keys) {
|
|
444
|
-
const wrapped: KeyMsg = {
|
|
445
|
-
...key,
|
|
446
|
-
defaultPrevented: false,
|
|
447
|
-
preventDefault() {
|
|
448
|
-
wrapped.defaultPrevented = true
|
|
449
|
-
},
|
|
450
|
-
}
|
|
451
|
-
for (const handler of keyHandlers) {
|
|
452
|
-
if (wrapped.defaultPrevented) break
|
|
453
|
-
handler(wrapped)
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Default Ctrl+C handling - exit unless user called preventDefault()
|
|
457
|
-
if (exitOnCtrlC && !wrapped.defaultPrevented && key.ctrl && key.text === "c") {
|
|
458
|
-
process.exit(0)
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
while (chunk.length > 0) {
|
|
464
|
-
if (pasteActive) {
|
|
465
|
-
const endIdx = chunk.indexOf(PASTE_END)
|
|
466
|
-
if (endIdx >= 0) {
|
|
467
|
-
pasteBuffer += chunk.slice(0, endIdx)
|
|
468
|
-
pasteHandlers.forEach((h) => h(pasteBuffer))
|
|
469
|
-
pasteBuffer = ""
|
|
470
|
-
pasteActive = false
|
|
471
|
-
chunk = chunk.slice(endIdx + PASTE_END.length)
|
|
472
|
-
continue
|
|
473
|
-
} else {
|
|
474
|
-
pasteBuffer += chunk
|
|
475
|
-
chunk = ""
|
|
476
|
-
break
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const startIdx = chunk.indexOf(PASTE_START)
|
|
481
|
-
if (startIdx >= 0) {
|
|
482
|
-
// Emit any keys before the paste start
|
|
483
|
-
emitKeys(chunk.slice(0, startIdx))
|
|
484
|
-
pasteActive = true
|
|
485
|
-
pasteBuffer = ""
|
|
486
|
-
chunk = chunk.slice(startIdx + PASTE_START.length)
|
|
487
|
-
continue
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// No paste markers; treat as normal keys
|
|
491
|
-
emitKeys(chunk)
|
|
492
|
-
chunk = ""
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
dirty = true
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
// Handle resize - render synchronously like Ink does
|
|
499
|
-
stdout.on("resize", () => {
|
|
500
|
-
const newWidth = stdout.columns || 80
|
|
501
|
-
const newHeight = stdout.rows || 24
|
|
502
|
-
|
|
503
|
-
// Fullscreen: clear entire screen on resize to prevent artifacts from terminal reflow
|
|
504
|
-
// The terminal may leave stale content that our diff-based rendering doesn't see
|
|
505
|
-
if (mode === "fullscreen") {
|
|
506
|
-
stdout.write(ANSI.screen.clear + ANSI.cursor.to(1, 1))
|
|
507
|
-
} else if (mode === "inline" && newWidth < lastWidth && previousHeight > 0) {
|
|
508
|
-
// Inline: on width shrink, previous content may have wrapped to MORE lines
|
|
509
|
-
// than previousHeight. Clear from content start to END OF SCREEN to catch all.
|
|
510
|
-
stdout.write(ANSI.cursor.up(previousHeight) + ANSI.cursor.startOfLine)
|
|
511
|
-
stdout.write(ANSI.screen.clearToEnd)
|
|
512
|
-
previousHeight = 0
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
width = newWidth
|
|
516
|
-
height = newHeight
|
|
517
|
-
lastWidth = newWidth
|
|
518
|
-
prevBuffer = null
|
|
519
|
-
nextBuffer = null
|
|
520
|
-
dirty = true
|
|
521
|
-
|
|
522
|
-
// Notify resize handlers
|
|
523
|
-
for (const handler of resizeHandlers) {
|
|
524
|
-
handler(newWidth, newHeight)
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Render immediately on resize (like Ink) to prevent cursor corruption
|
|
528
|
-
renderFrame()
|
|
529
|
-
})
|
|
530
|
-
|
|
531
|
-
// Automatic render loop (skip in manual mode)
|
|
532
|
-
if (!manualMode) {
|
|
533
|
-
const frameMs = 1000 / fps
|
|
534
|
-
loop = setInterval(() => {
|
|
535
|
-
if (!running) {
|
|
536
|
-
if (loop) clearInterval(loop)
|
|
537
|
-
teardown()
|
|
538
|
-
return
|
|
539
|
-
}
|
|
540
|
-
renderFrame()
|
|
541
|
-
}, frameMs)
|
|
542
|
-
}
|
|
543
|
-
// Store container reference for direct root access
|
|
544
|
-
;(renderer as TuiRendererInternal)._container = null
|
|
545
|
-
|
|
546
|
-
return renderer
|
|
30
|
+
const fps = options?.fps ?? DEFAULT_FPS
|
|
31
|
+
const stdout: TuiWriteStream = options?.stdout ?? process.stdout
|
|
32
|
+
const stdin: TuiReadStream = options?.stdin ?? process.stdin
|
|
33
|
+
const mode = options?.mode ?? "fullscreen"
|
|
34
|
+
const exitOnCtrlC = options?.exitOnCtrlC ?? true
|
|
35
|
+
const manualMode = options?.manualMode ?? false
|
|
36
|
+
const enableDiff = options?.diff ?? !manualMode
|
|
37
|
+
const skipTerminalSetup = options?.skipTerminalSetup ?? false
|
|
38
|
+
const enablePaste = options?.enablePaste ?? true
|
|
39
|
+
const enableMouse = options?.enableMouse ?? mode === "fullscreen"
|
|
40
|
+
const debugHook = options?.debug?.onFrame
|
|
41
|
+
|
|
42
|
+
// Initialize state
|
|
43
|
+
const state = new RendererState(stdout.columns || 80, stdout.rows || 24)
|
|
44
|
+
const events = new EventBus()
|
|
45
|
+
const frameBuilder = new FrameBuilder()
|
|
46
|
+
|
|
47
|
+
// Terminal setup/teardown
|
|
48
|
+
const terminal = new TerminalSetup(stdout, stdin, {
|
|
49
|
+
mode,
|
|
50
|
+
enablePaste,
|
|
51
|
+
enableMouse,
|
|
52
|
+
skipTerminalSetup,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// Render mode (fullscreen or inline)
|
|
56
|
+
const renderMode = mode === "fullscreen" ? new FullscreenRenderer() : new InlineRenderer()
|
|
57
|
+
|
|
58
|
+
// Static content renderer (inline mode only)
|
|
59
|
+
const staticRenderer = mode === "inline" ? new StaticContentRenderer(stdout, state.palette) : null
|
|
60
|
+
|
|
61
|
+
// Input processing
|
|
62
|
+
const inputProcessor = new InputProcessor({
|
|
63
|
+
exitOnCtrlC,
|
|
64
|
+
dispatchKey: (key) => {
|
|
65
|
+
events.dispatchKey(key)
|
|
66
|
+
return key.defaultPrevented ?? false
|
|
67
|
+
},
|
|
68
|
+
dispatchMouse: (mouse) => events.dispatchMouse(mouse),
|
|
69
|
+
dispatchPaste: (text) => events.dispatchPaste(text),
|
|
70
|
+
flushSync: (fn) => flushSync(fn) ?? (undefined as never),
|
|
71
|
+
onInputProcessed: () => {
|
|
72
|
+
if (!manualMode) renderFrame()
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// The render frame logic
|
|
77
|
+
const renderFrame = () => {
|
|
78
|
+
const frameStart = Prof.startFrame()
|
|
79
|
+
const frameStartMs = performance.now()
|
|
80
|
+
const frameWidth = state.width
|
|
81
|
+
const frameHeight = state.height
|
|
82
|
+
let contentH = frameHeight
|
|
83
|
+
|
|
84
|
+
const container = (renderer as TuiRendererInternal)._container
|
|
85
|
+
const root = container?.root ?? null
|
|
86
|
+
|
|
87
|
+
// Must render if dirty OR if static content needs flushing
|
|
88
|
+
if ((!state.dirty && !container?.staticDirty) || !root) return
|
|
89
|
+
state.dirty = false
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Handle full rerender on resize (Ink-style: clear everything + replay static)
|
|
93
|
+
if (mode === "inline" && staticRenderer) {
|
|
94
|
+
const inlineMode = renderMode as InlineRenderer
|
|
95
|
+
if (inlineMode.needsFullRerender()) {
|
|
96
|
+
// Clear screen + scrollback + cursor home
|
|
97
|
+
stdout.write(ANSI.screen.clear + ANSI.screen.clearScrollback + ANSI.cursor.home)
|
|
98
|
+
// Replay all cached static content
|
|
99
|
+
const cachedStatic = staticRenderer.getCachedOutput()
|
|
100
|
+
if (cachedStatic) {
|
|
101
|
+
stdout.write(cachedStatic)
|
|
102
|
+
}
|
|
103
|
+
// Reset state
|
|
104
|
+
inlineMode.clearFullRerenderFlag()
|
|
105
|
+
state.invalidateBuffers()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Handle static content: clear dynamic area, append static, then fresh dynamic render
|
|
110
|
+
// Note: IL (insert lines) won't work here because inline mode uses relative positioning
|
|
111
|
+
// and IL would desync the screen state from our buffer tracking.
|
|
112
|
+
let staticOutput = ""
|
|
113
|
+
if (mode === "inline" && container?.staticDirty && container?.staticRoot && staticRenderer) {
|
|
114
|
+
const inlineMode = renderMode as InlineRenderer
|
|
115
|
+
const prevHeight = inlineMode.getPreviousHeight()
|
|
116
|
+
|
|
117
|
+
// Step 1: Clear the dynamic area (move up + clear to end of screen)
|
|
118
|
+
if (prevHeight > 0) {
|
|
119
|
+
staticOutput += ANSI.cursor.up(prevHeight) + ANSI.cursor.startOfLine + ANSI.screen.clearToEnd
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Step 2: Append static content (cursor ends at bottom of static)
|
|
123
|
+
staticOutput += staticRenderer.render(container.staticRoot, frameWidth)
|
|
124
|
+
|
|
125
|
+
// Step 3: Reset previousHeight to 0 (we cleared dynamic, starting fresh)
|
|
126
|
+
inlineMode.reset()
|
|
127
|
+
state.invalidateBuffers()
|
|
128
|
+
container.staticDirty = false
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// For inline mode, measure content unconstrained to handle overflow
|
|
132
|
+
let actualContentHeight = frameHeight
|
|
133
|
+
if (mode === "inline") {
|
|
134
|
+
const size = root.measure(frameWidth, Number.MAX_SAFE_INTEGER)
|
|
135
|
+
actualContentHeight = size.h
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Buffer height: content height for inline (to capture all content), terminal height for fullscreen
|
|
139
|
+
const bufferHeight = mode === "inline" ? Math.max(actualContentHeight, frameHeight) : frameHeight
|
|
140
|
+
|
|
141
|
+
// Ensure buffers exist
|
|
142
|
+
state.ensureBuffers(frameWidth, bufferHeight)
|
|
143
|
+
if (!state.nextBuffer) return
|
|
144
|
+
|
|
145
|
+
// Build frame (clear, layout, render)
|
|
146
|
+
const timings = frameBuilder.build(root, state.nextBuffer, state.palette, frameWidth, bufferHeight)
|
|
147
|
+
|
|
148
|
+
// Generate output
|
|
149
|
+
const t = Prof.startPhase()
|
|
150
|
+
const diffStartMs = performance.now()
|
|
151
|
+
|
|
152
|
+
const { output: modeOutput, contentHeight } = renderMode.generateOutput({
|
|
153
|
+
nextBuffer: state.nextBuffer,
|
|
154
|
+
prevBuffer: state.prevBuffer,
|
|
155
|
+
palette: state.palette,
|
|
156
|
+
frameWidth,
|
|
157
|
+
frameHeight,
|
|
158
|
+
contentHeight: actualContentHeight,
|
|
159
|
+
enableDiff,
|
|
160
|
+
stdout,
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// Combine static + dynamic output for atomic write
|
|
164
|
+
let output = staticOutput + modeOutput + state.palette.sgr(0)
|
|
165
|
+
contentH = contentHeight
|
|
166
|
+
const diffAnsiMs = performance.now() - diffStartMs
|
|
167
|
+
Prof.endPhase("diff+ansi", t)
|
|
168
|
+
|
|
169
|
+
// Write output (single atomic write prevents visual glitches)
|
|
170
|
+
const writeT = Prof.startPhase()
|
|
171
|
+
const writeStart = performance.now()
|
|
172
|
+
stdout.write(output)
|
|
173
|
+
const writeMs = performance.now() - writeStart
|
|
174
|
+
Prof.endPhase("write", writeT)
|
|
175
|
+
|
|
176
|
+
Prof.endFrame(frameStart)
|
|
177
|
+
const frameMs = performance.now() - frameStartMs
|
|
178
|
+
|
|
179
|
+
// Swap buffers
|
|
180
|
+
state.swapBuffers()
|
|
181
|
+
|
|
182
|
+
// Build stats
|
|
183
|
+
const stats: FrameStats = {
|
|
184
|
+
mode,
|
|
185
|
+
width: state.width,
|
|
186
|
+
height: state.height,
|
|
187
|
+
contentHeight: contentH,
|
|
188
|
+
bytes: Buffer.byteLength(output, "utf8"),
|
|
189
|
+
frameMs,
|
|
190
|
+
phases: {
|
|
191
|
+
clear: timings.clear,
|
|
192
|
+
layout: timings.layout,
|
|
193
|
+
render: timings.render,
|
|
194
|
+
diffAnsi: diffAnsiMs,
|
|
195
|
+
write: writeMs,
|
|
196
|
+
},
|
|
197
|
+
timestamp: performance.now(),
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (debugHook) debugHook(stats)
|
|
201
|
+
if (events.hasFrameHandlers) events.dispatchFrame(stats)
|
|
202
|
+
} catch (err) {
|
|
203
|
+
console.error("[effect-tui] Render error:", err)
|
|
204
|
+
state.markDirty()
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Build renderer object
|
|
209
|
+
const renderer: TuiRenderer = {
|
|
210
|
+
get width() {
|
|
211
|
+
return state.width
|
|
212
|
+
},
|
|
213
|
+
get height() {
|
|
214
|
+
return state.height
|
|
215
|
+
},
|
|
216
|
+
requestRender() {
|
|
217
|
+
state.markDirty()
|
|
218
|
+
},
|
|
219
|
+
onKey: (handler: (key: KeyMsg) => void) => events.onKey(handler),
|
|
220
|
+
onMouse: (handler: (mouse: MouseMsg) => void) => events.onMouse(handler),
|
|
221
|
+
onPaste: (handler: (text: string) => void) => events.onPaste(handler),
|
|
222
|
+
onResize: (handler: (width: number, height: number) => void) => events.onResize(handler),
|
|
223
|
+
onFrameStats: (handler: (stats: FrameStats) => void) => events.onFrameStats(handler),
|
|
224
|
+
stop() {
|
|
225
|
+
state.running = false
|
|
226
|
+
if (state.loop) {
|
|
227
|
+
clearInterval(state.loop)
|
|
228
|
+
state.loop = null
|
|
229
|
+
}
|
|
230
|
+
if (state.inputHandler) {
|
|
231
|
+
stdin.removeListener("data", state.inputHandler)
|
|
232
|
+
state.inputHandler = null
|
|
233
|
+
}
|
|
234
|
+
if (state.resizeHandler) {
|
|
235
|
+
stdout.removeListener("resize", state.resizeHandler)
|
|
236
|
+
state.resizeHandler = null
|
|
237
|
+
}
|
|
238
|
+
terminal.teardown()
|
|
239
|
+
},
|
|
240
|
+
flush() {
|
|
241
|
+
renderFrame()
|
|
242
|
+
},
|
|
243
|
+
getScreenshot() {
|
|
244
|
+
// Return the previous buffer as ANSI string (it has the last rendered frame)
|
|
245
|
+
if (state.prevBuffer) {
|
|
246
|
+
return bufferToString(state.prevBuffer, state.palette, state.width, state.height)
|
|
247
|
+
}
|
|
248
|
+
return ""
|
|
249
|
+
},
|
|
250
|
+
dispatchKey(key: KeyMsg) {
|
|
251
|
+
events.dispatchKey(key)
|
|
252
|
+
if (!manualMode) renderFrame()
|
|
253
|
+
},
|
|
254
|
+
dispatchPaste(text: string) {
|
|
255
|
+
events.dispatchPaste(text)
|
|
256
|
+
if (!manualMode) renderFrame()
|
|
257
|
+
},
|
|
258
|
+
dispatchResize(width: number, height: number) {
|
|
259
|
+
state.updateDimensions(width, height)
|
|
260
|
+
state.invalidateBuffers()
|
|
261
|
+
state.markDirty()
|
|
262
|
+
events.dispatchResize(width, height)
|
|
263
|
+
if (!manualMode) renderFrame()
|
|
264
|
+
},
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Terminal setup
|
|
268
|
+
terminal.setup()
|
|
269
|
+
|
|
270
|
+
// Input handling
|
|
271
|
+
state.inputHandler = (data: Buffer) => inputProcessor.process(data)
|
|
272
|
+
stdin.on("data", state.inputHandler)
|
|
273
|
+
|
|
274
|
+
// Resize handling
|
|
275
|
+
state.resizeHandler = () => {
|
|
276
|
+
const newWidth = stdout.columns || 80
|
|
277
|
+
const newHeight = stdout.rows || 24
|
|
278
|
+
|
|
279
|
+
renderMode.handleResize(newWidth, newHeight, state.lastWidth)
|
|
280
|
+
|
|
281
|
+
state.updateDimensions(newWidth, newHeight)
|
|
282
|
+
state.invalidateBuffers()
|
|
283
|
+
state.markDirty()
|
|
284
|
+
|
|
285
|
+
events.dispatchResize(newWidth, newHeight)
|
|
286
|
+
}
|
|
287
|
+
stdout.on("resize", state.resizeHandler)
|
|
288
|
+
|
|
289
|
+
// Render loop
|
|
290
|
+
if (!manualMode) {
|
|
291
|
+
const frameMs = 1000 / fps
|
|
292
|
+
state.loop = setInterval(() => {
|
|
293
|
+
if (!state.running) {
|
|
294
|
+
if (state.loop) clearInterval(state.loop)
|
|
295
|
+
terminal.teardown()
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
renderFrame()
|
|
299
|
+
}, frameMs)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
;(renderer as TuiRendererInternal)._container = null
|
|
303
|
+
return renderer
|
|
547
304
|
}
|
|
548
305
|
|
|
549
306
|
export interface Root {
|
|
550
|
-
|
|
551
|
-
|
|
307
|
+
render(element: ReactNode, sync?: boolean): void
|
|
308
|
+
unmount(): void
|
|
552
309
|
}
|
|
553
310
|
|
|
554
311
|
export function createRoot(renderer: TuiRenderer): Root {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
})
|
|
602
|
-
},
|
|
603
|
-
}
|
|
312
|
+
const hostContext: HostContext = {
|
|
313
|
+
requestRender: () => renderer.requestRender(),
|
|
314
|
+
requestImmediateRender: () => renderer.flush(),
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const container: Container = {
|
|
318
|
+
root: null,
|
|
319
|
+
ctx: hostContext,
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const fiberRoot = reconciler.createContainer(
|
|
323
|
+
container,
|
|
324
|
+
0,
|
|
325
|
+
null,
|
|
326
|
+
false,
|
|
327
|
+
null,
|
|
328
|
+
"",
|
|
329
|
+
(err: Error) => console.error(err),
|
|
330
|
+
(err: Error) => console.error(err),
|
|
331
|
+
(err: Error) => console.error(err),
|
|
332
|
+
() => {},
|
|
333
|
+
null,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
;(renderer as TuiRendererInternal)._container = container
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
render(element: ReactNode, sync = false) {
|
|
340
|
+
const wrapped = React.createElement(RendererContext.Provider, { value: renderer }, element)
|
|
341
|
+
if (sync) {
|
|
342
|
+
flushSync(() => {
|
|
343
|
+
reconciler.updateContainer(wrapped, fiberRoot, null, null)
|
|
344
|
+
})
|
|
345
|
+
renderer.requestRender()
|
|
346
|
+
} else {
|
|
347
|
+
reconciler.updateContainer(wrapped, fiberRoot, null, () => {
|
|
348
|
+
renderer.requestRender()
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
unmount() {
|
|
353
|
+
reconciler.updateContainer(null, fiberRoot, null, () => {
|
|
354
|
+
renderer.stop()
|
|
355
|
+
})
|
|
356
|
+
},
|
|
357
|
+
}
|
|
604
358
|
}
|
|
605
359
|
|
|
606
360
|
// High-level convenience API (Ink-style)
|
|
607
361
|
export interface RenderInstance {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
362
|
+
renderer: TuiRenderer
|
|
363
|
+
root: Root
|
|
364
|
+
rerender(element: ReactNode): void
|
|
365
|
+
unmount(): void
|
|
366
|
+
waitUntilExit(): Promise<void>
|
|
613
367
|
}
|
|
614
368
|
|
|
615
|
-
/**
|
|
616
|
-
* Render a React tree to the terminal in one call.
|
|
617
|
-
* Returns helpers similar to Ink: rerender, unmount, waitUntilExit.
|
|
618
|
-
*/
|
|
619
369
|
export function render(element: ReactNode, options?: RendererOptions): RenderInstance {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
370
|
+
const renderer = createRenderer(options)
|
|
371
|
+
const root = createRoot(renderer)
|
|
372
|
+
|
|
373
|
+
root.render(element, true)
|
|
374
|
+
|
|
375
|
+
let resolved = false
|
|
376
|
+
let resolveExit: (() => void) | null = null
|
|
377
|
+
const exitPromise = new Promise<void>((resolve) => {
|
|
378
|
+
resolveExit = () => {
|
|
379
|
+
if (resolved) return
|
|
380
|
+
resolved = true
|
|
381
|
+
resolve()
|
|
382
|
+
}
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
const onExit = () => {
|
|
386
|
+
if (!resolved) {
|
|
387
|
+
renderer.stop()
|
|
388
|
+
resolveExit?.()
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
process.once("exit", onExit)
|
|
392
|
+
|
|
393
|
+
const unmount = () => {
|
|
394
|
+
process.off("exit", onExit)
|
|
395
|
+
renderer.stop()
|
|
396
|
+
resolveExit?.()
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const rerender = (next: ReactNode) => {
|
|
400
|
+
if (resolved) return
|
|
401
|
+
root.render(next)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
renderer,
|
|
406
|
+
root,
|
|
407
|
+
rerender,
|
|
408
|
+
unmount,
|
|
409
|
+
waitUntilExit: () => exitPromise,
|
|
410
|
+
}
|
|
662
411
|
}
|