@textui/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/adapters/index.d.ts +12 -0
  4. package/dist/adapters/index.d.ts.map +1 -0
  5. package/dist/adapters/index.js +11 -0
  6. package/dist/app/app.d.ts +200 -0
  7. package/dist/app/app.d.ts.map +1 -0
  8. package/dist/app/app.js +980 -0
  9. package/dist/core/animation.d.ts +34 -0
  10. package/dist/core/animation.d.ts.map +1 -0
  11. package/dist/core/animation.js +125 -0
  12. package/dist/core/clipboard.d.ts +29 -0
  13. package/dist/core/clipboard.d.ts.map +1 -0
  14. package/dist/core/clipboard.js +30 -0
  15. package/dist/core/commands.d.ts +42 -0
  16. package/dist/core/commands.d.ts.map +1 -0
  17. package/dist/core/commands.js +146 -0
  18. package/dist/core/components.d.ts +27 -0
  19. package/dist/core/components.d.ts.map +1 -0
  20. package/dist/core/components.js +78 -0
  21. package/dist/core/events.d.ts +23 -0
  22. package/dist/core/events.d.ts.map +1 -0
  23. package/dist/core/events.js +77 -0
  24. package/dist/core/focus.d.ts +69 -0
  25. package/dist/core/focus.d.ts.map +1 -0
  26. package/dist/core/focus.js +336 -0
  27. package/dist/core/i18n.d.ts +27 -0
  28. package/dist/core/i18n.d.ts.map +1 -0
  29. package/dist/core/i18n.js +88 -0
  30. package/dist/core/keybindings.d.ts +53 -0
  31. package/dist/core/keybindings.d.ts.map +1 -0
  32. package/dist/core/keybindings.js +163 -0
  33. package/dist/core/layers.d.ts +28 -0
  34. package/dist/core/layers.d.ts.map +1 -0
  35. package/dist/core/layers.js +84 -0
  36. package/dist/core/manifest.d.ts +22 -0
  37. package/dist/core/manifest.d.ts.map +1 -0
  38. package/dist/core/manifest.js +85 -0
  39. package/dist/core/navigation.d.ts +47 -0
  40. package/dist/core/navigation.d.ts.map +1 -0
  41. package/dist/core/navigation.js +110 -0
  42. package/dist/core/resources.d.ts +115 -0
  43. package/dist/core/resources.d.ts.map +1 -0
  44. package/dist/core/resources.js +321 -0
  45. package/dist/core/services.d.ts +21 -0
  46. package/dist/core/services.d.ts.map +1 -0
  47. package/dist/core/services.js +63 -0
  48. package/dist/core/store.d.ts +60 -0
  49. package/dist/core/store.d.ts.map +1 -0
  50. package/dist/core/store.js +593 -0
  51. package/dist/core/surfaces.d.ts +81 -0
  52. package/dist/core/surfaces.d.ts.map +1 -0
  53. package/dist/core/surfaces.js +237 -0
  54. package/dist/core/syntax.d.ts +49 -0
  55. package/dist/core/syntax.d.ts.map +1 -0
  56. package/dist/core/syntax.js +172 -0
  57. package/dist/core/when.d.ts +15 -0
  58. package/dist/core/when.d.ts.map +1 -0
  59. package/dist/core/when.js +224 -0
  60. package/dist/index.d.ts +49 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +54 -0
  63. package/dist/jsx/factory.d.ts +27 -0
  64. package/dist/jsx/factory.d.ts.map +1 -0
  65. package/dist/jsx/factory.js +102 -0
  66. package/dist/jsx/intrinsics.d.ts +96 -0
  67. package/dist/jsx/intrinsics.d.ts.map +1 -0
  68. package/dist/jsx/intrinsics.js +1 -0
  69. package/dist/jsx/jsx-dev-runtime.d.ts +13 -0
  70. package/dist/jsx/jsx-dev-runtime.d.ts.map +1 -0
  71. package/dist/jsx/jsx-dev-runtime.js +12 -0
  72. package/dist/jsx/jsx-runtime.d.ts +42 -0
  73. package/dist/jsx/jsx-runtime.d.ts.map +1 -0
  74. package/dist/jsx/jsx-runtime.js +16 -0
  75. package/dist/render/buffer.d.ts +77 -0
  76. package/dist/render/buffer.d.ts.map +1 -0
  77. package/dist/render/buffer.js +275 -0
  78. package/dist/render/color.d.ts +35 -0
  79. package/dist/render/color.d.ts.map +1 -0
  80. package/dist/render/color.js +160 -0
  81. package/dist/render/diff.d.ts +37 -0
  82. package/dist/render/diff.d.ts.map +1 -0
  83. package/dist/render/diff.js +66 -0
  84. package/dist/render/layout.d.ts +72 -0
  85. package/dist/render/layout.d.ts.map +1 -0
  86. package/dist/render/layout.js +633 -0
  87. package/dist/render/static.d.ts +63 -0
  88. package/dist/render/static.d.ts.map +1 -0
  89. package/dist/render/static.js +209 -0
  90. package/dist/runtime/bindings.d.ts +46 -0
  91. package/dist/runtime/bindings.d.ts.map +1 -0
  92. package/dist/runtime/bindings.js +116 -0
  93. package/dist/runtime/hooks.d.ts +284 -0
  94. package/dist/runtime/hooks.d.ts.map +1 -0
  95. package/dist/runtime/hooks.js +846 -0
  96. package/dist/runtime/instance.d.ts +106 -0
  97. package/dist/runtime/instance.d.ts.map +1 -0
  98. package/dist/runtime/instance.js +181 -0
  99. package/dist/runtime/paint.d.ts +27 -0
  100. package/dist/runtime/paint.d.ts.map +1 -0
  101. package/dist/runtime/paint.js +567 -0
  102. package/dist/runtime/reconcile.d.ts +23 -0
  103. package/dist/runtime/reconcile.d.ts.map +1 -0
  104. package/dist/runtime/reconcile.js +260 -0
  105. package/dist/runtime/runtime.d.ts +42 -0
  106. package/dist/runtime/runtime.d.ts.map +1 -0
  107. package/dist/runtime/runtime.js +1 -0
  108. package/dist/runtime/style.d.ts +55 -0
  109. package/dist/runtime/style.d.ts.map +1 -0
  110. package/dist/runtime/style.js +143 -0
  111. package/dist/themes/borders.d.ts +10 -0
  112. package/dist/themes/borders.d.ts.map +1 -0
  113. package/dist/themes/borders.js +88 -0
  114. package/dist/themes/builtin.d.ts +31 -0
  115. package/dist/themes/builtin.d.ts.map +1 -0
  116. package/dist/themes/builtin.js +291 -0
  117. package/dist/themes/glyphs.d.ts +15 -0
  118. package/dist/themes/glyphs.d.ts.map +1 -0
  119. package/dist/themes/glyphs.js +97 -0
  120. package/dist/themes/index.d.ts +5 -0
  121. package/dist/themes/index.d.ts.map +1 -0
  122. package/dist/themes/index.js +4 -0
  123. package/dist/themes/registry.d.ts +20 -0
  124. package/dist/themes/registry.d.ts.map +1 -0
  125. package/dist/themes/registry.js +204 -0
  126. package/dist/types/adapter.d.ts +38 -0
  127. package/dist/types/adapter.d.ts.map +1 -0
  128. package/dist/types/adapter.js +1 -0
  129. package/dist/types/animation.d.ts +31 -0
  130. package/dist/types/animation.d.ts.map +1 -0
  131. package/dist/types/animation.js +1 -0
  132. package/dist/types/app.d.ts +148 -0
  133. package/dist/types/app.d.ts.map +1 -0
  134. package/dist/types/app.js +1 -0
  135. package/dist/types/async.d.ts +30 -0
  136. package/dist/types/async.d.ts.map +1 -0
  137. package/dist/types/async.js +1 -0
  138. package/dist/types/capabilities.d.ts +44 -0
  139. package/dist/types/capabilities.d.ts.map +1 -0
  140. package/dist/types/capabilities.js +33 -0
  141. package/dist/types/cells.d.ts +72 -0
  142. package/dist/types/cells.d.ts.map +1 -0
  143. package/dist/types/cells.js +10 -0
  144. package/dist/types/command.d.ts +127 -0
  145. package/dist/types/command.d.ts.map +1 -0
  146. package/dist/types/command.js +1 -0
  147. package/dist/types/component-registry.d.ts +77 -0
  148. package/dist/types/component-registry.d.ts.map +1 -0
  149. package/dist/types/component-registry.js +1 -0
  150. package/dist/types/disposable.d.ts +8 -0
  151. package/dist/types/disposable.d.ts.map +1 -0
  152. package/dist/types/disposable.js +1 -0
  153. package/dist/types/focus.d.ts +72 -0
  154. package/dist/types/focus.d.ts.map +1 -0
  155. package/dist/types/focus.js +1 -0
  156. package/dist/types/geometry.d.ts +28 -0
  157. package/dist/types/geometry.d.ts.map +1 -0
  158. package/dist/types/geometry.js +14 -0
  159. package/dist/types/graph.d.ts +129 -0
  160. package/dist/types/graph.d.ts.map +1 -0
  161. package/dist/types/graph.js +31 -0
  162. package/dist/types/i18n.d.ts +23 -0
  163. package/dist/types/i18n.d.ts.map +1 -0
  164. package/dist/types/i18n.js +1 -0
  165. package/dist/types/index.d.ts +32 -0
  166. package/dist/types/index.d.ts.map +1 -0
  167. package/dist/types/index.js +31 -0
  168. package/dist/types/input.d.ts +65 -0
  169. package/dist/types/input.d.ts.map +1 -0
  170. package/dist/types/input.js +1 -0
  171. package/dist/types/keybinding.d.ts +49 -0
  172. package/dist/types/keybinding.d.ts.map +1 -0
  173. package/dist/types/keybinding.js +1 -0
  174. package/dist/types/layer.d.ts +54 -0
  175. package/dist/types/layer.d.ts.map +1 -0
  176. package/dist/types/layer.js +1 -0
  177. package/dist/types/manifest.d.ts +114 -0
  178. package/dist/types/manifest.d.ts.map +1 -0
  179. package/dist/types/manifest.js +1 -0
  180. package/dist/types/markdown.d.ts +58 -0
  181. package/dist/types/markdown.d.ts.map +1 -0
  182. package/dist/types/markdown.js +1 -0
  183. package/dist/types/navigation.d.ts +40 -0
  184. package/dist/types/navigation.d.ts.map +1 -0
  185. package/dist/types/navigation.js +1 -0
  186. package/dist/types/render.d.ts +72 -0
  187. package/dist/types/render.d.ts.map +1 -0
  188. package/dist/types/render.js +1 -0
  189. package/dist/types/resource.d.ts +170 -0
  190. package/dist/types/resource.d.ts.map +1 -0
  191. package/dist/types/resource.js +1 -0
  192. package/dist/types/services.d.ts +24 -0
  193. package/dist/types/services.d.ts.map +1 -0
  194. package/dist/types/services.js +3 -0
  195. package/dist/types/shell.d.ts +32 -0
  196. package/dist/types/shell.d.ts.map +1 -0
  197. package/dist/types/shell.js +1 -0
  198. package/dist/types/store.d.ts +124 -0
  199. package/dist/types/store.d.ts.map +1 -0
  200. package/dist/types/store.js +1 -0
  201. package/dist/types/stream.d.ts +18 -0
  202. package/dist/types/stream.d.ts.map +1 -0
  203. package/dist/types/stream.js +1 -0
  204. package/dist/types/style.d.ts +166 -0
  205. package/dist/types/style.d.ts.map +1 -0
  206. package/dist/types/style.js +1 -0
  207. package/dist/types/surface.d.ts +97 -0
  208. package/dist/types/surface.d.ts.map +1 -0
  209. package/dist/types/surface.js +1 -0
  210. package/dist/types/syntax.d.ts +68 -0
  211. package/dist/types/syntax.d.ts.map +1 -0
  212. package/dist/types/syntax.js +5 -0
  213. package/dist/types/terminal.d.ts +52 -0
  214. package/dist/types/terminal.d.ts.map +1 -0
  215. package/dist/types/terminal.js +1 -0
  216. package/dist/types/theme.d.ts +117 -0
  217. package/dist/types/theme.d.ts.map +1 -0
  218. package/dist/types/theme.js +1 -0
  219. package/dist/types/when.d.ts +19 -0
  220. package/dist/types/when.d.ts.map +1 -0
  221. package/dist/types/when.js +1 -0
  222. package/dist/ui/primitives.d.ts +56 -0
  223. package/dist/ui/primitives.d.ts.map +1 -0
  224. package/dist/ui/primitives.js +108 -0
  225. package/dist/ui/screen.d.ts +23 -0
  226. package/dist/ui/screen.d.ts.map +1 -0
  227. package/dist/ui/screen.js +18 -0
  228. package/dist/util/disposable.d.ts +6 -0
  229. package/dist/util/disposable.d.ts.map +1 -0
  230. package/dist/util/disposable.js +49 -0
  231. package/dist/util/markdown.d.ts +24 -0
  232. package/dist/util/markdown.d.ts.map +1 -0
  233. package/dist/util/markdown.js +220 -0
  234. package/dist/util/paths.d.ts +47 -0
  235. package/dist/util/paths.d.ts.map +1 -0
  236. package/dist/util/paths.js +134 -0
  237. package/dist/util/stream.d.ts +32 -0
  238. package/dist/util/stream.d.ts.map +1 -0
  239. package/dist/util/stream.js +206 -0
  240. package/dist/util/text.d.ts +65 -0
  241. package/dist/util/text.d.ts.map +1 -0
  242. package/dist/util/text.js +419 -0
  243. package/package.json +70 -0
  244. package/src/adapters/index.ts +11 -0
  245. package/src/app/app.ts +1096 -0
  246. package/src/core/animation.ts +144 -0
  247. package/src/core/clipboard.ts +40 -0
  248. package/src/core/commands.ts +169 -0
  249. package/src/core/components.ts +91 -0
  250. package/src/core/events.ts +95 -0
  251. package/src/core/focus.ts +358 -0
  252. package/src/core/i18n.ts +107 -0
  253. package/src/core/keybindings.ts +184 -0
  254. package/src/core/layers.ts +94 -0
  255. package/src/core/manifest.ts +84 -0
  256. package/src/core/navigation.ts +135 -0
  257. package/src/core/resources.ts +362 -0
  258. package/src/core/services.ts +69 -0
  259. package/src/core/store.ts +640 -0
  260. package/src/core/surfaces.ts +292 -0
  261. package/src/core/syntax.ts +200 -0
  262. package/src/core/when.ts +238 -0
  263. package/src/index.ts +76 -0
  264. package/src/jsx/factory.ts +124 -0
  265. package/src/jsx/intrinsics.ts +99 -0
  266. package/src/jsx/jsx-dev-runtime.ts +23 -0
  267. package/src/jsx/jsx-runtime.ts +76 -0
  268. package/src/render/buffer.ts +318 -0
  269. package/src/render/color.ts +180 -0
  270. package/src/render/diff.ts +99 -0
  271. package/src/render/layout.ts +764 -0
  272. package/src/render/static.ts +290 -0
  273. package/src/runtime/bindings.ts +150 -0
  274. package/src/runtime/hooks.ts +1057 -0
  275. package/src/runtime/instance.ts +284 -0
  276. package/src/runtime/paint.ts +737 -0
  277. package/src/runtime/reconcile.ts +335 -0
  278. package/src/runtime/runtime.ts +47 -0
  279. package/src/runtime/style.ts +205 -0
  280. package/src/themes/borders.ts +95 -0
  281. package/src/themes/builtin.ts +301 -0
  282. package/src/themes/glyphs.ts +101 -0
  283. package/src/themes/index.ts +4 -0
  284. package/src/themes/registry.ts +224 -0
  285. package/src/types/adapter.ts +41 -0
  286. package/src/types/animation.ts +34 -0
  287. package/src/types/app.ts +149 -0
  288. package/src/types/async.ts +35 -0
  289. package/src/types/capabilities.ts +79 -0
  290. package/src/types/cells.ts +79 -0
  291. package/src/types/command.ts +139 -0
  292. package/src/types/component-registry.ts +82 -0
  293. package/src/types/disposable.ts +8 -0
  294. package/src/types/focus.ts +77 -0
  295. package/src/types/geometry.ts +50 -0
  296. package/src/types/graph.ts +174 -0
  297. package/src/types/i18n.ts +25 -0
  298. package/src/types/index.ts +31 -0
  299. package/src/types/input.ts +81 -0
  300. package/src/types/keybinding.ts +51 -0
  301. package/src/types/layer.ts +46 -0
  302. package/src/types/manifest.ts +101 -0
  303. package/src/types/markdown.ts +47 -0
  304. package/src/types/navigation.ts +42 -0
  305. package/src/types/render.ts +86 -0
  306. package/src/types/resource.ts +185 -0
  307. package/src/types/services.ts +28 -0
  308. package/src/types/shell.ts +30 -0
  309. package/src/types/store.ts +148 -0
  310. package/src/types/stream.ts +24 -0
  311. package/src/types/style.ts +205 -0
  312. package/src/types/surface.ts +119 -0
  313. package/src/types/syntax.ts +93 -0
  314. package/src/types/terminal.ts +58 -0
  315. package/src/types/theme.ts +121 -0
  316. package/src/types/when.ts +21 -0
  317. package/src/ui/primitives.ts +118 -0
  318. package/src/ui/screen.ts +41 -0
  319. package/src/util/disposable.ts +49 -0
  320. package/src/util/markdown.ts +225 -0
  321. package/src/util/paths.ts +138 -0
  322. package/src/util/stream.ts +213 -0
  323. package/src/util/text.ts +428 -0
