@effect-tui/react 0.15.2 → 2.0.0
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/README.md +11 -2
- package/dist/src/codeblock.d.ts +1 -1
- package/dist/src/codeblock.d.ts.map +1 -1
- package/dist/src/codeblock.js +2 -2
- package/dist/src/codeblock.js.map +1 -1
- package/dist/src/components/ListView.d.ts +4 -4
- package/dist/src/components/ListView.d.ts.map +1 -1
- package/dist/src/components/ListView.js +16 -17
- package/dist/src/components/ListView.js.map +1 -1
- package/dist/src/components/Markdown.js +3 -3
- 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 +133 -305
- package/dist/src/components/MultilineTextInput.js.map +1 -1
- package/dist/src/components/TextInput.d.ts.map +1 -1
- package/dist/src/components/TextInput.js +51 -98
- package/dist/src/components/TextInput.js.map +1 -1
- package/dist/src/components/text-editing.d.ts +61 -0
- package/dist/src/components/text-editing.d.ts.map +1 -1
- package/dist/src/components/text-editing.js +131 -0
- package/dist/src/components/text-editing.js.map +1 -1
- package/dist/src/console/ConsolePopover.d.ts +7 -1
- package/dist/src/console/ConsolePopover.d.ts.map +1 -1
- package/dist/src/console/ConsolePopover.js +55 -74
- package/dist/src/console/ConsolePopover.js.map +1 -1
- package/dist/src/debug/DebugOverlay.d.ts.map +1 -1
- package/dist/src/debug/DebugOverlay.js +3 -57
- package/dist/src/debug/DebugOverlay.js.map +1 -1
- package/dist/src/debug/DiagnosticsPanel.js +1 -1
- package/dist/src/debug/DiagnosticsPanel.js.map +1 -1
- package/dist/src/dev.d.ts +5 -117
- package/dist/src/dev.d.ts.map +1 -1
- package/dist/src/dev.js +3 -333
- package/dist/src/dev.js.map +1 -1
- package/dist/src/hooks/use-scroll.d.ts +31 -35
- package/dist/src/hooks/use-scroll.d.ts.map +1 -1
- package/dist/src/hooks/use-scroll.js +51 -90
- package/dist/src/hooks/use-scroll.js.map +1 -1
- package/dist/src/hosts/base.d.ts +13 -2
- package/dist/src/hosts/base.d.ts.map +1 -1
- package/dist/src/hosts/base.js +74 -2
- package/dist/src/hosts/base.js.map +1 -1
- package/dist/src/hosts/box.d.ts +2 -2
- package/dist/src/hosts/box.d.ts.map +1 -1
- package/dist/src/hosts/box.js +29 -2
- package/dist/src/hosts/box.js.map +1 -1
- package/dist/src/hosts/canvas.d.ts +24 -4
- package/dist/src/hosts/canvas.d.ts.map +1 -1
- package/dist/src/hosts/canvas.js +107 -41
- package/dist/src/hosts/canvas.js.map +1 -1
- package/dist/src/hosts/codeblock.d.ts +10 -12
- package/dist/src/hosts/codeblock.d.ts.map +1 -1
- package/dist/src/hosts/codeblock.js +38 -35
- 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 +20 -5
- package/dist/src/hosts/flex-container.js.map +1 -1
- package/dist/src/hosts/index.d.ts +3 -2
- package/dist/src/hosts/index.d.ts.map +1 -1
- package/dist/src/hosts/index.js +2 -1
- package/dist/src/hosts/index.js.map +1 -1
- package/dist/src/hosts/layout-helpers.d.ts +10 -0
- package/dist/src/hosts/layout-helpers.d.ts.map +1 -0
- package/dist/src/hosts/layout-helpers.js +10 -0
- package/dist/src/hosts/layout-helpers.js.map +1 -0
- package/dist/src/hosts/leaf.d.ts +14 -0
- package/dist/src/hosts/leaf.d.ts.map +1 -0
- package/dist/src/hosts/leaf.js +31 -0
- package/dist/src/hosts/leaf.js.map +1 -0
- package/dist/src/hosts/overlay-item.d.ts +2 -2
- package/dist/src/hosts/overlay-item.d.ts.map +1 -1
- package/dist/src/hosts/overlay-item.js +7 -2
- package/dist/src/hosts/overlay-item.js.map +1 -1
- package/dist/src/hosts/overlay.d.ts +2 -2
- package/dist/src/hosts/overlay.d.ts.map +1 -1
- package/dist/src/hosts/overlay.js +6 -9
- package/dist/src/hosts/overlay.js.map +1 -1
- package/dist/src/hosts/scroll.d.ts +54 -26
- package/dist/src/hosts/scroll.d.ts.map +1 -1
- package/dist/src/hosts/scroll.js +185 -87
- package/dist/src/hosts/scroll.js.map +1 -1
- package/dist/src/hosts/single-child.d.ts.map +1 -1
- package/dist/src/hosts/single-child.js +2 -0
- package/dist/src/hosts/single-child.js.map +1 -1
- package/dist/src/hosts/spacer.d.ts +3 -3
- package/dist/src/hosts/spacer.d.ts.map +1 -1
- package/dist/src/hosts/spacer.js +8 -3
- package/dist/src/hosts/spacer.js.map +1 -1
- package/dist/src/hosts/text.d.ts +22 -18
- package/dist/src/hosts/text.d.ts.map +1 -1
- package/dist/src/hosts/text.js +108 -131
- package/dist/src/hosts/text.js.map +1 -1
- package/dist/src/hosts/vstack.js +1 -1
- package/dist/src/hosts/vstack.js.map +1 -1
- package/dist/src/hosts/zstack.d.ts +3 -3
- package/dist/src/hosts/zstack.d.ts.map +1 -1
- package/dist/src/hosts/zstack.js +13 -8
- package/dist/src/hosts/zstack.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/internal/dev/hmr.d.ts +20 -0
- package/dist/src/internal/dev/hmr.d.ts.map +1 -0
- package/dist/src/internal/dev/hmr.js +93 -0
- package/dist/src/internal/dev/hmr.js.map +1 -0
- package/dist/src/internal/dev/runtime.d.ts +24 -0
- package/dist/src/internal/dev/runtime.d.ts.map +1 -0
- package/dist/src/internal/dev/runtime.js +135 -0
- package/dist/src/internal/dev/runtime.js.map +1 -0
- package/dist/src/internal/dev/ui.d.ts +13 -0
- package/dist/src/internal/dev/ui.d.ts.map +1 -0
- package/dist/src/internal/dev/ui.js +51 -0
- package/dist/src/internal/dev/ui.js.map +1 -0
- package/dist/src/internal/renderer/context.d.ts +9 -0
- package/dist/src/internal/renderer/context.d.ts.map +1 -0
- package/dist/src/internal/renderer/context.js +22 -0
- package/dist/src/internal/renderer/context.js.map +1 -0
- package/dist/src/internal/renderer/core/FrameBuilder.d.ts +18 -0
- package/dist/src/internal/renderer/core/FrameBuilder.d.ts.map +1 -0
- package/dist/src/internal/renderer/core/FrameBuilder.js +40 -0
- package/dist/src/internal/renderer/core/FrameBuilder.js.map +1 -0
- package/dist/src/internal/renderer/core/RendererState.d.ts +41 -0
- package/dist/src/internal/renderer/core/RendererState.d.ts.map +1 -0
- package/dist/src/internal/renderer/core/RendererState.js +70 -0
- package/dist/src/internal/renderer/core/RendererState.js.map +1 -0
- package/dist/src/internal/renderer/core/index.d.ts +3 -0
- package/dist/src/internal/renderer/core/index.d.ts.map +1 -0
- package/dist/src/internal/renderer/core/index.js +3 -0
- package/dist/src/internal/renderer/core/index.js.map +1 -0
- package/dist/src/internal/renderer/index.d.ts +40 -0
- package/dist/src/internal/renderer/index.d.ts.map +1 -0
- package/dist/src/internal/renderer/index.js +543 -0
- package/dist/src/internal/renderer/index.js.map +1 -0
- package/dist/src/internal/renderer/input/InputProcessor.d.ts +30 -0
- package/dist/src/internal/renderer/input/InputProcessor.d.ts.map +1 -0
- package/dist/src/internal/renderer/input/InputProcessor.js +122 -0
- package/dist/src/internal/renderer/input/InputProcessor.js.map +1 -0
- package/dist/src/internal/renderer/input/index.d.ts +2 -0
- package/dist/src/internal/renderer/input/index.d.ts.map +1 -0
- package/dist/src/internal/renderer/input/index.js +2 -0
- package/dist/src/internal/renderer/input/index.js.map +1 -0
- package/dist/src/internal/renderer/lifecycle/EventBus.d.ts +42 -0
- package/dist/src/internal/renderer/lifecycle/EventBus.d.ts.map +1 -0
- package/dist/src/internal/renderer/lifecycle/EventBus.js +97 -0
- package/dist/src/internal/renderer/lifecycle/EventBus.js.map +1 -0
- package/dist/src/internal/renderer/lifecycle/ProcessLifecycle.d.ts +13 -0
- package/dist/src/internal/renderer/lifecycle/ProcessLifecycle.d.ts.map +1 -0
- package/dist/src/internal/renderer/lifecycle/ProcessLifecycle.js +111 -0
- package/dist/src/internal/renderer/lifecycle/ProcessLifecycle.js.map +1 -0
- package/dist/src/internal/renderer/lifecycle/RenderCache.d.ts +3 -0
- package/dist/src/internal/renderer/lifecycle/RenderCache.d.ts.map +1 -0
- package/dist/src/internal/renderer/lifecycle/RenderCache.js +9 -0
- package/dist/src/internal/renderer/lifecycle/RenderCache.js.map +1 -0
- package/dist/src/internal/renderer/lifecycle/index.d.ts +4 -0
- package/dist/src/internal/renderer/lifecycle/index.d.ts.map +1 -0
- package/dist/src/internal/renderer/lifecycle/index.js +4 -0
- package/dist/src/internal/renderer/lifecycle/index.js.map +1 -0
- package/dist/src/internal/renderer/modes/FullscreenRenderer.d.ts +12 -0
- package/dist/src/internal/renderer/modes/FullscreenRenderer.d.ts.map +1 -0
- package/dist/src/internal/renderer/modes/FullscreenRenderer.js +54 -0
- package/dist/src/internal/renderer/modes/FullscreenRenderer.js.map +1 -0
- package/dist/src/internal/renderer/modes/InlineRenderer.d.ts +25 -0
- package/dist/src/internal/renderer/modes/InlineRenderer.d.ts.map +1 -0
- package/dist/src/internal/renderer/modes/InlineRenderer.js +166 -0
- package/dist/src/internal/renderer/modes/InlineRenderer.js.map +1 -0
- package/dist/src/internal/renderer/modes/RendererMode.d.ts +42 -0
- package/dist/src/internal/renderer/modes/RendererMode.d.ts.map +1 -0
- package/dist/src/internal/renderer/modes/RendererMode.js +2 -0
- package/dist/src/internal/renderer/modes/RendererMode.js.map +1 -0
- package/dist/src/internal/renderer/modes/StaticContentRenderer.d.ts +25 -0
- package/dist/src/internal/renderer/modes/StaticContentRenderer.d.ts.map +1 -0
- package/dist/src/internal/renderer/modes/StaticContentRenderer.js +49 -0
- package/dist/src/internal/renderer/modes/StaticContentRenderer.js.map +1 -0
- package/dist/src/internal/renderer/modes/index.d.ts +5 -0
- package/dist/src/internal/renderer/modes/index.d.ts.map +1 -0
- package/dist/src/internal/renderer/modes/index.js +4 -0
- package/dist/src/internal/renderer/modes/index.js.map +1 -0
- package/dist/src/internal/renderer/terminal/KeyboardCapabilityProbe.d.ts +13 -0
- package/dist/src/internal/renderer/terminal/KeyboardCapabilityProbe.d.ts.map +1 -0
- package/dist/src/internal/renderer/terminal/KeyboardCapabilityProbe.js +75 -0
- package/dist/src/internal/renderer/terminal/KeyboardCapabilityProbe.js.map +1 -0
- package/dist/src/internal/renderer/terminal/TerminalSetup.d.ts +29 -0
- package/dist/src/internal/renderer/terminal/TerminalSetup.d.ts.map +1 -0
- package/dist/src/internal/renderer/terminal/TerminalSetup.js +82 -0
- package/dist/src/internal/renderer/terminal/TerminalSetup.js.map +1 -0
- package/dist/src/internal/renderer/terminal/index.d.ts +3 -0
- package/dist/src/internal/renderer/terminal/index.d.ts.map +1 -0
- package/dist/src/internal/renderer/terminal/index.js +3 -0
- package/dist/src/internal/renderer/terminal/index.js.map +1 -0
- package/dist/src/internal/renderer/types.d.ts +122 -0
- package/dist/src/internal/renderer/types.d.ts.map +1 -0
- package/dist/src/internal/renderer/types.js +2 -0
- package/dist/src/internal/renderer/types.js.map +1 -0
- package/dist/src/motion/hooks.d.ts +1 -1
- package/dist/src/motion/hooks.js +1 -1
- package/dist/src/reconciler/host-config.js +2 -2
- package/dist/src/reconciler/host-config.js.map +1 -1
- package/dist/src/reconciler/types.d.ts +5 -1
- package/dist/src/reconciler/types.d.ts.map +1 -1
- package/dist/src/renderer-context.d.ts +1 -8
- package/dist/src/renderer-context.d.ts.map +1 -1
- package/dist/src/renderer-context.js +1 -21
- package/dist/src/renderer-context.js.map +1 -1
- package/dist/src/renderer-types.d.ts +1 -115
- package/dist/src/renderer-types.d.ts.map +1 -1
- package/dist/src/renderer.d.ts +1 -31
- package/dist/src/renderer.d.ts.map +1 -1
- package/dist/src/renderer.js +1 -495
- package/dist/src/renderer.js.map +1 -1
- package/dist/src/test/render-tui.d.ts +3 -3
- package/dist/src/test/render-tui.d.ts.map +1 -1
- package/dist/src/test/render-tui.js +16 -9
- package/dist/src/test/render-tui.js.map +1 -1
- package/dist/src/utils/alignment.d.ts +1 -1
- package/dist/src/utils/alignment.d.ts.map +1 -1
- package/dist/src/utils/alignment.js +0 -2
- package/dist/src/utils/alignment.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 +2 -0
- package/dist/src/utils/border.js.map +1 -1
- package/dist/src/utils/console-helpers.d.ts +19 -0
- package/dist/src/utils/console-helpers.d.ts.map +1 -0
- package/dist/src/utils/console-helpers.js +61 -0
- package/dist/src/utils/console-helpers.js.map +1 -0
- package/dist/src/utils/index.d.ts +2 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +2 -1
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/styles.d.ts +8 -1
- package/dist/src/utils/styles.d.ts.map +1 -1
- package/dist/src/utils/styles.js +10 -8
- package/dist/src/utils/styles.js.map +1 -1
- package/dist/src/utils/text-layout.d.ts +22 -0
- package/dist/src/utils/text-layout.d.ts.map +1 -0
- package/dist/src/utils/text-layout.js +37 -0
- package/dist/src/utils/text-layout.js.map +1 -0
- package/dist/src/utils/text-wrap.d.ts +31 -1
- package/dist/src/utils/text-wrap.d.ts.map +1 -1
- package/dist/src/utils/text-wrap.js +205 -48
- package/dist/src/utils/text-wrap.js.map +1 -1
- package/dist/src/visualize/index.js +1 -1
- package/dist/src/visualize/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/codeblock.tsx +2 -2
- package/src/components/ListView.tsx +21 -23
- package/src/components/Markdown.tsx +3 -3
- package/src/components/MultilineTextInput.tsx +138 -344
- package/src/components/TextInput.tsx +54 -99
- package/src/components/text-editing.ts +180 -0
- package/src/console/ConsolePopover.tsx +124 -107
- package/src/debug/DebugOverlay.ts +15 -74
- package/src/debug/DiagnosticsPanel.tsx +1 -1
- package/src/dev.tsx +5 -458
- package/src/hooks/use-scroll.ts +85 -145
- package/src/hosts/base.ts +86 -3
- package/src/hosts/box.ts +37 -2
- package/src/hosts/canvas.ts +128 -42
- package/src/hosts/codeblock.ts +48 -35
- package/src/hosts/flex-container.ts +25 -6
- package/src/hosts/index.ts +11 -2
- package/src/hosts/layout-helpers.ts +20 -0
- package/src/hosts/leaf.ts +36 -0
- package/src/hosts/overlay-item.ts +8 -2
- package/src/hosts/overlay.ts +13 -11
- package/src/hosts/scroll.ts +228 -106
- package/src/hosts/single-child.ts +2 -0
- package/src/hosts/spacer.ts +8 -3
- package/src/hosts/text.ts +126 -132
- package/src/hosts/vstack.ts +1 -1
- package/src/hosts/zstack.ts +14 -9
- package/src/index.ts +2 -2
- package/src/internal/dev/hmr.ts +101 -0
- package/src/internal/dev/runtime.ts +170 -0
- package/src/internal/dev/ui.tsx +87 -0
- package/src/internal/renderer/context.ts +27 -0
- package/src/{renderer → internal/renderer}/core/FrameBuilder.ts +2 -2
- package/src/internal/renderer/index.ts +689 -0
- package/src/{renderer → internal/renderer}/input/InputProcessor.ts +10 -1
- package/src/{renderer → internal/renderer}/lifecycle/EventBus.ts +9 -1
- package/src/internal/renderer/lifecycle/ProcessLifecycle.ts +125 -0
- package/src/internal/renderer/lifecycle/index.ts +3 -0
- package/src/{renderer → internal/renderer}/modes/InlineRenderer.ts +5 -2
- package/src/{renderer → internal/renderer}/modes/RendererMode.ts +1 -1
- package/src/{renderer → internal/renderer}/modes/StaticContentRenderer.ts +5 -2
- package/src/internal/renderer/terminal/KeyboardCapabilityProbe.ts +91 -0
- package/src/{renderer/lifecycle → internal/renderer/terminal}/TerminalSetup.ts +4 -22
- package/src/internal/renderer/terminal/index.ts +2 -0
- package/src/internal/renderer/types.ts +129 -0
- package/src/motion/hooks.ts +1 -1
- package/src/reconciler/host-config.ts +2 -2
- package/src/reconciler/types.ts +7 -1
- package/src/renderer-context.ts +1 -27
- package/src/renderer-types.ts +10 -123
- package/src/renderer.ts +1 -619
- package/src/test/render-tui.ts +16 -10
- package/src/utils/alignment.ts +1 -3
- package/src/utils/border.ts +11 -1
- package/src/utils/console-helpers.ts +86 -0
- package/src/utils/index.ts +15 -1
- package/src/utils/styles.ts +16 -4
- package/src/utils/text-layout.ts +65 -0
- package/src/utils/text-wrap.ts +261 -48
- package/src/visualize/index.tsx +1 -1
- package/src/renderer/lifecycle/ResizeManager.ts +0 -65
- package/src/renderer/lifecycle/index.ts +0 -4
- /package/src/{renderer → internal/renderer}/core/RendererState.ts +0 -0
- /package/src/{renderer → internal/renderer}/core/index.ts +0 -0
- /package/src/{renderer → internal/renderer}/input/index.ts +0 -0
- /package/src/{renderer → internal/renderer}/lifecycle/RenderCache.ts +0 -0
- /package/src/{renderer → internal/renderer}/modes/FullscreenRenderer.ts +0 -0
- /package/src/{renderer → internal/renderer}/modes/index.ts +0 -0
package/src/utils/alignment.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { Rect, Size } from "../reconciler/types.js"
|
|
6
6
|
|
|
7
|
-
export type HAlign = "left" | "center" | "right"
|
|
7
|
+
export type HAlign = "left" | "center" | "right"
|
|
8
8
|
export type VAlign = "top" | "center" | "bottom"
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -22,14 +22,12 @@ export function alignInRect(
|
|
|
22
22
|
|
|
23
23
|
switch (hAlign) {
|
|
24
24
|
case "left":
|
|
25
|
-
case "leading":
|
|
26
25
|
x = rect.x
|
|
27
26
|
break
|
|
28
27
|
case "center":
|
|
29
28
|
x = rect.x + Math.floor((rect.w - size.w) / 2)
|
|
30
29
|
break
|
|
31
30
|
case "right":
|
|
32
|
-
case "trailing":
|
|
33
31
|
x = rect.x + Math.max(0, rect.w - size.w)
|
|
34
32
|
break
|
|
35
33
|
}
|
package/src/utils/border.ts
CHANGED
|
@@ -4,7 +4,15 @@
|
|
|
4
4
|
|
|
5
5
|
import type { CellBuffer } from "@effect-tui/core"
|
|
6
6
|
|
|
7
|
-
export type BorderKind =
|
|
7
|
+
export type BorderKind =
|
|
8
|
+
| "none"
|
|
9
|
+
| "rounded"
|
|
10
|
+
| "round"
|
|
11
|
+
| "square"
|
|
12
|
+
| "double"
|
|
13
|
+
| "heavy"
|
|
14
|
+
| "dashed"
|
|
15
|
+
| "ascii"
|
|
8
16
|
|
|
9
17
|
export interface BorderChars {
|
|
10
18
|
tl: string // top-left
|
|
@@ -33,6 +41,7 @@ export interface TableBorderChars extends BorderChars {
|
|
|
33
41
|
export function borderChars(kind: BorderKind): BorderChars {
|
|
34
42
|
switch (kind) {
|
|
35
43
|
case "rounded":
|
|
44
|
+
case "round":
|
|
36
45
|
return { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│" }
|
|
37
46
|
case "square":
|
|
38
47
|
return { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│" }
|
|
@@ -55,6 +64,7 @@ export function borderChars(kind: BorderKind): BorderChars {
|
|
|
55
64
|
export function tableBorderChars(kind: BorderKind): TableBorderChars {
|
|
56
65
|
switch (kind) {
|
|
57
66
|
case "rounded":
|
|
67
|
+
case "round":
|
|
58
68
|
return { tl: "╭", tr: "╮", bl: "╰", br: "╯", h: "─", v: "│", tt: "┬", bt: "┴", lt: "├", rt: "┤", cross: "┼" }
|
|
59
69
|
case "square":
|
|
60
70
|
return { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│", tt: "┬", bt: "┴", lt: "├", rt: "┤", cross: "┼" }
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export interface SelectionPoint {
|
|
2
|
+
line: number
|
|
3
|
+
col: number
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function formatTimestamp(date: Date): string {
|
|
7
|
+
const h = date.getHours().toString().padStart(2, "0")
|
|
8
|
+
const m = date.getMinutes().toString().padStart(2, "0")
|
|
9
|
+
const s = date.getSeconds().toString().padStart(2, "0")
|
|
10
|
+
return `${h}:${m}:${s}`
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function formatLocation(loc?: { file: string; line: number }): string {
|
|
14
|
+
if (!loc) return ""
|
|
15
|
+
const parts = loc.file.split("/")
|
|
16
|
+
const filename = parts[parts.length - 1]
|
|
17
|
+
return `${filename}:${loc.line}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function wrapLine(text: string, maxWidth: number): string[] {
|
|
21
|
+
if (text.length <= maxWidth) return [text]
|
|
22
|
+
|
|
23
|
+
const lines: string[] = []
|
|
24
|
+
let remaining = text
|
|
25
|
+
|
|
26
|
+
while (remaining.length > 0) {
|
|
27
|
+
if (remaining.length <= maxWidth) {
|
|
28
|
+
lines.push(remaining)
|
|
29
|
+
break
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let breakAt = maxWidth
|
|
33
|
+
const lastSpace = remaining.lastIndexOf(" ", maxWidth)
|
|
34
|
+
if (lastSpace > maxWidth * 0.5) {
|
|
35
|
+
breakAt = lastSpace
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
lines.push(remaining.slice(0, breakAt))
|
|
39
|
+
remaining = remaining.slice(breakAt).trimStart()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return lines
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Normalize selection so start is before end */
|
|
46
|
+
export function normalizeSelection(
|
|
47
|
+
anchor: SelectionPoint,
|
|
48
|
+
head: SelectionPoint,
|
|
49
|
+
): [SelectionPoint, SelectionPoint] {
|
|
50
|
+
if (anchor.line < head.line || (anchor.line === head.line && anchor.col <= head.col)) {
|
|
51
|
+
return [anchor, head]
|
|
52
|
+
}
|
|
53
|
+
return [head, anchor]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Get selection bounds for a specific line */
|
|
57
|
+
export function getLineSelection(
|
|
58
|
+
lineIndex: number,
|
|
59
|
+
lineLength: number,
|
|
60
|
+
start: SelectionPoint,
|
|
61
|
+
end: SelectionPoint,
|
|
62
|
+
): { startCol: number; endCol: number } | null {
|
|
63
|
+
if (lineIndex < start.line || lineIndex > end.line) return null
|
|
64
|
+
|
|
65
|
+
const startCol = lineIndex === start.line ? Math.min(start.col, lineLength) : 0
|
|
66
|
+
const endCol = lineIndex === end.line ? Math.min(end.col, lineLength) : lineLength
|
|
67
|
+
|
|
68
|
+
if (startCol >= endCol && lineIndex === start.line && lineIndex === end.line) return null
|
|
69
|
+
|
|
70
|
+
return { startCol, endCol }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function getSelectionText(
|
|
74
|
+
lines: string[],
|
|
75
|
+
start: SelectionPoint,
|
|
76
|
+
end: SelectionPoint,
|
|
77
|
+
): string {
|
|
78
|
+
const selected: string[] = []
|
|
79
|
+
for (let i = start.line; i <= end.line && i < lines.length; i++) {
|
|
80
|
+
const lineText = lines[i] ?? ""
|
|
81
|
+
const startCol = i === start.line ? Math.min(start.col, lineText.length) : 0
|
|
82
|
+
const endCol = i === end.line ? Math.min(end.col, lineText.length) : lineText.length
|
|
83
|
+
selected.push(lineText.slice(startCol, endCol))
|
|
84
|
+
}
|
|
85
|
+
return selected.join("\n")
|
|
86
|
+
}
|
package/src/utils/index.ts
CHANGED
|
@@ -20,5 +20,19 @@ export {
|
|
|
20
20
|
styleSpecFromProps,
|
|
21
21
|
toColorValue,
|
|
22
22
|
} from "./styles.js"
|
|
23
|
-
export {
|
|
23
|
+
export {
|
|
24
|
+
buildTextLayout,
|
|
25
|
+
type LineLayout,
|
|
26
|
+
type TextLayout,
|
|
27
|
+
type TextLayoutOptions,
|
|
28
|
+
type VisualLine,
|
|
29
|
+
} from "./text-layout.js"
|
|
30
|
+
export {
|
|
31
|
+
splitSpansByNewline,
|
|
32
|
+
spansDisplayWidth,
|
|
33
|
+
wrapLineWithRanges,
|
|
34
|
+
wrapSpans,
|
|
35
|
+
wrapSpansByLine,
|
|
36
|
+
wrapText,
|
|
37
|
+
} from "./text-wrap.js"
|
|
24
38
|
export { isWhitespace, matchNextWord, matchPrevWord, splitWords } from "./word-boundaries.js"
|
package/src/utils/styles.ts
CHANGED
|
@@ -83,14 +83,26 @@ export function resolveInheritedBgStyle(
|
|
|
83
83
|
/**
|
|
84
84
|
* Fill a rect with background color, inheriting from parent if needed.
|
|
85
85
|
*/
|
|
86
|
+
export interface FillRectWithInheritedBgOptions {
|
|
87
|
+
/** Skip filling when there is no inherited background value. */
|
|
88
|
+
skipWhenUndefined?: boolean
|
|
89
|
+
}
|
|
90
|
+
|
|
86
91
|
export function fillRectWithInheritedBg(
|
|
87
92
|
buffer: CellBuffer,
|
|
88
93
|
palette: Palette,
|
|
89
94
|
rect: Rect,
|
|
90
95
|
explicitBg: Color | undefined,
|
|
91
96
|
parent: HostInstance | null,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
97
|
+
options?: FillRectWithInheritedBgOptions,
|
|
98
|
+
): { value?: ColorValue; styleId: number } {
|
|
99
|
+
if (rect.w <= 0 || rect.h <= 0) {
|
|
100
|
+
return { value: undefined, styleId: 0 }
|
|
101
|
+
}
|
|
102
|
+
const resolved = resolveInheritedBgStyle(palette, explicitBg, parent)
|
|
103
|
+
if (options?.skipWhenUndefined && resolved.value === undefined) {
|
|
104
|
+
return resolved
|
|
105
|
+
}
|
|
106
|
+
buffer.fillRect(rect.x, rect.y, rect.w, rect.h, " ".codePointAt(0)!, resolved.styleId)
|
|
107
|
+
return resolved
|
|
96
108
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { displayWidth, graphemes } from "@effect-tui/core"
|
|
2
|
+
import { wrapLineWithRanges } from "./text-wrap.js"
|
|
3
|
+
|
|
4
|
+
export type VisualLine = {
|
|
5
|
+
logicalRow: number
|
|
6
|
+
startCol: number
|
|
7
|
+
endCol: number
|
|
8
|
+
text: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type LineLayout = {
|
|
12
|
+
graphemeList: string[]
|
|
13
|
+
widths: number[]
|
|
14
|
+
prefixWidths: number[]
|
|
15
|
+
visualLines: VisualLine[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type TextLayout = {
|
|
19
|
+
lines: LineLayout[]
|
|
20
|
+
allVisualLines: VisualLine[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type TextLayoutOptions = {
|
|
24
|
+
wrap?: boolean
|
|
25
|
+
preserveWhitespace?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildTextLayout(text: string, maxWidth: number, options?: TextLayoutOptions): TextLayout {
|
|
29
|
+
const logicalLines = text.split("\n")
|
|
30
|
+
const wrap = options?.wrap ?? true
|
|
31
|
+
const preserveWhitespace = options?.preserveWhitespace ?? false
|
|
32
|
+
const lines: LineLayout[] = []
|
|
33
|
+
const allVisualLines: VisualLine[] = []
|
|
34
|
+
|
|
35
|
+
for (let row = 0; row < logicalLines.length; row++) {
|
|
36
|
+
const line = logicalLines[row]
|
|
37
|
+
const graphemeList = graphemes(line)
|
|
38
|
+
const widths = graphemeList.map((g) => displayWidth(g))
|
|
39
|
+
const prefixWidths: number[] = [0]
|
|
40
|
+
for (let i = 0; i < widths.length; i++) {
|
|
41
|
+
prefixWidths.push(prefixWidths[i] + widths[i])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const visualLines: VisualLine[] = wrap
|
|
45
|
+
? wrapLineWithRanges(line, maxWidth, { preserveWhitespace }).map((range) => ({
|
|
46
|
+
logicalRow: row,
|
|
47
|
+
startCol: range.startCol,
|
|
48
|
+
endCol: range.endCol,
|
|
49
|
+
text: range.text,
|
|
50
|
+
}))
|
|
51
|
+
: [
|
|
52
|
+
{
|
|
53
|
+
logicalRow: row,
|
|
54
|
+
startCol: 0,
|
|
55
|
+
endCol: graphemeList.length,
|
|
56
|
+
text: line,
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
lines.push({ graphemeList, widths, prefixWidths, visualLines })
|
|
61
|
+
allVisualLines.push(...visualLines)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { lines, allVisualLines }
|
|
65
|
+
}
|
package/src/utils/text-wrap.ts
CHANGED
|
@@ -1,60 +1,123 @@
|
|
|
1
|
-
import { displayWidth } from "@effect-tui/core"
|
|
1
|
+
import { displayWidth, graphemes } from "@effect-tui/core"
|
|
2
2
|
import { isWhitespace, splitWords } from "./word-boundaries.js"
|
|
3
3
|
|
|
4
4
|
type SpanLike = { text: string }
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
type TokenSpec<T> = {
|
|
7
|
+
text: string
|
|
8
|
+
width: number
|
|
9
|
+
graphemeCount: number
|
|
10
|
+
isWhitespace: boolean
|
|
11
|
+
startCol?: number
|
|
12
|
+
segments?: string[]
|
|
13
|
+
make: (text: string, segmentStart: number, segmentEnd: number) => T
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type LineToken<T> = {
|
|
17
|
+
value: T
|
|
18
|
+
width: number
|
|
19
|
+
isWhitespace: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type WrapOptions = {
|
|
23
|
+
trimTrailingWhitespace?: boolean
|
|
24
|
+
preserveWhitespace?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function spansDisplayWidth<T extends { text: string }>(spans: T[]): number {
|
|
28
|
+
return spans.reduce((sum, span) => sum + displayWidth(span.text), 0)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function wrapTokens<T>(tokens: TokenSpec<T>[], maxWidth: number, options?: WrapOptions): T[][] {
|
|
32
|
+
const lines: LineToken<T>[][] = [[]]
|
|
12
33
|
let lineWidth = 0
|
|
13
34
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
35
|
+
const trimLine = () => {
|
|
36
|
+
if (!options?.trimTrailingWhitespace) return
|
|
37
|
+
const line = lines[lines.length - 1]
|
|
38
|
+
while (line.length > 0 && line[line.length - 1].isWhitespace) {
|
|
39
|
+
const removed = line.pop()
|
|
40
|
+
if (removed) {
|
|
41
|
+
lineWidth -= removed.width
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
17
45
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
const startNewLine = () => {
|
|
47
|
+
lines.push([])
|
|
48
|
+
lineWidth = 0
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const addToken = (
|
|
52
|
+
token: TokenSpec<T>,
|
|
53
|
+
text: string,
|
|
54
|
+
width: number,
|
|
55
|
+
isWhitespace: boolean,
|
|
56
|
+
segmentStart: number,
|
|
57
|
+
segmentEnd: number,
|
|
58
|
+
) => {
|
|
59
|
+
lines[lines.length - 1].push({ value: token.make(text, segmentStart, segmentEnd), width, isWhitespace })
|
|
60
|
+
lineWidth += width
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const token of tokens) {
|
|
64
|
+
if (!token.text) continue
|
|
65
|
+
const tokenWidth = token.width
|
|
66
|
+
const isWs = token.isWhitespace
|
|
67
|
+
|
|
68
|
+
if (lineWidth + tokenWidth <= maxWidth) {
|
|
69
|
+
const start = token.startCol ?? 0
|
|
70
|
+
addToken(token, token.text, tokenWidth, isWs, start, start + token.graphemeCount)
|
|
71
|
+
continue
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (isWs && !options?.preserveWhitespace) {
|
|
75
|
+
// Skip whitespace at line break
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (tokenWidth <= maxWidth) {
|
|
80
|
+
trimLine()
|
|
81
|
+
if (lines[lines.length - 1].length > 0) {
|
|
82
|
+
startNewLine()
|
|
83
|
+
}
|
|
84
|
+
const start = token.startCol ?? 0
|
|
85
|
+
addToken(token, token.text, tokenWidth, isWs, start, start + token.graphemeCount)
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Token is longer than maxWidth - break by character
|
|
90
|
+
trimLine()
|
|
91
|
+
if (lines[lines.length - 1].length > 0) {
|
|
92
|
+
startNewLine()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const segments = token.segments ?? Array.from(token.text)
|
|
96
|
+
const tokenStart = token.startCol ?? 0
|
|
97
|
+
let segment = ""
|
|
98
|
+
let segmentWidth = 0
|
|
99
|
+
let segmentStartOffset = 0
|
|
100
|
+
let segmentOffset = 0
|
|
101
|
+
|
|
102
|
+
for (const ch of segments) {
|
|
103
|
+
const chWidth = displayWidth(ch)
|
|
104
|
+
if (lineWidth + segmentWidth + chWidth > maxWidth && (segment || lineWidth > 0)) {
|
|
105
|
+
if (segment) {
|
|
106
|
+
addToken(token, segment, segmentWidth, false, tokenStart + segmentStartOffset, tokenStart + segmentOffset)
|
|
56
107
|
}
|
|
108
|
+
startNewLine()
|
|
109
|
+
segment = ch
|
|
110
|
+
segmentWidth = chWidth
|
|
111
|
+
segmentStartOffset = segmentOffset
|
|
112
|
+
} else {
|
|
113
|
+
segment += ch
|
|
114
|
+
segmentWidth += chWidth
|
|
57
115
|
}
|
|
116
|
+
segmentOffset += 1
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (segment) {
|
|
120
|
+
addToken(token, segment, segmentWidth, false, tokenStart + segmentStartOffset, tokenStart + segmentOffset)
|
|
58
121
|
}
|
|
59
122
|
}
|
|
60
123
|
|
|
@@ -63,5 +126,155 @@ export function wrapSpans<T extends SpanLike>(spans: T[], maxWidth: number): T[]
|
|
|
63
126
|
lines.pop()
|
|
64
127
|
}
|
|
65
128
|
|
|
129
|
+
return lines.length > 0 ? lines.map((line) => line.map((token) => token.value)) : [[]]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type WrappedLineRange = {
|
|
133
|
+
text: string
|
|
134
|
+
startCol: number
|
|
135
|
+
endCol: number
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Wrap a single line into visual lines with grapheme ranges.
|
|
140
|
+
*/
|
|
141
|
+
export function wrapLineWithRanges(line: string, maxWidth: number, options?: WrapOptions): WrappedLineRange[] {
|
|
142
|
+
if (line === "") {
|
|
143
|
+
return [{ text: "", startCol: 0, endCol: 0 }]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const tokens: Array<TokenSpec<WrappedLineRange>> = []
|
|
147
|
+
let col = 0
|
|
148
|
+
for (const token of splitWords(line)) {
|
|
149
|
+
if (!token) continue
|
|
150
|
+
const segments = graphemes(token)
|
|
151
|
+
const graphemeCount = segments.length
|
|
152
|
+
const startCol = col
|
|
153
|
+
tokens.push({
|
|
154
|
+
text: token,
|
|
155
|
+
width: displayWidth(token),
|
|
156
|
+
graphemeCount,
|
|
157
|
+
startCol,
|
|
158
|
+
segments,
|
|
159
|
+
isWhitespace: isWhitespace(token),
|
|
160
|
+
make: (text, segmentStart, segmentEnd) => ({ text, startCol: segmentStart, endCol: segmentEnd }),
|
|
161
|
+
})
|
|
162
|
+
col += graphemeCount
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const wrapped = wrapTokens(tokens, maxWidth, options)
|
|
166
|
+
const lines: WrappedLineRange[] = []
|
|
167
|
+
|
|
168
|
+
for (const lineTokens of wrapped) {
|
|
169
|
+
if (lineTokens.length === 0) {
|
|
170
|
+
lines.push({ text: "", startCol: 0, endCol: 0 })
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
lines.push({
|
|
174
|
+
text: lineTokens.map((token) => token.text).join(""),
|
|
175
|
+
startCol: lineTokens[0].startCol,
|
|
176
|
+
endCol: lineTokens[lineTokens.length - 1].endCol,
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return lines.length > 0 ? lines : [{ text: "", startCol: 0, endCol: 0 }]
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Wrap spans into lines, breaking at word boundaries.
|
|
185
|
+
* Preserves span styling by cloning span objects with updated text.
|
|
186
|
+
*/
|
|
187
|
+
export function wrapSpans<T extends SpanLike>(spans: T[], maxWidth: number, options?: WrapOptions): T[][] {
|
|
188
|
+
const tokens: Array<TokenSpec<T>> = []
|
|
189
|
+
for (const span of spans) {
|
|
190
|
+
for (const token of splitWords(span.text)) {
|
|
191
|
+
if (!token) continue
|
|
192
|
+
const segments = graphemes(token)
|
|
193
|
+
tokens.push({
|
|
194
|
+
text: token,
|
|
195
|
+
width: displayWidth(token),
|
|
196
|
+
graphemeCount: segments.length,
|
|
197
|
+
segments,
|
|
198
|
+
isWhitespace: isWhitespace(token),
|
|
199
|
+
make: (text) => ({ ...span, text }),
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return wrapTokens(tokens, maxWidth, options)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Split spans into logical lines at newline boundaries.
|
|
209
|
+
* Preserves empty lines.
|
|
210
|
+
*/
|
|
211
|
+
export function splitSpansByNewline<T extends SpanLike>(spans: T[]): T[][] {
|
|
212
|
+
const lines: T[][] = [[]]
|
|
213
|
+
|
|
214
|
+
for (const span of spans) {
|
|
215
|
+
if (!span.text) continue
|
|
216
|
+
const parts = span.text.split("\n")
|
|
217
|
+
for (let i = 0; i < parts.length; i++) {
|
|
218
|
+
if (i > 0) {
|
|
219
|
+
lines.push([])
|
|
220
|
+
}
|
|
221
|
+
const part = parts[i]
|
|
222
|
+
if (part) {
|
|
223
|
+
lines[lines.length - 1].push({ ...span, text: part })
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
66
228
|
return lines.length > 0 ? lines : [[]]
|
|
67
229
|
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Wrap spans per logical line (newline-aware).
|
|
233
|
+
*/
|
|
234
|
+
export function wrapSpansByLine<T extends SpanLike>(spans: T[], maxWidth: number, options?: WrapOptions): T[][] {
|
|
235
|
+
const logicalLines = splitSpansByNewline(spans)
|
|
236
|
+
const lines: T[][] = []
|
|
237
|
+
|
|
238
|
+
for (const line of logicalLines) {
|
|
239
|
+
if (line.length === 0) {
|
|
240
|
+
lines.push([])
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
lines.push(...wrapSpans(line, maxWidth, options))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return lines.length > 0 ? lines : [[]]
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Wrap plain text into lines, breaking at word boundaries.
|
|
251
|
+
* Trims trailing whitespace when a line breaks.
|
|
252
|
+
*/
|
|
253
|
+
export function wrapText(text: string, maxWidth: number): string[] {
|
|
254
|
+
const lines: string[] = []
|
|
255
|
+
for (const rawLine of text.split("\n")) {
|
|
256
|
+
if (rawLine === "") {
|
|
257
|
+
lines.push("")
|
|
258
|
+
continue
|
|
259
|
+
}
|
|
260
|
+
const tokens: Array<TokenSpec<string>> = []
|
|
261
|
+
for (const token of splitWords(rawLine)) {
|
|
262
|
+
if (!token) continue
|
|
263
|
+
const segments = graphemes(token)
|
|
264
|
+
tokens.push({
|
|
265
|
+
text: token,
|
|
266
|
+
width: displayWidth(token),
|
|
267
|
+
graphemeCount: segments.length,
|
|
268
|
+
segments,
|
|
269
|
+
isWhitespace: isWhitespace(token),
|
|
270
|
+
make: (segment) => segment,
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
const wrapped = wrapTokens(tokens, maxWidth, { trimTrailingWhitespace: true })
|
|
274
|
+
for (const line of wrapped) {
|
|
275
|
+
lines.push(line.join(""))
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return lines.length > 0 ? lines : [""]
|
|
280
|
+
}
|
package/src/visualize/index.tsx
CHANGED
|
@@ -273,7 +273,7 @@ export const visualize = <A, E, R>(
|
|
|
273
273
|
: { status: "failure", elapsedMs: Date.now() - startTime, errorSummary: summarizeCause(exit.cause) },
|
|
274
274
|
)
|
|
275
275
|
renderer.requestRender()
|
|
276
|
-
renderer.
|
|
276
|
+
renderer.renderNow() // Ensure final state is rendered
|
|
277
277
|
|
|
278
278
|
// Wait for completion animation
|
|
279
279
|
yield* Effect.sleep(COMPLETION_DELAY_MS)
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { TuiWriteStream } from "../../renderer-types.js"
|
|
2
|
-
|
|
3
|
-
export interface ResizeState {
|
|
4
|
-
width: number
|
|
5
|
-
height: number
|
|
6
|
-
lastWidth: number
|
|
7
|
-
previousHeight: number // For inline mode
|
|
8
|
-
inlineClearHeight: number
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface ResizeResult {
|
|
12
|
-
newWidth: number
|
|
13
|
-
newHeight: number
|
|
14
|
-
needsFullClear: boolean // Fullscreen mode
|
|
15
|
-
inlineClearHeight: number // Inline mode
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Handles terminal resize events and calculates necessary clear/redraw actions.
|
|
20
|
-
*/
|
|
21
|
-
export class ResizeManager {
|
|
22
|
-
constructor(
|
|
23
|
-
private stdout: TuiWriteStream,
|
|
24
|
-
private mode: "fullscreen" | "inline",
|
|
25
|
-
) {}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Process a resize event and determine what actions are needed.
|
|
29
|
-
*/
|
|
30
|
-
handleResize(currentState: ResizeState): ResizeResult {
|
|
31
|
-
const newWidth = this.stdout.columns || 80
|
|
32
|
-
const newHeight = this.stdout.rows || 24
|
|
33
|
-
|
|
34
|
-
let needsFullClear = false
|
|
35
|
-
let inlineClearHeight = currentState.inlineClearHeight
|
|
36
|
-
|
|
37
|
-
if (this.mode === "fullscreen") {
|
|
38
|
-
// Fullscreen: defer clear to renderFrame so it's written atomically with content
|
|
39
|
-
needsFullClear = true
|
|
40
|
-
} else if (newWidth !== currentState.lastWidth || newHeight !== currentState.height) {
|
|
41
|
-
// Inline: on dimension change, request clear on next render
|
|
42
|
-
const clearHeight = Math.max(currentState.previousHeight, currentState.height, newHeight)
|
|
43
|
-
if (clearHeight > inlineClearHeight) {
|
|
44
|
-
inlineClearHeight = clearHeight
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
newWidth,
|
|
50
|
-
newHeight,
|
|
51
|
-
needsFullClear,
|
|
52
|
-
inlineClearHeight,
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get current terminal dimensions.
|
|
58
|
-
*/
|
|
59
|
-
getDimensions(): { width: number; height: number } {
|
|
60
|
-
return {
|
|
61
|
-
width: this.stdout.columns || 80,
|
|
62
|
-
height: this.stdout.rows || 24,
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export { EventBus } from "./EventBus.js"
|
|
2
|
-
export { getRenderCache, type RenderCache } from "./RenderCache.js"
|
|
3
|
-
export { ResizeManager, type ResizeResult, type ResizeState } from "./ResizeManager.js"
|
|
4
|
-
export { TerminalSetup, type TerminalSetupConfig } from "./TerminalSetup.js"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|