@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.
Files changed (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/assets/schemas/theme.schema.json +899 -0
  4. package/assets/themes/LICENSE-tokyonight.txt +201 -0
  5. package/assets/themes/NOTICES.md +69 -0
  6. package/assets/themes/ayu-dark.json +119 -0
  7. package/assets/themes/catppuccin-frappe.json +124 -0
  8. package/assets/themes/catppuccin-latte.json +124 -0
  9. package/assets/themes/catppuccin-macchiato.json +124 -0
  10. package/assets/themes/catppuccin-mocha.json +124 -0
  11. package/assets/themes/dracula-soft.json +119 -0
  12. package/assets/themes/dracula.json +119 -0
  13. package/assets/themes/everforest-dark.json +118 -0
  14. package/assets/themes/everforest-light.json +118 -0
  15. package/assets/themes/github-dark.json +123 -0
  16. package/assets/themes/github-light.json +123 -0
  17. package/assets/themes/gruvbox-dark-medium.json +112 -0
  18. package/assets/themes/kanagawa-dragon.json +115 -0
  19. package/assets/themes/kanagawa-wave.json +115 -0
  20. package/assets/themes/material-theme-palenight.json +114 -0
  21. package/assets/themes/min-dark.json +55 -0
  22. package/assets/themes/min-light.json +59 -0
  23. package/assets/themes/monokai.json +116 -0
  24. package/assets/themes/night-owl.json +116 -0
  25. package/assets/themes/nord.json +101 -0
  26. package/assets/themes/one-dark-pro.json +110 -0
  27. package/assets/themes/one-light.json +108 -0
  28. package/assets/themes/poimandres.json +123 -0
  29. package/assets/themes/rose-pine.json +98 -0
  30. package/assets/themes/solarized-dark.json +116 -0
  31. package/assets/themes/solarized-light.json +116 -0
  32. package/assets/themes/synthwave-84.json +100 -0
  33. package/assets/themes/tokyonight-day.json +157 -0
  34. package/assets/themes/tokyonight-moon.json +157 -0
  35. package/assets/themes/tokyonight-night.json +157 -0
  36. package/assets/themes/tokyonight-storm.json +157 -0
  37. package/assets/themes/vesper.json +103 -0
  38. package/assets/themes/vitesse-dark.json +126 -0
  39. package/assets/themes/vitesse-light.json +126 -0
  40. package/dist/ansi-Clq0ozyA.mjs +206 -0
  41. package/dist/ansi-Clq0ozyA.mjs.map +1 -0
  42. package/dist/ansi.d.mts +48 -0
  43. package/dist/ansi.d.mts.map +1 -0
  44. package/dist/ansi.mjs +2 -0
  45. package/dist/autocomplete-Dfw-yQDa.d.mts +100 -0
  46. package/dist/autocomplete-Dfw-yQDa.d.mts.map +1 -0
  47. package/dist/box-DyaByEiG.mjs +386 -0
  48. package/dist/box-DyaByEiG.mjs.map +1 -0
  49. package/dist/builder-D49ECBEf.mjs +385 -0
  50. package/dist/builder-D49ECBEf.mjs.map +1 -0
  51. package/dist/callbacks-CNjGNxVw.mjs +279 -0
  52. package/dist/callbacks-CNjGNxVw.mjs.map +1 -0
  53. package/dist/client-D6C_uczF.mjs +142 -0
  54. package/dist/client-D6C_uczF.mjs.map +1 -0
  55. package/dist/clipboard.d.mts +104 -0
  56. package/dist/clipboard.d.mts.map +1 -0
  57. package/dist/clipboard.mjs +404 -0
  58. package/dist/clipboard.mjs.map +1 -0
  59. package/dist/flex-Bguo-fUe.mjs +202 -0
  60. package/dist/flex-Bguo-fUe.mjs.map +1 -0
  61. package/dist/image-Cm5T-uw-.mjs +108 -0
  62. package/dist/image-Cm5T-uw-.mjs.map +1 -0
  63. package/dist/image.d.mts +123 -0
  64. package/dist/image.d.mts.map +1 -0
  65. package/dist/image.mjs +2 -0
  66. package/dist/index.d.mts +64 -0
  67. package/dist/index.d.mts.map +1 -0
  68. package/dist/index.mjs +222 -0
  69. package/dist/index.mjs.map +1 -0
  70. package/dist/input-B5Vy-iaa.d.mts +141 -0
  71. package/dist/input-B5Vy-iaa.d.mts.map +1 -0
  72. package/dist/inspect-D1d9t-ni.mjs +101 -0
  73. package/dist/inspect-D1d9t-ni.mjs.map +1 -0
  74. package/dist/inspect-D6FW_QUo.d.mts +67 -0
  75. package/dist/inspect-D6FW_QUo.d.mts.map +1 -0
  76. package/dist/kitty-JbCJC0iQ.mjs +673 -0
  77. package/dist/kitty-JbCJC0iQ.mjs.map +1 -0
  78. package/dist/markdown.d.mts +24 -0
  79. package/dist/markdown.d.mts.map +1 -0
  80. package/dist/markdown.mjs +2 -0
  81. package/dist/md.bun.d.mts +14 -0
  82. package/dist/md.bun.d.mts.map +1 -0
  83. package/dist/md.bun.mjs +28 -0
  84. package/dist/md.bun.mjs.map +1 -0
  85. package/dist/md.node.d.mts +14 -0
  86. package/dist/md.node.d.mts.map +1 -0
  87. package/dist/md.node.mjs +126 -0
  88. package/dist/md.node.mjs.map +1 -0
  89. package/dist/node-BZ7f6HzU.mjs +316 -0
  90. package/dist/node-BZ7f6HzU.mjs.map +1 -0
  91. package/dist/picker-D8OLb6kZ.d.mts +36 -0
  92. package/dist/picker-D8OLb6kZ.d.mts.map +1 -0
  93. package/dist/picker-ndVVt6qF.mjs +621 -0
  94. package/dist/picker-ndVVt6qF.mjs.map +1 -0
  95. package/dist/queries-DH3bj74K.d.mts +1963 -0
  96. package/dist/queries-DH3bj74K.d.mts.map +1 -0
  97. package/dist/reactive-f7NpAi69.mjs +623 -0
  98. package/dist/reactive-f7NpAi69.mjs.map +1 -0
  99. package/dist/registry-U654j61Y.mjs +356 -0
  100. package/dist/registry-U654j61Y.mjs.map +1 -0
  101. package/dist/render-DxVLqqB9.mjs +74 -0
  102. package/dist/render-DxVLqqB9.mjs.map +1 -0
  103. package/dist/renderer-CdLCkJB-.mjs +42 -0
  104. package/dist/renderer-CdLCkJB-.mjs.map +1 -0
  105. package/dist/renderer-CxL4xbkl.mjs +2644 -0
  106. package/dist/renderer-CxL4xbkl.mjs.map +1 -0
  107. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  108. package/dist/search-FvGnswRl.d.mts +49 -0
  109. package/dist/search-FvGnswRl.d.mts.map +1 -0
  110. package/dist/select-CbmAZdpt.d.mts +116 -0
  111. package/dist/select-CbmAZdpt.d.mts.map +1 -0
  112. package/dist/services/logger.d.mts +2 -0
  113. package/dist/services/logger.mjs +71 -0
  114. package/dist/services/logger.mjs.map +1 -0
  115. package/dist/services/notifier.d.mts +17 -0
  116. package/dist/services/notifier.d.mts.map +1 -0
  117. package/dist/services/notifier.mjs +71 -0
  118. package/dist/services/notifier.mjs.map +1 -0
  119. package/dist/services/picker.d.mts +36 -0
  120. package/dist/services/picker.d.mts.map +1 -0
  121. package/dist/services/picker.mjs +137 -0
  122. package/dist/services/picker.mjs.map +1 -0
  123. package/dist/services/prompt.d.mts +30 -0
  124. package/dist/services/prompt.d.mts.map +1 -0
  125. package/dist/services/prompt.mjs +83 -0
  126. package/dist/services/prompt.mjs.map +1 -0
  127. package/dist/shiki-BrjVUkeh.mjs +423 -0
  128. package/dist/shiki-BrjVUkeh.mjs.map +1 -0
  129. package/dist/size-D8JogYLy.mjs +34 -0
  130. package/dist/size-D8JogYLy.mjs.map +1 -0
  131. package/dist/style.d.mts +10 -0
  132. package/dist/style.d.mts.map +1 -0
  133. package/dist/style.mjs +4 -0
  134. package/dist/text.d.mts +33 -0
  135. package/dist/text.d.mts.map +1 -0
  136. package/dist/text.mjs +111 -0
  137. package/dist/text.mjs.map +1 -0
  138. package/dist/theme.config-CU3B50HN.mjs +832 -0
  139. package/dist/theme.config-CU3B50HN.mjs.map +1 -0
  140. package/dist/themes.d.mts +113 -0
  141. package/dist/themes.d.mts.map +1 -0
  142. package/dist/themes.mjs +2 -0
  143. package/dist/tree-C28QOVIK.d.mts +23 -0
  144. package/dist/tree-C28QOVIK.d.mts.map +1 -0
  145. package/dist/types-B9Eh7k2v.d.mts +212 -0
  146. package/dist/types-B9Eh7k2v.d.mts.map +1 -0
  147. package/dist/types-D3jk1rtk.d.mts +140 -0
  148. package/dist/types-D3jk1rtk.d.mts.map +1 -0
  149. package/dist/utils-3M4D_AXZ.mjs +40 -0
  150. package/dist/utils-3M4D_AXZ.mjs.map +1 -0
  151. package/dist/widgets/autocomplete.d.mts +2 -0
  152. package/dist/widgets/autocomplete.mjs +165 -0
  153. package/dist/widgets/autocomplete.mjs.map +1 -0
  154. package/dist/widgets/box.d.mts +2 -0
  155. package/dist/widgets/box.mjs +2 -0
  156. package/dist/widgets/code.d.mts +48 -0
  157. package/dist/widgets/code.d.mts.map +1 -0
  158. package/dist/widgets/code.mjs +59 -0
  159. package/dist/widgets/code.mjs.map +1 -0
  160. package/dist/widgets/completions/actions.d.mts +41 -0
  161. package/dist/widgets/completions/actions.d.mts.map +1 -0
  162. package/dist/widgets/completions/actions.mjs +48 -0
  163. package/dist/widgets/completions/actions.mjs.map +1 -0
  164. package/dist/widgets/completions/files.d.mts +29 -0
  165. package/dist/widgets/completions/files.d.mts.map +1 -0
  166. package/dist/widgets/completions/files.mjs +42 -0
  167. package/dist/widgets/completions/files.mjs.map +1 -0
  168. package/dist/widgets/completions/github.d.mts +64 -0
  169. package/dist/widgets/completions/github.d.mts.map +1 -0
  170. package/dist/widgets/completions/github.mjs +137 -0
  171. package/dist/widgets/completions/github.mjs.map +1 -0
  172. package/dist/widgets/completions/index.d.mts +4 -0
  173. package/dist/widgets/completions/index.mjs +4 -0
  174. package/dist/widgets/diff.d.mts +57 -0
  175. package/dist/widgets/diff.d.mts.map +1 -0
  176. package/dist/widgets/diff.mjs +199 -0
  177. package/dist/widgets/diff.mjs.map +1 -0
  178. package/dist/widgets/divider.d.mts +11 -0
  179. package/dist/widgets/divider.d.mts.map +1 -0
  180. package/dist/widgets/divider.mjs +9 -0
  181. package/dist/widgets/divider.mjs.map +1 -0
  182. package/dist/widgets/image.d.mts +37 -0
  183. package/dist/widgets/image.d.mts.map +1 -0
  184. package/dist/widgets/image.mjs +77 -0
  185. package/dist/widgets/image.mjs.map +1 -0
  186. package/dist/widgets/input.d.mts +2 -0
  187. package/dist/widgets/input.mjs +497 -0
  188. package/dist/widgets/input.mjs.map +1 -0
  189. package/dist/widgets/log.d.mts +2 -0
  190. package/dist/widgets/log.mjs +130 -0
  191. package/dist/widgets/log.mjs.map +1 -0
  192. package/dist/widgets/markdown.d.mts +45 -0
  193. package/dist/widgets/markdown.d.mts.map +1 -0
  194. package/dist/widgets/markdown.mjs +124 -0
  195. package/dist/widgets/markdown.mjs.map +1 -0
  196. package/dist/widgets/overlay.d.mts +2 -0
  197. package/dist/widgets/overlay.mjs +18 -0
  198. package/dist/widgets/overlay.mjs.map +1 -0
  199. package/dist/widgets/picker.d.mts +2 -0
  200. package/dist/widgets/picker.mjs +2 -0
  201. package/dist/widgets/progress.d.mts +48 -0
  202. package/dist/widgets/progress.d.mts.map +1 -0
  203. package/dist/widgets/progress.mjs +48 -0
  204. package/dist/widgets/progress.mjs.map +1 -0
  205. package/dist/widgets/select.d.mts +2 -0
  206. package/dist/widgets/select.mjs +195 -0
  207. package/dist/widgets/select.mjs.map +1 -0
  208. package/dist/widgets/show.d.mts +55 -0
  209. package/dist/widgets/show.d.mts.map +1 -0
  210. package/dist/widgets/show.mjs +85 -0
  211. package/dist/widgets/show.mjs.map +1 -0
  212. package/dist/widgets/spinner.d.mts +65 -0
  213. package/dist/widgets/spinner.d.mts.map +1 -0
  214. package/dist/widgets/spinner.mjs +147 -0
  215. package/dist/widgets/spinner.mjs.map +1 -0
  216. package/dist/widgets/text.d.mts +2 -0
  217. package/dist/widgets/text.mjs +32 -0
  218. package/dist/widgets/text.mjs.map +1 -0
  219. package/dist/widgets/tree.d.mts +2 -0
  220. package/dist/widgets/tree.mjs +73 -0
  221. package/dist/widgets/tree.mjs.map +1 -0
  222. package/dist/widgets/widget.d.mts +49 -0
  223. package/dist/widgets/widget.d.mts.map +1 -0
  224. package/dist/widgets/widget.mjs +40 -0
  225. package/dist/widgets/widget.mjs.map +1 -0
  226. package/dist/worker-DjHuQSbY.mjs +27 -0
  227. package/dist/worker-DjHuQSbY.mjs.map +1 -0
  228. 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