@@ -0,0 +1,633 @@
1
+ import { ZERO_EDGES } from '../types/geometry.js';
2
+ export function resolveEdges(spec) {
3
+ if (spec === undefined)
4
+ return ZERO_EDGES;
5
+ if (typeof spec === 'number') {
6
+ return { top: spec, right: spec, bottom: spec, left: spec };
7
+ }
8
+ if (Array.isArray(spec)) {
9
+ if (spec.length === 2) {
10
+ const [v, h] = spec;
11
+ return { top: v, right: h, bottom: v, left: h };
12
+ }
13
+ if (spec.length === 3) {
14
+ const [t, h, b] = spec;
15
+ return { top: t, right: h, bottom: b, left: h };
16
+ }
17
+ const [t, r, b, l] = spec;
18
+ return { top: t, right: r, bottom: b, left: l };
19
+ }
20
+ return {
21
+ top: spec.top ?? 0,
22
+ right: spec.right ?? 0,
23
+ bottom: spec.bottom ?? 0,
24
+ left: spec.left ?? 0,
25
+ };
26
+ }
27
+ function edgeH(e) {
28
+ return e.left + e.right;
29
+ }
30
+ function edgeV(e) {
31
+ return e.top + e.bottom;
32
+ }
33
+ /** Resolve a dimension against an available extent. `auto` returns null. */
34
+ function resolveDimension(dim, available) {
35
+ if (dim === undefined || dim === 'auto')
36
+ return null;
37
+ if (typeof dim === 'number')
38
+ return Math.max(0, Math.round(dim));
39
+ const pct = Number.parseFloat(dim);
40
+ if (Number.isNaN(pct))
41
+ return null;
42
+ return Math.max(0, Math.round((available * pct) / 100));
43
+ }
44
+ function clamp(v, min, max) {
45
+ let out = v;
46
+ if (min !== undefined)
47
+ out = Math.max(out, min);
48
+ if (max !== undefined)
49
+ out = Math.min(out, max);
50
+ return Math.max(0, out);
51
+ }
52
+ function isColumn(box) {
53
+ return (box.style.direction ?? 'column') === 'column';
54
+ }
55
+ function isAbsolute(box) {
56
+ return box.style.position === 'absolute';
57
+ }
58
+ function isHidden(box) {
59
+ return box.style.display === 'none';
60
+ }
61
+ function isWrapping(box) {
62
+ return box.style.flexWrap === 'wrap';
63
+ }
64
+ /**
65
+ * `overflow` for one axis. The specific prop wins over the shorthand, so
66
+ * `overflow: 'hidden', overflowY: 'scroll'` is a pane that scrolls down and
67
+ * clips sideways - which is what a log viewer is.
68
+ */
69
+ export function overflowOn(style, axis) {
70
+ return (axis === 'x' ? style.overflowX : style.overflowY) ?? style.overflow ?? 'visible';
71
+ }
72
+ /** The overflow governing this container's own main axis. */
73
+ function mainOverflow(box) {
74
+ return overflowOn(box.style, isColumn(box) ? 'y' : 'x');
75
+ }
76
+ /**
77
+ * Gaps, resolved to this container's axes.
78
+ *
79
+ * `columnGap` and `rowGap` are named for the screen, not for the container:
80
+ * `columnGap` is always the horizontal one. So on a row it is the gap between
81
+ * children and on a column it is the gap between wrapped lines, and a style
82
+ * that sets both reads the same either way round.
83
+ */
84
+ function gapsOf(box) {
85
+ const vertical = box.style.rowGap ?? box.style.gap ?? 0;
86
+ const horizontal = box.style.columnGap ?? box.style.gap ?? 0;
87
+ return isColumn(box)
88
+ ? { main: vertical, cross: horizontal }
89
+ : { main: horizontal, cross: vertical };
90
+ }
91
+ /**
92
+ * Cut a run of main-axis sizes into lines that each fit `limit`.
93
+ *
94
+ * A child bigger than the whole line still gets a line to itself rather than
95
+ * an empty one pushed in front of it: it is going to overflow either way, and
96
+ * a blank row above it helps nobody.
97
+ */
98
+ function splitLines(mains, gap, limit) {
99
+ const lines = [];
100
+ let start = 0;
101
+ let used = 0;
102
+ for (let i = 0; i < mains.length; i++) {
103
+ const size = mains[i];
104
+ const next = used === 0 ? size : used + gap + size;
105
+ if (used > 0 && next > limit) {
106
+ lines.push([start, i]);
107
+ start = i;
108
+ used = size;
109
+ continue;
110
+ }
111
+ used = next;
112
+ }
113
+ lines.push([start, mains.length]);
114
+ return lines;
115
+ }
116
+ /** Space this box consumes outside its content: margin, border, padding. */
117
+ function frameOf(box) {
118
+ const margin = resolveEdges(box.style.margin);
119
+ const padding = resolveEdges(box.style.padding);
120
+ const b = box.borderEdges;
121
+ return {
122
+ margin,
123
+ inset: {
124
+ top: b.top + padding.top,
125
+ right: b.right + padding.right,
126
+ bottom: b.bottom + padding.bottom,
127
+ left: b.left + padding.left,
128
+ },
129
+ };
130
+ }
131
+ /**
132
+ * Intrinsic size, ignoring grow. Containers ask their children; leaves are
133
+ * asked directly. `available` bounds text wrapping, so a paragraph measures
134
+ * against the width it will actually get.
135
+ */
136
+ export function measureBox(box, availW, availH) {
137
+ if (isHidden(box))
138
+ return { width: 0, height: 0 };
139
+ const seen = box.measured;
140
+ if (seen !== undefined && seen.w === availW && seen.h === availH)
141
+ return seen.size;
142
+ const { margin, inset } = frameOf(box);
143
+ const fixedW = resolveDimension(box.style.width, availW);
144
+ const fixedH = resolveDimension(box.style.height, availH);
145
+ // What the children get to measure against.
146
+ //
147
+ // A stated width is a promise to the children, not just to the parent: a
148
+ // paragraph inside `width: 7` wraps at seven cells, and measuring it against
149
+ // whatever room the *parent* had leaves it one row tall with half of it
150
+ // unpainted. `maxWidth` binds the same way - it is the width the content
151
+ // will end up with, so it is the width the content should be measured at.
152
+ const boundW = Math.min(fixedW ?? availW - edgeH(margin), box.style.maxWidth ?? Infinity);
153
+ const boundH = Math.min(fixedH ?? availH - edgeV(margin), box.style.maxHeight ?? Infinity);
154
+ const innerAvailW = Math.max(0, boundW - edgeH(inset));
155
+ const innerAvailH = Math.max(0, boundH - edgeV(inset));
156
+ let contentW = 0;
157
+ let contentH = 0;
158
+ if (fixedW !== null && fixedH !== null) {
159
+ contentW = Math.max(0, fixedW - edgeH(inset));
160
+ contentH = Math.max(0, fixedH - edgeV(inset));
161
+ }
162
+ else if (box.measure) {
163
+ const m = box.measure(innerAvailW, innerAvailH);
164
+ contentW = fixedW !== null ? Math.max(0, fixedW - edgeH(inset)) : m.width;
165
+ contentH = fixedH !== null ? Math.max(0, fixedH - edgeV(inset)) : m.height;
166
+ }
167
+ else {
168
+ const gaps = gapsOf(box);
169
+ const column = isColumn(box);
170
+ // Skipped in the loop rather than filtered into an array first: measuring
171
+ // is the hot recursive walk of the whole tree, and this is one throwaway
172
+ // array per box per measure. `count` is what `flow.length` used to say.
173
+ let count = 0;
174
+ const mains = [];
175
+ const crosses = [];
176
+ for (const child of box.children) {
177
+ if (isAbsolute(child) || isHidden(child))
178
+ continue;
179
+ count++;
180
+ const m = measureBox(child, innerAvailW, innerAvailH);
181
+ const cm = resolveEdges(child.style.margin);
182
+ mains.push(column ? m.height + edgeV(cm) : m.width + edgeH(cm));
183
+ crosses.push(column ? m.width + edgeH(cm) : m.height + edgeV(cm));
184
+ }
185
+ // What a flexible child in a row will *actually* be given.
186
+ //
187
+ // Every child above was measured against the full inner width, which is
188
+ // only the width it gets when it is the only one there. In a row beside a
189
+ // rigid sibling it will be narrower - and a child whose height depends on
190
+ // its width, which is any wrapping text, then reports a height for a width
191
+ // it never has. `Alert` is `row(icon, column(flex: 1)(text wrap: 'word'))`,
192
+ // so a message one cell too long to fit measured as one row, was laid out
193
+ // two rows tall, and lost its last word off the bottom.
194
+ //
195
+ // `room` is what the flexible children share: the width left after the
196
+ // rigid ones and the gaps. That is their final size whichever way the
197
+ // distribution went - if there was space over they grow into all of it, and
198
+ // if there was not they are the ones that give way first (see `layoutLine`)
199
+ // - so it is the width to measure their height at.
200
+ //
201
+ // Rows only. The mirror case in a column would be a child whose *width*
202
+ // depends on its height, and nothing measures that way round.
203
+ if (!column && count > 1) {
204
+ let totalFlex = 0;
205
+ let rigidMain = 0;
206
+ let index = 0;
207
+ for (const child of box.children) {
208
+ if (isAbsolute(child) || isHidden(child))
209
+ continue;
210
+ const flex = Math.max(0, child.style.flex ?? 0);
211
+ if (flex > 0)
212
+ totalFlex += flex;
213
+ else
214
+ rigidMain += mains[index];
215
+ index++;
216
+ }
217
+ if (totalFlex > 0) {
218
+ const room = Math.max(0, innerAvailW - rigidMain - gaps.main * (count - 1));
219
+ index = 0;
220
+ for (const child of box.children) {
221
+ if (isAbsolute(child) || isHidden(child))
222
+ continue;
223
+ const flex = Math.max(0, child.style.flex ?? 0);
224
+ if (flex > 0) {
225
+ const cm = resolveEdges(child.style.margin);
226
+ const share = Math.max(0, Math.floor((room * flex) / totalFlex) - edgeH(cm));
227
+ const m = measureBox(child, share, innerAvailH);
228
+ crosses[index] = m.height + edgeV(cm);
229
+ }
230
+ index++;
231
+ }
232
+ }
233
+ }
234
+ let main = 0;
235
+ let cross = 0;
236
+ if (isWrapping(box) && count > 1) {
237
+ // Wrapping trades one axis for the other: the main extent is whatever
238
+ // the widest line came to - never more than the room it was given - and
239
+ // the cross extent grows by a line at a time. Measuring it any other way
240
+ // makes the parent size this box as though it were still one long line,
241
+ // and then it wraps inside a box too short to hold what it wrapped into.
242
+ const limit = column ? innerAvailH : innerAvailW;
243
+ const lines = splitLines(mains, gaps.main, limit);
244
+ for (const [from, to] of lines) {
245
+ let lineMain = 0;
246
+ let lineCross = 0;
247
+ for (let i = from; i < to; i++) {
248
+ lineMain += mains[i] + (i > from ? gaps.main : 0);
249
+ lineCross = Math.max(lineCross, crosses[i]);
250
+ }
251
+ main = Math.max(main, lineMain);
252
+ cross += lineCross;
253
+ }
254
+ if (lines.length > 1)
255
+ cross += gaps.cross * (lines.length - 1);
256
+ }
257
+ else {
258
+ for (let i = 0; i < mains.length; i++) {
259
+ main += mains[i];
260
+ cross = Math.max(cross, crosses[i]);
261
+ }
262
+ if (mains.length > 1)
263
+ main += gaps.main * (mains.length - 1);
264
+ }
265
+ contentW = column ? cross : main;
266
+ contentH = column ? main : cross;
267
+ // A scroll container that will be grown into place is a viewport, not a
268
+ // bag: what is inside it scrolls, so letting that content set its
269
+ // intrinsic size makes every sibling move when the document changes -
270
+ // which is what a file viewer does to a pane it shares. Grow decides its
271
+ // size; `minHeight`/`minWidth` still set the floor, via `clamp` below.
272
+ if (mainOverflow(box) === 'scroll' && (box.style.flex ?? 0) > 0) {
273
+ if (column)
274
+ contentH = 0;
275
+ else
276
+ contentW = 0;
277
+ }
278
+ if (fixedW !== null)
279
+ contentW = Math.max(0, fixedW - edgeH(inset));
280
+ if (fixedH !== null)
281
+ contentH = Math.max(0, fixedH - edgeV(inset));
282
+ }
283
+ const width = clamp(contentW + edgeH(inset), box.style.minWidth, box.style.maxWidth);
284
+ const height = clamp(contentH + edgeV(inset), box.style.minHeight, box.style.maxHeight);
285
+ const size = { width, height };
286
+ box.measured = { w: availW, h: availH, size };
287
+ return size;
288
+ }
289
+ /**
290
+ * Distribute `total` cells across weights, largest remainder first, so the
291
+ * parts sum to exactly `total` with no cell lost to rounding.
292
+ */
293
+ function distribute(total, weights) {
294
+ const sum = weights.reduce((a, b) => a + b, 0);
295
+ if (sum <= 0 || total <= 0)
296
+ return weights.map(() => 0);
297
+ const exact = weights.map((w) => (total * w) / sum);
298
+ const floors = exact.map(Math.floor);
299
+ let used = floors.reduce((a, b) => a + b, 0);
300
+ const order = exact
301
+ .map((v, i) => ({ i, frac: v - Math.floor(v) }))
302
+ .sort((a, b) => b.frac - a.frac);
303
+ let k = 0;
304
+ while (used < total && order.length > 0) {
305
+ const entry = order[k % order.length];
306
+ floors[entry.i] = floors[entry.i] + 1;
307
+ used++;
308
+ k++;
309
+ }
310
+ return floors;
311
+ }
312
+ /**
313
+ * Distribute a shrink deficit across weights.
314
+ *
315
+ * Deliberately not `distribute`: largest-remainder gives the leftover cells to
316
+ * the largest *fractions*, which are the small children, so a 200-row pane
317
+ * next to a one-row status bar rounds the status bar out of existence. Here
318
+ * the leftovers go to the largest weights instead - the pane that is too big
319
+ * pays for the rounding, which is the only answer that keeps chrome on screen.
320
+ */
321
+ function distributeCuts(total, weights) {
322
+ const sum = weights.reduce((a, b) => a + b, 0);
323
+ if (sum <= 0 || total <= 0)
324
+ return weights.map(() => 0);
325
+ const cuts = weights.map((w) => Math.floor((total * w) / sum));
326
+ const used = cuts.reduce((a, b) => a + b, 0);
327
+ // Every leftover cell goes to the heaviest child, not one each: spreading
328
+ // them is what costs a one-cell row its only cell.
329
+ let heaviest = 0;
330
+ for (let i = 1; i < weights.length; i++) {
331
+ if (weights[i] > weights[heaviest])
332
+ heaviest = i;
333
+ }
334
+ cuts[heaviest] = cuts[heaviest] + (total - used);
335
+ return cuts;
336
+ }
337
+ function alignOffset(align, free) {
338
+ if (free <= 0)
339
+ return 0;
340
+ switch (align) {
341
+ case 'center': return Math.floor(free / 2);
342
+ case 'end': return free;
343
+ default: return 0;
344
+ }
345
+ }
346
+ function justifySpacing(justify, free, count) {
347
+ if (free <= 0 || count === 0)
348
+ return { leading: 0, between: 0 };
349
+ switch (justify) {
350
+ case 'center': return { leading: Math.floor(free / 2), between: 0 };
351
+ case 'end': return { leading: free, between: 0 };
352
+ case 'between':
353
+ return count > 1
354
+ ? { leading: 0, between: Math.floor(free / (count - 1)) }
355
+ : { leading: 0, between: 0 };
356
+ case 'around': {
357
+ const each = Math.floor(free / count);
358
+ return { leading: Math.floor(each / 2), between: each };
359
+ }
360
+ case 'evenly': {
361
+ const each = Math.floor(free / (count + 1));
362
+ return { leading: each, between: each };
363
+ }
364
+ default:
365
+ return { leading: 0, between: 0 };
366
+ }
367
+ }
368
+ /** Lay a tree out into `viewport`. Mutates `rect` and `content` in place. */
369
+ export function layout(root, viewport) {
370
+ layoutBox(root, viewport);
371
+ }
372
+ function layoutBox(box, rect) {
373
+ const { inset } = frameOf(box);
374
+ box.rect = {
375
+ x: rect.x,
376
+ y: rect.y,
377
+ width: Math.max(0, rect.width),
378
+ height: Math.max(0, rect.height),
379
+ };
380
+ box.content = {
381
+ x: box.rect.x + inset.left,
382
+ y: box.rect.y + inset.top,
383
+ width: Math.max(0, box.rect.width - edgeH(inset)),
384
+ height: Math.max(0, box.rect.height - edgeV(inset)),
385
+ };
386
+ if (isHidden(box)) {
387
+ box.rect = { ...box.rect, width: 0, height: 0 };
388
+ return;
389
+ }
390
+ const flow = box.children.filter((c) => !isAbsolute(c) && !isHidden(c));
391
+ const absolute = box.children.filter((c) => isAbsolute(c) && !isHidden(c));
392
+ if (flow.length > 0)
393
+ layoutFlow(box, flow);
394
+ for (const child of absolute)
395
+ layoutAbsolute(box, child);
396
+ }
397
+ function layoutFlow(box, flow) {
398
+ if (!isWrapping(box) || flow.length < 2) {
399
+ const column = isColumn(box);
400
+ const mainAvail = column ? box.content.height : box.content.width;
401
+ const scrolling = mainOverflow(box) === 'scroll';
402
+ const extent = layoutLine(box, flow, box.content, scrolling ? (column ? box.scrollTop ?? 0 : box.scrollLeft ?? 0) : 0);
403
+ box.scrollSize = extent > mainAvail
404
+ ? (column
405
+ ? { width: box.content.width, height: extent }
406
+ : { width: extent, height: box.content.height })
407
+ : undefined;
408
+ return;
409
+ }
410
+ layoutWrapped(box, flow);
411
+ }
412
+ /**
413
+ * Wrapping: cut the children into lines, then lay each line out on its own.
414
+ *
415
+ * Each line is a flow in its own right - it grows, shrinks, justifies and
416
+ * aligns exactly as an unwrapped container does - which is why this is a loop
417
+ * around `layoutLine` rather than a second layout algorithm. The lines
418
+ * themselves stack along the cross axis at their natural heights.
419
+ *
420
+ * A wrapping container overflows across, not along: the main axis is the one
421
+ * it just fitted everything into. So its scroll offset and its recorded
422
+ * `scrollSize` are both the cross axis here, which is what a wrapping row
423
+ * inside a ScrollView needs to scroll down through its lines.
424
+ */
425
+ function layoutWrapped(box, flow) {
426
+ const column = isColumn(box);
427
+ const content = box.content;
428
+ const gaps = gapsOf(box);
429
+ const mainAvail = column ? content.height : content.width;
430
+ const crossAvail = column ? content.width : content.height;
431
+ const mains = [];
432
+ const crosses = [];
433
+ for (const child of flow) {
434
+ const margin = resolveEdges(child.style.margin);
435
+ const basis = child.style.basis ?? (column ? child.style.height : child.style.width);
436
+ const fixed = resolveDimension(basis, mainAvail);
437
+ const m = measureBox(child, column ? crossAvail : mainAvail, column ? mainAvail : crossAvail);
438
+ mains.push((fixed ?? (column ? m.height : m.width)) + (column ? edgeV(margin) : edgeH(margin)));
439
+ crosses.push((column ? m.width + edgeH(margin) : m.height + edgeV(margin)));
440
+ }
441
+ const lines = splitLines(mains, gaps.main, mainAvail);
442
+ const scrolling = overflowOn(box.style, column ? 'x' : 'y') === 'scroll';
443
+ const offset = scrolling ? (column ? box.scrollLeft ?? 0 : box.scrollTop ?? 0) : 0;
444
+ const origin = column ? content.x : content.y;
445
+ let cursor = origin - offset;
446
+ for (const [from, to] of lines) {
447
+ let lineCross = 0;
448
+ for (let i = from; i < to; i++)
449
+ lineCross = Math.max(lineCross, crosses[i]);
450
+ layoutLine(box, flow.slice(from, to), column
451
+ ? { x: cursor, y: content.y, width: lineCross, height: content.height }
452
+ : { x: content.x, y: cursor, width: content.width, height: lineCross }, 0);
453
+ cursor += lineCross + gaps.cross;
454
+ }
455
+ const extent = cursor + offset - origin - gaps.cross;
456
+ box.scrollSize = extent > crossAvail
457
+ ? (column
458
+ ? { width: extent, height: content.height }
459
+ : { width: content.width, height: extent })
460
+ : undefined;
461
+ }
462
+ /**
463
+ * One line of a flow, laid out into `region`.
464
+ *
465
+ * Returns the main-axis extent the children actually came to, which is what
466
+ * tells the caller whether there is anything to scroll.
467
+ */
468
+ function layoutLine(box, flow, content, scrollOffset) {
469
+ const column = isColumn(box);
470
+ const gap = gapsOf(box).main;
471
+ const mainAvail = column ? content.height : content.width;
472
+ const crossAvail = column ? content.width : content.height;
473
+ const gapTotal = flow.length > 1 ? gap * (flow.length - 1) : 0;
474
+ // 1. base sizes
475
+ const margins = flow.map((c) => resolveEdges(c.style.margin));
476
+ const bases = [];
477
+ for (let i = 0; i < flow.length; i++) {
478
+ const child = flow[i];
479
+ const m = margins[i];
480
+ const marginMain = column ? edgeV(m) : edgeH(m);
481
+ const basis = child.style.basis ?? (column ? child.style.height : child.style.width);
482
+ const fixed = resolveDimension(basis, mainAvail);
483
+ if (fixed !== null) {
484
+ bases.push(fixed + marginMain);
485
+ continue;
486
+ }
487
+ const measured = measureBox(child, column ? crossAvail : Math.max(0, mainAvail - gapTotal), column ? Math.max(0, mainAvail - gapTotal) : crossAvail);
488
+ bases.push((column ? measured.height : measured.width) + marginMain);
489
+ }
490
+ // 2. grow or shrink into the space that is actually there
491
+ //
492
+ // A scroll container is the exception: shrinking its children to fit is
493
+ // exactly what it must not do, because then there is nothing to scroll.
494
+ const scrolling = mainOverflow(box) === 'scroll';
495
+ const used = bases.reduce((a, b) => a + b, 0) + gapTotal;
496
+ let free = scrolling ? Math.max(0, mainAvail - used) : mainAvail - used;
497
+ const sizes = bases.slice();
498
+ if (free > 0) {
499
+ const grows = flow.map((c) => Math.max(0, c.style.flex ?? 0));
500
+ if (grows.some((g) => g > 0)) {
501
+ const extra = distribute(free, grows);
502
+ for (let i = 0; i < sizes.length; i++) {
503
+ sizes[i] = sizes[i] + extra[i];
504
+ }
505
+ free = 0;
506
+ }
507
+ }
508
+ else if (free < 0) {
509
+ // Shrink proportionally to size * shrink factor, never below minWidth.
510
+ const deficit = -free;
511
+ //
512
+ // Who gives way, and how a terminal should degrade when it cannot fit
513
+ // everything:
514
+ //
515
+ // A child that asked to grow gives way first. `flex` means elastic, and
516
+ // treating it as rigid is what makes a long document push a header and a
517
+ // status bar out of the frame - they are the fixed-size rows, so they are
518
+ // the ones the deficit would land on.
519
+ //
520
+ // After that the axis decides. Sideways, a rigid child shrinks and its
521
+ // text truncates, which is how terminals have always narrowed. Downwards,
522
+ // it does not: a panel with half a border and no bottom edge is unreadable
523
+ // in a way that a panel below the fold is not, so the overflow is clipped
524
+ // instead (see the clamp below). Either way an explicit `shrink` wins.
525
+ const rigidShrink = column ? 0 : 1;
526
+ const weights = flow.map((c, i) => {
527
+ const shrink = c.style.shrink
528
+ ?? (c.style.flex !== undefined ? Math.max(1, c.style.flex) : rigidShrink);
529
+ return Math.max(0, shrink) * bases[i];
530
+ });
531
+ if (weights.some((w) => w > 0)) {
532
+ const cuts = distributeCuts(deficit, weights);
533
+ for (let i = 0; i < sizes.length; i++) {
534
+ const child = flow[i];
535
+ const min = column ? child.style.minHeight : child.style.minWidth;
536
+ const next = sizes[i] - cuts[i];
537
+ sizes[i] = Math.max(min ?? 0, next);
538
+ }
539
+ }
540
+ free = mainAvail - (sizes.reduce((a, b) => a + b, 0) + gapTotal);
541
+ }
542
+ // Whatever is left over after growing and shrinking, nothing may be placed
543
+ // outside the container. A terminal clips at the edge, so a child that keeps
544
+ // its oversized rect is not "overflowing" in any useful sense - it is a
545
+ // child that measures itself wrong, and a component that sizes itself from
546
+ // that measurement never converges. Cells run out in painting order: earlier
547
+ // children keep their size, later ones get what remains.
548
+ if (!scrolling) {
549
+ let room = mainAvail;
550
+ for (let i = 0; i < sizes.length; i++) {
551
+ if (i > 0)
552
+ room -= gap;
553
+ const size = sizes[i];
554
+ if (size > room)
555
+ sizes[i] = Math.max(0, room);
556
+ room -= sizes[i];
557
+ }
558
+ free = Math.max(0, room);
559
+ }
560
+ // 3. place
561
+ const spacing = justifySpacing(box.style.justify ?? 'start', Math.max(0, free), flow.length);
562
+ const origin = (column ? content.y : content.x) + spacing.leading;
563
+ let cursor = origin - scrollOffset;
564
+ for (let i = 0; i < flow.length; i++) {
565
+ const child = flow[i];
566
+ const m = margins[i];
567
+ const mainSize = Math.max(0, sizes[i] - (column ? edgeV(m) : edgeH(m)));
568
+ const align = child.style.alignSelf ?? box.style.align ?? 'stretch';
569
+ const crossMargin = column ? edgeH(m) : edgeV(m);
570
+ const crossRoom = Math.max(0, crossAvail - crossMargin);
571
+ let crossSize;
572
+ const crossDim = column ? child.style.width : child.style.height;
573
+ const fixedCross = resolveDimension(crossDim, crossAvail);
574
+ if (fixedCross !== null) {
575
+ crossSize = fixedCross;
576
+ }
577
+ else if (align === 'stretch') {
578
+ crossSize = crossRoom;
579
+ }
580
+ else {
581
+ const measured = measureBox(child, column ? crossRoom : mainSize, column ? mainSize : crossRoom);
582
+ crossSize = column ? measured.width : measured.height;
583
+ }
584
+ crossSize = clamp(crossSize, column ? child.style.minWidth : child.style.minHeight, column ? child.style.maxWidth : child.style.maxHeight);
585
+ crossSize = Math.min(crossSize, crossRoom);
586
+ const crossStart = (column ? content.x + m.left : content.y + m.top) +
587
+ alignOffset(align === 'stretch' ? 'start' : align, crossRoom - crossSize);
588
+ const mainStart = cursor + (column ? m.top : m.left);
589
+ layoutBox(child, column
590
+ ? { x: crossStart, y: mainStart, width: crossSize, height: mainSize }
591
+ : { x: mainStart, y: crossStart, width: mainSize, height: crossSize });
592
+ cursor += sizes[i] + gap + (i < flow.length - 1 ? spacing.between : 0);
593
+ }
594
+ // 4. hand back the extent, so the caller knows how far it can scroll
595
+ return cursor + scrollOffset - origin - gap;
596
+ }
597
+ /**
598
+ * Absolute children position against the parent's content box. Opposite
599
+ * offsets both set means "stretch between them", which is how a full-bleed
600
+ * scrim or a bottom-anchored bar is written.
601
+ */
602
+ function layoutAbsolute(parent, child) {
603
+ const box = parent.content;
604
+ const s = child.style;
605
+ const measured = measureBox(child, box.width, box.height);
606
+ let width = resolveDimension(s.width, box.width) ?? measured.width;
607
+ let height = resolveDimension(s.height, box.height) ?? measured.height;
608
+ let x;
609
+ if (s.left !== undefined && s.right !== undefined) {
610
+ x = box.x + s.left;
611
+ width = Math.max(0, box.width - s.left - s.right);
612
+ }
613
+ else if (s.right !== undefined) {
614
+ x = box.x + box.width - s.right - width;
615
+ }
616
+ else {
617
+ x = box.x + (s.left ?? 0);
618
+ }
619
+ let y;
620
+ if (s.top !== undefined && s.bottom !== undefined) {
621
+ y = box.y + s.top;
622
+ height = Math.max(0, box.height - s.top - s.bottom);
623
+ }
624
+ else if (s.bottom !== undefined) {
625
+ y = box.y + box.height - s.bottom - height;
626
+ }
627
+ else {
628
+ y = box.y + (s.top ?? 0);
629
+ }
630
+ width = clamp(width, s.minWidth, s.maxWidth);
631
+ height = clamp(height, s.minHeight, s.maxHeight);
632
+ layoutBox(child, { x, y, width, height });
633
+ }
@@ -0,0 +1,63 @@
1
+ import type { ComponentNode } from '../types/graph.js';
2
+ import type { ComponentDefinition } from '../types/component-registry.js';
3
+ import type { ThemeDefinition } from '../types/theme.js';
4
+ import type { CapabilityOverrides } from '../types/capabilities.js';
5
+ import type { Instance } from '../runtime/instance.js';
6
+ import type { Runtime } from '../runtime/runtime.js';
7
+ import { Buffer } from './buffer.js';
8
+ /**
9
+ * One-shot rendering.
10
+ *
11
+ * The same component model, no terminal and no frame loop - which is what
12
+ * makes it useful for reports, `--help` output, generated documentation and
13
+ * tests. A component that only works interactively is a component that cannot
14
+ * be tested cheaply, so this path is not an afterthought.
15
+ *
16
+ * `renderOnce`, not `render`, because the difference that matters is lifetime.
17
+ * This draws a frame and hands it back; `render` mounts an application onto a
18
+ * terminal and keeps running. Two functions called `render` where one returns
19
+ * a string and the other takes over your screen is a trap, not a convenience -
20
+ * and every other library's `render` is the second one.
21
+ */
22
+ export interface StaticRenderOptions {
23
+ width?: number;
24
+ height?: number | 'auto';
25
+ theme?: string;
26
+ themes?: ThemeDefinition[];
27
+ /**
28
+ * Components to register by name.
29
+ *
30
+ * Only nodes addressed by name need this. A screen written in JSX carries
31
+ * its components with it - `<Card/>` compiles to a node holding the
32
+ * imported function, and the runtime uses that in preference to any
33
+ * registry - so this is for screens authored as data: JSON, a template
34
+ * renderer, anything naming a component in a string.
35
+ *
36
+ * Pass `CATALOG` for the shipped set.
37
+ */
38
+ components?: ComponentDefinition[];
39
+ capabilities?: CapabilityOverrides;
40
+ locale?: string;
41
+ /** Seed the store before rendering. */
42
+ initialState?: Record<string, unknown>;
43
+ /** Passes allowed for effects that change state. */
44
+ maxPasses?: number;
45
+ onError?(err: unknown, context: string): void;
46
+ }
47
+ export interface StaticRenderResult {
48
+ buffer: Buffer;
49
+ text: string;
50
+ root: Instance;
51
+ runtime: Runtime;
52
+ /** Release subscriptions and timers. Always call this. */
53
+ dispose(): void;
54
+ }
55
+ export declare function createStaticRuntime(options?: StaticRenderOptions): {
56
+ runtime: Runtime;
57
+ setSize(width: number, height: number): void;
58
+ dispose(): void;
59
+ };
60
+ export declare function renderOnce(node: ComponentNode, options?: StaticRenderOptions): StaticRenderResult;
61
+ /** Render to plain text. The shortest path from a component to a string. */
62
+ export declare function renderToString(node: ComponentNode, options?: StaticRenderOptions): string;
63
+ //# sourceMappingURL=static.d.ts.map