@averyyy/pi-tui 0.84.1-piclient.3 → 0.84.2-piclient.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -121,7 +121,7 @@ if (isViewportTUI(tui)) {
121
121
  }
122
122
  ```
123
123
 
124
- Stack entries support `basis`, `grow`, `shrink`, `minSize`, `maxSize`, and responsive `visible` callbacks. Mouse-wheel input targets the scroll view under the pointer and unused delta chains to outer scroll views by default. The primary scroll view receives the alternate-screen keyboard navigation actions and wheel input over non-scrollable regions. It can also jump between OSC 133 semantic prompt markers, matching common terminal prompt-navigation shortcuts.
124
+ Stack entries support `basis`, `grow`, `shrink`, `minSize`, `maxSize`, and responsive `visible` callbacks. Mouse-wheel input targets the scroll view under the pointer and unused delta chains to outer scroll views by default. The primary scroll view receives the alternate-screen keyboard navigation actions and wheel input over non-scrollable regions. It can also jump between OSC 133 semantic prompt markers, matching common terminal prompt-navigation shortcuts. Press `Ctrl+Shift+F` to search its rendered content, `Enter`/`Ctrl+G` and `Shift+Enter`/`Ctrl+Shift+G` to move between matches, and `Escape` to close search. `TuiAltScreenOptions.searchMatchStyle` and `searchCurrentMatchStyle` customize match highlighting.
125
125
 
126
126
  Layout geometry is rebuilt for each requested frame. Stateful components are retained, and their existing rendered-line caches remain effective. Calling `render(width)` directly on these layout components produces an unbounded document, which is also used when alt mode restores the main screen.
127
127
 
@@ -0,0 +1,26 @@
1
+ import type { Component, Focusable } from "./tui.ts";
2
+ export interface AltScreenSearchSegment {
3
+ row: number;
4
+ startCol: number;
5
+ endCol: number;
6
+ }
7
+ export interface AltScreenSearchMatch {
8
+ segments: AltScreenSearchSegment[];
9
+ }
10
+ export declare function findAltScreenSearchMatches(lines: readonly string[], query: string): AltScreenSearchMatch[];
11
+ export declare function getAltScreenSearchMatchKey(match: AltScreenSearchMatch): string;
12
+ export declare class AltScreenSearchComponent implements Component, Focusable {
13
+ private readonly input;
14
+ private readonly onQueryChange;
15
+ private resultCount;
16
+ private resultIndex;
17
+ private _focused;
18
+ constructor(onQueryChange: (query: string) => void);
19
+ get focused(): boolean;
20
+ set focused(value: boolean);
21
+ setResult(index: number, count: number): void;
22
+ handleInput(data: string): void;
23
+ invalidate(): void;
24
+ render(width: number): string[];
25
+ }
26
+ //# sourceMappingURL=alt-screen-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alt-screen-search.d.ts","sourceRoot":"","sources":["../src/alt-screen-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAWrD,MAAM,WAAW,sBAAsB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CACnC;AAkDD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,oBAAoB,EAAE,CA0B1G;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAI9E;AAED,qBAAa,wBAAyB,YAAW,SAAS,EAAE,SAAS;IACpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EAEjD;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAG5C;IAED,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAK9B;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAe9B;CACD","sourcesContent":["import { Input } from \"./components/input.ts\";\nimport type { Component, Focusable } from \"./tui.ts\";\nimport { getGraphemeSegmenter, stripTerminalSequences, truncateToWidth, visibleWidth } from \"./utils.ts\";\n\nconst segmenter = getGraphemeSegmenter();\n\ninterface SearchSourceSpan {\n\trow: number;\n\tstartCol: number;\n\tendCol: number;\n}\n\nexport interface AltScreenSearchSegment {\n\trow: number;\n\tstartCol: number;\n\tendCol: number;\n}\n\nexport interface AltScreenSearchMatch {\n\tsegments: AltScreenSearchSegment[];\n}\n\nfunction appendMappedText(\n\ttext: string,\n\tspan: SearchSourceSpan | undefined,\n\tcorpus: { text: string; source: Array<SearchSourceSpan | undefined> },\n): void {\n\tcorpus.text += text;\n\tfor (let index = 0; index < text.length; index++) corpus.source.push(span);\n}\n\nfunction buildSearchCorpus(lines: readonly string[]): {\n\ttext: string;\n\tsource: Array<SearchSourceSpan | undefined>;\n} {\n\tconst corpus: { text: string; source: Array<SearchSourceSpan | undefined> } = { text: \"\", source: [] };\n\tlet pendingSeparator = false;\n\n\tfor (let row = 0; row < lines.length; row++) {\n\t\tconst line = stripTerminalSequences(lines[row] ?? \"\");\n\t\tlet column = 0;\n\t\tfor (const grapheme of segmenter.segment(line)) {\n\t\t\tconst text = grapheme.segment;\n\t\t\tconst width = visibleWidth(text);\n\t\t\tif (/^\\s+$/u.test(text)) {\n\t\t\t\tif (corpus.text.length > 0) pendingSeparator = true;\n\t\t\t\tcolumn += width;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (pendingSeparator) {\n\t\t\t\tappendMappedText(\" \", undefined, corpus);\n\t\t\t\tpendingSeparator = false;\n\t\t\t}\n\t\t\tappendMappedText(text, { row, startCol: column, endCol: column + width }, corpus);\n\t\t\tcolumn += width;\n\t\t}\n\t\tif (corpus.text.length > 0) pendingSeparator = true;\n\t}\n\n\treturn corpus;\n}\n\nfunction normalizeQuery(query: string): string {\n\treturn query.replace(/\\s+/gu, \" \").trim();\n}\n\nfunction escapeRegExp(text: string): string {\n\treturn text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function findAltScreenSearchMatches(lines: readonly string[], query: string): AltScreenSearchMatch[] {\n\tconst normalizedQuery = normalizeQuery(query);\n\tif (!normalizedQuery) return [];\n\n\tconst corpus = buildSearchCorpus(lines);\n\tconst expression = new RegExp(escapeRegExp(normalizedQuery), \"giu\");\n\tconst matches: AltScreenSearchMatch[] = [];\n\n\tfor (const match of corpus.text.matchAll(expression)) {\n\t\tconst start = match.index;\n\t\tconst end = start + match[0].length;\n\t\tconst segments: AltScreenSearchSegment[] = [];\n\t\tfor (let index = start; index < end; index++) {\n\t\t\tconst span = corpus.source[index];\n\t\t\tif (!span) continue;\n\t\t\tconst previous = segments[segments.length - 1];\n\t\t\tif (previous && previous.row === span.row && span.startCol <= previous.endCol) {\n\t\t\t\tprevious.endCol = Math.max(previous.endCol, span.endCol);\n\t\t\t} else {\n\t\t\t\tsegments.push({ ...span });\n\t\t\t}\n\t\t}\n\t\tif (segments.length > 0) matches.push({ segments });\n\t}\n\n\treturn matches;\n}\n\nexport function getAltScreenSearchMatchKey(match: AltScreenSearchMatch): string {\n\tconst first = match.segments[0];\n\tconst last = match.segments[match.segments.length - 1];\n\treturn first && last ? `${first.row}:${first.startCol}:${last.row}:${last.endCol}` : \"\";\n}\n\nexport class AltScreenSearchComponent implements Component, Focusable {\n\tprivate readonly input = new Input();\n\tprivate readonly onQueryChange: (query: string) => void;\n\tprivate resultCount = 0;\n\tprivate resultIndex = -1;\n\tprivate _focused = false;\n\n\tconstructor(onQueryChange: (query: string) => void) {\n\t\tthis.onQueryChange = onQueryChange;\n\t}\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tsetResult(index: number, count: number): void {\n\t\tthis.resultIndex = index;\n\t\tthis.resultCount = count;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst previous = this.input.getValue();\n\t\tthis.input.handleInput(data);\n\t\tconst query = this.input.getValue();\n\t\tif (query !== previous) this.onQueryChange(query);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.input.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst label = \" Find transcript\";\n\t\tconst query = this.input.getValue();\n\t\tconst status = !query\n\t\t\t? \"\"\n\t\t\t: this.resultCount === 0\n\t\t\t\t? \"No matches \"\n\t\t\t\t: `${this.resultIndex + 1}/${this.resultCount} `;\n\t\tconst labelWidth = visibleWidth(label);\n\t\tconst statusWidth = visibleWidth(status);\n\t\tconst gap = \" \".repeat(Math.max(1, safeWidth - labelWidth - statusWidth));\n\t\tconst title = truncateToWidth(`${label}${gap}${status}`, safeWidth, \"\");\n\t\tconst padding = \" \".repeat(Math.max(0, safeWidth - visibleWidth(title)));\n\t\treturn [`\\x1b[7m${title}${padding}\\x1b[27m`, ...this.input.render(safeWidth)];\n\t}\n}\n"]}
@@ -0,0 +1,122 @@
1
+ import { Input } from "./components/input.js";
2
+ import { getGraphemeSegmenter, stripTerminalSequences, truncateToWidth, visibleWidth } from "./utils.js";
3
+ const segmenter = getGraphemeSegmenter();
4
+ function appendMappedText(text, span, corpus) {
5
+ corpus.text += text;
6
+ for (let index = 0; index < text.length; index++)
7
+ corpus.source.push(span);
8
+ }
9
+ function buildSearchCorpus(lines) {
10
+ const corpus = { text: "", source: [] };
11
+ let pendingSeparator = false;
12
+ for (let row = 0; row < lines.length; row++) {
13
+ const line = stripTerminalSequences(lines[row] ?? "");
14
+ let column = 0;
15
+ for (const grapheme of segmenter.segment(line)) {
16
+ const text = grapheme.segment;
17
+ const width = visibleWidth(text);
18
+ if (/^\s+$/u.test(text)) {
19
+ if (corpus.text.length > 0)
20
+ pendingSeparator = true;
21
+ column += width;
22
+ continue;
23
+ }
24
+ if (pendingSeparator) {
25
+ appendMappedText(" ", undefined, corpus);
26
+ pendingSeparator = false;
27
+ }
28
+ appendMappedText(text, { row, startCol: column, endCol: column + width }, corpus);
29
+ column += width;
30
+ }
31
+ if (corpus.text.length > 0)
32
+ pendingSeparator = true;
33
+ }
34
+ return corpus;
35
+ }
36
+ function normalizeQuery(query) {
37
+ return query.replace(/\s+/gu, " ").trim();
38
+ }
39
+ function escapeRegExp(text) {
40
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
41
+ }
42
+ export function findAltScreenSearchMatches(lines, query) {
43
+ const normalizedQuery = normalizeQuery(query);
44
+ if (!normalizedQuery)
45
+ return [];
46
+ const corpus = buildSearchCorpus(lines);
47
+ const expression = new RegExp(escapeRegExp(normalizedQuery), "giu");
48
+ const matches = [];
49
+ for (const match of corpus.text.matchAll(expression)) {
50
+ const start = match.index;
51
+ const end = start + match[0].length;
52
+ const segments = [];
53
+ for (let index = start; index < end; index++) {
54
+ const span = corpus.source[index];
55
+ if (!span)
56
+ continue;
57
+ const previous = segments[segments.length - 1];
58
+ if (previous && previous.row === span.row && span.startCol <= previous.endCol) {
59
+ previous.endCol = Math.max(previous.endCol, span.endCol);
60
+ }
61
+ else {
62
+ segments.push({ ...span });
63
+ }
64
+ }
65
+ if (segments.length > 0)
66
+ matches.push({ segments });
67
+ }
68
+ return matches;
69
+ }
70
+ export function getAltScreenSearchMatchKey(match) {
71
+ const first = match.segments[0];
72
+ const last = match.segments[match.segments.length - 1];
73
+ return first && last ? `${first.row}:${first.startCol}:${last.row}:${last.endCol}` : "";
74
+ }
75
+ export class AltScreenSearchComponent {
76
+ input = new Input();
77
+ onQueryChange;
78
+ resultCount = 0;
79
+ resultIndex = -1;
80
+ _focused = false;
81
+ constructor(onQueryChange) {
82
+ this.onQueryChange = onQueryChange;
83
+ }
84
+ get focused() {
85
+ return this._focused;
86
+ }
87
+ set focused(value) {
88
+ this._focused = value;
89
+ this.input.focused = value;
90
+ }
91
+ setResult(index, count) {
92
+ this.resultIndex = index;
93
+ this.resultCount = count;
94
+ }
95
+ handleInput(data) {
96
+ const previous = this.input.getValue();
97
+ this.input.handleInput(data);
98
+ const query = this.input.getValue();
99
+ if (query !== previous)
100
+ this.onQueryChange(query);
101
+ }
102
+ invalidate() {
103
+ this.input.invalidate();
104
+ }
105
+ render(width) {
106
+ const safeWidth = Math.max(1, width);
107
+ const label = " Find transcript";
108
+ const query = this.input.getValue();
109
+ const status = !query
110
+ ? ""
111
+ : this.resultCount === 0
112
+ ? "No matches "
113
+ : `${this.resultIndex + 1}/${this.resultCount} `;
114
+ const labelWidth = visibleWidth(label);
115
+ const statusWidth = visibleWidth(status);
116
+ const gap = " ".repeat(Math.max(1, safeWidth - labelWidth - statusWidth));
117
+ const title = truncateToWidth(`${label}${gap}${status}`, safeWidth, "");
118
+ const padding = " ".repeat(Math.max(0, safeWidth - visibleWidth(title)));
119
+ return [`\x1b[7m${title}${padding}\x1b[27m`, ...this.input.render(safeWidth)];
120
+ }
121
+ }
122
+ //# sourceMappingURL=alt-screen-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alt-screen-search.js","sourceRoot":"","sources":["../src/alt-screen-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { Input } from \"./components/input.ts\";\nimport type { Component, Focusable } from \"./tui.ts\";\nimport { getGraphemeSegmenter, stripTerminalSequences, truncateToWidth, visibleWidth } from \"./utils.ts\";\n\nconst segmenter = getGraphemeSegmenter();\n\ninterface SearchSourceSpan {\n\trow: number;\n\tstartCol: number;\n\tendCol: number;\n}\n\nexport interface AltScreenSearchSegment {\n\trow: number;\n\tstartCol: number;\n\tendCol: number;\n}\n\nexport interface AltScreenSearchMatch {\n\tsegments: AltScreenSearchSegment[];\n}\n\nfunction appendMappedText(\n\ttext: string,\n\tspan: SearchSourceSpan | undefined,\n\tcorpus: { text: string; source: Array<SearchSourceSpan | undefined> },\n): void {\n\tcorpus.text += text;\n\tfor (let index = 0; index < text.length; index++) corpus.source.push(span);\n}\n\nfunction buildSearchCorpus(lines: readonly string[]): {\n\ttext: string;\n\tsource: Array<SearchSourceSpan | undefined>;\n} {\n\tconst corpus: { text: string; source: Array<SearchSourceSpan | undefined> } = { text: \"\", source: [] };\n\tlet pendingSeparator = false;\n\n\tfor (let row = 0; row < lines.length; row++) {\n\t\tconst line = stripTerminalSequences(lines[row] ?? \"\");\n\t\tlet column = 0;\n\t\tfor (const grapheme of segmenter.segment(line)) {\n\t\t\tconst text = grapheme.segment;\n\t\t\tconst width = visibleWidth(text);\n\t\t\tif (/^\\s+$/u.test(text)) {\n\t\t\t\tif (corpus.text.length > 0) pendingSeparator = true;\n\t\t\t\tcolumn += width;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (pendingSeparator) {\n\t\t\t\tappendMappedText(\" \", undefined, corpus);\n\t\t\t\tpendingSeparator = false;\n\t\t\t}\n\t\t\tappendMappedText(text, { row, startCol: column, endCol: column + width }, corpus);\n\t\t\tcolumn += width;\n\t\t}\n\t\tif (corpus.text.length > 0) pendingSeparator = true;\n\t}\n\n\treturn corpus;\n}\n\nfunction normalizeQuery(query: string): string {\n\treturn query.replace(/\\s+/gu, \" \").trim();\n}\n\nfunction escapeRegExp(text: string): string {\n\treturn text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function findAltScreenSearchMatches(lines: readonly string[], query: string): AltScreenSearchMatch[] {\n\tconst normalizedQuery = normalizeQuery(query);\n\tif (!normalizedQuery) return [];\n\n\tconst corpus = buildSearchCorpus(lines);\n\tconst expression = new RegExp(escapeRegExp(normalizedQuery), \"giu\");\n\tconst matches: AltScreenSearchMatch[] = [];\n\n\tfor (const match of corpus.text.matchAll(expression)) {\n\t\tconst start = match.index;\n\t\tconst end = start + match[0].length;\n\t\tconst segments: AltScreenSearchSegment[] = [];\n\t\tfor (let index = start; index < end; index++) {\n\t\t\tconst span = corpus.source[index];\n\t\t\tif (!span) continue;\n\t\t\tconst previous = segments[segments.length - 1];\n\t\t\tif (previous && previous.row === span.row && span.startCol <= previous.endCol) {\n\t\t\t\tprevious.endCol = Math.max(previous.endCol, span.endCol);\n\t\t\t} else {\n\t\t\t\tsegments.push({ ...span });\n\t\t\t}\n\t\t}\n\t\tif (segments.length > 0) matches.push({ segments });\n\t}\n\n\treturn matches;\n}\n\nexport function getAltScreenSearchMatchKey(match: AltScreenSearchMatch): string {\n\tconst first = match.segments[0];\n\tconst last = match.segments[match.segments.length - 1];\n\treturn first && last ? `${first.row}:${first.startCol}:${last.row}:${last.endCol}` : \"\";\n}\n\nexport class AltScreenSearchComponent implements Component, Focusable {\n\tprivate readonly input = new Input();\n\tprivate readonly onQueryChange: (query: string) => void;\n\tprivate resultCount = 0;\n\tprivate resultIndex = -1;\n\tprivate _focused = false;\n\n\tconstructor(onQueryChange: (query: string) => void) {\n\t\tthis.onQueryChange = onQueryChange;\n\t}\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.input.focused = value;\n\t}\n\n\tsetResult(index: number, count: number): void {\n\t\tthis.resultIndex = index;\n\t\tthis.resultCount = count;\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst previous = this.input.getValue();\n\t\tthis.input.handleInput(data);\n\t\tconst query = this.input.getValue();\n\t\tif (query !== previous) this.onQueryChange(query);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.input.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst label = \" Find transcript\";\n\t\tconst query = this.input.getValue();\n\t\tconst status = !query\n\t\t\t? \"\"\n\t\t\t: this.resultCount === 0\n\t\t\t\t? \"No matches \"\n\t\t\t\t: `${this.resultIndex + 1}/${this.resultCount} `;\n\t\tconst labelWidth = visibleWidth(label);\n\t\tconst statusWidth = visibleWidth(status);\n\t\tconst gap = \" \".repeat(Math.max(1, safeWidth - labelWidth - statusWidth));\n\t\tconst title = truncateToWidth(`${label}${gap}${status}`, safeWidth, \"\");\n\t\tconst padding = \" \".repeat(Math.max(0, safeWidth - visibleWidth(title)));\n\t\treturn [`\\x1b[7m${title}${padding}\\x1b[27m`, ...this.input.render(safeWidth)];\n\t}\n}\n"]}
@@ -11,6 +11,10 @@ export interface ScrollViewOptions {
11
11
  scrollbarStyle?: (text: string) => string;
12
12
  scrollbarHideDelayMs?: number;
13
13
  }
14
+ export interface ScrollViewScrollToOptions {
15
+ /** Keep follow-end disabled even when the target is the current content end. */
16
+ disableFollow?: boolean;
17
+ }
14
18
  export declare class ScrollView extends Container {
15
19
  private readonly child;
16
20
  private readonly followEnd;
@@ -24,6 +28,7 @@ export declare class ScrollView extends Container {
24
28
  private contentHeight;
25
29
  private currentViewportHeight;
26
30
  private followingEnd;
31
+ private followSuppressedAtEnd;
27
32
  private requestRenderCallback;
28
33
  private transientScrollbarVisible;
29
34
  private scrollbarActive;
@@ -40,7 +45,7 @@ export declare class ScrollView extends Container {
40
45
  private hideTransientScrollbar;
41
46
  private notifyScroll;
42
47
  setScrollbarActive(active: boolean): void;
43
- scrollTo(scrollTop: number): void;
48
+ scrollTo(scrollTop: number, options?: ScrollViewScrollToOptions): void;
44
49
  scrollBy(lines: number): number;
45
50
  scrollToStart(): void;
46
51
  scrollToEnd(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-view.d.ts","sourceRoot":"","sources":["../../src/components/scroll-view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE/D,MAAM,WAAW,iBAAiB;IACjC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,qBAAa,UAAW,SAAQ,SAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAA8B;IACxD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAClD,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,qBAAqB,CAA2B;IACxD,OAAO,CAAC,yBAAyB,CAAS;IAC1C,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,kBAAkB,CAA6B;IAEvD,YAAY,SAAS,EAAE,SAAS,EAAE,OAAO,GAAE,iBAAsB,EAehE;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,SAAS,IAAI,mBAAmB,CAEnC;IAED,IAAI,kBAAkB,IAAI,OAAO,CAKhC;IAED,YAAY,CAAC,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAMjD;IAED,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErC;IAED,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,YAAY;IAIpB,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAIxC;IAED,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAUhC;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiB9B;IAED,aAAa,IAAI,IAAI,CAWpB;IAED,WAAW,IAAI,IAAI,CASlB;IAED,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,IAAI,GAAG,IAAI,CAS3F;IAEQ,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,IAAI,CAE7C;IAEQ,WAAW,CAAC,UAAU,EAAE,SAAS,GAAG,IAAI,CAEhD;IAEQ,KAAK,IAAI,IAAI,CAErB;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAIvC;IAED,CAAC,WAAW,CAAC,IAAI,gBAAgB,CAEhC;CACD","sourcesContent":["import { LAYOUT_NODE, type ScrollLayoutNode } from \"../layout-node.ts\";\nimport { type Component, Container } from \"../tui.ts\";\n\nexport type ScrollViewScrollbar = \"hidden\" | \"auto\" | \"always\";\n\nexport interface ScrollViewOptions {\n\taxis?: \"vertical\";\n\tfollow?: \"none\" | \"end\";\n\tonScroll?: (scrollTop: number) => void;\n\tprimary?: boolean;\n\toverscroll?: \"chain\" | \"contain\";\n\tscrollbar?: ScrollViewScrollbar;\n\tscrollbarStyle?: (text: string) => string;\n\tscrollbarHideDelayMs?: number;\n}\n\nexport class ScrollView extends Container {\n\tprivate readonly child: Component;\n\tprivate readonly followEnd: boolean;\n\tprivate readonly onScroll?: (scrollTop: number) => void;\n\treadonly primary: boolean;\n\treadonly overscroll: \"chain\" | \"contain\";\n\treadonly scrollbarStyle: (text: string) => string;\n\tprivate currentScrollbar: ScrollViewScrollbar;\n\tprivate readonly scrollbarHideDelayMs: number;\n\tprivate currentScrollTop = 0;\n\tprivate contentHeight = 0;\n\tprivate currentViewportHeight = 0;\n\tprivate followingEnd: boolean;\n\tprivate requestRenderCallback: (() => void) | undefined;\n\tprivate transientScrollbarVisible = false;\n\tprivate scrollbarActive = false;\n\tprivate scrollbarHideTimer: NodeJS.Timeout | undefined;\n\n\tconstructor(component: Component, options: ScrollViewOptions = {}) {\n\t\tsuper();\n\t\tif (options.axis !== undefined && options.axis !== \"vertical\") {\n\t\t\tthrow new Error(`Unsupported ScrollView axis: ${options.axis}`);\n\t\t}\n\t\tthis.child = component;\n\t\tthis.children.push(component);\n\t\tthis.followEnd = (options.follow ?? \"none\") === \"end\";\n\t\tthis.onScroll = options.onScroll;\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.primary = options.primary ?? false;\n\t\tthis.overscroll = options.overscroll ?? \"chain\";\n\t\tthis.currentScrollbar = options.scrollbar ?? \"hidden\";\n\t\tthis.scrollbarStyle = options.scrollbarStyle ?? ((text) => `\\x1b[100m${text}\\x1b[49m`);\n\t\tthis.scrollbarHideDelayMs = Math.max(0, Math.floor(options.scrollbarHideDelayMs ?? 1000));\n\t}\n\n\tget scrollTop(): number {\n\t\treturn this.currentScrollTop;\n\t}\n\n\tget isFollowingEnd(): boolean {\n\t\treturn this.followingEnd;\n\t}\n\n\tget viewportHeight(): number {\n\t\treturn this.currentViewportHeight;\n\t}\n\n\tget scrollbar(): ScrollViewScrollbar {\n\t\treturn this.currentScrollbar;\n\t}\n\n\tget isScrollbarVisible(): boolean {\n\t\tif (this.scrollbar === \"always\") return this.currentViewportHeight > 0;\n\t\treturn (\n\t\t\tthis.scrollbar === \"auto\" && this.contentHeight > this.currentViewportHeight && this.transientScrollbarVisible\n\t\t);\n\t}\n\n\tsetScrollbar(scrollbar: ScrollViewScrollbar): void {\n\t\tif (scrollbar === this.currentScrollbar) return;\n\t\tthis.currentScrollbar = scrollbar;\n\t\tif (scrollbar !== \"auto\") this.hideTransientScrollbar();\n\t\telse if (this.scrollbarActive) this.markScrollbarActivity();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tgetContentWidth(width: number): number {\n\t\treturn this.scrollbar === \"always\" && width > 1 ? width - 1 : width;\n\t}\n\n\tprivate markScrollbarActivity(): void {\n\t\tif (this.scrollbar !== \"auto\" || this.contentHeight <= this.currentViewportHeight) return;\n\t\tthis.transientScrollbarVisible = true;\n\t\tif (this.scrollbarHideTimer) {\n\t\t\tclearTimeout(this.scrollbarHideTimer);\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t}\n\t\tif (this.scrollbarActive) return;\n\t\tthis.scrollbarHideTimer = setTimeout(() => {\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t\tthis.transientScrollbarVisible = false;\n\t\t\tthis.requestRenderCallback?.();\n\t\t}, this.scrollbarHideDelayMs);\n\t\tthis.scrollbarHideTimer.unref();\n\t}\n\n\tprivate hideTransientScrollbar(): void {\n\t\tthis.transientScrollbarVisible = false;\n\t\tif (!this.scrollbarHideTimer) return;\n\t\tclearTimeout(this.scrollbarHideTimer);\n\t\tthis.scrollbarHideTimer = undefined;\n\t}\n\n\tprivate notifyScroll(): void {\n\t\tthis.onScroll?.(this.currentScrollTop);\n\t}\n\n\tsetScrollbarActive(active: boolean): void {\n\t\tif (active === this.scrollbarActive) return;\n\t\tthis.scrollbarActive = active;\n\t\tthis.markScrollbarActivity();\n\t}\n\n\tscrollTo(scrollTop: number): void {\n\t\tconst requested = Number.isFinite(scrollTop) ? Math.trunc(scrollTop) : this.currentScrollTop;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, requested));\n\t\tif (next === this.currentScrollTop) return;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd && next === maxScrollTop;\n\t\tthis.markScrollbarActivity();\n\t\tthis.notifyScroll();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tscrollBy(lines: number): number {\n\t\tconst requested = Number.isFinite(lines) ? Math.trunc(lines) : 0;\n\t\tif (requested === 0) return 0;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst start = this.followingEnd ? maxScrollTop : this.currentScrollTop;\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, start + requested));\n\t\tconst moved = next - start;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd && next === maxScrollTop;\n\t\tif (moved !== 0) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.notifyScroll();\n\t\t\tthis.requestRenderCallback?.();\n\t\t} else if (requested < 0 && start === 0) {\n\t\t\tthis.notifyScroll();\n\t\t}\n\t\treturn requested - moved;\n\t}\n\n\tscrollToStart(): void {\n\t\tconst changed =\n\t\t\tthis.currentScrollTop !== 0 ||\n\t\t\tthis.followingEnd !== (this.followEnd && this.contentHeight <= this.currentViewportHeight);\n\t\tthis.currentScrollTop = 0;\n\t\tthis.followingEnd = this.followEnd && this.contentHeight <= this.currentViewportHeight;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t}\n\t\tthis.notifyScroll();\n\t\tif (changed) this.requestRenderCallback?.();\n\t}\n\n\tscrollToEnd(): void {\n\t\tconst next = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst changed = this.currentScrollTop !== next || this.followingEnd !== this.followEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t}\n\n\tupdateLayout(contentHeight: number, viewportHeight: number, requestRender: () => void): void {\n\t\tthis.contentHeight = Math.max(0, Math.floor(contentHeight));\n\t\tthis.currentViewportHeight = Math.max(0, Math.floor(viewportHeight));\n\t\tthis.requestRenderCallback = requestRender;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tif (this.followingEnd) this.currentScrollTop = maxScrollTop;\n\t\telse this.currentScrollTop = Math.max(0, Math.min(this.currentScrollTop, maxScrollTop));\n\t\tif (this.followEnd && this.currentScrollTop === maxScrollTop) this.followingEnd = true;\n\t\tif (this.contentHeight <= this.currentViewportHeight) this.hideTransientScrollbar();\n\t}\n\n\toverride addChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView has exactly one child\");\n\t}\n\n\toverride removeChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView child cannot be removed\");\n\t}\n\n\toverride clear(): void {\n\t\tthrow new Error(\"ScrollView child cannot be cleared\");\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst contentWidth = this.getContentWidth(width);\n\t\tconst lines = this.child.render(contentWidth);\n\t\treturn contentWidth === width ? lines : lines.map((line) => `${line} `);\n\t}\n\n\t[LAYOUT_NODE](): ScrollLayoutNode {\n\t\treturn { type: \"scroll\", component: this.child, state: this };\n\t}\n}\n"]}
1
+ {"version":3,"file":"scroll-view.d.ts","sourceRoot":"","sources":["../../src/components/scroll-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { LAYOUT_NODE, type ScrollLayoutNode } from \"../layout-node.ts\";\nimport { type Component, Container } from \"../tui.ts\";\n\nexport type ScrollViewScrollbar = \"hidden\" | \"auto\" | \"always\";\n\nexport interface ScrollViewOptions {\n\taxis?: \"vertical\";\n\tfollow?: \"none\" | \"end\";\n\tonScroll?: (scrollTop: number) => void;\n\tprimary?: boolean;\n\toverscroll?: \"chain\" | \"contain\";\n\tscrollbar?: ScrollViewScrollbar;\n\tscrollbarStyle?: (text: string) => string;\n\tscrollbarHideDelayMs?: number;\n}\n\nexport interface ScrollViewScrollToOptions {\n\t/** Keep follow-end disabled even when the target is the current content end. */\n\tdisableFollow?: boolean;\n}\n\nexport class ScrollView extends Container {\n\tprivate readonly child: Component;\n\tprivate readonly followEnd: boolean;\n\tprivate readonly onScroll?: (scrollTop: number) => void;\n\treadonly primary: boolean;\n\treadonly overscroll: \"chain\" | \"contain\";\n\treadonly scrollbarStyle: (text: string) => string;\n\tprivate currentScrollbar: ScrollViewScrollbar;\n\tprivate readonly scrollbarHideDelayMs: number;\n\tprivate currentScrollTop = 0;\n\tprivate contentHeight = 0;\n\tprivate currentViewportHeight = 0;\n\tprivate followingEnd: boolean;\n\tprivate followSuppressedAtEnd = false;\n\tprivate requestRenderCallback: (() => void) | undefined;\n\tprivate transientScrollbarVisible = false;\n\tprivate scrollbarActive = false;\n\tprivate scrollbarHideTimer: NodeJS.Timeout | undefined;\n\n\tconstructor(component: Component, options: ScrollViewOptions = {}) {\n\t\tsuper();\n\t\tif (options.axis !== undefined && options.axis !== \"vertical\") {\n\t\t\tthrow new Error(`Unsupported ScrollView axis: ${options.axis}`);\n\t\t}\n\t\tthis.child = component;\n\t\tthis.children.push(component);\n\t\tthis.followEnd = (options.follow ?? \"none\") === \"end\";\n\t\tthis.onScroll = options.onScroll;\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.primary = options.primary ?? false;\n\t\tthis.overscroll = options.overscroll ?? \"chain\";\n\t\tthis.currentScrollbar = options.scrollbar ?? \"hidden\";\n\t\tthis.scrollbarStyle = options.scrollbarStyle ?? ((text) => `\\x1b[100m${text}\\x1b[49m`);\n\t\tthis.scrollbarHideDelayMs = Math.max(0, Math.floor(options.scrollbarHideDelayMs ?? 1000));\n\t}\n\n\tget scrollTop(): number {\n\t\treturn this.currentScrollTop;\n\t}\n\n\tget isFollowingEnd(): boolean {\n\t\treturn this.followingEnd;\n\t}\n\n\tget viewportHeight(): number {\n\t\treturn this.currentViewportHeight;\n\t}\n\n\tget scrollbar(): ScrollViewScrollbar {\n\t\treturn this.currentScrollbar;\n\t}\n\n\tget isScrollbarVisible(): boolean {\n\t\tif (this.scrollbar === \"always\") return this.currentViewportHeight > 0;\n\t\treturn (\n\t\t\tthis.scrollbar === \"auto\" && this.contentHeight > this.currentViewportHeight && this.transientScrollbarVisible\n\t\t);\n\t}\n\n\tsetScrollbar(scrollbar: ScrollViewScrollbar): void {\n\t\tif (scrollbar === this.currentScrollbar) return;\n\t\tthis.currentScrollbar = scrollbar;\n\t\tif (scrollbar !== \"auto\") this.hideTransientScrollbar();\n\t\telse if (this.scrollbarActive) this.markScrollbarActivity();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tgetContentWidth(width: number): number {\n\t\treturn this.scrollbar === \"always\" && width > 1 ? width - 1 : width;\n\t}\n\n\tprivate markScrollbarActivity(): void {\n\t\tif (this.scrollbar !== \"auto\" || this.contentHeight <= this.currentViewportHeight) return;\n\t\tthis.transientScrollbarVisible = true;\n\t\tif (this.scrollbarHideTimer) {\n\t\t\tclearTimeout(this.scrollbarHideTimer);\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t}\n\t\tif (this.scrollbarActive) return;\n\t\tthis.scrollbarHideTimer = setTimeout(() => {\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t\tthis.transientScrollbarVisible = false;\n\t\t\tthis.requestRenderCallback?.();\n\t\t}, this.scrollbarHideDelayMs);\n\t\tthis.scrollbarHideTimer.unref();\n\t}\n\n\tprivate hideTransientScrollbar(): void {\n\t\tthis.transientScrollbarVisible = false;\n\t\tif (!this.scrollbarHideTimer) return;\n\t\tclearTimeout(this.scrollbarHideTimer);\n\t\tthis.scrollbarHideTimer = undefined;\n\t}\n\n\tprivate notifyScroll(): void {\n\t\tthis.onScroll?.(this.currentScrollTop);\n\t}\n\n\tsetScrollbarActive(active: boolean): void {\n\t\tif (active === this.scrollbarActive) return;\n\t\tthis.scrollbarActive = active;\n\t\tthis.markScrollbarActivity();\n\t}\n\n\tscrollTo(scrollTop: number, options: ScrollViewScrollToOptions = {}): void {\n\t\tconst requested = Number.isFinite(scrollTop) ? Math.trunc(scrollTop) : this.currentScrollTop;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, requested));\n\t\tconst nextFollowSuppressedAtEnd = options.disableFollow === true && next === maxScrollTop;\n\t\tconst nextFollowingEnd = !nextFollowSuppressedAtEnd && this.followEnd && next === maxScrollTop;\n\t\tif (\n\t\t\tnext === this.currentScrollTop &&\n\t\t\tnextFollowingEnd === this.followingEnd &&\n\t\t\tnextFollowSuppressedAtEnd === this.followSuppressedAtEnd\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst moved = next !== this.currentScrollTop;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = nextFollowingEnd;\n\t\tthis.followSuppressedAtEnd = nextFollowSuppressedAtEnd;\n\t\tif (moved) this.markScrollbarActivity();\n\t\tthis.notifyScroll();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tscrollBy(lines: number): number {\n\t\tconst requested = Number.isFinite(lines) ? Math.trunc(lines) : 0;\n\t\tif (requested === 0) return 0;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst start = this.followingEnd ? maxScrollTop : this.currentScrollTop;\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, start + requested));\n\t\tconst moved = next - start;\n\t\tconst wasFollowingEnd = this.followingEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd && next === maxScrollTop;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (moved !== 0) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.notifyScroll();\n\t\t\tthis.requestRenderCallback?.();\n\t\t} else if (requested < 0 && start === 0) {\n\t\t\tthis.notifyScroll();\n\t\t} else if (this.followingEnd !== wasFollowingEnd) {\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t\treturn requested - moved;\n\t}\n\n\tscrollToStart(): void {\n\t\tconst changed =\n\t\t\tthis.currentScrollTop !== 0 ||\n\t\t\tthis.followingEnd !== (this.followEnd && this.contentHeight <= this.currentViewportHeight);\n\t\tthis.currentScrollTop = 0;\n\t\tthis.followingEnd = this.followEnd && this.contentHeight <= this.currentViewportHeight;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t}\n\t\tthis.notifyScroll();\n\t\tif (changed) this.requestRenderCallback?.();\n\t}\n\n\tscrollToEnd(): void {\n\t\tconst next = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst changed = this.currentScrollTop !== next || this.followingEnd !== this.followEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t}\n\n\tupdateLayout(contentHeight: number, viewportHeight: number, requestRender: () => void): void {\n\t\tthis.contentHeight = Math.max(0, Math.floor(contentHeight));\n\t\tthis.currentViewportHeight = Math.max(0, Math.floor(viewportHeight));\n\t\tthis.requestRenderCallback = requestRender;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tif (this.followingEnd) this.currentScrollTop = maxScrollTop;\n\t\telse this.currentScrollTop = Math.max(0, Math.min(this.currentScrollTop, maxScrollTop));\n\t\tif (this.currentScrollTop < maxScrollTop) this.followSuppressedAtEnd = false;\n\t\tif (this.followEnd && this.currentScrollTop === maxScrollTop && !this.followSuppressedAtEnd) {\n\t\t\tthis.followingEnd = true;\n\t\t}\n\t\tif (this.contentHeight <= this.currentViewportHeight) this.hideTransientScrollbar();\n\t}\n\n\toverride addChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView has exactly one child\");\n\t}\n\n\toverride removeChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView child cannot be removed\");\n\t}\n\n\toverride clear(): void {\n\t\tthrow new Error(\"ScrollView child cannot be cleared\");\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst contentWidth = this.getContentWidth(width);\n\t\tconst lines = this.child.render(contentWidth);\n\t\treturn contentWidth === width ? lines : lines.map((line) => `${line} `);\n\t}\n\n\t[LAYOUT_NODE](): ScrollLayoutNode {\n\t\treturn { type: \"scroll\", component: this.child, state: this };\n\t}\n}\n"]}
@@ -13,6 +13,7 @@ export class ScrollView extends Container {
13
13
  contentHeight = 0;
14
14
  currentViewportHeight = 0;
15
15
  followingEnd;
16
+ followSuppressedAtEnd = false;
16
17
  requestRenderCallback;
17
18
  transientScrollbarVisible = false;
18
19
  scrollbarActive = false;
@@ -96,15 +97,23 @@ export class ScrollView extends Container {
96
97
  this.scrollbarActive = active;
97
98
  this.markScrollbarActivity();
98
99
  }
99
- scrollTo(scrollTop) {
100
+ scrollTo(scrollTop, options = {}) {
100
101
  const requested = Number.isFinite(scrollTop) ? Math.trunc(scrollTop) : this.currentScrollTop;
101
102
  const maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);
102
103
  const next = Math.max(0, Math.min(maxScrollTop, requested));
103
- if (next === this.currentScrollTop)
104
+ const nextFollowSuppressedAtEnd = options.disableFollow === true && next === maxScrollTop;
105
+ const nextFollowingEnd = !nextFollowSuppressedAtEnd && this.followEnd && next === maxScrollTop;
106
+ if (next === this.currentScrollTop &&
107
+ nextFollowingEnd === this.followingEnd &&
108
+ nextFollowSuppressedAtEnd === this.followSuppressedAtEnd) {
104
109
  return;
110
+ }
111
+ const moved = next !== this.currentScrollTop;
105
112
  this.currentScrollTop = next;
106
- this.followingEnd = this.followEnd && next === maxScrollTop;
107
- this.markScrollbarActivity();
113
+ this.followingEnd = nextFollowingEnd;
114
+ this.followSuppressedAtEnd = nextFollowSuppressedAtEnd;
115
+ if (moved)
116
+ this.markScrollbarActivity();
108
117
  this.notifyScroll();
109
118
  this.requestRenderCallback?.();
110
119
  }
@@ -116,8 +125,10 @@ export class ScrollView extends Container {
116
125
  const start = this.followingEnd ? maxScrollTop : this.currentScrollTop;
117
126
  const next = Math.max(0, Math.min(maxScrollTop, start + requested));
118
127
  const moved = next - start;
128
+ const wasFollowingEnd = this.followingEnd;
119
129
  this.currentScrollTop = next;
120
130
  this.followingEnd = this.followEnd && next === maxScrollTop;
131
+ this.followSuppressedAtEnd = false;
121
132
  if (moved !== 0) {
122
133
  this.markScrollbarActivity();
123
134
  this.notifyScroll();
@@ -126,6 +137,9 @@ export class ScrollView extends Container {
126
137
  else if (requested < 0 && start === 0) {
127
138
  this.notifyScroll();
128
139
  }
140
+ else if (this.followingEnd !== wasFollowingEnd) {
141
+ this.requestRenderCallback?.();
142
+ }
129
143
  return requested - moved;
130
144
  }
131
145
  scrollToStart() {
@@ -133,6 +147,7 @@ export class ScrollView extends Container {
133
147
  this.followingEnd !== (this.followEnd && this.contentHeight <= this.currentViewportHeight);
134
148
  this.currentScrollTop = 0;
135
149
  this.followingEnd = this.followEnd && this.contentHeight <= this.currentViewportHeight;
150
+ this.followSuppressedAtEnd = false;
136
151
  if (changed) {
137
152
  this.markScrollbarActivity();
138
153
  }
@@ -145,6 +160,7 @@ export class ScrollView extends Container {
145
160
  const changed = this.currentScrollTop !== next || this.followingEnd !== this.followEnd;
146
161
  this.currentScrollTop = next;
147
162
  this.followingEnd = this.followEnd;
163
+ this.followSuppressedAtEnd = false;
148
164
  if (changed) {
149
165
  this.markScrollbarActivity();
150
166
  this.requestRenderCallback?.();
@@ -159,8 +175,11 @@ export class ScrollView extends Container {
159
175
  this.currentScrollTop = maxScrollTop;
160
176
  else
161
177
  this.currentScrollTop = Math.max(0, Math.min(this.currentScrollTop, maxScrollTop));
162
- if (this.followEnd && this.currentScrollTop === maxScrollTop)
178
+ if (this.currentScrollTop < maxScrollTop)
179
+ this.followSuppressedAtEnd = false;
180
+ if (this.followEnd && this.currentScrollTop === maxScrollTop && !this.followSuppressedAtEnd) {
163
181
  this.followingEnd = true;
182
+ }
164
183
  if (this.contentHeight <= this.currentViewportHeight)
165
184
  this.hideTransientScrollbar();
166
185
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../../src/components/scroll-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { LAYOUT_NODE, type ScrollLayoutNode } from \"../layout-node.ts\";\nimport { type Component, Container } from \"../tui.ts\";\n\nexport type ScrollViewScrollbar = \"hidden\" | \"auto\" | \"always\";\n\nexport interface ScrollViewOptions {\n\taxis?: \"vertical\";\n\tfollow?: \"none\" | \"end\";\n\tonScroll?: (scrollTop: number) => void;\n\tprimary?: boolean;\n\toverscroll?: \"chain\" | \"contain\";\n\tscrollbar?: ScrollViewScrollbar;\n\tscrollbarStyle?: (text: string) => string;\n\tscrollbarHideDelayMs?: number;\n}\n\nexport class ScrollView extends Container {\n\tprivate readonly child: Component;\n\tprivate readonly followEnd: boolean;\n\tprivate readonly onScroll?: (scrollTop: number) => void;\n\treadonly primary: boolean;\n\treadonly overscroll: \"chain\" | \"contain\";\n\treadonly scrollbarStyle: (text: string) => string;\n\tprivate currentScrollbar: ScrollViewScrollbar;\n\tprivate readonly scrollbarHideDelayMs: number;\n\tprivate currentScrollTop = 0;\n\tprivate contentHeight = 0;\n\tprivate currentViewportHeight = 0;\n\tprivate followingEnd: boolean;\n\tprivate requestRenderCallback: (() => void) | undefined;\n\tprivate transientScrollbarVisible = false;\n\tprivate scrollbarActive = false;\n\tprivate scrollbarHideTimer: NodeJS.Timeout | undefined;\n\n\tconstructor(component: Component, options: ScrollViewOptions = {}) {\n\t\tsuper();\n\t\tif (options.axis !== undefined && options.axis !== \"vertical\") {\n\t\t\tthrow new Error(`Unsupported ScrollView axis: ${options.axis}`);\n\t\t}\n\t\tthis.child = component;\n\t\tthis.children.push(component);\n\t\tthis.followEnd = (options.follow ?? \"none\") === \"end\";\n\t\tthis.onScroll = options.onScroll;\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.primary = options.primary ?? false;\n\t\tthis.overscroll = options.overscroll ?? \"chain\";\n\t\tthis.currentScrollbar = options.scrollbar ?? \"hidden\";\n\t\tthis.scrollbarStyle = options.scrollbarStyle ?? ((text) => `\\x1b[100m${text}\\x1b[49m`);\n\t\tthis.scrollbarHideDelayMs = Math.max(0, Math.floor(options.scrollbarHideDelayMs ?? 1000));\n\t}\n\n\tget scrollTop(): number {\n\t\treturn this.currentScrollTop;\n\t}\n\n\tget isFollowingEnd(): boolean {\n\t\treturn this.followingEnd;\n\t}\n\n\tget viewportHeight(): number {\n\t\treturn this.currentViewportHeight;\n\t}\n\n\tget scrollbar(): ScrollViewScrollbar {\n\t\treturn this.currentScrollbar;\n\t}\n\n\tget isScrollbarVisible(): boolean {\n\t\tif (this.scrollbar === \"always\") return this.currentViewportHeight > 0;\n\t\treturn (\n\t\t\tthis.scrollbar === \"auto\" && this.contentHeight > this.currentViewportHeight && this.transientScrollbarVisible\n\t\t);\n\t}\n\n\tsetScrollbar(scrollbar: ScrollViewScrollbar): void {\n\t\tif (scrollbar === this.currentScrollbar) return;\n\t\tthis.currentScrollbar = scrollbar;\n\t\tif (scrollbar !== \"auto\") this.hideTransientScrollbar();\n\t\telse if (this.scrollbarActive) this.markScrollbarActivity();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tgetContentWidth(width: number): number {\n\t\treturn this.scrollbar === \"always\" && width > 1 ? width - 1 : width;\n\t}\n\n\tprivate markScrollbarActivity(): void {\n\t\tif (this.scrollbar !== \"auto\" || this.contentHeight <= this.currentViewportHeight) return;\n\t\tthis.transientScrollbarVisible = true;\n\t\tif (this.scrollbarHideTimer) {\n\t\t\tclearTimeout(this.scrollbarHideTimer);\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t}\n\t\tif (this.scrollbarActive) return;\n\t\tthis.scrollbarHideTimer = setTimeout(() => {\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t\tthis.transientScrollbarVisible = false;\n\t\t\tthis.requestRenderCallback?.();\n\t\t}, this.scrollbarHideDelayMs);\n\t\tthis.scrollbarHideTimer.unref();\n\t}\n\n\tprivate hideTransientScrollbar(): void {\n\t\tthis.transientScrollbarVisible = false;\n\t\tif (!this.scrollbarHideTimer) return;\n\t\tclearTimeout(this.scrollbarHideTimer);\n\t\tthis.scrollbarHideTimer = undefined;\n\t}\n\n\tprivate notifyScroll(): void {\n\t\tthis.onScroll?.(this.currentScrollTop);\n\t}\n\n\tsetScrollbarActive(active: boolean): void {\n\t\tif (active === this.scrollbarActive) return;\n\t\tthis.scrollbarActive = active;\n\t\tthis.markScrollbarActivity();\n\t}\n\n\tscrollTo(scrollTop: number): void {\n\t\tconst requested = Number.isFinite(scrollTop) ? Math.trunc(scrollTop) : this.currentScrollTop;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, requested));\n\t\tif (next === this.currentScrollTop) return;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd && next === maxScrollTop;\n\t\tthis.markScrollbarActivity();\n\t\tthis.notifyScroll();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tscrollBy(lines: number): number {\n\t\tconst requested = Number.isFinite(lines) ? Math.trunc(lines) : 0;\n\t\tif (requested === 0) return 0;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst start = this.followingEnd ? maxScrollTop : this.currentScrollTop;\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, start + requested));\n\t\tconst moved = next - start;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd && next === maxScrollTop;\n\t\tif (moved !== 0) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.notifyScroll();\n\t\t\tthis.requestRenderCallback?.();\n\t\t} else if (requested < 0 && start === 0) {\n\t\t\tthis.notifyScroll();\n\t\t}\n\t\treturn requested - moved;\n\t}\n\n\tscrollToStart(): void {\n\t\tconst changed =\n\t\t\tthis.currentScrollTop !== 0 ||\n\t\t\tthis.followingEnd !== (this.followEnd && this.contentHeight <= this.currentViewportHeight);\n\t\tthis.currentScrollTop = 0;\n\t\tthis.followingEnd = this.followEnd && this.contentHeight <= this.currentViewportHeight;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t}\n\t\tthis.notifyScroll();\n\t\tif (changed) this.requestRenderCallback?.();\n\t}\n\n\tscrollToEnd(): void {\n\t\tconst next = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst changed = this.currentScrollTop !== next || this.followingEnd !== this.followEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t}\n\n\tupdateLayout(contentHeight: number, viewportHeight: number, requestRender: () => void): void {\n\t\tthis.contentHeight = Math.max(0, Math.floor(contentHeight));\n\t\tthis.currentViewportHeight = Math.max(0, Math.floor(viewportHeight));\n\t\tthis.requestRenderCallback = requestRender;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tif (this.followingEnd) this.currentScrollTop = maxScrollTop;\n\t\telse this.currentScrollTop = Math.max(0, Math.min(this.currentScrollTop, maxScrollTop));\n\t\tif (this.followEnd && this.currentScrollTop === maxScrollTop) this.followingEnd = true;\n\t\tif (this.contentHeight <= this.currentViewportHeight) this.hideTransientScrollbar();\n\t}\n\n\toverride addChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView has exactly one child\");\n\t}\n\n\toverride removeChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView child cannot be removed\");\n\t}\n\n\toverride clear(): void {\n\t\tthrow new Error(\"ScrollView child cannot be cleared\");\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst contentWidth = this.getContentWidth(width);\n\t\tconst lines = this.child.render(contentWidth);\n\t\treturn contentWidth === width ? lines : lines.map((line) => `${line} `);\n\t}\n\n\t[LAYOUT_NODE](): ScrollLayoutNode {\n\t\treturn { type: \"scroll\", component: this.child, state: this };\n\t}\n}\n"]}
1
+ {"version":3,"file":"scroll-view.js","sourceRoot":"","sources":["../../src/components/scroll-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { LAYOUT_NODE, type ScrollLayoutNode } from \"../layout-node.ts\";\nimport { type Component, Container } from \"../tui.ts\";\n\nexport type ScrollViewScrollbar = \"hidden\" | \"auto\" | \"always\";\n\nexport interface ScrollViewOptions {\n\taxis?: \"vertical\";\n\tfollow?: \"none\" | \"end\";\n\tonScroll?: (scrollTop: number) => void;\n\tprimary?: boolean;\n\toverscroll?: \"chain\" | \"contain\";\n\tscrollbar?: ScrollViewScrollbar;\n\tscrollbarStyle?: (text: string) => string;\n\tscrollbarHideDelayMs?: number;\n}\n\nexport interface ScrollViewScrollToOptions {\n\t/** Keep follow-end disabled even when the target is the current content end. */\n\tdisableFollow?: boolean;\n}\n\nexport class ScrollView extends Container {\n\tprivate readonly child: Component;\n\tprivate readonly followEnd: boolean;\n\tprivate readonly onScroll?: (scrollTop: number) => void;\n\treadonly primary: boolean;\n\treadonly overscroll: \"chain\" | \"contain\";\n\treadonly scrollbarStyle: (text: string) => string;\n\tprivate currentScrollbar: ScrollViewScrollbar;\n\tprivate readonly scrollbarHideDelayMs: number;\n\tprivate currentScrollTop = 0;\n\tprivate contentHeight = 0;\n\tprivate currentViewportHeight = 0;\n\tprivate followingEnd: boolean;\n\tprivate followSuppressedAtEnd = false;\n\tprivate requestRenderCallback: (() => void) | undefined;\n\tprivate transientScrollbarVisible = false;\n\tprivate scrollbarActive = false;\n\tprivate scrollbarHideTimer: NodeJS.Timeout | undefined;\n\n\tconstructor(component: Component, options: ScrollViewOptions = {}) {\n\t\tsuper();\n\t\tif (options.axis !== undefined && options.axis !== \"vertical\") {\n\t\t\tthrow new Error(`Unsupported ScrollView axis: ${options.axis}`);\n\t\t}\n\t\tthis.child = component;\n\t\tthis.children.push(component);\n\t\tthis.followEnd = (options.follow ?? \"none\") === \"end\";\n\t\tthis.onScroll = options.onScroll;\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.primary = options.primary ?? false;\n\t\tthis.overscroll = options.overscroll ?? \"chain\";\n\t\tthis.currentScrollbar = options.scrollbar ?? \"hidden\";\n\t\tthis.scrollbarStyle = options.scrollbarStyle ?? ((text) => `\\x1b[100m${text}\\x1b[49m`);\n\t\tthis.scrollbarHideDelayMs = Math.max(0, Math.floor(options.scrollbarHideDelayMs ?? 1000));\n\t}\n\n\tget scrollTop(): number {\n\t\treturn this.currentScrollTop;\n\t}\n\n\tget isFollowingEnd(): boolean {\n\t\treturn this.followingEnd;\n\t}\n\n\tget viewportHeight(): number {\n\t\treturn this.currentViewportHeight;\n\t}\n\n\tget scrollbar(): ScrollViewScrollbar {\n\t\treturn this.currentScrollbar;\n\t}\n\n\tget isScrollbarVisible(): boolean {\n\t\tif (this.scrollbar === \"always\") return this.currentViewportHeight > 0;\n\t\treturn (\n\t\t\tthis.scrollbar === \"auto\" && this.contentHeight > this.currentViewportHeight && this.transientScrollbarVisible\n\t\t);\n\t}\n\n\tsetScrollbar(scrollbar: ScrollViewScrollbar): void {\n\t\tif (scrollbar === this.currentScrollbar) return;\n\t\tthis.currentScrollbar = scrollbar;\n\t\tif (scrollbar !== \"auto\") this.hideTransientScrollbar();\n\t\telse if (this.scrollbarActive) this.markScrollbarActivity();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tgetContentWidth(width: number): number {\n\t\treturn this.scrollbar === \"always\" && width > 1 ? width - 1 : width;\n\t}\n\n\tprivate markScrollbarActivity(): void {\n\t\tif (this.scrollbar !== \"auto\" || this.contentHeight <= this.currentViewportHeight) return;\n\t\tthis.transientScrollbarVisible = true;\n\t\tif (this.scrollbarHideTimer) {\n\t\t\tclearTimeout(this.scrollbarHideTimer);\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t}\n\t\tif (this.scrollbarActive) return;\n\t\tthis.scrollbarHideTimer = setTimeout(() => {\n\t\t\tthis.scrollbarHideTimer = undefined;\n\t\t\tthis.transientScrollbarVisible = false;\n\t\t\tthis.requestRenderCallback?.();\n\t\t}, this.scrollbarHideDelayMs);\n\t\tthis.scrollbarHideTimer.unref();\n\t}\n\n\tprivate hideTransientScrollbar(): void {\n\t\tthis.transientScrollbarVisible = false;\n\t\tif (!this.scrollbarHideTimer) return;\n\t\tclearTimeout(this.scrollbarHideTimer);\n\t\tthis.scrollbarHideTimer = undefined;\n\t}\n\n\tprivate notifyScroll(): void {\n\t\tthis.onScroll?.(this.currentScrollTop);\n\t}\n\n\tsetScrollbarActive(active: boolean): void {\n\t\tif (active === this.scrollbarActive) return;\n\t\tthis.scrollbarActive = active;\n\t\tthis.markScrollbarActivity();\n\t}\n\n\tscrollTo(scrollTop: number, options: ScrollViewScrollToOptions = {}): void {\n\t\tconst requested = Number.isFinite(scrollTop) ? Math.trunc(scrollTop) : this.currentScrollTop;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, requested));\n\t\tconst nextFollowSuppressedAtEnd = options.disableFollow === true && next === maxScrollTop;\n\t\tconst nextFollowingEnd = !nextFollowSuppressedAtEnd && this.followEnd && next === maxScrollTop;\n\t\tif (\n\t\t\tnext === this.currentScrollTop &&\n\t\t\tnextFollowingEnd === this.followingEnd &&\n\t\t\tnextFollowSuppressedAtEnd === this.followSuppressedAtEnd\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tconst moved = next !== this.currentScrollTop;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = nextFollowingEnd;\n\t\tthis.followSuppressedAtEnd = nextFollowSuppressedAtEnd;\n\t\tif (moved) this.markScrollbarActivity();\n\t\tthis.notifyScroll();\n\t\tthis.requestRenderCallback?.();\n\t}\n\n\tscrollBy(lines: number): number {\n\t\tconst requested = Number.isFinite(lines) ? Math.trunc(lines) : 0;\n\t\tif (requested === 0) return 0;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst start = this.followingEnd ? maxScrollTop : this.currentScrollTop;\n\t\tconst next = Math.max(0, Math.min(maxScrollTop, start + requested));\n\t\tconst moved = next - start;\n\t\tconst wasFollowingEnd = this.followingEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd && next === maxScrollTop;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (moved !== 0) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.notifyScroll();\n\t\t\tthis.requestRenderCallback?.();\n\t\t} else if (requested < 0 && start === 0) {\n\t\t\tthis.notifyScroll();\n\t\t} else if (this.followingEnd !== wasFollowingEnd) {\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t\treturn requested - moved;\n\t}\n\n\tscrollToStart(): void {\n\t\tconst changed =\n\t\t\tthis.currentScrollTop !== 0 ||\n\t\t\tthis.followingEnd !== (this.followEnd && this.contentHeight <= this.currentViewportHeight);\n\t\tthis.currentScrollTop = 0;\n\t\tthis.followingEnd = this.followEnd && this.contentHeight <= this.currentViewportHeight;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t}\n\t\tthis.notifyScroll();\n\t\tif (changed) this.requestRenderCallback?.();\n\t}\n\n\tscrollToEnd(): void {\n\t\tconst next = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tconst changed = this.currentScrollTop !== next || this.followingEnd !== this.followEnd;\n\t\tthis.currentScrollTop = next;\n\t\tthis.followingEnd = this.followEnd;\n\t\tthis.followSuppressedAtEnd = false;\n\t\tif (changed) {\n\t\t\tthis.markScrollbarActivity();\n\t\t\tthis.requestRenderCallback?.();\n\t\t}\n\t}\n\n\tupdateLayout(contentHeight: number, viewportHeight: number, requestRender: () => void): void {\n\t\tthis.contentHeight = Math.max(0, Math.floor(contentHeight));\n\t\tthis.currentViewportHeight = Math.max(0, Math.floor(viewportHeight));\n\t\tthis.requestRenderCallback = requestRender;\n\t\tconst maxScrollTop = Math.max(0, this.contentHeight - this.currentViewportHeight);\n\t\tif (this.followingEnd) this.currentScrollTop = maxScrollTop;\n\t\telse this.currentScrollTop = Math.max(0, Math.min(this.currentScrollTop, maxScrollTop));\n\t\tif (this.currentScrollTop < maxScrollTop) this.followSuppressedAtEnd = false;\n\t\tif (this.followEnd && this.currentScrollTop === maxScrollTop && !this.followSuppressedAtEnd) {\n\t\t\tthis.followingEnd = true;\n\t\t}\n\t\tif (this.contentHeight <= this.currentViewportHeight) this.hideTransientScrollbar();\n\t}\n\n\toverride addChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView has exactly one child\");\n\t}\n\n\toverride removeChild(_component: Component): void {\n\t\tthrow new Error(\"ScrollView child cannot be removed\");\n\t}\n\n\toverride clear(): void {\n\t\tthrow new Error(\"ScrollView child cannot be cleared\");\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst contentWidth = this.getContentWidth(width);\n\t\tconst lines = this.child.render(contentWidth);\n\t\treturn contentWidth === width ? lines : lines.map((line) => `${line} `);\n\t}\n\n\t[LAYOUT_NODE](): ScrollLayoutNode {\n\t\treturn { type: \"scroll\", component: this.child, state: this };\n\t}\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { Image, type ImageOptions, type ImageTheme } from "./components/image.ts
8
8
  export { Input } from "./components/input.ts";
9
9
  export { Loader, type LoaderIndicatorOptions } from "./components/loader.ts";
10
10
  export { type DefaultTextStyle, Markdown, type MarkdownOptions, type MarkdownTheme } from "./components/markdown.ts";
11
- export { ScrollView, type ScrollViewOptions, type ScrollViewScrollbar } from "./components/scroll-view.ts";
11
+ export { ScrollView, type ScrollViewOptions, type ScrollViewScrollbar, type ScrollViewScrollToOptions, } from "./components/scroll-view.ts";
12
12
  export { type SelectItem, SelectList, type SelectListLayoutOptions, type SelectListTheme, type SelectListTruncatePrimaryContext, } from "./components/select-list.ts";
13
13
  export { type SettingItem, SettingsList, type SettingsListTheme } from "./components/settings-list.ts";
14
14
  export { Spacer } from "./components/spacer.ts";
@@ -1 +1 @@
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\nexport { Marked, type Token, type Tokens } from \"marked\";\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 { HStack } from \"./components/h-stack.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 { ScrollView, type ScrollViewOptions, type ScrollViewScrollbar } from \"./components/scroll-view.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\";\nexport {\n\ttype StackChild,\n\ttype StackEntry,\n\ttype StackEntryOptions,\n\ttype StackOptions,\n\tVStack,\n} from \"./components/v-stack.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\";\n// LaTeX rendering\nexport { type RenderLatexOptions, renderLatex } from \"./latex.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 colors\nexport {\n\tparseOsc11BackgroundColor,\n\tparseTerminalColorSchemeReport,\n\ttype RgbColor,\n\ttype TerminalColorScheme,\n} from \"./terminal-colors.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 {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\tcompositeTuiLine,\n\ttype Focusable,\n\tisFocusable,\n\tisViewportTUI,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\ttype TUI,\n\ttype TuiInputListener,\n\ttype TuiInputListenerResult,\n\ttype TuiMode,\n\ttype TuiStopOptions,\n\ttype ViewportTUI,\n} from \"./tui.ts\";\nexport { TuiAltScreen, type TuiAltScreenOptions } from \"./tui-alt-screen.ts\";\nexport { TuiMainScreen, type TuiMainScreenRenderState } from \"./tui-main-screen.ts\";\n// Utilities\nexport {\n\tgetOsc8LinkAtColumn,\n\tsliceByColumn,\n\tstripTerminalSequences,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"./utils.ts\";\n"]}
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\nexport { Marked, type Token, type Tokens } from \"marked\";\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 { HStack } from \"./components/h-stack.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\tScrollView,\n\ttype ScrollViewOptions,\n\ttype ScrollViewScrollbar,\n\ttype ScrollViewScrollToOptions,\n} from \"./components/scroll-view.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\";\nexport {\n\ttype StackChild,\n\ttype StackEntry,\n\ttype StackEntryOptions,\n\ttype StackOptions,\n\tVStack,\n} from \"./components/v-stack.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\";\n// LaTeX rendering\nexport { type RenderLatexOptions, renderLatex } from \"./latex.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 colors\nexport {\n\tparseOsc11BackgroundColor,\n\tparseTerminalColorSchemeReport,\n\ttype RgbColor,\n\ttype TerminalColorScheme,\n} from \"./terminal-colors.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 {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\tcompositeTuiLine,\n\ttype Focusable,\n\tisFocusable,\n\tisViewportTUI,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\ttype TUI,\n\ttype TuiInputListener,\n\ttype TuiInputListenerResult,\n\ttype TuiMode,\n\ttype TuiStopOptions,\n\ttype ViewportTUI,\n} from \"./tui.ts\";\nexport { TuiAltScreen, type TuiAltScreenOptions } from \"./tui-alt-screen.ts\";\nexport { TuiMainScreen, type TuiMainScreenRenderState } from \"./tui-main-screen.ts\";\n// Utilities\nexport {\n\tgetOsc8LinkAtColumn,\n\tsliceByColumn,\n\tstripTerminalSequences,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"./utils.ts\";\n"]}
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ export { Image } from "./components/image.js";
11
11
  export { Input } from "./components/input.js";
12
12
  export { Loader } from "./components/loader.js";
13
13
  export { Markdown } from "./components/markdown.js";
14
- export { ScrollView } from "./components/scroll-view.js";
14
+ export { ScrollView, } from "./components/scroll-view.js";
15
15
  export { SelectList, } from "./components/select-list.js";
16
16
  export { SettingsList } from "./components/settings-list.js";
17
17
  export { Spacer } from "./components/spacer.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,OAAO,EAAE,MAAM,EAA2B,MAAM,QAAQ,CAAC;AACzD,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,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,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,EAAE,UAAU,EAAoD,MAAM,6BAA6B,CAAC;AAC3G,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;AAC/D,OAAO,EAKN,MAAM,GACN,MAAM,yBAAyB,CAAC;AAGjC,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,kBAAkB;AAClB,OAAO,EAA2B,WAAW,EAAE,MAAM,YAAY,CAAC;AAClE,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,kBAAkB;AAClB,OAAO,EACN,yBAAyB,EACzB,8BAA8B,GAG9B,MAAM,sBAAsB,CAAC;AAC9B,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,EAEN,SAAS,EACT,aAAa,EACb,gBAAgB,EAEhB,WAAW,EACX,aAAa,GAab,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAA4B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAiC,MAAM,sBAAsB,CAAC;AACpF,YAAY;AACZ,OAAO,EACN,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,YAAY,EACZ,gBAAgB,GAChB,MAAM,YAAY,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\nexport { Marked, type Token, type Tokens } from \"marked\";\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 { HStack } from \"./components/h-stack.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 { ScrollView, type ScrollViewOptions, type ScrollViewScrollbar } from \"./components/scroll-view.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\";\nexport {\n\ttype StackChild,\n\ttype StackEntry,\n\ttype StackEntryOptions,\n\ttype StackOptions,\n\tVStack,\n} from \"./components/v-stack.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\";\n// LaTeX rendering\nexport { type RenderLatexOptions, renderLatex } from \"./latex.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 colors\nexport {\n\tparseOsc11BackgroundColor,\n\tparseTerminalColorSchemeReport,\n\ttype RgbColor,\n\ttype TerminalColorScheme,\n} from \"./terminal-colors.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 {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\tcompositeTuiLine,\n\ttype Focusable,\n\tisFocusable,\n\tisViewportTUI,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\ttype TUI,\n\ttype TuiInputListener,\n\ttype TuiInputListenerResult,\n\ttype TuiMode,\n\ttype TuiStopOptions,\n\ttype ViewportTUI,\n} from \"./tui.ts\";\nexport { TuiAltScreen, type TuiAltScreenOptions } from \"./tui-alt-screen.ts\";\nexport { TuiMainScreen, type TuiMainScreenRenderState } from \"./tui-main-screen.ts\";\n// Utilities\nexport {\n\tgetOsc8LinkAtColumn,\n\tsliceByColumn,\n\tstripTerminalSequences,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"./utils.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,OAAO,EAAE,MAAM,EAA2B,MAAM,QAAQ,CAAC;AACzD,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,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,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,EACN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,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;AAC/D,OAAO,EAKN,MAAM,GACN,MAAM,yBAAyB,CAAC;AAGjC,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,kBAAkB;AAClB,OAAO,EAA2B,WAAW,EAAE,MAAM,YAAY,CAAC;AAClE,sCAAsC;AACtC,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,kBAAkB;AAClB,OAAO,EACN,yBAAyB,EACzB,8BAA8B,GAG9B,MAAM,sBAAsB,CAAC;AAC9B,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,EAEN,SAAS,EACT,aAAa,EACb,gBAAgB,EAEhB,WAAW,EACX,aAAa,GAab,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAA4B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAiC,MAAM,sBAAsB,CAAC;AACpF,YAAY;AACZ,OAAO,EACN,mBAAmB,EACnB,aAAa,EACb,sBAAsB,EACtB,eAAe,EACf,YAAY,EACZ,gBAAgB,GAChB,MAAM,YAAY,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\nexport { Marked, type Token, type Tokens } from \"marked\";\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 { HStack } from \"./components/h-stack.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\tScrollView,\n\ttype ScrollViewOptions,\n\ttype ScrollViewScrollbar,\n\ttype ScrollViewScrollToOptions,\n} from \"./components/scroll-view.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\";\nexport {\n\ttype StackChild,\n\ttype StackEntry,\n\ttype StackEntryOptions,\n\ttype StackOptions,\n\tVStack,\n} from \"./components/v-stack.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\";\n// LaTeX rendering\nexport { type RenderLatexOptions, renderLatex } from \"./latex.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 colors\nexport {\n\tparseOsc11BackgroundColor,\n\tparseTerminalColorSchemeReport,\n\ttype RgbColor,\n\ttype TerminalColorScheme,\n} from \"./terminal-colors.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 {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\tcompositeTuiLine,\n\ttype Focusable,\n\tisFocusable,\n\tisViewportTUI,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype OverlayUnfocusOptions,\n\ttype SizeValue,\n\ttype TUI,\n\ttype TuiInputListener,\n\ttype TuiInputListenerResult,\n\ttype TuiMode,\n\ttype TuiStopOptions,\n\ttype ViewportTUI,\n} from \"./tui.ts\";\nexport { TuiAltScreen, type TuiAltScreenOptions } from \"./tui-alt-screen.ts\";\nexport { TuiMainScreen, type TuiMainScreenRenderState } from \"./tui-main-screen.ts\";\n// Utilities\nexport {\n\tgetOsc8LinkAtColumn,\n\tsliceByColumn,\n\tstripTerminalSequences,\n\ttruncateToWidth,\n\tvisibleWidth,\n\twrapTextWithAnsi,\n} from \"./utils.ts\";\n"]}
@@ -41,8 +41,14 @@ export interface Keybindings {
41
41
  "tui.altScreen.pageDown": true;
42
42
  "tui.altScreen.halfPageUp": true;
43
43
  "tui.altScreen.halfPageDown": true;
44
+ "tui.altScreen.lineUp": true;
45
+ "tui.altScreen.lineDown": true;
44
46
  "tui.altScreen.previousPrompt": true;
45
47
  "tui.altScreen.nextPrompt": true;
48
+ "tui.altScreen.search": true;
49
+ "tui.altScreen.searchNext": true;
50
+ "tui.altScreen.searchPrevious": true;
51
+ "tui.altScreen.searchClose": true;
46
52
  "tui.altScreen.top": true;
47
53
  "tui.altScreen.bottom": true;
48
54
  }
@@ -202,6 +208,14 @@ export declare const TUI_KEYBINDINGS: {
202
208
  readonly defaultKeys: [];
203
209
  readonly description: "Scroll viewport down half a page";
204
210
  };
211
+ readonly "tui.altScreen.lineUp": {
212
+ readonly defaultKeys: [];
213
+ readonly description: "Scroll viewport up one line";
214
+ };
215
+ readonly "tui.altScreen.lineDown": {
216
+ readonly defaultKeys: [];
217
+ readonly description: "Scroll viewport down one line";
218
+ };
205
219
  readonly "tui.altScreen.previousPrompt": {
206
220
  readonly defaultKeys: "ctrl+shift+up";
207
221
  readonly description: "Jump to previous semantic prompt";
@@ -210,6 +224,22 @@ export declare const TUI_KEYBINDINGS: {
210
224
  readonly defaultKeys: "ctrl+shift+down";
211
225
  readonly description: "Jump to next semantic prompt";
212
226
  };
227
+ readonly "tui.altScreen.search": {
228
+ readonly defaultKeys: "ctrl+shift+f";
229
+ readonly description: "Search the primary scroll view";
230
+ };
231
+ readonly "tui.altScreen.searchNext": {
232
+ readonly defaultKeys: ["enter", "ctrl+g"];
233
+ readonly description: "Select the next search match";
234
+ };
235
+ readonly "tui.altScreen.searchPrevious": {
236
+ readonly defaultKeys: ["shift+enter", "ctrl+shift+g"];
237
+ readonly description: "Select the previous search match";
238
+ };
239
+ readonly "tui.altScreen.searchClose": {
240
+ readonly defaultKeys: "escape";
241
+ readonly description: "Close transcript search";
242
+ };
213
243
  readonly "tui.altScreen.top": {
214
244
  readonly defaultKeys: "home";
215
245
  readonly description: "Scroll viewport to top";