@zaly/tui 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions-CnVzKVID.d.mts +41 -0
- package/dist/actions-CnVzKVID.d.mts.map +1 -0
- package/dist/actions-DEIKF2ts.mjs +48 -0
- package/dist/actions-DEIKF2ts.mjs.map +1 -0
- package/dist/{box-DyaByEiG.mjs → box-DepqUtuo.mjs} +2 -2
- package/dist/{box-DyaByEiG.mjs.map → box-DepqUtuo.mjs.map} +1 -1
- package/dist/{builder-D49ECBEf.mjs → builder-T9oGIWFa.mjs} +2 -2
- package/dist/{builder-D49ECBEf.mjs.map → builder-T9oGIWFa.mjs.map} +1 -1
- package/dist/{callbacks-CNjGNxVw.mjs → callbacks-DcMBGQEO.mjs} +2 -2
- package/dist/{callbacks-CNjGNxVw.mjs.map → callbacks-DcMBGQEO.mjs.map} +1 -1
- package/dist/{client-D6C_uczF.mjs → client-BBdtbLp4.mjs} +5 -10
- package/dist/client-BBdtbLp4.mjs.map +1 -0
- package/dist/client-cIckDgAj.mjs +2 -0
- package/dist/clipboard-l_TWxxTf.mjs +404 -0
- package/dist/clipboard-l_TWxxTf.mjs.map +1 -0
- package/dist/clipboard.mjs +1 -403
- package/dist/ctx-Ctmvof1N.mjs +43 -0
- package/dist/ctx-Ctmvof1N.mjs.map +1 -0
- package/dist/ctx-DhJIKuOX.mjs +2 -0
- package/dist/divider-cclEAdPx.mjs +9 -0
- package/dist/divider-cclEAdPx.mjs.map +1 -0
- package/dist/files-Aw3iWpXD.d.mts +29 -0
- package/dist/files-Aw3iWpXD.d.mts.map +1 -0
- package/dist/files-BvThQpOf.mjs +42 -0
- package/dist/files-BvThQpOf.mjs.map +1 -0
- package/dist/github-Cy2hfWJ7.mjs +137 -0
- package/dist/github-Cy2hfWJ7.mjs.map +1 -0
- package/dist/github-DS00VoOj.d.mts +64 -0
- package/dist/github-DS00VoOj.d.mts.map +1 -0
- package/dist/image-BTxDPPiT.d.mts +37 -0
- package/dist/image-BTxDPPiT.d.mts.map +1 -0
- package/dist/index.mjs +6 -202
- package/dist/index.mjs.map +1 -1
- package/dist/input-CRBuTrsL.mjs +497 -0
- package/dist/input-CRBuTrsL.mjs.map +1 -0
- package/dist/{inspect-D1d9t-ni.mjs → inspect-BaKG6cU_.mjs} +2 -2
- package/dist/{inspect-D1d9t-ni.mjs.map → inspect-BaKG6cU_.mjs.map} +1 -1
- package/dist/log-DYHEYX_z.mjs +130 -0
- package/dist/log-DYHEYX_z.mjs.map +1 -0
- package/dist/logger-Ch0LwSG_.mjs +71 -0
- package/dist/logger-Ch0LwSG_.mjs.map +1 -0
- package/dist/markdown-CYHHoYu0.mjs +124 -0
- package/dist/markdown-CYHHoYu0.mjs.map +1 -0
- package/dist/markdown.mjs +1 -1
- package/dist/overlay-9xNgsuuy.mjs +18 -0
- package/dist/overlay-9xNgsuuy.mjs.map +1 -0
- package/dist/{picker-ndVVt6qF.mjs → picker-cH-rc7-X.mjs} +5 -5
- package/dist/{picker-ndVVt6qF.mjs.map → picker-cH-rc7-X.mjs.map} +1 -1
- package/dist/queries-B_lOwEiE.mjs +200 -0
- package/dist/queries-B_lOwEiE.mjs.map +1 -0
- package/dist/{registry-U654j61Y.mjs → registry-CdeLjUo2.mjs} +2 -4
- package/dist/registry-CdeLjUo2.mjs.map +1 -0
- package/dist/render-CPUD4yaW.mjs +33 -0
- package/dist/render-CPUD4yaW.mjs.map +1 -0
- package/dist/{renderer-CxL4xbkl.mjs → renderer-DJ1pWXSL.mjs} +8 -7
- package/dist/{renderer-CxL4xbkl.mjs.map → renderer-DJ1pWXSL.mjs.map} +1 -1
- package/dist/renderer-Dgj_qEOA.mjs +2 -0
- package/dist/{renderer-CdLCkJB-.mjs → renderer-qSMVeNSV.mjs} +3 -5
- package/dist/{renderer-CdLCkJB-.mjs.map → renderer-qSMVeNSV.mjs.map} +1 -1
- package/dist/select-m7NE6Rwa.mjs +195 -0
- package/dist/select-m7NE6Rwa.mjs.map +1 -0
- package/dist/services/logger.mjs +1 -70
- package/dist/services/notifier.mjs +2 -2
- package/dist/services/picker.d.mts +1 -1
- package/dist/services/picker.mjs +6 -6
- package/dist/services/prompt.d.mts +1 -1
- package/dist/services/prompt.mjs +5 -5
- package/dist/shiki/server.d.mts +1 -0
- package/dist/shiki/server.mjs +129 -0
- package/dist/shiki/server.mjs.map +1 -0
- package/dist/{shiki-BrjVUkeh.mjs → shiki-B5NwIiot.mjs} +5 -10
- package/dist/shiki-B5NwIiot.mjs.map +1 -0
- package/dist/shiki-BK-v9CtF.mjs +12 -0
- package/dist/shiki-BK-v9CtF.mjs.map +1 -0
- package/dist/show-DR2x1YFc.mjs +85 -0
- package/dist/show-DR2x1YFc.mjs.map +1 -0
- package/dist/style.mjs +2 -2
- package/dist/text-9lwQeqYr.d.mts +33 -0
- package/dist/text-9lwQeqYr.d.mts.map +1 -0
- package/dist/text-DILrol9j.mjs +32 -0
- package/dist/text-DILrol9j.mjs.map +1 -0
- package/dist/text-q26V6_Sc.mjs +111 -0
- package/dist/text-q26V6_Sc.mjs.map +1 -0
- package/dist/text.d.mts +2 -33
- package/dist/text.mjs +1 -110
- package/dist/themes.mjs +1 -1
- package/dist/tree-Bo1afYhe.mjs +73 -0
- package/dist/tree-Bo1afYhe.mjs.map +1 -0
- package/dist/widget-BkMwhY2p.mjs +40 -0
- package/dist/widget-BkMwhY2p.mjs.map +1 -0
- package/dist/widget-M7jD8Rgg.d.mts +49 -0
- package/dist/widget-M7jD8Rgg.d.mts.map +1 -0
- package/dist/widgets/autocomplete.mjs +2 -2
- package/dist/widgets/box.mjs +1 -1
- package/dist/widgets/code.mjs +7 -7
- package/dist/widgets/completions/actions.d.mts +2 -41
- package/dist/widgets/completions/actions.mjs +1 -47
- package/dist/widgets/completions/files.d.mts +2 -29
- package/dist/widgets/completions/files.mjs +1 -41
- package/dist/widgets/completions/github.d.mts +2 -64
- package/dist/widgets/completions/github.mjs +1 -136
- package/dist/widgets/completions/index.d.mts +3 -3
- package/dist/widgets/completions/index.mjs +3 -3
- package/dist/widgets/diff.d.mts +1 -1
- package/dist/widgets/diff.mjs +3 -3
- package/dist/widgets/divider.mjs +1 -8
- package/dist/widgets/image.d.mts +2 -37
- package/dist/widgets/image.mjs +1 -1
- package/dist/widgets/input.mjs +1 -496
- package/dist/widgets/log.mjs +1 -129
- package/dist/widgets/markdown.d.mts +2 -2
- package/dist/widgets/markdown.mjs +1 -123
- package/dist/widgets/overlay.mjs +1 -17
- package/dist/widgets/picker.mjs +1 -1
- package/dist/widgets/select.mjs +1 -194
- package/dist/widgets/show.mjs +1 -84
- package/dist/widgets/text.mjs +1 -31
- package/dist/widgets/tree.mjs +1 -72
- package/dist/widgets/widget.d.mts +2 -49
- package/dist/widgets/widget.mjs +1 -39
- package/dist/worker-BvRm4OsW.mjs +2 -0
- package/dist/worker-T6SdPzUH.mjs +52 -0
- package/dist/worker-T6SdPzUH.mjs.map +1 -0
- package/package.json +5 -1
- package/dist/client-D6C_uczF.mjs.map +0 -1
- package/dist/clipboard.mjs.map +0 -1
- package/dist/registry-U654j61Y.mjs.map +0 -1
- package/dist/render-DxVLqqB9.mjs +0 -74
- package/dist/render-DxVLqqB9.mjs.map +0 -1
- package/dist/rolldown-runtime-D7D4PA-g.mjs +0 -13
- package/dist/services/logger.mjs.map +0 -1
- package/dist/shiki-BrjVUkeh.mjs.map +0 -1
- package/dist/text.d.mts.map +0 -1
- package/dist/text.mjs.map +0 -1
- package/dist/widgets/completions/actions.d.mts.map +0 -1
- package/dist/widgets/completions/actions.mjs.map +0 -1
- package/dist/widgets/completions/files.d.mts.map +0 -1
- package/dist/widgets/completions/files.mjs.map +0 -1
- package/dist/widgets/completions/github.d.mts.map +0 -1
- package/dist/widgets/completions/github.mjs.map +0 -1
- package/dist/widgets/divider.mjs.map +0 -1
- package/dist/widgets/image.d.mts.map +0 -1
- package/dist/widgets/input.mjs.map +0 -1
- package/dist/widgets/log.mjs.map +0 -1
- package/dist/widgets/markdown.mjs.map +0 -1
- package/dist/widgets/overlay.mjs.map +0 -1
- package/dist/widgets/select.mjs.map +0 -1
- package/dist/widgets/show.mjs.map +0 -1
- package/dist/widgets/text.mjs.map +0 -1
- package/dist/widgets/tree.mjs.map +0 -1
- package/dist/widgets/widget.d.mts.map +0 -1
- package/dist/widgets/widget.mjs.map +0 -1
- package/dist/worker-DjHuQSbY.mjs +0 -27
- package/dist/worker-DjHuQSbY.mjs.map +0 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { C as useActiveOwner, E as withOwner, S as unwrap, x as untrack } from "./reactive-f7NpAi69.mjs";
|
|
2
|
+
import { t as Node } from "./node-BZ7f6HzU.mjs";
|
|
3
|
+
//#region src/widgets/show.ts
|
|
4
|
+
/** Conditional fragment backing `show()`. Branches are created lazily:
|
|
5
|
+
* a `use` factory only runs the first time its condition wins. Once
|
|
6
|
+
* created, the branch remains mounted and owned; inactive branches simply
|
|
7
|
+
* don't participate in layout/rendering and contribute zero rows.
|
|
8
|
+
*
|
|
9
|
+
* Lazy factories run under the owner captured at `show()` creation time,
|
|
10
|
+
* so context reads behave as if the branch had been constructed eagerly.
|
|
11
|
+
*/
|
|
12
|
+
var Show = class extends Node {
|
|
13
|
+
#state = [];
|
|
14
|
+
#owner = useActiveOwner();
|
|
15
|
+
constructor(spec) {
|
|
16
|
+
super({});
|
|
17
|
+
let fallback = false;
|
|
18
|
+
for (const s of spec) {
|
|
19
|
+
if (fallback) throw new Error("Show: `fallback` must be the final argument");
|
|
20
|
+
if (typeof s === "function") {
|
|
21
|
+
this.#state.push({
|
|
22
|
+
use: s,
|
|
23
|
+
when: true
|
|
24
|
+
});
|
|
25
|
+
fallback = true;
|
|
26
|
+
} else this.#state.push(s);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
#nodes(s, use) {
|
|
30
|
+
if (s.children) return s.children;
|
|
31
|
+
const create = () => untrack(use);
|
|
32
|
+
const nodes = this.#owner ? withOwner(this.#owner, create) : create();
|
|
33
|
+
s.children = Array.isArray(nodes) ? nodes : [nodes];
|
|
34
|
+
for (const c of s.children) this.add(c);
|
|
35
|
+
return s.children;
|
|
36
|
+
}
|
|
37
|
+
layoutChildren() {
|
|
38
|
+
for (const s of this.#state) {
|
|
39
|
+
const matches = unwrap(s.when);
|
|
40
|
+
if (s.use === void 0) {
|
|
41
|
+
if (!matches) return [];
|
|
42
|
+
continue;
|
|
43
|
+
} else if (!matches) continue;
|
|
44
|
+
return this.#nodes(s, s.use);
|
|
45
|
+
}
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
/** Standalone-render fallback. Box parents go through
|
|
49
|
+
* `layoutChildren` and bypass this; only fires when `Show` sits
|
|
50
|
+
* outside a layout container (root, overlay, etc.). */
|
|
51
|
+
async _render(ctx) {
|
|
52
|
+
const targets = this.layoutChildren();
|
|
53
|
+
return (await Promise.all(targets.map((c) => c.render(ctx)))).flat();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
/** Lazy conditional rendering.
|
|
57
|
+
*
|
|
58
|
+
* Specs are evaluated from left to right:
|
|
59
|
+
* - `{ when }` is a guard. When false, rendering stops with no rows; when
|
|
60
|
+
* true, evaluation continues.
|
|
61
|
+
* - `{ when, use }` is a conditional branch. The first branch whose
|
|
62
|
+
* condition is truthy is rendered.
|
|
63
|
+
* - A final bare factory is the fallback branch and must be the last
|
|
64
|
+
* argument.
|
|
65
|
+
*
|
|
66
|
+
* Branch factories are lazy and run at most once. Created branches stay
|
|
67
|
+
* mounted across toggles, preserving state, effects, and async resources.
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* show({ when: isLoading }, () => spinner({ color: "accent" }))
|
|
71
|
+
*
|
|
72
|
+
* show(
|
|
73
|
+
* { when: full },
|
|
74
|
+
* { when: isError, use: () => errorView() },
|
|
75
|
+
* () => resultView()
|
|
76
|
+
* )
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
function show(...spec) {
|
|
80
|
+
return new Show(spec);
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { show as t };
|
|
84
|
+
|
|
85
|
+
//# sourceMappingURL=show-DR2x1YFc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show-DR2x1YFc.mjs","names":["#state","#owner","#nodes"],"sources":["../src/widgets/show.ts"],"sourcesContent":["import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Reactive } from \"../core/reactive.ts\"\n\nimport { Node } from \"../core/node.ts\"\nimport { untrack, unwrap, useActiveOwner, withOwner } from \"../core/reactive.ts\"\n\nexport type ShowGate = { when: Reactive<unknown> }\nexport type ShowBranch = { when: Reactive<unknown>; use: ShowChildren }\nexport type ShowChildren = () => Node | Node[]\nexport type ShowSpec = (ShowGate | ShowBranch)[] | [...(ShowGate | ShowBranch)[], ShowChildren]\n\ntype ShowState = {\n when: Reactive<unknown>\n use?: ShowChildren\n children?: Node[]\n}\n\nexport type { Show }\n\n/** Conditional fragment backing `show()`. Branches are created lazily:\n * a `use` factory only runs the first time its condition wins. Once\n * created, the branch remains mounted and owned; inactive branches simply\n * don't participate in layout/rendering and contribute zero rows.\n *\n * Lazy factories run under the owner captured at `show()` creation time,\n * so context reads behave as if the branch had been constructed eagerly.\n */\nclass Show extends Node {\n #state: ShowState[] = []\n #owner = useActiveOwner()\n\n constructor(spec: ShowSpec) {\n super({})\n let fallback = false\n for (const s of spec) {\n if (fallback) throw new Error(\"Show: `fallback` must be the final argument\")\n if (typeof s === \"function\") {\n this.#state.push({ use: s, when: true })\n fallback = true\n } else this.#state.push(s)\n }\n }\n\n #nodes(s: ShowState, use: ShowChildren): Node[] {\n if (s.children) return s.children\n const create = () => untrack(use)\n const nodes = this.#owner ? withOwner(this.#owner, create) : create()\n s.children = Array.isArray(nodes) ? nodes : [nodes]\n for (const c of s.children) this.add(c)\n return s.children\n }\n\n override layoutChildren(): readonly Node[] {\n for (const s of this.#state) {\n const matches = unwrap(s.when)\n if (s.use === undefined) {\n if (!matches) return []\n continue\n } else if (!matches) continue\n return this.#nodes(s, s.use)\n }\n return []\n }\n\n /** Standalone-render fallback. Box parents go through\n * `layoutChildren` and bypass this; only fires when `Show` sits\n * outside a layout container (root, overlay, etc.). */\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n const targets = this.layoutChildren()\n const rows = await Promise.all(targets.map((c) => c.render(ctx)))\n return rows.flat()\n }\n}\n\n/** Lazy conditional rendering.\n *\n * Specs are evaluated from left to right:\n * - `{ when }` is a guard. When false, rendering stops with no rows; when\n * true, evaluation continues.\n * - `{ when, use }` is a conditional branch. The first branch whose\n * condition is truthy is rendered.\n * - A final bare factory is the fallback branch and must be the last\n * argument.\n *\n * Branch factories are lazy and run at most once. Created branches stay\n * mounted across toggles, preserving state, effects, and async resources.\n *\n * ```ts\n * show({ when: isLoading }, () => spinner({ color: \"accent\" }))\n *\n * show(\n * { when: full },\n * { when: isError, use: () => errorView() },\n * () => resultView()\n * )\n * ```\n */\nexport function show(...spec: ShowSpec): Show {\n return new Show(spec)\n}\n"],"mappings":";;;;;;;;;;;AA2BA,IAAM,OAAN,cAAmB,KAAK;CACtB,SAAsB,CAAC;CACvB,SAAS,eAAe;CAExB,YAAY,MAAgB;EAC1B,MAAM,CAAC,CAAC;EACR,IAAI,WAAW;EACf,KAAK,MAAM,KAAK,MAAM;GACpB,IAAI,UAAU,MAAM,IAAI,MAAM,6CAA6C;GAC3E,IAAI,OAAO,MAAM,YAAY;IAC3B,KAAKA,OAAO,KAAK;KAAE,KAAK;KAAG,MAAM;IAAK,CAAC;IACvC,WAAW;GACb,OAAO,KAAKA,OAAO,KAAK,CAAC;EAC3B;CACF;CAEA,OAAO,GAAc,KAA2B;EAC9C,IAAI,EAAE,UAAU,OAAO,EAAE;EACzB,MAAM,eAAe,QAAQ,GAAG;EAChC,MAAM,QAAQ,KAAKC,SAAS,UAAU,KAAKA,QAAQ,MAAM,IAAI,OAAO;EACpE,EAAE,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;EAClD,KAAK,MAAM,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;EACtC,OAAO,EAAE;CACX;CAEA,iBAA2C;EACzC,KAAK,MAAM,KAAK,KAAKD,QAAQ;GAC3B,MAAM,UAAU,OAAO,EAAE,IAAI;GAC7B,IAAI,EAAE,QAAQ,KAAA,GAAW;IACvB,IAAI,CAAC,SAAS,OAAO,CAAC;IACtB;GACF,OAAO,IAAI,CAAC,SAAS;GACrB,OAAO,KAAKE,OAAO,GAAG,EAAE,GAAG;EAC7B;EACA,OAAO,CAAC;CACV;;;;CAKA,MAAgB,QAAQ,KAAmC;EACzD,MAAM,UAAU,KAAK,eAAe;EAEpC,QAAO,MADY,QAAQ,IAAI,QAAQ,KAAK,MAAM,EAAE,OAAO,GAAG,CAAC,CAAC,EAAA,CACpD,KAAK;CACnB;AACF;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,KAAK,GAAG,MAAsB;CAC5C,OAAO,IAAI,KAAK,IAAI;AACtB"}
|
package/dist/style.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { d as toHex, l as isHexColor, u as parseHex } from "./ansi-Clq0ozyA.mjs";
|
|
2
|
-
import { t as styleBuilder } from "./builder-
|
|
3
|
-
import { n as inspectFormat, r as isMarkdown, t as inspect } from "./inspect-
|
|
2
|
+
import { t as styleBuilder } from "./builder-T9oGIWFa.mjs";
|
|
3
|
+
import { n as inspectFormat, r as isMarkdown, t as inspect } from "./inspect-BaKG6cU_.mjs";
|
|
4
4
|
export { inspect, inspectFormat, isHexColor, isMarkdown, parseHex, styleBuilder, toHex };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { J as Layout } from "./queries-DH3bj74K.mjs";
|
|
2
|
+
import { a as StyleBuilder } from "./inspect-D6FW_QUo.mjs";
|
|
3
|
+
//#region src/layout/text.d.ts
|
|
4
|
+
type WrapMode = "word" | "char" | "none";
|
|
5
|
+
declare function formatText(text: string, opts: {
|
|
6
|
+
wrap?: WrapMode;
|
|
7
|
+
width: number;
|
|
8
|
+
style?: StyleBuilder;
|
|
9
|
+
/** When wrapping, preserve the indentation of the original text. */
|
|
10
|
+
indent?: boolean;
|
|
11
|
+
/** When wrapping ANSI-styled text, pad wrapped fragments to `width`
|
|
12
|
+
* using the background color active at the fragment's last cell.
|
|
13
|
+
* Natural lines that already fit are left unchanged. */
|
|
14
|
+
wrapBg?: boolean;
|
|
15
|
+
}): string[];
|
|
16
|
+
declare function expandTabs(s: string, tabSize?: number): string;
|
|
17
|
+
declare function formatLines(text: string | string[], opts?: {
|
|
18
|
+
numbered?: boolean;
|
|
19
|
+
numberOffset?: number;
|
|
20
|
+
maxLineLength?: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
limit?: number;
|
|
23
|
+
more?: false | ((more: number, msg: string) => string);
|
|
24
|
+
style?: StyleBuilder;
|
|
25
|
+
}): string[];
|
|
26
|
+
declare function calcLayout(text: string, opts?: {
|
|
27
|
+
wrap?: WrapMode;
|
|
28
|
+
}): Layout;
|
|
29
|
+
declare function countLines(text: string): number;
|
|
30
|
+
declare function capitalize(s: string): string;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { expandTabs as a, countLines as i, calcLayout as n, formatLines as o, capitalize as r, formatText as s, WrapMode as t };
|
|
33
|
+
//# sourceMappingURL=text-9lwQeqYr.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-9lwQeqYr.d.mts","names":[],"sources":["../src/layout/text.ts"],"mappings":";;;KAeY;iBAKI,WACd,cACA;EACE,OAAO;EACP;EACA,QAAQ;;EAER;;;;EAIA;;iBAmDY,WAAW,WAAW;iBAsBtB,YACd,yBACA;EACE;EACA;EACA;EACA;EACA;EACA,iBAAiB,cAAc;EAC/B,QAAQ;;iBA0DI,WAAW,cAAc;EAAQ,OAAO;IAAkB;iBAS1D,WAAW;iBAUX,WAAW"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { S as unwrap } from "./reactive-f7NpAi69.mjs";
|
|
2
|
+
import { t as Node } from "./node-BZ7f6HzU.mjs";
|
|
3
|
+
import { o as formatText, t as calcLayout } from "./text-q26V6_Sc.mjs";
|
|
4
|
+
//#region src/widgets/text.ts
|
|
5
|
+
var Text = class extends Node {
|
|
6
|
+
async _render(ctx) {
|
|
7
|
+
return formatText(await textContent(this.state.content, ctx), {
|
|
8
|
+
style: ctx.style.add(this.state),
|
|
9
|
+
width: ctx.width,
|
|
10
|
+
wrap: this.state.wrap
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
async layout(ctx) {
|
|
14
|
+
return calcLayout(await textContent(this.state.content, ctx), { wrap: this.state.wrap });
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
async function textContent(content, ctx) {
|
|
18
|
+
if (content === void 0) return;
|
|
19
|
+
const raw = unwrap(content);
|
|
20
|
+
return unwrap(typeof raw === "string" ? raw : await raw(ctx));
|
|
21
|
+
}
|
|
22
|
+
function text(first, style) {
|
|
23
|
+
if (typeof first === "string" || typeof first === "function") return new Text({
|
|
24
|
+
content: first,
|
|
25
|
+
...style
|
|
26
|
+
});
|
|
27
|
+
return new Text(first);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { text as n, textContent as r, Text as t };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=text-DILrol9j.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-DILrol9j.mjs","names":[],"sources":["../src/widgets/text.ts"],"sourcesContent":["import type { MaybePromise } from \"@zaly/shared\"\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Reactive } from \"../core/reactive.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Style } from \"../style/types.ts\"\n\nimport { Node } from \"../core/node.ts\"\nimport { unwrap } from \"../core/reactive.ts\"\nimport { calcLayout, formatText } from \"../layout/text.ts\"\n\n/**\n * Text content — three forms, resolved at render time:\n *\n * 1. Plain string — static content.\n * 2. Reactive accessor (`signal()` / `memo()`) — re-reads on each\n * render and subscribes the node so signal writes invalidate.\n * 3. `(ctx) => string` — computed from the render context. Use this\n * for inline-styled spans via `ctx.style`.\n *\n * Branded accessors are picked up via `isAccessor`; any other function\n * is treated as the ctx-aware form. Plain strings pass through.\n *\n * ```ts\n * text(\"hello\")\n * text(name) // signal accessor\n * text(({ style }) => style.ok(`✓ ${count}`)) // ctx-aware\n * ```\n */\nexport type TextContent = Reactive<string> | ((ctx: RenderCtx) => MaybePromise<Reactive<string>>)\n\nexport interface TextStyle extends Style {\n content: TextContent\n wrap?: \"word\" | \"char\" | \"none\"\n}\n\nexport class Text extends Node<TextStyle> {\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n const content = await textContent(this.state.content, ctx)\n return formatText(content, {\n style: ctx.style.add(this.state),\n width: ctx.width,\n wrap: this.state.wrap,\n })\n }\n\n override async layout(ctx: RenderCtx) {\n return calcLayout(await textContent(this.state.content, ctx), { wrap: this.state.wrap })\n }\n}\n\nexport function textContent(content: TextContent, ctx: RenderCtx): Promise<string>\nexport function textContent(content: TextContent | undefined, ctx: RenderCtx): Promise<undefined>\nexport async function textContent(\n content: TextContent | undefined,\n ctx: RenderCtx\n): Promise<string | undefined> {\n if (content === undefined) return\n const raw = unwrap(content)\n return unwrap(typeof raw === \"string\" ? raw : await raw(ctx))\n}\n\n/**\n * Factory for `Text`. Content can be a plain string or a `(ctx) => string`\n * function that's called at render time — use the function form when you\n * want inline-styled spans via `ctx.style`.\n *\n * ```ts\n * text(\"hello\")\n * text(\"hello\", { fg: \"primary\", bold: true })\n * text({ content: \"hello\", fg: \"primary\" })\n * text(({ style }) => `ok: ${style.ok(\"yes\")}`)\n * ```\n */\nexport function text(content: TextContent, style?: Omit<State<TextStyle>, \"content\">): Text\nexport function text(style: State<TextStyle>): Text\nexport function text(\n first: TextContent | State<TextStyle>,\n style?: Omit<State<TextStyle>, \"content\">\n): Text {\n // TextContent: string | function (incl. reactive accessor) | …\n // TextStyle: plain object with a `content` field.\n // The TextStyle branch is picked only when `first` is a non-function\n // object — accessors are functions, so they go through the content path.\n if (typeof first === \"string\" || typeof first === \"function\") {\n return new Text({ content: first, ...style })\n }\n return new Text(first)\n}\n"],"mappings":";;;;AAmCA,IAAa,OAAb,cAA0B,KAAgB;CACxC,MAAgB,QAAQ,KAAmC;EAEzD,OAAO,WAAW,MADI,YAAY,KAAK,MAAM,SAAS,GAAG,GAC9B;GACzB,OAAO,IAAI,MAAM,IAAI,KAAK,KAAK;GAC/B,OAAO,IAAI;GACX,MAAM,KAAK,MAAM;EACnB,CAAC;CACH;CAEA,MAAe,OAAO,KAAgB;EACpC,OAAO,WAAW,MAAM,YAAY,KAAK,MAAM,SAAS,GAAG,GAAG,EAAE,MAAM,KAAK,MAAM,KAAK,CAAC;CACzF;AACF;AAIA,eAAsB,YACpB,SACA,KAC6B;CAC7B,IAAI,YAAY,KAAA,GAAW;CAC3B,MAAM,MAAM,OAAO,OAAO;CAC1B,OAAO,OAAO,OAAO,QAAQ,WAAW,MAAM,MAAM,IAAI,GAAG,CAAC;AAC9D;AAgBA,SAAgB,KACd,OACA,OACM;CAKN,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAChD,OAAO,IAAI,KAAK;EAAE,SAAS;EAAO,GAAG;CAAM,CAAC;CAE9C,OAAO,IAAI,KAAK,KAAK;AACvB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { c as styleAnsi, n as ansiBg } from "./ansi-Clq0ozyA.mjs";
|
|
2
|
+
import { clamp } from "@zaly/shared";
|
|
3
|
+
import { hasAnsi, sliceAnsi, splitAnsi, stringWidth, stripAnsi, truncateAnsi, wrapAnsi } from "@zaly/shared/ansi";
|
|
4
|
+
//#region src/layout/text.ts
|
|
5
|
+
const ELLIPSIS = "…";
|
|
6
|
+
const TAB_WIDTH = 4;
|
|
7
|
+
function formatText(text, opts) {
|
|
8
|
+
const mode = opts.wrap ?? "word";
|
|
9
|
+
const lines = splitAnsi(expandTabs(text));
|
|
10
|
+
const ret = [];
|
|
11
|
+
if (mode === "none") ret.push(...lines);
|
|
12
|
+
else for (const line of lines) {
|
|
13
|
+
if (stringWidth(line) <= opts.width) {
|
|
14
|
+
ret.push(line);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const stripped = stripAnsi(line);
|
|
18
|
+
if (stripped.slice(opts.width).trim() === "") {
|
|
19
|
+
ret.push(sliceAnsi(line, 0, opts.width));
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const indentWidth = opts.indent ? stringWidth(stripped.match(/^\s*/)?.[0] ?? "") : 0;
|
|
23
|
+
const indent = indentWidth > 0 ? sliceAnsi(line, 0, indentWidth) : "";
|
|
24
|
+
const wrapped = splitAnsi(wrapAnsi(indent ? sliceAnsi(line, indentWidth) : line, opts.width - indentWidth, { mode }));
|
|
25
|
+
for (let r = 0; r < wrapped.length; r++) {
|
|
26
|
+
let row = r > 0 && wrapped[r].startsWith(" ") ? wrapped[r].slice(1) : wrapped[r];
|
|
27
|
+
if (row === "" && r > 0 && r < wrapped.length - 1) continue;
|
|
28
|
+
if (indent) row = `${indent}${row}`;
|
|
29
|
+
if (opts.wrapBg) {
|
|
30
|
+
const rowWidth = stringWidth(row);
|
|
31
|
+
const bg = rowWidth > 0 ? ansiBg(row, rowWidth - 1) : void 0;
|
|
32
|
+
if (bg && rowWidth < opts.width) row = `${row}${styleAnsi(" ".repeat(opts.width - rowWidth), { bg })}`;
|
|
33
|
+
}
|
|
34
|
+
ret.push(row);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return ret.map((line) => opts.style ? opts.style(line) : line);
|
|
38
|
+
}
|
|
39
|
+
function expandTabs(s, tabSize = TAB_WIDTH) {
|
|
40
|
+
if (!s.includes(" ")) return s;
|
|
41
|
+
const plain = hasAnsi(s) ? stripAnsi(s) : s;
|
|
42
|
+
const spaces = [];
|
|
43
|
+
let col = 0;
|
|
44
|
+
for (const ch of plain) if (ch === " ") {
|
|
45
|
+
const n = tabSize - col % tabSize;
|
|
46
|
+
spaces.push(" ".repeat(n));
|
|
47
|
+
col += n;
|
|
48
|
+
} else if (ch === "\n" || ch === "\r") col = 0;
|
|
49
|
+
else col += stringWidth(ch);
|
|
50
|
+
let i = 0;
|
|
51
|
+
return s.replaceAll(" ", () => spaces[i++] ?? " ".repeat(tabSize));
|
|
52
|
+
}
|
|
53
|
+
function formatLines(text, opts = {}) {
|
|
54
|
+
const lines = (typeof text === "string" ? splitAnsi(text) : text).map((line) => expandTabs(line));
|
|
55
|
+
const offset = clamp(opts.offset ?? 0, 0, lines.length - 1);
|
|
56
|
+
const limit = clamp(opts.limit ?? lines.length, 1, lines.length - offset);
|
|
57
|
+
const ellipsis = opts.style?.(ELLIPSIS) ?? ELLIPSIS;
|
|
58
|
+
let slice = lines.slice(offset, offset + limit).map((line) => opts.maxLineLength ? truncateAnsi(line, opts.maxLineLength, ellipsis) : line);
|
|
59
|
+
const numberOffset = opts.numberOffset ?? offset + 1;
|
|
60
|
+
const gutterWidth = String(numberOffset + slice.length - 1).length + 2;
|
|
61
|
+
if (opts.numbered) slice = slice.map((line, i) => {
|
|
62
|
+
const n = numberOffset + i;
|
|
63
|
+
const lineo = `${String(n).padStart(gutterWidth - 2)} │`;
|
|
64
|
+
return `${opts.style?.(lineo) ?? lineo} ${line}`;
|
|
65
|
+
});
|
|
66
|
+
if (slice.length < lines.length && opts.more !== false) {
|
|
67
|
+
const moreN = lines.length - slice.length;
|
|
68
|
+
let more = `+${moreN} more line${moreN > 1 ? "s" : ""}`;
|
|
69
|
+
more = opts.more?.(moreN, more) ?? more;
|
|
70
|
+
if (opts.numbered) {
|
|
71
|
+
const moreGutter = `${"…".padStart(gutterWidth - 2)} │`;
|
|
72
|
+
slice.push(`${opts.style?.(moreGutter) ?? moreGutter} ${opts.style?.(more) ?? more}`);
|
|
73
|
+
} else slice.push(opts.style?.(more) ?? more);
|
|
74
|
+
}
|
|
75
|
+
return slice;
|
|
76
|
+
}
|
|
77
|
+
function maxContent(text) {
|
|
78
|
+
return text.split("\n").reduce((m, line) => Math.max(m, stringWidth(line)), 0);
|
|
79
|
+
}
|
|
80
|
+
function minContent(text, mode) {
|
|
81
|
+
switch (mode) {
|
|
82
|
+
case "none": return;
|
|
83
|
+
case "char": return 1;
|
|
84
|
+
case "word": return text.split(/\s+/).reduce((m, word) => Math.max(m, stringWidth(word)), 0);
|
|
85
|
+
default: return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function calcLayout(text, opts = {}) {
|
|
89
|
+
text = expandTabs(text);
|
|
90
|
+
const width = maxContent(text);
|
|
91
|
+
return {
|
|
92
|
+
minWidth: minContent(text, opts.wrap ?? "word") ?? width,
|
|
93
|
+
width
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function countLines(text) {
|
|
97
|
+
let count = 0;
|
|
98
|
+
let pos = text.indexOf("\n");
|
|
99
|
+
while (pos !== -1) {
|
|
100
|
+
count++;
|
|
101
|
+
pos = text.indexOf("\n", pos + 1);
|
|
102
|
+
}
|
|
103
|
+
return count;
|
|
104
|
+
}
|
|
105
|
+
function capitalize(s) {
|
|
106
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { formatLines as a, expandTabs as i, capitalize as n, formatText as o, countLines as r, calcLayout as t };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=text-q26V6_Sc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-q26V6_Sc.mjs","names":[],"sources":["../src/layout/text.ts"],"sourcesContent":["import type { Layout } from \"../core/state.ts\"\nimport type { StyleBuilder } from \"../style/builder.ts\"\n\nimport { clamp } from \"@zaly/shared\"\nimport {\n hasAnsi,\n sliceAnsi,\n splitAnsi,\n stringWidth,\n stripAnsi,\n truncateAnsi,\n wrapAnsi,\n} from \"@zaly/shared/ansi\"\nimport { ansiBg, styleAnsi } from \"../style/ansi.ts\"\n\nexport type WrapMode = \"word\" | \"char\" | \"none\"\n\nconst ELLIPSIS = \"…\"\nconst TAB_WIDTH = 4\n\nexport function formatText(\n text: string,\n opts: {\n wrap?: WrapMode\n width: number\n style?: StyleBuilder\n /** When wrapping, preserve the indentation of the original text. */\n indent?: boolean\n /** When wrapping ANSI-styled text, pad wrapped fragments to `width`\n * using the background color active at the fragment's last cell.\n * Natural lines that already fit are left unchanged. */\n wrapBg?: boolean\n }\n): string[] {\n const mode = opts.wrap ?? \"word\"\n const lines = splitAnsi(expandTabs(text))\n const ret: string[] = []\n if (mode === \"none\") ret.push(...lines)\n else {\n for (const line of lines) {\n const sw = stringWidth(line)\n\n // Fast path for short lines that don't need wrapping\n if (sw <= opts.width) {\n ret.push(line)\n continue\n }\n\n const stripped = stripAnsi(line)\n\n // Long line that ends in (ansi) whitespace, so just strip\n if (stripped.slice(opts.width).trim() === \"\") {\n ret.push(sliceAnsi(line, 0, opts.width))\n continue\n }\n\n const indentWidth = opts.indent ? stringWidth(stripped.match(/^\\s*/)?.[0] ?? \"\") : 0\n const indent = indentWidth > 0 ? sliceAnsi(line, 0, indentWidth) : \"\"\n const bare = indent ? sliceAnsi(line, indentWidth) : line\n const wrapped = splitAnsi(wrapAnsi(bare, opts.width - indentWidth, { mode }))\n\n // * strip single leading space from wrapped lines after the first\n // * preserve indentation when enabled\n // * pad wrapped lines with bg color if padding is enabled and the line is short\n for (let r = 0; r < wrapped.length; r++) {\n let row = r > 0 && wrapped[r].startsWith(\" \") ? wrapped[r].slice(1) : wrapped[r]\n if (row === \"\" && r > 0 && r < wrapped.length - 1) continue\n if (indent) row = `${indent}${row}`\n if (opts.wrapBg) {\n const rowWidth = stringWidth(row)\n const bg = rowWidth > 0 ? ansiBg(row, rowWidth - 1) : undefined\n if (bg && rowWidth < opts.width) {\n row = `${row}${styleAnsi(\" \".repeat(opts.width - rowWidth), { bg })}`\n }\n }\n ret.push(row)\n }\n }\n }\n return ret.map((line) => (opts.style ? opts.style(line) : line))\n}\n\nexport function expandTabs(s: string, tabSize = TAB_WIDTH): string {\n if (!s.includes(\"\\t\")) return s\n const plain = hasAnsi(s) ? stripAnsi(s) : s\n const spaces: string[] = []\n let col = 0\n\n for (const ch of plain) {\n if (ch === \"\\t\") {\n const n = tabSize - (col % tabSize)\n spaces.push(\" \".repeat(n))\n col += n\n } else if (ch === \"\\n\" || ch === \"\\r\") {\n col = 0\n } else {\n col += stringWidth(ch)\n }\n }\n\n let i = 0\n return s.replaceAll(\"\\t\", () => spaces[i++] ?? \" \".repeat(tabSize))\n}\n\nexport function formatLines(\n text: string | string[],\n opts: {\n numbered?: boolean\n numberOffset?: number\n maxLineLength?: number\n offset?: number\n limit?: number\n more?: false | ((more: number, msg: string) => string)\n style?: StyleBuilder\n } = {}\n): string[] {\n const lines = (typeof text === \"string\" ? splitAnsi(text) : text).map((line) => expandTabs(line))\n const offset = clamp(opts.offset ?? 0, 0, lines.length - 1)\n const limit = clamp(opts.limit ?? lines.length, 1, lines.length - offset)\n const ellipsis = opts.style?.(ELLIPSIS) ?? ELLIPSIS\n let slice = lines\n .slice(offset, offset + limit)\n .map((line) => (opts.maxLineLength ? truncateAnsi(line, opts.maxLineLength, ellipsis) : line))\n\n const numberOffset = opts.numberOffset ?? offset + 1\n const gutterWidth = String(numberOffset + slice.length - 1).length + 2\n\n if (opts.numbered) {\n slice = slice.map((line, i) => {\n const n = numberOffset + i\n const lineo = `${String(n).padStart(gutterWidth - 2)} │`\n return `${opts.style?.(lineo) ?? lineo} ${line}`\n })\n }\n\n if (slice.length < lines.length && opts.more !== false) {\n const moreN = lines.length - slice.length\n let more = `+${moreN} more line${moreN > 1 ? \"s\" : \"\"}`\n more = opts.more?.(moreN, more) ?? more\n if (opts.numbered) {\n const moreGutter = `${\"…\".padStart(gutterWidth - 2)} │`\n slice.push(`${opts.style?.(moreGutter) ?? moreGutter} ${opts.style?.(more) ?? more}`)\n } else slice.push(opts.style?.(more) ?? more)\n }\n\n return slice\n}\n\nfunction maxContent(text: string): number {\n return text.split(\"\\n\").reduce((m, line) => Math.max(m, stringWidth(line)), 0)\n}\n\nfunction minContent(text: string, mode: WrapMode): number | undefined {\n switch (mode) {\n case \"none\": {\n return\n } // can't break, same as max\n case \"char\": {\n return 1\n } // can break anywhere → 1cell floor\n case \"word\": {\n // longest unbreakable run\n return text.split(/\\s+/).reduce((m, word) => Math.max(m, stringWidth(word)), 0)\n }\n default: {\n mode satisfies never // future modes get caught at type-check time\n return undefined\n }\n }\n}\n\nexport function calcLayout(text: string, opts: { wrap?: WrapMode } = {}): Layout {\n text = expandTabs(text)\n const width = maxContent(text)\n return {\n minWidth: minContent(text, opts.wrap ?? \"word\") ?? width,\n width,\n }\n}\n\nexport function countLines(text: string): number {\n let count = 0\n let pos = text.indexOf(\"\\n\")\n while (pos !== -1) {\n count++\n pos = text.indexOf(\"\\n\", pos + 1)\n }\n return count\n}\n\nexport function capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1)\n}\n"],"mappings":"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"}
|
package/dist/text.d.mts
CHANGED
|
@@ -1,33 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//#region src/layout/text.d.ts
|
|
4
|
-
type WrapMode = "word" | "char" | "none";
|
|
5
|
-
declare function formatText(text: string, opts: {
|
|
6
|
-
wrap?: WrapMode;
|
|
7
|
-
width: number;
|
|
8
|
-
style?: StyleBuilder;
|
|
9
|
-
/** When wrapping, preserve the indentation of the original text. */
|
|
10
|
-
indent?: boolean;
|
|
11
|
-
/** When wrapping ANSI-styled text, pad wrapped fragments to `width`
|
|
12
|
-
* using the background color active at the fragment's last cell.
|
|
13
|
-
* Natural lines that already fit are left unchanged. */
|
|
14
|
-
wrapBg?: boolean;
|
|
15
|
-
}): string[];
|
|
16
|
-
declare function expandTabs(s: string, tabSize?: number): string;
|
|
17
|
-
declare function formatLines(text: string | string[], opts?: {
|
|
18
|
-
numbered?: boolean;
|
|
19
|
-
numberOffset?: number;
|
|
20
|
-
maxLineLength?: number;
|
|
21
|
-
offset?: number;
|
|
22
|
-
limit?: number;
|
|
23
|
-
more?: false | ((more: number, msg: string) => string);
|
|
24
|
-
style?: StyleBuilder;
|
|
25
|
-
}): string[];
|
|
26
|
-
declare function calcLayout(text: string, opts?: {
|
|
27
|
-
wrap?: WrapMode;
|
|
28
|
-
}): Layout;
|
|
29
|
-
declare function countLines(text: string): number;
|
|
30
|
-
declare function capitalize(s: string): string;
|
|
31
|
-
//#endregion
|
|
32
|
-
export { WrapMode, calcLayout, capitalize, countLines, expandTabs, formatLines, formatText };
|
|
33
|
-
//# sourceMappingURL=text.d.mts.map
|
|
1
|
+
import { a as expandTabs, i as countLines, n as calcLayout, o as formatLines, r as capitalize, s as formatText, t as WrapMode } from "./text-9lwQeqYr.mjs";
|
|
2
|
+
export { WrapMode, calcLayout, capitalize, countLines, expandTabs, formatLines, formatText };
|
package/dist/text.mjs
CHANGED
|
@@ -1,111 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { clamp } from "@zaly/shared";
|
|
3
|
-
import { hasAnsi, sliceAnsi, splitAnsi, stringWidth, stripAnsi, truncateAnsi, wrapAnsi } from "@zaly/shared/ansi";
|
|
4
|
-
//#region src/layout/text.ts
|
|
5
|
-
const ELLIPSIS = "…";
|
|
6
|
-
const TAB_WIDTH = 4;
|
|
7
|
-
function formatText(text, opts) {
|
|
8
|
-
const mode = opts.wrap ?? "word";
|
|
9
|
-
const lines = splitAnsi(expandTabs(text));
|
|
10
|
-
const ret = [];
|
|
11
|
-
if (mode === "none") ret.push(...lines);
|
|
12
|
-
else for (const line of lines) {
|
|
13
|
-
if (stringWidth(line) <= opts.width) {
|
|
14
|
-
ret.push(line);
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
const stripped = stripAnsi(line);
|
|
18
|
-
if (stripped.slice(opts.width).trim() === "") {
|
|
19
|
-
ret.push(sliceAnsi(line, 0, opts.width));
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
const indentWidth = opts.indent ? stringWidth(stripped.match(/^\s*/)?.[0] ?? "") : 0;
|
|
23
|
-
const indent = indentWidth > 0 ? sliceAnsi(line, 0, indentWidth) : "";
|
|
24
|
-
const wrapped = splitAnsi(wrapAnsi(indent ? sliceAnsi(line, indentWidth) : line, opts.width - indentWidth, { mode }));
|
|
25
|
-
for (let r = 0; r < wrapped.length; r++) {
|
|
26
|
-
let row = r > 0 && wrapped[r].startsWith(" ") ? wrapped[r].slice(1) : wrapped[r];
|
|
27
|
-
if (row === "" && r > 0 && r < wrapped.length - 1) continue;
|
|
28
|
-
if (indent) row = `${indent}${row}`;
|
|
29
|
-
if (opts.wrapBg) {
|
|
30
|
-
const rowWidth = stringWidth(row);
|
|
31
|
-
const bg = rowWidth > 0 ? ansiBg(row, rowWidth - 1) : void 0;
|
|
32
|
-
if (bg && rowWidth < opts.width) row = `${row}${styleAnsi(" ".repeat(opts.width - rowWidth), { bg })}`;
|
|
33
|
-
}
|
|
34
|
-
ret.push(row);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return ret.map((line) => opts.style ? opts.style(line) : line);
|
|
38
|
-
}
|
|
39
|
-
function expandTabs(s, tabSize = TAB_WIDTH) {
|
|
40
|
-
if (!s.includes(" ")) return s;
|
|
41
|
-
const plain = hasAnsi(s) ? stripAnsi(s) : s;
|
|
42
|
-
const spaces = [];
|
|
43
|
-
let col = 0;
|
|
44
|
-
for (const ch of plain) if (ch === " ") {
|
|
45
|
-
const n = tabSize - col % tabSize;
|
|
46
|
-
spaces.push(" ".repeat(n));
|
|
47
|
-
col += n;
|
|
48
|
-
} else if (ch === "\n" || ch === "\r") col = 0;
|
|
49
|
-
else col += stringWidth(ch);
|
|
50
|
-
let i = 0;
|
|
51
|
-
return s.replaceAll(" ", () => spaces[i++] ?? " ".repeat(tabSize));
|
|
52
|
-
}
|
|
53
|
-
function formatLines(text, opts = {}) {
|
|
54
|
-
const lines = (typeof text === "string" ? splitAnsi(text) : text).map((line) => expandTabs(line));
|
|
55
|
-
const offset = clamp(opts.offset ?? 0, 0, lines.length - 1);
|
|
56
|
-
const limit = clamp(opts.limit ?? lines.length, 1, lines.length - offset);
|
|
57
|
-
const ellipsis = opts.style?.(ELLIPSIS) ?? ELLIPSIS;
|
|
58
|
-
let slice = lines.slice(offset, offset + limit).map((line) => opts.maxLineLength ? truncateAnsi(line, opts.maxLineLength, ellipsis) : line);
|
|
59
|
-
const numberOffset = opts.numberOffset ?? offset + 1;
|
|
60
|
-
const gutterWidth = String(numberOffset + slice.length - 1).length + 2;
|
|
61
|
-
if (opts.numbered) slice = slice.map((line, i) => {
|
|
62
|
-
const n = numberOffset + i;
|
|
63
|
-
const lineo = `${String(n).padStart(gutterWidth - 2)} │`;
|
|
64
|
-
return `${opts.style?.(lineo) ?? lineo} ${line}`;
|
|
65
|
-
});
|
|
66
|
-
if (slice.length < lines.length && opts.more !== false) {
|
|
67
|
-
const moreN = lines.length - slice.length;
|
|
68
|
-
let more = `+${moreN} more line${moreN > 1 ? "s" : ""}`;
|
|
69
|
-
more = opts.more?.(moreN, more) ?? more;
|
|
70
|
-
if (opts.numbered) {
|
|
71
|
-
const moreGutter = `${"…".padStart(gutterWidth - 2)} │`;
|
|
72
|
-
slice.push(`${opts.style?.(moreGutter) ?? moreGutter} ${opts.style?.(more) ?? more}`);
|
|
73
|
-
} else slice.push(opts.style?.(more) ?? more);
|
|
74
|
-
}
|
|
75
|
-
return slice;
|
|
76
|
-
}
|
|
77
|
-
function maxContent(text) {
|
|
78
|
-
return text.split("\n").reduce((m, line) => Math.max(m, stringWidth(line)), 0);
|
|
79
|
-
}
|
|
80
|
-
function minContent(text, mode) {
|
|
81
|
-
switch (mode) {
|
|
82
|
-
case "none": return;
|
|
83
|
-
case "char": return 1;
|
|
84
|
-
case "word": return text.split(/\s+/).reduce((m, word) => Math.max(m, stringWidth(word)), 0);
|
|
85
|
-
default: return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function calcLayout(text, opts = {}) {
|
|
89
|
-
text = expandTabs(text);
|
|
90
|
-
const width = maxContent(text);
|
|
91
|
-
return {
|
|
92
|
-
minWidth: minContent(text, opts.wrap ?? "word") ?? width,
|
|
93
|
-
width
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
function countLines(text) {
|
|
97
|
-
let count = 0;
|
|
98
|
-
let pos = text.indexOf("\n");
|
|
99
|
-
while (pos !== -1) {
|
|
100
|
-
count++;
|
|
101
|
-
pos = text.indexOf("\n", pos + 1);
|
|
102
|
-
}
|
|
103
|
-
return count;
|
|
104
|
-
}
|
|
105
|
-
function capitalize(s) {
|
|
106
|
-
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
107
|
-
}
|
|
108
|
-
//#endregion
|
|
1
|
+
import { a as formatLines, i as expandTabs, n as capitalize, o as formatText, r as countLines, t as calcLayout } from "./text-q26V6_Sc.mjs";
|
|
109
2
|
export { calcLayout, capitalize, countLines, expandTabs, formatLines, formatText };
|
|
110
|
-
|
|
111
|
-
//# sourceMappingURL=text.mjs.map
|
package/dist/themes.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as loadTheme, r as themeRegistry, t as defaultTheme } from "./registry-
|
|
1
|
+
import { n as loadTheme, r as themeRegistry, t as defaultTheme } from "./registry-CdeLjUo2.mjs";
|
|
2
2
|
export { defaultTheme, loadTheme, themeRegistry };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { S as unwrap, h as memo } from "./reactive-f7NpAi69.mjs";
|
|
2
|
+
import { t as widget } from "./widget-BkMwhY2p.mjs";
|
|
3
|
+
import { n as select } from "./select-m7NE6Rwa.mjs";
|
|
4
|
+
import { stringWidth } from "@zaly/shared/ansi";
|
|
5
|
+
//#region src/widgets/tree.ts
|
|
6
|
+
const icons = {
|
|
7
|
+
last: "╰─",
|
|
8
|
+
middle: "├─",
|
|
9
|
+
vertical: "│ "
|
|
10
|
+
};
|
|
11
|
+
var Tree = class {
|
|
12
|
+
root;
|
|
13
|
+
nodes = /* @__PURE__ */ new Map();
|
|
14
|
+
items = [];
|
|
15
|
+
constructor(root) {
|
|
16
|
+
this.root = root;
|
|
17
|
+
this.#build(root, void 0, true);
|
|
18
|
+
}
|
|
19
|
+
#build(item, parent, last) {
|
|
20
|
+
this.nodes.set(item, {
|
|
21
|
+
last,
|
|
22
|
+
parent
|
|
23
|
+
});
|
|
24
|
+
this.items.push(item);
|
|
25
|
+
const children = item.children ?? [];
|
|
26
|
+
for (let c = 0; c < children.length; c++) {
|
|
27
|
+
const child = children[c];
|
|
28
|
+
this.#build(child, item, c === children.length - 1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
node(item) {
|
|
32
|
+
return this.nodes.get(item);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const tree = widget((props) => {
|
|
36
|
+
const builder = memo(() => new Tree(unwrap(props.tree)));
|
|
37
|
+
const items = memo(() => props.root === true ? builder().items : builder().items.slice(1));
|
|
38
|
+
const activeFn = typeof props.active === "function" ? props.active : (i) => i === props.active;
|
|
39
|
+
const active = props.active ? items().findIndex((i) => activeFn(i)) : -1;
|
|
40
|
+
const ret = select({
|
|
41
|
+
...props,
|
|
42
|
+
active: active === -1 ? 0 : active,
|
|
43
|
+
items
|
|
44
|
+
});
|
|
45
|
+
ret.extendRenderer((prev) => (item, ctx) => {
|
|
46
|
+
const t = builder();
|
|
47
|
+
const path = [];
|
|
48
|
+
const s = ctx.style;
|
|
49
|
+
const itemNode = t.node(item);
|
|
50
|
+
let n = t.node(item);
|
|
51
|
+
while (n?.parent) {
|
|
52
|
+
if (props.root !== true && n.parent === props.tree) break;
|
|
53
|
+
let icon = "";
|
|
54
|
+
if (n !== itemNode) icon = n.last ? " " : icons.vertical;
|
|
55
|
+
else icon = n.last ? icons.last : icons.middle;
|
|
56
|
+
path.unshift(icon);
|
|
57
|
+
n = t.node(n.parent);
|
|
58
|
+
}
|
|
59
|
+
const prefix = path.join("");
|
|
60
|
+
const prefixWidth = stringWidth(prefix);
|
|
61
|
+
const text = prev(item, {
|
|
62
|
+
...ctx,
|
|
63
|
+
prefixWidth,
|
|
64
|
+
width: ctx.width - prefixWidth
|
|
65
|
+
}).replace(/\s/g, " ");
|
|
66
|
+
return `${s.gutter(prefix)}${text}`;
|
|
67
|
+
});
|
|
68
|
+
return Object.assign(ret, { items });
|
|
69
|
+
});
|
|
70
|
+
//#endregion
|
|
71
|
+
export { tree as t };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=tree-Bo1afYhe.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-Bo1afYhe.mjs","names":["#build"],"sources":["../src/widgets/tree.ts"],"sourcesContent":["import type { Reactive } from \"../core/reactive.ts\"\nimport type { Option, Select, SelectState } from \"./select.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { memo, unwrap } from \"../core/reactive.ts\"\nimport { select } from \"./select.ts\"\nimport { widget } from \"./widget.ts\"\n\nexport type TreeItem<T extends Option = Option> = T & {\n children?: TreeItem<T>[]\n icon?: string\n}\n\nexport type TreeProps<T extends TreeItem = TreeItem> = Omit<SelectState<T>, \"items\" | \"active\"> & {\n tree: Reactive<T>\n /** If true, the root node will be rendered and selectable. Defaults to false. */\n root?: boolean\n active?: T | ((item: T) => boolean)\n}\n\nconst icons = {\n // last: \"└─\",\n last: \"╰─\",\n middle: \"├─\",\n vertical: \"│ \",\n}\n\nexport type TreeNode<T extends TreeItem> = {\n last?: boolean\n parent?: T\n}\n\nclass Tree<T extends TreeItem> {\n nodes = new Map<T, TreeNode<T>>()\n items: T[] = []\n\n constructor(public root: T) {\n this.#build(root, undefined, true)\n }\n\n #build(item: T, parent?: T, last?: boolean) {\n this.nodes.set(item, { last, parent })\n this.items.push(item)\n const children = item.children ?? []\n for (let c = 0; c < children.length; c++) {\n const child = children[c] as T\n this.#build(child, item, c === children.length - 1)\n }\n }\n\n node(item: T) {\n return this.nodes.get(item)\n }\n}\n\nexport const tree = widget(\n <T extends TreeItem>(props: TreeProps<T>): Select<T> & { items: Reactive<T[]> } => {\n const builder = memo(() => new Tree(unwrap(props.tree)))\n const items = memo(() => (props.root === true ? builder().items : builder().items.slice(1)))\n\n const activeFn =\n typeof props.active === \"function\" ? props.active : (i: T) => i === props.active\n const active = props.active ? items().findIndex((i) => activeFn(i)) : -1\n\n const ret = select({ ...props, active: active === -1 ? 0 : active, items })\n\n ret.extendRenderer((prev) => (item, ctx) => {\n const t = builder()\n const path: string[] = []\n const s = ctx.style\n const itemNode = t.node(item)\n let n = t.node(item)\n while (n?.parent) {\n if (props.root !== true && n.parent === props.tree) break\n let icon = \"\"\n if (n !== itemNode) icon = n.last ? \" \" : icons.vertical\n else icon = n.last ? icons.last : icons.middle\n path.unshift(icon)\n n = t.node(n.parent)\n }\n const prefix = path.join(\"\")\n const prefixWidth = stringWidth(prefix)\n const text = prev(item, { ...ctx, prefixWidth, width: ctx.width - prefixWidth }).replace(\n /\\s/g,\n \" \"\n )\n return `${s.gutter(prefix)}${text}`\n })\n\n return Object.assign(ret, { items })\n }\n)\n"],"mappings":";;;;;AAoBA,MAAM,QAAQ;CAEZ,MAAM;CACN,QAAQ;CACR,UAAU;AACZ;AAOA,IAAM,OAAN,MAA+B;CAIV;CAHnB,wBAAQ,IAAI,IAAoB;CAChC,QAAa,CAAC;CAEd,YAAY,MAAgB;EAAT,KAAA,OAAA;EACjB,KAAKA,OAAO,MAAM,KAAA,GAAW,IAAI;CACnC;CAEA,OAAO,MAAS,QAAY,MAAgB;EAC1C,KAAK,MAAM,IAAI,MAAM;GAAE;GAAM;EAAO,CAAC;EACrC,KAAK,MAAM,KAAK,IAAI;EACpB,MAAM,WAAW,KAAK,YAAY,CAAC;EACnC,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;GACxC,MAAM,QAAQ,SAAS;GACvB,KAAKA,OAAO,OAAO,MAAM,MAAM,SAAS,SAAS,CAAC;EACpD;CACF;CAEA,KAAK,MAAS;EACZ,OAAO,KAAK,MAAM,IAAI,IAAI;CAC5B;AACF;AAEA,MAAa,OAAO,QACG,UAA8D;CACjF,MAAM,UAAU,WAAW,IAAI,KAAK,OAAO,MAAM,IAAI,CAAC,CAAC;CACvD,MAAM,QAAQ,WAAY,MAAM,SAAS,OAAO,QAAQ,CAAC,CAAC,QAAQ,QAAQ,CAAC,CAAC,MAAM,MAAM,CAAC,CAAE;CAE3F,MAAM,WACJ,OAAO,MAAM,WAAW,aAAa,MAAM,UAAU,MAAS,MAAM,MAAM;CAC5E,MAAM,SAAS,MAAM,SAAS,MAAM,CAAC,CAAC,WAAW,MAAM,SAAS,CAAC,CAAC,IAAI;CAEtE,MAAM,MAAM,OAAO;EAAE,GAAG;EAAO,QAAQ,WAAW,KAAK,IAAI;EAAQ;CAAM,CAAC;CAE1E,IAAI,gBAAgB,UAAU,MAAM,QAAQ;EAC1C,MAAM,IAAI,QAAQ;EAClB,MAAM,OAAiB,CAAC;EACxB,MAAM,IAAI,IAAI;EACd,MAAM,WAAW,EAAE,KAAK,IAAI;EAC5B,IAAI,IAAI,EAAE,KAAK,IAAI;EACnB,OAAO,GAAG,QAAQ;GAChB,IAAI,MAAM,SAAS,QAAQ,EAAE,WAAW,MAAM,MAAM;GACpD,IAAI,OAAO;GACX,IAAI,MAAM,UAAU,OAAO,EAAE,OAAO,OAAO,MAAM;QAC5C,OAAO,EAAE,OAAO,MAAM,OAAO,MAAM;GACxC,KAAK,QAAQ,IAAI;GACjB,IAAI,EAAE,KAAK,EAAE,MAAM;EACrB;EACA,MAAM,SAAS,KAAK,KAAK,EAAE;EAC3B,MAAM,cAAc,YAAY,MAAM;EACtC,MAAM,OAAO,KAAK,MAAM;GAAE,GAAG;GAAK;GAAa,OAAO,IAAI,QAAQ;EAAY,CAAC,CAAC,CAAC,QAC/E,OACA,GACF;EACA,OAAO,GAAG,EAAE,OAAO,MAAM,IAAI;CAC/B,CAAC;CAED,OAAO,OAAO,OAAO,KAAK,EAAE,MAAM,CAAC;AACrC,CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { o as createNode } from "./reactive-f7NpAi69.mjs";
|
|
2
|
+
//#region src/widgets/widget.ts
|
|
3
|
+
/**
|
|
4
|
+
* Type helper for declaring widgets — runtime identity, just `return fn`.
|
|
5
|
+
*
|
|
6
|
+
* Three things it earns:
|
|
7
|
+
* 1. Inference at the declaration site: TS infers `S` from the function
|
|
8
|
+
* parameter and enforces it on every call site.
|
|
9
|
+
* 2. A consistent grep target: every component reads as
|
|
10
|
+
* `export const foo = widget((props) => …)`.
|
|
11
|
+
* 3. A future seam for instrumentation, lifecycle wrappers, devtools.
|
|
12
|
+
*
|
|
13
|
+
* The factory body runs **once** at construction. Reactive content lives
|
|
14
|
+
* in leaf thunks (`text(({ style }) => …)`), not by re-running the body
|
|
15
|
+
* — this is the Solid model, not React's. `ctx` is therefore unavailable
|
|
16
|
+
* inside the factory; reach for it inside leaf thunks where it belongs.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const status = widget((props: { level: "ok" | "warn"; msg: string }) =>
|
|
20
|
+
* text(({ style }) => `${style.bold[props.level]("●")} ${style.dim(props.msg)}`),
|
|
21
|
+
* )
|
|
22
|
+
*
|
|
23
|
+
* status({ level: "ok", msg: "all systems nominal" })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Call-site args derived from `S`:
|
|
28
|
+
* - If `S` declares `children`, `children` is injected from rest args;
|
|
29
|
+
* state at the call site has `children` omitted, then `...children`.
|
|
30
|
+
* - Otherwise, no rest is allowed.
|
|
31
|
+
* - Either way, the state arg is optional when every field of the
|
|
32
|
+
* resolved state is optional (so bare `widget()` works).
|
|
33
|
+
*/
|
|
34
|
+
function widget(fn) {
|
|
35
|
+
return ((...args) => createNode(() => fn(...args)));
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { widget as t };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=widget-BkMwhY2p.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-BkMwhY2p.mjs","names":[],"sources":["../src/widgets/widget.ts"],"sourcesContent":["import type { Node } from \"../core/node.ts\"\n\nimport { createNode } from \"../core/reactive.ts\"\n\nexport type Props = Record<string, unknown>\n\n/**\n * A widget factory: takes some args, returns a `Node`.\n *\n * The default `A = never[]` makes the *bare* `Widget` the universal\n * \"any widget\" type — the `(...args: never[]) => Node` idiom. Every\n * concrete factory (zero-arg, `state + ...children`, single-props) is\n * assignable to it, so it works both as the `widget()` constraint and\n * as a stored value type (`details?: Widget`, invoked zero-arg). Supply\n * `A` explicitly for a precise signature: `Widget<[State<LogState>]>`.\n */\nexport type Widget<A extends unknown[] = never[], N extends Node = Node> = (...args: A) => N\n\n/**\n * Type helper for declaring widgets — runtime identity, just `return fn`.\n *\n * Three things it earns:\n * 1. Inference at the declaration site: TS infers `S` from the function\n * parameter and enforces it on every call site.\n * 2. A consistent grep target: every component reads as\n * `export const foo = widget((props) => …)`.\n * 3. A future seam for instrumentation, lifecycle wrappers, devtools.\n *\n * The factory body runs **once** at construction. Reactive content lives\n * in leaf thunks (`text(({ style }) => …)`), not by re-running the body\n * — this is the Solid model, not React's. `ctx` is therefore unavailable\n * inside the factory; reach for it inside leaf thunks where it belongs.\n *\n * ```ts\n * const status = widget((props: { level: \"ok\" | \"warn\"; msg: string }) =>\n * text(({ style }) => `${style.bold[props.level](\"●\")} ${style.dim(props.msg)}`),\n * )\n *\n * status({ level: \"ok\", msg: \"all systems nominal\" })\n * ```\n */\n/**\n * Call-site args derived from `S`:\n * - If `S` declares `children`, `children` is injected from rest args;\n * state at the call site has `children` omitted, then `...children`.\n * - Otherwise, no rest is allowed.\n * - Either way, the state arg is optional when every field of the\n * resolved state is optional (so bare `widget()` works).\n */\n\nexport function widget<T extends Widget>(fn: T): T {\n return ((...args: Parameters<T>) => createNode(() => fn(...args))) as T\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAgB,OAAyB,IAAU;CACjD,SAAS,GAAG,SAAwB,iBAAiB,GAAG,GAAG,IAAI,CAAC;AAClE"}
|