@dungle-scrubs/tallow 0.9.3 → 0.9.6
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/cli.js +7 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/interactive-mode-patch.d.ts +24 -10
- package/dist/interactive-mode-patch.d.ts.map +1 -1
- package/dist/interactive-mode-patch.js +285 -148
- package/dist/interactive-mode-patch.js.map +1 -1
- package/dist/interactive-reset.d.ts +49 -0
- package/dist/interactive-reset.d.ts.map +1 -0
- package/dist/interactive-reset.js +40 -0
- package/dist/interactive-reset.js.map +1 -0
- package/dist/pi-tui-editor-patch.d.ts +10 -0
- package/dist/pi-tui-editor-patch.d.ts.map +1 -0
- package/dist/pi-tui-editor-patch.js +159 -0
- package/dist/pi-tui-editor-patch.js.map +1 -0
- package/dist/pi-tui-patch.d.ts +2 -0
- package/dist/pi-tui-patch.d.ts.map +1 -0
- package/dist/pi-tui-patch.js +563 -0
- package/dist/pi-tui-patch.js.map +1 -0
- package/dist/pi-tui-settings-list-patch.d.ts +11 -0
- package/dist/pi-tui-settings-list-patch.d.ts.map +1 -0
- package/dist/pi-tui-settings-list-patch.js +38 -0
- package/dist/pi-tui-settings-list-patch.js.map +1 -0
- package/dist/reset-diagnostics.d.ts +69 -0
- package/dist/reset-diagnostics.d.ts.map +1 -0
- package/dist/reset-diagnostics.js +41 -0
- package/dist/reset-diagnostics.js.map +1 -0
- package/dist/sdk.d.ts +5 -21
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +180 -149
- package/dist/sdk.js.map +1 -1
- package/dist/workspace-transition-interactive.d.ts +1 -0
- package/dist/workspace-transition-interactive.d.ts.map +1 -1
- package/dist/workspace-transition-interactive.js +7 -17
- package/dist/workspace-transition-interactive.js.map +1 -1
- package/extensions/__integration__/audit-findings.test.ts +6 -16
- package/extensions/__integration__/teams-runtime.test.ts +4 -1
- package/extensions/_icons/index.ts +2 -4
- package/extensions/_shared/__tests__/image-metadata.test.ts +33 -0
- package/extensions/_shared/__tests__/terminal-links.test.ts +18 -0
- package/extensions/_shared/image-metadata.ts +99 -0
- package/extensions/_shared/inline-preview.ts +1 -1
- package/extensions/_shared/pid-registry.ts +0 -1
- package/extensions/_shared/terminal-links.ts +22 -0
- package/extensions/ask-user-question-tool/index.ts +0 -3
- package/extensions/clear/__tests__/clear.test.ts +270 -3
- package/extensions/command-expansion/index.ts +1 -1
- package/extensions/context-files/index.ts +5 -1
- package/extensions/context-fork/__tests__/context-fork.test.ts +94 -1
- package/extensions/context-fork/extension.json +1 -1
- package/extensions/context-fork/index.ts +32 -0
- package/extensions/edit-tool-enhanced/index.ts +2 -1
- package/extensions/hooks/index.ts +33 -11
- package/extensions/loop/index.ts +14 -1
- package/extensions/lsp/index.ts +64 -13
- package/extensions/lsp/package.json +2 -2
- package/extensions/permissions/__tests__/permissions.test.ts +4 -4
- package/extensions/random-spinner/index.ts +7 -642
- package/extensions/read-tool-enhanced/index.ts +6 -8
- package/extensions/render-stabilizer/__tests__/render-stabilizer.test.ts +4 -5
- package/extensions/render-stabilizer/index.ts +6 -6
- package/extensions/show-system-prompt/__tests__/show-system-prompt.test.ts +1 -1
- package/extensions/slash-command-bridge/__tests__/slash-command-bridge.test.ts +26 -0
- package/extensions/slash-command-bridge/index.ts +14 -2
- package/extensions/subagent-tool/index.ts +1 -1
- package/extensions/subagent-tool/model-resolver.ts +274 -7
- package/extensions/tasks/__tests__/state-ui.test.ts +3 -3
- package/extensions/tasks/__tests__/widget-subagents.test.ts +2 -2
- package/extensions/tasks/commands/register-tasks-extension.ts +10 -10
- package/extensions/tasks/state/index.ts +1 -1
- package/extensions/tasks/ui/index.ts +2 -7
- package/extensions/teams-tool/tools/register-extension.ts +1 -3
- package/extensions/web-search-tool/index.ts +2 -1
- package/extensions/write-tool-enhanced/__tests__/write-tool-enhanced.test.ts +21 -6
- package/extensions/write-tool-enhanced/index.ts +2 -1
- package/node_modules/@mariozechner/pi-tui/README.md +56 -34
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts +18 -13
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js +182 -113
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js +3 -3
- package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts +45 -36
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.js +489 -325
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts +1 -99
- package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/image.js +17 -192
- package/node_modules/@mariozechner/pi-tui/dist/components/image.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/input.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/input.js +57 -60
- package/node_modules/@mariozechner/pi-tui/dist/components/input.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts +2 -69
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.js +5 -102
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js +111 -53
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts +19 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js +78 -67
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts +0 -2
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js +12 -23
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/index.d.ts +8 -10
- package/node_modules/@mariozechner/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/index.js +6 -9
- package/node_modules/@mariozechner/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts +108 -238
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.js +108 -365
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts +33 -48
- package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keys.js +239 -155
- package/node_modules/@mariozechner/pi-tui/dist/keys.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts +14 -94
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js +44 -186
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts +13 -58
- package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal.js +78 -111
- package/node_modules/@mariozechner/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts +24 -110
- package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/tui.js +188 -435
- package/node_modules/@mariozechner/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts +0 -18
- package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/utils.js +251 -119
- package/node_modules/@mariozechner/pi-tui/dist/utils.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/package.json +6 -6
- package/node_modules/@mariozechner/pi-tui/src/__tests__/__snapshots__/render.test.ts.snap +3 -40
- package/node_modules/@mariozechner/pi-tui/src/__tests__/image-component.test.ts +71 -81
- package/node_modules/@mariozechner/pi-tui/src/__tests__/render.test.ts +0 -33
- package/node_modules/@mariozechner/pi-tui/src/__tests__/terminal-image.test.ts +93 -334
- package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-render-scheduling.test.ts +1 -1
- package/node_modules/@mariozechner/pi-tui/src/__tests__/utils.test.ts +11 -196
- package/node_modules/@mariozechner/pi-tui/src/autocomplete.ts +228 -142
- package/node_modules/@mariozechner/pi-tui/src/components/cancellable-loader.ts +3 -3
- package/node_modules/@mariozechner/pi-tui/src/components/editor.ts +624 -390
- package/node_modules/@mariozechner/pi-tui/src/components/image.ts +17 -227
- package/node_modules/@mariozechner/pi-tui/src/components/input.ts +71 -63
- package/node_modules/@mariozechner/pi-tui/src/components/loader.ts +5 -137
- package/node_modules/@mariozechner/pi-tui/src/components/markdown.ts +143 -52
- package/node_modules/@mariozechner/pi-tui/src/components/select-list.ts +136 -70
- package/node_modules/@mariozechner/pi-tui/src/components/settings-list.ts +11 -23
- package/node_modules/@mariozechner/pi-tui/src/index.ts +17 -36
- package/node_modules/@mariozechner/pi-tui/src/keybindings.ts +148 -421
- package/node_modules/@mariozechner/pi-tui/src/keys.ts +253 -181
- package/node_modules/@mariozechner/pi-tui/src/terminal-image.ts +51 -252
- package/node_modules/@mariozechner/pi-tui/src/terminal.ts +78 -133
- package/node_modules/@mariozechner/pi-tui/src/tui.ts +202 -478
- package/node_modules/@mariozechner/pi-tui/src/utils.ts +289 -125
- package/node_modules/@mariozechner/pi-tui/tsconfig.build.json +1 -0
- package/package.json +13 -13
- package/packages/tallow-tui/node_modules/@types/mime-types/README.md +8 -2
- package/packages/tallow-tui/node_modules/@types/mime-types/index.d.ts +6 -0
- package/packages/tallow-tui/node_modules/@types/mime-types/package.json +9 -3
- package/packages/tallow-tui/node_modules/get-east-asian-width/lookup-data.js +18 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/lookup.js +116 -384
- package/packages/tallow-tui/node_modules/get-east-asian-width/package.json +5 -4
- package/packages/tallow-tui/node_modules/get-east-asian-width/utilities.js +24 -0
- package/packages/tallow-tui/node_modules/marked/README.md +5 -4
- package/packages/tallow-tui/node_modules/marked/bin/main.js +10 -8
- package/packages/tallow-tui/node_modules/marked/bin/marked.js +2 -1
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.ts +156 -125
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js +67 -2179
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js.map +3 -3
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js +67 -2201
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js.map +3 -3
- package/packages/tallow-tui/node_modules/marked/man/marked.1 +4 -2
- package/packages/tallow-tui/node_modules/marked/man/marked.1.md +2 -1
- package/packages/tallow-tui/node_modules/marked/package.json +26 -34
- package/runtime/model-metadata-overrides.ts +10 -1
- package/runtime/pid-schema.ts +26 -6
- package/skills/tallow-expert/SKILL.md +1 -3
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts +0 -32
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.js +0 -46
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts +0 -52
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js +0 -89
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts +0 -14
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js +0 -55
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-change-listener.test.ts +0 -121
- package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-ghost-text.test.ts +0 -112
- package/node_modules/@mariozechner/pi-tui/src/__tests__/mouse-events.test.ts +0 -134
- package/node_modules/@mariozechner/pi-tui/src/__tests__/settings-list.test.ts +0 -49
- package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-diff-regression.test.ts +0 -555
- package/node_modules/@mariozechner/pi-tui/src/border-styles.ts +0 -60
- package/node_modules/@mariozechner/pi-tui/src/components/bordered-box.ts +0 -113
- package/node_modules/@mariozechner/pi-tui/src/test-utils/capability-env.ts +0 -56
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs +0 -2211
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs.map +0 -7
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.cts +0 -728
- package/packages/tallow-tui/node_modules/marked/marked.min.js +0 -69
|
@@ -1,555 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { stripAnsi } from "../../../../test-utils/virtual-terminal.js";
|
|
3
|
-
import type { Terminal } from "../terminal.js";
|
|
4
|
-
import { type Component, TUI } from "../tui.js";
|
|
5
|
-
|
|
6
|
-
/** Terminal test double that records all writes for assertion. */
|
|
7
|
-
class MockTerminal implements Terminal {
|
|
8
|
-
private readonly width: number;
|
|
9
|
-
private readonly height: number;
|
|
10
|
-
public readonly writes: string[] = [];
|
|
11
|
-
|
|
12
|
-
constructor(width: number, height: number) {
|
|
13
|
-
this.width = width;
|
|
14
|
-
this.height = height;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
start(_onInput: (data: string) => void, _onResize: () => void): void {}
|
|
18
|
-
|
|
19
|
-
stop(): void {}
|
|
20
|
-
|
|
21
|
-
async drainInput(): Promise<void> {}
|
|
22
|
-
|
|
23
|
-
write(data: string): void {
|
|
24
|
-
this.writes.push(data);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get columns(): number {
|
|
28
|
-
return this.width;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get rows(): number {
|
|
32
|
-
return this.height;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get kittyProtocolActive(): boolean {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
moveBy(_lines: number): void {}
|
|
40
|
-
|
|
41
|
-
hideCursor(): void {}
|
|
42
|
-
|
|
43
|
-
showCursor(): void {}
|
|
44
|
-
|
|
45
|
-
clearLine(): void {}
|
|
46
|
-
|
|
47
|
-
clearFromCursor(): void {}
|
|
48
|
-
|
|
49
|
-
clearScreen(): void {}
|
|
50
|
-
|
|
51
|
-
enableMouse(): void {}
|
|
52
|
-
disableMouse(): void {}
|
|
53
|
-
enterAlternateScreen(): void {}
|
|
54
|
-
|
|
55
|
-
leaveAlternateScreen(): void {}
|
|
56
|
-
|
|
57
|
-
setTitle(_title: string): void {}
|
|
58
|
-
|
|
59
|
-
setProgress(_percent: number): void {}
|
|
60
|
-
|
|
61
|
-
clearProgress(): void {}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Mutable component that lets tests drive exact rendered line sequences. */
|
|
65
|
-
class MutableLinesComponent implements Component {
|
|
66
|
-
private lines: string[];
|
|
67
|
-
|
|
68
|
-
constructor(lines: string[]) {
|
|
69
|
-
this.lines = lines;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
setLines(lines: string[]): void {
|
|
73
|
-
this.lines = lines;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
render(_width: number): string[] {
|
|
77
|
-
return this.lines;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
invalidate(): void {}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
interface ScenarioResult {
|
|
84
|
-
border: string;
|
|
85
|
-
finalWrite: string;
|
|
86
|
-
redrawsBeforeUpdate: number;
|
|
87
|
-
redrawsAfterUpdate: number;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Invoke TUI's internal render synchronously for deterministic testing.
|
|
92
|
-
*
|
|
93
|
-
* @param tui - TUI instance under test
|
|
94
|
-
*/
|
|
95
|
-
function renderNow(tui: TUI): void {
|
|
96
|
-
const renderer = tui as unknown as { doRender: () => void };
|
|
97
|
-
renderer.doRender();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Build a frame with stable lines, editor-like borders, and optional trailing lines.
|
|
102
|
-
*
|
|
103
|
-
* @param stableLines - Number of unchanged lines before editor
|
|
104
|
-
* @param inputText - Editor content line between borders
|
|
105
|
-
* @param trailingLines - Number of transient lines after editor
|
|
106
|
-
* @param width - Terminal width in columns
|
|
107
|
-
* @returns Frame lines for the component render output
|
|
108
|
-
*/
|
|
109
|
-
function createFrame(
|
|
110
|
-
stableLines: number,
|
|
111
|
-
inputText: string,
|
|
112
|
-
trailingLines: number,
|
|
113
|
-
width: number
|
|
114
|
-
): string[] {
|
|
115
|
-
const stable = Array.from({ length: stableLines }, (_, index) => `stable ${index}`);
|
|
116
|
-
const border = "─".repeat(width);
|
|
117
|
-
const inputLine = inputText.padEnd(width, " ").slice(0, width);
|
|
118
|
-
const trailing = Array.from({ length: trailingLines }, (_, index) => `tail ${index}`);
|
|
119
|
-
return [...stable, border, inputLine, border, ...trailing];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Reproduce grow -> shrink -> update sequence that previously risked row drift.
|
|
124
|
-
*
|
|
125
|
-
* @returns Scenario outputs used by assertions
|
|
126
|
-
*/
|
|
127
|
-
function runGrowShrinkUpdateScenario(): ScenarioResult {
|
|
128
|
-
const width = 32;
|
|
129
|
-
const height = 10;
|
|
130
|
-
const stableLines = 18;
|
|
131
|
-
const border = "─".repeat(width);
|
|
132
|
-
const terminal = new MockTerminal(width, height);
|
|
133
|
-
const tui = new TUI(terminal);
|
|
134
|
-
const component = new MutableLinesComponent(createFrame(stableLines, "input A", 9, width));
|
|
135
|
-
tui.addChild(component);
|
|
136
|
-
|
|
137
|
-
// Grow to establish a large working area.
|
|
138
|
-
renderNow(tui);
|
|
139
|
-
|
|
140
|
-
// Shrink without changing earlier viewport lines. This keeps maxLinesRendered > previousLines.length.
|
|
141
|
-
component.setLines(createFrame(stableLines, "input A", 0, width));
|
|
142
|
-
renderNow(tui);
|
|
143
|
-
|
|
144
|
-
const redrawsBeforeUpdate = tui.fullRedraws;
|
|
145
|
-
|
|
146
|
-
// Trigger a regular update in the editor band after shrink.
|
|
147
|
-
component.setLines(createFrame(stableLines, "input B", 0, width));
|
|
148
|
-
renderNow(tui);
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
border,
|
|
152
|
-
finalWrite: terminal.writes[terminal.writes.length - 1] ?? "",
|
|
153
|
-
redrawsBeforeUpdate,
|
|
154
|
-
redrawsAfterUpdate: tui.fullRedraws,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Reproduce grow -> shrink -> grow -> update cycle that simulates agent turn
|
|
160
|
-
* content fluctuations (loader appears, content shrinks, new content arrives).
|
|
161
|
-
*
|
|
162
|
-
* @returns All terminal writes and final redraw count
|
|
163
|
-
*/
|
|
164
|
-
function runHeightFluctuationScenario(): { allWrites: string[]; fullRedraws: number } {
|
|
165
|
-
const width = 32;
|
|
166
|
-
const height = 10;
|
|
167
|
-
const stableLines = 18;
|
|
168
|
-
const terminal = new MockTerminal(width, height);
|
|
169
|
-
const tui = new TUI(terminal);
|
|
170
|
-
const component = new MutableLinesComponent(createFrame(stableLines, "input A", 9, width));
|
|
171
|
-
tui.addChild(component);
|
|
172
|
-
|
|
173
|
-
// Phase 1: Grow to establish a large working area (simulates loader + streaming).
|
|
174
|
-
renderNow(tui);
|
|
175
|
-
|
|
176
|
-
// Phase 2: Shrink (simulates loader stopping or tool result replacing progress).
|
|
177
|
-
component.setLines(createFrame(stableLines, "input A", 0, width));
|
|
178
|
-
renderNow(tui);
|
|
179
|
-
|
|
180
|
-
// Phase 3: Grow again (simulates new streaming content arriving).
|
|
181
|
-
component.setLines(createFrame(stableLines, "input A", 5, width));
|
|
182
|
-
renderNow(tui);
|
|
183
|
-
|
|
184
|
-
// Phase 4: Update within content (simulates editor input change).
|
|
185
|
-
component.setLines(createFrame(stableLines, "input B", 5, width));
|
|
186
|
-
renderNow(tui);
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
allWrites: [...terminal.writes],
|
|
190
|
-
fullRedraws: tui.fullRedraws,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
describe("TUI differential rendering shrink regression", () => {
|
|
195
|
-
test("realigns viewport basis on drift instead of full redraw", () => {
|
|
196
|
-
const result = runGrowShrinkUpdateScenario();
|
|
197
|
-
|
|
198
|
-
// Viewport basis drift is now handled by realignment, not full redraw.
|
|
199
|
-
// The update render should use a partial redraw (no increase in fullRedraws).
|
|
200
|
-
expect(result.redrawsAfterUpdate).toBe(result.redrawsBeforeUpdate);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
test("keeps editor content correct after grow->shrink->update", () => {
|
|
204
|
-
const result = runGrowShrinkUpdateScenario();
|
|
205
|
-
const plain = stripAnsi(result.finalWrite);
|
|
206
|
-
|
|
207
|
-
// The partial redraw only writes the changed line, not the full content.
|
|
208
|
-
// Borders are already on screen from the prior render and don't need
|
|
209
|
-
// to be redrawn — their absence in the final write proves partial redraw worked.
|
|
210
|
-
expect(plain).toContain("input B");
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test("never clears scrollback during content height fluctuation", () => {
|
|
214
|
-
const result = runHeightFluctuationScenario();
|
|
215
|
-
|
|
216
|
-
// No write should ever contain \x1b[3J (clear scrollback).
|
|
217
|
-
// This sequence destroys the user's scroll position and reading context.
|
|
218
|
-
for (const write of result.allWrites) {
|
|
219
|
-
expect(write).not.toContain("\x1b[3J");
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test("never clears scrollback during grow->shrink->update", () => {
|
|
224
|
-
const width = 32;
|
|
225
|
-
const height = 10;
|
|
226
|
-
const stableLines = 18;
|
|
227
|
-
const terminal = new MockTerminal(width, height);
|
|
228
|
-
const tui = new TUI(terminal);
|
|
229
|
-
const component = new MutableLinesComponent(createFrame(stableLines, "input A", 9, width));
|
|
230
|
-
tui.addChild(component);
|
|
231
|
-
|
|
232
|
-
renderNow(tui);
|
|
233
|
-
|
|
234
|
-
component.setLines(createFrame(stableLines, "input A", 0, width));
|
|
235
|
-
renderNow(tui);
|
|
236
|
-
|
|
237
|
-
component.setLines(createFrame(stableLines, "input B", 0, width));
|
|
238
|
-
renderNow(tui);
|
|
239
|
-
|
|
240
|
-
for (const write of terminal.writes) {
|
|
241
|
-
expect(write).not.toContain("\x1b[3J");
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
test("extraLines > height in diff path triggers safety full redraw", () => {
|
|
246
|
-
const width = 40;
|
|
247
|
-
const height = 10;
|
|
248
|
-
const terminal = new MockTerminal(width, height);
|
|
249
|
-
const tui = new TUI(terminal);
|
|
250
|
-
// Start with 30 lines (extraLines on shrink = 25, which exceeds height = 10)
|
|
251
|
-
const component = new MutableLinesComponent(Array.from({ length: 30 }, (_, i) => `line ${i}`));
|
|
252
|
-
tui.addChild(component);
|
|
253
|
-
renderNow(tui);
|
|
254
|
-
|
|
255
|
-
// Shrink to 5 lines with changed content so we don't hit the "all deleted" path
|
|
256
|
-
component.setLines(Array.from({ length: 5 }, (_, i) => `changed ${i}`));
|
|
257
|
-
const redrawsBefore = tui.fullRedraws;
|
|
258
|
-
renderNow(tui);
|
|
259
|
-
|
|
260
|
-
// Should have triggered safety full redraw (extraLines = 25 > height = 10)
|
|
261
|
-
expect(tui.fullRedraws).toBe(redrawsBefore + 1);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
test("maxLinesRendered decays after shrink without overlays", () => {
|
|
265
|
-
const width = 40;
|
|
266
|
-
const height = 20;
|
|
267
|
-
const terminal = new MockTerminal(width, height);
|
|
268
|
-
const tui = new TUI(terminal);
|
|
269
|
-
const component = new MutableLinesComponent(Array.from({ length: 15 }, (_, i) => `line ${i}`));
|
|
270
|
-
tui.addChild(component);
|
|
271
|
-
renderNow(tui);
|
|
272
|
-
|
|
273
|
-
component.setLines(Array.from({ length: 8 }, (_, i) => `short ${i}`));
|
|
274
|
-
renderNow(tui);
|
|
275
|
-
|
|
276
|
-
// maxLinesRendered should have decayed to actual content size
|
|
277
|
-
const tuiInternal = tui as unknown as { maxLinesRendered: number };
|
|
278
|
-
expect(tuiInternal.maxLinesRendered).toBe(8);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
test("grow-shrink-grow does not leave ghost gap", () => {
|
|
282
|
-
const width = 40;
|
|
283
|
-
const height = 10;
|
|
284
|
-
const terminal = new MockTerminal(width, height);
|
|
285
|
-
const tui = new TUI(terminal);
|
|
286
|
-
const component = new MutableLinesComponent(Array.from({ length: 25 }, (_, i) => `line ${i}`));
|
|
287
|
-
tui.addChild(component);
|
|
288
|
-
renderNow(tui); // 25 lines
|
|
289
|
-
|
|
290
|
-
component.setLines(Array.from({ length: 8 }, (_, i) => `shrunk ${i}`));
|
|
291
|
-
renderNow(tui); // Shrink to 8
|
|
292
|
-
|
|
293
|
-
component.setLines(Array.from({ length: 12 }, (_, i) => `grown ${i}`));
|
|
294
|
-
renderNow(tui); // Grow to 12
|
|
295
|
-
|
|
296
|
-
// maxLinesRendered should track actual content, not stale high-water mark
|
|
297
|
-
const tuiInternal = tui as unknown as { maxLinesRendered: number };
|
|
298
|
-
expect(tuiInternal.maxLinesRendered).toBe(12);
|
|
299
|
-
|
|
300
|
-
// Verify no blank-line ghost: writes should not contain scrollback destruction
|
|
301
|
-
const lastWrite = terminal.writes[terminal.writes.length - 1] ?? "";
|
|
302
|
-
expect(lastWrite).not.toContain("\x1b[3J");
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test("viewport drift corrected on same render as shrink", () => {
|
|
306
|
-
const width = 40;
|
|
307
|
-
const height = 10;
|
|
308
|
-
const terminal = new MockTerminal(width, height);
|
|
309
|
-
const tui = new TUI(terminal);
|
|
310
|
-
// Start with 80 lines to establish large working area
|
|
311
|
-
const component = new MutableLinesComponent(Array.from({ length: 80 }, (_, i) => `line ${i}`));
|
|
312
|
-
tui.addChild(component);
|
|
313
|
-
renderNow(tui); // maxLinesRendered = 80
|
|
314
|
-
|
|
315
|
-
// Shrink to 30 — drift should be corrected immediately (same cycle)
|
|
316
|
-
component.setLines(Array.from({ length: 30 }, (_, i) => `shrunk ${i}`));
|
|
317
|
-
renderNow(tui);
|
|
318
|
-
|
|
319
|
-
const tuiInternal = tui as unknown as {
|
|
320
|
-
maxLinesRendered: number;
|
|
321
|
-
previousViewportTop: number;
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
// maxLinesRendered should have decayed to 30 (not stuck at 80)
|
|
325
|
-
expect(tuiInternal.maxLinesRendered).toBe(30);
|
|
326
|
-
// previousViewportTop should be consistent with the decayed maxLinesRendered
|
|
327
|
-
expect(tuiInternal.previousViewportTop).toBe(Math.max(0, 30 - height));
|
|
328
|
-
|
|
329
|
-
// Grow to 40 — should NOT require full redraw since drift was already corrected
|
|
330
|
-
component.setLines(Array.from({ length: 40 }, (_, i) => `grown ${i}`));
|
|
331
|
-
renderNow(tui);
|
|
332
|
-
|
|
333
|
-
// The key assertion is maxLinesRendered is correct
|
|
334
|
-
expect(tuiInternal.maxLinesRendered).toBe(40);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
test("large shrink (>5 lines) triggers full redraw to prevent ghosting", () => {
|
|
338
|
-
const width = 40;
|
|
339
|
-
const height = 20;
|
|
340
|
-
const terminal = new MockTerminal(width, height);
|
|
341
|
-
const tui = new TUI(terminal);
|
|
342
|
-
// Start with 18 lines
|
|
343
|
-
const component = new MutableLinesComponent(Array.from({ length: 18 }, (_, i) => `line ${i}`));
|
|
344
|
-
tui.addChild(component);
|
|
345
|
-
renderNow(tui); // First render (fullRedraw #1)
|
|
346
|
-
|
|
347
|
-
// Shrink by 6 lines (above the 5-line threshold) — should trigger full redraw
|
|
348
|
-
component.setLines(Array.from({ length: 12 }, (_, i) => `shrunk ${i}`));
|
|
349
|
-
const redrawsBefore = tui.fullRedraws;
|
|
350
|
-
renderNow(tui);
|
|
351
|
-
|
|
352
|
-
expect(tui.fullRedraws).toBe(redrawsBefore + 1);
|
|
353
|
-
const tuiInternal = tui as unknown as { maxLinesRendered: number };
|
|
354
|
-
expect(tuiInternal.maxLinesRendered).toBe(12);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test("small shrink (<=5 lines) uses partial diff, not full redraw", () => {
|
|
358
|
-
const width = 40;
|
|
359
|
-
const height = 20;
|
|
360
|
-
const terminal = new MockTerminal(width, height);
|
|
361
|
-
const tui = new TUI(terminal);
|
|
362
|
-
// Start with 12 lines
|
|
363
|
-
const component = new MutableLinesComponent(Array.from({ length: 12 }, (_, i) => `line ${i}`));
|
|
364
|
-
tui.addChild(component);
|
|
365
|
-
renderNow(tui); // First render (fullRedraw #1)
|
|
366
|
-
|
|
367
|
-
// Shrink by exactly 5 lines (at threshold, NOT over) — should NOT trigger
|
|
368
|
-
// the large-shrink full redraw path
|
|
369
|
-
component.setLines(Array.from({ length: 7 }, (_, i) => `shrunk ${i}`));
|
|
370
|
-
const redrawsBefore = tui.fullRedraws;
|
|
371
|
-
renderNow(tui);
|
|
372
|
-
|
|
373
|
-
// Should use partial diff (no increase in fullRedraws)
|
|
374
|
-
expect(tui.fullRedraws).toBe(redrawsBefore);
|
|
375
|
-
const tuiInternal = tui as unknown as { maxLinesRendered: number };
|
|
376
|
-
expect(tuiInternal.maxLinesRendered).toBe(7);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test("rapid grow-shrink-grow cycles don't accumulate ghost state", () => {
|
|
380
|
-
const width = 40;
|
|
381
|
-
const height = 15;
|
|
382
|
-
const terminal = new MockTerminal(width, height);
|
|
383
|
-
const tui = new TUI(terminal);
|
|
384
|
-
const component = new MutableLinesComponent(
|
|
385
|
-
Array.from({ length: 10 }, (_, i) => `initial ${i}`)
|
|
386
|
-
);
|
|
387
|
-
tui.addChild(component);
|
|
388
|
-
renderNow(tui);
|
|
389
|
-
|
|
390
|
-
const tuiInternal = tui as unknown as {
|
|
391
|
-
maxLinesRendered: number;
|
|
392
|
-
previousViewportTop: number;
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
// Simulate 5 rapid grow-shrink cycles (like streaming tool output + collapse)
|
|
396
|
-
for (let cycle = 0; cycle < 5; cycle++) {
|
|
397
|
-
// Grow: simulate streaming output
|
|
398
|
-
component.setLines(Array.from({ length: 40 }, (_, i) => `stream-${cycle}-${i}`));
|
|
399
|
-
renderNow(tui);
|
|
400
|
-
|
|
401
|
-
// Shrink: simulate tool result collapse
|
|
402
|
-
component.setLines(Array.from({ length: 8 }, (_, i) => `result-${cycle}-${i}`));
|
|
403
|
-
renderNow(tui);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// After all cycles, maxLinesRendered should match actual content (8), not
|
|
407
|
-
// any stale high-water mark from the streaming phases
|
|
408
|
-
expect(tuiInternal.maxLinesRendered).toBe(8);
|
|
409
|
-
expect(tuiInternal.previousViewportTop).toBe(0); // 8 < height(15), so 0
|
|
410
|
-
|
|
411
|
-
// A subsequent update should work correctly via partial diff
|
|
412
|
-
component.setLines(Array.from({ length: 8 }, (_, i) => `final ${i}`));
|
|
413
|
-
const redrawsBefore = tui.fullRedraws;
|
|
414
|
-
renderNow(tui);
|
|
415
|
-
|
|
416
|
-
// No full redraw needed — content didn't shrink
|
|
417
|
-
expect(tui.fullRedraws).toBe(redrawsBefore);
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
test("drift correction uses newLines.length, not previousLines.length", () => {
|
|
421
|
-
const width = 40;
|
|
422
|
-
const height = 10;
|
|
423
|
-
const terminal = new MockTerminal(width, height);
|
|
424
|
-
const tui = new TUI(terminal);
|
|
425
|
-
|
|
426
|
-
// Use an overlay to keep maxLinesRendered inflated (overlay padding).
|
|
427
|
-
// With overlays, maxLinesRendered = Math.max(old, new) — only grows.
|
|
428
|
-
const component = new MutableLinesComponent(Array.from({ length: 50 }, (_, i) => `line ${i}`));
|
|
429
|
-
tui.addChild(component);
|
|
430
|
-
|
|
431
|
-
const overlayComponent: Component = {
|
|
432
|
-
render: () => ["overlay line"],
|
|
433
|
-
invalidate: () => {},
|
|
434
|
-
};
|
|
435
|
-
tui.showOverlay(overlayComponent);
|
|
436
|
-
renderNow(tui); // maxLinesRendered >= 50 (padded by overlay)
|
|
437
|
-
|
|
438
|
-
// Remove overlay and shrink content simultaneously — maxLinesRendered is
|
|
439
|
-
// still inflated from overlay padding, but newLines.length will be much
|
|
440
|
-
// smaller. The large-shrink guard (>5 lines) triggers a full redraw,
|
|
441
|
-
// which correctly resets maxLinesRendered to newLines.length.
|
|
442
|
-
tui.hideOverlay();
|
|
443
|
-
component.setLines(Array.from({ length: 20 }, (_, i) => `shrunk ${i}`));
|
|
444
|
-
renderNow(tui);
|
|
445
|
-
|
|
446
|
-
const tuiInternal = tui as unknown as {
|
|
447
|
-
maxLinesRendered: number;
|
|
448
|
-
previousViewportTop: number;
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
// maxLinesRendered should be 20 (newLines.length), not whatever
|
|
452
|
-
// previousLines.length was (which includes overlay padding)
|
|
453
|
-
expect(tuiInternal.maxLinesRendered).toBe(20);
|
|
454
|
-
expect(tuiInternal.previousViewportTop).toBe(Math.max(0, 20 - height));
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
test("requestScrollbackClear includes \\x1b[3J only on next full render", () => {
|
|
458
|
-
const width = 40;
|
|
459
|
-
const height = 10;
|
|
460
|
-
const terminal = new MockTerminal(width, height);
|
|
461
|
-
const tui = new TUI(terminal);
|
|
462
|
-
const component = new MutableLinesComponent(Array.from({ length: 20 }, (_, i) => `line ${i}`));
|
|
463
|
-
tui.addChild(component);
|
|
464
|
-
|
|
465
|
-
// Initial render — no scrollback clear
|
|
466
|
-
renderNow(tui);
|
|
467
|
-
expect(terminal.writes.some((w) => w.includes("\x1b[3J"))).toBe(false);
|
|
468
|
-
|
|
469
|
-
// Request scrollback clear, then shrink content to trigger fullRender(true)
|
|
470
|
-
tui.requestScrollbackClear();
|
|
471
|
-
component.setLines(Array.from({ length: 5 }, (_, i) => `new ${i}`));
|
|
472
|
-
renderNow(tui);
|
|
473
|
-
|
|
474
|
-
// The large shrink (20→5) triggers fullRender(true) which should include \x1b[3J
|
|
475
|
-
const clearWrite = terminal.writes.find((w) => w.includes("\x1b[3J"));
|
|
476
|
-
expect(clearWrite).toBeDefined();
|
|
477
|
-
|
|
478
|
-
// Subsequent renders should NOT include \x1b[3J (flag consumed)
|
|
479
|
-
terminal.writes.length = 0;
|
|
480
|
-
component.setLines(Array.from({ length: 3 }, (_, i) => `final ${i}`));
|
|
481
|
-
renderNow(tui);
|
|
482
|
-
expect(terminal.writes.some((w) => w.includes("\x1b[3J"))).toBe(false);
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
test("gradual shrink across multiple frames triggers full redraw", () => {
|
|
486
|
-
const width = 40;
|
|
487
|
-
const height = 10;
|
|
488
|
-
const terminal = new MockTerminal(width, height);
|
|
489
|
-
const tui = new TUI(terminal);
|
|
490
|
-
const component = new MutableLinesComponent(Array.from({ length: 20 }, (_, i) => `line ${i}`));
|
|
491
|
-
tui.addChild(component);
|
|
492
|
-
renderNow(tui); // Establish peak at 20 lines
|
|
493
|
-
|
|
494
|
-
const redraws = () => tui.fullRedraws;
|
|
495
|
-
|
|
496
|
-
// Shrink by 2 lines per frame — each frame delta is ≤5 (not caught by
|
|
497
|
-
// single-frame guard). The rolling peak should catch the accumulated drop.
|
|
498
|
-
const base = redraws();
|
|
499
|
-
component.setLines(Array.from({ length: 18 }, (_, i) => `line ${i}`)); // -2
|
|
500
|
-
renderNow(tui);
|
|
501
|
-
component.setLines(Array.from({ length: 16 }, (_, i) => `line ${i}`)); // -4 total
|
|
502
|
-
renderNow(tui);
|
|
503
|
-
// Still within threshold — partial redraws only
|
|
504
|
-
expect(redraws() - base).toBe(0);
|
|
505
|
-
|
|
506
|
-
component.setLines(Array.from({ length: 14 }, (_, i) => `line ${i}`)); // -6 total from peak
|
|
507
|
-
renderNow(tui);
|
|
508
|
-
// Accumulated shrink exceeds threshold — should have triggered full redraw
|
|
509
|
-
expect(redraws() - base).toBeGreaterThanOrEqual(1);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
test("rolling shrink peak resets after full redraw", () => {
|
|
513
|
-
const width = 40;
|
|
514
|
-
const height = 10;
|
|
515
|
-
const terminal = new MockTerminal(width, height);
|
|
516
|
-
const tui = new TUI(terminal);
|
|
517
|
-
const component = new MutableLinesComponent(Array.from({ length: 20 }, (_, i) => `line ${i}`));
|
|
518
|
-
tui.addChild(component);
|
|
519
|
-
renderNow(tui); // Peak = 20
|
|
520
|
-
|
|
521
|
-
// Trigger rolling shrink detection
|
|
522
|
-
component.setLines(Array.from({ length: 13 }, (_, i) => `line ${i}`)); // -7
|
|
523
|
-
renderNow(tui);
|
|
524
|
-
const afterFirstRedraw = tui.fullRedraws;
|
|
525
|
-
|
|
526
|
-
// Now shrink by only 2 from the new peak (13) — should NOT trigger
|
|
527
|
-
component.setLines(Array.from({ length: 11 }, (_, i) => `line ${i}`)); // -2 from reset peak
|
|
528
|
-
renderNow(tui);
|
|
529
|
-
expect(tui.fullRedraws).toBe(afterFirstRedraw);
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
test("requestScrollbackClear has no effect on partial (differential) renders", () => {
|
|
533
|
-
const width = 40;
|
|
534
|
-
const height = 10;
|
|
535
|
-
const terminal = new MockTerminal(width, height);
|
|
536
|
-
const tui = new TUI(terminal);
|
|
537
|
-
const component = new MutableLinesComponent(Array.from({ length: 8 }, (_, i) => `line ${i}`));
|
|
538
|
-
tui.addChild(component);
|
|
539
|
-
renderNow(tui);
|
|
540
|
-
|
|
541
|
-
// Request scrollback clear but only change one line (small diff, no fullRender)
|
|
542
|
-
tui.requestScrollbackClear();
|
|
543
|
-
component.setLines(Array.from({ length: 8 }, (_, i) => (i === 3 ? "CHANGED" : `line ${i}`)));
|
|
544
|
-
renderNow(tui);
|
|
545
|
-
|
|
546
|
-
// Partial render path — no \x1b[3J emitted, flag stays pending
|
|
547
|
-
expect(terminal.writes.some((w) => w.includes("\x1b[3J"))).toBe(false);
|
|
548
|
-
|
|
549
|
-
// Now trigger a large shrink to fire fullRender(true) — flag should still be pending
|
|
550
|
-
terminal.writes.length = 0;
|
|
551
|
-
component.setLines(["single line"]);
|
|
552
|
-
renderNow(tui);
|
|
553
|
-
expect(terminal.writes.some((w) => w.includes("\x1b[3J"))).toBe(true);
|
|
554
|
-
});
|
|
555
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Border character sets for box-drawing.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/** A set of box-drawing characters for rendering borders. */
|
|
8
|
-
export interface BorderStyle {
|
|
9
|
-
topLeft: string;
|
|
10
|
-
topRight: string;
|
|
11
|
-
bottomLeft: string;
|
|
12
|
-
bottomRight: string;
|
|
13
|
-
horizontal: string;
|
|
14
|
-
vertical: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Sharp corners — standard box-drawing (┌┐└┘). */
|
|
18
|
-
export const SHARP: BorderStyle = {
|
|
19
|
-
topLeft: "┌",
|
|
20
|
-
topRight: "┐",
|
|
21
|
-
bottomLeft: "└",
|
|
22
|
-
bottomRight: "┘",
|
|
23
|
-
horizontal: "─",
|
|
24
|
-
vertical: "│",
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/** Rounded corners — Unicode arc box-drawing (╭╮╰╯). */
|
|
28
|
-
export const ROUNDED: BorderStyle = {
|
|
29
|
-
topLeft: "╭",
|
|
30
|
-
topRight: "╮",
|
|
31
|
-
bottomLeft: "╰",
|
|
32
|
-
bottomRight: "╯",
|
|
33
|
-
horizontal: "─",
|
|
34
|
-
vertical: "│",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/** Flat — horizontal rules only, no corners or verticals. */
|
|
38
|
-
export const FLAT: BorderStyle = {
|
|
39
|
-
topLeft: "─",
|
|
40
|
-
topRight: "─",
|
|
41
|
-
bottomLeft: "─",
|
|
42
|
-
bottomRight: "─",
|
|
43
|
-
horizontal: "─",
|
|
44
|
-
vertical: " ",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Global default border style — set once, applies to all new BorderedBox instances.
|
|
49
|
-
* Extensions can set this at session_start to override.
|
|
50
|
-
*/
|
|
51
|
-
export let defaultBorderStyle: BorderStyle = SHARP;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Set the global default border style.
|
|
55
|
-
*
|
|
56
|
-
* @param style - Border style to use as default
|
|
57
|
-
*/
|
|
58
|
-
export function setDefaultBorderStyle(style: BorderStyle): void {
|
|
59
|
-
defaultBorderStyle = style;
|
|
60
|
-
}
|