omk-tui 1.3.2 → 1.4.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/CHANGELOG.md +34 -0
- package/dist/components/box.d.ts +5 -0
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +50 -15
- package/dist/components/box.js.map +1 -1
- package/dist/components/editor.d.ts +3 -1
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +4 -7
- package/dist/components/editor.js.map +1 -1
- package/dist/components/image.d.ts +2 -0
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js +4 -0
- package/dist/components/image.js.map +1 -1
- package/dist/components/markdown.d.ts +4 -0
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +14 -0
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/spacer.d.ts +2 -0
- package/dist/components/spacer.d.ts.map +1 -1
- package/dist/components/spacer.js +8 -0
- package/dist/components/spacer.js.map +1 -1
- package/dist/components/text.d.ts +4 -0
- package/dist/components/text.d.ts.map +1 -1
- package/dist/components/text.js +14 -0
- package/dist/components/text.js.map +1 -1
- package/dist/editor-component.d.ts +2 -0
- package/dist/editor-component.d.ts.map +1 -1
- package/dist/editor-component.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/line-reset-memo.d.ts +27 -0
- package/dist/line-reset-memo.d.ts.map +1 -0
- package/dist/line-reset-memo.js +101 -0
- package/dist/line-reset-memo.js.map +1 -0
- package/dist/render-generation.d.ts +24 -0
- package/dist/render-generation.d.ts.map +1 -0
- package/dist/render-generation.js +48 -0
- package/dist/render-generation.js.map +1 -0
- package/dist/terminal-output.d.ts +20 -1
- package/dist/terminal-output.d.ts.map +1 -1
- package/dist/terminal-output.js +62 -1
- package/dist/terminal-output.js.map +1 -1
- package/dist/terminal-resize-log.d.ts +39 -0
- package/dist/terminal-resize-log.d.ts.map +1 -0
- package/dist/terminal-resize-log.js +57 -0
- package/dist/terminal-resize-log.js.map +1 -0
- package/dist/terminal-resync.d.ts +74 -0
- package/dist/terminal-resync.d.ts.map +1 -0
- package/dist/terminal-resync.js +112 -0
- package/dist/terminal-resync.js.map +1 -0
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +1 -1
- package/dist/terminal.js.map +1 -1
- package/dist/tui.d.ts +29 -9
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +65 -86
- package/dist/tui.js.map +1 -1
- package/dist/undo-stack.d.ts +8 -2
- package/dist/undo-stack.d.ts.map +1 -1
- package/dist/undo-stack.js +12 -2
- package/dist/undo-stack.js.map +1 -1
- package/dist/utils.d.ts +2 -4
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -128
- package/dist/utils.js.map +1 -1
- package/dist/visible-width.d.ts +14 -0
- package/dist/visible-width.d.ts.map +1 -0
- package/dist/visible-width.js +163 -0
- package/dist/visible-width.js.map +1 -0
- package/dist/windowed-container.d.ts +120 -0
- package/dist/windowed-container.d.ts.map +1 -0
- package/dist/windowed-container.js +258 -0
- package/dist/windowed-container.js.map +1 -0
- package/dist/windowed-segments.d.ts +37 -0
- package/dist/windowed-segments.d.ts.map +1 -0
- package/dist/windowed-segments.js +133 -0
- package/dist/windowed-segments.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spacer.js","sourceRoot":"","sources":["../../src/components/spacer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"spacer.js","sourceRoot":"","sources":["../../src/components/spacer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAG/D;;GAEG;AACH,MAAM,OAAO,MAAM;IACV,KAAK,CAAS;IACd,gBAAgB,GAAG,CAAC,CAAC;IAE7B,YAAY,KAAK,GAAW,CAAC,EAAE;QAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAAA,CACnB;IAED,QAAQ,CAAC,KAAa,EAAQ;QAC7B,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK;YAAE,OAAO;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,gBAAgB,GAAG,oBAAoB,EAAE,CAAC;IAAA,CAC/C;IAED,mBAAmB,GAAW;QAC7B,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAAA,CAC7B;IAED,UAAU,GAAS;QAClB,0CAA0C;IADvB,CAEnB;IAED,MAAM,CAAC,MAAc,EAAY;QAChC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QACD,OAAO,MAAM,CAAC;IAAA,CACd;CACD","sourcesContent":["import { nextRenderGeneration } from \"../render-generation.ts\";\nimport type { Component } from \"../tui.ts\";\n\n/**\n * Spacer component that renders empty lines\n */\nexport class Spacer implements Component {\n\tprivate lines: number;\n\tprivate renderGeneration = 0;\n\n\tconstructor(lines: number = 1) {\n\t\tthis.lines = lines;\n\t}\n\n\tsetLines(lines: number): void {\n\t\tif (lines === this.lines) return;\n\t\tthis.lines = lines;\n\t\tthis.renderGeneration = nextRenderGeneration();\n\t}\n\n\tgetRenderGeneration(): number {\n\t\treturn this.renderGeneration;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(_width: number): string[] {\n\t\tconst result: string[] = [];\n\t\tfor (let i = 0; i < this.lines; i++) {\n\t\t\tresult.push(\"\");\n\t\t}\n\t\treturn result;\n\t}\n}\n"]}
|
|
@@ -10,9 +10,13 @@ export declare class Text implements Component {
|
|
|
10
10
|
private cachedText?;
|
|
11
11
|
private cachedWidth?;
|
|
12
12
|
private cachedLines?;
|
|
13
|
+
private renderGeneration;
|
|
13
14
|
constructor(text?: string, paddingX?: number, paddingY?: number, customBgFn?: (text: string) => string);
|
|
14
15
|
setText(text: string): void;
|
|
15
16
|
setCustomBgFn(customBgFn?: (text: string) => string): void;
|
|
17
|
+
getRenderGeneration(): number;
|
|
18
|
+
/** Off-screen windowing: drop wrapped lines without counting as a change. */
|
|
19
|
+
releaseRenderCache(): void;
|
|
16
20
|
invalidate(): void;
|
|
17
21
|
render(width: number): string[];
|
|
18
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/components/text.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/components/text.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C;;GAEG;AACH,qBAAa,IAAK,YAAW,SAAS;IACrC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAC,CAA2B;IAG9C,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAW;IAC/B,OAAO,CAAC,gBAAgB,CAAK;IAE7B,YAAY,IAAI,GAAE,MAAW,EAAE,QAAQ,GAAE,MAAU,EAAE,QAAQ,GAAE,MAAU,EAAE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAK/G;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAO1B;IAED,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAMzD;IAED,mBAAmB,IAAI,MAAM,CAE5B;IAED,6EAA6E;IAC7E,kBAAkB,IAAI,IAAI,CAIzB;IAED,UAAU,IAAI,IAAI,CAKjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4D9B;CACD","sourcesContent":["import { nextRenderGeneration } from \"../render-generation.ts\";\nimport type { Component } from \"../tui.ts\";\nimport { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\n\n/**\n * Text component - displays multi-line text with word wrapping\n */\nexport class Text implements Component {\n\tprivate text: string;\n\tprivate paddingX: number; // Left/right padding\n\tprivate paddingY: number; // Top/bottom padding\n\tprivate customBgFn?: (text: string) => string;\n\n\t// Cache for rendered output\n\tprivate cachedText?: string;\n\tprivate cachedWidth?: number;\n\tprivate cachedLines?: string[];\n\tprivate renderGeneration = 0;\n\n\tconstructor(text: string = \"\", paddingX: number = 1, paddingY: number = 1, customBgFn?: (text: string) => string) {\n\t\tthis.text = text;\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\n\t\tthis.customBgFn = customBgFn;\n\t}\n\n\tsetText(text: string): void {\n\t\tif (text === this.text) return;\n\t\tthis.text = text;\n\t\tthis.renderGeneration = nextRenderGeneration();\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tsetCustomBgFn(customBgFn?: (text: string) => string): void {\n\t\tthis.customBgFn = customBgFn;\n\t\tthis.renderGeneration = nextRenderGeneration();\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tgetRenderGeneration(): number {\n\t\treturn this.renderGeneration;\n\t}\n\n\t/** Off-screen windowing: drop wrapped lines without counting as a change. */\n\treleaseRenderCache(): void {\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.renderGeneration = nextRenderGeneration();\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Check cache\n\t\tif (this.cachedLines && this.cachedText === this.text && this.cachedWidth === width) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\t// Don't render anything if there's no actual text\n\t\tif (!this.text || this.text.trim() === \"\") {\n\t\t\tconst result: string[] = [];\n\t\t\tthis.cachedText = this.text;\n\t\t\tthis.cachedWidth = width;\n\t\t\tthis.cachedLines = result;\n\t\t\treturn result;\n\t\t}\n\n\t\t// Replace tabs with 3 spaces\n\t\tconst normalizedText = this.text.replace(/\\t/g, \" \");\n\n\t\t// Calculate content width (subtract left/right margins)\n\t\tconst contentWidth = Math.max(1, width - this.paddingX * 2);\n\n\t\t// Wrap text (this preserves ANSI codes but does NOT pad)\n\t\tconst wrappedLines = wrapTextWithAnsi(normalizedText, contentWidth);\n\n\t\t// Add margins and background to each line\n\t\tconst leftMargin = \" \".repeat(this.paddingX);\n\t\tconst rightMargin = \" \".repeat(this.paddingX);\n\t\tconst contentLines: string[] = [];\n\n\t\tfor (const line of wrappedLines) {\n\t\t\t// Add margins\n\t\t\tconst lineWithMargins = leftMargin + line + rightMargin;\n\n\t\t\t// Apply background if specified (this also pads to full width)\n\t\t\tif (this.customBgFn) {\n\t\t\t\tcontentLines.push(applyBackgroundToLine(lineWithMargins, width, this.customBgFn));\n\t\t\t} else {\n\t\t\t\t// No background - just pad to width with spaces\n\t\t\t\tconst visibleLen = visibleWidth(lineWithMargins);\n\t\t\t\tconst paddingNeeded = Math.max(0, width - visibleLen);\n\t\t\t\tcontentLines.push(lineWithMargins + \" \".repeat(paddingNeeded));\n\t\t\t}\n\t\t}\n\n\t\t// Add top/bottom padding (empty lines)\n\t\tconst emptyLine = \" \".repeat(width);\n\t\tconst emptyLines: string[] = [];\n\t\tfor (let i = 0; i < this.paddingY; i++) {\n\t\t\tconst line = this.customBgFn ? applyBackgroundToLine(emptyLine, width, this.customBgFn) : emptyLine;\n\t\t\temptyLines.push(line);\n\t\t}\n\n\t\tconst result = [...emptyLines, ...contentLines, ...emptyLines];\n\n\t\t// Update cache\n\t\tthis.cachedText = this.text;\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedLines = result;\n\n\t\treturn result.length > 0 ? result : [\"\"];\n\t}\n}\n"]}
|
package/dist/components/text.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nextRenderGeneration } from "../render-generation.js";
|
|
1
2
|
import { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
|
2
3
|
/**
|
|
3
4
|
* Text component - displays multi-line text with word wrapping
|
|
@@ -11,6 +12,7 @@ export class Text {
|
|
|
11
12
|
cachedText;
|
|
12
13
|
cachedWidth;
|
|
13
14
|
cachedLines;
|
|
15
|
+
renderGeneration = 0;
|
|
14
16
|
constructor(text = "", paddingX = 1, paddingY = 1, customBgFn) {
|
|
15
17
|
this.text = text;
|
|
16
18
|
this.paddingX = paddingX;
|
|
@@ -21,17 +23,29 @@ export class Text {
|
|
|
21
23
|
if (text === this.text)
|
|
22
24
|
return;
|
|
23
25
|
this.text = text;
|
|
26
|
+
this.renderGeneration = nextRenderGeneration();
|
|
24
27
|
this.cachedText = undefined;
|
|
25
28
|
this.cachedWidth = undefined;
|
|
26
29
|
this.cachedLines = undefined;
|
|
27
30
|
}
|
|
28
31
|
setCustomBgFn(customBgFn) {
|
|
29
32
|
this.customBgFn = customBgFn;
|
|
33
|
+
this.renderGeneration = nextRenderGeneration();
|
|
34
|
+
this.cachedText = undefined;
|
|
35
|
+
this.cachedWidth = undefined;
|
|
36
|
+
this.cachedLines = undefined;
|
|
37
|
+
}
|
|
38
|
+
getRenderGeneration() {
|
|
39
|
+
return this.renderGeneration;
|
|
40
|
+
}
|
|
41
|
+
/** Off-screen windowing: drop wrapped lines without counting as a change. */
|
|
42
|
+
releaseRenderCache() {
|
|
30
43
|
this.cachedText = undefined;
|
|
31
44
|
this.cachedWidth = undefined;
|
|
32
45
|
this.cachedLines = undefined;
|
|
33
46
|
}
|
|
34
47
|
invalidate() {
|
|
48
|
+
this.renderGeneration = nextRenderGeneration();
|
|
35
49
|
this.cachedText = undefined;
|
|
36
50
|
this.cachedWidth = undefined;
|
|
37
51
|
this.cachedLines = undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAE/D,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpF;;GAEG;AACH,MAAM,OAAO,IAAI;IACR,IAAI,CAAS;IACb,QAAQ,CAAS,CAAC,qBAAqB;IACvC,QAAQ,CAAS,CAAC,qBAAqB;IACvC,UAAU,CAA4B;IAE9C,4BAA4B;IACpB,UAAU,CAAU;IACpB,WAAW,CAAU;IACrB,WAAW,CAAY;IACvB,gBAAgB,GAAG,CAAC,CAAC;IAE7B,YAAY,IAAI,GAAW,EAAE,EAAE,QAAQ,GAAW,CAAC,EAAE,QAAQ,GAAW,CAAC,EAAE,UAAqC,EAAE;QACjH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAAA,CAC7B;IAED,OAAO,CAAC,IAAY,EAAQ;QAC3B,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI;YAAE,OAAO;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,gBAAgB,GAAG,oBAAoB,EAAE,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAAA,CAC7B;IAED,aAAa,CAAC,UAAqC,EAAQ;QAC1D,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,gBAAgB,GAAG,oBAAoB,EAAE,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAAA,CAC7B;IAED,mBAAmB,GAAW;QAC7B,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAAA,CAC7B;IAED,6EAA6E;IAC7E,kBAAkB,GAAS;QAC1B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAAA,CAC7B;IAED,UAAU,GAAS;QAClB,IAAI,CAAC,gBAAgB,GAAG,oBAAoB,EAAE,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAAA,CAC7B;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,cAAc;QACd,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;YACrF,OAAO,IAAI,CAAC,WAAW,CAAC;QACzB,CAAC;QAED,kDAAkD;QAClD,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC3C,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;YAC5B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;YAC1B,OAAO,MAAM,CAAC;QACf,CAAC;QAED,6BAA6B;QAC7B,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAEvD,wDAAwD;QACxD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAE5D,yDAAyD;QACzD,MAAM,YAAY,GAAG,gBAAgB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QAEpE,0CAA0C;QAC1C,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YACjC,cAAc;YACd,MAAM,eAAe,GAAG,UAAU,GAAG,IAAI,GAAG,WAAW,CAAC;YAExD,+DAA+D;YAC/D,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACrB,YAAY,CAAC,IAAI,CAAC,qBAAqB,CAAC,eAAe,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YACnF,CAAC;iBAAM,CAAC;gBACP,gDAAgD;gBAChD,MAAM,UAAU,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;gBACjD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,CAAC;gBACtD,YAAY,CAAC,IAAI,CAAC,eAAe,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;YAChE,CAAC;QACF,CAAC;QAED,uCAAuC;QACvC,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,qBAAqB,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACpG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,GAAG,UAAU,EAAE,GAAG,YAAY,EAAE,GAAG,UAAU,CAAC,CAAC;QAE/D,eAAe;QACf,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;QAE1B,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAAA,CACzC;CACD","sourcesContent":["import { nextRenderGeneration } from \"../render-generation.ts\";\nimport type { Component } from \"../tui.ts\";\nimport { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\n\n/**\n * Text component - displays multi-line text with word wrapping\n */\nexport class Text implements Component {\n\tprivate text: string;\n\tprivate paddingX: number; // Left/right padding\n\tprivate paddingY: number; // Top/bottom padding\n\tprivate customBgFn?: (text: string) => string;\n\n\t// Cache for rendered output\n\tprivate cachedText?: string;\n\tprivate cachedWidth?: number;\n\tprivate cachedLines?: string[];\n\tprivate renderGeneration = 0;\n\n\tconstructor(text: string = \"\", paddingX: number = 1, paddingY: number = 1, customBgFn?: (text: string) => string) {\n\t\tthis.text = text;\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\n\t\tthis.customBgFn = customBgFn;\n\t}\n\n\tsetText(text: string): void {\n\t\tif (text === this.text) return;\n\t\tthis.text = text;\n\t\tthis.renderGeneration = nextRenderGeneration();\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tsetCustomBgFn(customBgFn?: (text: string) => string): void {\n\t\tthis.customBgFn = customBgFn;\n\t\tthis.renderGeneration = nextRenderGeneration();\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tgetRenderGeneration(): number {\n\t\treturn this.renderGeneration;\n\t}\n\n\t/** Off-screen windowing: drop wrapped lines without counting as a change. */\n\treleaseRenderCache(): void {\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.renderGeneration = nextRenderGeneration();\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Check cache\n\t\tif (this.cachedLines && this.cachedText === this.text && this.cachedWidth === width) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\t// Don't render anything if there's no actual text\n\t\tif (!this.text || this.text.trim() === \"\") {\n\t\t\tconst result: string[] = [];\n\t\t\tthis.cachedText = this.text;\n\t\t\tthis.cachedWidth = width;\n\t\t\tthis.cachedLines = result;\n\t\t\treturn result;\n\t\t}\n\n\t\t// Replace tabs with 3 spaces\n\t\tconst normalizedText = this.text.replace(/\\t/g, \" \");\n\n\t\t// Calculate content width (subtract left/right margins)\n\t\tconst contentWidth = Math.max(1, width - this.paddingX * 2);\n\n\t\t// Wrap text (this preserves ANSI codes but does NOT pad)\n\t\tconst wrappedLines = wrapTextWithAnsi(normalizedText, contentWidth);\n\n\t\t// Add margins and background to each line\n\t\tconst leftMargin = \" \".repeat(this.paddingX);\n\t\tconst rightMargin = \" \".repeat(this.paddingX);\n\t\tconst contentLines: string[] = [];\n\n\t\tfor (const line of wrappedLines) {\n\t\t\t// Add margins\n\t\t\tconst lineWithMargins = leftMargin + line + rightMargin;\n\n\t\t\t// Apply background if specified (this also pads to full width)\n\t\t\tif (this.customBgFn) {\n\t\t\t\tcontentLines.push(applyBackgroundToLine(lineWithMargins, width, this.customBgFn));\n\t\t\t} else {\n\t\t\t\t// No background - just pad to width with spaces\n\t\t\t\tconst visibleLen = visibleWidth(lineWithMargins);\n\t\t\t\tconst paddingNeeded = Math.max(0, width - visibleLen);\n\t\t\t\tcontentLines.push(lineWithMargins + \" \".repeat(paddingNeeded));\n\t\t\t}\n\t\t}\n\n\t\t// Add top/bottom padding (empty lines)\n\t\tconst emptyLine = \" \".repeat(width);\n\t\tconst emptyLines: string[] = [];\n\t\tfor (let i = 0; i < this.paddingY; i++) {\n\t\t\tconst line = this.customBgFn ? applyBackgroundToLine(emptyLine, width, this.customBgFn) : emptyLine;\n\t\t\temptyLines.push(line);\n\t\t}\n\n\t\tconst result = [...emptyLines, ...contentLines, ...emptyLines];\n\n\t\t// Update cache\n\t\tthis.cachedText = this.text;\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedLines = result;\n\n\t\treturn result.length > 0 ? result : [\"\"];\n\t}\n}\n"]}
|
|
@@ -18,6 +18,8 @@ export interface EditorComponent extends Component {
|
|
|
18
18
|
onSubmit?: (text: string) => void;
|
|
19
19
|
/** Called when text changes */
|
|
20
20
|
onChange?: (text: string) => void;
|
|
21
|
+
/** Optional complete bracketed-paste interception; true consumes the paste without inserting text. */
|
|
22
|
+
onPaste?: (text: string) => boolean;
|
|
21
23
|
/** Add text to history for up/down navigation */
|
|
22
24
|
addToHistory?(text: string): void;
|
|
23
25
|
/** Insert text at current cursor position */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-component.d.ts","sourceRoot":"","sources":["../src/editor-component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAKjD,mCAAmC;IACnC,OAAO,IAAI,MAAM,CAAC;IAElB,2BAA2B;IAC3B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B,qEAAqE;IACrE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAMhC,iDAAiD;IACjD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"editor-component.d.ts","sourceRoot":"","sources":["../src/editor-component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAKjD,mCAAmC;IACnC,OAAO,IAAI,MAAM,CAAC;IAElB,2BAA2B;IAC3B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B,qEAAqE;IACrE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAMhC,iDAAiD;IACjD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,sGAAsG;IACtG,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAMpC,iDAAiD;IACjD,YAAY,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAMlC,6CAA6C;IAC7C,kBAAkB,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAExC;;;OAGG;IACH,eAAe,CAAC,IAAI,MAAM,CAAC;IAM3B,oCAAoC;IACpC,uBAAuB,CAAC,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAM/D,4BAA4B;IAC5B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAEtC,6BAA6B;IAC7B,WAAW,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEpC,qDAAqD;IACrD,yBAAyB,CAAC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAErD,8DAA8D;IAC9D,cAAc,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC","sourcesContent":["import type { AutocompleteProvider } from \"./autocomplete.ts\";\nimport type { Component } from \"./tui.ts\";\n\n/**\n * Interface for custom editor components.\n *\n * This allows extensions to provide their own editor implementation\n * (e.g., vim mode, emacs mode, custom keybindings) while maintaining\n * compatibility with the core application.\n */\nexport interface EditorComponent extends Component {\n\t// =========================================================================\n\t// Core text access (required)\n\t// =========================================================================\n\n\t/** Get the current text content */\n\tgetText(): string;\n\n\t/** Set the text content */\n\tsetText(text: string): void;\n\n\t/** Handle raw terminal input (key presses, paste sequences, etc.) */\n\thandleInput(data: string): void;\n\n\t// =========================================================================\n\t// Callbacks (required)\n\t// =========================================================================\n\n\t/** Called when user submits (e.g., Enter key) */\n\tonSubmit?: (text: string) => void;\n\n\t/** Called when text changes */\n\tonChange?: (text: string) => void;\n\n\t/** Optional complete bracketed-paste interception; true consumes the paste without inserting text. */\n\tonPaste?: (text: string) => boolean;\n\n\t// =========================================================================\n\t// History support (optional)\n\t// =========================================================================\n\n\t/** Add text to history for up/down navigation */\n\taddToHistory?(text: string): void;\n\n\t// =========================================================================\n\t// Advanced text manipulation (optional)\n\t// =========================================================================\n\n\t/** Insert text at current cursor position */\n\tinsertTextAtCursor?(text: string): void;\n\n\t/**\n\t * Get text with any markers expanded (e.g., paste markers).\n\t * Falls back to getText() if not implemented.\n\t */\n\tgetExpandedText?(): string;\n\n\t// =========================================================================\n\t// Autocomplete support (optional)\n\t// =========================================================================\n\n\t/** Set the autocomplete provider */\n\tsetAutocompleteProvider?(provider: AutocompleteProvider): void;\n\n\t// =========================================================================\n\t// Appearance (optional)\n\t// =========================================================================\n\n\t/** Border color function */\n\tborderColor?: (str: string) => string;\n\n\t/** Set horizontal padding */\n\tsetPaddingX?(padding: number): void;\n\n\t/** Set max visible items in autocomplete dropdown */\n\tsetAutocompleteMaxVisible?(maxVisible: number): void;\n\n\t/** Set placeholder text rendered while the editor is empty */\n\tsetPlaceholder?(text: string): void;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-component.js","sourceRoot":"","sources":["../src/editor-component.ts"],"names":[],"mappings":"","sourcesContent":["import type { AutocompleteProvider } from \"./autocomplete.ts\";\nimport type { Component } from \"./tui.ts\";\n\n/**\n * Interface for custom editor components.\n *\n * This allows extensions to provide their own editor implementation\n * (e.g., vim mode, emacs mode, custom keybindings) while maintaining\n * compatibility with the core application.\n */\nexport interface EditorComponent extends Component {\n\t// =========================================================================\n\t// Core text access (required)\n\t// =========================================================================\n\n\t/** Get the current text content */\n\tgetText(): string;\n\n\t/** Set the text content */\n\tsetText(text: string): void;\n\n\t/** Handle raw terminal input (key presses, paste sequences, etc.) */\n\thandleInput(data: string): void;\n\n\t// =========================================================================\n\t// Callbacks (required)\n\t// =========================================================================\n\n\t/** Called when user submits (e.g., Enter key) */\n\tonSubmit?: (text: string) => void;\n\n\t/** Called when text changes */\n\tonChange?: (text: string) => void;\n\n\t// =========================================================================\n\t// History support (optional)\n\t// =========================================================================\n\n\t/** Add text to history for up/down navigation */\n\taddToHistory?(text: string): void;\n\n\t// =========================================================================\n\t// Advanced text manipulation (optional)\n\t// =========================================================================\n\n\t/** Insert text at current cursor position */\n\tinsertTextAtCursor?(text: string): void;\n\n\t/**\n\t * Get text with any markers expanded (e.g., paste markers).\n\t * Falls back to getText() if not implemented.\n\t */\n\tgetExpandedText?(): string;\n\n\t// =========================================================================\n\t// Autocomplete support (optional)\n\t// =========================================================================\n\n\t/** Set the autocomplete provider */\n\tsetAutocompleteProvider?(provider: AutocompleteProvider): void;\n\n\t// =========================================================================\n\t// Appearance (optional)\n\t// =========================================================================\n\n\t/** Border color function */\n\tborderColor?: (str: string) => string;\n\n\t/** Set horizontal padding */\n\tsetPaddingX?(padding: number): void;\n\n\t/** Set max visible items in autocomplete dropdown */\n\tsetAutocompleteMaxVisible?(maxVisible: number): void;\n\n\t/** Set placeholder text rendered while the editor is empty */\n\tsetPlaceholder?(text: string): void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"editor-component.js","sourceRoot":"","sources":["../src/editor-component.ts"],"names":[],"mappings":"","sourcesContent":["import type { AutocompleteProvider } from \"./autocomplete.ts\";\nimport type { Component } from \"./tui.ts\";\n\n/**\n * Interface for custom editor components.\n *\n * This allows extensions to provide their own editor implementation\n * (e.g., vim mode, emacs mode, custom keybindings) while maintaining\n * compatibility with the core application.\n */\nexport interface EditorComponent extends Component {\n\t// =========================================================================\n\t// Core text access (required)\n\t// =========================================================================\n\n\t/** Get the current text content */\n\tgetText(): string;\n\n\t/** Set the text content */\n\tsetText(text: string): void;\n\n\t/** Handle raw terminal input (key presses, paste sequences, etc.) */\n\thandleInput(data: string): void;\n\n\t// =========================================================================\n\t// Callbacks (required)\n\t// =========================================================================\n\n\t/** Called when user submits (e.g., Enter key) */\n\tonSubmit?: (text: string) => void;\n\n\t/** Called when text changes */\n\tonChange?: (text: string) => void;\n\n\t/** Optional complete bracketed-paste interception; true consumes the paste without inserting text. */\n\tonPaste?: (text: string) => boolean;\n\n\t// =========================================================================\n\t// History support (optional)\n\t// =========================================================================\n\n\t/** Add text to history for up/down navigation */\n\taddToHistory?(text: string): void;\n\n\t// =========================================================================\n\t// Advanced text manipulation (optional)\n\t// =========================================================================\n\n\t/** Insert text at current cursor position */\n\tinsertTextAtCursor?(text: string): void;\n\n\t/**\n\t * Get text with any markers expanded (e.g., paste markers).\n\t * Falls back to getText() if not implemented.\n\t */\n\tgetExpandedText?(): string;\n\n\t// =========================================================================\n\t// Autocomplete support (optional)\n\t// =========================================================================\n\n\t/** Set the autocomplete provider */\n\tsetAutocompleteProvider?(provider: AutocompleteProvider): void;\n\n\t// =========================================================================\n\t// Appearance (optional)\n\t// =========================================================================\n\n\t/** Border color function */\n\tborderColor?: (str: string) => string;\n\n\t/** Set horizontal padding */\n\tsetPaddingX?(padding: number): void;\n\n\t/** Set max visible items in autocomplete dropdown */\n\tsetAutocompleteMaxVisible?(maxVisible: number): void;\n\n\t/** Set placeholder text rendered while the editor is empty */\n\tsetPlaceholder?(text: string): void;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,10 +15,12 @@ export type { EditorComponent } from "./editor-component.ts";
|
|
|
15
15
|
export { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from "./fuzzy.ts";
|
|
16
16
|
export { getKeybindings, type Keybinding, type KeybindingConflict, type KeybindingDefinition, type KeybindingDefinitions, type Keybindings, type KeybindingsConfig, KeybindingsManager, setKeybindings, TUI_KEYBINDINGS, } from "./keybindings.ts";
|
|
17
17
|
export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, Key, type KeyEventType, type KeyId, matchesKey, parseKey, setKittyProtocolActive, } from "./keys.ts";
|
|
18
|
+
export { isRenderSettled, nextRenderGeneration, releaseRenderCache, renderGeneration, } from "./render-generation.ts";
|
|
18
19
|
export { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from "./stdin-buffer.ts";
|
|
19
20
|
export { ProcessTerminal, type Terminal } from "./terminal.ts";
|
|
20
21
|
export { allocateImageId, type CellDimensions, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, type ImageDimensions, type ImageProtocol, type ImageRenderOptions, imageFallback, renderImage, resetCapabilitiesCache, setCapabilities, setCellDimensions, type TerminalCapabilities, } from "./terminal-image.ts";
|
|
21
|
-
export type
|
|
22
|
+
export { type TerminalOutputStats, writeTerminalRaw } from "./terminal-output.ts";
|
|
22
23
|
export { type Component, Container, CURSOR_MARKER, type Focusable, isFocusable, type OverlayAnchor, type OverlayHandle, type OverlayMargin, type OverlayOptions, type OverlayUnfocusOptions, type SizeValue, TUI, } from "./tui.ts";
|
|
23
24
|
export { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.ts";
|
|
25
|
+
export { WindowedContainer, type WindowedRenderStats } from "./windowed-container.ts";
|
|
24
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,YAAY,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,KAAK,gBAAgB,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACrH,OAAO,EACN,KAAK,UAAU,EACf,UAAU,EACV,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,gCAAgC,GACrC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EACN,cAAc,EACd,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EACH,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.ts\";\n// Components\nexport { Box } from \"./components/box.ts\";\nexport { CancellableLoader } from \"./components/cancellable-loader.ts\";\nexport { Editor, type EditorOptions, type EditorTheme } from \"./components/editor.ts\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.ts\";\nexport { Input } from \"./components/input.ts\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.ts\";\nexport { type DefaultTextStyle, Markdown, type MarkdownOptions, type MarkdownTheme } from \"./components/markdown.ts\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.ts\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.ts\";\nexport { Spacer } from \"./components/spacer.ts\";\nexport { Text } from \"./components/text.ts\";\nexport { TruncatedText } from \"./components/truncated-text.ts\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.ts\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.ts\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.ts\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.ts\";\nexport {\n\tisRenderSettled,\n\tnextRenderGeneration,\n\treleaseRenderCache,\n\trenderGeneration,\n} from \"./render-generation.ts\";\n// Input buffering for batch splitting\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.ts\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.ts\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.ts\";\nexport { type TerminalOutputStats, writeTerminalRaw } from \"./terminal-output.ts\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\tTUI,\n} from \"./tui.ts\";\n// Utilities\nexport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.ts\";\nexport { WindowedContainer, type WindowedRenderStats } from \"./windowed-container.ts\";\n"]}
|
package/dist/index.js
CHANGED
|
@@ -20,13 +20,16 @@ export { fuzzyFilter, fuzzyMatch } from "./fuzzy.js";
|
|
|
20
20
|
export { getKeybindings, KeybindingsManager, setKeybindings, TUI_KEYBINDINGS, } from "./keybindings.js";
|
|
21
21
|
// Keyboard input handling
|
|
22
22
|
export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, Key, matchesKey, parseKey, setKittyProtocolActive, } from "./keys.js";
|
|
23
|
+
export { isRenderSettled, nextRenderGeneration, releaseRenderCache, renderGeneration, } from "./render-generation.js";
|
|
23
24
|
// Input buffering for batch splitting
|
|
24
25
|
export { StdinBuffer } from "./stdin-buffer.js";
|
|
25
26
|
// Terminal interface and implementations
|
|
26
27
|
export { ProcessTerminal } from "./terminal.js";
|
|
27
28
|
// Terminal image support
|
|
28
29
|
export { allocateImageId, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, imageFallback, renderImage, resetCapabilitiesCache, setCapabilities, setCellDimensions, } from "./terminal-image.js";
|
|
30
|
+
export { writeTerminalRaw } from "./terminal-output.js";
|
|
29
31
|
export { Container, CURSOR_MARKER, isFocusable, TUI, } from "./tui.js";
|
|
30
32
|
// Utilities
|
|
31
33
|
export { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
|
|
34
|
+
export { WindowedContainer } from "./windowed-container.js";
|
|
32
35
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,uBAAuB;AACvB,OAAO,EAIN,4BAA4B,GAE5B,MAAM,mBAAmB,CAAC;AAC3B,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAwC,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAA+B,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAyB,QAAQ,EAA4C,MAAM,0BAA0B,CAAC;AACrH,OAAO,EAEN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,YAAY,EAA0B,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,iBAAiB;AACjB,OAAO,EAAmB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,cAAc;AACd,OAAO,EACN,cAAc,EAOd,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,0BAA0B;AAC1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EAGH,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AACnB,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,yBAAyB;AACzB,OAAO,EACN,eAAe,EAEf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EAIT,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAEjB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,uBAAuB;AACvB,OAAO,EAIN,4BAA4B,GAE5B,MAAM,mBAAmB,CAAC;AAC3B,aAAa;AACb,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAwC,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAA+B,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAyB,QAAQ,EAA4C,MAAM,0BAA0B,CAAC;AACrH,OAAO,EAEN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,YAAY,EAA0B,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,iBAAiB;AACjB,OAAO,EAAmB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,cAAc;AACd,OAAO,EACN,cAAc,EAOd,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,0BAA0B;AAC1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EAGH,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,GAChB,MAAM,wBAAwB,CAAC;AAChC,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,yBAAyB;AACzB,OAAO,EACN,eAAe,EAEf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EAIT,aAAa,EACb,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA4B,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAEN,SAAS,EACT,aAAa,EAEb,WAAW,EAOX,GAAG,GACH,MAAM,UAAU,CAAC;AAClB,YAAY;AACZ,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAA4B,MAAM,yBAAyB,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.ts\";\n// Components\nexport { Box } from \"./components/box.ts\";\nexport { CancellableLoader } from \"./components/cancellable-loader.ts\";\nexport { Editor, type EditorOptions, type EditorTheme } from \"./components/editor.ts\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.ts\";\nexport { Input } from \"./components/input.ts\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.ts\";\nexport { type DefaultTextStyle, Markdown, type MarkdownOptions, type MarkdownTheme } from \"./components/markdown.ts\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.ts\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.ts\";\nexport { Spacer } from \"./components/spacer.ts\";\nexport { Text } from \"./components/text.ts\";\nexport { TruncatedText } from \"./components/truncated-text.ts\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.ts\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.ts\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.ts\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.ts\";\nexport {\n\tisRenderSettled,\n\tnextRenderGeneration,\n\treleaseRenderCache,\n\trenderGeneration,\n} from \"./render-generation.ts\";\n// Input buffering for batch splitting\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.ts\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.ts\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.ts\";\nexport { type TerminalOutputStats, writeTerminalRaw } from \"./terminal-output.ts\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\tTUI,\n} from \"./tui.ts\";\n// Utilities\nexport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.ts\";\nexport { WindowedContainer, type WindowedRenderStats } from \"./windowed-container.ts\";\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Reset SGR and close any open OSC 8 hyperlink at segment boundaries. */
|
|
2
|
+
export declare const SEGMENT_RESET = "\u001B[0m\u001B]8;;\u0007";
|
|
3
|
+
export declare function extractKittyImageIds(line: string): readonly number[];
|
|
4
|
+
/**
|
|
5
|
+
* Per-row memo for line resets (normalization + SEGMENT_RESET) and Kitty image IDs.
|
|
6
|
+
*
|
|
7
|
+
* A row reuses the previous frame's output when its raw text equals the previous
|
|
8
|
+
* raw row at the same index, or at the same distance from the end. The second
|
|
9
|
+
* candidate covers a block of rows inserted or removed above (an off-screen
|
|
10
|
+
* message growing by a line shifts every row below it), so one early change no
|
|
11
|
+
* longer re-normalizes the whole transcript. Output depends only on the raw
|
|
12
|
+
* text, so reuse on equal text is always exact. Unchanged component caches
|
|
13
|
+
* usually compare by reference, keeping the renderer's diff on the cheap path.
|
|
14
|
+
*/
|
|
15
|
+
export declare class LineResetMemo {
|
|
16
|
+
private raw;
|
|
17
|
+
private out;
|
|
18
|
+
/** Per-row Kitty IDs, or null when no row of the frame carries any (the common case). */
|
|
19
|
+
private ids;
|
|
20
|
+
/** Raw buffer from two frames ago, refilled instead of reallocated. */
|
|
21
|
+
private spareRaw;
|
|
22
|
+
/** Kitty image IDs present in the most recent `apply` result. */
|
|
23
|
+
kittyImageIds: Set<number>;
|
|
24
|
+
/** Normalizes `lines` in place (image lines untouched) and returns it. */
|
|
25
|
+
apply(lines: string[]): string[];
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=line-reset-memo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-reset-memo.d.ts","sourceRoot":"","sources":["../src/line-reset-memo.ts"],"names":[],"mappings":"AAGA,0EAA0E;AAC1E,eAAO,MAAM,aAAa,8BAAwB,CAAC;AAKnD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAkBpE;AAED;;;;;;;;;;GAUG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,GAAG,CAAgB;IAC3B,yFAAyF;IACzF,OAAO,CAAC,GAAG,CAAsC;IACjD,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAgB;IAChC,iEAAiE;IACjE,aAAa,cAAqB;IAElC,0EAA0E;IAC1E,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CA6C/B;CACD","sourcesContent":["import { isImageLine } from \"./terminal-image.ts\";\nimport { normalizeTerminalOutput } from \"./utils.ts\";\n\n/** Reset SGR and close any open OSC 8 hyperlink at segment boundaries. */\nexport const SEGMENT_RESET = \"\\x1b[0m\\x1b]8;;\\x07\";\n\nconst KITTY_SEQUENCE_PREFIX = \"\\x1b_G\";\nconst NO_IDS: readonly number[] = [];\n\nexport function extractKittyImageIds(line: string): readonly number[] {\n\tconst sequenceStart = line.indexOf(KITTY_SEQUENCE_PREFIX);\n\tif (sequenceStart === -1) return NO_IDS;\n\n\tconst paramsStart = sequenceStart + KITTY_SEQUENCE_PREFIX.length;\n\tconst paramsEnd = line.indexOf(\";\", paramsStart);\n\tif (paramsEnd === -1) return NO_IDS;\n\n\tconst params = line.slice(paramsStart, paramsEnd);\n\tfor (const param of params.split(\",\")) {\n\t\tconst [key, value] = param.split(\"=\", 2);\n\t\tif (key !== \"i\" || value === undefined) continue;\n\t\tconst id = Number(value);\n\t\tif (Number.isInteger(id) && id > 0 && id <= 0xffffffff) {\n\t\t\treturn [id];\n\t\t}\n\t}\n\treturn NO_IDS;\n}\n\n/**\n * Per-row memo for line resets (normalization + SEGMENT_RESET) and Kitty image IDs.\n *\n * A row reuses the previous frame's output when its raw text equals the previous\n * raw row at the same index, or at the same distance from the end. The second\n * candidate covers a block of rows inserted or removed above (an off-screen\n * message growing by a line shifts every row below it), so one early change no\n * longer re-normalizes the whole transcript. Output depends only on the raw\n * text, so reuse on equal text is always exact. Unchanged component caches\n * usually compare by reference, keeping the renderer's diff on the cheap path.\n */\nexport class LineResetMemo {\n\tprivate raw: string[] = [];\n\tprivate out: string[] = [];\n\t/** Per-row Kitty IDs, or null when no row of the frame carries any (the common case). */\n\tprivate ids: (readonly number[])[] | null = null;\n\t/** Raw buffer from two frames ago, refilled instead of reallocated. */\n\tprivate spareRaw: string[] = [];\n\t/** Kitty image IDs present in the most recent `apply` result. */\n\tkittyImageIds = new Set<number>();\n\n\t/** Normalizes `lines` in place (image lines untouched) and returns it. */\n\tapply(lines: string[]): string[] {\n\t\tconst count = lines.length;\n\t\tconst raw = this.spareRaw;\n\t\traw.length = count;\n\t\tconst previousRaw = this.raw;\n\t\tconst previousOut = this.out;\n\t\tconst previousIds = this.ids;\n\t\tconst previousLength = previousRaw.length;\n\t\tconst shift = previousLength - count;\n\t\tlet ids: (readonly number[])[] | null = null;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst line = lines[i];\n\t\t\traw[i] = line;\n\t\t\tlet from = -1;\n\t\t\tif (i < previousLength && previousRaw[i] === line) {\n\t\t\t\tfrom = i;\n\t\t\t} else if (shift !== 0) {\n\t\t\t\tconst aligned = i + shift;\n\t\t\t\tif (aligned >= 0 && aligned < previousLength && previousRaw[aligned] === line) from = aligned;\n\t\t\t}\n\t\t\tlet rowIds: readonly number[];\n\t\t\tif (from !== -1) {\n\t\t\t\tlines[i] = previousOut[from];\n\t\t\t\tif (previousIds === null) continue;\n\t\t\t\trowIds = previousIds[from];\n\t\t\t} else {\n\t\t\t\tconst output = isImageLine(line) ? line : normalizeTerminalOutput(line) + SEGMENT_RESET;\n\t\t\t\tlines[i] = output;\n\t\t\t\trowIds = extractKittyImageIds(output);\n\t\t\t}\n\t\t\tif (rowIds.length > 0) {\n\t\t\t\tids ??= new Array<readonly number[]>(count).fill(NO_IDS);\n\t\t\t\tids[i] = rowIds;\n\t\t\t}\n\t\t}\n\t\tconst kittyImageIds = new Set<number>();\n\t\tif (ids !== null) {\n\t\t\tfor (const rowIds of ids) for (const id of rowIds) kittyImageIds.add(id);\n\t\t}\n\t\tthis.spareRaw = previousRaw;\n\t\tthis.raw = raw;\n\t\tthis.out = lines;\n\t\tthis.ids = ids;\n\t\tthis.kittyImageIds = kittyImageIds;\n\t\treturn lines;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { isImageLine } from "./terminal-image.js";
|
|
2
|
+
import { normalizeTerminalOutput } from "./utils.js";
|
|
3
|
+
/** Reset SGR and close any open OSC 8 hyperlink at segment boundaries. */
|
|
4
|
+
export const SEGMENT_RESET = "\x1b[0m\x1b]8;;\x07";
|
|
5
|
+
const KITTY_SEQUENCE_PREFIX = "\x1b_G";
|
|
6
|
+
const NO_IDS = [];
|
|
7
|
+
export function extractKittyImageIds(line) {
|
|
8
|
+
const sequenceStart = line.indexOf(KITTY_SEQUENCE_PREFIX);
|
|
9
|
+
if (sequenceStart === -1)
|
|
10
|
+
return NO_IDS;
|
|
11
|
+
const paramsStart = sequenceStart + KITTY_SEQUENCE_PREFIX.length;
|
|
12
|
+
const paramsEnd = line.indexOf(";", paramsStart);
|
|
13
|
+
if (paramsEnd === -1)
|
|
14
|
+
return NO_IDS;
|
|
15
|
+
const params = line.slice(paramsStart, paramsEnd);
|
|
16
|
+
for (const param of params.split(",")) {
|
|
17
|
+
const [key, value] = param.split("=", 2);
|
|
18
|
+
if (key !== "i" || value === undefined)
|
|
19
|
+
continue;
|
|
20
|
+
const id = Number(value);
|
|
21
|
+
if (Number.isInteger(id) && id > 0 && id <= 0xffffffff) {
|
|
22
|
+
return [id];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return NO_IDS;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Per-row memo for line resets (normalization + SEGMENT_RESET) and Kitty image IDs.
|
|
29
|
+
*
|
|
30
|
+
* A row reuses the previous frame's output when its raw text equals the previous
|
|
31
|
+
* raw row at the same index, or at the same distance from the end. The second
|
|
32
|
+
* candidate covers a block of rows inserted or removed above (an off-screen
|
|
33
|
+
* message growing by a line shifts every row below it), so one early change no
|
|
34
|
+
* longer re-normalizes the whole transcript. Output depends only on the raw
|
|
35
|
+
* text, so reuse on equal text is always exact. Unchanged component caches
|
|
36
|
+
* usually compare by reference, keeping the renderer's diff on the cheap path.
|
|
37
|
+
*/
|
|
38
|
+
export class LineResetMemo {
|
|
39
|
+
raw = [];
|
|
40
|
+
out = [];
|
|
41
|
+
/** Per-row Kitty IDs, or null when no row of the frame carries any (the common case). */
|
|
42
|
+
ids = null;
|
|
43
|
+
/** Raw buffer from two frames ago, refilled instead of reallocated. */
|
|
44
|
+
spareRaw = [];
|
|
45
|
+
/** Kitty image IDs present in the most recent `apply` result. */
|
|
46
|
+
kittyImageIds = new Set();
|
|
47
|
+
/** Normalizes `lines` in place (image lines untouched) and returns it. */
|
|
48
|
+
apply(lines) {
|
|
49
|
+
const count = lines.length;
|
|
50
|
+
const raw = this.spareRaw;
|
|
51
|
+
raw.length = count;
|
|
52
|
+
const previousRaw = this.raw;
|
|
53
|
+
const previousOut = this.out;
|
|
54
|
+
const previousIds = this.ids;
|
|
55
|
+
const previousLength = previousRaw.length;
|
|
56
|
+
const shift = previousLength - count;
|
|
57
|
+
let ids = null;
|
|
58
|
+
for (let i = 0; i < count; i++) {
|
|
59
|
+
const line = lines[i];
|
|
60
|
+
raw[i] = line;
|
|
61
|
+
let from = -1;
|
|
62
|
+
if (i < previousLength && previousRaw[i] === line) {
|
|
63
|
+
from = i;
|
|
64
|
+
}
|
|
65
|
+
else if (shift !== 0) {
|
|
66
|
+
const aligned = i + shift;
|
|
67
|
+
if (aligned >= 0 && aligned < previousLength && previousRaw[aligned] === line)
|
|
68
|
+
from = aligned;
|
|
69
|
+
}
|
|
70
|
+
let rowIds;
|
|
71
|
+
if (from !== -1) {
|
|
72
|
+
lines[i] = previousOut[from];
|
|
73
|
+
if (previousIds === null)
|
|
74
|
+
continue;
|
|
75
|
+
rowIds = previousIds[from];
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const output = isImageLine(line) ? line : normalizeTerminalOutput(line) + SEGMENT_RESET;
|
|
79
|
+
lines[i] = output;
|
|
80
|
+
rowIds = extractKittyImageIds(output);
|
|
81
|
+
}
|
|
82
|
+
if (rowIds.length > 0) {
|
|
83
|
+
ids ??= new Array(count).fill(NO_IDS);
|
|
84
|
+
ids[i] = rowIds;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const kittyImageIds = new Set();
|
|
88
|
+
if (ids !== null) {
|
|
89
|
+
for (const rowIds of ids)
|
|
90
|
+
for (const id of rowIds)
|
|
91
|
+
kittyImageIds.add(id);
|
|
92
|
+
}
|
|
93
|
+
this.spareRaw = previousRaw;
|
|
94
|
+
this.raw = raw;
|
|
95
|
+
this.out = lines;
|
|
96
|
+
this.ids = ids;
|
|
97
|
+
this.kittyImageIds = kittyImageIds;
|
|
98
|
+
return lines;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=line-reset-memo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"line-reset-memo.js","sourceRoot":"","sources":["../src/line-reset-memo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAErD,0EAA0E;AAC1E,MAAM,CAAC,MAAM,aAAa,GAAG,qBAAqB,CAAC;AAEnD,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AACvC,MAAM,MAAM,GAAsB,EAAE,CAAC;AAErC,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAqB;IACrE,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC1D,IAAI,aAAa,KAAK,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAExC,MAAM,WAAW,GAAG,aAAa,GAAG,qBAAqB,CAAC,MAAM,CAAC;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACjD,IAAI,SAAS,KAAK,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAEpC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzC,IAAI,GAAG,KAAK,GAAG,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QACjD,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACzB,IAAI,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,UAAU,EAAE,CAAC;YACxD,OAAO,CAAC,EAAE,CAAC,CAAC;QACb,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AAAA,CACd;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,aAAa;IACjB,GAAG,GAAa,EAAE,CAAC;IACnB,GAAG,GAAa,EAAE,CAAC;IAC3B,yFAAyF;IACjF,GAAG,GAAiC,IAAI,CAAC;IACjD,uEAAuE;IAC/D,QAAQ,GAAa,EAAE,CAAC;IAChC,iEAAiE;IACjE,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,0EAA0E;IAC1E,KAAK,CAAC,KAAe,EAAY;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC1B,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC;QACnB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC;QAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC;QAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC;QAC7B,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,CAAC;QAC1C,MAAM,KAAK,GAAG,cAAc,GAAG,KAAK,CAAC;QACrC,IAAI,GAAG,GAAiC,IAAI,CAAC;QAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YACd,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;YACd,IAAI,CAAC,GAAG,cAAc,IAAI,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACnD,IAAI,GAAG,CAAC,CAAC;YACV,CAAC;iBAAM,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBACxB,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC;gBAC1B,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,GAAG,cAAc,IAAI,WAAW,CAAC,OAAO,CAAC,KAAK,IAAI;oBAAE,IAAI,GAAG,OAAO,CAAC;YAC/F,CAAC;YACD,IAAI,MAAyB,CAAC;YAC9B,IAAI,IAAI,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,KAAK,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;gBAC7B,IAAI,WAAW,KAAK,IAAI;oBAAE,SAAS;gBACnC,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACP,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC;gBACxF,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;gBAClB,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;YACvC,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,GAAG,KAAK,IAAI,KAAK,CAAoB,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACzD,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC;YACjB,CAAC;QACF,CAAC;QACD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;QACxC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YAClB,KAAK,MAAM,MAAM,IAAI,GAAG;gBAAE,KAAK,MAAM,EAAE,IAAI,MAAM;oBAAE,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC;QAC5B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,OAAO,KAAK,CAAC;IAAA,CACb;CACD","sourcesContent":["import { isImageLine } from \"./terminal-image.ts\";\nimport { normalizeTerminalOutput } from \"./utils.ts\";\n\n/** Reset SGR and close any open OSC 8 hyperlink at segment boundaries. */\nexport const SEGMENT_RESET = \"\\x1b[0m\\x1b]8;;\\x07\";\n\nconst KITTY_SEQUENCE_PREFIX = \"\\x1b_G\";\nconst NO_IDS: readonly number[] = [];\n\nexport function extractKittyImageIds(line: string): readonly number[] {\n\tconst sequenceStart = line.indexOf(KITTY_SEQUENCE_PREFIX);\n\tif (sequenceStart === -1) return NO_IDS;\n\n\tconst paramsStart = sequenceStart + KITTY_SEQUENCE_PREFIX.length;\n\tconst paramsEnd = line.indexOf(\";\", paramsStart);\n\tif (paramsEnd === -1) return NO_IDS;\n\n\tconst params = line.slice(paramsStart, paramsEnd);\n\tfor (const param of params.split(\",\")) {\n\t\tconst [key, value] = param.split(\"=\", 2);\n\t\tif (key !== \"i\" || value === undefined) continue;\n\t\tconst id = Number(value);\n\t\tif (Number.isInteger(id) && id > 0 && id <= 0xffffffff) {\n\t\t\treturn [id];\n\t\t}\n\t}\n\treturn NO_IDS;\n}\n\n/**\n * Per-row memo for line resets (normalization + SEGMENT_RESET) and Kitty image IDs.\n *\n * A row reuses the previous frame's output when its raw text equals the previous\n * raw row at the same index, or at the same distance from the end. The second\n * candidate covers a block of rows inserted or removed above (an off-screen\n * message growing by a line shifts every row below it), so one early change no\n * longer re-normalizes the whole transcript. Output depends only on the raw\n * text, so reuse on equal text is always exact. Unchanged component caches\n * usually compare by reference, keeping the renderer's diff on the cheap path.\n */\nexport class LineResetMemo {\n\tprivate raw: string[] = [];\n\tprivate out: string[] = [];\n\t/** Per-row Kitty IDs, or null when no row of the frame carries any (the common case). */\n\tprivate ids: (readonly number[])[] | null = null;\n\t/** Raw buffer from two frames ago, refilled instead of reallocated. */\n\tprivate spareRaw: string[] = [];\n\t/** Kitty image IDs present in the most recent `apply` result. */\n\tkittyImageIds = new Set<number>();\n\n\t/** Normalizes `lines` in place (image lines untouched) and returns it. */\n\tapply(lines: string[]): string[] {\n\t\tconst count = lines.length;\n\t\tconst raw = this.spareRaw;\n\t\traw.length = count;\n\t\tconst previousRaw = this.raw;\n\t\tconst previousOut = this.out;\n\t\tconst previousIds = this.ids;\n\t\tconst previousLength = previousRaw.length;\n\t\tconst shift = previousLength - count;\n\t\tlet ids: (readonly number[])[] | null = null;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst line = lines[i];\n\t\t\traw[i] = line;\n\t\t\tlet from = -1;\n\t\t\tif (i < previousLength && previousRaw[i] === line) {\n\t\t\t\tfrom = i;\n\t\t\t} else if (shift !== 0) {\n\t\t\t\tconst aligned = i + shift;\n\t\t\t\tif (aligned >= 0 && aligned < previousLength && previousRaw[aligned] === line) from = aligned;\n\t\t\t}\n\t\t\tlet rowIds: readonly number[];\n\t\t\tif (from !== -1) {\n\t\t\t\tlines[i] = previousOut[from];\n\t\t\t\tif (previousIds === null) continue;\n\t\t\t\trowIds = previousIds[from];\n\t\t\t} else {\n\t\t\t\tconst output = isImageLine(line) ? line : normalizeTerminalOutput(line) + SEGMENT_RESET;\n\t\t\t\tlines[i] = output;\n\t\t\t\trowIds = extractKittyImageIds(output);\n\t\t\t}\n\t\t\tif (rowIds.length > 0) {\n\t\t\t\tids ??= new Array<readonly number[]>(count).fill(NO_IDS);\n\t\t\t\tids[i] = rowIds;\n\t\t\t}\n\t\t}\n\t\tconst kittyImageIds = new Set<number>();\n\t\tif (ids !== null) {\n\t\t\tfor (const rowIds of ids) for (const id of rowIds) kittyImageIds.add(id);\n\t\t}\n\t\tthis.spareRaw = previousRaw;\n\t\tthis.raw = raw;\n\t\tthis.out = lines;\n\t\tthis.ids = ids;\n\t\tthis.kittyImageIds = kittyImageIds;\n\t\treturn lines;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural stand-in for tui.ts `Component` (importing it, even as a type,
|
|
3
|
+
* would put this module in an import cycle with tui.ts).
|
|
4
|
+
*/
|
|
5
|
+
interface Component {
|
|
6
|
+
render(width: number): string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function nextRenderGeneration(): number;
|
|
9
|
+
/** Missing hook ⇒ settled (plain components keep freeze-eligible behavior). */
|
|
10
|
+
export declare function isRenderSettled(component: Component): boolean;
|
|
11
|
+
/** Missing hook ⇒ 0 (component is treated as immutable once rendered). */
|
|
12
|
+
export declare function renderGeneration(component: Component): number;
|
|
13
|
+
/** Max generation over `own` and every child (children without hooks count as 0). */
|
|
14
|
+
export declare function maxChildGeneration(own: number, children: readonly Component[]): number;
|
|
15
|
+
/**
|
|
16
|
+
* Drop render caches of a frozen (off-screen) component without changing its
|
|
17
|
+
* render generation. Opt-in only: a component releases exactly what its own
|
|
18
|
+
* `releaseRenderCache()` drops (Container and Box forward to their children).
|
|
19
|
+
* Components without the hook are left untouched; fields are never cleared by
|
|
20
|
+
* name, because a third-party field called `cache` may not be a render cache.
|
|
21
|
+
*/
|
|
22
|
+
export declare function releaseRenderCache(component: Component): void;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=render-generation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-generation.d.ts","sourceRoot":"","sources":["../src/render-generation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,UAAU,SAAS;IAClB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAcD,wBAAgB,oBAAoB,IAAI,MAAM,CAG7C;AAOD,iFAA+E;AAC/E,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAG7D;AAED,4EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAG7D;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,SAAS,EAAE,GAAG,MAAM,CAOtF;AAID;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAG7D","sourcesContent":["/**\n * Structural stand-in for tui.ts `Component` (importing it, even as a type,\n * would put this module in an import cycle with tui.ts).\n */\ninterface Component {\n\trender(width: number): string[];\n}\n\n/**\n * Process-wide render generation counter.\n *\n * Mutable TUI primitives stamp `nextRenderGeneration()` on every visible\n * change, and containers report the max stamp of themselves and their\n * children. Because stamps only ever increase, any change anywhere in a\n * subtree strictly raises that subtree's generation — removing a child, then\n * adding another, can never land back on a previously observed value. Off-\n * screen windowing compares these numbers instead of object identity.\n */\nlet renderGenerationCounter = 0;\n\nexport function nextRenderGeneration(): number {\n\trenderGenerationCounter += 1;\n\treturn renderGenerationCounter;\n}\n\ntype LifecycleNode = Component & {\n\tisRenderSettled?: () => boolean;\n\tgetRenderGeneration?: () => number;\n};\n\n/** Missing hook ⇒ settled (plain components keep freeze-eligible behavior). */\nexport function isRenderSettled(component: Component): boolean {\n\tconst node = component as LifecycleNode;\n\treturn typeof node.isRenderSettled === \"function\" ? node.isRenderSettled() : true;\n}\n\n/** Missing hook ⇒ 0 (component is treated as immutable once rendered). */\nexport function renderGeneration(component: Component): number {\n\tconst node = component as LifecycleNode;\n\treturn typeof node.getRenderGeneration === \"function\" ? node.getRenderGeneration() : 0;\n}\n\n/** Max generation over `own` and every child (children without hooks count as 0). */\nexport function maxChildGeneration(own: number, children: readonly Component[]): number {\n\tlet max = own;\n\tfor (const child of children) {\n\t\tconst generation = renderGeneration(child);\n\t\tif (generation > max) max = generation;\n\t}\n\treturn max;\n}\n\ntype CacheNode = Component & { releaseRenderCache?: () => void };\n\n/**\n * Drop render caches of a frozen (off-screen) component without changing its\n * render generation. Opt-in only: a component releases exactly what its own\n * `releaseRenderCache()` drops (Container and Box forward to their children).\n * Components without the hook are left untouched; fields are never cleared by\n * name, because a third-party field called `cache` may not be a render cache.\n */\nexport function releaseRenderCache(component: Component): void {\n\tconst node = component as CacheNode;\n\tif (typeof node.releaseRenderCache === \"function\") node.releaseRenderCache();\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide render generation counter.
|
|
3
|
+
*
|
|
4
|
+
* Mutable TUI primitives stamp `nextRenderGeneration()` on every visible
|
|
5
|
+
* change, and containers report the max stamp of themselves and their
|
|
6
|
+
* children. Because stamps only ever increase, any change anywhere in a
|
|
7
|
+
* subtree strictly raises that subtree's generation — removing a child, then
|
|
8
|
+
* adding another, can never land back on a previously observed value. Off-
|
|
9
|
+
* screen windowing compares these numbers instead of object identity.
|
|
10
|
+
*/
|
|
11
|
+
let renderGenerationCounter = 0;
|
|
12
|
+
export function nextRenderGeneration() {
|
|
13
|
+
renderGenerationCounter += 1;
|
|
14
|
+
return renderGenerationCounter;
|
|
15
|
+
}
|
|
16
|
+
/** Missing hook ⇒ settled (plain components keep freeze-eligible behavior). */
|
|
17
|
+
export function isRenderSettled(component) {
|
|
18
|
+
const node = component;
|
|
19
|
+
return typeof node.isRenderSettled === "function" ? node.isRenderSettled() : true;
|
|
20
|
+
}
|
|
21
|
+
/** Missing hook ⇒ 0 (component is treated as immutable once rendered). */
|
|
22
|
+
export function renderGeneration(component) {
|
|
23
|
+
const node = component;
|
|
24
|
+
return typeof node.getRenderGeneration === "function" ? node.getRenderGeneration() : 0;
|
|
25
|
+
}
|
|
26
|
+
/** Max generation over `own` and every child (children without hooks count as 0). */
|
|
27
|
+
export function maxChildGeneration(own, children) {
|
|
28
|
+
let max = own;
|
|
29
|
+
for (const child of children) {
|
|
30
|
+
const generation = renderGeneration(child);
|
|
31
|
+
if (generation > max)
|
|
32
|
+
max = generation;
|
|
33
|
+
}
|
|
34
|
+
return max;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Drop render caches of a frozen (off-screen) component without changing its
|
|
38
|
+
* render generation. Opt-in only: a component releases exactly what its own
|
|
39
|
+
* `releaseRenderCache()` drops (Container and Box forward to their children).
|
|
40
|
+
* Components without the hook are left untouched; fields are never cleared by
|
|
41
|
+
* name, because a third-party field called `cache` may not be a render cache.
|
|
42
|
+
*/
|
|
43
|
+
export function releaseRenderCache(component) {
|
|
44
|
+
const node = component;
|
|
45
|
+
if (typeof node.releaseRenderCache === "function")
|
|
46
|
+
node.releaseRenderCache();
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=render-generation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-generation.js","sourceRoot":"","sources":["../src/render-generation.ts"],"names":[],"mappings":"AAQA;;;;;;;;;GASG;AACH,IAAI,uBAAuB,GAAG,CAAC,CAAC;AAEhC,MAAM,UAAU,oBAAoB,GAAW;IAC9C,uBAAuB,IAAI,CAAC,CAAC;IAC7B,OAAO,uBAAuB,CAAC;AAAA,CAC/B;AAOD,iFAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,SAAoB,EAAW;IAC9D,MAAM,IAAI,GAAG,SAA0B,CAAC;IACxC,OAAO,OAAO,IAAI,CAAC,eAAe,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,CAClF;AAED,4EAA0E;AAC1E,MAAM,UAAU,gBAAgB,CAAC,SAAoB,EAAU;IAC9D,MAAM,IAAI,GAAG,SAA0B,CAAC;IACxC,OAAO,OAAO,IAAI,CAAC,mBAAmB,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAAA,CACvF;AAED,qFAAqF;AACrF,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,QAA8B,EAAU;IACvF,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,UAAU,GAAG,GAAG;YAAE,GAAG,GAAG,UAAU,CAAC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC;AAAA,CACX;AAID;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAoB,EAAQ;IAC9D,MAAM,IAAI,GAAG,SAAsB,CAAC;IACpC,IAAI,OAAO,IAAI,CAAC,kBAAkB,KAAK,UAAU;QAAE,IAAI,CAAC,kBAAkB,EAAE,CAAC;AAAA,CAC7E","sourcesContent":["/**\n * Structural stand-in for tui.ts `Component` (importing it, even as a type,\n * would put this module in an import cycle with tui.ts).\n */\ninterface Component {\n\trender(width: number): string[];\n}\n\n/**\n * Process-wide render generation counter.\n *\n * Mutable TUI primitives stamp `nextRenderGeneration()` on every visible\n * change, and containers report the max stamp of themselves and their\n * children. Because stamps only ever increase, any change anywhere in a\n * subtree strictly raises that subtree's generation — removing a child, then\n * adding another, can never land back on a previously observed value. Off-\n * screen windowing compares these numbers instead of object identity.\n */\nlet renderGenerationCounter = 0;\n\nexport function nextRenderGeneration(): number {\n\trenderGenerationCounter += 1;\n\treturn renderGenerationCounter;\n}\n\ntype LifecycleNode = Component & {\n\tisRenderSettled?: () => boolean;\n\tgetRenderGeneration?: () => number;\n};\n\n/** Missing hook ⇒ settled (plain components keep freeze-eligible behavior). */\nexport function isRenderSettled(component: Component): boolean {\n\tconst node = component as LifecycleNode;\n\treturn typeof node.isRenderSettled === \"function\" ? node.isRenderSettled() : true;\n}\n\n/** Missing hook ⇒ 0 (component is treated as immutable once rendered). */\nexport function renderGeneration(component: Component): number {\n\tconst node = component as LifecycleNode;\n\treturn typeof node.getRenderGeneration === \"function\" ? node.getRenderGeneration() : 0;\n}\n\n/** Max generation over `own` and every child (children without hooks count as 0). */\nexport function maxChildGeneration(own: number, children: readonly Component[]): number {\n\tlet max = own;\n\tfor (const child of children) {\n\t\tconst generation = renderGeneration(child);\n\t\tif (generation > max) max = generation;\n\t}\n\treturn max;\n}\n\ntype CacheNode = Component & { releaseRenderCache?: () => void };\n\n/**\n * Drop render caches of a frozen (off-screen) component without changing its\n * render generation. Opt-in only: a component releases exactly what its own\n * `releaseRenderCache()` drops (Container and Box forward to their children).\n * Components without the hook are left untouched; fields are never cleared by\n * name, because a third-party field called `cache` may not be a render cache.\n */\nexport function releaseRenderCache(component: Component): void {\n\tconst node = component as CacheNode;\n\tif (typeof node.releaseRenderCache === \"function\") node.releaseRenderCache();\n}\n"]}
|
|
@@ -10,6 +10,13 @@ export interface TerminalOutputStats {
|
|
|
10
10
|
readonly writableLength: number;
|
|
11
11
|
readonly backpressured: boolean;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Raw terminal bytes written from outside the render path (completion BEL,
|
|
15
|
+
* OSC 52 clipboard). While a TUI is writing they go through its
|
|
16
|
+
* TerminalOutput, so output stats and the OMK_TUI_RESIZE_LOG byte offsets
|
|
17
|
+
* count them; otherwise straight to stdout. May throw like stream.write.
|
|
18
|
+
*/
|
|
19
|
+
export declare function writeTerminalRaw(data: string): void;
|
|
13
20
|
/** Metadata-only observation. No paint queue, retries, or interception of error handling. */
|
|
14
21
|
export declare class TerminalOutput {
|
|
15
22
|
private readonly stream;
|
|
@@ -18,7 +25,19 @@ export declare class TerminalOutput {
|
|
|
18
25
|
private readonly counters;
|
|
19
26
|
private readonly onDrain;
|
|
20
27
|
private readonly onError;
|
|
21
|
-
|
|
28
|
+
private readonly resizeLog;
|
|
29
|
+
constructor(stream: Writable, log?: string, resizeLog?: string);
|
|
30
|
+
private readonly finalLine;
|
|
31
|
+
private owesFinal;
|
|
32
|
+
/**
|
|
33
|
+
* Wraps a resize handler so each resize first logs the bytes written so far
|
|
34
|
+
* (OMK_TUI_RESIZE_LOG, see terminal-resize-log.ts). Returns the handler
|
|
35
|
+
* itself when the log is off, so an unset variable costs nothing per event.
|
|
36
|
+
*/
|
|
37
|
+
withResizeLog(onResize: () => void, size: () => {
|
|
38
|
+
cols: number;
|
|
39
|
+
rows: number;
|
|
40
|
+
}): () => void;
|
|
22
41
|
write(data: string, log?: boolean): void;
|
|
23
42
|
snapshot(): TerminalOutputStats;
|
|
24
43
|
stop(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-output.d.ts","sourceRoot":"","sources":["../src/terminal-output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"terminal-output.d.ts","sourceRoot":"","sources":["../src/terminal-output.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CAChC;AAmBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGnD;AAUD,6FAA6F;AAC7F,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAOvB;IACF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAEtB;IACF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAEtB;IAEF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;IAE1D,YAAY,MAAM,EAAE,QAAQ,EAAE,GAAG,SAAK,EAAE,SAAS,SAAuC,EAKvF;IAED,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6D;IAEvF,OAAO,CAAC,SAAS;IASjB;;;;OAIG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,IAAI,EAAE,MAAM;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,IAAI,CAU1F;IAED,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAQ,GAAG,IAAI,CAyBrC;IAED,QAAQ,IAAI,mBAAmB,CAM9B;IAED,IAAI,IAAI,IAAI,CAOX;CACD","sourcesContent":["import { errorMonitor } from \"node:events\";\nimport { appendFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Writable } from \"node:stream\";\nimport { TerminalResizeLog } from \"./terminal-resize-log.ts\";\n\nexport interface TerminalOutputStats {\n\treadonly writeCalls: number;\n\t/** Bytes offered to write, not a claim that the terminal displayed them. */\n\treadonly submittedBytes: number;\n\treadonly writeFalseCount: number;\n\treadonly drainCount: number;\n\treadonly errorCount: number;\n\treadonly peakWritableLength: number;\n\treadonly writableLength: number;\n\treadonly backpressured: boolean;\n}\n\nfunction writeLogPath(value: string): string {\n\tif (!value) return \"\";\n\ttry {\n\t\tif (statSync(value).isDirectory()) {\n\t\t\tconst now = new Date();\n\t\t\tconst ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, \"0\")}-${String(now.getDate()).padStart(2, \"0\")}_${String(now.getHours()).padStart(2, \"0\")}-${String(now.getMinutes()).padStart(2, \"0\")}-${String(now.getSeconds()).padStart(2, \"0\")}`;\n\t\t\treturn join(value, `tui-${ts}-${process.pid}.log`);\n\t\t}\n\t} catch {\n\t\t// A non-existing path is a filename, matching the existing debug-log contract.\n\t}\n\treturn value;\n}\n\n/** The TerminalOutput of the running TUI: set on its first write, cleared by stop. */\nlet activeOutput: TerminalOutput | undefined;\n\n/**\n * Raw terminal bytes written from outside the render path (completion BEL,\n * OSC 52 clipboard). While a TUI is writing they go through its\n * TerminalOutput, so output stats and the OMK_TUI_RESIZE_LOG byte offsets\n * count them; otherwise straight to stdout. May throw like stream.write.\n */\nexport function writeTerminalRaw(data: string): void {\n\tif (activeOutput) activeOutput.write(data);\n\telse process.stdout.write(data);\n}\n\n/**\n * Final resize-log lines still owed at process exit, for exits that skip stop\n * (a dead tty's EIO goes straight to process.exit). One exit listener serves\n * every TerminalOutput; stop removes its own entry, the next write re-adds it.\n */\nconst finalOnExit = new Set<() => void>();\nlet exitHooked = false;\n\n/** Metadata-only observation. No paint queue, retries, or interception of error handling. */\nexport class TerminalOutput {\n\tprivate readonly stream: Writable;\n\tprivate readonly logPath: string;\n\tprivate observing = false;\n\tprivate readonly counters = {\n\t\twriteCalls: 0,\n\t\tsubmittedBytes: 0,\n\t\twriteFalseCount: 0,\n\t\tdrainCount: 0,\n\t\terrorCount: 0,\n\t\tpeakWritableLength: 0,\n\t};\n\tprivate readonly onDrain = () => {\n\t\tthis.counters.drainCount++;\n\t};\n\tprivate readonly onError = () => {\n\t\tthis.counters.errorCount++;\n\t};\n\n\tprivate readonly resizeLog: TerminalResizeLog | undefined;\n\n\tconstructor(stream: Writable, log = \"\", resizeLog = process.env.OMK_TUI_RESIZE_LOG || \"\") {\n\t\tthis.stream = stream;\n\t\tthis.logPath = writeLogPath(log);\n\t\tthis.resizeLog = resizeLog ? new TerminalResizeLog(resizeLog) : undefined;\n\t\tif (this.resizeLog) this.owesFinal();\n\t}\n\n\tprivate readonly finalLine = () => this.resizeLog?.final(this.counters.submittedBytes);\n\n\tprivate owesFinal(): void {\n\t\tfinalOnExit.add(this.finalLine);\n\t\tif (exitHooked) return;\n\t\texitHooked = true;\n\t\tprocess.once(\"exit\", () => {\n\t\t\tfor (const finalLine of finalOnExit) finalLine();\n\t\t});\n\t}\n\n\t/**\n\t * Wraps a resize handler so each resize first logs the bytes written so far\n\t * (OMK_TUI_RESIZE_LOG, see terminal-resize-log.ts). Returns the handler\n\t * itself when the log is off, so an unset variable costs nothing per event.\n\t */\n\twithResizeLog(onResize: () => void, size: () => { cols: number; rows: number }): () => void {\n\t\tconst log = this.resizeLog;\n\t\tif (!log) return onResize;\n\t\tlet prev = size();\n\t\treturn () => {\n\t\t\tconst next = size();\n\t\t\tlog.resize(this.counters.submittedBytes, next.cols, next.rows, prev.cols, prev.rows);\n\t\t\tprev = next;\n\t\t\tonResize();\n\t\t};\n\t}\n\n\twrite(data: string, log = false): void {\n\t\tif (!this.observing) {\n\t\t\tthis.stream.on(\"drain\", this.onDrain);\n\t\t\tthis.stream.on(errorMonitor, this.onError);\n\t\t\tthis.observing = true;\n\t\t\tactiveOutput = this;\n\t\t\tif (this.resizeLog) this.owesFinal();\n\t\t}\n\t\tthis.counters.writeCalls++;\n\t\tthis.counters.submittedBytes += Buffer.byteLength(data, \"utf8\");\n\t\ttry {\n\t\t\t// false still accepts the write. Never resend it.\n\t\t\tif (!this.stream.write(data)) this.counters.writeFalseCount++;\n\t\t} catch (error) {\n\t\t\tthis.counters.errorCount++;\n\t\t\tthrow error;\n\t\t}\n\t\tthis.counters.peakWritableLength = Math.max(this.counters.peakWritableLength, this.stream.writableLength);\n\t\tif (log && this.logPath) {\n\t\t\ttry {\n\t\t\t\tappendFileSync(this.logPath, data, { encoding: \"utf8\" });\n\t\t\t} catch {\n\t\t\t\t/* Explicit raw debug logging remains best-effort. */\n\t\t\t}\n\t\t}\n\t}\n\n\tsnapshot(): TerminalOutputStats {\n\t\treturn {\n\t\t\t...this.counters,\n\t\t\twritableLength: this.stream.writableLength,\n\t\t\tbackpressured: this.stream.writableNeedDrain,\n\t\t};\n\t}\n\n\tstop(): void {\n\t\tthis.finalLine();\n\t\tfinalOnExit.delete(this.finalLine);\n\t\tthis.stream.off(\"drain\", this.onDrain);\n\t\tthis.stream.off(errorMonitor, this.onError);\n\t\tthis.observing = false;\n\t\tif (activeOutput === this) activeOutput = undefined;\n\t}\n}\n"]}
|