@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,1963 @@
|
|
|
1
|
+
import { c as Color, f as Style, t as Theme } from "./types-B9Eh7k2v.mjs";
|
|
2
|
+
import { a as StyleBuilder, t as InspectOpts } from "./inspect-D6FW_QUo.mjs";
|
|
3
|
+
import { Emitter, MaybePromise } from "@zaly/shared";
|
|
4
|
+
import { LogEntry, LogLevel, LogReporter, Logger, TryResult } from "@zaly/shared/logger";
|
|
5
|
+
import { ArgsOpts, ArgsResult } from "@zaly/shared/args";
|
|
6
|
+
//#region src/input/keys.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Keyboard event shape + pattern-based matcher.
|
|
9
|
+
*
|
|
10
|
+
* `KeyEvent.name` is a canonical name:
|
|
11
|
+
* - Single characters for printable keys: `"a"`, `"A"`, `" "`, `"!"`. The
|
|
12
|
+
* `name` reflects the raw character the decoder produced — so `Shift+A`
|
|
13
|
+
* arrives as `{ name: "A", shift: true }` (terminals already fold
|
|
14
|
+
* shift into the uppercase byte for letters).
|
|
15
|
+
* - Named constants for non-printing keys: `"enter"`, `"tab"`, `"esc"`,
|
|
16
|
+
* `"backspace"`, `"delete"`, `"space"`, arrows `"up"/"down"/"left"/
|
|
17
|
+
* "right"`, navigation `"home"/"end"/"pageup"/"pagedown"/"insert"`,
|
|
18
|
+
* function keys `"f1"` .. `"f12"`.
|
|
19
|
+
*
|
|
20
|
+
* Patterns for `keyMatches` are dash-separated modifier prefixes plus the
|
|
21
|
+
* name: `"a"`, `"ctrl-c"`, `"shift-tab"`, `"ctrl-shift-x"`. Unknown
|
|
22
|
+
* prefixes are treated as literal characters in the name, so
|
|
23
|
+
* `"something-foo"` is interpreted as the name `"something-foo"` rather
|
|
24
|
+
* than a missing-modifier error.
|
|
25
|
+
*
|
|
26
|
+
* Modifier prefixes (order-independent):
|
|
27
|
+
* - `ctrl-`
|
|
28
|
+
* - `alt-`
|
|
29
|
+
* - `shift-`
|
|
30
|
+
* - `meta-` (a.k.a. super / cmd)
|
|
31
|
+
*/
|
|
32
|
+
interface KeyEvent {
|
|
33
|
+
/** Canonical key name — raw character or one of the named constants above. */
|
|
34
|
+
name: string;
|
|
35
|
+
/**
|
|
36
|
+
* The literal text the keystroke would insert, when that makes sense.
|
|
37
|
+
* For printable chars this is the char itself; for Enter, Tab, etc. it's
|
|
38
|
+
* undefined. Widgets use this to decide whether to insert content vs.
|
|
39
|
+
* treat the key as a command.
|
|
40
|
+
*/
|
|
41
|
+
text?: string;
|
|
42
|
+
ctrl: boolean;
|
|
43
|
+
alt: boolean;
|
|
44
|
+
shift: boolean;
|
|
45
|
+
meta: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** Canonical modifier names used in `KeyPattern`. */
|
|
48
|
+
type KeyModifier = "ctrl" | "alt" | "shift" | "meta";
|
|
49
|
+
/**
|
|
50
|
+
* Named keys the decoder emits for non-printable keystrokes. Bindings
|
|
51
|
+
* reference these directly (`"enter"`, `"f5"`, `"pageup"`, …).
|
|
52
|
+
*/
|
|
53
|
+
type SpecialKeyName = (typeof specialKeys)[number];
|
|
54
|
+
declare const KeyPatternBrand: unique symbol;
|
|
55
|
+
/** A validated, canonical key binding string. */
|
|
56
|
+
type KeyPattern = string & {
|
|
57
|
+
readonly [KeyPatternBrand]: true;
|
|
58
|
+
};
|
|
59
|
+
declare const specialKeys: readonly ["enter", "tab", "backspace", "delete", "space", "esc", "up", "down", "left", "right", "home", "end", "pageup", "pagedown", "insert", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12"];
|
|
60
|
+
declare class KeyPatternError extends Error {
|
|
61
|
+
constructor(pattern: string, reason: string);
|
|
62
|
+
}
|
|
63
|
+
declare function isKeyPattern(s: string): s is KeyPattern;
|
|
64
|
+
/**
|
|
65
|
+
* Canonical string form of a pattern or event — modifiers in a fixed
|
|
66
|
+
* alphabetical order, then the key name, joined by `-`. Used by the
|
|
67
|
+
* router to build a pattern index whose keys match regardless of how
|
|
68
|
+
* the user spelled the modifier order (`"ctrl-shift-a"` and
|
|
69
|
+
* `"shift-ctrl-a"` both canonicalize to `"ctrl-shift-a"`).
|
|
70
|
+
*/
|
|
71
|
+
declare function canonical(patternOrEvent: string | KeyEvent): KeyPattern;
|
|
72
|
+
/**
|
|
73
|
+
* Test whether an event matches a pattern (or any of a list of patterns).
|
|
74
|
+
* Unspecified modifiers in the pattern must be *absent* on the event —
|
|
75
|
+
* matching is strict, not inclusive, so `"a"` only matches a bare `a`
|
|
76
|
+
* (no ctrl/alt/etc.).
|
|
77
|
+
*/
|
|
78
|
+
declare function keyMatches(ev: KeyEvent, pattern: string | readonly string[]): boolean;
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/input/decoder.d.ts
|
|
81
|
+
/**
|
|
82
|
+
* Decoder — bytes (as a string, after the caller UTF-8 decodes them) in,
|
|
83
|
+
* structured `InputEvent`s out. Holds partial state across `feed()` calls
|
|
84
|
+
* so split-chunk CSI / SS3 / paste sequences work transparently.
|
|
85
|
+
*
|
|
86
|
+
* ESC handling: a lone ESC byte is held as pending (because `ESC + <byte>`
|
|
87
|
+
* might be Alt+<key> or the start of a CSI sequence). Call `flush()` from
|
|
88
|
+
* an idle-timer tick to emit a bare `esc` event when no follow-up arrives.
|
|
89
|
+
*/
|
|
90
|
+
type MouseButton = "left" | "middle" | "right";
|
|
91
|
+
type MouseEvent = {
|
|
92
|
+
type: "mouse";
|
|
93
|
+
x: number;
|
|
94
|
+
y: number;
|
|
95
|
+
alt: boolean;
|
|
96
|
+
ctrl: boolean;
|
|
97
|
+
meta: boolean;
|
|
98
|
+
shift: boolean;
|
|
99
|
+
} & ({
|
|
100
|
+
kind: "scroll";
|
|
101
|
+
deltaY: number;
|
|
102
|
+
} | {
|
|
103
|
+
kind: "down" | "drag" | "up";
|
|
104
|
+
button: MouseButton;
|
|
105
|
+
click?: 1 | 2 | 3;
|
|
106
|
+
});
|
|
107
|
+
type TerminalResponseEvent = {
|
|
108
|
+
type: "term-response";
|
|
109
|
+
kind: "csi";
|
|
110
|
+
sequence: string;
|
|
111
|
+
params: string;
|
|
112
|
+
final: string;
|
|
113
|
+
} | {
|
|
114
|
+
type: "term-response";
|
|
115
|
+
kind: "osc" | "dcs" | "apc";
|
|
116
|
+
sequence: string;
|
|
117
|
+
payload: string;
|
|
118
|
+
};
|
|
119
|
+
type InputEvent = {
|
|
120
|
+
type: "key";
|
|
121
|
+
event: KeyEvent;
|
|
122
|
+
} | {
|
|
123
|
+
type: "paste";
|
|
124
|
+
text: string;
|
|
125
|
+
} | {
|
|
126
|
+
type: "focus";
|
|
127
|
+
gained: boolean;
|
|
128
|
+
} | TerminalResponseEvent | MouseEvent;
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/input/actions.d.ts
|
|
131
|
+
/**
|
|
132
|
+
* Context handed to every action handler. Whether the action was
|
|
133
|
+
* fired by a key, programmatic dispatch, command palette, or a plugin,
|
|
134
|
+
* handlers see a uniform payload.
|
|
135
|
+
*/
|
|
136
|
+
type ActionCtx<T extends ArgsOpts = ArgsOpts, N = void> = {
|
|
137
|
+
/** The action id being dispatched. */
|
|
138
|
+
readonly id: string;
|
|
139
|
+
readonly node?: Node;
|
|
140
|
+
/** The node dispatch started from — typically the focused node for
|
|
141
|
+
* key- or palette-triggered actions. `undefined` for purely
|
|
142
|
+
* programmatic dispatch with no anchor. */
|
|
143
|
+
readonly target?: Node;
|
|
144
|
+
/** What triggered the dispatch. Open string for extensibility. */
|
|
145
|
+
readonly source: "key" | "programmatic" | (string & {});
|
|
146
|
+
/** When `source === "key"`, the originating routed key event. */
|
|
147
|
+
readonly key?: RoutedKey;
|
|
148
|
+
args?: ArgsResult<T>;
|
|
149
|
+
} & (N extends Node ? {
|
|
150
|
+
/** The node whose `actions[id]` handler matched, or `undefined` when
|
|
151
|
+
* the action was handled by a catalog-level `fn` (not tied to a
|
|
152
|
+
* specific node). */
|
|
153
|
+
readonly node: N;
|
|
154
|
+
} : {});
|
|
155
|
+
type ActionFn<T extends ArgsOpts = ArgsOpts, N = void> = (ctx: ActionCtx<T, N>) => MaybePromise<unknown>;
|
|
156
|
+
type KeyBinding = Omit<ActionDef, "fn" | "keys"> & {
|
|
157
|
+
id: string;
|
|
158
|
+
fn: ActionFn;
|
|
159
|
+
keys: string | readonly string[];
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Metadata + default bindings for a single action. Used as catalog
|
|
163
|
+
* entries inside the `Actions` registry.
|
|
164
|
+
*
|
|
165
|
+
* - `desc` / `name`: human-facing copy for help screens, command palette.
|
|
166
|
+
* - `keys`: default key pattern(s). The registry builds the router's
|
|
167
|
+
* keymap from these so apps don't have to wire bindings themselves.
|
|
168
|
+
* - `hidden`: omit from command palette enumeration.
|
|
169
|
+
* - `fn`: optional handler. When present, `dispatch(id)` calls it
|
|
170
|
+
* directly (used for global/app actions that aren't anchored to a
|
|
171
|
+
* widget instance). When absent, dispatch walks the focus chain
|
|
172
|
+
* looking for a node with `actions[id]`.
|
|
173
|
+
*/
|
|
174
|
+
interface ActionDef<T extends ArgsOpts = ArgsOpts, N = void> {
|
|
175
|
+
cmd?: string;
|
|
176
|
+
desc?: string;
|
|
177
|
+
keys?: readonly string[];
|
|
178
|
+
hidden?: boolean;
|
|
179
|
+
args?: T;
|
|
180
|
+
source?: string;
|
|
181
|
+
fn?: ActionFn<T, N>;
|
|
182
|
+
/** Optional numeric priority for ordering in the command palette.
|
|
183
|
+
* Higher number is higher priority. Defaults to 0 */
|
|
184
|
+
priority?: number;
|
|
185
|
+
}
|
|
186
|
+
type Action<T extends ArgsOpts = ArgsOpts, N = void> = ActionDef<T, N> & {
|
|
187
|
+
id: string;
|
|
188
|
+
};
|
|
189
|
+
type ActionMap = Record<string, ActionDef>;
|
|
190
|
+
type ActionFilter = {
|
|
191
|
+
cmd?: string;
|
|
192
|
+
id?: string | RegExp;
|
|
193
|
+
hidden?: boolean;
|
|
194
|
+
source?: string;
|
|
195
|
+
filter?: (info: Action) => boolean;
|
|
196
|
+
};
|
|
197
|
+
/** A single entry in a Node's `actions` dict.
|
|
198
|
+
*
|
|
199
|
+
* Either a bare handler — the historical form, ideal for built-in
|
|
200
|
+
* widgets whose metadata lives in `defaultActions` — or the full
|
|
201
|
+
* `ActionInfo` shape (with a *required* `fn`). The object form lets
|
|
202
|
+
* widget / plugin authors colocate desc + keys + impl in one place;
|
|
203
|
+
* on mount, the metadata (everything except `fn`) is auto-registered
|
|
204
|
+
* into `ctx.actions` with `extend: false`, so it contributes defaults
|
|
205
|
+
* without clobbering anything the user already configured. */
|
|
206
|
+
type NodeAction<T extends Node = Node> = ActionFn<ArgsOpts, T> | (ActionDef<ArgsOpts, T> & {
|
|
207
|
+
fn: ActionFn<ArgsOpts, T>;
|
|
208
|
+
});
|
|
209
|
+
/** Shape of a Node's `actions` dict — full action ids as keys. */
|
|
210
|
+
type NodeActionMap<T extends Node = Node> = Record<string, NodeAction<T>>;
|
|
211
|
+
type ActionEvents = {
|
|
212
|
+
change: {};
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Runtime registry of action metadata + impls.
|
|
216
|
+
*
|
|
217
|
+
* `register(entries)` merges new `ActionInfo` objects into the catalog
|
|
218
|
+
* by id — passing `{ "x": { keys: [...] } }` updates only the `keys`
|
|
219
|
+
* field of `x`, preserving existing `fn`/`desc`. That lets docs,
|
|
220
|
+
* bindings, and impls be registered in separate calls / from different
|
|
221
|
+
* layers (bundled defaults, app-level overrides, plugin contributions).
|
|
222
|
+
*
|
|
223
|
+
* `dispatch(id, ctx)` runs the action:
|
|
224
|
+
* - If the catalog entry has `fn`, it's called directly (global
|
|
225
|
+
* actions).
|
|
226
|
+
* - Otherwise, the focus chain is walked from `ctx.target` (default:
|
|
227
|
+
* the router's focused node) up to the root, and the first node
|
|
228
|
+
* with `actions[id]` handles it.
|
|
229
|
+
*
|
|
230
|
+
* `list()` returns the catalog for command palette / help enumeration.
|
|
231
|
+
*/
|
|
232
|
+
declare class Actions extends Emitter<ActionEvents> {
|
|
233
|
+
#private;
|
|
234
|
+
constructor(logger?: Logger);
|
|
235
|
+
/** Internal — the Renderer wires this after construction. */
|
|
236
|
+
setTargetResolver(fn: () => Node | undefined): void;
|
|
237
|
+
bind(binding: KeyBinding): () => void;
|
|
238
|
+
/**
|
|
239
|
+
* Register or merge catalog entries. Values are shallow-merged into
|
|
240
|
+
* existing entries by id.
|
|
241
|
+
*
|
|
242
|
+
* When `opts.default` is `true`, the new entry is merged *before* the
|
|
243
|
+
* existing one, so it provides defaults.
|
|
244
|
+
*
|
|
245
|
+
* When `opts.default` is `false` (the default), the new entry is merged
|
|
246
|
+
* *after* the existing one, so it overrides prior values.
|
|
247
|
+
*
|
|
248
|
+
* Returns an unregister function that rolls this call back.
|
|
249
|
+
*/
|
|
250
|
+
register(actions: ActionMap | Action[], opts?: {
|
|
251
|
+
default?: boolean;
|
|
252
|
+
}): () => void;
|
|
253
|
+
/** Remove entries by id. */
|
|
254
|
+
delete(filter: string[] | ActionFilter): void;
|
|
255
|
+
get(id: string): Action | undefined;
|
|
256
|
+
list(opts?: ActionFilter): Action[];
|
|
257
|
+
find(opts?: ActionFilter): Action | undefined;
|
|
258
|
+
/**
|
|
259
|
+
* Fire the action `id`. Returns `true` if anything handled it.
|
|
260
|
+
*
|
|
261
|
+
* Order:
|
|
262
|
+
* 1. If the catalog entry has `fn`, call it with the full ctx.
|
|
263
|
+
* 2. Otherwise walk `target` → root and fire the first node's
|
|
264
|
+
* `actions[id]`.
|
|
265
|
+
* 3. Return `false` if nothing consumed.
|
|
266
|
+
*/
|
|
267
|
+
dispatch(id: string, ctx?: Partial<ActionCtx>): boolean;
|
|
268
|
+
dispatch(action: Action, ctx?: Partial<ActionCtx>): boolean;
|
|
269
|
+
dispatchKey(routed: RoutedKey, opts?: {
|
|
270
|
+
node?: boolean;
|
|
271
|
+
global?: boolean;
|
|
272
|
+
}): boolean;
|
|
273
|
+
whichKey(node: Node, opts?: {
|
|
274
|
+
filter?: ActionFilter;
|
|
275
|
+
}): (Action & {
|
|
276
|
+
keys: string[];
|
|
277
|
+
})[];
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/layout/size.d.ts
|
|
281
|
+
/** Percentage string like `'50%'`, resolved against the parent's content axis. */
|
|
282
|
+
type Pct = `${number}%`;
|
|
283
|
+
/**
|
|
284
|
+
* Size along one axis.
|
|
285
|
+
* - `number` — absolute cells
|
|
286
|
+
* - `Pct` — percentage of the parent's content axis
|
|
287
|
+
* - `'fill'` — take all of the remaining space in the flex allocation
|
|
288
|
+
* - `'fit'` — shrink to the content's natural size, capped at the
|
|
289
|
+
* available axis. Mirrors CSS `fit-content`. Pairs nicely
|
|
290
|
+
* with `'fill'` — `fill` grows, `fit` shrinks.
|
|
291
|
+
*/
|
|
292
|
+
type Size = number | Pct | "fill" | "fit";
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region src/core/reactive.d.ts
|
|
295
|
+
/** Run `fn` outside any tracking scope. Signal reads inside don't
|
|
296
|
+
* subscribe the surrounding render, and async work started inside
|
|
297
|
+
* (timers, promises, intervals) doesn't capture the current ctx —
|
|
298
|
+
* callbacks fire with `getStore() === undefined`.
|
|
299
|
+
*
|
|
300
|
+
* Use this when starting persistent async work from inside a render
|
|
301
|
+
* pass: a `setInterval` set up during `_render` would otherwise
|
|
302
|
+
* inherit the render's ALS context, and every timer tick would look
|
|
303
|
+
* like it's "inside" that render — its invalidates would be silently
|
|
304
|
+
* suppressed.
|
|
305
|
+
*
|
|
306
|
+
* ```ts
|
|
307
|
+
* // inside _render() or a render-driven reconcile():
|
|
308
|
+
* untracked(() => {
|
|
309
|
+
* this.#timer = setInterval(() => this.invalidate(), speed)
|
|
310
|
+
* })
|
|
311
|
+
* ```
|
|
312
|
+
*
|
|
313
|
+
* Equivalent to Solid's `untrack`. */
|
|
314
|
+
declare function untrack<T>(fn: () => T): T;
|
|
315
|
+
/** Resolve a `Reactive<T>` to its current `T` without subscribing to
|
|
316
|
+
* any tracking context. */
|
|
317
|
+
declare function peek<T>(s: Reactive<T>): T;
|
|
318
|
+
/**
|
|
319
|
+
* Solid-style context for sharing values down the owner chain without
|
|
320
|
+
* prop-drilling. Widgets publish values via `provideContext` (in setup
|
|
321
|
+
* or `_render`); descendants read via `useContext`.
|
|
322
|
+
*
|
|
323
|
+
* ```ts
|
|
324
|
+
* const ThemeOverride = createContext<Theme | undefined>(undefined)
|
|
325
|
+
*
|
|
326
|
+
* // provider (in a widget body / setup / _render):
|
|
327
|
+
* provideContext(ThemeOverride, customTheme)
|
|
328
|
+
*
|
|
329
|
+
* // consumer (anywhere with an active node or owner chain):
|
|
330
|
+
* const theme = useContext(ThemeOverride) ?? defaultTheme
|
|
331
|
+
* ```
|
|
332
|
+
*
|
|
333
|
+
* Implementation: each `Node` carries an optional `#contexts` map keyed
|
|
334
|
+
* by context id. Lookups walk the *owner frame chain* (`OwnerFrame.owner`
|
|
335
|
+
* pointers), reading the live `#contexts` of each frame's node and
|
|
336
|
+
* falling back to the context's default if no ancestor provides it.
|
|
337
|
+
*
|
|
338
|
+
* The owner chain is the render-call ancestry captured at frame push
|
|
339
|
+
* (via `withActiveNode`) — independent of mount status. Effects/memos
|
|
340
|
+
* capture their creation frame and re-establish it on re-fire, so
|
|
341
|
+
* detached re-runs walk the same chain they would have at creation.
|
|
342
|
+
*/
|
|
343
|
+
interface Context<T> {
|
|
344
|
+
readonly id: symbol;
|
|
345
|
+
readonly defaultValue: T;
|
|
346
|
+
}
|
|
347
|
+
declare function createContext<T>(): Context<T | undefined>;
|
|
348
|
+
declare function createContext<T>(defaultValue: T): Context<T>;
|
|
349
|
+
/** Publish `value` for `ctx` on the active Owner. Persistent for the
|
|
350
|
+
* Owner's lifetime. Descendants (in the render-call sense) that read
|
|
351
|
+
* via `useContext` walk up the Owner chain and find it. */
|
|
352
|
+
declare function provideContext<T>(ctx: Context<T>, value: T): void;
|
|
353
|
+
/** Read the current value of `ctx`. Walks the Owner chain
|
|
354
|
+
* (render-call ancestry, not mount tree). Returns the context's
|
|
355
|
+
* default when no ancestor has provided a value. */
|
|
356
|
+
declare function useContext<T>(ctx: Context<T>): T;
|
|
357
|
+
/** Brand tag: every accessor / setter returned by `signal` / `memo`
|
|
358
|
+
* carries this symbol so `unwrap` can tell a reactive source from a
|
|
359
|
+
* plain function-valued prop (label formatter, text callback, etc.). */
|
|
360
|
+
declare const REACTIVE: unique symbol;
|
|
361
|
+
type Signal<T> = readonly [get: Accessor<T>, set: Setter<T>] & {
|
|
362
|
+
readonly get: Accessor<T>;
|
|
363
|
+
readonly set: Setter<T>;
|
|
364
|
+
};
|
|
365
|
+
/** A read-only reactive source. Branded so `isAccessor` can detect it
|
|
366
|
+
* without false positives on arbitrary callables. */
|
|
367
|
+
type Accessor<T> = (() => T) & {
|
|
368
|
+
readonly [REACTIVE]: "get";
|
|
369
|
+
};
|
|
370
|
+
/** A signal setter. Same branding as `Accessor` (different tag) so a
|
|
371
|
+
* setter isn't mistaken for an accessor by `unwrap`. */
|
|
372
|
+
type Setter<T> = ((next: T | ((prev: T) => T)) => void) & {
|
|
373
|
+
readonly [REACTIVE]: "set";
|
|
374
|
+
};
|
|
375
|
+
/** A widget prop that's either a literal value or a reactive accessor. */
|
|
376
|
+
type Reactive<T> = T | Accessor<T>;
|
|
377
|
+
/** Runtime check: `true` when `v` was produced by `signal` / `memo`.
|
|
378
|
+
* False for plain callables (text-content functions, label formatters).
|
|
379
|
+
*
|
|
380
|
+
* @internal */
|
|
381
|
+
declare function isAccessor<T>(v: unknown): v is Accessor<T>;
|
|
382
|
+
/** Resolve a `Reactive<T>` to its current `T`. Call inside `_render`
|
|
383
|
+
* so accessor reads subscribe the rendering node. Only brand-tagged
|
|
384
|
+
* accessors are invoked — other function values pass through
|
|
385
|
+
* untouched, so widgets whose `T` is itself a function type (label
|
|
386
|
+
* formatters etc.) don't need a hand-rolled guard. */
|
|
387
|
+
declare function unwrap<T>(v: Reactive<T>): T;
|
|
388
|
+
/**
|
|
389
|
+
* Reactive ownership scope — bundles cleanups + provided contexts
|
|
390
|
+
* scoped to a single widget body (or a `createRoot` headless scope).
|
|
391
|
+
*
|
|
392
|
+
* Owners form a chain via `parent` mirroring the call-time widget
|
|
393
|
+
* nesting: when `widget()` is invoked inside another widget's body,
|
|
394
|
+
* `createNode` creates the inner Owner with `parent =
|
|
395
|
+
* activeOwnerStore.getStore()` (the outer widget's Owner). That chain
|
|
396
|
+
* is what `useContext` walks.
|
|
397
|
+
*
|
|
398
|
+
* Owners are **not** bound to Nodes anymore — a widget's `createNode`
|
|
399
|
+
* pairs the Owner with the constructed Node only through
|
|
400
|
+
* `Node.once("unmount", () => owner.dispose())`. Primitive Nodes
|
|
401
|
+
* (`text`, `box`, …) don't get their own Owner; they share whatever
|
|
402
|
+
* Owner created them.
|
|
403
|
+
*
|
|
404
|
+
* Use `onCleanup(cb)` to register cleanups against the active Owner.
|
|
405
|
+
* Disposing the Owner runs them in reverse-registration order.
|
|
406
|
+
*
|
|
407
|
+
* @internal
|
|
408
|
+
*/
|
|
409
|
+
declare class Owner {
|
|
410
|
+
#private;
|
|
411
|
+
parent?: Owner;
|
|
412
|
+
constructor(parent?: Owner);
|
|
413
|
+
get disposed(): boolean;
|
|
414
|
+
/** Live view of this Owner's provided contexts. Populated by
|
|
415
|
+
* `provideContext`. Walked upward via `parent` by `useContext`. */
|
|
416
|
+
get contexts(): ReadonlyMap<symbol, unknown> | undefined;
|
|
417
|
+
setContext(id: symbol, value: unknown): void;
|
|
418
|
+
/** Register a cleanup. Runs on `dispose()`. */
|
|
419
|
+
addCleanup(fn: () => void): void;
|
|
420
|
+
/** Fire cleanups in reverse-registration order. Idempotent. */
|
|
421
|
+
dispose(): void;
|
|
422
|
+
}
|
|
423
|
+
/** Run `fn` as the `node`'s render pass. Signal reads inside subscribe
|
|
424
|
+
* the node's tracking ctx; the ctx's `notify` is `node.invalidate`,
|
|
425
|
+
* so any signal write that this render depended on invalidates the
|
|
426
|
+
* node and clears its cache. Cleanups are registered against the
|
|
427
|
+
* Node's `unmount` event.
|
|
428
|
+
*
|
|
429
|
+
* Does **not** push an Owner — primitive Nodes share whatever Owner
|
|
430
|
+
* was active in their caller. Widget bodies install their own Owner
|
|
431
|
+
* via `createNode` at construction time, which is when `useContext`
|
|
432
|
+
* / `provideContext` / `effect` / `memo` care about the active scope.
|
|
433
|
+
*
|
|
434
|
+
* @internal */
|
|
435
|
+
declare function withActiveNode<T>(node: Node, fn: () => T): T;
|
|
436
|
+
/** Run `fn` with a pre-existing Owner restored. Used by `effect` /
|
|
437
|
+
* `memo` to re-establish their creation-time owner chain on re-fire,
|
|
438
|
+
* so `useContext` walks the same ancestry regardless of whether the
|
|
439
|
+
* re-fire was triggered from inside or outside a render.
|
|
440
|
+
*
|
|
441
|
+
* @internal */
|
|
442
|
+
declare function withOwner<T>(owner: Owner, fn: () => T): T;
|
|
443
|
+
/** Create a fresh Owner scope and run `fn` inside it. Returns
|
|
444
|
+
* whatever `fn` returns. The Owner's parent is whatever was active at
|
|
445
|
+
* the call site (or `undefined` at module top).
|
|
446
|
+
*
|
|
447
|
+
* The Owner is *not* auto-disposed — callers manage its lifetime via
|
|
448
|
+
* the Owner returned alongside the result, or (more commonly) by
|
|
449
|
+
* binding the Owner to a Node and disposing it on that Node's
|
|
450
|
+
* unmount.
|
|
451
|
+
*
|
|
452
|
+
* Typical use is via a surface's function-shape `add(fn)` API which
|
|
453
|
+
* wraps this internally:
|
|
454
|
+
*
|
|
455
|
+
* ```ts
|
|
456
|
+
* ui.add(() => {
|
|
457
|
+
* const [count, setCount] = signal(0)
|
|
458
|
+
* onCleanup(() => console.log("gone"))
|
|
459
|
+
* return text(() => `count: ${count()}`)
|
|
460
|
+
* })
|
|
461
|
+
* ```
|
|
462
|
+
*
|
|
463
|
+
* For headless reactive scopes (tests, programmatic effects), call
|
|
464
|
+
* `createRoot` directly:
|
|
465
|
+
*
|
|
466
|
+
* ```ts
|
|
467
|
+
* const { result, owner } = createRoot(() => signal(0))
|
|
468
|
+
* // ... use result ...
|
|
469
|
+
* owner.dispose()
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
declare function createRoot<T>(fn: (dispose: () => void) => T): T;
|
|
473
|
+
/** Run `fn` inside a fresh Owner scope and bind the returned Node to
|
|
474
|
+
* that scope — when the Node unmounts, the Owner disposes (firing
|
|
475
|
+
* every `onCleanup`, tearing down every `effect` / `memo` /
|
|
476
|
+
* `createAsync` registered in the body).
|
|
477
|
+
*
|
|
478
|
+
* This is what `widget()` calls under the hood: it's the per-instance
|
|
479
|
+
* scope boundary. Surfaces also call it directly at their `add` /
|
|
480
|
+
* `append` entry points so each appended subtree gets its own root.
|
|
481
|
+
*
|
|
482
|
+
* ```ts
|
|
483
|
+
* ui.add(() => {
|
|
484
|
+
* const [c, setC] = signal(0)
|
|
485
|
+
* onCleanup(() => clearInterval(timer))
|
|
486
|
+
* const timer = setInterval(() => setC(x => x + 1), 1000)
|
|
487
|
+
* return text(() => `count: ${c()}`)
|
|
488
|
+
* })
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
491
|
+
declare function createNode<T extends Node>(fn: () => T): T;
|
|
492
|
+
/** Register a cleanup against the active Owner. Fires when the Owner
|
|
493
|
+
* disposes — i.e. when the widget body's `createNode` Node unmounts,
|
|
494
|
+
* or when the surrounding `createRoot` is disposed.
|
|
495
|
+
*
|
|
496
|
+
* ```ts
|
|
497
|
+
* function widgetBody() {
|
|
498
|
+
* const id = setInterval(() => tick(), 1000)
|
|
499
|
+
* onCleanup(() => clearInterval(id))
|
|
500
|
+
* return box(…)
|
|
501
|
+
* }
|
|
502
|
+
* ```
|
|
503
|
+
*
|
|
504
|
+
* Throws if called outside any active Owner — the cleanup would
|
|
505
|
+
* never fire and the caller is almost certainly mistaken. */
|
|
506
|
+
declare function onCleanup(fn: () => void): void;
|
|
507
|
+
/** Current Owner, or `undefined` outside any render. Captured by
|
|
508
|
+
* effects/memos at creation time and restored on re-fire.
|
|
509
|
+
*
|
|
510
|
+
* @internal */
|
|
511
|
+
declare function useActiveOwner(): Owner | undefined;
|
|
512
|
+
/** Whether we're currently executing inside `node`'s own render call
|
|
513
|
+
* stack — i.e. this code path was reached synchronously (or via an
|
|
514
|
+
* await preserved by `AsyncLocalStorage`) from `node`'s `withActiveNode`.
|
|
515
|
+
*
|
|
516
|
+
* Used by `invalidate()` to suppress *internal* cascades — e.g.
|
|
517
|
+
* Markdown mutating its child Text inside its own `_render`. External
|
|
518
|
+
* mutations (network callbacks, event handlers) run outside any
|
|
519
|
+
* render's ALS scope, so this returns `false` for them and they emit
|
|
520
|
+
* normally even when a render is in flight.
|
|
521
|
+
*
|
|
522
|
+
* @internal */
|
|
523
|
+
declare function inRenderContextOf(node: Node): boolean;
|
|
524
|
+
/**
|
|
525
|
+
* Reactive value. Returns a `[get, set]` tuple that's also accessible
|
|
526
|
+
* as `.get` / `.set` for destructure-skipping callers. Reads inside a
|
|
527
|
+
* tracking context (node render, effect, memo) auto-subscribe; writes
|
|
528
|
+
* notify every subscriber synchronously.
|
|
529
|
+
*
|
|
530
|
+
* ```ts
|
|
531
|
+
* const [count, setCount] = signal(0)
|
|
532
|
+
* setCount(1) // value form
|
|
533
|
+
* setCount((prev) => prev + 1) // updater form
|
|
534
|
+
* ```
|
|
535
|
+
*
|
|
536
|
+
* **Storing a function as a value — gotcha**: `set` interprets any
|
|
537
|
+
* function argument as the *updater* form `(prev) => next`, calls it
|
|
538
|
+
* with the current value, and stores the result. To store a function
|
|
539
|
+
* value (e.g. a callable `StyleBuilder` or a handler ref), wrap with
|
|
540
|
+
* the updater form so the call resolves to the function you wanted:
|
|
541
|
+
*
|
|
542
|
+
* ```ts
|
|
543
|
+
* const [style, setStyle] = signal<StyleBuilder | undefined>(undefined)
|
|
544
|
+
* setStyle(ctx.style) // ✗ stored as `ctx.style(undefined)` — a string
|
|
545
|
+
* setStyle(() => ctx.style) // ✓ stored as the StyleBuilder itself
|
|
546
|
+
* ```
|
|
547
|
+
*
|
|
548
|
+
* Same trap exists in Solid and React's `useState`. If you find
|
|
549
|
+
* yourself reaching for it, also consider whether storing a *plain*
|
|
550
|
+
* value (the underlying data, not the chain/builder) and recomputing
|
|
551
|
+
* the function-y derivative inside a `memo` is cleaner — usually it
|
|
552
|
+
* is.
|
|
553
|
+
*
|
|
554
|
+
* Equality is reference-based: `set(v)` where `v === current` skips
|
|
555
|
+
* the notify. Wrap mutable values you want to "publish" in a fresh
|
|
556
|
+
* object/array, or use a counter signal to force fan-out.
|
|
557
|
+
*/
|
|
558
|
+
declare function signal<T>(initial: T, opts?: {
|
|
559
|
+
equals?: false | ((prev: T, next: T) => boolean);
|
|
560
|
+
}): Signal<T>;
|
|
561
|
+
/**
|
|
562
|
+
* Lift a plain value to an `Accessor<T>`. The returned function always
|
|
563
|
+
* returns `value`, never tracks, never notifies — useful at the boundary
|
|
564
|
+
* where a static value enters a tree that otherwise expects `Accessor<T>`.
|
|
565
|
+
*
|
|
566
|
+
* Branded so `isAccessor` reports `true` and downstream `unwrap` calls
|
|
567
|
+
* resolve to a stable function call rather than treating the value as
|
|
568
|
+
* a ctx-aware thunk.
|
|
569
|
+
*
|
|
570
|
+
* ```ts
|
|
571
|
+
* // Component expects `Accessor<Theme>` deep in the tree:
|
|
572
|
+
* <CodeBlock theme={toAccessor(myStaticTheme)} />
|
|
573
|
+
* ```
|
|
574
|
+
*/
|
|
575
|
+
declare function toAccessor<T>(fn: () => T): Accessor<T>;
|
|
576
|
+
/**
|
|
577
|
+
* Run `fn` immediately, then re-run whenever a signal it read writes.
|
|
578
|
+
* Returns a disposer that stops the effect and drops all tracked
|
|
579
|
+
* subscriptions. Unlike node render, effect dependencies are re-tracked
|
|
580
|
+
* from scratch on every run — reads that disappear from `fn` stop
|
|
581
|
+
* triggering re-runs.
|
|
582
|
+
*
|
|
583
|
+
* ```ts
|
|
584
|
+
* const dispose = effect(() => {
|
|
585
|
+
* console.log("status is", status())
|
|
586
|
+
* })
|
|
587
|
+
* // ...
|
|
588
|
+
* dispose()
|
|
589
|
+
* ```
|
|
590
|
+
*
|
|
591
|
+
* **Footgun**: don't write a signal from inside an effect that reads
|
|
592
|
+
* the same signal — you'll loop. Writing unrelated signals is fine.
|
|
593
|
+
*/
|
|
594
|
+
declare function effect(fn: () => void | (() => void)): () => void;
|
|
595
|
+
/**
|
|
596
|
+
* Derived, cached signal. `fn` runs once eagerly; the cached value is
|
|
597
|
+
* returned from the accessor. When any signal `fn` reads writes, the
|
|
598
|
+
* memo recomputes and notifies its own subscribers — but only if the
|
|
599
|
+
* new value differs from the cached one (reference equality).
|
|
600
|
+
*
|
|
601
|
+
* ```ts
|
|
602
|
+
* const [value, setValue] = signal(0)
|
|
603
|
+
* const pct = memo(() => Math.round(value() * 100))
|
|
604
|
+
* text(({ style }) => `${pct()}%`) // subscribes to pct, not value
|
|
605
|
+
* ```
|
|
606
|
+
*
|
|
607
|
+
* Returns an `Accessor<T>`, not a `[read, write]` tuple — memos are
|
|
608
|
+
* read-only by construction.
|
|
609
|
+
*/
|
|
610
|
+
declare function memo<T>(fn: () => T): Accessor<T>;
|
|
611
|
+
/**
|
|
612
|
+
* Reactive struct with fine-grained, lazy per-field tracking.
|
|
613
|
+
*
|
|
614
|
+
* Reads:
|
|
615
|
+
* - `store.foo` — tracked when called inside a tracking ctx; allocates
|
|
616
|
+
* a signal for `foo` on first read.
|
|
617
|
+
*
|
|
618
|
+
* Writes:
|
|
619
|
+
* - `store.foo = x` — direct assignment.
|
|
620
|
+
* - `store({ foo: x, bar: y })` — partial patch (merge).
|
|
621
|
+
* - `store(current => ({ foo: current.foo + 1 }))` — updater form;
|
|
622
|
+
* the function receives the current plain state (untracked) and
|
|
623
|
+
* returns a partial patch which is then merged.
|
|
624
|
+
*
|
|
625
|
+
* All writes are value-equality (`===`) gated; setting a field to its
|
|
626
|
+
* current value is a no-op. The function-form updater receives the
|
|
627
|
+
* underlying state object directly so reads inside it are NOT tracked
|
|
628
|
+
* — exactly what an updater wants ("compute next from current").
|
|
629
|
+
*
|
|
630
|
+
* Spread (`{ ...store }`) yields the current values of every field via
|
|
631
|
+
* the proxy's `ownKeys` + `getOwnPropertyDescriptor` traps. Be aware:
|
|
632
|
+
* spreading inside a tracking ctx subscribes that ctx to every field
|
|
633
|
+
* (one signal allocation per field). Outside a tracking ctx, spread is
|
|
634
|
+
* cheap (no signal allocation).
|
|
635
|
+
*
|
|
636
|
+
* Implementation: an inner `state` object is the source of truth for
|
|
637
|
+
* plain reads / spreads / the updater's `current`. A `sigs` map is a
|
|
638
|
+
* lazy tracking layer; a key only gets a signal once it's read inside
|
|
639
|
+
* a tracking ctx. Writes update `state` and notify the signal *if it
|
|
640
|
+
* exists* — fields nobody subscribed to never allocate.
|
|
641
|
+
*/
|
|
642
|
+
type SignalStore<T extends object> = T & {
|
|
643
|
+
/** Apply a partial patch (merge), or call with an updater that
|
|
644
|
+
* receives the current plain state (untracked) and returns a
|
|
645
|
+
* partial to merge. Each field's signal short-circuits on value-
|
|
646
|
+
* equality, so unchanged fields don't notify subscribers. */
|
|
647
|
+
set: (patch: Partial<T> | ((current: T) => Partial<T>)) => void;
|
|
648
|
+
};
|
|
649
|
+
declare function createStore<T extends object>(initial: T): SignalStore<T>;
|
|
650
|
+
/**
|
|
651
|
+
* Suspense boundary handle — the value of `SuspenseContext`. Consumers
|
|
652
|
+
* (`createAsync`) call `increment` / `decrement` around in-flight work
|
|
653
|
+
* so providers (Stream's append, a `suspense()` widget) can observe
|
|
654
|
+
* "is anything pending below me".
|
|
655
|
+
*
|
|
656
|
+
* Boundaries chain: when a `suspense()` body installs its own boundary,
|
|
657
|
+
* its `parent` is the nearest ancestor boundary. Count flips at the
|
|
658
|
+
* 0→1 and 1→0 transitions propagate to the parent, so the outermost
|
|
659
|
+
* boundary's count is `> 0` iff *any* descendant has pending work —
|
|
660
|
+
* a single integer, no walks.
|
|
661
|
+
*
|
|
662
|
+
* `whenIdle` resolves when count returns to 0. Used by Stream to gate
|
|
663
|
+
* paint on resolved async; could be used by any other "wait for
|
|
664
|
+
* subtree to settle" caller.
|
|
665
|
+
*/
|
|
666
|
+
type SuspenseBoundary = {
|
|
667
|
+
increment: () => void;
|
|
668
|
+
decrement: () => void;
|
|
669
|
+
active: () => boolean;
|
|
670
|
+
whenIdle: () => Promise<void>;
|
|
671
|
+
};
|
|
672
|
+
/** Nearest Suspense provider in the Owner chain. `undefined` when no
|
|
673
|
+
* boundary is installed — `createAsync` runs fire-and-forget in that
|
|
674
|
+
* case (signal resolves later, normal invalidation re-renders). */
|
|
675
|
+
declare const SuspenseContext: Context<SuspenseBoundary | undefined>;
|
|
676
|
+
/**
|
|
677
|
+
* Build a boundary whose count flips propagate to `parent` on the 0→1
|
|
678
|
+
* and 1→0 transitions. Used by Stream's `append` and the `suspense()`
|
|
679
|
+
* widget. Internal-ish — most callers shouldn't construct boundaries
|
|
680
|
+
* directly.
|
|
681
|
+
*
|
|
682
|
+
* @internal
|
|
683
|
+
*/
|
|
684
|
+
declare function createSuspenseBoundary(parent?: SuspenseBoundary): SuspenseBoundary;
|
|
685
|
+
/**
|
|
686
|
+
* Resource-style async accessor. `fn` reads signals (tracked); the
|
|
687
|
+
* effect re-fires when any of them change. Each run notifies the
|
|
688
|
+
* nearest `SuspenseContext` so a surrounding boundary (Stream's
|
|
689
|
+
* append, a `suspense()` widget) can observe pending work.
|
|
690
|
+
*
|
|
691
|
+
* ```ts
|
|
692
|
+
* const highlighted = createAsync(
|
|
693
|
+
* async (prev) => highlight(unwrap(props.code)),
|
|
694
|
+
* { initialValue: unwrap(props.code) },
|
|
695
|
+
* )
|
|
696
|
+
* text(highlighted)
|
|
697
|
+
* ```
|
|
698
|
+
*
|
|
699
|
+
* **Stale-write protection**: each run bumps a generation counter;
|
|
700
|
+
* late `.then` callbacks check it and skip `setValue` if a newer run
|
|
701
|
+
* has already fired. The boundary is decremented exactly once per
|
|
702
|
+
* increment (in the settle callback, regardless of staleness).
|
|
703
|
+
*
|
|
704
|
+
* **`prev`** is the previously resolved value (or `initialValue`) —
|
|
705
|
+
* useful for diff-based fetches. Reading it does *not* track, so
|
|
706
|
+
* resolving the new value won't re-fire the effect.
|
|
707
|
+
*
|
|
708
|
+
* **No active boundary** → increment/decrement no-op, work is
|
|
709
|
+
* fire-and-forget. Right for UI surface widgets that re-render
|
|
710
|
+
* cheaply on signal change. The Stream surface installs a boundary
|
|
711
|
+
* per appended subtree so committed-to-scrollback rows always reflect
|
|
712
|
+
* resolved values.
|
|
713
|
+
*/
|
|
714
|
+
declare function createAsync<T>(fn: (prev: T | undefined) => Promise<T>, opts: {
|
|
715
|
+
initialValue: T;
|
|
716
|
+
}): Accessor<T>;
|
|
717
|
+
declare function createAsync<T>(fn: (prev: T | undefined) => Promise<T>, opts?: {
|
|
718
|
+
initialValue?: T;
|
|
719
|
+
}): Accessor<T | undefined>;
|
|
720
|
+
type Progressive<T> = Accessor<T> & {
|
|
721
|
+
readonly loading: Accessor<boolean>;
|
|
722
|
+
whenIdle: () => Promise<void>;
|
|
723
|
+
};
|
|
724
|
+
declare function createProgressive<T>(fn: (ctx: {
|
|
725
|
+
signal: AbortSignal;
|
|
726
|
+
set: (value: T) => void;
|
|
727
|
+
}) => MaybePromise<T>, opts: {
|
|
728
|
+
throttle?: number;
|
|
729
|
+
initialValue: T;
|
|
730
|
+
}): Progressive<T>;
|
|
731
|
+
declare function createProgressive<T>(fn: (ctx: {
|
|
732
|
+
signal: AbortSignal;
|
|
733
|
+
set: (value: T) => void;
|
|
734
|
+
}) => MaybePromise<T>, opts: {
|
|
735
|
+
throttle?: number;
|
|
736
|
+
initialValue?: T;
|
|
737
|
+
}): Progressive<T | undefined>;
|
|
738
|
+
declare function createIterable<T>(fn: (ctx: {
|
|
739
|
+
signal: AbortSignal;
|
|
740
|
+
}) => AsyncIterable<T | readonly T[]>, opts?: {
|
|
741
|
+
throttle?: number;
|
|
742
|
+
initialValue?: readonly T[];
|
|
743
|
+
}): Progressive<readonly T[]>;
|
|
744
|
+
interface Ref<T> {
|
|
745
|
+
(): T;
|
|
746
|
+
get value(): T | undefined;
|
|
747
|
+
set value(value: T);
|
|
748
|
+
}
|
|
749
|
+
declare function createRef<T>(value?: T, opts?: {
|
|
750
|
+
onSet?: (value: T, prev: T | undefined) => void | (() => void);
|
|
751
|
+
}): Ref<T>;
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/core/state.d.ts
|
|
754
|
+
/**
|
|
755
|
+
* Fields every Node reads off its state. Widget state types should
|
|
756
|
+
* extend this (directly or transitively via `Style`, which extends
|
|
757
|
+
* `BaseState`) so the base behaviour wires up automatically.
|
|
758
|
+
*
|
|
759
|
+
* - `visible: false` suppresses rendering with zero layout footprint.
|
|
760
|
+
* Accepts a `Reactive<boolean>` — pass a signal accessor to toggle
|
|
761
|
+
* visibility from shared state. `Node.render` unwraps it at render
|
|
762
|
+
* time so the subscription goes through the usual tracking ctx.
|
|
763
|
+
*/
|
|
764
|
+
interface BaseState extends FlexState, LayoutState {
|
|
765
|
+
/** Sticky nodes stay at the bottom of the stream and don't scroll up with the rest of the content. */
|
|
766
|
+
sticky?: boolean;
|
|
767
|
+
}
|
|
768
|
+
type State<T extends object = object> = T & BaseState;
|
|
769
|
+
/** Widget state mixin: `Style` (fg/bg/attrs) plus `BaseState`
|
|
770
|
+
* (visibility + any future framework-level state fields). Widget state
|
|
771
|
+
* interfaces extend this so base-state concerns and pure styling stay
|
|
772
|
+
* cleanly separated at the type level without each widget having to
|
|
773
|
+
* compose the two manually. */
|
|
774
|
+
type StyleState = Style;
|
|
775
|
+
interface LayoutState {
|
|
776
|
+
visible?: Reactive<boolean>;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Fields a flex-row child contributes to the allocator. Widget state
|
|
780
|
+
* types that want to participate in `flexDirection: "row"` layout
|
|
781
|
+
* (Box, Text, and anything else where per-child sizing makes sense)
|
|
782
|
+
* extend this interface.
|
|
783
|
+
*
|
|
784
|
+
* `allocateRow` reads these fields off each child's `state` — having
|
|
785
|
+
* them in one shared shape means widgets opt in explicitly without
|
|
786
|
+
* Box's row code needing per-widget casts.
|
|
787
|
+
*/
|
|
788
|
+
interface FlexState {
|
|
789
|
+
/** Requested width. `"fill"` requests whatever remains after fixed
|
|
790
|
+
* children; a number requests exactly that many columns. */
|
|
791
|
+
width?: Size;
|
|
792
|
+
/** Floor for allocated width. Combined with the node's intrinsic
|
|
793
|
+
* `Layout.minWidth` — the larger of the two wins. */
|
|
794
|
+
minWidth?: Size;
|
|
795
|
+
/** Ceiling for allocated width. */
|
|
796
|
+
maxWidth?: Size;
|
|
797
|
+
/** Weight used to distribute remaining space. Higher weight = more
|
|
798
|
+
* growth. Unset children default to weight 1 if requested `"fill"`. */
|
|
799
|
+
flexGrow?: number;
|
|
800
|
+
/** Weight used to distribute the deficit when sibling bases exceed
|
|
801
|
+
* available width. Higher weight shrinks more aggressively. Defaults
|
|
802
|
+
* to `1` (CSS `flex-shrink`). Set to `0` to opt out of shrinking. */
|
|
803
|
+
flexShrink?: number;
|
|
804
|
+
}
|
|
805
|
+
interface Layout {
|
|
806
|
+
width: number;
|
|
807
|
+
minWidth: number;
|
|
808
|
+
}
|
|
809
|
+
//#endregion
|
|
810
|
+
//#region src/layout/border.d.ts
|
|
811
|
+
/**
|
|
812
|
+
* Border-character glyphs for drawing a box outline. Any single-cell string
|
|
813
|
+
* is allowed (including multi-byte glyphs like rounded corners).
|
|
814
|
+
*/
|
|
815
|
+
interface BorderChars {
|
|
816
|
+
h: string;
|
|
817
|
+
v: string;
|
|
818
|
+
tl: string;
|
|
819
|
+
tr: string;
|
|
820
|
+
bl: string;
|
|
821
|
+
br: string;
|
|
822
|
+
}
|
|
823
|
+
type BorderSpec = boolean | "single" | "double" | "rounded" | BorderChars;
|
|
824
|
+
type TitleAlign = "left" | "center" | "right";
|
|
825
|
+
//#endregion
|
|
826
|
+
//#region src/widgets/text.d.ts
|
|
827
|
+
/**
|
|
828
|
+
* Text content — three forms, resolved at render time:
|
|
829
|
+
*
|
|
830
|
+
* 1. Plain string — static content.
|
|
831
|
+
* 2. Reactive accessor (`signal()` / `memo()`) — re-reads on each
|
|
832
|
+
* render and subscribes the node so signal writes invalidate.
|
|
833
|
+
* 3. `(ctx) => string` — computed from the render context. Use this
|
|
834
|
+
* for inline-styled spans via `ctx.style`.
|
|
835
|
+
*
|
|
836
|
+
* Branded accessors are picked up via `isAccessor`; any other function
|
|
837
|
+
* is treated as the ctx-aware form. Plain strings pass through.
|
|
838
|
+
*
|
|
839
|
+
* ```ts
|
|
840
|
+
* text("hello")
|
|
841
|
+
* text(name) // signal accessor
|
|
842
|
+
* text(({ style }) => style.ok(`✓ ${count}`)) // ctx-aware
|
|
843
|
+
* ```
|
|
844
|
+
*/
|
|
845
|
+
type TextContent = Reactive<string> | ((ctx: RenderCtx) => MaybePromise<Reactive<string>>);
|
|
846
|
+
interface TextStyle extends Style {
|
|
847
|
+
content: TextContent;
|
|
848
|
+
wrap?: "word" | "char" | "none";
|
|
849
|
+
}
|
|
850
|
+
declare class Text extends Node<TextStyle> {
|
|
851
|
+
protected _render(ctx: RenderCtx): Promise<string[]>;
|
|
852
|
+
layout(ctx: RenderCtx): Promise<Layout>;
|
|
853
|
+
}
|
|
854
|
+
declare function textContent(content: TextContent, ctx: RenderCtx): Promise<string>;
|
|
855
|
+
declare function textContent(content: TextContent | undefined, ctx: RenderCtx): Promise<undefined>;
|
|
856
|
+
/**
|
|
857
|
+
* Factory for `Text`. Content can be a plain string or a `(ctx) => string`
|
|
858
|
+
* function that's called at render time — use the function form when you
|
|
859
|
+
* want inline-styled spans via `ctx.style`.
|
|
860
|
+
*
|
|
861
|
+
* ```ts
|
|
862
|
+
* text("hello")
|
|
863
|
+
* text("hello", { fg: "primary", bold: true })
|
|
864
|
+
* text({ content: "hello", fg: "primary" })
|
|
865
|
+
* text(({ style }) => `ok: ${style.ok("yes")}`)
|
|
866
|
+
* ```
|
|
867
|
+
*/
|
|
868
|
+
declare function text(content: TextContent, style?: Omit<State<TextStyle>, "content">): Text;
|
|
869
|
+
declare function text(style: State<TextStyle>): Text;
|
|
870
|
+
//#endregion
|
|
871
|
+
//#region src/widgets/box.d.ts
|
|
872
|
+
type Padding = number | readonly [v: number, h: number] | readonly [t: number, r: number, b: number, l: number];
|
|
873
|
+
interface BoxStyle extends Style {
|
|
874
|
+
flexDirection?: "row" | "column";
|
|
875
|
+
gap?: number;
|
|
876
|
+
/** Fixed outer height in rows. When unset, the box's height emerges
|
|
877
|
+
* naturally from its content + padding + border. When set, the
|
|
878
|
+
* content area is truncated or padded with blank rows to land at
|
|
879
|
+
* exactly this many rows of *outer* height. Pair with
|
|
880
|
+
* `verticalAlign` to control which edge is anchored. */
|
|
881
|
+
height?: number;
|
|
882
|
+
/** Anchor for the content when `height` truncates or extends.
|
|
883
|
+
* `"top"` (default) keeps the top edge: extra rows added at the
|
|
884
|
+
* bottom, overflow truncated from the bottom. `"bottom"` keeps the
|
|
885
|
+
* bottom edge: extra rows prepended, overflow truncated from the
|
|
886
|
+
* top. Other CSS-like values (`middle`) are not implemented. */
|
|
887
|
+
verticalAlign?: "top" | "bottom";
|
|
888
|
+
padding?: Padding;
|
|
889
|
+
border?: BorderSpec;
|
|
890
|
+
borderTitle?: TextContent;
|
|
891
|
+
/** Placement of `borderTitle` along the top border. Defaults to `"left"`. */
|
|
892
|
+
borderTitleAlign?: TitleAlign;
|
|
893
|
+
/**
|
|
894
|
+
* Style applied to the border glyphs. Either a theme slot name (resolved
|
|
895
|
+
* via the active theme) or an inline `Style`. Defaults to the `"border"`
|
|
896
|
+
* theme slot when a border is drawn.
|
|
897
|
+
*/
|
|
898
|
+
borderStyle?: string | Style;
|
|
899
|
+
/**
|
|
900
|
+
* Style applied to the border title. Defaults to the `"borderTitle"` theme
|
|
901
|
+
* slot when a title is set.
|
|
902
|
+
*/
|
|
903
|
+
borderTitleStyle?: string | Style;
|
|
904
|
+
}
|
|
905
|
+
declare class Box<T extends object = {}> extends Node<BoxStyle & T> {
|
|
906
|
+
#private;
|
|
907
|
+
protected _render(ctx: RenderCtx): Promise<string[]>;
|
|
908
|
+
/**
|
|
909
|
+
* Intrinsic content width derived from children (or the explicit
|
|
910
|
+
* `width` when fixed). Lets parent flex containers size this Box
|
|
911
|
+
* without falling back to a measure-render — important for
|
|
912
|
+
* side-effecting children like `Image` whose first render allocates
|
|
913
|
+
* a KGP placement: rendering twice would emit two placements and the
|
|
914
|
+
* second render's `transmitOnce` returns no bytes, so the bytes from
|
|
915
|
+
* the discarded measure pass would be lost.
|
|
916
|
+
*
|
|
917
|
+
* Row direction → bases sum (plus gaps). Column direction → bases
|
|
918
|
+
* max. Both add chrome (padding + border) at the end.
|
|
919
|
+
*
|
|
920
|
+
* Children without `layout()` contribute 0 — the row allocator's
|
|
921
|
+
* render-measure fallback still kicks in for them at allocation
|
|
922
|
+
* time, but their absence here just means this Box reports a
|
|
923
|
+
* smaller intrinsic. That's the safe direction: under-report and
|
|
924
|
+
* let shrink/grow handle slack, never over-report.
|
|
925
|
+
*/
|
|
926
|
+
layout(ctx: RenderCtx): Promise<{
|
|
927
|
+
minWidth: number;
|
|
928
|
+
width: number;
|
|
929
|
+
}>;
|
|
930
|
+
}
|
|
931
|
+
type Child = Node | false | null | undefined;
|
|
932
|
+
/**
|
|
933
|
+
* Factory for `Box`. The style object is required (use `{}` if you don't
|
|
934
|
+
* need any styling) — the previous style-less overload caused TS to fail
|
|
935
|
+
* to discriminate between a Node child and an inferred-empty-state node
|
|
936
|
+
* like `WidgetNode`. Pass `box({}, child1, child2)` for un-styled boxes.
|
|
937
|
+
*
|
|
938
|
+
* Falsy children (`false`, `null`, `undefined`) are filtered out, enabling
|
|
939
|
+
* conditional JSX-like composition: `box({}, cond && child)`.
|
|
940
|
+
*/
|
|
941
|
+
declare function box(style: State<BoxStyle>, ...children: Child[]): Box;
|
|
942
|
+
//#endregion
|
|
943
|
+
//#region src/widgets/overlay.d.ts
|
|
944
|
+
interface OverlayState {
|
|
945
|
+
/** Absolute column (1-based) of the overlay's left edge. */
|
|
946
|
+
x: number;
|
|
947
|
+
/** Absolute row (1-based) of the overlay's top edge. */
|
|
948
|
+
y: number;
|
|
949
|
+
/** Higher zIndex paints on top. Default: 0. */
|
|
950
|
+
zIndex?: number;
|
|
951
|
+
verticalAnchor?: "top" | "center" | "bottom";
|
|
952
|
+
horizontalAnchor?: "left" | "center" | "right";
|
|
953
|
+
relative?: "screen" | "ui" | "stream";
|
|
954
|
+
/** If true, the overlay will be automatically removed from the renderer when closed.
|
|
955
|
+
* When false, the overlay will remain in the renderer but will be hidden.
|
|
956
|
+
* Default: true. */
|
|
957
|
+
removeOnClose?: boolean;
|
|
958
|
+
}
|
|
959
|
+
type OverlayEvents = {
|
|
960
|
+
close: {};
|
|
961
|
+
};
|
|
962
|
+
type Overlay<T extends Node[] = Node[]> = Box<OverlayState> & {
|
|
963
|
+
children: T;
|
|
964
|
+
} & Emitter<OverlayEvents>;
|
|
965
|
+
declare function overlay<T extends Node[] = Node[]>(state: State<OverlayState & BoxStyle>, ...children: T): Overlay<T>;
|
|
966
|
+
//#endregion
|
|
967
|
+
//#region src/core/ctx.d.ts
|
|
968
|
+
/**
|
|
969
|
+
* Passed to every `render(ctx)` call. Width flows in; height emerges from
|
|
970
|
+
* the returned row count. Theme is ambient — children share the parent's.
|
|
971
|
+
*
|
|
972
|
+
* `style` is a theme-bound chainable builder made available to components so
|
|
973
|
+
* they can produce inline-styled strings without re-binding the theme.
|
|
974
|
+
*
|
|
975
|
+
* `version` is a monotonic cache-key bumped by the `Renderer` whenever any
|
|
976
|
+
* identity-relevant ctx field changes (resize, theme swap). Node caches
|
|
977
|
+
* compare this integer — no hashing, no string keys.
|
|
978
|
+
*/
|
|
979
|
+
interface RenderCtx {
|
|
980
|
+
width: number;
|
|
981
|
+
style: StyleBuilder;
|
|
982
|
+
version: number;
|
|
983
|
+
/** Queue a side-channel ANSI payload (e.g. KGP image transmit) for
|
|
984
|
+
* the renderer to flush before the next paint. Use this to keep
|
|
985
|
+
* side-effecting bytes out of cached row strings — what `_render`
|
|
986
|
+
* returns should be safe to reuse across paints, and transmit
|
|
987
|
+
* bytes shouldn't be. */
|
|
988
|
+
readonly transmit: (seq: string) => void;
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Context handed to a node when it mounts onto a surface. Gives widget
|
|
992
|
+
* authors a scoped handle to the services they legitimately need —
|
|
993
|
+
* focus, overlays, tree lookups, out-of-band repaints — without
|
|
994
|
+
* exposing the full Renderer.
|
|
995
|
+
*
|
|
996
|
+
* MountCtx is *per-lifetime*: set once when `mount()` is called and
|
|
997
|
+
* cleared on `unmount()`. Distinct from `RenderCtx` (per-tick, carries
|
|
998
|
+
* width/theme/version). If a node moves between surfaces, it receives
|
|
999
|
+
* a fresh MountCtx via the subsequent `mount()` call.
|
|
1000
|
+
*/
|
|
1001
|
+
interface MountCtx {
|
|
1002
|
+
/** Which surface owns this subtree. */
|
|
1003
|
+
readonly surface: SurfaceType;
|
|
1004
|
+
/** Overlay capabilities — open/close an Overlay node from inside a
|
|
1005
|
+
* widget (e.g. a confirm dialog, autocomplete popover, tooltip). */
|
|
1006
|
+
readonly overlay: {
|
|
1007
|
+
readonly add: (o: () => Overlay) => void;
|
|
1008
|
+
readonly remove: (o: Overlay) => void;
|
|
1009
|
+
};
|
|
1010
|
+
/** Input capabilities — a narrow slice of the router. We deliberately
|
|
1011
|
+
* don't expose the full `InputRouter`: `setKeymap` / `dispatch` are
|
|
1012
|
+
* app-level concerns. Widgets legitimately need to install direct
|
|
1013
|
+
* global key bindings (autocomplete) and move focus. */
|
|
1014
|
+
readonly input: {
|
|
1015
|
+
readonly terminalFocus: boolean;
|
|
1016
|
+
readonly events: Emitter<InputRouterEvents>;
|
|
1017
|
+
readonly bind: Actions["bind"];
|
|
1018
|
+
/** Move focus to `node`. Mirrors `router.focus(node)`. */
|
|
1019
|
+
readonly focus: (node: Node) => void;
|
|
1020
|
+
/** Clear focus. */
|
|
1021
|
+
readonly blur: (node: Node) => void;
|
|
1022
|
+
readonly queries: TerminalQueries;
|
|
1023
|
+
};
|
|
1024
|
+
/** Action registry — widgets and plugins can `register` catalog
|
|
1025
|
+
* entries (with desc + default keys + optional `fn`) and `dispatch`
|
|
1026
|
+
* actions by id. The Router feeds key-triggered dispatches through
|
|
1027
|
+
* here; programmatic callers do the same. */
|
|
1028
|
+
readonly actions: Actions;
|
|
1029
|
+
/** Look up a node anywhere in the tree by its `id`. First match
|
|
1030
|
+
* wins; `undefined` when nothing matches. Same semantics as
|
|
1031
|
+
* `Renderer.getNode`. */
|
|
1032
|
+
readonly getNode: (id: string) => Node | undefined;
|
|
1033
|
+
/** Find every node matching a predicate. Strings match `node.type`;
|
|
1034
|
+
* pass a function for richer predicates. Same semantics as
|
|
1035
|
+
* `Renderer.findNode`. */
|
|
1036
|
+
readonly findNode: (match: string | ((n: Node) => boolean)) => Node[];
|
|
1037
|
+
logger: Logger;
|
|
1038
|
+
}
|
|
1039
|
+
//#endregion
|
|
1040
|
+
//#region src/widgets/log.d.ts
|
|
1041
|
+
type LogStyle = "badge" | "icon" | "prompt" | "title" | "text" | "notif";
|
|
1042
|
+
interface LogState {
|
|
1043
|
+
level: LogLevel;
|
|
1044
|
+
content: TextContent;
|
|
1045
|
+
title?: string;
|
|
1046
|
+
/** Rendering of the per-level prefix chunk. Defaults by level. */
|
|
1047
|
+
style?: LogStyle;
|
|
1048
|
+
/** Single-glyph icon for `icon` / `prompt` styles. */
|
|
1049
|
+
icon?: string;
|
|
1050
|
+
/** Theme slot or color name used to tint the prefix. */
|
|
1051
|
+
color?: Color;
|
|
1052
|
+
/** Extra plain text prepended to the auto prefix (unstyled). */
|
|
1053
|
+
prefix?: string;
|
|
1054
|
+
/** Theme slot or color name for the body text. Defaults by level, except */
|
|
1055
|
+
textColor?: Color;
|
|
1056
|
+
markdown?: boolean;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* A single log entry — level-styled prefix (icon / badge / prompt / title /
|
|
1060
|
+
* plain text) followed by the body content. The body can be any Node, so
|
|
1061
|
+
* callers can stack markdown, code, images, etc. inside a log line.
|
|
1062
|
+
*
|
|
1063
|
+
* ```ts
|
|
1064
|
+
* log({ level: "info", content: "hello" })
|
|
1065
|
+
* log({ level: "error", content: markdown("**boom**") })
|
|
1066
|
+
* ```
|
|
1067
|
+
*/
|
|
1068
|
+
declare const log: (state: State<LogState>, ...children: Node[]) => Box<{}>;
|
|
1069
|
+
//#endregion
|
|
1070
|
+
//#region src/services/logger.d.ts
|
|
1071
|
+
/** Minimal stream surface the logger needs. Stream satisfies this. */
|
|
1072
|
+
interface LoggerStream {
|
|
1073
|
+
append: (node: () => Node) => unknown;
|
|
1074
|
+
}
|
|
1075
|
+
type LogEntryFactory = (level: LogLevel, msg: unknown[]) => Node;
|
|
1076
|
+
type LogStyleOverride = Omit<LogState, "level" | "content">;
|
|
1077
|
+
interface TuiReporterOpts extends InspectOpts {
|
|
1078
|
+
/** Render string messages as markdown when they look like it.
|
|
1079
|
+
* Default: `true`. */
|
|
1080
|
+
markdown?: boolean;
|
|
1081
|
+
/** Per-level overrides for the default `log()` widget style fields. */
|
|
1082
|
+
styles?: Partial<Record<LogLevel, LogStyleOverride>>;
|
|
1083
|
+
/** Override the node produced per entry. Default uses `log()`. */
|
|
1084
|
+
factory?: LogEntryFactory;
|
|
1085
|
+
wrap?: (node: Node) => Node;
|
|
1086
|
+
/** Fallback writer for when no stream is attached. Defaults to
|
|
1087
|
+
* `process.stdout/stderr`. Injectable for tests. */
|
|
1088
|
+
write?: (text: string, kind: "stdout" | "stderr") => void;
|
|
1089
|
+
}
|
|
1090
|
+
declare class TuiReporter implements LogReporter {
|
|
1091
|
+
#private;
|
|
1092
|
+
constructor(opts?: TuiReporterOpts);
|
|
1093
|
+
attach(stream: LoggerStream): this;
|
|
1094
|
+
detach(): this;
|
|
1095
|
+
$log({ level, msg }: LogEntry): void;
|
|
1096
|
+
}
|
|
1097
|
+
//#endregion
|
|
1098
|
+
//#region src/renderer/terminal.d.ts
|
|
1099
|
+
/**
|
|
1100
|
+
* Terminal primitive — owns stdin raw mode, stdout writes, and the
|
|
1101
|
+
* standard set of control modes we flip for a direct-mode renderer:
|
|
1102
|
+
* hide cursor, DECAWM off, DECSTBM scroll region, synchronized output.
|
|
1103
|
+
*
|
|
1104
|
+
* No rendering logic lives here; upstream surfaces (stream, ui) push
|
|
1105
|
+
* escape sequences through the helpers exposed below.
|
|
1106
|
+
*/
|
|
1107
|
+
interface TerminalWriter {
|
|
1108
|
+
readonly columns: number;
|
|
1109
|
+
readonly rows: number;
|
|
1110
|
+
readonly isTTY?: boolean;
|
|
1111
|
+
write: (s: string) => boolean | void;
|
|
1112
|
+
}
|
|
1113
|
+
interface TerminalReader {
|
|
1114
|
+
readonly isTTY?: boolean;
|
|
1115
|
+
setRawMode?: (mode: boolean) => void;
|
|
1116
|
+
ref?: () => void;
|
|
1117
|
+
unref?: () => void;
|
|
1118
|
+
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
1119
|
+
off: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
1120
|
+
resume?: () => void;
|
|
1121
|
+
pause?: () => void;
|
|
1122
|
+
}
|
|
1123
|
+
interface TerminalOpts {
|
|
1124
|
+
stdin?: TerminalReader;
|
|
1125
|
+
stdout?: TerminalWriter;
|
|
1126
|
+
/** Rows reserved at the bottom for a sticky footer (UI surface). */
|
|
1127
|
+
reserveBottom?: number;
|
|
1128
|
+
/** Register SIGINT/SIGTERM/process.exit handlers. Disable for tests. */
|
|
1129
|
+
hookSignals?: boolean;
|
|
1130
|
+
/** Enable alternate screen buffer. */
|
|
1131
|
+
altScreen?: boolean;
|
|
1132
|
+
/** Enable SGR mouse reporting. */
|
|
1133
|
+
mouse?: boolean;
|
|
1134
|
+
}
|
|
1135
|
+
type ResizeListener = (cols: number, rows: number) => void;
|
|
1136
|
+
/** @internal */
|
|
1137
|
+
declare class Terminal {
|
|
1138
|
+
#private;
|
|
1139
|
+
constructor(opts?: TerminalOpts);
|
|
1140
|
+
/** Current terminal columns (number of cells wide). */
|
|
1141
|
+
get cols(): number;
|
|
1142
|
+
/** Current terminal rows (number of cells tall). */
|
|
1143
|
+
get rows(): number;
|
|
1144
|
+
/** Rows reserved at the bottom for the footer. */
|
|
1145
|
+
get reserveBottom(): number;
|
|
1146
|
+
/** Bottom row of the scroll region (1-based, inclusive). */
|
|
1147
|
+
get scrollBottom(): number;
|
|
1148
|
+
/** Top row of the footer (1-based, inclusive). 0 when no footer. */
|
|
1149
|
+
get footerTop(): number;
|
|
1150
|
+
get altScreen(): boolean;
|
|
1151
|
+
get mouse(): boolean;
|
|
1152
|
+
set mouse(mouse: boolean);
|
|
1153
|
+
/**
|
|
1154
|
+
* Install control-mode state: hide cursor, DECAWM off, DECSTBM scroll
|
|
1155
|
+
* region, stdin raw mode, SIGWINCH + SIGINT/SIGTERM hooks.
|
|
1156
|
+
*/
|
|
1157
|
+
start(): void;
|
|
1158
|
+
deleteImages(opts?: {
|
|
1159
|
+
data?: boolean;
|
|
1160
|
+
}): void;
|
|
1161
|
+
/** Restore terminal modes, detach listeners. Idempotent. */
|
|
1162
|
+
stop(): void;
|
|
1163
|
+
/** Set terminal/window progress when supported (OSC 9;4).
|
|
1164
|
+
*
|
|
1165
|
+
* - omitted / `"inactive"`: clear progress
|
|
1166
|
+
* - `"loading"`: indeterminate progress
|
|
1167
|
+
* - `"progress"`: determinate progress percentage, clamped to `0..100`
|
|
1168
|
+
* - `"error"` / `"paused"`: terminal-specific error/paused states with optional percentage
|
|
1169
|
+
*/
|
|
1170
|
+
setProgress(state: "progress", value: number): void;
|
|
1171
|
+
setProgress(state: "error" | "paused", value?: number): void;
|
|
1172
|
+
setProgress(state?: "inactive" | "loading"): void;
|
|
1173
|
+
/** Change the bottom reservation and re-apply the scroll region. */
|
|
1174
|
+
setReserveBottom(rows: number): void;
|
|
1175
|
+
/** Low-level write. Use escape helpers + this to emit sequences. */
|
|
1176
|
+
write(s: string): void;
|
|
1177
|
+
/** Queue a side-channel ANSI payload (KGP transmit, etc.) for the
|
|
1178
|
+
* next `flushTransmits()`. No-op for empty strings. */
|
|
1179
|
+
enqueueTransmit(seq: string): void;
|
|
1180
|
+
/** Drain the transmit queue. Called by the renderer at the start of
|
|
1181
|
+
* each paint, before frame composition. Idempotent when empty. */
|
|
1182
|
+
flushTransmits(): void;
|
|
1183
|
+
/** Absolute cursor move (1-based). */
|
|
1184
|
+
moveTo(row: number, col?: number): string;
|
|
1185
|
+
/** Erase the entire line the cursor is on. */
|
|
1186
|
+
clearLine(): string;
|
|
1187
|
+
/** Clear from cursor to end of screen. */
|
|
1188
|
+
clearBelow(): string;
|
|
1189
|
+
/** Scroll the scroll region up by `n` rows (SU). Top rows enter scrollback. */
|
|
1190
|
+
scrollUp(n: number): string;
|
|
1191
|
+
/**
|
|
1192
|
+
* Scroll the scroll region down by `n` rows (SD). Top `n` rows become
|
|
1193
|
+
* blank, existing content shifts down, bottom `n` rows fall off the
|
|
1194
|
+
* region (they don't move to scrollback — scrollback is a top-side
|
|
1195
|
+
* concept; SD's displaced rows are simply lost).
|
|
1196
|
+
*/
|
|
1197
|
+
scrollDown(n: number): string;
|
|
1198
|
+
/** Delete `n` lines at the cursor, pulling content below upward (DL). */
|
|
1199
|
+
deleteLines(n: number): string;
|
|
1200
|
+
/** Set the scroll region (DECSTBM), 1-based inclusive. */
|
|
1201
|
+
setScrollRegion(top: number, bottom: number): void;
|
|
1202
|
+
/** Reset to the full-viewport scroll region. */
|
|
1203
|
+
clearScrollRegion(): void;
|
|
1204
|
+
/**
|
|
1205
|
+
* Wrap a block of writes in begin/end-synchronized-update. Supporting
|
|
1206
|
+
* terminals buffer the enclosed bytes and paint atomically — eliminates
|
|
1207
|
+
* flicker on multi-row diff rewrites. Unsupported terminals ignore
|
|
1208
|
+
* both escapes, so there's no downside.
|
|
1209
|
+
*/
|
|
1210
|
+
sync(fn: () => void): void;
|
|
1211
|
+
onResize(fn: ResizeListener): () => void;
|
|
1212
|
+
}
|
|
1213
|
+
//#endregion
|
|
1214
|
+
//#region src/renderer/frame.d.ts
|
|
1215
|
+
type FrameOp = (terminal: Terminal) => void;
|
|
1216
|
+
type LineSlice = {
|
|
1217
|
+
from: number;
|
|
1218
|
+
line: string;
|
|
1219
|
+
to?: number;
|
|
1220
|
+
};
|
|
1221
|
+
declare class Frame {
|
|
1222
|
+
#private;
|
|
1223
|
+
readonly terminal: Terminal;
|
|
1224
|
+
constructor(terminal: Terminal);
|
|
1225
|
+
reset(rows?: number): void;
|
|
1226
|
+
begin(): RenderFrame;
|
|
1227
|
+
slice(row: number, from: number | undefined, to: number | undefined, width?: number): LineSlice | undefined;
|
|
1228
|
+
}
|
|
1229
|
+
declare class RenderFrame {
|
|
1230
|
+
#private;
|
|
1231
|
+
readonly terminal: Terminal;
|
|
1232
|
+
readonly current: (string | undefined)[];
|
|
1233
|
+
readonly base: (string | undefined)[];
|
|
1234
|
+
constructor(terminal: Terminal, current: (string | undefined)[], base: (string | undefined)[]);
|
|
1235
|
+
get(row: number): string;
|
|
1236
|
+
set(row: number, content: string): void;
|
|
1237
|
+
clear(row: number): void;
|
|
1238
|
+
overlay(row: number, col: number, content: string): void;
|
|
1239
|
+
slice(row: number, from: number | undefined, to: number | undefined, width?: number): LineSlice | undefined;
|
|
1240
|
+
highlight(row: number, from: number | undefined, to: number | undefined, ctx: RenderCtx): LineSlice | undefined;
|
|
1241
|
+
queue(op: FrameOp): void;
|
|
1242
|
+
commitBase(): void;
|
|
1243
|
+
flush(): void;
|
|
1244
|
+
scrollUp(top: number, bottom: number, lines: number, op: FrameOp): void;
|
|
1245
|
+
scrollDown(top: number, bottom: number, lines: number, op: FrameOp): void;
|
|
1246
|
+
paint(): {
|
|
1247
|
+
rows: number;
|
|
1248
|
+
ops: number;
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
//#endregion
|
|
1252
|
+
//#region src/renderer/surface.d.ts
|
|
1253
|
+
/**
|
|
1254
|
+
* Base event map for renderer surfaces. Surfaces usually schedule work
|
|
1255
|
+
* through their owning `Renderer`; subclasses may extend this with their
|
|
1256
|
+
* own surface-specific events.
|
|
1257
|
+
*
|
|
1258
|
+
* @internal
|
|
1259
|
+
*/
|
|
1260
|
+
type SurfaceEvents = {};
|
|
1261
|
+
type Point = {
|
|
1262
|
+
row: number;
|
|
1263
|
+
col: number;
|
|
1264
|
+
};
|
|
1265
|
+
/**
|
|
1266
|
+
* Shared base for the three renderer surfaces (`Stream`, `UI`,
|
|
1267
|
+
* `OverlaySurface`). Owns the common lifecycle and paint bookkeeping:
|
|
1268
|
+
*
|
|
1269
|
+
* - `#running` flag and `#mountCtx` reference, driven by the Renderer
|
|
1270
|
+
* `start` / `stop` events. Subclasses read `mountCtx` to mount nodes
|
|
1271
|
+
* appended while the renderer is running.
|
|
1272
|
+
* - `#dirty`, set by `invalidate()`, means this surface has content or
|
|
1273
|
+
* state changes and schedules a frame.
|
|
1274
|
+
* Concrete surfaces override `mountAll` / `unmountAll` to walk whichever
|
|
1275
|
+
* node set they track, and `_render()` to compute rows and enqueue a paint
|
|
1276
|
+
* closure into the renderer's atomic terminal sync.
|
|
1277
|
+
*
|
|
1278
|
+
* @internal
|
|
1279
|
+
*/
|
|
1280
|
+
declare abstract class Surface<E extends {} = never> extends Emitter<SurfaceEvents, E> {
|
|
1281
|
+
#private;
|
|
1282
|
+
abstract readonly type: SurfaceType;
|
|
1283
|
+
$r: Renderer;
|
|
1284
|
+
/** 1-based inclusive row bounds of this surface's content */
|
|
1285
|
+
abstract bounds: {
|
|
1286
|
+
top: number;
|
|
1287
|
+
bottom: number;
|
|
1288
|
+
};
|
|
1289
|
+
constructor(renderer: Renderer);
|
|
1290
|
+
/** Check if this surface contains the given screen position */
|
|
1291
|
+
contains(point: Point): boolean;
|
|
1292
|
+
get dirty(): boolean;
|
|
1293
|
+
track(ev: string, inc?: number): void;
|
|
1294
|
+
/** Mark this surface as changed and schedule a renderer frame. */
|
|
1295
|
+
invalidate: () => void;
|
|
1296
|
+
/** `true` between `onStart()` and `onStop()`. Subclasses read this
|
|
1297
|
+
* to decide whether an append should mount immediately or wait. */
|
|
1298
|
+
protected get running(): boolean;
|
|
1299
|
+
/** The MountCtx handed in by the Renderer on `start()`. Undefined
|
|
1300
|
+
* when the surface isn't running. Subclasses read this to mount
|
|
1301
|
+
* nodes appended while the renderer is up. */
|
|
1302
|
+
protected get mountCtx(): MountCtx | undefined;
|
|
1303
|
+
/** Mount every node this surface currently tracks under `ctx`.
|
|
1304
|
+
* Called by `onStart` after `#running` flips on. */
|
|
1305
|
+
protected abstract mountAll(ctx: MountCtx): void;
|
|
1306
|
+
/** Unmount every node this surface currently tracks. Called by
|
|
1307
|
+
* `onStop` before `#mountCtx` is cleared. */
|
|
1308
|
+
protected abstract unmountAll(): void;
|
|
1309
|
+
/** Every node this surface currently tracks, for renderer traversals. */
|
|
1310
|
+
abstract get nodes(): readonly Node[];
|
|
1311
|
+
/** Paint the surface into the renderer frame. Direct callers (tests) may
|
|
1312
|
+
* omit a frame; in that case a one-off frame is created and flushed. */
|
|
1313
|
+
abstract _render(frame: RenderFrame): Promise<void>;
|
|
1314
|
+
render(frame?: RenderFrame): Promise<void>;
|
|
1315
|
+
}
|
|
1316
|
+
//#endregion
|
|
1317
|
+
//#region src/renderer/overlay.d.ts
|
|
1318
|
+
type OverlayRenderState = {
|
|
1319
|
+
x: number;
|
|
1320
|
+
y: number;
|
|
1321
|
+
width: number;
|
|
1322
|
+
height: number;
|
|
1323
|
+
zIndex: number;
|
|
1324
|
+
node: Overlay;
|
|
1325
|
+
};
|
|
1326
|
+
type OverlaySurfaceEvents = {
|
|
1327
|
+
"render-node": OverlayRenderState;
|
|
1328
|
+
};
|
|
1329
|
+
/**
|
|
1330
|
+
* Overlay surface — paints absolute-positioned `Overlay` nodes on top
|
|
1331
|
+
* of the stream and UI surfaces after each tick.
|
|
1332
|
+
*
|
|
1333
|
+
* Open/close is explicit. While an overlay is open, mutations on its
|
|
1334
|
+
* state (or its subtree) bubble through `invalidate` → this surface's
|
|
1335
|
+
* `dirty` → Renderer's scheduler, same as the other surfaces. On
|
|
1336
|
+
* close, the covered area is overdrawn by asking the stream and UI
|
|
1337
|
+
* to invalidate so the next flush repaints them in full.
|
|
1338
|
+
*
|
|
1339
|
+
* v1 is intentionally minimal:
|
|
1340
|
+
* - Overlays are painted top-to-bottom in z-order on every flush; we
|
|
1341
|
+
* don't track per-overlay dirty flags, just paint all active ones.
|
|
1342
|
+
* - No clipping against the viewport — callers are responsible for
|
|
1343
|
+
* sizing / positioning sensibly. The stream lives inside DECSTBM,
|
|
1344
|
+
* so overlays that touch row 0 can disrupt scrollback.
|
|
1345
|
+
*/
|
|
1346
|
+
declare class OverlaySurface extends Surface<OverlaySurfaceEvents> {
|
|
1347
|
+
#private;
|
|
1348
|
+
readonly type = "overlay";
|
|
1349
|
+
get state(): readonly OverlayRenderState[];
|
|
1350
|
+
get bounds(): {
|
|
1351
|
+
top: number;
|
|
1352
|
+
bottom: number;
|
|
1353
|
+
};
|
|
1354
|
+
get dirty(): boolean;
|
|
1355
|
+
/** Active overlays in paint order (low → high z-index). */
|
|
1356
|
+
get active(): readonly Overlay[];
|
|
1357
|
+
/** Return the topmost overlay at the given `(row, col)`, if any. */
|
|
1358
|
+
at({ row, col }: Point): OverlayRenderState | undefined;
|
|
1359
|
+
contains(point: Point): boolean;
|
|
1360
|
+
/** Register an overlay with the surface. Function form runs `fn`
|
|
1361
|
+
* inside a fresh Owner scope and adopts the returned Overlay. */
|
|
1362
|
+
add<O extends Overlay>(overlay: () => O): O;
|
|
1363
|
+
remove(overlay: Overlay): this;
|
|
1364
|
+
open<T extends Overlay = Overlay>(overlay: () => T): T;
|
|
1365
|
+
close(overlay: Overlay): this;
|
|
1366
|
+
/** Every currently-open overlay node, for renderer traversals. */
|
|
1367
|
+
get nodes(): readonly Node[];
|
|
1368
|
+
protected mountAll(ctx: MountCtx): void;
|
|
1369
|
+
protected unmountAll(): void;
|
|
1370
|
+
/**
|
|
1371
|
+
* Render pass: compute rows for each active overlay, then paint them
|
|
1372
|
+
* at their absolute `(y, x)` inside the outer sync block. Matches the
|
|
1373
|
+
* `render(sync?)` shape used by Stream and UI so the Renderer can
|
|
1374
|
+
* capture all three paints and emit them in one atomic frame.
|
|
1375
|
+
*/
|
|
1376
|
+
_render(frame: RenderFrame): Promise<void>;
|
|
1377
|
+
}
|
|
1378
|
+
//#endregion
|
|
1379
|
+
//#region src/renderer/selection.d.ts
|
|
1380
|
+
type SelectionSurface = "stream" | "screen";
|
|
1381
|
+
type Selection = {
|
|
1382
|
+
dragging: boolean;
|
|
1383
|
+
from: Point;
|
|
1384
|
+
surface: SelectionSurface;
|
|
1385
|
+
to: Point;
|
|
1386
|
+
};
|
|
1387
|
+
type SelectionEvents = {
|
|
1388
|
+
change: {
|
|
1389
|
+
selection?: Selection;
|
|
1390
|
+
prev?: Selection;
|
|
1391
|
+
};
|
|
1392
|
+
selection: {
|
|
1393
|
+
text: string;
|
|
1394
|
+
selection: Selection;
|
|
1395
|
+
};
|
|
1396
|
+
};
|
|
1397
|
+
declare class SelectionLayer extends Emitter<SelectionEvents> {
|
|
1398
|
+
#private;
|
|
1399
|
+
$r: Renderer;
|
|
1400
|
+
constructor(renderer: Renderer);
|
|
1401
|
+
get selection(): Selection | undefined;
|
|
1402
|
+
set selection(value: Selection | undefined);
|
|
1403
|
+
get text(): string | undefined;
|
|
1404
|
+
set text(value: string);
|
|
1405
|
+
invalidate(): void;
|
|
1406
|
+
mouse(event: MouseEvent): boolean;
|
|
1407
|
+
clear(): void;
|
|
1408
|
+
select(screen: Point, what: "line" | "word"): void;
|
|
1409
|
+
renderStream(frame: RenderFrame, ctx: RenderCtx): void;
|
|
1410
|
+
renderScreen(frame: RenderFrame, ctx: RenderCtx): void;
|
|
1411
|
+
render(frame: RenderFrame, ctx: RenderCtx): void;
|
|
1412
|
+
}
|
|
1413
|
+
//#endregion
|
|
1414
|
+
//#region src/renderer/stream.d.ts
|
|
1415
|
+
type StreamEvents = {
|
|
1416
|
+
idle: void;
|
|
1417
|
+
scroll: {
|
|
1418
|
+
offset: number;
|
|
1419
|
+
total: number;
|
|
1420
|
+
below: number;
|
|
1421
|
+
};
|
|
1422
|
+
};
|
|
1423
|
+
type StreamOptions = {
|
|
1424
|
+
maxLive: number;
|
|
1425
|
+
/** Baseline ("steady state") footer height in rows. The commit-to-
|
|
1426
|
+
* scrollback threshold uses `terminal.rows - fixedFooterHeight`
|
|
1427
|
+
* instead of `terminal.rows`. With this set to the natural footer
|
|
1428
|
+
* size (e.g. 2 for an input bar), scrollback is exactly contiguous
|
|
1429
|
+
* with the visible region in steady state — no rows hidden behind
|
|
1430
|
+
* the footer. Footer growth past this size (autocomplete) still
|
|
1431
|
+
* hides rows temporarily; they reappear when the footer returns
|
|
1432
|
+
* to baseline.
|
|
1433
|
+
*
|
|
1434
|
+
* Default `0`: commit at full `terminal.rows` (old behavior).
|
|
1435
|
+
*/
|
|
1436
|
+
fixedFooterHeight: number;
|
|
1437
|
+
};
|
|
1438
|
+
declare class Stream extends Surface<StreamEvents> {
|
|
1439
|
+
#private;
|
|
1440
|
+
readonly type = "stream";
|
|
1441
|
+
constructor(renderer: Renderer, opts?: Partial<StreamOptions>);
|
|
1442
|
+
get bounds(): {
|
|
1443
|
+
top: number;
|
|
1444
|
+
bottom: number;
|
|
1445
|
+
};
|
|
1446
|
+
get terminal(): Terminal;
|
|
1447
|
+
/**
|
|
1448
|
+
* Append `node` as the new live tail. The previous tail is frozen —
|
|
1449
|
+
* it stops receiving re-renders even if its state mutates.
|
|
1450
|
+
*
|
|
1451
|
+
* Function form: `append(() => node)` runs the function inside a
|
|
1452
|
+
* fresh Owner scope (so `signal` / `effect` / `onCleanup` /
|
|
1453
|
+
* `provideContext` inside `fn` attach to that scope) and appends the
|
|
1454
|
+
* returned Node. The Owner disposes when the Node unmounts.
|
|
1455
|
+
*/
|
|
1456
|
+
append<N extends Node>(node: () => N): N;
|
|
1457
|
+
/** How many rows the live region may occupy. */
|
|
1458
|
+
get liveHeight(): number;
|
|
1459
|
+
/** Tracked nodes (oldest first). Used by renderer traversals. */
|
|
1460
|
+
get nodes(): readonly Node[];
|
|
1461
|
+
waitIdle(): Promise<void>;
|
|
1462
|
+
/**
|
|
1463
|
+
* Rows currently painted in the live region, top-to-bottom. Bottom
|
|
1464
|
+
* of the returned array aligns with `terminal.scrollBottom`. Exposed
|
|
1465
|
+
* so the overlay surface can restore what it drew over.
|
|
1466
|
+
*/
|
|
1467
|
+
get rows(): readonly string[];
|
|
1468
|
+
getRow(row: number): string | undefined;
|
|
1469
|
+
fromScreen(point: Point): Point | undefined;
|
|
1470
|
+
toScreen(point: Point): Point | undefined;
|
|
1471
|
+
emitScroll(): void;
|
|
1472
|
+
scroll(lines?: number): Promise<void>;
|
|
1473
|
+
scrollBottom(): Promise<void>;
|
|
1474
|
+
scrollTop(): Promise<void>;
|
|
1475
|
+
scrollUp(lines?: number): Promise<void>;
|
|
1476
|
+
scrollDown(lines?: number): Promise<void>;
|
|
1477
|
+
_render(frame: RenderFrame): Promise<void>;
|
|
1478
|
+
get pending(): boolean;
|
|
1479
|
+
get active(): boolean;
|
|
1480
|
+
/**
|
|
1481
|
+
* Terminal was resized. Forget only the paint mirror that mapped stream
|
|
1482
|
+
* rows onto the old screen geometry. App scrollback and per-state commit
|
|
1483
|
+
* boundaries are preserved; retained nodes re-render at the new width
|
|
1484
|
+
* through Node.render()'s cache key.
|
|
1485
|
+
*
|
|
1486
|
+
* Paired with a screen-clear in the Renderer's resize handler — this
|
|
1487
|
+
* method only resets our mirror of what's on screen; the actual wipe
|
|
1488
|
+
* and re-establishment of DECSTBM lives in the terminal-level handler.
|
|
1489
|
+
*/
|
|
1490
|
+
onResize(): void;
|
|
1491
|
+
resetPaint(): void;
|
|
1492
|
+
reset(opts?: {
|
|
1493
|
+
keepNodes?: boolean;
|
|
1494
|
+
}): void;
|
|
1495
|
+
protected mountAll(ctx: MountCtx): void;
|
|
1496
|
+
protected unmountAll(): void;
|
|
1497
|
+
}
|
|
1498
|
+
//#endregion
|
|
1499
|
+
//#region src/renderer/ui.d.ts
|
|
1500
|
+
/**
|
|
1501
|
+
* UI surface — the sticky footer. Renders a single Box tree at the
|
|
1502
|
+
* bottom of the terminal, inside the rows reserved by `Terminal`'s
|
|
1503
|
+
* scroll region. Row-diff redraws: only changed rows are rewritten on
|
|
1504
|
+
* each flush, so an input widget re-rendering every keystroke doesn't
|
|
1505
|
+
* repaint the entire footer.
|
|
1506
|
+
*
|
|
1507
|
+
* Height is determined by the root's rendered row count, capped to a
|
|
1508
|
+
* sensible upper bound so a runaway render can't take over the screen.
|
|
1509
|
+
* When the height changes the terminal's reserved bottom gets updated,
|
|
1510
|
+
* which rewrites `DECSTBM`; the stream surface then has less/more room
|
|
1511
|
+
* and its next flush naturally fills the new geometry.
|
|
1512
|
+
*/
|
|
1513
|
+
declare class UI extends Surface {
|
|
1514
|
+
#private;
|
|
1515
|
+
readonly type = "ui";
|
|
1516
|
+
constructor(renderer: Renderer);
|
|
1517
|
+
/** The footer's root Box. Add children via `ui.root.add(child)`. */
|
|
1518
|
+
get root(): Box;
|
|
1519
|
+
get terminal(): Terminal;
|
|
1520
|
+
/** Shortcut: add a child to the footer root. Same semantics as
|
|
1521
|
+
* `ui.root.add(child)`; returns `this` for chaining.
|
|
1522
|
+
*
|
|
1523
|
+
* Function form: `add(() => box(…))` runs the function inside a
|
|
1524
|
+
* fresh Owner scope (so `signal` / `effect` / `onCleanup` /
|
|
1525
|
+
* `provideContext` inside `fn` attach to that scope) and adds the
|
|
1526
|
+
* returned Node. The Owner disposes when the Node unmounts. */
|
|
1527
|
+
add<N extends Node>(child: () => N): N;
|
|
1528
|
+
/** Current rendered height (rows). */
|
|
1529
|
+
get height(): number;
|
|
1530
|
+
/**
|
|
1531
|
+
* The rows currently painted into the footer region, in order from the
|
|
1532
|
+
* top of the reserved area downward. Exposed so the overlay surface
|
|
1533
|
+
* can re-emit this content after it tears down, without re-rendering
|
|
1534
|
+
* the tree.
|
|
1535
|
+
*/
|
|
1536
|
+
get rows(): readonly string[];
|
|
1537
|
+
/**
|
|
1538
|
+
* Render the footer tree and paint the diff against the last paint.
|
|
1539
|
+
* Updates the terminal's reservation whenever the height changes so
|
|
1540
|
+
* the scroll region stays correctly sized.
|
|
1541
|
+
*
|
|
1542
|
+
* When called from `Renderer.render()`, a capture-style `sync` is
|
|
1543
|
+
* provided so all surfaces paint inside one atomic sync frame. Direct
|
|
1544
|
+
* callers (tests) omit it and get an immediate `terminal.sync`.
|
|
1545
|
+
*/
|
|
1546
|
+
_render(frame: RenderFrame): Promise<void>;
|
|
1547
|
+
/**
|
|
1548
|
+
* Terminal was resized. Drop the painted-rows mirror so the next
|
|
1549
|
+
* render's grow/shrink math treats the footer as if it's just
|
|
1550
|
+
* appearing — correct after a screen clear, when the terminal no
|
|
1551
|
+
* longer holds any footer bytes. The scroll region gets re-established
|
|
1552
|
+
* through the normal `setReserveBottom` path inside `render()`.
|
|
1553
|
+
*/
|
|
1554
|
+
onResize(): void;
|
|
1555
|
+
/** UI's tracked node set is just the footer root. */
|
|
1556
|
+
get nodes(): readonly Node[];
|
|
1557
|
+
protected mountAll(ctx: MountCtx): void;
|
|
1558
|
+
protected unmountAll(): void;
|
|
1559
|
+
get bounds(): {
|
|
1560
|
+
top: number;
|
|
1561
|
+
bottom: number;
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
//#endregion
|
|
1565
|
+
//#region src/renderer/renderer.d.ts
|
|
1566
|
+
interface RendererOptions {
|
|
1567
|
+
stdin?: TerminalReader;
|
|
1568
|
+
stdout?: TerminalWriter;
|
|
1569
|
+
images?: Reactive<boolean>;
|
|
1570
|
+
theme?: Theme;
|
|
1571
|
+
/** Baseline footer height in rows — the size the footer occupies in
|
|
1572
|
+
* "steady state" (no autocomplete, no transient widget growth).
|
|
1573
|
+
* Stream uses `terminal.rows - fixedFooterHeight` as its commit
|
|
1574
|
+
* threshold, so scrollback ends exactly at the visible region's top
|
|
1575
|
+
* edge whenever the footer is at this size. Default `0` (commit at
|
|
1576
|
+
* the full terminal — preserves pre-existing behavior). Set to
|
|
1577
|
+
* match your app's persistent footer (e.g. 2 for a single-line
|
|
1578
|
+
* input with a one-line border). */
|
|
1579
|
+
fixedFooterHeight?: number;
|
|
1580
|
+
/** Enable alternate screen buffer. Useful to test whether native
|
|
1581
|
+
* scrollback is competing with app-level scrolling. */
|
|
1582
|
+
altScreen?: boolean;
|
|
1583
|
+
/** Register SIGINT/SIGTERM cleanup (default: true). Disable in tests. */
|
|
1584
|
+
hookSignals?: boolean;
|
|
1585
|
+
logger?: Logger;
|
|
1586
|
+
/** Enable terminal mouse reporting. Scroll events scroll the stream. */
|
|
1587
|
+
mouse?: boolean;
|
|
1588
|
+
reporter?: TuiReporterOpts;
|
|
1589
|
+
}
|
|
1590
|
+
type RenderEvents = {
|
|
1591
|
+
start: {};
|
|
1592
|
+
stop: {};
|
|
1593
|
+
dirty: {};
|
|
1594
|
+
};
|
|
1595
|
+
type SurfaceType = "stream" | "ui" | "overlay";
|
|
1596
|
+
/** Visitor signature for `Renderer.walk`. Return `"stop"` to halt. */
|
|
1597
|
+
type NodeVisitor = (node: Node) => void | "stop";
|
|
1598
|
+
declare class RenderStats {
|
|
1599
|
+
#private;
|
|
1600
|
+
/** Increment a stat by `delta` (default: `1`). */
|
|
1601
|
+
inc(stat: string, delta?: number): void;
|
|
1602
|
+
set(stat: string, value: number): void;
|
|
1603
|
+
get(): Record<string, number>;
|
|
1604
|
+
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Wires a `Terminal` primitive with the `Stream` and `UI` surfaces.
|
|
1607
|
+
* Doesn't render anything until you either append to `renderer.stream`
|
|
1608
|
+
* or add children to `renderer.ui.root`.
|
|
1609
|
+
*
|
|
1610
|
+
* ```ts
|
|
1611
|
+
* const renderer = new Renderer()
|
|
1612
|
+
* renderer.start()
|
|
1613
|
+
* renderer.stream.append(text("hello"))
|
|
1614
|
+
* renderer.ui.root.add(input({ placeholder: "> " }))
|
|
1615
|
+
* // ... later
|
|
1616
|
+
* renderer.stop()
|
|
1617
|
+
* ```
|
|
1618
|
+
*/
|
|
1619
|
+
declare class Renderer extends Emitter<RenderEvents> {
|
|
1620
|
+
#private;
|
|
1621
|
+
readonly stream: Stream;
|
|
1622
|
+
readonly ui: UI;
|
|
1623
|
+
readonly overlay: OverlaySurface;
|
|
1624
|
+
readonly selection: SelectionLayer;
|
|
1625
|
+
readonly terminal: Terminal;
|
|
1626
|
+
readonly input: InputRouter;
|
|
1627
|
+
readonly frame: Frame;
|
|
1628
|
+
/** Always-on logger, auto-attached to `this.stream`. Calling
|
|
1629
|
+
* `renderer.log("msg")` logs at `"log"` level; level methods
|
|
1630
|
+
* (`renderer.log.error(...)` etc.) are also available. */
|
|
1631
|
+
readonly logger: Logger;
|
|
1632
|
+
queries: TerminalQueries;
|
|
1633
|
+
/** Runtime action registry (catalog + dispatcher). Populated with
|
|
1634
|
+
* `defaultActions` + `globalActions` at construction; apps extend
|
|
1635
|
+
* via `renderer.actions.register({ "app.foo": { ... } })`. */
|
|
1636
|
+
readonly actions: Actions;
|
|
1637
|
+
stats: RenderStats;
|
|
1638
|
+
/** Global action impls. Merged into the `actions` catalog on
|
|
1639
|
+
* construction so the ids declared here gain their keys and desc
|
|
1640
|
+
* from `defaultActions`. Using `satisfies` (rather than an explicit
|
|
1641
|
+
* type annotation) keeps the literal keys so `BuiltinAction` can
|
|
1642
|
+
* pull them out. */
|
|
1643
|
+
globalActions: {
|
|
1644
|
+
"global.quit": {
|
|
1645
|
+
fn: () => void;
|
|
1646
|
+
};
|
|
1647
|
+
};
|
|
1648
|
+
constructor(opts?: RendererOptions);
|
|
1649
|
+
/**
|
|
1650
|
+
* Shared `RenderCtx` for the current tick. Built on demand and
|
|
1651
|
+
* reused across every `Node.render(ctx)` call until the version
|
|
1652
|
+
* bumps (resize, theme swap). Nodes compare `ctx.version` against
|
|
1653
|
+
* their cached row's version — simple integer check, no hashing.
|
|
1654
|
+
*/
|
|
1655
|
+
get ctx(): RenderCtx;
|
|
1656
|
+
get rootOwner(): Owner;
|
|
1657
|
+
get mouse(): boolean;
|
|
1658
|
+
set mouse(mouse: boolean);
|
|
1659
|
+
get debug(): boolean;
|
|
1660
|
+
set debug(debug: boolean);
|
|
1661
|
+
/** Whether the renderer is currently running. Surfaces use this to
|
|
1662
|
+
* decide whether a fresh append/open should immediately mount the
|
|
1663
|
+
* node or just queue it for the next `start()`. */
|
|
1664
|
+
get running(): boolean;
|
|
1665
|
+
/** Shortcut for `renderer.input.bind(pattern, handler)`. Returns an
|
|
1666
|
+
* unsubscribe function. Use for one-off app-level bindings where a
|
|
1667
|
+
* named action would be overkill. */
|
|
1668
|
+
bind: Actions["bind"];
|
|
1669
|
+
/** Swap the ambient theme. Widgets that read `theme()` / `style()`
|
|
1670
|
+
* from `useContext(RenderContext)` re-fire automatically; primitive
|
|
1671
|
+
* Nodes that compare cache against `ctx.version` need the version
|
|
1672
|
+
* bump that already happens elsewhere on theme-driven invalidation. */
|
|
1673
|
+
set theme(theme: Theme);
|
|
1674
|
+
/** Get the current theme. (tracked) */
|
|
1675
|
+
get theme(): Theme;
|
|
1676
|
+
start(): void;
|
|
1677
|
+
stop(): void;
|
|
1678
|
+
/** Look up a node by its `id`. Returns `undefined` if nothing matches. */
|
|
1679
|
+
getNode(id: string): Node | undefined;
|
|
1680
|
+
/**
|
|
1681
|
+
* Find every node matching a predicate. When passed a string, it's
|
|
1682
|
+
* interpreted as `node.type === string` — the common "find all
|
|
1683
|
+
* Inputs" case. For anything richer, pass a function.
|
|
1684
|
+
*/
|
|
1685
|
+
findNode(match: string | ((node: Node) => boolean)): Node[];
|
|
1686
|
+
/**
|
|
1687
|
+
* Walk every node in the combined stream + UI tree, depth-first,
|
|
1688
|
+
* parents before children. Return `"stop"` from the visitor to halt.
|
|
1689
|
+
*/
|
|
1690
|
+
walk(visit: NodeVisitor): void;
|
|
1691
|
+
/** Build a MountCtx for a given surface. Each call creates a fresh
|
|
1692
|
+
* object but closes over the same underlying services, so nodes
|
|
1693
|
+
* across surfaces share a single overlay surface, router, etc. */
|
|
1694
|
+
mountCtx(surface: SurfaceType): MountCtx;
|
|
1695
|
+
/** Force a paint if dirty, and wait until the loop is idle. Useful
|
|
1696
|
+
* for tests and external "redraw and tell me when done" flows.
|
|
1697
|
+
* Concurrent callers share the same in-flight promise — no extra
|
|
1698
|
+
* paints are queued. */
|
|
1699
|
+
render(): Promise<void>;
|
|
1700
|
+
}
|
|
1701
|
+
//#endregion
|
|
1702
|
+
//#region src/core/node.d.ts
|
|
1703
|
+
/** Minimum event map every node carries. Custom event maps intersect
|
|
1704
|
+
* this with their own events via `&`. */
|
|
1705
|
+
type BaseEvents = {
|
|
1706
|
+
invalidate: {};
|
|
1707
|
+
mount: {};
|
|
1708
|
+
unmount: {};
|
|
1709
|
+
focus: {};
|
|
1710
|
+
blur: {};
|
|
1711
|
+
show: {};
|
|
1712
|
+
hide: {};
|
|
1713
|
+
key: {
|
|
1714
|
+
key: RoutedKey;
|
|
1715
|
+
};
|
|
1716
|
+
paste: {
|
|
1717
|
+
paste: RoutedPaste;
|
|
1718
|
+
};
|
|
1719
|
+
childadded: {
|
|
1720
|
+
child: Node;
|
|
1721
|
+
};
|
|
1722
|
+
childremoved: {
|
|
1723
|
+
child: Node;
|
|
1724
|
+
};
|
|
1725
|
+
};
|
|
1726
|
+
/**
|
|
1727
|
+
* Public Node handle. Calling `render(ctx)` returns the node's rows from the
|
|
1728
|
+
* per-node cache, recomputing only if state read inside `_render` has changed
|
|
1729
|
+
* since the last call.
|
|
1730
|
+
*
|
|
1731
|
+
* Concrete subclasses implement the protected `_render(ctx)` hook; the public
|
|
1732
|
+
* `render` is the caching wrapper around it.
|
|
1733
|
+
*
|
|
1734
|
+
* State is a `createStore` proxy: reads inside `_render` track per-field;
|
|
1735
|
+
* mutating a read field invalidates this node. Three write forms:
|
|
1736
|
+
*
|
|
1737
|
+
* - `n.state.field = value` (direct assignment)
|
|
1738
|
+
* - `n.state.set({ field: value, ... })` (patch — partial merge)
|
|
1739
|
+
* - `n.state.set(current => ({ ... }))` (updater — receives untracked current)
|
|
1740
|
+
*
|
|
1741
|
+
* Writes through nested objects/arrays (`n.state.padding[0] = 1`) do NOT —
|
|
1742
|
+
* reassign the whole field instead.
|
|
1743
|
+
*/
|
|
1744
|
+
declare abstract class Node<T extends object = object, E extends {} = {}> extends Emitter<BaseEvents, E> {
|
|
1745
|
+
#private;
|
|
1746
|
+
actions?: NodeActionMap;
|
|
1747
|
+
type?: string;
|
|
1748
|
+
protected layout?(ctx: RenderCtx): MaybePromise<Layout | undefined>;
|
|
1749
|
+
constructor(state: State<T>, ...children: Node[]);
|
|
1750
|
+
withEvents<Events extends {}>(): this & Emitter<Events>;
|
|
1751
|
+
withActions<A extends NodeActionMap>(actions: A): this & {
|
|
1752
|
+
actions: A;
|
|
1753
|
+
};
|
|
1754
|
+
action(id: keyof NonNullable<this["actions"]> & string, opts?: Partial<ActionCtx>): boolean;
|
|
1755
|
+
get mountSignal(): AbortSignal;
|
|
1756
|
+
get state(): SignalStore<T> & State<T>;
|
|
1757
|
+
get children(): readonly Node[];
|
|
1758
|
+
protected get logger(): import("@zaly/shared/logger").Logger<{
|
|
1759
|
+
name: string;
|
|
1760
|
+
} & Partial<{}> & {
|
|
1761
|
+
id: string | undefined;
|
|
1762
|
+
name: string;
|
|
1763
|
+
node: string;
|
|
1764
|
+
}> | undefined;
|
|
1765
|
+
protected try<R>(fn: () => R): TryResult<R>;
|
|
1766
|
+
/** Fragment protocol — opt-in. When defined, layout containers
|
|
1767
|
+
* (Box's row/column allocator) treat this node as a transparent
|
|
1768
|
+
* fragment and substitute the returned children for `this` during
|
|
1769
|
+
* flex allocation and rendering. The fragment Node still exists in
|
|
1770
|
+
* the tree (it's mounted; it bubbles invalidates from its
|
|
1771
|
+
* descendants), but layout sees through it.
|
|
1772
|
+
*
|
|
1773
|
+
* Used by `show`, `errorBoundary`, `suspense`: their children carry
|
|
1774
|
+
* their own flex props through to the parent box, can share its
|
|
1775
|
+
* `gap`, and a row containing a `show` with multiple flex children
|
|
1776
|
+
* allocates per-leaf-slot rather than collapsing into one. */
|
|
1777
|
+
protected layoutChildren?(): readonly Node[];
|
|
1778
|
+
get parent(): Node | undefined;
|
|
1779
|
+
/** Mount context when this node is attached to a surface — surface
|
|
1780
|
+
* identifier plus scoped handles to router / overlay / tree lookups.
|
|
1781
|
+
* `undefined` before mount and after unmount. Exposed so widget
|
|
1782
|
+
* authors can reach renderer services without closing over a ref. */
|
|
1783
|
+
get ctx(): MountCtx | undefined;
|
|
1784
|
+
get surface(): SurfaceType | undefined;
|
|
1785
|
+
get mounted(): boolean;
|
|
1786
|
+
/**
|
|
1787
|
+
* Read or set the node's `id`. Called without args, returns the
|
|
1788
|
+
* current id. Called with a string, sets it and returns `this` for
|
|
1789
|
+
* chaining — useful when building trees inline:
|
|
1790
|
+
*
|
|
1791
|
+
* ```ts
|
|
1792
|
+
* input({...}).id("chat-input").focus().on("submit", ...)
|
|
1793
|
+
* ```
|
|
1794
|
+
*
|
|
1795
|
+
* The id is used by `ctx.getNode(id)` / `Renderer.getNode(id)` for
|
|
1796
|
+
* tree lookups, and by the input router as a per-instance scope for
|
|
1797
|
+
* keymap bindings (alongside the class-level `type`).
|
|
1798
|
+
*/
|
|
1799
|
+
id(): string | undefined;
|
|
1800
|
+
id(value: string): this;
|
|
1801
|
+
getLayout(ctx: RenderCtx): Promise<Layout | undefined>;
|
|
1802
|
+
get layoutNodes(): readonly Node[];
|
|
1803
|
+
addActionTarget(node: Node): this;
|
|
1804
|
+
removeActionTarget(node: Node): this;
|
|
1805
|
+
get actionTargets(): Iterable<Node>;
|
|
1806
|
+
invalidate(): this;
|
|
1807
|
+
with<R>(fn: () => R): R;
|
|
1808
|
+
render(ctx: RenderCtx): Promise<string[]>;
|
|
1809
|
+
protected abstract _render(ctx: RenderCtx): Promise<string[]> | string[];
|
|
1810
|
+
add(child: Node): this;
|
|
1811
|
+
remove(child: Node): this;
|
|
1812
|
+
splice(start: number, deleteCount: number, ...items: Node[]): this;
|
|
1813
|
+
clear(): this;
|
|
1814
|
+
mount(ctx: MountCtx): this;
|
|
1815
|
+
unmount(): this;
|
|
1816
|
+
/** Become the focused node. Routes through the MountCtx, which emits
|
|
1817
|
+
* `blur` on the previous focus and `focus` on this node.
|
|
1818
|
+
*
|
|
1819
|
+
* When called before the node is mounted (the common case for
|
|
1820
|
+
* `input({ focus: true }).on(...)` style setup), the focus is
|
|
1821
|
+
* deferred until the next `mount` event. That keeps the API
|
|
1822
|
+
* synchronous and side-effect-free at build time — callers don't
|
|
1823
|
+
* have to think about ordering. */
|
|
1824
|
+
focus(): this;
|
|
1825
|
+
/** Release focus. No-op when not mounted. */
|
|
1826
|
+
blur(): this;
|
|
1827
|
+
get visible(): boolean;
|
|
1828
|
+
show(): this;
|
|
1829
|
+
hide(): this;
|
|
1830
|
+
toggle(): this;
|
|
1831
|
+
ref<N extends Node>(ref?: Ref<this extends N ? N : never>): this;
|
|
1832
|
+
isVisible(): boolean;
|
|
1833
|
+
}
|
|
1834
|
+
/** Runtime type guard for Node.
|
|
1835
|
+
*
|
|
1836
|
+
* @internal*/
|
|
1837
|
+
declare function isNode(x: unknown): x is Node;
|
|
1838
|
+
//#endregion
|
|
1839
|
+
//#region src/input/router.d.ts
|
|
1840
|
+
type KeyPatterns = KeyPattern | readonly KeyPattern[];
|
|
1841
|
+
type KeyHandler = (ev: KeyEvent) => boolean | void;
|
|
1842
|
+
/** A keymap entry is either an action id (string) or a direct key
|
|
1843
|
+
* handler — the Router treats them as alternate branches on match.
|
|
1844
|
+
* Multiple entries can share the same key; dispatch tries each in
|
|
1845
|
+
* order until one consumes. */
|
|
1846
|
+
type KeymapEntry = string | KeyHandler;
|
|
1847
|
+
/**
|
|
1848
|
+
* A routed keyboard event — the raw `KeyEvent` data plus a propagation
|
|
1849
|
+
* control and a precomputed canonical `pattern`. Listeners call
|
|
1850
|
+
* `.stop()` to consume the event; the router checks `.stopped` between
|
|
1851
|
+
* parent-chain hops and halts bubbling.
|
|
1852
|
+
*/
|
|
1853
|
+
interface RoutedKey {
|
|
1854
|
+
name: string;
|
|
1855
|
+
text?: string;
|
|
1856
|
+
ctrl: boolean;
|
|
1857
|
+
alt: boolean;
|
|
1858
|
+
shift: boolean;
|
|
1859
|
+
meta: boolean;
|
|
1860
|
+
/** Canonical pattern form (e.g. `"ctrl-a"`, `"shift-enter"`). Handy
|
|
1861
|
+
* for `switch(ev.pattern)` matching without calling `keyMatches`. */
|
|
1862
|
+
pattern: string;
|
|
1863
|
+
stopped: boolean;
|
|
1864
|
+
stop: () => void;
|
|
1865
|
+
}
|
|
1866
|
+
/** A routed paste event — the pasted text + the same propagation control. */
|
|
1867
|
+
interface RoutedPaste {
|
|
1868
|
+
text: string;
|
|
1869
|
+
stopped: boolean;
|
|
1870
|
+
stop: () => void;
|
|
1871
|
+
}
|
|
1872
|
+
type InputRouterEvents = {
|
|
1873
|
+
"terminal-focus": {
|
|
1874
|
+
gained: boolean;
|
|
1875
|
+
};
|
|
1876
|
+
key: {
|
|
1877
|
+
event: KeyEvent;
|
|
1878
|
+
};
|
|
1879
|
+
mouse: {
|
|
1880
|
+
event: MouseEvent;
|
|
1881
|
+
};
|
|
1882
|
+
"term-response": {
|
|
1883
|
+
event: TerminalResponseEvent;
|
|
1884
|
+
};
|
|
1885
|
+
focus: {
|
|
1886
|
+
node: Node;
|
|
1887
|
+
};
|
|
1888
|
+
blur: {
|
|
1889
|
+
node: Node;
|
|
1890
|
+
};
|
|
1891
|
+
};
|
|
1892
|
+
/**
|
|
1893
|
+
* Routes decoder events to the right node and/or action.
|
|
1894
|
+
*
|
|
1895
|
+
* Dispatch priority for a key event (most-specific-first):
|
|
1896
|
+
*
|
|
1897
|
+
* 1. **Per-node `"key"` bubble** — focused node up to root. Each
|
|
1898
|
+
* node's `on("key")` / `bind()` handlers fire. Calling `.stop()`
|
|
1899
|
+
* ends dispatch. This is where local overrides live.
|
|
1900
|
+
* 2. **Keymap lookup** — a single table built by the `Actions`
|
|
1901
|
+
* registry (`canonical pattern → action id | direct handler`).
|
|
1902
|
+
* Action ids are dispatched through `Actions.dispatch(id)`,
|
|
1903
|
+
* which walks the focus chain for `node.actions[id]` — or
|
|
1904
|
+
* calls a catalog-level `fn` if the action has one.
|
|
1905
|
+
* 3. **Global `bind()` handlers** — a last-resort fallback for
|
|
1906
|
+
* ad-hoc app-level bindings registered via `router.bind(...)`.
|
|
1907
|
+
*
|
|
1908
|
+
* Paste events follow a simple emit-and-bubble path — no keymap.
|
|
1909
|
+
*/
|
|
1910
|
+
declare class InputRouter extends Emitter<InputRouterEvents> {
|
|
1911
|
+
#private;
|
|
1912
|
+
constructor(logger?: Logger);
|
|
1913
|
+
/** Internal — wired by the Renderer at construction. */
|
|
1914
|
+
setActions(actions: Actions): void;
|
|
1915
|
+
/** Currently-focused node, or `undefined` when none. */
|
|
1916
|
+
get focused(): Node | undefined;
|
|
1917
|
+
/**
|
|
1918
|
+
* Move focus to `node`. Emits `blur`
|
|
1919
|
+
* on the previously-focused node and `focus` on the new one.
|
|
1920
|
+
* Focusing the already-focused node is a no-op.
|
|
1921
|
+
*/
|
|
1922
|
+
focus(node: Node): void;
|
|
1923
|
+
/** Clear focus from `node`. Emits `blur` on the node and `focus`
|
|
1924
|
+
* on the next-most-recently-focused node, if any. */
|
|
1925
|
+
blur(node: Node): void;
|
|
1926
|
+
get terminalFocus(): boolean;
|
|
1927
|
+
/**
|
|
1928
|
+
* Dispatch a decoder event. Returns `true` if consumed.
|
|
1929
|
+
*/
|
|
1930
|
+
dispatch(ev: InputEvent): boolean;
|
|
1931
|
+
}
|
|
1932
|
+
//#endregion
|
|
1933
|
+
//#region src/input/queries.d.ts
|
|
1934
|
+
type TerminalQuery<T = TerminalResponseEvent> = TerminalQueryOpts & {
|
|
1935
|
+
request: string;
|
|
1936
|
+
match: (event: TerminalResponseEvent) => T | undefined;
|
|
1937
|
+
};
|
|
1938
|
+
type TerminalQueryOpts = {
|
|
1939
|
+
timeout?: number;
|
|
1940
|
+
wrap?: (seq: string) => string;
|
|
1941
|
+
};
|
|
1942
|
+
type TerminalQueryWriter = {
|
|
1943
|
+
write: (seq: string) => void;
|
|
1944
|
+
};
|
|
1945
|
+
declare class TerminalQueries {
|
|
1946
|
+
#private;
|
|
1947
|
+
constructor(router: InputRouter, terminal: TerminalQueryWriter);
|
|
1948
|
+
query<T>(query: TerminalQuery<T>): Promise<T | undefined>;
|
|
1949
|
+
primaryDeviceAttributes(opts?: TerminalQueryOpts): Promise<(TerminalResponseEvent & {
|
|
1950
|
+
attrs: number[];
|
|
1951
|
+
}) | undefined>;
|
|
1952
|
+
secondaryDeviceAttributes(opts?: TerminalQueryOpts): Promise<(TerminalResponseEvent & {
|
|
1953
|
+
attrs: number[];
|
|
1954
|
+
}) | undefined>;
|
|
1955
|
+
xtVersion(opts?: TerminalQueryOpts): Promise<(TerminalResponseEvent & {
|
|
1956
|
+
name: string;
|
|
1957
|
+
version?: string;
|
|
1958
|
+
}) | undefined>;
|
|
1959
|
+
}
|
|
1960
|
+
declare function parseAttrs(params: string): number[];
|
|
1961
|
+
//#endregion
|
|
1962
|
+
export { Context as $, LogState as A, useContext as At, Padding as B, Actions as Bt, OverlaySurface as C, peek as Ct, LoggerStream as D, untrack as Dt, LogStyleOverride as E, toAccessor as Et, Overlay as F, ActionCtx as Ft, text as G, KeyModifier as Gt, Text as H, NodeAction as Ht, OverlayState as I, ActionDef as It, Layout as J, SpecialKeyName as Jt, textContent as K, KeyPattern as Kt, overlay as L, ActionFilter as Lt, log as M, withOwner as Mt, MountCtx as N, Size as Nt, TuiReporter as O, unwrap as Ot, RenderCtx as P, Action as Pt, Accessor as Q, Box as R, ActionFn as Rt, Stream as S, onCleanup as St, LogEntryFactory as T, signal as Tt, TextContent as U, NodeActionMap as Ut, box as V, KeyBinding as Vt, TextStyle as W, KeyEvent as Wt, State as X, isKeyPattern as Xt, LayoutState as Y, canonical as Yt, StyleState as Z, keyMatches as Zt, RenderStats as _, createSuspenseBoundary as _t, InputRouter as a, Signal as at, SurfaceType as b, isAccessor as bt, KeyPatterns as c, SuspenseContext as ct, RoutedPaste as d, createIterable as dt, Owner as et, BaseEvents as f, createNode as ft, RenderEvents as g, createStore as gt, NodeVisitor as h, createRoot as ht, parseAttrs as i, Setter as it, LogStyle as j, withActiveNode as jt, TuiReporterOpts as k, useActiveOwner as kt, KeymapEntry as l, createAsync as lt, isNode as m, createRef as mt, TerminalQuery as n, Reactive as nt, InputRouterEvents as o, SignalStore as ot, Node as p, createProgressive as pt, FlexState as q, KeyPatternError as qt, TerminalQueryOpts as r, Ref as rt, KeyHandler as s, SuspenseBoundary as st, TerminalQueries as t, Progressive as tt, RoutedKey as u, createContext as ut, Renderer as v, effect as vt, Terminal as w, provideContext as wt, UI as x, memo as xt, RendererOptions as y, inRenderContextOf as yt, BoxStyle as z, ActionMap as zt };
|
|
1963
|
+
//# sourceMappingURL=queries-DH3bj74K.d.mts.map
|