@ny4n/tui 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +948 -2
- package/dist/alt-screen-search.d.ts +46 -0
- package/dist/alt-screen-search.d.ts.map +1 -0
- package/dist/alt-screen-search.js +282 -0
- package/dist/alt-screen-search.js.map +1 -0
- package/dist/autocomplete.d.ts +57 -0
- package/dist/autocomplete.d.ts.map +1 -0
- package/dist/autocomplete.js +693 -0
- package/dist/autocomplete.js.map +1 -0
- package/dist/colors.d.ts +73 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +273 -0
- package/dist/colors.js.map +1 -0
- package/dist/components/alt-screen-flash.d.ts +13 -0
- package/dist/components/alt-screen-flash.d.ts.map +1 -0
- package/dist/components/alt-screen-flash.js +37 -0
- package/dist/components/alt-screen-flash.js.map +1 -0
- package/dist/components/box.d.ts +25 -0
- package/dist/components/box.d.ts.map +1 -0
- package/dist/components/box.js +138 -0
- package/dist/components/box.js.map +1 -0
- package/dist/components/cancellable-loader.d.ts +22 -0
- package/dist/components/cancellable-loader.d.ts.map +1 -0
- package/dist/components/cancellable-loader.js +35 -0
- package/dist/components/cancellable-loader.js.map +1 -0
- package/dist/components/editor.d.ts +260 -0
- package/dist/components/editor.d.ts.map +1 -0
- package/dist/components/editor.js +2044 -0
- package/dist/components/editor.js.map +1 -0
- package/dist/components/h-stack.d.ts +7 -0
- package/dist/components/h-stack.d.ts.map +1 -0
- package/dist/components/h-stack.js +43 -0
- package/dist/components/h-stack.js.map +1 -0
- package/dist/components/image.d.ts +40 -0
- package/dist/components/image.d.ts.map +1 -0
- package/dist/components/image.js +123 -0
- package/dist/components/image.js.map +1 -0
- package/dist/components/input.d.ts +48 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +417 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/loader.d.ts +33 -0
- package/dist/components/loader.d.ts.map +1 -0
- package/dist/components/loader.js +76 -0
- package/dist/components/loader.js.map +1 -0
- package/dist/components/markdown.d.ts +104 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +821 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/mouse-region.d.ts +12 -0
- package/dist/components/mouse-region.d.ts.map +1 -0
- package/dist/components/mouse-region.js +21 -0
- package/dist/components/mouse-region.js.map +1 -0
- package/dist/components/scroll-view.d.ts +59 -0
- package/dist/components/scroll-view.d.ts.map +1 -0
- package/dist/components/scroll-view.js +195 -0
- package/dist/components/scroll-view.js.map +1 -0
- package/dist/components/select-list.d.ts +53 -0
- package/dist/components/select-list.d.ts.map +1 -0
- package/dist/components/select-list.js +206 -0
- package/dist/components/select-list.js.map +1 -0
- package/dist/components/settings-list.d.ts +60 -0
- package/dist/components/settings-list.d.ts.map +1 -0
- package/dist/components/settings-list.js +252 -0
- package/dist/components/settings-list.js.map +1 -0
- package/dist/components/spacer.d.ts +12 -0
- package/dist/components/spacer.d.ts.map +1 -0
- package/dist/components/spacer.js +23 -0
- package/dist/components/spacer.js.map +1 -0
- package/dist/components/stack.d.ts +32 -0
- package/dist/components/stack.d.ts.map +1 -0
- package/dist/components/stack.js +111 -0
- package/dist/components/stack.js.map +1 -0
- package/dist/components/text.d.ts +20 -0
- package/dist/components/text.d.ts.map +1 -0
- package/dist/components/text.js +95 -0
- package/dist/components/text.js.map +1 -0
- package/dist/components/truncated-text.d.ts +13 -0
- package/dist/components/truncated-text.d.ts.map +1 -0
- package/dist/components/truncated-text.js +51 -0
- package/dist/components/truncated-text.js.map +1 -0
- package/dist/components/v-stack.d.ts +8 -0
- package/dist/components/v-stack.d.ts.map +1 -0
- package/dist/components/v-stack.js +26 -0
- package/dist/components/v-stack.js.map +1 -0
- package/dist/editor-component.d.ts +39 -0
- package/dist/editor-component.d.ts.map +1 -0
- package/dist/editor-component.js +2 -0
- package/dist/editor-component.js.map +1 -0
- package/dist/fuzzy.d.ts +16 -0
- package/dist/fuzzy.d.ts.map +1 -0
- package/dist/fuzzy.js +111 -0
- package/dist/fuzzy.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/keybindings.d.ts +273 -0
- package/dist/keybindings.d.ts.map +1 -0
- package/dist/keybindings.js +233 -0
- package/dist/keybindings.js.map +1 -0
- package/dist/keys.d.ts +184 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +1206 -0
- package/dist/keys.js.map +1 -0
- package/dist/kill-ring.d.ts +28 -0
- package/dist/kill-ring.d.ts.map +1 -0
- package/dist/kill-ring.js +44 -0
- package/dist/kill-ring.js.map +1 -0
- package/dist/latex.d.ts +10 -0
- package/dist/latex.d.ts.map +1 -0
- package/dist/latex.js +1378 -0
- package/dist/latex.js.map +1 -0
- package/dist/layout-node.d.ts +40 -0
- package/dist/layout-node.d.ts.map +1 -0
- package/dist/layout-node.js +6 -0
- package/dist/layout-node.js.map +1 -0
- package/dist/layout.d.ts +42 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +344 -0
- package/dist/layout.js.map +1 -0
- package/dist/native-modifiers.d.ts +4 -0
- package/dist/native-modifiers.d.ts.map +1 -0
- package/dist/native-modifiers.js +13 -0
- package/dist/native-modifiers.js.map +1 -0
- package/dist/native-module-path.d.ts +7 -0
- package/dist/native-module-path.d.ts.map +1 -0
- package/dist/native-module-path.js +19 -0
- package/dist/native-module-path.js.map +1 -0
- package/dist/native-platform.d.ts +20 -0
- package/dist/native-platform.d.ts.map +1 -0
- package/dist/native-platform.js +42 -0
- package/dist/native-platform.js.map +1 -0
- package/dist/oklab.d.ts +43 -0
- package/dist/oklab.d.ts.map +1 -0
- package/dist/oklab.js +210 -0
- package/dist/oklab.js.map +1 -0
- package/dist/program-status.d.ts +23 -0
- package/dist/program-status.d.ts.map +1 -0
- package/dist/program-status.js +43 -0
- package/dist/program-status.js.map +1 -0
- package/dist/stdin-buffer.d.ts +56 -0
- package/dist/stdin-buffer.d.ts.map +1 -0
- package/dist/stdin-buffer.js +366 -0
- package/dist/stdin-buffer.js.map +1 -0
- package/dist/terminal-colors.d.ts +27 -0
- package/dist/terminal-colors.d.ts.map +1 -0
- package/dist/terminal-colors.js +64 -0
- package/dist/terminal-colors.js.map +1 -0
- package/dist/terminal-image.d.ts +123 -0
- package/dist/terminal-image.d.ts.map +1 -0
- package/dist/terminal-image.js +603 -0
- package/dist/terminal-image.js.map +1 -0
- package/dist/terminal.d.ts +143 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +493 -0
- package/dist/terminal.js.map +1 -0
- package/dist/tui-alt-screen.d.ts +168 -0
- package/dist/tui-alt-screen.d.ts.map +1 -0
- package/dist/tui-alt-screen.js +1552 -0
- package/dist/tui-alt-screen.js.map +1 -0
- package/dist/tui-main-screen.d.ts +39 -0
- package/dist/tui-main-screen.d.ts.map +1 -0
- package/dist/tui-main-screen.js +595 -0
- package/dist/tui-main-screen.js.map +1 -0
- package/dist/tui.d.ts +398 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +1096 -0
- package/dist/tui.js.map +1 -0
- package/dist/undo-stack.d.ts +17 -0
- package/dist/undo-stack.d.ts.map +1 -0
- package/dist/undo-stack.js +25 -0
- package/dist/undo-stack.js.map +1 -0
- package/dist/utils.d.ts +107 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +1263 -0
- package/dist/utils.js.map +1 -0
- package/dist/wheel-scroll.d.ts +23 -0
- package/dist/wheel-scroll.d.ts.map +1 -0
- package/dist/wheel-scroll.js +73 -0
- package/dist/wheel-scroll.js.map +1 -0
- package/dist/word-navigation.d.ts +25 -0
- package/dist/word-navigation.d.ts.map +1 -0
- package/dist/word-navigation.js +96 -0
- package/dist/word-navigation.js.map +1 -0
- package/native/clipboard.h +208 -0
- package/native/darwin/README.md +22 -0
- package/native/darwin/build.sh +69 -0
- package/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/native/darwin/src/darwin-platform.m +132 -0
- package/native/linux/README.md +23 -0
- package/native/linux/build.sh +65 -0
- package/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
- package/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
- package/native/linux/src/clipboard-worker.h +70 -0
- package/native/linux/src/linux-platform-x11.c +274 -0
- package/native/napi.h +91 -0
- package/native/win32/README.md +31 -0
- package/native/win32/build.mjs +232 -0
- package/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
- package/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
- package/native/win32/src/win32-platform.c +241 -0
- package/package.json +61 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spacer.js","sourceRoot":"","sources":["../../src/components/spacer.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,MAAM;IACV,KAAK,CAAS;IAEtB,YAAY,KAAK,GAAW,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,KAAa;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,UAAU;QACT,0CAA0C;IAC3C,CAAC;IAED,MAAM,CAAC,MAAc;QACpB,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;IACf,CAAC;CACD","sourcesContent":["import 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\n\tconstructor(lines: number = 1) {\n\t\tthis.lines = lines;\n\t}\n\n\tsetLines(lines: number): void {\n\t\tthis.lines = lines;\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"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { LAYOUT_NODE, type LayoutViewport, type StackLayoutEntry, type StackLayoutNode } from "../layout-node.ts";
|
|
2
|
+
import { type Component, Container } from "../tui.ts";
|
|
3
|
+
export interface StackEntryOptions {
|
|
4
|
+
basis?: number | "auto";
|
|
5
|
+
grow?: number;
|
|
6
|
+
shrink?: number;
|
|
7
|
+
minSize?: number;
|
|
8
|
+
maxSize?: number;
|
|
9
|
+
visible?: (viewport: LayoutViewport) => boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface StackEntry extends StackEntryOptions {
|
|
12
|
+
component: Component;
|
|
13
|
+
}
|
|
14
|
+
export type StackChild = Component | StackEntry;
|
|
15
|
+
export interface StackOptions {
|
|
16
|
+
gap?: number;
|
|
17
|
+
align?: "stretch" | "start" | "center" | "end";
|
|
18
|
+
}
|
|
19
|
+
export declare abstract class Stack extends Container {
|
|
20
|
+
protected readonly entries: StackLayoutEntry[];
|
|
21
|
+
protected readonly gap: number;
|
|
22
|
+
protected readonly align: "stretch" | "start" | "center" | "end";
|
|
23
|
+
protected abstract readonly layoutType: "vstack" | "hstack";
|
|
24
|
+
constructor(children?: StackChild[], options?: StackOptions);
|
|
25
|
+
addChild(component: Component, options?: StackEntryOptions): void;
|
|
26
|
+
removeChild(component: Component): void;
|
|
27
|
+
clear(): void;
|
|
28
|
+
[LAYOUT_NODE](): StackLayoutNode;
|
|
29
|
+
}
|
|
30
|
+
export declare function visibleStackEntries(entries: readonly StackLayoutEntry[], viewport: LayoutViewport): StackLayoutEntry[];
|
|
31
|
+
export declare function allocateStackSizes(entries: readonly StackLayoutEntry[], intrinsicSizes: readonly number[], availableSize: number | undefined, gap: number): number[];
|
|
32
|
+
//# sourceMappingURL=stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../src/components/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAClH,OAAO,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC;CAChD;AAED,MAAM,WAAW,UAAW,SAAQ,iBAAiB;IACpD,SAAS,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;AAEhD,MAAM,WAAW,YAAY;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;CAC/C;AAUD,8BAAsB,KAAM,SAAQ,SAAS;IAC5C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAM;IACpD,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjE,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAE5D,YAAY,QAAQ,GAAE,UAAU,EAAO,EAAE,OAAO,GAAE,YAAiB,EAQlE;IAEQ,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,GAAE,iBAAsB,GAAG,IAAI,CAW7E;IAEQ,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAI/C;IAEQ,KAAK,IAAI,IAAI,CAGrB;IAED,CAAC,WAAW,CAAC,IAAI,eAAe,CAO/B;CACD;AAED,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,QAAQ,EAAE,cAAc,GACtB,gBAAgB,EAAE,CAEpB;AAgDD,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,GAAG,EAAE,MAAM,GACT,MAAM,EAAE,CAcV"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { LAYOUT_NODE } from "../layout-node.js";
|
|
2
|
+
import { Container } from "../tui.js";
|
|
3
|
+
function isStackEntry(child) {
|
|
4
|
+
return !("render" in child);
|
|
5
|
+
}
|
|
6
|
+
function normalizeSize(value, fallback) {
|
|
7
|
+
return value === undefined || !Number.isFinite(value) ? fallback : Math.max(0, Math.floor(value));
|
|
8
|
+
}
|
|
9
|
+
export class Stack extends Container {
|
|
10
|
+
entries = [];
|
|
11
|
+
gap;
|
|
12
|
+
align;
|
|
13
|
+
constructor(children = [], options = {}) {
|
|
14
|
+
super();
|
|
15
|
+
this.gap = normalizeSize(options.gap, 0);
|
|
16
|
+
this.align = options.align ?? "stretch";
|
|
17
|
+
for (const child of children) {
|
|
18
|
+
if (isStackEntry(child))
|
|
19
|
+
this.addChild(child.component, child);
|
|
20
|
+
else
|
|
21
|
+
this.addChild(child);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
addChild(component, options = {}) {
|
|
25
|
+
super.addChild(component);
|
|
26
|
+
this.entries.push({
|
|
27
|
+
component,
|
|
28
|
+
...(options.basis === undefined ? {} : { basis: options.basis }),
|
|
29
|
+
...(options.grow === undefined ? {} : { grow: normalizeSize(options.grow, 0) }),
|
|
30
|
+
...(options.shrink === undefined ? {} : { shrink: normalizeSize(options.shrink, 1) }),
|
|
31
|
+
...(options.minSize === undefined ? {} : { minSize: normalizeSize(options.minSize, 0) }),
|
|
32
|
+
...(options.maxSize === undefined ? {} : { maxSize: normalizeSize(options.maxSize, Number.MAX_SAFE_INTEGER) }),
|
|
33
|
+
...(options.visible === undefined ? {} : { visible: options.visible }),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
removeChild(component) {
|
|
37
|
+
super.removeChild(component);
|
|
38
|
+
const index = this.entries.findIndex((entry) => entry.component === component);
|
|
39
|
+
if (index !== -1)
|
|
40
|
+
this.entries.splice(index, 1);
|
|
41
|
+
}
|
|
42
|
+
clear() {
|
|
43
|
+
super.clear();
|
|
44
|
+
this.entries.length = 0;
|
|
45
|
+
}
|
|
46
|
+
[LAYOUT_NODE]() {
|
|
47
|
+
return {
|
|
48
|
+
type: this.layoutType,
|
|
49
|
+
entries: this.entries,
|
|
50
|
+
gap: this.gap,
|
|
51
|
+
align: this.align,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function visibleStackEntries(entries, viewport) {
|
|
56
|
+
return entries.filter((entry) => entry.visible?.(viewport) ?? true);
|
|
57
|
+
}
|
|
58
|
+
function clampSize(size, entry) {
|
|
59
|
+
const min = Math.max(0, Math.floor(entry.minSize ?? 0));
|
|
60
|
+
const max = Math.max(min, Math.floor(entry.maxSize ?? Number.MAX_SAFE_INTEGER));
|
|
61
|
+
return Math.max(min, Math.min(max, Math.max(0, Math.floor(size))));
|
|
62
|
+
}
|
|
63
|
+
function distribute(sizes, entries, amount, mode) {
|
|
64
|
+
let remaining = amount;
|
|
65
|
+
while (remaining > 0) {
|
|
66
|
+
const candidates = entries
|
|
67
|
+
.map((entry, index) => ({ entry, index }))
|
|
68
|
+
.filter(({ entry, index }) => {
|
|
69
|
+
if (mode === "grow") {
|
|
70
|
+
return (entry.grow ?? 0) > 0 && sizes[index] < (entry.maxSize ?? Number.MAX_SAFE_INTEGER);
|
|
71
|
+
}
|
|
72
|
+
return (entry.shrink ?? 1) > 0 && sizes[index] > (entry.minSize ?? 0);
|
|
73
|
+
});
|
|
74
|
+
if (candidates.length === 0)
|
|
75
|
+
return;
|
|
76
|
+
const totalWeight = candidates.reduce((sum, { entry, index }) => {
|
|
77
|
+
return sum + (mode === "grow" ? (entry.grow ?? 0) : (entry.shrink ?? 1) * Math.max(1, sizes[index]));
|
|
78
|
+
}, 0);
|
|
79
|
+
let distributed = 0;
|
|
80
|
+
for (const { entry, index } of candidates) {
|
|
81
|
+
if (remaining <= 0)
|
|
82
|
+
break;
|
|
83
|
+
const weight = mode === "grow" ? (entry.grow ?? 0) : (entry.shrink ?? 1) * Math.max(1, sizes[index]);
|
|
84
|
+
const proposed = Math.max(1, Math.floor((remaining * weight) / totalWeight));
|
|
85
|
+
const capacity = mode === "grow"
|
|
86
|
+
? (entry.maxSize ?? Number.MAX_SAFE_INTEGER) - sizes[index]
|
|
87
|
+
: sizes[index] - (entry.minSize ?? 0);
|
|
88
|
+
const delta = Math.min(remaining, proposed, capacity);
|
|
89
|
+
if (delta <= 0)
|
|
90
|
+
continue;
|
|
91
|
+
sizes[index] = sizes[index] + (mode === "grow" ? delta : -delta);
|
|
92
|
+
remaining -= delta;
|
|
93
|
+
distributed += delta;
|
|
94
|
+
}
|
|
95
|
+
if (distributed === 0)
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export function allocateStackSizes(entries, intrinsicSizes, availableSize, gap) {
|
|
100
|
+
const sizes = entries.map((entry, index) => clampSize(entry.basis === undefined || entry.basis === "auto" ? (intrinsicSizes[index] ?? 0) : entry.basis, entry));
|
|
101
|
+
if (availableSize === undefined)
|
|
102
|
+
return sizes;
|
|
103
|
+
const contentSize = Math.max(0, Math.floor(availableSize) - Math.max(0, entries.length - 1) * gap);
|
|
104
|
+
const total = sizes.reduce((sum, size) => sum + size, 0);
|
|
105
|
+
if (total < contentSize)
|
|
106
|
+
distribute(sizes, entries, contentSize - total, "grow");
|
|
107
|
+
else if (total > contentSize)
|
|
108
|
+
distribute(sizes, entries, total - contentSize, "shrink");
|
|
109
|
+
return sizes;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack.js","sourceRoot":"","sources":["../../src/components/stack.ts"],"names":[],"mappings":"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","sourcesContent":["import { LAYOUT_NODE, type LayoutViewport, type StackLayoutEntry, type StackLayoutNode } from \"../layout-node.ts\";\nimport { type Component, Container } from \"../tui.ts\";\n\nexport interface StackEntryOptions {\n\tbasis?: number | \"auto\";\n\tgrow?: number;\n\tshrink?: number;\n\tminSize?: number;\n\tmaxSize?: number;\n\tvisible?: (viewport: LayoutViewport) => boolean;\n}\n\nexport interface StackEntry extends StackEntryOptions {\n\tcomponent: Component;\n}\n\nexport type StackChild = Component | StackEntry;\n\nexport interface StackOptions {\n\tgap?: number;\n\talign?: \"stretch\" | \"start\" | \"center\" | \"end\";\n}\n\nfunction isStackEntry(child: StackChild): child is StackEntry {\n\treturn !(\"render\" in child);\n}\n\nfunction normalizeSize(value: number | undefined, fallback: number): number {\n\treturn value === undefined || !Number.isFinite(value) ? fallback : Math.max(0, Math.floor(value));\n}\n\nexport abstract class Stack extends Container {\n\tprotected readonly entries: StackLayoutEntry[] = [];\n\tprotected readonly gap: number;\n\tprotected readonly align: \"stretch\" | \"start\" | \"center\" | \"end\";\n\tprotected abstract readonly layoutType: \"vstack\" | \"hstack\";\n\n\tconstructor(children: StackChild[] = [], options: StackOptions = {}) {\n\t\tsuper();\n\t\tthis.gap = normalizeSize(options.gap, 0);\n\t\tthis.align = options.align ?? \"stretch\";\n\t\tfor (const child of children) {\n\t\t\tif (isStackEntry(child)) this.addChild(child.component, child);\n\t\t\telse this.addChild(child);\n\t\t}\n\t}\n\n\toverride addChild(component: Component, options: StackEntryOptions = {}): void {\n\t\tsuper.addChild(component);\n\t\tthis.entries.push({\n\t\t\tcomponent,\n\t\t\t...(options.basis === undefined ? {} : { basis: options.basis }),\n\t\t\t...(options.grow === undefined ? {} : { grow: normalizeSize(options.grow, 0) }),\n\t\t\t...(options.shrink === undefined ? {} : { shrink: normalizeSize(options.shrink, 1) }),\n\t\t\t...(options.minSize === undefined ? {} : { minSize: normalizeSize(options.minSize, 0) }),\n\t\t\t...(options.maxSize === undefined ? {} : { maxSize: normalizeSize(options.maxSize, Number.MAX_SAFE_INTEGER) }),\n\t\t\t...(options.visible === undefined ? {} : { visible: options.visible }),\n\t\t});\n\t}\n\n\toverride removeChild(component: Component): void {\n\t\tsuper.removeChild(component);\n\t\tconst index = this.entries.findIndex((entry) => entry.component === component);\n\t\tif (index !== -1) this.entries.splice(index, 1);\n\t}\n\n\toverride clear(): void {\n\t\tsuper.clear();\n\t\tthis.entries.length = 0;\n\t}\n\n\t[LAYOUT_NODE](): StackLayoutNode {\n\t\treturn {\n\t\t\ttype: this.layoutType,\n\t\t\tentries: this.entries,\n\t\t\tgap: this.gap,\n\t\t\talign: this.align,\n\t\t};\n\t}\n}\n\nexport function visibleStackEntries(\n\tentries: readonly StackLayoutEntry[],\n\tviewport: LayoutViewport,\n): StackLayoutEntry[] {\n\treturn entries.filter((entry) => entry.visible?.(viewport) ?? true);\n}\n\nfunction clampSize(size: number, entry: StackLayoutEntry): number {\n\tconst min = Math.max(0, Math.floor(entry.minSize ?? 0));\n\tconst max = Math.max(min, Math.floor(entry.maxSize ?? Number.MAX_SAFE_INTEGER));\n\treturn Math.max(min, Math.min(max, Math.max(0, Math.floor(size))));\n}\n\nfunction distribute(\n\tsizes: number[],\n\tentries: readonly StackLayoutEntry[],\n\tamount: number,\n\tmode: \"grow\" | \"shrink\",\n): void {\n\tlet remaining = amount;\n\twhile (remaining > 0) {\n\t\tconst candidates = entries\n\t\t\t.map((entry, index) => ({ entry, index }))\n\t\t\t.filter(({ entry, index }) => {\n\t\t\t\tif (mode === \"grow\") {\n\t\t\t\t\treturn (entry.grow ?? 0) > 0 && sizes[index]! < (entry.maxSize ?? Number.MAX_SAFE_INTEGER);\n\t\t\t\t}\n\t\t\t\treturn (entry.shrink ?? 1) > 0 && sizes[index]! > (entry.minSize ?? 0);\n\t\t\t});\n\t\tif (candidates.length === 0) return;\n\n\t\tconst totalWeight = candidates.reduce((sum, { entry, index }) => {\n\t\t\treturn sum + (mode === \"grow\" ? (entry.grow ?? 0) : (entry.shrink ?? 1) * Math.max(1, sizes[index]!));\n\t\t}, 0);\n\t\tlet distributed = 0;\n\t\tfor (const { entry, index } of candidates) {\n\t\t\tif (remaining <= 0) break;\n\t\t\tconst weight = mode === \"grow\" ? (entry.grow ?? 0) : (entry.shrink ?? 1) * Math.max(1, sizes[index]!);\n\t\t\tconst proposed = Math.max(1, Math.floor((remaining * weight) / totalWeight));\n\t\t\tconst capacity =\n\t\t\t\tmode === \"grow\"\n\t\t\t\t\t? (entry.maxSize ?? Number.MAX_SAFE_INTEGER) - sizes[index]!\n\t\t\t\t\t: sizes[index]! - (entry.minSize ?? 0);\n\t\t\tconst delta = Math.min(remaining, proposed, capacity);\n\t\t\tif (delta <= 0) continue;\n\t\t\tsizes[index] = sizes[index]! + (mode === \"grow\" ? delta : -delta);\n\t\t\tremaining -= delta;\n\t\t\tdistributed += delta;\n\t\t}\n\t\tif (distributed === 0) return;\n\t}\n}\n\nexport function allocateStackSizes(\n\tentries: readonly StackLayoutEntry[],\n\tintrinsicSizes: readonly number[],\n\tavailableSize: number | undefined,\n\tgap: number,\n): number[] {\n\tconst sizes = entries.map((entry, index) =>\n\t\tclampSize(\n\t\t\tentry.basis === undefined || entry.basis === \"auto\" ? (intrinsicSizes[index] ?? 0) : entry.basis,\n\t\t\tentry,\n\t\t),\n\t);\n\tif (availableSize === undefined) return sizes;\n\n\tconst contentSize = Math.max(0, Math.floor(availableSize) - Math.max(0, entries.length - 1) * gap);\n\tconst total = sizes.reduce((sum, size) => sum + size, 0);\n\tif (total < contentSize) distribute(sizes, entries, contentSize - total, \"grow\");\n\telse if (total > contentSize) distribute(sizes, entries, total - contentSize, \"shrink\");\n\treturn sizes;\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Component } from "../tui.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Text component - displays multi-line text with word wrapping
|
|
4
|
+
*/
|
|
5
|
+
export declare class Text implements Component {
|
|
6
|
+
private text;
|
|
7
|
+
private paddingX;
|
|
8
|
+
private paddingY;
|
|
9
|
+
private customBgFn?;
|
|
10
|
+
private cachedText?;
|
|
11
|
+
private cachedWidth?;
|
|
12
|
+
private cachedLines?;
|
|
13
|
+
constructor(text?: string, paddingX?: number, paddingY?: number, customBgFn?: (text: string) => string);
|
|
14
|
+
setText(text: string): void;
|
|
15
|
+
setCustomBgFn(customBgFn?: (text: string) => string): void;
|
|
16
|
+
setPaddingX(paddingX: number): void;
|
|
17
|
+
invalidate(): void;
|
|
18
|
+
render(width: number): string[];
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/components/text.ts"],"names":[],"mappings":"AAAA,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;IAE/B,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,CAK1B;IAED,aAAa,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAKzD;IAED,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAGlC;IAED,UAAU,IAAI,IAAI,CAIjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA8D9B;CACD"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { applyBackgroundToLine, flattenLines, visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* Text component - displays multi-line text with word wrapping
|
|
4
|
+
*/
|
|
5
|
+
export class Text {
|
|
6
|
+
text;
|
|
7
|
+
paddingX; // Left/right padding
|
|
8
|
+
paddingY; // Top/bottom padding
|
|
9
|
+
customBgFn;
|
|
10
|
+
// Cache for rendered output
|
|
11
|
+
cachedText;
|
|
12
|
+
cachedWidth;
|
|
13
|
+
cachedLines;
|
|
14
|
+
constructor(text = "", paddingX = 1, paddingY = 1, customBgFn) {
|
|
15
|
+
this.text = text;
|
|
16
|
+
this.paddingX = paddingX;
|
|
17
|
+
this.paddingY = paddingY;
|
|
18
|
+
this.customBgFn = customBgFn;
|
|
19
|
+
}
|
|
20
|
+
setText(text) {
|
|
21
|
+
this.text = text;
|
|
22
|
+
this.cachedText = undefined;
|
|
23
|
+
this.cachedWidth = undefined;
|
|
24
|
+
this.cachedLines = undefined;
|
|
25
|
+
}
|
|
26
|
+
setCustomBgFn(customBgFn) {
|
|
27
|
+
this.customBgFn = customBgFn;
|
|
28
|
+
this.cachedText = undefined;
|
|
29
|
+
this.cachedWidth = undefined;
|
|
30
|
+
this.cachedLines = undefined;
|
|
31
|
+
}
|
|
32
|
+
setPaddingX(paddingX) {
|
|
33
|
+
this.paddingX = paddingX;
|
|
34
|
+
this.invalidate();
|
|
35
|
+
}
|
|
36
|
+
invalidate() {
|
|
37
|
+
this.cachedText = undefined;
|
|
38
|
+
this.cachedWidth = undefined;
|
|
39
|
+
this.cachedLines = undefined;
|
|
40
|
+
}
|
|
41
|
+
render(width) {
|
|
42
|
+
// Check cache
|
|
43
|
+
if (this.cachedLines && this.cachedText === this.text && this.cachedWidth === width) {
|
|
44
|
+
return this.cachedLines;
|
|
45
|
+
}
|
|
46
|
+
// Don't render anything if there's no actual text
|
|
47
|
+
if (!this.text || this.text.trim() === "") {
|
|
48
|
+
const result = [];
|
|
49
|
+
this.cachedText = this.text;
|
|
50
|
+
this.cachedWidth = width;
|
|
51
|
+
this.cachedLines = result;
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
// Replace tabs with 3 spaces
|
|
55
|
+
const normalizedText = this.text.replace(/\t/g, " ");
|
|
56
|
+
// Reduce margins when necessary so content and padding fit within the available width.
|
|
57
|
+
const paddingX = Math.min(this.paddingX, Math.max(0, Math.floor((width - 1) / 2)));
|
|
58
|
+
const contentWidth = Math.max(1, width - paddingX * 2);
|
|
59
|
+
// Wrap text (this preserves ANSI codes but does NOT pad)
|
|
60
|
+
const wrappedLines = wrapTextWithAnsi(normalizedText, contentWidth);
|
|
61
|
+
// Add margins and background to each line
|
|
62
|
+
const leftMargin = " ".repeat(paddingX);
|
|
63
|
+
const rightMargin = " ".repeat(paddingX);
|
|
64
|
+
const contentLines = [];
|
|
65
|
+
for (const line of wrappedLines) {
|
|
66
|
+
// Add margins
|
|
67
|
+
const lineWithMargins = leftMargin + line + rightMargin;
|
|
68
|
+
// Apply background if specified (this also pads to full width)
|
|
69
|
+
if (this.customBgFn) {
|
|
70
|
+
contentLines.push(applyBackgroundToLine(lineWithMargins, width, this.customBgFn));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// No background - just pad to width with spaces
|
|
74
|
+
const visibleLen = visibleWidth(lineWithMargins);
|
|
75
|
+
const paddingNeeded = Math.max(0, width - visibleLen);
|
|
76
|
+
contentLines.push(lineWithMargins + " ".repeat(paddingNeeded));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Add top/bottom padding (empty lines)
|
|
80
|
+
const emptyLine = " ".repeat(width);
|
|
81
|
+
const emptyLines = [];
|
|
82
|
+
for (let i = 0; i < this.paddingY; i++) {
|
|
83
|
+
const line = this.customBgFn ? applyBackgroundToLine(emptyLine, width, this.customBgFn) : emptyLine;
|
|
84
|
+
emptyLines.push(line);
|
|
85
|
+
}
|
|
86
|
+
const result = [...emptyLines, ...contentLines, ...emptyLines];
|
|
87
|
+
flattenLines(result);
|
|
88
|
+
// Update cache
|
|
89
|
+
this.cachedText = this.text;
|
|
90
|
+
this.cachedWidth = width;
|
|
91
|
+
this.cachedLines = result;
|
|
92
|
+
return result.length > 0 ? result : [""];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/components/text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAElG;;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;IAE/B,YAAY,IAAI,GAAW,EAAE,EAAE,QAAQ,GAAW,CAAC,EAAE,QAAQ,GAAW,CAAC,EAAE,UAAqC;QAC/G,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;IAC9B,CAAC;IAED,OAAO,CAAC,IAAY;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,aAAa,CAAC,UAAqC;QAClD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,WAAW,CAAC,QAAgB;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,EAAE,CAAC;IACnB,CAAC;IAED,UAAU;QACT,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,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,uFAAuF;QACvF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnF,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;QAEvD,yDAAyD;QACzD,MAAM,YAAY,GAAG,gBAAgB,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QAEpE,0CAA0C;QAC1C,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzC,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;QAC/D,YAAY,CAAC,MAAM,CAAC,CAAC;QAErB,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;IAC1C,CAAC;CACD","sourcesContent":["import type { Component } from \"../tui.ts\";\nimport { applyBackgroundToLine, flattenLines, 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\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\tthis.text = text;\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.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tsetPaddingX(paddingX: number): void {\n\t\tthis.paddingX = paddingX;\n\t\tthis.invalidate();\n\t}\n\n\tinvalidate(): void {\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// Reduce margins when necessary so content and padding fit within the available width.\n\t\tconst paddingX = Math.min(this.paddingX, Math.max(0, Math.floor((width - 1) / 2)));\n\t\tconst contentWidth = Math.max(1, width - 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(paddingX);\n\t\tconst rightMargin = \" \".repeat(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\t\tflattenLines(result);\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"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Component } from "../tui.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Text component that truncates to fit viewport width
|
|
4
|
+
*/
|
|
5
|
+
export declare class TruncatedText implements Component {
|
|
6
|
+
private text;
|
|
7
|
+
private paddingX;
|
|
8
|
+
private paddingY;
|
|
9
|
+
constructor(text: string, paddingX?: number, paddingY?: number);
|
|
10
|
+
invalidate(): void;
|
|
11
|
+
render(width: number): string[];
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=truncated-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncated-text.d.ts","sourceRoot":"","sources":["../../src/components/truncated-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C;;GAEG;AACH,qBAAa,aAAc,YAAW,SAAS;IAC9C,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,EAAE,QAAQ,GAAE,MAAU,EAInE;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA0C9B;CACD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { truncateToWidth, visibleWidth } from "../utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* Text component that truncates to fit viewport width
|
|
4
|
+
*/
|
|
5
|
+
export class TruncatedText {
|
|
6
|
+
text;
|
|
7
|
+
paddingX;
|
|
8
|
+
paddingY;
|
|
9
|
+
constructor(text, paddingX = 0, paddingY = 0) {
|
|
10
|
+
this.text = text;
|
|
11
|
+
this.paddingX = paddingX;
|
|
12
|
+
this.paddingY = paddingY;
|
|
13
|
+
}
|
|
14
|
+
invalidate() {
|
|
15
|
+
// No cached state to invalidate currently
|
|
16
|
+
}
|
|
17
|
+
render(width) {
|
|
18
|
+
const result = [];
|
|
19
|
+
// Empty line padded to width
|
|
20
|
+
const emptyLine = " ".repeat(width);
|
|
21
|
+
// Add vertical padding above
|
|
22
|
+
for (let i = 0; i < this.paddingY; i++) {
|
|
23
|
+
result.push(emptyLine);
|
|
24
|
+
}
|
|
25
|
+
// Calculate available width after horizontal padding
|
|
26
|
+
const availableWidth = Math.max(1, width - this.paddingX * 2);
|
|
27
|
+
// Take only the first line (stop at newline)
|
|
28
|
+
let singleLineText = this.text;
|
|
29
|
+
const newlineIndex = this.text.indexOf("\n");
|
|
30
|
+
if (newlineIndex !== -1) {
|
|
31
|
+
singleLineText = this.text.substring(0, newlineIndex);
|
|
32
|
+
}
|
|
33
|
+
// Truncate text if needed (accounting for ANSI codes)
|
|
34
|
+
const displayText = truncateToWidth(singleLineText, availableWidth);
|
|
35
|
+
// Add horizontal padding
|
|
36
|
+
const leftPadding = " ".repeat(this.paddingX);
|
|
37
|
+
const rightPadding = " ".repeat(this.paddingX);
|
|
38
|
+
const lineWithPadding = leftPadding + displayText + rightPadding;
|
|
39
|
+
// Pad line to exactly width characters
|
|
40
|
+
const lineVisibleWidth = visibleWidth(lineWithPadding);
|
|
41
|
+
const paddingNeeded = Math.max(0, width - lineVisibleWidth);
|
|
42
|
+
const finalLine = lineWithPadding + " ".repeat(paddingNeeded);
|
|
43
|
+
result.push(finalLine);
|
|
44
|
+
// Add vertical padding below
|
|
45
|
+
for (let i = 0; i < this.paddingY; i++) {
|
|
46
|
+
result.push(emptyLine);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=truncated-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncated-text.js","sourceRoot":"","sources":["../../src/components/truncated-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5D;;GAEG;AACH,MAAM,OAAO,aAAa;IACjB,IAAI,CAAS;IACb,QAAQ,CAAS;IACjB,QAAQ,CAAS;IAEzB,YAAY,IAAY,EAAE,QAAQ,GAAW,CAAC,EAAE,QAAQ,GAAW,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;IAED,UAAU;QACT,0CAA0C;IAC3C,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,6BAA6B;QAC7B,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEpC,6BAA6B;QAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAED,qDAAqD;QACrD,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAE9D,6CAA6C;QAC7C,IAAI,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;QAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;YACzB,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;QACvD,CAAC;QAED,sDAAsD;QACtD,MAAM,WAAW,GAAG,eAAe,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;QAEpE,yBAAyB;QACzB,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,eAAe,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;QAEjE,uCAAuC;QACvC,MAAM,gBAAgB,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;QACvD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,gBAAgB,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,eAAe,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAE9D,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEvB,6BAA6B;QAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;QAED,OAAO,MAAM,CAAC;IACf,CAAC;CACD","sourcesContent":["import type { Component } from \"../tui.ts\";\nimport { truncateToWidth, visibleWidth } from \"../utils.ts\";\n\n/**\n * Text component that truncates to fit viewport width\n */\nexport class TruncatedText implements Component {\n\tprivate text: string;\n\tprivate paddingX: number;\n\tprivate paddingY: number;\n\n\tconstructor(text: string, paddingX: number = 0, paddingY: number = 0) {\n\t\tthis.text = text;\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\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\n\t\t// Empty line padded to width\n\t\tconst emptyLine = \" \".repeat(width);\n\n\t\t// Add vertical padding above\n\t\tfor (let i = 0; i < this.paddingY; i++) {\n\t\t\tresult.push(emptyLine);\n\t\t}\n\n\t\t// Calculate available width after horizontal padding\n\t\tconst availableWidth = Math.max(1, width - this.paddingX * 2);\n\n\t\t// Take only the first line (stop at newline)\n\t\tlet singleLineText = this.text;\n\t\tconst newlineIndex = this.text.indexOf(\"\\n\");\n\t\tif (newlineIndex !== -1) {\n\t\t\tsingleLineText = this.text.substring(0, newlineIndex);\n\t\t}\n\n\t\t// Truncate text if needed (accounting for ANSI codes)\n\t\tconst displayText = truncateToWidth(singleLineText, availableWidth);\n\n\t\t// Add horizontal padding\n\t\tconst leftPadding = \" \".repeat(this.paddingX);\n\t\tconst rightPadding = \" \".repeat(this.paddingX);\n\t\tconst lineWithPadding = leftPadding + displayText + rightPadding;\n\n\t\t// Pad line to exactly width characters\n\t\tconst lineVisibleWidth = visibleWidth(lineWithPadding);\n\t\tconst paddingNeeded = Math.max(0, width - lineVisibleWidth);\n\t\tconst finalLine = lineWithPadding + \" \".repeat(paddingNeeded);\n\n\t\tresult.push(finalLine);\n\n\t\t// Add vertical padding below\n\t\tfor (let i = 0; i < this.paddingY; i++) {\n\t\t\tresult.push(emptyLine);\n\t\t}\n\n\t\treturn result;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Stack, type StackChild, type StackOptions } from "./stack.ts";
|
|
2
|
+
export declare class VStack extends Stack {
|
|
3
|
+
protected readonly layoutType: "vstack";
|
|
4
|
+
constructor(children?: StackChild[], options?: StackOptions);
|
|
5
|
+
render(width: number): string[];
|
|
6
|
+
}
|
|
7
|
+
export type { StackChild, StackEntry, StackEntryOptions, StackOptions } from "./stack.ts";
|
|
8
|
+
//# sourceMappingURL=v-stack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v-stack.d.ts","sourceRoot":"","sources":["../../src/components/v-stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAuB,MAAM,YAAY,CAAC;AAEhH,qBAAa,MAAO,SAAQ,KAAK;IAChC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAG,QAAQ,CAAU;IAElD,YAAY,QAAQ,GAAE,UAAU,EAAO,EAAE,OAAO,GAAE,YAAiB,EAElE;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoBvC;CACD;AAED,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { allocateStackSizes, Stack, visibleStackEntries } from "./stack.js";
|
|
2
|
+
export class VStack extends Stack {
|
|
3
|
+
layoutType = "vstack";
|
|
4
|
+
constructor(children = [], options = {}) {
|
|
5
|
+
super(children, options);
|
|
6
|
+
}
|
|
7
|
+
render(width) {
|
|
8
|
+
const viewport = { width: Math.max(1, width), height: Number.MAX_SAFE_INTEGER };
|
|
9
|
+
const entries = visibleStackEntries(this.entries, viewport);
|
|
10
|
+
const rendered = entries.map((entry) => entry.component.render(viewport.width));
|
|
11
|
+
const sizes = allocateStackSizes(entries, rendered.map((lines) => lines.length), undefined, this.gap);
|
|
12
|
+
const lines = [];
|
|
13
|
+
for (let index = 0; index < entries.length; index++) {
|
|
14
|
+
if (index > 0) {
|
|
15
|
+
for (let gap = 0; gap < this.gap; gap++)
|
|
16
|
+
lines.push("");
|
|
17
|
+
}
|
|
18
|
+
const childLines = rendered[index].slice(0, sizes[index]);
|
|
19
|
+
lines.push(...childLines);
|
|
20
|
+
for (let padding = childLines.length; padding < sizes[index]; padding++)
|
|
21
|
+
lines.push("");
|
|
22
|
+
}
|
|
23
|
+
return lines;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=v-stack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v-stack.js","sourceRoot":"","sources":["../../src/components/v-stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAsC,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEhH,MAAM,OAAO,MAAO,SAAQ,KAAK;IACb,UAAU,GAAG,QAAiB,CAAC;IAElD,YAAY,QAAQ,GAAiB,EAAE,EAAE,OAAO,GAAiB,EAAE;QAClE,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC1B,CAAC;IAEQ,MAAM,CAAC,KAAa;QAC5B,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAChF,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAChF,MAAM,KAAK,GAAG,kBAAkB,CAC/B,OAAO,EACP,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,EACrC,SAAS,EACT,IAAI,CAAC,GAAG,CACR,CAAC;QACF,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YACrD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACf,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAE,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3D,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;YAC1B,KAAK,IAAI,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,CAAC,KAAK,CAAE,EAAE,OAAO,EAAE;gBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;CACD","sourcesContent":["import { allocateStackSizes, Stack, type StackChild, type StackOptions, visibleStackEntries } from \"./stack.ts\";\n\nexport class VStack extends Stack {\n\tprotected readonly layoutType = \"vstack\" as const;\n\n\tconstructor(children: StackChild[] = [], options: StackOptions = {}) {\n\t\tsuper(children, options);\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst viewport = { width: Math.max(1, width), height: Number.MAX_SAFE_INTEGER };\n\t\tconst entries = visibleStackEntries(this.entries, viewport);\n\t\tconst rendered = entries.map((entry) => entry.component.render(viewport.width));\n\t\tconst sizes = allocateStackSizes(\n\t\t\tentries,\n\t\t\trendered.map((lines) => lines.length),\n\t\t\tundefined,\n\t\t\tthis.gap,\n\t\t);\n\t\tconst lines: string[] = [];\n\t\tfor (let index = 0; index < entries.length; index++) {\n\t\t\tif (index > 0) {\n\t\t\t\tfor (let gap = 0; gap < this.gap; gap++) lines.push(\"\");\n\t\t\t}\n\t\t\tconst childLines = rendered[index]!.slice(0, sizes[index]);\n\t\t\tlines.push(...childLines);\n\t\t\tfor (let padding = childLines.length; padding < sizes[index]!; padding++) lines.push(\"\");\n\t\t}\n\t\treturn lines;\n\t}\n}\n\nexport type { StackChild, StackEntry, StackEntryOptions, StackOptions } from \"./stack.ts\";\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AutocompleteProvider } from "./autocomplete.ts";
|
|
2
|
+
import type { Component } from "./tui.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Interface for custom editor components.
|
|
5
|
+
*
|
|
6
|
+
* This allows extensions to provide their own editor implementation
|
|
7
|
+
* (e.g., vim mode, emacs mode, custom keybindings) while maintaining
|
|
8
|
+
* compatibility with the core application.
|
|
9
|
+
*/
|
|
10
|
+
export interface EditorComponent extends Component {
|
|
11
|
+
/** Get the current text content */
|
|
12
|
+
getText(): string;
|
|
13
|
+
/** Set the text content */
|
|
14
|
+
setText(text: string): void;
|
|
15
|
+
/** Handle raw terminal input (key presses, paste sequences, etc.) */
|
|
16
|
+
handleInput(data: string): void;
|
|
17
|
+
/** Called when user submits (e.g., Enter key) */
|
|
18
|
+
onSubmit?: (text: string) => void;
|
|
19
|
+
/** Called when text changes */
|
|
20
|
+
onChange?: (text: string) => void;
|
|
21
|
+
/** Add text to history for up/down navigation */
|
|
22
|
+
addToHistory?(text: string): void;
|
|
23
|
+
/** Insert text at current cursor position */
|
|
24
|
+
insertTextAtCursor?(text: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get text with any markers expanded (e.g., paste markers).
|
|
27
|
+
* Falls back to getText() if not implemented.
|
|
28
|
+
*/
|
|
29
|
+
getExpandedText?(): string;
|
|
30
|
+
/** Set the autocomplete provider */
|
|
31
|
+
setAutocompleteProvider?(provider: AutocompleteProvider): void;
|
|
32
|
+
/** Border color function */
|
|
33
|
+
borderColor?: (str: string) => string;
|
|
34
|
+
/** Set horizontal padding */
|
|
35
|
+
setPaddingX?(padding: number): void;
|
|
36
|
+
/** Set max visible items in autocomplete dropdown */
|
|
37
|
+
setAutocompleteMaxVisible?(maxVisible: number): void;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=editor-component.d.ts.map
|
|
@@ -0,0 +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;IAMlC,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;CACrD"}
|
|
@@ -0,0 +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"]}
|
package/dist/fuzzy.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy matching utilities.
|
|
3
|
+
* Matches if all query characters appear in order (not necessarily consecutive).
|
|
4
|
+
* Lower score = better match.
|
|
5
|
+
*/
|
|
6
|
+
export interface FuzzyMatch {
|
|
7
|
+
matches: boolean;
|
|
8
|
+
score: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function fuzzyMatch(query: string, text: string): FuzzyMatch;
|
|
11
|
+
/**
|
|
12
|
+
* Filter and sort items by fuzzy match quality (best matches first).
|
|
13
|
+
* Supports whitespace- and slash-separated tokens: all tokens must match.
|
|
14
|
+
*/
|
|
15
|
+
export declare function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) => string): T[];
|
|
16
|
+
//# sourceMappingURL=fuzzy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fuzzy.d.ts","sourceRoot":"","sources":["../src/fuzzy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAkFlE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,CAAC,EAAE,CAsC3F"}
|
package/dist/fuzzy.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy matching utilities.
|
|
3
|
+
* Matches if all query characters appear in order (not necessarily consecutive).
|
|
4
|
+
* Lower score = better match.
|
|
5
|
+
*/
|
|
6
|
+
export function fuzzyMatch(query, text) {
|
|
7
|
+
const queryLower = query.toLowerCase();
|
|
8
|
+
const textLower = text.toLowerCase();
|
|
9
|
+
const matchQuery = (normalizedQuery) => {
|
|
10
|
+
if (normalizedQuery.length === 0) {
|
|
11
|
+
return { matches: true, score: 0 };
|
|
12
|
+
}
|
|
13
|
+
if (normalizedQuery.length > textLower.length) {
|
|
14
|
+
return { matches: false, score: 0 };
|
|
15
|
+
}
|
|
16
|
+
let queryIndex = 0;
|
|
17
|
+
let score = 0;
|
|
18
|
+
let lastMatchIndex = -1;
|
|
19
|
+
let consecutiveMatches = 0;
|
|
20
|
+
while (queryIndex < normalizedQuery.length) {
|
|
21
|
+
const i = textLower.indexOf(normalizedQuery[queryIndex], lastMatchIndex + 1);
|
|
22
|
+
if (i === -1)
|
|
23
|
+
break;
|
|
24
|
+
const isWordBoundary = i === 0 || /[\s\-_./:]/.test(textLower[i - 1]);
|
|
25
|
+
// Reward consecutive matches
|
|
26
|
+
if (lastMatchIndex === i - 1) {
|
|
27
|
+
consecutiveMatches++;
|
|
28
|
+
score -= consecutiveMatches * 5;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
consecutiveMatches = 0;
|
|
32
|
+
// Penalize gaps
|
|
33
|
+
if (lastMatchIndex >= 0) {
|
|
34
|
+
score += (i - lastMatchIndex - 1) * 2;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Reward word boundary matches
|
|
38
|
+
if (isWordBoundary) {
|
|
39
|
+
score -= 10;
|
|
40
|
+
}
|
|
41
|
+
// Slight penalty for later matches
|
|
42
|
+
score += i * 0.1;
|
|
43
|
+
lastMatchIndex = i;
|
|
44
|
+
queryIndex++;
|
|
45
|
+
}
|
|
46
|
+
if (queryIndex < normalizedQuery.length) {
|
|
47
|
+
return { matches: false, score: 0 };
|
|
48
|
+
}
|
|
49
|
+
if (normalizedQuery === textLower) {
|
|
50
|
+
score -= 100;
|
|
51
|
+
}
|
|
52
|
+
return { matches: true, score };
|
|
53
|
+
};
|
|
54
|
+
const primaryMatch = matchQuery(queryLower);
|
|
55
|
+
if (primaryMatch.matches) {
|
|
56
|
+
return primaryMatch;
|
|
57
|
+
}
|
|
58
|
+
const alphaNumericMatch = queryLower.match(/^(?<letters>[a-z]+)(?<digits>[0-9]+)$/);
|
|
59
|
+
const numericAlphaMatch = queryLower.match(/^(?<digits>[0-9]+)(?<letters>[a-z]+)$/);
|
|
60
|
+
const swappedQuery = alphaNumericMatch
|
|
61
|
+
? `${alphaNumericMatch.groups?.digits ?? ""}${alphaNumericMatch.groups?.letters ?? ""}`
|
|
62
|
+
: numericAlphaMatch
|
|
63
|
+
? `${numericAlphaMatch.groups?.letters ?? ""}${numericAlphaMatch.groups?.digits ?? ""}`
|
|
64
|
+
: "";
|
|
65
|
+
if (!swappedQuery) {
|
|
66
|
+
return primaryMatch;
|
|
67
|
+
}
|
|
68
|
+
const swappedMatch = matchQuery(swappedQuery);
|
|
69
|
+
if (!swappedMatch.matches) {
|
|
70
|
+
return primaryMatch;
|
|
71
|
+
}
|
|
72
|
+
return { matches: true, score: swappedMatch.score + 5 };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Filter and sort items by fuzzy match quality (best matches first).
|
|
76
|
+
* Supports whitespace- and slash-separated tokens: all tokens must match.
|
|
77
|
+
*/
|
|
78
|
+
export function fuzzyFilter(items, query, getText) {
|
|
79
|
+
if (!query.trim()) {
|
|
80
|
+
return items;
|
|
81
|
+
}
|
|
82
|
+
const tokens = query
|
|
83
|
+
.trim()
|
|
84
|
+
.split(/[\s/]+/)
|
|
85
|
+
.filter((t) => t.length > 0);
|
|
86
|
+
if (tokens.length === 0) {
|
|
87
|
+
return items;
|
|
88
|
+
}
|
|
89
|
+
const results = [];
|
|
90
|
+
for (const item of items) {
|
|
91
|
+
const text = getText(item);
|
|
92
|
+
let totalScore = 0;
|
|
93
|
+
let allMatch = true;
|
|
94
|
+
for (const token of tokens) {
|
|
95
|
+
const match = fuzzyMatch(token, text);
|
|
96
|
+
if (match.matches) {
|
|
97
|
+
totalScore += match.score;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
allMatch = false;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (allMatch) {
|
|
105
|
+
results.push({ item, totalScore });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
results.sort((a, b) => a.totalScore - b.totalScore);
|
|
109
|
+
return results.map((r) => r.item);
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=fuzzy.js.map
|