@effect-tui/react 0.1.7 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jsx-runtime.d.ts +6 -6
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/src/codeblock.js.map +1 -1
- package/dist/src/components/Divider.d.ts.map +1 -1
- package/dist/src/components/Divider.js +1 -1
- package/dist/src/components/Divider.js.map +1 -1
- package/dist/src/components/Markdown.d.ts.map +1 -1
- package/dist/src/components/Markdown.js +1 -1
- package/dist/src/components/Markdown.js.map +1 -1
- package/dist/src/components/MultilineTextInput.d.ts.map +1 -1
- package/dist/src/components/MultilineTextInput.js +2 -2
- package/dist/src/components/MultilineTextInput.js.map +1 -1
- package/dist/src/components/Static.d.ts.map +1 -1
- package/dist/src/components/Static.js +1 -1
- package/dist/src/components/Static.js.map +1 -1
- package/dist/src/components/TextInput.d.ts.map +1 -1
- package/dist/src/components/TextInput.js +2 -2
- package/dist/src/components/TextInput.js.map +1 -1
- package/dist/src/components/index.d.ts +4 -4
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/components/index.js +4 -4
- package/dist/src/components/index.js.map +1 -1
- package/dist/src/console/ConsoleCapture.d.ts.map +1 -1
- package/dist/src/console/ConsoleCapture.js +1 -1
- package/dist/src/console/ConsoleCapture.js.map +1 -1
- package/dist/src/console/ConsolePopover.d.ts.map +1 -1
- package/dist/src/console/ConsolePopover.js +5 -7
- package/dist/src/console/ConsolePopover.js.map +1 -1
- package/dist/src/console/index.d.ts +1 -1
- package/dist/src/console/index.d.ts.map +1 -1
- package/dist/src/console/index.js +1 -1
- package/dist/src/console/index.js.map +1 -1
- package/dist/src/console/useConsole.d.ts.map +1 -1
- package/dist/src/console/useConsole.js +2 -2
- package/dist/src/console/useConsole.js.map +1 -1
- package/dist/src/debug/DebugOverlay.d.ts +1 -1
- package/dist/src/debug/DebugOverlay.d.ts.map +1 -1
- package/dist/src/debug/DebugOverlay.js +1 -1
- package/dist/src/debug/DebugOverlay.js.map +1 -1
- package/dist/src/dev/Toast.d.ts.map +1 -1
- package/dist/src/dev/Toast.js +2 -2
- package/dist/src/dev/Toast.js.map +1 -1
- package/dist/src/dev/index.d.ts +1 -1
- package/dist/src/dev/index.d.ts.map +1 -1
- package/dist/src/dev/index.js +1 -1
- package/dist/src/dev/index.js.map +1 -1
- package/dist/src/dev.d.ts +1 -2
- package/dist/src/dev.d.ts.map +1 -1
- package/dist/src/dev.js +10 -10
- package/dist/src/dev.js.map +1 -1
- package/dist/src/highlight.d.ts +1 -1
- package/dist/src/highlight.d.ts.map +1 -1
- package/dist/src/highlight.js.map +1 -1
- package/dist/src/hooks/index.d.ts +4 -3
- package/dist/src/hooks/index.d.ts.map +1 -1
- package/dist/src/hooks/index.js +1 -0
- package/dist/src/hooks/index.js.map +1 -1
- package/dist/src/hooks/use-keyboard.d.ts.map +1 -1
- package/dist/src/hooks/use-keyboard.js.map +1 -1
- package/dist/src/hooks/use-mouse.d.ts +1 -1
- package/dist/src/hooks/use-mouse.d.ts.map +1 -1
- package/dist/src/hooks/use-mouse.js.map +1 -1
- package/dist/src/hooks/use-quit.d.ts +21 -0
- package/dist/src/hooks/use-quit.d.ts.map +1 -0
- package/dist/src/hooks/use-quit.js +29 -0
- package/dist/src/hooks/use-quit.js.map +1 -0
- package/dist/src/hooks/use-scroll.d.ts.map +1 -1
- package/dist/src/hooks/use-scroll.js +2 -2
- package/dist/src/hooks/use-scroll.js.map +1 -1
- package/dist/src/hooks/useFrameStats.d.ts.map +1 -1
- package/dist/src/hooks/useFrameStats.js.map +1 -1
- package/dist/src/hosts/base.d.ts +3 -4
- package/dist/src/hosts/base.d.ts.map +1 -1
- package/dist/src/hosts/base.js +42 -6
- package/dist/src/hosts/base.js.map +1 -1
- package/dist/src/hosts/box.d.ts +3 -3
- package/dist/src/hosts/box.d.ts.map +1 -1
- package/dist/src/hosts/box.js +1 -1
- package/dist/src/hosts/box.js.map +1 -1
- package/dist/src/hosts/canvas.d.ts +3 -3
- package/dist/src/hosts/canvas.d.ts.map +1 -1
- package/dist/src/hosts/canvas.js +1 -1
- package/dist/src/hosts/canvas.js.map +1 -1
- package/dist/src/hosts/codeblock.d.ts +2 -2
- package/dist/src/hosts/codeblock.d.ts.map +1 -1
- package/dist/src/hosts/codeblock.js +1 -1
- package/dist/src/hosts/codeblock.js.map +1 -1
- package/dist/src/hosts/flex-container.d.ts +3 -3
- package/dist/src/hosts/flex-container.d.ts.map +1 -1
- package/dist/src/hosts/flex-container.js +5 -2
- package/dist/src/hosts/flex-container.js.map +1 -1
- package/dist/src/hosts/hstack.d.ts +1 -1
- package/dist/src/hosts/index.d.ts +8 -8
- package/dist/src/hosts/index.d.ts.map +1 -1
- package/dist/src/hosts/index.js +13 -13
- package/dist/src/hosts/index.js.map +1 -1
- package/dist/src/hosts/overlay-item.d.ts +1 -1
- package/dist/src/hosts/overlay.d.ts +1 -1
- package/dist/src/hosts/overlay.d.ts.map +1 -1
- package/dist/src/hosts/overlay.js +1 -1
- package/dist/src/hosts/overlay.js.map +1 -1
- package/dist/src/hosts/scroll.d.ts +3 -2
- package/dist/src/hosts/scroll.d.ts.map +1 -1
- package/dist/src/hosts/scroll.js +12 -3
- package/dist/src/hosts/scroll.js.map +1 -1
- package/dist/src/hosts/single-child.d.ts +1 -1
- package/dist/src/hosts/single-child.d.ts.map +1 -1
- package/dist/src/hosts/spacer.d.ts +2 -1
- package/dist/src/hosts/spacer.d.ts.map +1 -1
- package/dist/src/hosts/spacer.js +10 -9
- package/dist/src/hosts/spacer.js.map +1 -1
- package/dist/src/hosts/text.d.ts +2 -2
- package/dist/src/hosts/text.d.ts.map +1 -1
- package/dist/src/hosts/text.js +1 -1
- package/dist/src/hosts/text.js.map +1 -1
- package/dist/src/hosts/vstack.d.ts +1 -1
- package/dist/src/hosts/zstack.d.ts +2 -2
- package/dist/src/hosts/zstack.d.ts.map +1 -1
- package/dist/src/hosts/zstack.js +1 -1
- package/dist/src/hosts/zstack.js.map +1 -1
- package/dist/src/index.d.ts +17 -21
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -15
- package/dist/src/index.js.map +1 -1
- package/dist/src/inline/index.d.ts.map +1 -1
- package/dist/src/inline/index.js +1 -1
- package/dist/src/inline/index.js.map +1 -1
- package/dist/src/motion/color-motion-value.d.ts +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 -1
- package/dist/src/motion/color.d.ts.map +1 -1
- package/dist/src/motion/color.js +1 -1
- package/dist/src/motion/color.js.map +1 -1
- package/dist/src/motion/color.test.js +2 -2
- package/dist/src/motion/color.test.js.map +1 -1
- package/dist/src/motion/hooks.d.ts +3 -3
- package/dist/src/motion/hooks.d.ts.map +1 -1
- package/dist/src/motion/hooks.js +1 -1
- package/dist/src/motion/hooks.js.map +1 -1
- package/dist/src/motion/index.d.ts +2 -2
- package/dist/src/motion/index.d.ts.map +1 -1
- package/dist/src/motion/index.js +3 -3
- package/dist/src/motion/index.js.map +1 -1
- package/dist/src/motion/motion-value.d.ts +5 -5
- package/dist/src/motion/motion-value.d.ts.map +1 -1
- package/dist/src/motion/motion-value.js +4 -4
- package/dist/src/motion/motion-value.js.map +1 -1
- package/dist/src/motion/motion-value.test.js +1 -1
- package/dist/src/motion/motion-value.test.js.map +1 -1
- package/dist/src/reconciler/host-config.d.ts +1 -1
- package/dist/src/reconciler/host-config.d.ts.map +1 -1
- package/dist/src/reconciler/host-config.js +1 -1
- package/dist/src/reconciler/host-config.js.map +1 -1
- package/dist/src/reconciler/types.d.ts +10 -5
- package/dist/src/reconciler/types.d.ts.map +1 -1
- package/dist/src/remote/Procedures.d.ts.map +1 -1
- package/dist/src/remote/Procedures.js.map +1 -1
- package/dist/src/remote/Router.d.ts +1 -1
- package/dist/src/remote/Router.d.ts.map +1 -1
- package/dist/src/remote/Router.js +1 -1
- package/dist/src/remote/Router.js.map +1 -1
- package/dist/src/remote/Server.d.ts +1 -1
- package/dist/src/remote/Server.d.ts.map +1 -1
- package/dist/src/remote/Server.js +3 -3
- package/dist/src/remote/Server.js.map +1 -1
- package/dist/src/remote/index.d.ts +2 -2
- package/dist/src/remote/index.d.ts.map +1 -1
- package/dist/src/remote/index.js +5 -5
- package/dist/src/remote/index.js.map +1 -1
- package/dist/src/renderer/core/FrameBuilder.d.ts.map +1 -1
- package/dist/src/renderer/core/FrameBuilder.js.map +1 -1
- package/dist/src/renderer/input/InputProcessor.d.ts +1 -0
- package/dist/src/renderer/input/InputProcessor.d.ts.map +1 -1
- package/dist/src/renderer/input/InputProcessor.js +7 -2
- package/dist/src/renderer/input/InputProcessor.js.map +1 -1
- package/dist/src/renderer/lifecycle/TerminalSetup.d.ts +2 -1
- package/dist/src/renderer/lifecycle/TerminalSetup.d.ts.map +1 -1
- package/dist/src/renderer/lifecycle/TerminalSetup.js +26 -17
- package/dist/src/renderer/lifecycle/TerminalSetup.js.map +1 -1
- package/dist/src/renderer/lifecycle/index.d.ts +1 -1
- package/dist/src/renderer/lifecycle/index.d.ts.map +1 -1
- package/dist/src/renderer/lifecycle/index.js +1 -1
- package/dist/src/renderer/lifecycle/index.js.map +1 -1
- package/dist/src/renderer/modes/FullscreenRenderer.d.ts +1 -1
- package/dist/src/renderer/modes/FullscreenRenderer.d.ts.map +1 -1
- package/dist/src/renderer/modes/InlineRenderer.d.ts +1 -1
- package/dist/src/renderer/modes/InlineRenderer.d.ts.map +1 -1
- package/dist/src/renderer/modes/InlineRenderer.js +1 -1
- package/dist/src/renderer/modes/InlineRenderer.js.map +1 -1
- package/dist/src/renderer/modes/StaticContentRenderer.d.ts.map +1 -1
- package/dist/src/renderer/modes/StaticContentRenderer.js.map +1 -1
- package/dist/src/renderer/modes/index.d.ts +1 -1
- package/dist/src/renderer/modes/index.d.ts.map +1 -1
- package/dist/src/renderer/modes/index.js.map +1 -1
- package/dist/src/renderer-context.js +1 -1
- package/dist/src/renderer-context.js.map +1 -1
- package/dist/src/renderer-types.d.ts +1 -1
- package/dist/src/renderer-types.d.ts.map +1 -1
- package/dist/src/renderer.d.ts +4 -2
- package/dist/src/renderer.d.ts.map +1 -1
- package/dist/src/renderer.js +45 -13
- package/dist/src/renderer.js.map +1 -1
- package/dist/src/test/index.d.ts +2 -2
- package/dist/src/test/index.d.ts.map +1 -1
- package/dist/src/test/index.js +1 -1
- package/dist/src/test/index.js.map +1 -1
- package/dist/src/test/render-tui.d.ts +7 -2
- package/dist/src/test/render-tui.d.ts.map +1 -1
- package/dist/src/test/render-tui.js +5 -2
- package/dist/src/test/render-tui.js.map +1 -1
- package/dist/src/utils/flex-layout.d.ts +1 -1
- package/dist/src/utils/flex-layout.d.ts.map +1 -1
- package/dist/src/utils/flex-layout.js +21 -7
- package/dist/src/utils/flex-layout.js.map +1 -1
- package/dist/src/utils/index.d.ts +4 -4
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -2
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/styles.d.ts.map +1 -1
- package/dist/src/utils/styles.js.map +1 -1
- package/dist/src/visualize/index.js +2 -2
- package/dist/src/visualize/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/jsx-runtime.ts +6 -6
- package/package.json +3 -2
- package/src/codeblock.tsx +1 -1
- package/src/components/Divider.tsx +1 -1
- package/src/components/Markdown.tsx +2 -2
- package/src/components/MultilineTextInput.tsx +9 -9
- package/src/components/Static.tsx +1 -1
- package/src/components/TextInput.tsx +8 -8
- package/src/components/index.ts +4 -4
- package/src/console/ConsoleCapture.ts +1 -1
- package/src/console/ConsolePopover.tsx +27 -26
- package/src/console/index.ts +1 -3
- package/src/console/useConsole.ts +2 -2
- package/src/debug/DebugOverlay.ts +3 -6
- package/src/dev/Toast.tsx +12 -10
- package/src/dev/index.ts +1 -1
- package/src/dev.tsx +11 -12
- package/src/highlight.ts +1 -1
- package/src/hooks/index.ts +4 -3
- package/src/hooks/use-keyboard.ts +1 -1
- package/src/hooks/use-mouse.ts +1 -1
- package/src/hooks/use-quit.ts +33 -0
- package/src/hooks/use-scroll.ts +9 -11
- package/src/hooks/useFrameStats.ts +1 -1
- package/src/hosts/base.ts +38 -8
- package/src/hosts/box.ts +4 -4
- package/src/hosts/canvas.ts +3 -3
- package/src/hosts/codeblock.ts +3 -3
- package/src/hosts/flex-container.ts +6 -4
- package/src/hosts/hstack.ts +1 -1
- package/src/hosts/index.ts +14 -14
- package/src/hosts/overlay-item.ts +1 -1
- package/src/hosts/overlay.ts +2 -2
- package/src/hosts/scroll.ts +16 -5
- package/src/hosts/single-child.ts +1 -1
- package/src/hosts/spacer.ts +12 -10
- package/src/hosts/text.ts +3 -3
- package/src/hosts/vstack.ts +1 -1
- package/src/hosts/zstack.ts +2 -2
- package/src/index.ts +60 -60
- package/src/inline/index.tsx +1 -1
- package/src/motion/color-motion-value.ts +1 -1
- package/src/motion/color.test.ts +2 -2
- package/src/motion/color.ts +2 -2
- package/src/motion/hooks.ts +3 -3
- package/src/motion/index.ts +8 -8
- package/src/motion/motion-value.test.ts +1 -1
- package/src/motion/motion-value.ts +11 -11
- package/src/reconciler/host-config.ts +3 -3
- package/src/reconciler/types.ts +10 -5
- package/src/remote/Procedures.ts +1 -7
- package/src/remote/Router.ts +3 -7
- package/src/remote/Server.ts +7 -12
- package/src/remote/index.ts +6 -10
- package/src/renderer/core/FrameBuilder.ts +1 -1
- package/src/renderer/input/InputProcessor.ts +7 -2
- package/src/renderer/lifecycle/TerminalSetup.ts +29 -18
- package/src/renderer/lifecycle/index.ts +1 -1
- package/src/renderer/modes/FullscreenRenderer.ts +1 -1
- package/src/renderer/modes/InlineRenderer.ts +2 -2
- package/src/renderer/modes/StaticContentRenderer.ts +1 -1
- package/src/renderer/modes/index.ts +1 -1
- package/src/renderer-context.ts +1 -1
- package/src/renderer-types.ts +1 -1
- package/src/renderer.ts +62 -25
- package/src/test/index.ts +4 -4
- package/src/test/render-tui.ts +10 -3
- package/src/utils/flex-layout.ts +21 -8
- package/src/utils/index.ts +8 -8
- package/src/utils/styles.ts +2 -2
- package/src/visualize/index.tsx +2 -2
package/src/remote/index.ts
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
// 2. Connect using etui CLI or raw socket at /tmp/effect-tui-sessions/<pid>.sock
|
|
9
9
|
|
|
10
10
|
export { TuiRpcs } from "./Procedures.js"
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
11
|
+
export { HandlersLive, TuiSession } from "./Router.js"
|
|
12
|
+
export { getSocketPath, makeServerLayer } from "./Server.js"
|
|
13
13
|
|
|
14
|
-
import { Effect, Exit, Layer, Scope } from "effect"
|
|
15
14
|
import * as fs from "node:fs"
|
|
15
|
+
import { Effect, Exit, Layer, Scope } from "effect"
|
|
16
16
|
import type { TuiRenderer } from "../renderer-types.js"
|
|
17
17
|
import type { TuiSessionImpl } from "./Router.js"
|
|
18
|
-
import {
|
|
18
|
+
import { getSocketPath, makeServerLayer } from "./Server.js"
|
|
19
19
|
|
|
20
20
|
export interface EnableRemoteOptions {
|
|
21
21
|
/** Custom socket path (defaults to /tmp/effect-tui-sessions/<pid>.sock) */
|
|
@@ -55,13 +55,9 @@ function deriveSessionName(entryPath: string): string {
|
|
|
55
55
|
* @param options - Socket path and entry path for session identification
|
|
56
56
|
* @returns A cleanup function to stop the server
|
|
57
57
|
*/
|
|
58
|
-
export function enableRemote(
|
|
59
|
-
renderer: TuiRenderer,
|
|
60
|
-
options?: EnableRemoteOptions | string,
|
|
61
|
-
): () => void {
|
|
58
|
+
export function enableRemote(renderer: TuiRenderer, options?: EnableRemoteOptions | string): () => void {
|
|
62
59
|
// Support old API: enableRemote(renderer, socketPath?)
|
|
63
|
-
const opts: EnableRemoteOptions =
|
|
64
|
-
typeof options === "string" ? { socketPath: options } : options ?? {}
|
|
60
|
+
const opts: EnableRemoteOptions = typeof options === "string" ? { socketPath: options } : (options ?? {})
|
|
65
61
|
const actualPath = opts.socketPath ?? getSocketPath()
|
|
66
62
|
|
|
67
63
|
// Derive session name from entry path
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks"
|
|
2
2
|
import type { CellBuffer, Palette } from "@effect-tui/core"
|
|
3
|
-
import type { HostInstance } from "../../reconciler/types.js"
|
|
4
3
|
import * as Prof from "../../profiler.js"
|
|
4
|
+
import type { HostInstance } from "../../reconciler/types.js"
|
|
5
5
|
|
|
6
6
|
export interface FrameTimings {
|
|
7
7
|
clear: number
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ANSI, decodeInput, type KeyMsg, type MouseMsg } from "@effect-tui/core"
|
|
2
2
|
|
|
3
3
|
export interface InputProcessorConfig {
|
|
4
4
|
exitOnCtrlC: boolean
|
|
@@ -7,6 +7,7 @@ export interface InputProcessorConfig {
|
|
|
7
7
|
dispatchPaste: (text: string) => void
|
|
8
8
|
flushSync: <T>(fn: () => T) => T
|
|
9
9
|
onInputProcessed: () => void
|
|
10
|
+
onQuit?: () => void // Called instead of process.exit() for proper cleanup
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -86,7 +87,11 @@ export class InputProcessor {
|
|
|
86
87
|
// Default Ctrl+C handling - exit unless user called preventDefault()
|
|
87
88
|
// Only match Ctrl+C (not Ctrl+Shift+C which should be available for copy)
|
|
88
89
|
if (this.config.exitOnCtrlC && !wrapped.defaultPrevented && event.ctrl && !event.shift && event.text === "c") {
|
|
89
|
-
|
|
90
|
+
if (this.config.onQuit) {
|
|
91
|
+
this.config.onQuit()
|
|
92
|
+
} else {
|
|
93
|
+
process.exit(0)
|
|
94
|
+
}
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ANSI, Terminal } from "@effect-tui/core"
|
|
2
|
-
import type {
|
|
2
|
+
import type { TuiReadStream, TuiWriteStream } from "../../renderer-types.js"
|
|
3
3
|
|
|
4
4
|
export interface TerminalSetupConfig {
|
|
5
5
|
mode: "fullscreen" | "inline"
|
|
@@ -70,36 +70,47 @@ export class TerminalSetup {
|
|
|
70
70
|
/**
|
|
71
71
|
* Restore terminal to normal state.
|
|
72
72
|
* Call this when stopping the renderer.
|
|
73
|
+
* Uses a single write call to ensure atomic output and reliable flushing.
|
|
73
74
|
*/
|
|
74
75
|
teardown(): void {
|
|
75
76
|
if (this.config.skipTerminalSetup) return
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.stdout.write(ANSI.reflow.enable)
|
|
82
|
-
this.stdout.write("\r\n")
|
|
78
|
+
// Disable raw mode FIRST - this is critical for proper terminal restoration
|
|
79
|
+
// Must happen before writing escape sequences so the terminal processes them correctly
|
|
80
|
+
if (this.stdin.isTTY && this.stdin.setRawMode) {
|
|
81
|
+
this.stdin.setRawMode(false)
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
// Build all escape sequences into a single string for atomic write
|
|
85
|
+
// This ensures all sequences are flushed together before process exit
|
|
86
|
+
let output = ""
|
|
87
|
+
|
|
88
|
+
// Disable enhanced keyboard protocols first
|
|
89
|
+
if (this.config.enableKittyKeyboard !== false) {
|
|
90
|
+
output += ANSI.keyboard.disable
|
|
91
|
+
output += ANSI.modifyOtherKeys.disable
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
if (this.config.enableMouse) {
|
|
90
|
-
|
|
95
|
+
output += ANSI.mouse.disable
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.stdout.write(ANSI.keyboard.disable)
|
|
96
|
-
this.stdout.write(ANSI.modifyOtherKeys.disable)
|
|
98
|
+
if (this.config.enablePaste) {
|
|
99
|
+
output += ANSI.paste.disable
|
|
97
100
|
}
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
// Exit fullscreen or re-enable reflow
|
|
103
|
+
if (this.config.mode === "fullscreen") {
|
|
104
|
+
output += Terminal.exitFullscreen
|
|
105
|
+
} else {
|
|
106
|
+
output += ANSI.reflow.enable
|
|
107
|
+
output += "\r\n"
|
|
103
108
|
}
|
|
109
|
+
|
|
110
|
+
// Always show cursor at the end
|
|
111
|
+
output += Terminal.showCursor
|
|
112
|
+
|
|
113
|
+
// Single atomic write - more reliable for process exit scenarios
|
|
114
|
+
this.stdout.write(output)
|
|
104
115
|
}
|
|
105
116
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { EventBus } from "./EventBus.js"
|
|
2
|
+
export { ResizeManager, type ResizeResult, type ResizeState } from "./ResizeManager.js"
|
|
2
3
|
export { TerminalSetup, type TerminalSetupConfig } from "./TerminalSetup.js"
|
|
3
|
-
export { ResizeManager, type ResizeState, type ResizeResult } from "./ResizeManager.js"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ANSI, emitRowWithReset, rowChanged } from "@effect-tui/core"
|
|
2
|
-
import type {
|
|
2
|
+
import type { RenderContext, RendererMode, RenderOutput } from "./RendererMode.js"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Fullscreen rendering mode using alternate buffer.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ANSI, emitRowWithReset,
|
|
2
|
-
import type {
|
|
1
|
+
import { ANSI, emitRowWithReset, findChangeWindow, rowContentWidth } from "@effect-tui/core"
|
|
2
|
+
import type { RenderContext, RendererMode, RenderOutput } from "./RendererMode.js"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Inline rendering mode that renders in-place without alternate buffer.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CellBuffer, emitRowWithReset,
|
|
1
|
+
import { CellBuffer, emitRowWithReset, type Palette, rowContentWidth } from "@effect-tui/core"
|
|
2
2
|
import type { HostInstance } from "../../reconciler/types.js"
|
|
3
3
|
import type { TuiWriteStream } from "../../renderer-types.js"
|
|
4
4
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { RendererMode, RenderContext, RenderOutput } from "./RendererMode.js"
|
|
2
1
|
export { FullscreenRenderer } from "./FullscreenRenderer.js"
|
|
3
2
|
export { InlineRenderer } from "./InlineRenderer.js"
|
|
3
|
+
export type { RenderContext, RendererMode, RenderOutput } from "./RendererMode.js"
|
|
4
4
|
export { StaticContentRenderer } from "./StaticContentRenderer.js"
|
package/src/renderer-context.ts
CHANGED
package/src/renderer-types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KeyMsg, MouseMsg } from "@effect-tui/core"
|
|
2
|
-
import type {
|
|
2
|
+
import type { HostContext, HostInstance } from "./reconciler/types.js"
|
|
3
3
|
|
|
4
4
|
/** Minimal write stream interface for renderer output */
|
|
5
5
|
export interface TuiWriteStream {
|
package/src/renderer.ts
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
import React, { type ReactNode } from "react"
|
|
2
1
|
import { performance } from "node:perf_hooks"
|
|
3
|
-
import { ANSI, type KeyMsg, type MouseMsg
|
|
4
|
-
import {
|
|
5
|
-
import type { HostContext } from "./reconciler/types.js"
|
|
6
|
-
import * as Prof from "./profiler.js"
|
|
2
|
+
import { ANSI, bufferToString, type KeyMsg, type MouseMsg } from "@effect-tui/core"
|
|
3
|
+
import React, { type ReactNode } from "react"
|
|
7
4
|
import { DEFAULT_FPS } from "./constants.js"
|
|
5
|
+
import * as Prof from "./profiler.js"
|
|
6
|
+
import { flushSync, reconciler } from "./reconciler/host-config.js"
|
|
7
|
+
import type { HostContext } from "./reconciler/types.js"
|
|
8
|
+
// Extracted modules
|
|
9
|
+
import { FrameBuilder, RendererState } from "./renderer/core/index.js"
|
|
10
|
+
import { InputProcessor } from "./renderer/input/index.js"
|
|
11
|
+
import { EventBus, TerminalSetup } from "./renderer/lifecycle/index.js"
|
|
12
|
+
import { FullscreenRenderer, InlineRenderer, StaticContentRenderer } from "./renderer/modes/index.js"
|
|
13
|
+
import { RendererContext } from "./renderer-context.js"
|
|
8
14
|
import type {
|
|
9
|
-
|
|
15
|
+
Container,
|
|
16
|
+
FrameStats,
|
|
17
|
+
RendererOptions,
|
|
10
18
|
TuiReadStream,
|
|
11
19
|
TuiRenderer,
|
|
12
20
|
TuiRendererInternal,
|
|
13
|
-
|
|
14
|
-
RendererOptions,
|
|
15
|
-
FrameStats,
|
|
21
|
+
TuiWriteStream,
|
|
16
22
|
} 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
23
|
|
|
25
|
-
// Re-export types and context for backwards compatibility
|
|
26
|
-
export type { TuiWriteStream, TuiReadStream, TuiRenderer, RendererOptions, FrameStats } from "./renderer-types.js"
|
|
27
24
|
export { RendererContext, useRenderer, useTerminalSize } from "./renderer-context.js"
|
|
25
|
+
// Re-export types and context for backwards compatibility
|
|
26
|
+
export type { FrameStats, RendererOptions, TuiReadStream, TuiRenderer, TuiWriteStream } from "./renderer-types.js"
|
|
28
27
|
|
|
29
28
|
export function createRenderer(options?: RendererOptions): TuiRenderer {
|
|
30
29
|
const fps = options?.fps ?? DEFAULT_FPS
|
|
@@ -71,6 +70,11 @@ export function createRenderer(options?: RendererOptions): TuiRenderer {
|
|
|
71
70
|
onInputProcessed: () => {
|
|
72
71
|
if (!manualMode) renderFrame()
|
|
73
72
|
},
|
|
73
|
+
onQuit: () => {
|
|
74
|
+
// Clean up terminal state before exiting
|
|
75
|
+
renderer.stop()
|
|
76
|
+
process.exit(0)
|
|
77
|
+
},
|
|
74
78
|
})
|
|
75
79
|
|
|
76
80
|
// The render frame logic
|
|
@@ -162,7 +166,7 @@ export function createRenderer(options?: RendererOptions): TuiRenderer {
|
|
|
162
166
|
})
|
|
163
167
|
|
|
164
168
|
// Combine static + dynamic output for atomic write
|
|
165
|
-
|
|
169
|
+
const output = staticOutput + modeOutput + state.palette.sgr(0)
|
|
166
170
|
contentH = contentHeight
|
|
167
171
|
const diffAnsiMs = performance.now() - diffStartMs
|
|
168
172
|
Prof.endPhase("diff+ansi", t)
|
|
@@ -300,6 +304,27 @@ export function createRenderer(options?: RendererOptions): TuiRenderer {
|
|
|
300
304
|
}, frameMs)
|
|
301
305
|
}
|
|
302
306
|
|
|
307
|
+
// Process exit handlers - ensure terminal is restored on any exit
|
|
308
|
+
// These handlers are critical for proper cleanup when process.exit() is called
|
|
309
|
+
let cleanedUp = false
|
|
310
|
+
const cleanup = () => {
|
|
311
|
+
if (cleanedUp) return
|
|
312
|
+
cleanedUp = true
|
|
313
|
+
renderer.stop()
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Handle normal process exit (synchronous - runs before exit completes)
|
|
317
|
+
const onExit = () => cleanup()
|
|
318
|
+
process.on("exit", onExit)
|
|
319
|
+
|
|
320
|
+
// Handle SIGINT (Ctrl+C from shell, not from TUI input) and SIGTERM
|
|
321
|
+
const onSignal = () => {
|
|
322
|
+
cleanup()
|
|
323
|
+
process.exit(0)
|
|
324
|
+
}
|
|
325
|
+
process.on("SIGINT", onSignal)
|
|
326
|
+
process.on("SIGTERM", onSignal)
|
|
327
|
+
|
|
303
328
|
;(renderer as TuiRendererInternal)._container = null
|
|
304
329
|
return renderer
|
|
305
330
|
}
|
|
@@ -365,6 +390,8 @@ export interface RenderInstance {
|
|
|
365
390
|
rerender(element: ReactNode): void
|
|
366
391
|
unmount(): void
|
|
367
392
|
waitUntilExit(): Promise<void>
|
|
393
|
+
/** Cleanly exit the application, restoring terminal state before process.exit() */
|
|
394
|
+
quit(code?: number): void
|
|
368
395
|
}
|
|
369
396
|
|
|
370
397
|
export function render(element: ReactNode, options?: RendererOptions): RenderInstance {
|
|
@@ -383,18 +410,20 @@ export function render(element: ReactNode, options?: RendererOptions): RenderIns
|
|
|
383
410
|
}
|
|
384
411
|
})
|
|
385
412
|
|
|
413
|
+
// Resolve the exit promise on process exit
|
|
414
|
+
// Note: Terminal cleanup is handled by createRenderer's exit handlers
|
|
386
415
|
const onExit = () => {
|
|
387
|
-
|
|
388
|
-
renderer.stop()
|
|
389
|
-
resolveExit?.()
|
|
390
|
-
}
|
|
416
|
+
resolveExit?.()
|
|
391
417
|
}
|
|
392
418
|
process.once("exit", onExit)
|
|
393
419
|
|
|
394
420
|
const unmount = () => {
|
|
395
421
|
process.off("exit", onExit)
|
|
396
|
-
|
|
397
|
-
|
|
422
|
+
if (!resolved) {
|
|
423
|
+
resolved = true
|
|
424
|
+
renderer.stop()
|
|
425
|
+
resolveExit?.()
|
|
426
|
+
}
|
|
398
427
|
}
|
|
399
428
|
|
|
400
429
|
const rerender = (next: ReactNode) => {
|
|
@@ -402,11 +431,19 @@ export function render(element: ReactNode, options?: RendererOptions): RenderIns
|
|
|
402
431
|
root.render(next)
|
|
403
432
|
}
|
|
404
433
|
|
|
434
|
+
// Clean quit function - renderer.stop() + process.exit()
|
|
435
|
+
// The createRenderer exit handler will also run, but it's idempotent
|
|
436
|
+
const quit = (code = 0) => {
|
|
437
|
+
renderer.stop()
|
|
438
|
+
process.exit(code)
|
|
439
|
+
}
|
|
440
|
+
|
|
405
441
|
return {
|
|
406
442
|
renderer,
|
|
407
443
|
root,
|
|
408
444
|
rerender,
|
|
409
445
|
unmount,
|
|
410
446
|
waitUntilExit: () => exitPromise,
|
|
447
|
+
quit,
|
|
411
448
|
}
|
|
412
449
|
}
|
package/src/test/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export { renderTUI, type RenderTUIOptions, type RenderTUIResult } from "./render-tui.js"
|
|
2
1
|
export {
|
|
3
|
-
MockStdout,
|
|
4
|
-
MockStdin,
|
|
5
2
|
encodeKey,
|
|
6
|
-
stripAnsi,
|
|
7
3
|
getVisibleLines,
|
|
4
|
+
MockStdin,
|
|
5
|
+
MockStdout,
|
|
6
|
+
stripAnsi,
|
|
8
7
|
} from "./mock-streams.js"
|
|
8
|
+
export { type RenderTUIOptions, type RenderTUIResult, renderTUI } from "./render-tui.js"
|
package/src/test/render-tui.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import type { ReactElement } from "react"
|
|
2
1
|
import type { KeyMsg } from "@effect-tui/core"
|
|
3
|
-
import {
|
|
2
|
+
import type { ReactElement } from "react"
|
|
4
3
|
import { flushPassiveEffects, flushSync } from "../reconciler/host-config.js"
|
|
5
|
-
import {
|
|
4
|
+
import { createRenderer, createRoot } from "../renderer.js"
|
|
5
|
+
import { getVisibleLines, MockStdin, MockStdout, stripAnsi } from "./mock-streams.js"
|
|
6
6
|
|
|
7
7
|
export interface RenderTUIOptions {
|
|
8
8
|
width?: number
|
|
9
9
|
height?: number
|
|
10
|
+
mode?: "fullscreen" | "inline"
|
|
11
|
+
diff?: boolean
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export interface RenderTUIResult {
|
|
@@ -26,6 +28,8 @@ export interface RenderTUIResult {
|
|
|
26
28
|
stdout: MockStdout
|
|
27
29
|
/** Access mock stdin for advanced input simulation */
|
|
28
30
|
stdin: MockStdin
|
|
31
|
+
/** Access renderer for advanced assertions */
|
|
32
|
+
renderer: ReturnType<typeof createRenderer>
|
|
29
33
|
/** Resize the terminal */
|
|
30
34
|
resize(width: number, height: number): void
|
|
31
35
|
}
|
|
@@ -60,6 +64,8 @@ export function renderTUI(element: ReactElement, options?: RenderTUIOptions): Re
|
|
|
60
64
|
stdin: stdin as any,
|
|
61
65
|
manualMode: true,
|
|
62
66
|
skipTerminalSetup: true,
|
|
67
|
+
mode: options?.mode,
|
|
68
|
+
diff: options?.diff,
|
|
63
69
|
})
|
|
64
70
|
|
|
65
71
|
const root = createRoot(renderer)
|
|
@@ -106,6 +112,7 @@ export function renderTUI(element: ReactElement, options?: RenderTUIOptions): Re
|
|
|
106
112
|
|
|
107
113
|
stdout,
|
|
108
114
|
stdin,
|
|
115
|
+
renderer,
|
|
109
116
|
|
|
110
117
|
resize(w: number, h: number) {
|
|
111
118
|
stdout.resize(w, h)
|
package/src/utils/flex-layout.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Generic flex layout algorithm for VStack and HStack.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type
|
|
5
|
+
import { type Axis, crossDim, crossPos, crossSize, mainDim, mainPos, mainSize, makeRect } from "@effect-tui/core"
|
|
6
6
|
import type { BaseHost } from "../hosts/base.js"
|
|
7
|
-
import
|
|
7
|
+
import type { HostInstance, Rect, Size } from "../reconciler/types.js"
|
|
8
8
|
|
|
9
9
|
export type FlexAxis = Axis
|
|
10
10
|
export type FlexAlignment = "start" | "center" | "end"
|
|
@@ -53,6 +53,19 @@ export function measureFlex(
|
|
|
53
53
|
return { sizes, totalSize }
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Get the greedy weight for a child.
|
|
58
|
+
* - undefined/false = 0 (not greedy, hugs content)
|
|
59
|
+
* - true = 1
|
|
60
|
+
* - number = that weight
|
|
61
|
+
*/
|
|
62
|
+
function getGreedyWeight(child: HostInstance): number {
|
|
63
|
+
const greedy = (child as BaseHost).greedy
|
|
64
|
+
if (greedy === undefined || greedy === false) return 0
|
|
65
|
+
if (greedy === true) return 1
|
|
66
|
+
return greedy
|
|
67
|
+
}
|
|
68
|
+
|
|
56
69
|
/**
|
|
57
70
|
* Layout children along a flex axis using cached sizes.
|
|
58
71
|
*/
|
|
@@ -67,17 +80,17 @@ export function layoutFlex(
|
|
|
67
80
|
): void {
|
|
68
81
|
// Calculate totals
|
|
69
82
|
let totalNaturalMain = 0
|
|
70
|
-
let
|
|
83
|
+
let totalGreedyWeight = 0
|
|
71
84
|
const totalSpacing = Math.max(0, (children.length - 1) * spacing)
|
|
72
85
|
|
|
73
86
|
for (let i = 0; i < children.length; i++) {
|
|
74
87
|
const child = children[i]
|
|
75
88
|
const size = cachedSizes[i] ?? child.measure(rect.w, rect.h)
|
|
76
89
|
totalNaturalMain += mainSize(axis, size)
|
|
77
|
-
|
|
90
|
+
totalGreedyWeight += getGreedyWeight(child)
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
// Calculate extra space to distribute
|
|
93
|
+
// Calculate extra space to distribute to greedy children
|
|
81
94
|
const availableMain = mainDim(axis, rect)
|
|
82
95
|
const extraSpace = Math.max(0, availableMain - totalNaturalMain - totalSpacing)
|
|
83
96
|
|
|
@@ -89,10 +102,10 @@ export function layoutFlex(
|
|
|
89
102
|
for (let i = 0; i < children.length; i++) {
|
|
90
103
|
const child = children[i]
|
|
91
104
|
const size = cachedSizes[i] ?? { w: 0, h: 0 }
|
|
92
|
-
const
|
|
93
|
-
const
|
|
105
|
+
const greedyWeight = getGreedyWeight(child)
|
|
106
|
+
const greedyExtra = totalGreedyWeight > 0 ? (extraSpace * greedyWeight) / totalGreedyWeight : 0
|
|
94
107
|
|
|
95
|
-
const childMainDim = mainSize(axis, size) +
|
|
108
|
+
const childMainDim = mainSize(axis, size) + greedyExtra
|
|
96
109
|
const childCrossDim = crossSize(axis, size)
|
|
97
110
|
|
|
98
111
|
// Calculate cross position based on alignment
|
package/src/utils/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export { type HAlign, type VAlign
|
|
2
|
-
export { type
|
|
1
|
+
export { alignInRect, type HAlign, type VAlign } from "./alignment.js"
|
|
2
|
+
export { type BorderChars, type BorderKind, borderChars, type ClipRect, drawBorder } from "./border.js"
|
|
3
|
+
export { type FlexAlignment, type FlexAxis, type FlexMeasureResult, layoutFlex, measureFlex } from "./flex-layout.js"
|
|
3
4
|
export { type Padding, type PaddingInput, resolvePadding } from "./padding.js"
|
|
4
5
|
export {
|
|
5
|
-
type StyleOptions,
|
|
6
|
-
type StyleInput,
|
|
7
|
-
toColorValue,
|
|
8
|
-
styleSpecFromProps,
|
|
9
|
-
styleIdFromProps,
|
|
10
6
|
resolveBgStyle,
|
|
11
7
|
resolveInheritedBgStyle,
|
|
8
|
+
type StyleInput,
|
|
9
|
+
type StyleOptions,
|
|
10
|
+
styleIdFromProps,
|
|
11
|
+
styleSpecFromProps,
|
|
12
|
+
toColorValue,
|
|
12
13
|
} from "./styles.js"
|
|
13
|
-
export { type FlexAxis, type FlexAlignment, type FlexMeasureResult, measureFlex, layoutFlex } from "./flex-layout.js"
|
package/src/utils/styles.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Style utilities for building palette-compatible style objects.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type Color, type ColorValue, type Palette, parseColor, type StyleSpec } from "@effect-tui/core"
|
|
6
6
|
|
|
7
7
|
export interface StyleOptions {
|
|
8
8
|
fg?: ColorValue
|
|
@@ -56,8 +56,8 @@ export function resolveBgStyle(palette: Palette, bg?: Color): { value?: ColorVal
|
|
|
56
56
|
return { value, styleId }
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
import type { HostInstance } from "../reconciler/types.js"
|
|
60
59
|
import { getInheritedBg } from "../hosts/base.js"
|
|
60
|
+
import type { HostInstance } from "../reconciler/types.js"
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Resolve background style, inheriting from parent if not explicitly set.
|
package/src/visualize/index.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Effect visualization wrapper using React
|
|
2
2
|
// Uses Effect Layer for proper terminal state management across multiple calls
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { ANSI, Colors } from "@effect-tui/core"
|
|
5
5
|
import { Cause, Context, Duration, Effect, Exit, Fiber, Layer, Option } from "effect"
|
|
6
|
-
import {
|
|
6
|
+
import { useEffect, useState } from "react"
|
|
7
7
|
import { createRenderer, createRoot } from "../renderer.js"
|
|
8
8
|
|
|
9
9
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠸", "⠴", "⠦", "⠇"] as const
|