@zaly/tui 0.0.4
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 +59 -0
- package/assets/schemas/theme.schema.json +899 -0
- package/assets/themes/LICENSE-tokyonight.txt +201 -0
- package/assets/themes/NOTICES.md +69 -0
- package/assets/themes/ayu-dark.json +119 -0
- package/assets/themes/catppuccin-frappe.json +124 -0
- package/assets/themes/catppuccin-latte.json +124 -0
- package/assets/themes/catppuccin-macchiato.json +124 -0
- package/assets/themes/catppuccin-mocha.json +124 -0
- package/assets/themes/dracula-soft.json +119 -0
- package/assets/themes/dracula.json +119 -0
- package/assets/themes/everforest-dark.json +118 -0
- package/assets/themes/everforest-light.json +118 -0
- package/assets/themes/github-dark.json +123 -0
- package/assets/themes/github-light.json +123 -0
- package/assets/themes/gruvbox-dark-medium.json +112 -0
- package/assets/themes/kanagawa-dragon.json +115 -0
- package/assets/themes/kanagawa-wave.json +115 -0
- package/assets/themes/material-theme-palenight.json +114 -0
- package/assets/themes/min-dark.json +55 -0
- package/assets/themes/min-light.json +59 -0
- package/assets/themes/monokai.json +116 -0
- package/assets/themes/night-owl.json +116 -0
- package/assets/themes/nord.json +101 -0
- package/assets/themes/one-dark-pro.json +110 -0
- package/assets/themes/one-light.json +108 -0
- package/assets/themes/poimandres.json +123 -0
- package/assets/themes/rose-pine.json +98 -0
- package/assets/themes/solarized-dark.json +116 -0
- package/assets/themes/solarized-light.json +116 -0
- package/assets/themes/synthwave-84.json +100 -0
- package/assets/themes/tokyonight-day.json +157 -0
- package/assets/themes/tokyonight-moon.json +157 -0
- package/assets/themes/tokyonight-night.json +157 -0
- package/assets/themes/tokyonight-storm.json +157 -0
- package/assets/themes/vesper.json +103 -0
- package/assets/themes/vitesse-dark.json +126 -0
- package/assets/themes/vitesse-light.json +126 -0
- package/dist/ansi-Clq0ozyA.mjs +206 -0
- package/dist/ansi-Clq0ozyA.mjs.map +1 -0
- package/dist/ansi.d.mts +48 -0
- package/dist/ansi.d.mts.map +1 -0
- package/dist/ansi.mjs +2 -0
- package/dist/autocomplete-Dfw-yQDa.d.mts +100 -0
- package/dist/autocomplete-Dfw-yQDa.d.mts.map +1 -0
- package/dist/box-DyaByEiG.mjs +386 -0
- package/dist/box-DyaByEiG.mjs.map +1 -0
- package/dist/builder-D49ECBEf.mjs +385 -0
- package/dist/builder-D49ECBEf.mjs.map +1 -0
- package/dist/callbacks-CNjGNxVw.mjs +279 -0
- package/dist/callbacks-CNjGNxVw.mjs.map +1 -0
- package/dist/client-D6C_uczF.mjs +142 -0
- package/dist/client-D6C_uczF.mjs.map +1 -0
- package/dist/clipboard.d.mts +104 -0
- package/dist/clipboard.d.mts.map +1 -0
- package/dist/clipboard.mjs +404 -0
- package/dist/clipboard.mjs.map +1 -0
- package/dist/flex-Bguo-fUe.mjs +202 -0
- package/dist/flex-Bguo-fUe.mjs.map +1 -0
- package/dist/image-Cm5T-uw-.mjs +108 -0
- package/dist/image-Cm5T-uw-.mjs.map +1 -0
- package/dist/image.d.mts +123 -0
- package/dist/image.d.mts.map +1 -0
- package/dist/image.mjs +2 -0
- package/dist/index.d.mts +64 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +222 -0
- package/dist/index.mjs.map +1 -0
- package/dist/input-B5Vy-iaa.d.mts +141 -0
- package/dist/input-B5Vy-iaa.d.mts.map +1 -0
- package/dist/inspect-D1d9t-ni.mjs +101 -0
- package/dist/inspect-D1d9t-ni.mjs.map +1 -0
- package/dist/inspect-D6FW_QUo.d.mts +67 -0
- package/dist/inspect-D6FW_QUo.d.mts.map +1 -0
- package/dist/kitty-JbCJC0iQ.mjs +673 -0
- package/dist/kitty-JbCJC0iQ.mjs.map +1 -0
- package/dist/markdown.d.mts +24 -0
- package/dist/markdown.d.mts.map +1 -0
- package/dist/markdown.mjs +2 -0
- package/dist/md.bun.d.mts +14 -0
- package/dist/md.bun.d.mts.map +1 -0
- package/dist/md.bun.mjs +28 -0
- package/dist/md.bun.mjs.map +1 -0
- package/dist/md.node.d.mts +14 -0
- package/dist/md.node.d.mts.map +1 -0
- package/dist/md.node.mjs +126 -0
- package/dist/md.node.mjs.map +1 -0
- package/dist/node-BZ7f6HzU.mjs +316 -0
- package/dist/node-BZ7f6HzU.mjs.map +1 -0
- package/dist/picker-D8OLb6kZ.d.mts +36 -0
- package/dist/picker-D8OLb6kZ.d.mts.map +1 -0
- package/dist/picker-ndVVt6qF.mjs +621 -0
- package/dist/picker-ndVVt6qF.mjs.map +1 -0
- package/dist/queries-DH3bj74K.d.mts +1963 -0
- package/dist/queries-DH3bj74K.d.mts.map +1 -0
- package/dist/reactive-f7NpAi69.mjs +623 -0
- package/dist/reactive-f7NpAi69.mjs.map +1 -0
- package/dist/registry-U654j61Y.mjs +356 -0
- package/dist/registry-U654j61Y.mjs.map +1 -0
- package/dist/render-DxVLqqB9.mjs +74 -0
- package/dist/render-DxVLqqB9.mjs.map +1 -0
- package/dist/renderer-CdLCkJB-.mjs +42 -0
- package/dist/renderer-CdLCkJB-.mjs.map +1 -0
- package/dist/renderer-CxL4xbkl.mjs +2644 -0
- package/dist/renderer-CxL4xbkl.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/search-FvGnswRl.d.mts +49 -0
- package/dist/search-FvGnswRl.d.mts.map +1 -0
- package/dist/select-CbmAZdpt.d.mts +116 -0
- package/dist/select-CbmAZdpt.d.mts.map +1 -0
- package/dist/services/logger.d.mts +2 -0
- package/dist/services/logger.mjs +71 -0
- package/dist/services/logger.mjs.map +1 -0
- package/dist/services/notifier.d.mts +17 -0
- package/dist/services/notifier.d.mts.map +1 -0
- package/dist/services/notifier.mjs +71 -0
- package/dist/services/notifier.mjs.map +1 -0
- package/dist/services/picker.d.mts +36 -0
- package/dist/services/picker.d.mts.map +1 -0
- package/dist/services/picker.mjs +137 -0
- package/dist/services/picker.mjs.map +1 -0
- package/dist/services/prompt.d.mts +30 -0
- package/dist/services/prompt.d.mts.map +1 -0
- package/dist/services/prompt.mjs +83 -0
- package/dist/services/prompt.mjs.map +1 -0
- package/dist/shiki-BrjVUkeh.mjs +423 -0
- package/dist/shiki-BrjVUkeh.mjs.map +1 -0
- package/dist/size-D8JogYLy.mjs +34 -0
- package/dist/size-D8JogYLy.mjs.map +1 -0
- package/dist/style.d.mts +10 -0
- package/dist/style.d.mts.map +1 -0
- package/dist/style.mjs +4 -0
- package/dist/text.d.mts +33 -0
- package/dist/text.d.mts.map +1 -0
- package/dist/text.mjs +111 -0
- package/dist/text.mjs.map +1 -0
- package/dist/theme.config-CU3B50HN.mjs +832 -0
- package/dist/theme.config-CU3B50HN.mjs.map +1 -0
- package/dist/themes.d.mts +113 -0
- package/dist/themes.d.mts.map +1 -0
- package/dist/themes.mjs +2 -0
- package/dist/tree-C28QOVIK.d.mts +23 -0
- package/dist/tree-C28QOVIK.d.mts.map +1 -0
- package/dist/types-B9Eh7k2v.d.mts +212 -0
- package/dist/types-B9Eh7k2v.d.mts.map +1 -0
- package/dist/types-D3jk1rtk.d.mts +140 -0
- package/dist/types-D3jk1rtk.d.mts.map +1 -0
- package/dist/utils-3M4D_AXZ.mjs +40 -0
- package/dist/utils-3M4D_AXZ.mjs.map +1 -0
- package/dist/widgets/autocomplete.d.mts +2 -0
- package/dist/widgets/autocomplete.mjs +165 -0
- package/dist/widgets/autocomplete.mjs.map +1 -0
- package/dist/widgets/box.d.mts +2 -0
- package/dist/widgets/box.mjs +2 -0
- package/dist/widgets/code.d.mts +48 -0
- package/dist/widgets/code.d.mts.map +1 -0
- package/dist/widgets/code.mjs +59 -0
- package/dist/widgets/code.mjs.map +1 -0
- package/dist/widgets/completions/actions.d.mts +41 -0
- package/dist/widgets/completions/actions.d.mts.map +1 -0
- package/dist/widgets/completions/actions.mjs +48 -0
- package/dist/widgets/completions/actions.mjs.map +1 -0
- package/dist/widgets/completions/files.d.mts +29 -0
- package/dist/widgets/completions/files.d.mts.map +1 -0
- package/dist/widgets/completions/files.mjs +42 -0
- package/dist/widgets/completions/files.mjs.map +1 -0
- package/dist/widgets/completions/github.d.mts +64 -0
- package/dist/widgets/completions/github.d.mts.map +1 -0
- package/dist/widgets/completions/github.mjs +137 -0
- package/dist/widgets/completions/github.mjs.map +1 -0
- package/dist/widgets/completions/index.d.mts +4 -0
- package/dist/widgets/completions/index.mjs +4 -0
- package/dist/widgets/diff.d.mts +57 -0
- package/dist/widgets/diff.d.mts.map +1 -0
- package/dist/widgets/diff.mjs +199 -0
- package/dist/widgets/diff.mjs.map +1 -0
- package/dist/widgets/divider.d.mts +11 -0
- package/dist/widgets/divider.d.mts.map +1 -0
- package/dist/widgets/divider.mjs +9 -0
- package/dist/widgets/divider.mjs.map +1 -0
- package/dist/widgets/image.d.mts +37 -0
- package/dist/widgets/image.d.mts.map +1 -0
- package/dist/widgets/image.mjs +77 -0
- package/dist/widgets/image.mjs.map +1 -0
- package/dist/widgets/input.d.mts +2 -0
- package/dist/widgets/input.mjs +497 -0
- package/dist/widgets/input.mjs.map +1 -0
- package/dist/widgets/log.d.mts +2 -0
- package/dist/widgets/log.mjs +130 -0
- package/dist/widgets/log.mjs.map +1 -0
- package/dist/widgets/markdown.d.mts +45 -0
- package/dist/widgets/markdown.d.mts.map +1 -0
- package/dist/widgets/markdown.mjs +124 -0
- package/dist/widgets/markdown.mjs.map +1 -0
- package/dist/widgets/overlay.d.mts +2 -0
- package/dist/widgets/overlay.mjs +18 -0
- package/dist/widgets/overlay.mjs.map +1 -0
- package/dist/widgets/picker.d.mts +2 -0
- package/dist/widgets/picker.mjs +2 -0
- package/dist/widgets/progress.d.mts +48 -0
- package/dist/widgets/progress.d.mts.map +1 -0
- package/dist/widgets/progress.mjs +48 -0
- package/dist/widgets/progress.mjs.map +1 -0
- package/dist/widgets/select.d.mts +2 -0
- package/dist/widgets/select.mjs +195 -0
- package/dist/widgets/select.mjs.map +1 -0
- package/dist/widgets/show.d.mts +55 -0
- package/dist/widgets/show.d.mts.map +1 -0
- package/dist/widgets/show.mjs +85 -0
- package/dist/widgets/show.mjs.map +1 -0
- package/dist/widgets/spinner.d.mts +65 -0
- package/dist/widgets/spinner.d.mts.map +1 -0
- package/dist/widgets/spinner.mjs +147 -0
- package/dist/widgets/spinner.mjs.map +1 -0
- package/dist/widgets/text.d.mts +2 -0
- package/dist/widgets/text.mjs +32 -0
- package/dist/widgets/text.mjs.map +1 -0
- package/dist/widgets/tree.d.mts +2 -0
- package/dist/widgets/tree.mjs +73 -0
- package/dist/widgets/tree.mjs.map +1 -0
- package/dist/widgets/widget.d.mts +49 -0
- package/dist/widgets/widget.d.mts.map +1 -0
- package/dist/widgets/widget.mjs +40 -0
- package/dist/widgets/widget.mjs.map +1 -0
- package/dist/worker-DjHuQSbY.mjs +27 -0
- package/dist/worker-DjHuQSbY.mjs.map +1 -0
- package/package.json +117 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { n as box } from "../box-DyaByEiG.mjs";
|
|
2
|
+
import { text } from "./text.mjs";
|
|
3
|
+
import { markdown } from "./markdown.mjs";
|
|
4
|
+
import { widget } from "./widget.mjs";
|
|
5
|
+
import { stringWidth } from "@zaly/shared/ansi";
|
|
6
|
+
import { hasColors } from "@zaly/shared/env";
|
|
7
|
+
//#region src/widgets/log.ts
|
|
8
|
+
/** Per-level default prefix style + icon + color. Matches rekal's
|
|
9
|
+
* `defaultStyles` table, adapted to zaly's theme slots. */
|
|
10
|
+
const defaultLogStyles = {
|
|
11
|
+
cancel: {
|
|
12
|
+
color: "warn",
|
|
13
|
+
icon: "✖ ",
|
|
14
|
+
style: "icon"
|
|
15
|
+
},
|
|
16
|
+
debug: {
|
|
17
|
+
color: "info",
|
|
18
|
+
icon: "⚙ ",
|
|
19
|
+
style: "prompt"
|
|
20
|
+
},
|
|
21
|
+
error: {
|
|
22
|
+
color: "error",
|
|
23
|
+
icon: "✖ ",
|
|
24
|
+
style: "badge",
|
|
25
|
+
textColor: "error"
|
|
26
|
+
},
|
|
27
|
+
fatal: {
|
|
28
|
+
color: "error",
|
|
29
|
+
icon: "☢ ",
|
|
30
|
+
style: "badge"
|
|
31
|
+
},
|
|
32
|
+
info: {
|
|
33
|
+
color: "info",
|
|
34
|
+
icon: "ℹ ",
|
|
35
|
+
style: "icon"
|
|
36
|
+
},
|
|
37
|
+
log: {
|
|
38
|
+
color: "muted",
|
|
39
|
+
icon: "●",
|
|
40
|
+
style: "icon"
|
|
41
|
+
},
|
|
42
|
+
success: {
|
|
43
|
+
color: "success",
|
|
44
|
+
icon: "✔ ",
|
|
45
|
+
style: "icon"
|
|
46
|
+
},
|
|
47
|
+
trace: {
|
|
48
|
+
color: "muted",
|
|
49
|
+
icon: "⠿",
|
|
50
|
+
style: "icon",
|
|
51
|
+
textColor: "muted"
|
|
52
|
+
},
|
|
53
|
+
warn: {
|
|
54
|
+
color: "warn",
|
|
55
|
+
icon: "⚠",
|
|
56
|
+
style: "badge",
|
|
57
|
+
textColor: "warn"
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const noColorStyles = {
|
|
61
|
+
badge: "title",
|
|
62
|
+
icon: "text",
|
|
63
|
+
notif: "text",
|
|
64
|
+
prompt: "text",
|
|
65
|
+
text: "text",
|
|
66
|
+
title: "text"
|
|
67
|
+
};
|
|
68
|
+
function renderPrefix(s, ctx) {
|
|
69
|
+
const style = ctx.style;
|
|
70
|
+
const base = defaultLogStyles[s.level];
|
|
71
|
+
let ls = s.style ?? base.style;
|
|
72
|
+
if (!hasColors) ls = noColorStyles[ls];
|
|
73
|
+
let icon = s.icon ?? base.icon ?? "";
|
|
74
|
+
icon = icon === "" ? "" : `${icon}${" ".repeat(2 - stringWidth(icon))}`;
|
|
75
|
+
const color = s.color ?? base.color ?? "inherit";
|
|
76
|
+
let styledPrefix = "";
|
|
77
|
+
if (ls === "badge") styledPrefix = style.bg(color).fg("black").bold(` ${s.level} `);
|
|
78
|
+
else if (ls === "icon") styledPrefix = style.fg(color)(icon);
|
|
79
|
+
else if (ls === "prompt") styledPrefix = style.fg(color).bold(`${icon} ${s.level}`);
|
|
80
|
+
else if (ls === "title") styledPrefix = style.fg(color).bold(`${s.level}:`);
|
|
81
|
+
else if (ls === "notif") styledPrefix = style.fg(color).bold(`${icon} ${s.title ?? s.level}`);
|
|
82
|
+
if (s.prefix) styledPrefix = s.prefix + styledPrefix;
|
|
83
|
+
if (s.title && ls !== "notif") styledPrefix += style.bold(` ${s.title}`);
|
|
84
|
+
return styledPrefix;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A single log entry — level-styled prefix (icon / badge / prompt / title /
|
|
88
|
+
* plain text) followed by the body content. The body can be any Node, so
|
|
89
|
+
* callers can stack markdown, code, images, etc. inside a log line.
|
|
90
|
+
*
|
|
91
|
+
* ```ts
|
|
92
|
+
* log({ level: "info", content: "hello" })
|
|
93
|
+
* log({ level: "error", content: markdown("**boom**") })
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
const log = widget((state, ...children) => {
|
|
97
|
+
const base = defaultLogStyles[state.level];
|
|
98
|
+
const color = state.color ?? base.color ?? "inherit";
|
|
99
|
+
const s = state;
|
|
100
|
+
const textColor = state.textColor ?? (s.style === "notif" ? "inherit" : base.textColor);
|
|
101
|
+
const style = {
|
|
102
|
+
style: textColor ? { fg: textColor } : void 0,
|
|
103
|
+
width: "fill"
|
|
104
|
+
};
|
|
105
|
+
if (state.content !== "") children = [state.markdown ?? true ? markdown(state.content, style) : text(state.content, style), ...children];
|
|
106
|
+
return box({
|
|
107
|
+
...s.style === "notif" ? {
|
|
108
|
+
border: "single",
|
|
109
|
+
borderStyle: { fg: color },
|
|
110
|
+
borderTitle: (ctx) => renderPrefix(state, ctx),
|
|
111
|
+
borderTitleAlign: "center",
|
|
112
|
+
padding: [0, 1],
|
|
113
|
+
width: "fill"
|
|
114
|
+
} : void 0,
|
|
115
|
+
flexDirection: "row",
|
|
116
|
+
gap: 1,
|
|
117
|
+
visible: state.visible
|
|
118
|
+
}, s.style === "notif" ? void 0 : text((ctx) => renderPrefix(state, ctx), {
|
|
119
|
+
flexShrink: 0,
|
|
120
|
+
wrap: "none"
|
|
121
|
+
}), box({
|
|
122
|
+
flexDirection: "column",
|
|
123
|
+
flexGrow: 1,
|
|
124
|
+
gap: 1
|
|
125
|
+
}, ...children));
|
|
126
|
+
});
|
|
127
|
+
//#endregion
|
|
128
|
+
export { log };
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=log.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.mjs","names":[],"sources":["../../src/widgets/log.ts"],"sourcesContent":["import type { LogLevel } from \"@zaly/shared/logger\"\nimport type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Node } from \"../core/node.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Color } from \"../style/types.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { hasColors } from \"@zaly/shared/env\"\nimport { box } from \"./box.ts\"\nimport { markdown } from \"./markdown.ts\"\nimport { text } from \"./text.ts\"\nimport { widget } from \"./widget.ts\"\n\nexport type LogStyle = \"badge\" | \"icon\" | \"prompt\" | \"title\" | \"text\" | \"notif\"\n\nexport interface LogState {\n level: LogLevel\n content: TextContent\n title?: string\n /** Rendering of the per-level prefix chunk. Defaults by level. */\n style?: LogStyle\n /** Single-glyph icon for `icon` / `prompt` styles. */\n icon?: string\n /** Theme slot or color name used to tint the prefix. */\n color?: Color\n /** Extra plain text prepended to the auto prefix (unstyled). */\n prefix?: string\n /** Theme slot or color name for the body text. Defaults by level, except */\n textColor?: Color\n markdown?: boolean\n}\n\ninterface LevelDefaults {\n style: LogStyle\n icon?: string\n color?: Color\n textColor?: Color\n}\n\n/** Per-level default prefix style + icon + color. Matches rekal's\n * `defaultStyles` table, adapted to zaly's theme slots. */\nconst defaultLogStyles: Record<LogLevel, LevelDefaults> = {\n cancel: { color: \"warn\", icon: \"✖ \", style: \"icon\" },\n debug: { color: \"info\", icon: \"⚙ \", style: \"prompt\" },\n error: { color: \"error\", icon: \"✖ \", style: \"badge\", textColor: \"error\" },\n fatal: { color: \"error\", icon: \"☢ \", style: \"badge\" },\n info: { color: \"info\", icon: \"ℹ \", style: \"icon\" },\n log: { color: \"muted\", icon: \"●\", style: \"icon\" },\n success: { color: \"success\", icon: \"✔ \", style: \"icon\" },\n trace: { color: \"muted\", icon: \"⠿\", style: \"icon\", textColor: \"muted\" },\n warn: { color: \"warn\", icon: \"⚠\", style: \"badge\", textColor: \"warn\" },\n}\n\nconst noColorStyles: Record<LogStyle, LogStyle> = {\n badge: \"title\",\n icon: \"text\",\n notif: \"text\",\n prompt: \"text\",\n text: \"text\",\n title: \"text\",\n}\n\nfunction renderPrefix(s: LogState, ctx: RenderCtx): string {\n const style = ctx.style\n\n const base = defaultLogStyles[s.level]\n let ls: LogStyle = s.style ?? base.style\n if (!hasColors) ls = noColorStyles[ls]\n\n let icon = s.icon ?? base.icon ?? \"\"\n icon = icon === \"\" ? \"\" : `${icon}${\" \".repeat(2 - stringWidth(icon))}`\n const color: Color = s.color ?? base.color ?? \"inherit\"\n\n // text doesn't get a prefix\n let styledPrefix = \"\"\n if (ls === \"badge\") {\n styledPrefix = style.bg(color).fg(\"black\").bold(` ${s.level} `)\n } else if (ls === \"icon\") {\n styledPrefix = style.fg(color)(icon)\n } else if (ls === \"prompt\") {\n styledPrefix = style.fg(color).bold(`${icon} ${s.level}`)\n } else if (ls === \"title\") {\n styledPrefix = style.fg(color).bold(`${s.level}:`)\n } else if (ls === \"notif\") {\n styledPrefix = style.fg(color).bold(`${icon} ${s.title ?? s.level}`)\n }\n\n if (s.prefix) styledPrefix = s.prefix + styledPrefix\n if (s.title && ls !== \"notif\") styledPrefix += style.bold(` ${s.title}`)\n return styledPrefix\n}\n\n/**\n * A single log entry — level-styled prefix (icon / badge / prompt / title /\n * plain text) followed by the body content. The body can be any Node, so\n * callers can stack markdown, code, images, etc. inside a log line.\n *\n * ```ts\n * log({ level: \"info\", content: \"hello\" })\n * log({ level: \"error\", content: markdown(\"**boom**\") })\n * ```\n */\nexport const log = widget((state: State<LogState>, ...children: Node[]) => {\n const base = defaultLogStyles[state.level]\n const color: Color = state.color ?? base.color ?? \"inherit\"\n const s = state\n const textColor = state.textColor ?? (s.style === \"notif\" ? \"inherit\" : base.textColor)\n const style = {\n style: textColor\n ? {\n fg: textColor,\n }\n : undefined,\n width: \"fill\",\n } as const\n if (state.content !== \"\") {\n children = [\n (state.markdown ?? true) ? markdown(state.content, style) : text(state.content, style),\n ...children,\n ]\n }\n return box(\n {\n ...(s.style === \"notif\"\n ? {\n border: \"single\",\n borderStyle: { fg: color },\n borderTitle: (ctx) => renderPrefix(state, ctx),\n borderTitleAlign: \"center\",\n padding: [0, 1],\n //style: \"overlay\",\n width: \"fill\",\n }\n : undefined),\n flexDirection: \"row\",\n gap: 1,\n visible: state.visible,\n },\n s.style === \"notif\"\n ? undefined\n : text((ctx) => renderPrefix(state, ctx), { flexShrink: 0, wrap: \"none\" }),\n box({ flexDirection: \"column\", flexGrow: 1, gap: 1 }, ...children)\n )\n})\n"],"mappings":";;;;;;;;;AA0CA,MAAM,mBAAoD;CACxD,QAAQ;EAAE,OAAO;EAAQ,MAAM;EAAM,OAAO;CAAO;CACnD,OAAO;EAAE,OAAO;EAAQ,MAAM;EAAM,OAAO;CAAS;CACpD,OAAO;EAAE,OAAO;EAAS,MAAM;EAAM,OAAO;EAAS,WAAW;CAAQ;CACxE,OAAO;EAAE,OAAO;EAAS,MAAM;EAAM,OAAO;CAAQ;CACpD,MAAM;EAAE,OAAO;EAAQ,MAAM;EAAM,OAAO;CAAO;CACjD,KAAK;EAAE,OAAO;EAAS,MAAM;EAAK,OAAO;CAAO;CAChD,SAAS;EAAE,OAAO;EAAW,MAAM;EAAM,OAAO;CAAO;CACvD,OAAO;EAAE,OAAO;EAAS,MAAM;EAAK,OAAO;EAAQ,WAAW;CAAQ;CACtE,MAAM;EAAE,OAAO;EAAQ,MAAM;EAAK,OAAO;EAAS,WAAW;CAAO;AACtE;AAEA,MAAM,gBAA4C;CAChD,OAAO;CACP,MAAM;CACN,OAAO;CACP,QAAQ;CACR,MAAM;CACN,OAAO;AACT;AAEA,SAAS,aAAa,GAAa,KAAwB;CACzD,MAAM,QAAQ,IAAI;CAElB,MAAM,OAAO,iBAAiB,EAAE;CAChC,IAAI,KAAe,EAAE,SAAS,KAAK;CACnC,IAAI,CAAC,WAAW,KAAK,cAAc;CAEnC,IAAI,OAAO,EAAE,QAAQ,KAAK,QAAQ;CAClC,OAAO,SAAS,KAAK,KAAK,GAAG,OAAO,IAAI,OAAO,IAAI,YAAY,IAAI,CAAC;CACpE,MAAM,QAAe,EAAE,SAAS,KAAK,SAAS;CAG9C,IAAI,eAAe;CACnB,IAAI,OAAO,SACT,eAAe,MAAM,GAAG,KAAK,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,MAAM,EAAE;MACzD,IAAI,OAAO,QAChB,eAAe,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI;MAC9B,IAAI,OAAO,UAChB,eAAe,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,GAAG,EAAE,OAAO;MACnD,IAAI,OAAO,SAChB,eAAe,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE,MAAM,EAAE;MAC5C,IAAI,OAAO,SAChB,eAAe,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,GAAG,EAAE,SAAS,EAAE,OAAO;CAGrE,IAAI,EAAE,QAAQ,eAAe,EAAE,SAAS;CACxC,IAAI,EAAE,SAAS,OAAO,SAAS,gBAAgB,MAAM,KAAK,IAAI,EAAE,OAAO;CACvE,OAAO;AACT;;;;;;;;;;;AAYA,MAAa,MAAM,QAAQ,OAAwB,GAAG,aAAqB;CACzE,MAAM,OAAO,iBAAiB,MAAM;CACpC,MAAM,QAAe,MAAM,SAAS,KAAK,SAAS;CAClD,MAAM,IAAI;CACV,MAAM,YAAY,MAAM,cAAc,EAAE,UAAU,UAAU,YAAY,KAAK;CAC7E,MAAM,QAAQ;EACZ,OAAO,YACH,EACE,IAAI,UACN,IACA,KAAA;EACJ,OAAO;CACT;CACA,IAAI,MAAM,YAAY,IACpB,WAAW,CACR,MAAM,YAAY,OAAQ,SAAS,MAAM,SAAS,KAAK,IAAI,KAAK,MAAM,SAAS,KAAK,GACrF,GAAG,QACL;CAEF,OAAO,IACL;EACE,GAAI,EAAE,UAAU,UACZ;GACE,QAAQ;GACR,aAAa,EAAE,IAAI,MAAM;GACzB,cAAc,QAAQ,aAAa,OAAO,GAAG;GAC7C,kBAAkB;GAClB,SAAS,CAAC,GAAG,CAAC;GAEd,OAAO;EACT,IACA,KAAA;EACJ,eAAe;EACf,KAAK;EACL,SAAS,MAAM;CACjB,GACA,EAAE,UAAU,UACR,KAAA,IACA,MAAM,QAAQ,aAAa,OAAO,GAAG,GAAG;EAAE,YAAY;EAAG,MAAM;CAAO,CAAC,GAC3E,IAAI;EAAE,eAAe;EAAU,UAAU;EAAG,KAAK;CAAE,GAAG,GAAG,QAAQ,CACnE;AACF,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { J as Layout, P as RenderCtx, U as TextContent, X as State, p as Node } from "../queries-DH3bj74K.mjs";
|
|
2
|
+
import { o as AnyStyle } from "../types-B9Eh7k2v.mjs";
|
|
3
|
+
import { WrapMode } from "../text.mjs";
|
|
4
|
+
import { n as MdOptions } from "../types-D3jk1rtk.mjs";
|
|
5
|
+
import { Image } from "./image.mjs";
|
|
6
|
+
//#region src/widgets/markdown.d.ts
|
|
7
|
+
interface MarkdownState {
|
|
8
|
+
/** Markdown source. Accepts a plain string or a reactive accessor —
|
|
9
|
+
* pass `signal()` / `memo()` for streaming content that re-parses on
|
|
10
|
+
* each render and subscribes the node to the signal. */
|
|
11
|
+
content: TextContent;
|
|
12
|
+
/** Options forwarded to `renderMarkdown`. */
|
|
13
|
+
options?: MdOptions;
|
|
14
|
+
/**
|
|
15
|
+
* Enable shiki-backed syntax highlighting for fenced code blocks. Defaults
|
|
16
|
+
* to `true` — code blocks get per-token colors from the shiki theme, with
|
|
17
|
+
* `mdCodeBlock`'s bg applied as a backdrop. Unknown languages fall through
|
|
18
|
+
* as plain text.
|
|
19
|
+
*/
|
|
20
|
+
syntax?: boolean;
|
|
21
|
+
wrap?: WrapMode;
|
|
22
|
+
style?: AnyStyle;
|
|
23
|
+
}
|
|
24
|
+
declare class Markdown extends Node<MarkdownState> {
|
|
25
|
+
#private;
|
|
26
|
+
readonly images: Map<string, Image>;
|
|
27
|
+
constructor(state: State<MarkdownState>);
|
|
28
|
+
protected _render(ctx: RenderCtx): Promise<string[]>;
|
|
29
|
+
layout(ctx: RenderCtx): Promise<Layout>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Render a markdown string as a TUI node. Produces ANSI-styled text that
|
|
33
|
+
* resolves per-element styling through theme slots (`mdHeading1`, `mdCode`,
|
|
34
|
+
* `mdLink`, …). Links become clickable in terminals that support OSC 8.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* markdown("# Hello\n\nThis is **bold** and *italic*.")
|
|
38
|
+
* markdown({ content, wrap: "word", fg: "fg" })
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare function markdown(content: TextContent, style?: Omit<State<MarkdownState>, "content">): Markdown;
|
|
42
|
+
declare function markdown(state: State<MarkdownState>): Markdown;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { Markdown, MarkdownState, markdown };
|
|
45
|
+
//# sourceMappingURL=markdown.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.mts","names":[],"sources":["../../src/widgets/markdown.ts"],"mappings":";;;;;;UAoBiB;;;;EAIf,SAAS;;EAET,UAAU;;;;;;;EAOV;EACA,OAAO;EACP,QAAQ;;cAcG,iBAAiB,KAAK;;WAKxB,QAAM,YAAA;EASf,YAAY,OAAO,MAAM;YAwDT,QAAQ,KAAK,YAAY;EA0C1B,OAAO,KAAK,YAAS,QAAT;;;;;;;;;;;;iBAeb,SACd,SAAS,aACT,QAAQ,KAAK,MAAM,6BAClB;iBACa,SAAS,OAAO,MAAM,iBAAiB"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { S as unwrap, r as createAsync, w as useContext, y as signal } from "../reactive-f7NpAi69.mjs";
|
|
2
|
+
import { t as Node } from "../node-BZ7f6HzU.mjs";
|
|
3
|
+
import { calcLayout, expandTabs, formatText } from "../text.mjs";
|
|
4
|
+
import { textContent } from "./text.mjs";
|
|
5
|
+
import { t as RenderContext } from "../render-DxVLqqB9.mjs";
|
|
6
|
+
import { t as codeToAnsi } from "../shiki-BrjVUkeh.mjs";
|
|
7
|
+
import { n as shikiWorker } from "../client-D6C_uczF.mjs";
|
|
8
|
+
import { hasColors } from "@zaly/shared/env";
|
|
9
|
+
//#region src/widgets/markdown.ts
|
|
10
|
+
var Markdown = class extends Node {
|
|
11
|
+
images = /* @__PURE__ */ new Map();
|
|
12
|
+
#renderer;
|
|
13
|
+
#code = /* @__PURE__ */ new Map();
|
|
14
|
+
#update = signal(0);
|
|
15
|
+
#gen = 0;
|
|
16
|
+
#worker;
|
|
17
|
+
#images;
|
|
18
|
+
constructor(state) {
|
|
19
|
+
super(state);
|
|
20
|
+
const context = useContext(RenderContext);
|
|
21
|
+
this.#images = context?.images;
|
|
22
|
+
this.#worker = createAsync(async () => {
|
|
23
|
+
const update = this.#update.get();
|
|
24
|
+
const todo = [...this.#code.values()].filter((c) => c.gen === this.#gen && !c.result);
|
|
25
|
+
if (!todo.length) return update;
|
|
26
|
+
await Promise.all(todo.map(async (req) => {
|
|
27
|
+
req.ac ??= new AbortController();
|
|
28
|
+
req.inflight ??= codeToAnsi(req.code, req.lang, {
|
|
29
|
+
signal: req.ac.signal,
|
|
30
|
+
theme: req.theme
|
|
31
|
+
});
|
|
32
|
+
try {
|
|
33
|
+
req.result = await req.inflight;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (req.ac.signal.aborted) {
|
|
36
|
+
if (this.#code.get(req.key) === req) this.#code.delete(req.key);
|
|
37
|
+
} else throw error;
|
|
38
|
+
}
|
|
39
|
+
}));
|
|
40
|
+
return update + 1;
|
|
41
|
+
}, { initialValue: 0 });
|
|
42
|
+
}
|
|
43
|
+
/** Find a highlighted result for a prefix of `code` in the cache. This
|
|
44
|
+
* handles streaming updates where the full code block isn't available yet
|
|
45
|
+
* and prevents flashing un-highlighted text on each update. */
|
|
46
|
+
#streamingHighlight(code, lang, theme) {
|
|
47
|
+
let ret;
|
|
48
|
+
for (const c of this.#code.values()) {
|
|
49
|
+
if (!c.result || c.lang !== lang || c.theme !== theme) continue;
|
|
50
|
+
if (code.startsWith(c.code) && c.code.length > (ret?.code.length ?? 0)) ret = c;
|
|
51
|
+
}
|
|
52
|
+
if (ret) ret.gen = this.#gen;
|
|
53
|
+
return ret?.result ? ret.result + code.slice(ret.code.length) : code;
|
|
54
|
+
}
|
|
55
|
+
#highlight(code, lang, theme) {
|
|
56
|
+
if (!lang) return code;
|
|
57
|
+
const key = shikiWorker.key({
|
|
58
|
+
code,
|
|
59
|
+
lang,
|
|
60
|
+
theme
|
|
61
|
+
});
|
|
62
|
+
const ret = this.#code.get(key);
|
|
63
|
+
if (ret) ret.gen = this.#gen;
|
|
64
|
+
if (ret?.result) return ret.result;
|
|
65
|
+
else if (!ret) this.#code.set(key, {
|
|
66
|
+
code,
|
|
67
|
+
gen: this.#gen,
|
|
68
|
+
key,
|
|
69
|
+
lang,
|
|
70
|
+
theme
|
|
71
|
+
});
|
|
72
|
+
return this.#streamingHighlight(code, lang, theme);
|
|
73
|
+
}
|
|
74
|
+
async _render(ctx) {
|
|
75
|
+
if (!this.#renderer) {
|
|
76
|
+
const { MarkdownRenderer } = await import("../renderer-CdLCkJB-.mjs").then((n) => n.n);
|
|
77
|
+
this.#renderer = new MarkdownRenderer({
|
|
78
|
+
...this.state.options,
|
|
79
|
+
parent: this
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
let source = await textContent(this.state.content, ctx);
|
|
83
|
+
source = expandTabs(source);
|
|
84
|
+
let formatted;
|
|
85
|
+
if (!hasColors) formatted = this.#renderer.normalizeEol(source, source);
|
|
86
|
+
else {
|
|
87
|
+
this.#gen++;
|
|
88
|
+
formatted = await this.#renderer.render(source, {
|
|
89
|
+
...ctx,
|
|
90
|
+
highlight: (code, lang) => this.#highlight(code, lang, ctx.style.theme.shiki),
|
|
91
|
+
images: unwrap(this.#images) ?? true
|
|
92
|
+
});
|
|
93
|
+
for (const [k, c] of this.#code.entries()) if (c.gen !== this.#gen) {
|
|
94
|
+
c.ac?.abort();
|
|
95
|
+
if (c.result !== void 0) this.#code.delete(k);
|
|
96
|
+
}
|
|
97
|
+
if (this.#code.values().some((c) => !c.inflight)) {
|
|
98
|
+
this.#update.set(this.#gen);
|
|
99
|
+
this.#worker();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return formatText(formatted, {
|
|
103
|
+
indent: true,
|
|
104
|
+
style: this.state.style ? ctx.style.add(this.state.style) : void 0,
|
|
105
|
+
width: ctx.width,
|
|
106
|
+
wrap: this.state.wrap,
|
|
107
|
+
wrapBg: true
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async layout(ctx) {
|
|
111
|
+
return calcLayout(await textContent(this.state.content, ctx));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
function markdown(first, style) {
|
|
115
|
+
if (typeof first === "string" || typeof first === "function") return new Markdown({
|
|
116
|
+
content: first,
|
|
117
|
+
...style
|
|
118
|
+
});
|
|
119
|
+
return new Markdown(first);
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { Markdown, markdown };
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=markdown.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.mjs","names":["#images","#worker","#update","#code","#gen","#streamingHighlight","#renderer","#highlight"],"sources":["../../src/widgets/markdown.ts"],"sourcesContent":["import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Accessor } from \"../core/reactive.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { WrapMode } from \"../layout/text.ts\"\nimport type { MarkdownRenderer } from \"../markdown/renderer.ts\"\nimport type { MdOptions } from \"../markdown/types.ts\"\nimport type { ShikiTheme } from \"../shiki/types.ts\"\nimport type { AnyStyle } from \"../style/types.ts\"\nimport type { Image } from \"./image.ts\"\nimport type { TextContent } from \"./text.ts\"\n\nimport { hasColors } from \"@zaly/shared/env\"\nimport { Node } from \"../core/node.ts\"\nimport { createAsync, signal, unwrap, useContext } from \"../core/reactive.ts\"\nimport { RenderContext } from \"../core/render.ts\"\nimport { calcLayout, expandTabs, formatText } from \"../layout/text.ts\"\nimport { shikiWorker } from \"../shiki/client.ts\"\nimport { codeToAnsi } from \"../shiki/shiki.ts\"\nimport { textContent } from \"./text.ts\"\n\nexport interface MarkdownState {\n /** Markdown source. Accepts a plain string or a reactive accessor —\n * pass `signal()` / `memo()` for streaming content that re-parses on\n * each render and subscribes the node to the signal. */\n content: TextContent\n /** Options forwarded to `renderMarkdown`. */\n options?: MdOptions\n /**\n * Enable shiki-backed syntax highlighting for fenced code blocks. Defaults\n * to `true` — code blocks get per-token colors from the shiki theme, with\n * `mdCodeBlock`'s bg applied as a backdrop. Unknown languages fall through\n * as plain text.\n */\n syntax?: boolean\n wrap?: WrapMode\n style?: AnyStyle\n}\n\ntype ShikiCode = {\n key: string\n code: string\n lang: string\n theme?: ShikiTheme\n inflight?: Promise<string>\n ac?: AbortController\n result?: string\n gen: number\n}\n\nexport class Markdown extends Node<MarkdownState> {\n // Image nodes cached per-src per-Markdown-instance. Re-rendering the\n // same markdown (streaming updates) reuses the same `Image` — same\n // `placementId`, which the KGP spec guarantees is a flicker-free\n // move/resize of the existing placement.\n readonly images = new Map<string, Image>()\n\n #renderer?: MarkdownRenderer\n #code = new Map<string, ShikiCode>()\n #update = signal(0)\n #gen = 0\n #worker: Accessor<number>\n #images?: Accessor<boolean>\n\n constructor(state: State<MarkdownState>) {\n super(state)\n\n const context = useContext(RenderContext)\n this.#images = context?.images\n\n this.#worker = createAsync(\n async () => {\n const update = this.#update.get() // track\n const todo = [...this.#code.values()].filter((c) => c.gen === this.#gen && !c.result)\n if (!todo.length) return update // nothing to do, skip\n await Promise.all(\n todo.map(async (req) => {\n req.ac ??= new AbortController()\n req.inflight ??= codeToAnsi(req.code, req.lang, {\n signal: req.ac.signal,\n theme: req.theme,\n })\n try {\n req.result = await req.inflight\n } catch (error) {\n if (req.ac.signal.aborted) {\n if (this.#code.get(req.key) === req) this.#code.delete(req.key)\n } else throw error\n }\n })\n )\n return update + 1\n },\n { initialValue: 0 }\n )\n }\n\n /** Find a highlighted result for a prefix of `code` in the cache. This\n * handles streaming updates where the full code block isn't available yet\n * and prevents flashing un-highlighted text on each update. */\n #streamingHighlight(code: string, lang: string, theme?: ShikiTheme) {\n let ret: ShikiCode | undefined\n for (const c of this.#code.values()) {\n if (!c.result || c.lang !== lang || c.theme !== theme) continue\n if (code.startsWith(c.code) && c.code.length > (ret?.code.length ?? 0)) ret = c\n }\n if (ret) ret.gen = this.#gen // bump gen to keep it alive in cache\n return ret?.result ? ret.result + code.slice(ret.code.length) : code\n }\n\n #highlight(code: string, lang?: string, theme?: ShikiTheme): string {\n if (!lang) return code\n const key = shikiWorker.key({ code, lang, theme })\n const ret = this.#code.get(key)\n if (ret) ret.gen = this.#gen // bump gen to keep it alive in cache\n if (ret?.result) return ret.result\n else if (!ret) this.#code.set(key, { code, gen: this.#gen, key, lang, theme })\n return this.#streamingHighlight(code, lang, theme)\n }\n\n protected async _render(ctx: RenderCtx): Promise<string[]> {\n if (!this.#renderer) {\n const { MarkdownRenderer } = await import(\"../markdown/renderer.ts\")\n this.#renderer = new MarkdownRenderer({ ...this.state.options, parent: this })\n }\n let source = await textContent(this.state.content, ctx)\n source = expandTabs(source)\n let formatted: string\n\n if (!hasColors) formatted = this.#renderer.normalizeEol(source, source)\n else {\n this.#gen++ // bump generation to track what is still used\n\n formatted = await this.#renderer.render(source, {\n ...ctx,\n highlight: (code, lang) => this.#highlight(code, lang, ctx.style.theme.shiki),\n images: unwrap(this.#images) ?? true,\n })\n\n // prune old entries\n for (const [k, c] of this.#code.entries())\n if (c.gen !== this.#gen) {\n c.ac?.abort()\n if (c.result !== undefined) this.#code.delete(k)\n }\n\n // Trigger the worker if needed\n if (this.#code.values().some((c) => !c.inflight)) {\n this.#update.set(this.#gen) // trigger worker\n this.#worker() // tracked\n }\n }\n\n return formatText(formatted, {\n indent: true,\n style: this.state.style ? ctx.style.add(this.state.style) : undefined,\n width: ctx.width,\n wrap: this.state.wrap,\n wrapBg: true,\n })\n }\n\n override async layout(ctx: RenderCtx) {\n return calcLayout(await textContent(this.state.content, ctx))\n }\n}\n\n/**\n * Render a markdown string as a TUI node. Produces ANSI-styled text that\n * resolves per-element styling through theme slots (`mdHeading1`, `mdCode`,\n * `mdLink`, …). Links become clickable in terminals that support OSC 8.\n *\n * ```ts\n * markdown(\"# Hello\\n\\nThis is **bold** and *italic*.\")\n * markdown({ content, wrap: \"word\", fg: \"fg\" })\n * ```\n */\nexport function markdown(\n content: TextContent,\n style?: Omit<State<MarkdownState>, \"content\">\n): Markdown\nexport function markdown(state: State<MarkdownState>): Markdown\nexport function markdown(\n first: TextContent | MarkdownState,\n style?: Omit<State<MarkdownState>, \"content\">\n): Markdown {\n // Plain strings and accessor functions go through the content path;\n // a non-function object is the full state form.\n if (typeof first === \"string\" || typeof first === \"function\") {\n return new Markdown({ content: first, ...style })\n }\n return new Markdown(first)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { n as box } from "../box-DyaByEiG.mjs";
|
|
2
|
+
//#region src/widgets/overlay.ts
|
|
3
|
+
function overlay(state, ...children) {
|
|
4
|
+
const node = box({
|
|
5
|
+
visible: false,
|
|
6
|
+
...state
|
|
7
|
+
}, ...children);
|
|
8
|
+
node.withActions({ "overlay.close": {
|
|
9
|
+
desc: "Close overlay",
|
|
10
|
+
fn: () => node.state.removeOnClose ?? true ? node.ctx?.overlay.remove(node) : node.hide(),
|
|
11
|
+
keys: ["esc", "ctrl-c"]
|
|
12
|
+
} });
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { overlay };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=overlay.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.mjs","names":[],"sources":["../../src/widgets/overlay.ts"],"sourcesContent":["import type { Emitter } from \"@zaly/shared\"\nimport type { Node } from \"../core/node.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Box, BoxStyle } from \"./box.ts\"\n\nimport { box } from \"./box.ts\"\n\nexport interface OverlayState {\n /** Absolute column (1-based) of the overlay's left edge. */\n x: number\n /** Absolute row (1-based) of the overlay's top edge. */\n y: number\n /** Higher zIndex paints on top. Default: 0. */\n zIndex?: number\n verticalAnchor?: \"top\" | \"center\" | \"bottom\"\n horizontalAnchor?: \"left\" | \"center\" | \"right\"\n relative?: \"screen\" | \"ui\" | \"stream\"\n /** If true, the overlay will be automatically removed from the renderer when closed.\n * When false, the overlay will remain in the renderer but will be hidden.\n * Default: true. */\n removeOnClose?: boolean\n}\n\ntype OverlayEvents = {\n close: {}\n}\n\nexport type Overlay<T extends Node[] = Node[]> = Box<OverlayState> & {\n children: T\n} & Emitter<OverlayEvents>\n\nexport function overlay<T extends Node[] = Node[]>(\n state: State<OverlayState & BoxStyle>,\n ...children: T\n): Overlay<T> {\n const node = box({ visible: false, ...state }, ...children) as Overlay<T>\n node.withActions({\n \"overlay.close\": {\n desc: \"Close overlay\",\n fn: () => ((node.state.removeOnClose ?? true) ? node.ctx?.overlay.remove(node) : node.hide()),\n keys: [\"esc\", \"ctrl-c\"],\n },\n })\n return node\n}\n"],"mappings":";;AA+BA,SAAgB,QACd,OACA,GAAG,UACS;CACZ,MAAM,OAAO,IAAI;EAAE,SAAS;EAAO,GAAG;CAAM,GAAG,GAAG,QAAQ;CAC1D,KAAK,YAAY,EACf,iBAAiB;EACf,MAAM;EACN,UAAY,KAAK,MAAM,iBAAiB,OAAQ,KAAK,KAAK,QAAQ,OAAO,IAAI,IAAI,KAAK,KAAK;EAC3F,MAAM,CAAC,OAAO,QAAQ;CACxB,EACF,CAAC;CACD,OAAO;AACT"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Nt as Size, P as RenderCtx, X as State, nt as Reactive, p as Node } from "../queries-DH3bj74K.mjs";
|
|
2
|
+
import { c as Color } from "../types-B9Eh7k2v.mjs";
|
|
3
|
+
//#region src/widgets/progress.d.ts
|
|
4
|
+
interface ProgressState {
|
|
5
|
+
/** Current value. Clamped to `[0, total]`. Accepts a signal accessor
|
|
6
|
+
* so callers can drive the bar from reactive state without manual
|
|
7
|
+
* `state.set` plumbing. */
|
|
8
|
+
value: Reactive<number>;
|
|
9
|
+
/** Maximum value. Defaults to `1` — pass a fraction in `[0, 1]` to value. */
|
|
10
|
+
total?: Reactive<number>;
|
|
11
|
+
/**
|
|
12
|
+
* Bar width in cells. Defaults to `fill` (uses the full `ctx.width`).
|
|
13
|
+
* Shrinks to fit if a `label` is provided.
|
|
14
|
+
*/
|
|
15
|
+
width?: Size;
|
|
16
|
+
/**
|
|
17
|
+
* Optional label shown to the right of the bar. Pass a string, or a
|
|
18
|
+
* function to style it inline. `null`/`undefined` hides it; `"auto"`
|
|
19
|
+
* formats as a percentage.
|
|
20
|
+
*/
|
|
21
|
+
label?: string | ((ctx: RenderCtx, fraction: number) => string);
|
|
22
|
+
/** Foreground theme slot for the filled portion. Defaults to `primary`. */
|
|
23
|
+
color?: Color;
|
|
24
|
+
/** Foreground theme slot for the empty portion. Defaults to `muted`. */
|
|
25
|
+
trackColor?: Color;
|
|
26
|
+
/** Filled glyph. Defaults to `█`. */
|
|
27
|
+
complete?: string;
|
|
28
|
+
/** Empty glyph. Defaults to `░`. */
|
|
29
|
+
incomplete?: string;
|
|
30
|
+
}
|
|
31
|
+
declare class Progress extends Node<ProgressState> {
|
|
32
|
+
protected _render(ctx: RenderCtx): string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Factory for `Progress`. Value is required; everything else has a
|
|
36
|
+
* sensible default — `progress({ value: 0.4 })` fills 40% of the
|
|
37
|
+
* available width with `█`, backed by `░` in the theme's `muted` slot.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* progress({ value: done, total, label: "auto" })
|
|
41
|
+
* progress({ value: p, color: "ok", width: 20 })
|
|
42
|
+
* progress({ value: 0.7, label: (ctx, f) => ctx.style.dim(`${f*100|0}%`) })
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
declare function progress(state: State<ProgressState>): Progress;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { Progress, ProgressState, progress };
|
|
48
|
+
//# sourceMappingURL=progress.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.d.mts","names":[],"sources":["../../src/widgets/progress.ts"],"mappings":";;;UAWiB;;;;EAIf,OAAO;;EAEP,QAAQ;;;;;EAKR,QAAQ;;;;;;EAMR,mBAAmB,KAAK,WAAW;;EAEnC,QAAQ;;EAER,aAAa;;EAEb;;EAEA;;cAGW,iBAAiB,KAAK;YACvB,QAAQ,KAAK;;;;;;;;;;;;;iBAwCT,SAAS,OAAO,MAAM,iBAAiB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { S as unwrap } from "../reactive-f7NpAi69.mjs";
|
|
2
|
+
import { t as Node } from "../node-BZ7f6HzU.mjs";
|
|
3
|
+
import { n as resolveSize } from "../size-D8JogYLy.mjs";
|
|
4
|
+
import { stringWidth } from "@zaly/shared/ansi";
|
|
5
|
+
//#region src/widgets/progress.ts
|
|
6
|
+
var Progress = class extends Node {
|
|
7
|
+
_render(ctx) {
|
|
8
|
+
const total = unwrap(this.state.total ?? 1);
|
|
9
|
+
const raw = total > 0 ? unwrap(this.state.value) / total : 0;
|
|
10
|
+
const fraction = Math.max(0, Math.min(1, raw));
|
|
11
|
+
const complete = this.state.complete ?? "█";
|
|
12
|
+
const incomplete = this.state.incomplete ?? "░";
|
|
13
|
+
const colorFilled = this.state.color ?? "primary";
|
|
14
|
+
const colorTrack = this.state.trackColor ?? "muted";
|
|
15
|
+
const labelStr = resolveLabel(this.state.label, ctx, fraction);
|
|
16
|
+
const target = resolveSize(this.state.width ?? "fill", ctx.width) ?? ctx.width;
|
|
17
|
+
const labelW = labelStr === "" ? 0 : stringWidth(labelStr) + 1;
|
|
18
|
+
const barWidth = Math.max(1, target - labelW);
|
|
19
|
+
const filled = Math.round(fraction * barWidth);
|
|
20
|
+
const empty = barWidth - filled;
|
|
21
|
+
const bar = ctx.style.fg(colorFilled)(complete.repeat(filled)) + ctx.style.fg(colorTrack)(incomplete.repeat(empty));
|
|
22
|
+
return [labelStr === "" ? bar : `${bar} ${labelStr}`];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Factory for `Progress`. Value is required; everything else has a
|
|
27
|
+
* sensible default — `progress({ value: 0.4 })` fills 40% of the
|
|
28
|
+
* available width with `█`, backed by `░` in the theme's `muted` slot.
|
|
29
|
+
*
|
|
30
|
+
* ```ts
|
|
31
|
+
* progress({ value: done, total, label: "auto" })
|
|
32
|
+
* progress({ value: p, color: "ok", width: 20 })
|
|
33
|
+
* progress({ value: 0.7, label: (ctx, f) => ctx.style.dim(`${f*100|0}%`) })
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function progress(state) {
|
|
37
|
+
return new Progress(state);
|
|
38
|
+
}
|
|
39
|
+
function resolveLabel(label, ctx, fraction) {
|
|
40
|
+
if (label === void 0) return "";
|
|
41
|
+
if (typeof label === "function") return label(ctx, fraction);
|
|
42
|
+
if (label === "auto") return `${Math.round(fraction * 100)}%`;
|
|
43
|
+
return label;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { Progress, progress };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=progress.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.mjs","names":[],"sources":["../../src/widgets/progress.ts"],"sourcesContent":["import type { RenderCtx } from \"../core/ctx.ts\"\nimport type { Reactive } from \"../core/reactive.ts\"\nimport type { State } from \"../core/state.ts\"\nimport type { Size } from \"../layout/size.ts\"\nimport type { Color } from \"../style/types.ts\"\n\nimport { stringWidth } from \"@zaly/shared/ansi\"\nimport { Node } from \"../core/node.ts\"\nimport { unwrap } from \"../core/reactive.ts\"\nimport { resolveSize } from \"../layout/size.ts\"\n\nexport interface ProgressState {\n /** Current value. Clamped to `[0, total]`. Accepts a signal accessor\n * so callers can drive the bar from reactive state without manual\n * `state.set` plumbing. */\n value: Reactive<number>\n /** Maximum value. Defaults to `1` — pass a fraction in `[0, 1]` to value. */\n total?: Reactive<number>\n /**\n * Bar width in cells. Defaults to `fill` (uses the full `ctx.width`).\n * Shrinks to fit if a `label` is provided.\n */\n width?: Size\n /**\n * Optional label shown to the right of the bar. Pass a string, or a\n * function to style it inline. `null`/`undefined` hides it; `\"auto\"`\n * formats as a percentage.\n */\n label?: string | ((ctx: RenderCtx, fraction: number) => string)\n /** Foreground theme slot for the filled portion. Defaults to `primary`. */\n color?: Color\n /** Foreground theme slot for the empty portion. Defaults to `muted`. */\n trackColor?: Color\n /** Filled glyph. Defaults to `█`. */\n complete?: string\n /** Empty glyph. Defaults to `░`. */\n incomplete?: string\n}\n\nexport class Progress extends Node<ProgressState> {\n protected _render(ctx: RenderCtx): string[] {\n const total = unwrap(this.state.total ?? 1)\n const raw = total > 0 ? unwrap(this.state.value) / total : 0\n const fraction = Math.max(0, Math.min(1, raw))\n\n const complete = this.state.complete ?? \"█\"\n const incomplete = this.state.incomplete ?? \"░\"\n const colorFilled = this.state.color ?? \"primary\"\n const colorTrack = this.state.trackColor ?? \"muted\"\n\n // Decide the label (may be empty).\n const labelStr = resolveLabel(this.state.label, ctx, fraction)\n\n // Resolve target width, reserving space for the label (\" <label>\").\n const target = resolveSize(this.state.width ?? \"fill\", ctx.width) ?? ctx.width\n const labelW = labelStr === \"\" ? 0 : stringWidth(labelStr) + 1\n const barWidth = Math.max(1, target - labelW)\n\n const filled = Math.round(fraction * barWidth)\n const empty = barWidth - filled\n\n const bar =\n ctx.style.fg(colorFilled)(complete.repeat(filled)) +\n ctx.style.fg(colorTrack)(incomplete.repeat(empty))\n\n return [labelStr === \"\" ? bar : `${bar} ${labelStr}`]\n }\n}\n\n/**\n * Factory for `Progress`. Value is required; everything else has a\n * sensible default — `progress({ value: 0.4 })` fills 40% of the\n * available width with `█`, backed by `░` in the theme's `muted` slot.\n *\n * ```ts\n * progress({ value: done, total, label: \"auto\" })\n * progress({ value: p, color: \"ok\", width: 20 })\n * progress({ value: 0.7, label: (ctx, f) => ctx.style.dim(`${f*100|0}%`) })\n * ```\n */\nexport function progress(state: State<ProgressState>): Progress {\n return new Progress(state)\n}\n\nfunction resolveLabel(label: ProgressState[\"label\"], ctx: RenderCtx, fraction: number): string {\n if (label === undefined) return \"\"\n if (typeof label === \"function\") return label(ctx, fraction)\n if (label === \"auto\") return `${Math.round(fraction * 100)}%`\n return label\n}\n"],"mappings":";;;;;AAuCA,IAAa,WAAb,cAA8B,KAAoB;CAChD,QAAkB,KAA0B;EAC1C,MAAM,QAAQ,OAAO,KAAK,MAAM,SAAS,CAAC;EAC1C,MAAM,MAAM,QAAQ,IAAI,OAAO,KAAK,MAAM,KAAK,IAAI,QAAQ;EAC3D,MAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,GAAG,CAAC;EAE7C,MAAM,WAAW,KAAK,MAAM,YAAY;EACxC,MAAM,aAAa,KAAK,MAAM,cAAc;EAC5C,MAAM,cAAc,KAAK,MAAM,SAAS;EACxC,MAAM,aAAa,KAAK,MAAM,cAAc;EAG5C,MAAM,WAAW,aAAa,KAAK,MAAM,OAAO,KAAK,QAAQ;EAG7D,MAAM,SAAS,YAAY,KAAK,MAAM,SAAS,QAAQ,IAAI,KAAK,KAAK,IAAI;EACzE,MAAM,SAAS,aAAa,KAAK,IAAI,YAAY,QAAQ,IAAI;EAC7D,MAAM,WAAW,KAAK,IAAI,GAAG,SAAS,MAAM;EAE5C,MAAM,SAAS,KAAK,MAAM,WAAW,QAAQ;EAC7C,MAAM,QAAQ,WAAW;EAEzB,MAAM,MACJ,IAAI,MAAM,GAAG,WAAW,CAAC,CAAC,SAAS,OAAO,MAAM,CAAC,IACjD,IAAI,MAAM,GAAG,UAAU,CAAC,CAAC,WAAW,OAAO,KAAK,CAAC;EAEnD,OAAO,CAAC,aAAa,KAAK,MAAM,GAAG,IAAI,GAAG,UAAU;CACtD;AACF;;;;;;;;;;;;AAaA,SAAgB,SAAS,OAAuC;CAC9D,OAAO,IAAI,SAAS,KAAK;AAC3B;AAEA,SAAS,aAAa,OAA+B,KAAgB,UAA0B;CAC7F,IAAI,UAAU,KAAA,GAAW,OAAO;CAChC,IAAI,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,QAAQ;CAC3D,IAAI,UAAU,QAAQ,OAAO,GAAG,KAAK,MAAM,WAAW,GAAG,EAAE;CAC3D,OAAO;AACT"}
|