zard-cli 1.0.0-beta.99 → 1.0.1

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 (258) hide show
  1. package/README.md +98 -36
  2. package/commands/add/component-installer.d.ts +3 -0
  3. package/commands/add/component-installer.d.ts.map +1 -1
  4. package/commands/add/component-installer.js +31 -8
  5. package/commands/add/component-installer.js.map +1 -1
  6. package/commands/add/component-selector.d.ts +6 -0
  7. package/commands/add/component-selector.d.ts.map +1 -1
  8. package/commands/add/component-selector.js +9 -27
  9. package/commands/add/component-selector.js.map +1 -1
  10. package/commands/add/dark-mode-setup.d.ts +2 -0
  11. package/commands/add/dark-mode-setup.d.ts.map +1 -1
  12. package/commands/add/dark-mode-setup.js +16 -11
  13. package/commands/add/dark-mode-setup.js.map +1 -1
  14. package/commands/add/dependency-resolver.d.ts +8 -1
  15. package/commands/add/dependency-resolver.d.ts.map +1 -1
  16. package/commands/add/dependency-resolver.js +87 -16
  17. package/commands/add/dependency-resolver.js.map +1 -1
  18. package/commands/add/index.d.ts.map +1 -1
  19. package/commands/add/index.js +142 -96
  20. package/commands/add/index.js.map +1 -1
  21. package/commands/add/stylesheet-setup.d.ts +3 -0
  22. package/commands/add/stylesheet-setup.d.ts.map +1 -0
  23. package/commands/add/stylesheet-setup.js +92 -0
  24. package/commands/add/stylesheet-setup.js.map +1 -0
  25. package/commands/add/wizard.d.ts +35 -0
  26. package/commands/add/wizard.d.ts.map +1 -0
  27. package/commands/add/wizard.js +299 -0
  28. package/commands/add/wizard.js.map +1 -0
  29. package/commands/init/config-prompter.d.ts +65 -3
  30. package/commands/init/config-prompter.d.ts.map +1 -1
  31. package/commands/init/config-prompter.js +93 -74
  32. package/commands/init/config-prompter.js.map +1 -1
  33. package/commands/init/dependencies.d.ts +1 -8
  34. package/commands/init/dependencies.d.ts.map +1 -1
  35. package/commands/init/dependencies.js +54 -24
  36. package/commands/init/dependencies.js.map +1 -1
  37. package/commands/init/index.d.ts.map +1 -1
  38. package/commands/init/index.js +175 -81
  39. package/commands/init/index.js.map +1 -1
  40. package/commands/init/library-peers.d.ts +28 -0
  41. package/commands/init/library-peers.d.ts.map +1 -0
  42. package/commands/init/library-peers.js +61 -0
  43. package/commands/init/library-peers.js.map +1 -0
  44. package/commands/init/project-kind.d.ts +82 -0
  45. package/commands/init/project-kind.d.ts.map +1 -0
  46. package/commands/init/project-kind.js +166 -0
  47. package/commands/init/project-kind.js.map +1 -0
  48. package/commands/init/steps.d.ts +15 -0
  49. package/commands/init/steps.d.ts.map +1 -0
  50. package/commands/init/steps.js +188 -0
  51. package/commands/init/steps.js.map +1 -0
  52. package/commands/init/tailwind-setup.d.ts +9 -2
  53. package/commands/init/tailwind-setup.d.ts.map +1 -1
  54. package/commands/init/tailwind-setup.js +47 -12
  55. package/commands/init/tailwind-setup.js.map +1 -1
  56. package/commands/init/tsconfig-updater.d.ts +8 -1
  57. package/commands/init/tsconfig-updater.d.ts.map +1 -1
  58. package/commands/init/tsconfig-updater.js +30 -11
  59. package/commands/init/tsconfig-updater.js.map +1 -1
  60. package/commands/init/update-angular-config.d.ts.map +1 -1
  61. package/commands/init/update-angular-config.js +28 -50
  62. package/commands/init/update-angular-config.js.map +1 -1
  63. package/commands/init/vite-setup.d.ts +11 -0
  64. package/commands/init/vite-setup.d.ts.map +1 -0
  65. package/commands/init/vite-setup.js +61 -0
  66. package/commands/init/vite-setup.js.map +1 -0
  67. package/commands/init/wizard.d.ts +33 -0
  68. package/commands/init/wizard.d.ts.map +1 -0
  69. package/commands/init/wizard.js +561 -0
  70. package/commands/init/wizard.js.map +1 -0
  71. package/constants/app.constants.d.ts +17 -0
  72. package/constants/app.constants.d.ts.map +1 -1
  73. package/constants/app.constants.js +39 -16
  74. package/constants/app.constants.js.map +1 -1
  75. package/core/icons/families.d.ts +38 -0
  76. package/core/icons/families.d.ts.map +1 -0
  77. package/core/icons/families.js +26 -0
  78. package/core/icons/families.js.map +1 -0
  79. package/core/icons/icon-map.d.ts +24 -0
  80. package/core/icons/icon-map.d.ts.map +1 -0
  81. package/core/icons/icon-map.js +141 -0
  82. package/core/icons/icon-map.js.map +1 -0
  83. package/core/icons/index.d.ts +107 -0
  84. package/core/icons/index.d.ts.map +1 -0
  85. package/core/icons/index.js +183 -0
  86. package/core/icons/index.js.map +1 -0
  87. package/core/registry/registry-data.d.ts.map +1 -1
  88. package/core/registry/registry-data.js +498 -36
  89. package/core/registry/registry-data.js.map +1 -1
  90. package/core/themes/theme-definitions.d.ts.map +1 -1
  91. package/core/themes/theme-definitions.js +61 -72
  92. package/core/themes/theme-definitions.js.map +1 -1
  93. package/index.js +6 -3
  94. package/index.js.map +1 -1
  95. package/package.json +6 -11
  96. package/ui/blocks.d.ts +71 -0
  97. package/ui/blocks.d.ts.map +1 -0
  98. package/ui/blocks.js +116 -0
  99. package/ui/blocks.js.map +1 -0
  100. package/ui/brand.d.ts +14 -0
  101. package/ui/brand.d.ts.map +1 -0
  102. package/ui/brand.js +35 -0
  103. package/ui/brand.js.map +1 -0
  104. package/ui/engine/animation/index.d.ts +38 -0
  105. package/ui/engine/animation/index.d.ts.map +1 -0
  106. package/ui/engine/animation/index.js +102 -0
  107. package/ui/engine/animation/index.js.map +1 -0
  108. package/ui/engine/ansi/index.d.ts +51 -0
  109. package/ui/engine/ansi/index.d.ts.map +1 -0
  110. package/ui/engine/ansi/index.js +116 -0
  111. package/ui/engine/ansi/index.js.map +1 -0
  112. package/ui/engine/components/index.d.ts +211 -0
  113. package/ui/engine/components/index.d.ts.map +1 -0
  114. package/ui/engine/components/index.js +109 -0
  115. package/ui/engine/components/index.js.map +1 -0
  116. package/ui/engine/cursor/index.d.ts +16 -0
  117. package/ui/engine/cursor/index.d.ts.map +1 -0
  118. package/ui/engine/cursor/index.js +40 -0
  119. package/ui/engine/cursor/index.js.map +1 -0
  120. package/ui/engine/diff/index.d.ts +26 -0
  121. package/ui/engine/diff/index.d.ts.map +1 -0
  122. package/ui/engine/diff/index.js +56 -0
  123. package/ui/engine/diff/index.js.map +1 -0
  124. package/ui/engine/events/index.d.ts +24 -0
  125. package/ui/engine/events/index.d.ts.map +1 -0
  126. package/ui/engine/events/index.js +60 -0
  127. package/ui/engine/events/index.js.map +1 -0
  128. package/ui/engine/focus/index.d.ts +25 -0
  129. package/ui/engine/focus/index.d.ts.map +1 -0
  130. package/ui/engine/focus/index.js +77 -0
  131. package/ui/engine/focus/index.js.map +1 -0
  132. package/ui/engine/frame/index.d.ts +48 -0
  133. package/ui/engine/frame/index.d.ts.map +1 -0
  134. package/ui/engine/frame/index.js +0 -0
  135. package/ui/engine/frame/index.js.map +1 -0
  136. package/ui/engine/index.d.ts +28 -0
  137. package/ui/engine/index.d.ts.map +1 -0
  138. package/ui/engine/index.js +28 -0
  139. package/ui/engine/index.js.map +1 -0
  140. package/ui/engine/input/index.d.ts +36 -0
  141. package/ui/engine/input/index.d.ts.map +1 -0
  142. package/ui/engine/input/index.js +224 -0
  143. package/ui/engine/input/index.js.map +1 -0
  144. package/ui/engine/layout/index.d.ts +42 -0
  145. package/ui/engine/layout/index.d.ts.map +1 -0
  146. package/ui/engine/layout/index.js +241 -0
  147. package/ui/engine/layout/index.js.map +1 -0
  148. package/ui/engine/renderer/index.d.ts +43 -0
  149. package/ui/engine/renderer/index.d.ts.map +1 -0
  150. package/ui/engine/renderer/index.js +619 -0
  151. package/ui/engine/renderer/index.js.map +1 -0
  152. package/ui/engine/scheduler/index.d.ts +22 -0
  153. package/ui/engine/scheduler/index.d.ts.map +1 -0
  154. package/ui/engine/scheduler/index.js +77 -0
  155. package/ui/engine/scheduler/index.js.map +1 -0
  156. package/ui/engine/screen/index.d.ts +39 -0
  157. package/ui/engine/screen/index.d.ts.map +1 -0
  158. package/ui/engine/screen/index.js +255 -0
  159. package/ui/engine/screen/index.js.map +1 -0
  160. package/ui/engine/terminal/index.d.ts +84 -0
  161. package/ui/engine/terminal/index.d.ts.map +1 -0
  162. package/ui/engine/terminal/index.js +235 -0
  163. package/ui/engine/terminal/index.js.map +1 -0
  164. package/ui/engine/testing/index.d.ts +27 -0
  165. package/ui/engine/testing/index.d.ts.map +1 -0
  166. package/ui/engine/testing/index.js +63 -0
  167. package/ui/engine/testing/index.js.map +1 -0
  168. package/ui/engine/theme/index.d.ts +26 -0
  169. package/ui/engine/theme/index.d.ts.map +1 -0
  170. package/ui/engine/theme/index.js +128 -0
  171. package/ui/engine/theme/index.js.map +1 -0
  172. package/ui/engine/utils/index.d.ts +12 -0
  173. package/ui/engine/utils/index.d.ts.map +1 -0
  174. package/ui/engine/utils/index.js +143 -0
  175. package/ui/engine/utils/index.js.map +1 -0
  176. package/ui/gate.d.ts +19 -0
  177. package/ui/gate.d.ts.map +1 -0
  178. package/ui/gate.js +36 -0
  179. package/ui/gate.js.map +1 -0
  180. package/ui/index.d.ts +18 -0
  181. package/ui/index.d.ts.map +1 -0
  182. package/ui/index.js +18 -0
  183. package/ui/index.js.map +1 -0
  184. package/ui/log-sink.d.ts +24 -0
  185. package/ui/log-sink.d.ts.map +1 -0
  186. package/ui/log-sink.js +35 -0
  187. package/ui/log-sink.js.map +1 -0
  188. package/ui/output.d.ts +14 -0
  189. package/ui/output.d.ts.map +1 -0
  190. package/ui/output.js +28 -0
  191. package/ui/output.js.map +1 -0
  192. package/ui/report.d.ts +30 -0
  193. package/ui/report.d.ts.map +1 -0
  194. package/ui/report.js +45 -0
  195. package/ui/report.js.map +1 -0
  196. package/ui/runner.d.ts +66 -0
  197. package/ui/runner.d.ts.map +1 -0
  198. package/ui/runner.js +135 -0
  199. package/ui/runner.js.map +1 -0
  200. package/ui/style.d.ts +24 -0
  201. package/ui/style.d.ts.map +1 -0
  202. package/ui/style.js +44 -0
  203. package/ui/style.js.map +1 -0
  204. package/ui/testing/fake-tty.d.ts +26 -0
  205. package/ui/testing/fake-tty.d.ts.map +1 -0
  206. package/ui/testing/fake-tty.js +61 -0
  207. package/ui/testing/fake-tty.js.map +1 -0
  208. package/ui/text-input.d.ts +31 -0
  209. package/ui/text-input.d.ts.map +1 -0
  210. package/ui/text-input.js +89 -0
  211. package/ui/text-input.js.map +1 -0
  212. package/ui/theme.d.ts +10 -0
  213. package/ui/theme.d.ts.map +1 -0
  214. package/ui/theme.js +20 -0
  215. package/ui/theme.js.map +1 -0
  216. package/utils/angular-compat.d.ts +21 -0
  217. package/utils/angular-compat.d.ts.map +1 -0
  218. package/utils/angular-compat.js +52 -0
  219. package/utils/angular-compat.js.map +1 -0
  220. package/utils/config.d.ts +29 -0
  221. package/utils/config.d.ts.map +1 -1
  222. package/utils/config.js +50 -4
  223. package/utils/config.js.map +1 -1
  224. package/utils/errors.d.ts +11 -0
  225. package/utils/errors.d.ts.map +1 -1
  226. package/utils/errors.js +14 -0
  227. package/utils/errors.js.map +1 -1
  228. package/utils/get-project-info.d.ts +29 -0
  229. package/utils/get-project-info.d.ts.map +1 -1
  230. package/utils/get-project-info.js +246 -1
  231. package/utils/get-project-info.js.map +1 -1
  232. package/utils/http-client.d.ts.map +1 -1
  233. package/utils/http-client.js +18 -3
  234. package/utils/http-client.js.map +1 -1
  235. package/utils/icon-catalog.d.ts +34 -0
  236. package/utils/icon-catalog.d.ts.map +1 -0
  237. package/utils/icon-catalog.js +63 -0
  238. package/utils/icon-catalog.js.map +1 -0
  239. package/utils/logger.d.ts +13 -1
  240. package/utils/logger.d.ts.map +1 -1
  241. package/utils/logger.js +95 -15
  242. package/utils/logger.js.map +1 -1
  243. package/utils/package-manager.d.ts +42 -1
  244. package/utils/package-manager.d.ts.map +1 -1
  245. package/utils/package-manager.js +204 -11
  246. package/utils/package-manager.js.map +1 -1
  247. package/utils/registry.d.ts +106 -2
  248. package/utils/registry.d.ts.map +1 -1
  249. package/utils/registry.js +155 -21
  250. package/utils/registry.js.map +1 -1
  251. package/utils/schema-version.d.ts +21 -0
  252. package/utils/schema-version.d.ts.map +1 -0
  253. package/utils/schema-version.js +28 -0
  254. package/utils/schema-version.js.map +1 -0
  255. package/utils/source-file.d.ts +40 -0
  256. package/utils/source-file.d.ts.map +1 -0
  257. package/utils/source-file.js +104 -0
  258. package/utils/source-file.js.map +1 -0
@@ -0,0 +1,619 @@
1
+ /**
2
+ * renderer — orchestrates the hot path: layout → paint (Frame) → diff → encode
3
+ * (ANSI) → a single write. Keeps ONE retained frame, with a pool of 2 buffers.
4
+ * Holds the painters for every component, focusable ones included.
5
+ */
6
+ import { performance } from 'node:perf_hooks';
7
+ import { spinnerFrames } from '../animation/index.js';
8
+ import { Attr, csi, createSgrBuilder } from '../ansi/index.js';
9
+ import { createCursor } from '../cursor/index.js';
10
+ import { createDiffer } from '../diff/index.js';
11
+ import { createFrame, DEFAULT_STYLE } from '../frame/index.js';
12
+ import { computeLayout } from '../layout/index.js';
13
+ import { defaultTheme } from '../theme/index.js';
14
+ import { charWidth, graphemes, stringWidth, truncate, wrapText as wrap } from '../utils/index.js';
15
+ const SYNC_BEGIN = csi('?2026h');
16
+ const SYNC_END = csi('?2026l');
17
+ const BORDERS = {
18
+ single: { tl: '┌', tr: '┐', bl: '└', br: '┘', h: '─', v: '│' },
19
+ round: { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│' },
20
+ double: { tl: '╔', tr: '╗', bl: '╚', br: '╝', h: '═', v: '║' },
21
+ bold: { tl: '┏', tr: '┓', bl: '┗', br: '┛', h: '━', v: '┃' },
22
+ ascii: { tl: '+', tr: '+', bl: '+', br: '+', h: '-', v: '|' },
23
+ };
24
+ const STATUS_GLYPH = {
25
+ ok: ['●', 'success'],
26
+ info: ['●', 'primary'],
27
+ warn: ['●', 'warning'],
28
+ error: ['●', 'danger'],
29
+ pending: ['○', 'muted'],
30
+ };
31
+ function mkStyle(fg, bg, attrs) {
32
+ const s = { attrs };
33
+ if (fg !== undefined)
34
+ s.fg = fg;
35
+ if (bg !== undefined)
36
+ s.bg = bg;
37
+ return s;
38
+ }
39
+ function optColor(v) {
40
+ return typeof v === 'string' ? v : undefined;
41
+ }
42
+ function str(v) {
43
+ return typeof v === 'string' ? v : '';
44
+ }
45
+ function num(v, d = 0) {
46
+ return typeof v === 'number' ? v : d;
47
+ }
48
+ function fit(s, w) {
49
+ const sw = stringWidth(s);
50
+ if (sw === w)
51
+ return s;
52
+ if (sw < w)
53
+ return s + ' '.repeat(w - sw);
54
+ return truncate(s, w, '…');
55
+ }
56
+ function hexToRgb(hex) {
57
+ const s = hex.replace('#', '');
58
+ const f = s.length === 3
59
+ ? s
60
+ .split('')
61
+ .map(c => c + c)
62
+ .join('')
63
+ : s;
64
+ return [parseInt(f.slice(0, 2), 16) || 0, parseInt(f.slice(2, 4), 16) || 0, parseInt(f.slice(4, 6), 16) || 0];
65
+ }
66
+ function rgbToHex(r, g, b) {
67
+ const h = (n) => Math.max(0, Math.min(255, Math.round(n)))
68
+ .toString(16)
69
+ .padStart(2, '0');
70
+ return `#${h(r)}${h(g)}${h(b)}`;
71
+ }
72
+ function lerpStops(stops, t) {
73
+ if (stops.length === 0)
74
+ return '#ffffff';
75
+ if (stops.length === 1)
76
+ return stops[0];
77
+ const c = Math.max(0, Math.min(1, t));
78
+ const seg = c * (stops.length - 1);
79
+ const i = Math.min(stops.length - 2, Math.floor(seg));
80
+ const local = seg - i;
81
+ const a = hexToRgb(stops[i]);
82
+ const b = hexToRgb(stops[i + 1]);
83
+ return rgbToHex(a[0] + (b[0] - a[0]) * local, a[1] + (b[1] - a[1]) * local, a[2] + (b[2] - a[2]) * local);
84
+ }
85
+ const GRADIENT_DEFAULT = ['#4796E4', '#9B7FD4', '#E26B8F'];
86
+ function paintBanner(frame, box) {
87
+ const p = box.node.props;
88
+ const lines = Array.isArray(p['lines']) ? p['lines'] : [];
89
+ const colors = Array.isArray(p['colors']) && p['colors'].length ? p['colors'] : GRADIENT_DEFAULT;
90
+ const width = Math.max(1, ...lines.map(l => stringWidth(l)));
91
+ const centered = p['align'] === 'center';
92
+ for (let i = 0; i < lines.length && i < box.rect.h; i++) {
93
+ const l = lines[i] ?? '';
94
+ const baseX = box.rect.x + (centered ? Math.max(0, Math.floor((box.rect.w - stringWidth(l)) / 2)) : 0);
95
+ let col = 0;
96
+ for (const g of graphemes(l)) {
97
+ const w = charWidth(g);
98
+ if (g !== ' ' && g !== '') {
99
+ const hex = lerpStops(colors, width > 1 ? col / (width - 1) : 0);
100
+ frame.set(baseX + col, box.rect.y + i, {
101
+ char: g,
102
+ width: w === 2 ? 2 : 1,
103
+ style: mkStyle(hex, undefined, Attr.Bold),
104
+ });
105
+ if (w === 2)
106
+ frame.set(baseX + col + 1, box.rect.y + i, { char: '', width: 0, style: mkStyle(hex, undefined, Attr.Bold) });
107
+ }
108
+ col += w;
109
+ }
110
+ }
111
+ }
112
+ function paintGradientText(frame, box) {
113
+ const p = box.node.props;
114
+ const content = str(p['content']);
115
+ const colors = Array.isArray(p['colors']) && p['colors'].length ? p['colors'] : GRADIENT_DEFAULT;
116
+ const width = Math.max(1, stringWidth(content));
117
+ const bold = p['bold'] ? Attr.Bold : Attr.None;
118
+ let baseX = box.rect.x;
119
+ if (p['align'] === 'center')
120
+ baseX += Math.max(0, Math.floor((box.rect.w - width) / 2));
121
+ else if (p['align'] === 'right')
122
+ baseX += Math.max(0, box.rect.w - width);
123
+ let col = 0;
124
+ for (const g of graphemes(content)) {
125
+ const w = charWidth(g);
126
+ if (g !== ' ' && g !== '') {
127
+ const hex = lerpStops(colors, width > 1 ? col / (width - 1) : 0);
128
+ frame.set(baseX + col, box.rect.y, { char: g, width: w === 2 ? 2 : 1, style: mkStyle(hex, undefined, bold) });
129
+ if (w === 2)
130
+ frame.set(baseX + col + 1, box.rect.y, { char: '', width: 0, style: mkStyle(hex, undefined, bold) });
131
+ }
132
+ col += w;
133
+ }
134
+ }
135
+ function textStyle(p) {
136
+ let attrs = Attr.None;
137
+ if (p['bold'])
138
+ attrs |= Attr.Bold;
139
+ if (p['dim'])
140
+ attrs |= Attr.Dim;
141
+ if (p['italic'])
142
+ attrs |= Attr.Italic;
143
+ if (p['underline'])
144
+ attrs |= Attr.Underline;
145
+ if (p['inverse'])
146
+ attrs |= Attr.Inverse;
147
+ if (typeof p['attrs'] === 'number')
148
+ attrs |= p['attrs'];
149
+ return mkStyle(optColor(p['color']), optColor(p['background']), attrs);
150
+ }
151
+ function paintContainer(frame, box) {
152
+ const p = box.node.props;
153
+ const rect = box.rect;
154
+ const bg = optColor(p['background']);
155
+ if (bg !== undefined)
156
+ frame.fillRect(rect, { char: ' ', width: 1, style: mkStyle(undefined, bg, Attr.None) });
157
+ const border = str(p['border']) || 'none';
158
+ if (border === 'none' || rect.w < 2 || rect.h < 2)
159
+ return;
160
+ const b = BORDERS[border] ?? BORDERS['single'];
161
+ const st = mkStyle(optColor(p['borderColor']) ?? 'border', bg, Attr.None);
162
+ const { x, y, w, h } = rect;
163
+ frame.set(x, y, { char: b.tl, width: 1, style: st });
164
+ frame.set(x + w - 1, y, { char: b.tr, width: 1, style: st });
165
+ frame.set(x, y + h - 1, { char: b.bl, width: 1, style: st });
166
+ frame.set(x + w - 1, y + h - 1, { char: b.br, width: 1, style: st });
167
+ for (let i = x + 1; i < x + w - 1; i++) {
168
+ frame.set(i, y, { char: b.h, width: 1, style: st });
169
+ frame.set(i, y + h - 1, { char: b.h, width: 1, style: st });
170
+ }
171
+ for (let j = y + 1; j < y + h - 1; j++) {
172
+ frame.set(x, j, { char: b.v, width: 1, style: st });
173
+ frame.set(x + w - 1, j, { char: b.v, width: 1, style: st });
174
+ }
175
+ const title = str(p['title']);
176
+ if (title) {
177
+ const ts = mkStyle(optColor(p['borderColor']) ?? 'primary', bg, Attr.Bold);
178
+ frame.putText(x + 2, y, ` ${title} `, ts, { x: x + 1, y, w: w - 2, h: 1 });
179
+ }
180
+ const subtitle = str(p['subtitle']);
181
+ if (subtitle && box.node.type === 'card') {
182
+ const sw = stringWidth(` ${subtitle} `);
183
+ frame.putText(x + w - 2 - sw, y + h - 1, ` ${subtitle} `, mkStyle('muted', bg, Attr.Dim), {
184
+ x: x + 1,
185
+ y: y + h - 1,
186
+ w: w - 2,
187
+ h: 1,
188
+ });
189
+ }
190
+ }
191
+ function paintText(frame, box) {
192
+ const p = box.node.props;
193
+ const style = textStyle(p);
194
+ const align = p['align'];
195
+ const lines = wrap(str(p['content']), Math.max(1, box.rect.w));
196
+ for (let i = 0; i < lines.length && i < box.rect.h; i++) {
197
+ const line = lines[i] ?? '';
198
+ let x = box.rect.x;
199
+ const lw = stringWidth(line);
200
+ if (align === 'center')
201
+ x += Math.floor((box.rect.w - lw) / 2);
202
+ else if (align === 'right')
203
+ x += box.rect.w - lw;
204
+ frame.putText(x, box.rect.y + i, line, style, box.rect);
205
+ }
206
+ }
207
+ function line(frame, box, text, style, dy = 0) {
208
+ frame.putText(box.rect.x, box.rect.y + dy, text, style, { ...box.rect, y: box.rect.y + dy, h: 1 });
209
+ }
210
+ function paintInput(frame, box, ctx) {
211
+ const p = box.node.props;
212
+ const focused = ctx.focusKey !== undefined && box.node.key === ctx.focusKey;
213
+ const isPw = box.node.type === 'password';
214
+ const value = str(p['value']);
215
+ const mask = str(p['mask']) || '•';
216
+ const shown = isPw ? mask.repeat([...value].length) : value;
217
+ const label = str(p['label']);
218
+ const placeholder = str(p['placeholder']);
219
+ let x = box.rect.x;
220
+ x = frame.putText(x, box.rect.y, focused ? '❯ ' : ' ', mkStyle('primary', undefined, focused ? Attr.Bold : Attr.None), box.rect);
221
+ if (label)
222
+ x = frame.putText(x, box.rect.y, label + ' ', mkStyle('foreground', undefined, Attr.Bold), box.rect);
223
+ const startX = x;
224
+ const display = shown || placeholder;
225
+ const st = shown ? mkStyle('foreground', undefined, Attr.None) : mkStyle('muted', undefined, Attr.Dim);
226
+ frame.putText(x, box.rect.y, display, st, box.rect);
227
+ if (focused) {
228
+ const caret = Math.min(ctx.caret ?? [...value].length, [...shown].length);
229
+ const cx = startX + caret;
230
+ if (cx < box.rect.x + box.rect.w) {
231
+ const under = frame.cellAt(cx, box.rect.y);
232
+ const ch = under.char && under.char !== '\x00' && under.width !== 0 ? under.char : ' ';
233
+ frame.set(cx, box.rect.y, { char: ch, width: 1, style: mkStyle('background', 'primary', Attr.None) });
234
+ }
235
+ }
236
+ }
237
+ function paintSelect(frame, box, ctx) {
238
+ const p = box.node.props;
239
+ const focused = ctx.focusKey !== undefined && box.node.key === ctx.focusKey;
240
+ const options = Array.isArray(p['options'])
241
+ ? p['options']
242
+ : [];
243
+ const value = str(p['value']);
244
+ let y = box.rect.y;
245
+ const label = str(p['label']);
246
+ if (label) {
247
+ line(frame, box, label, mkStyle('muted', undefined, Attr.Dim), y - box.rect.y);
248
+ y++;
249
+ }
250
+ for (const opt of options) {
251
+ const selected = opt.value === value;
252
+ const prefix = selected ? '❯ ' : ' ';
253
+ const st = opt.disabled
254
+ ? mkStyle('muted', undefined, Attr.Dim)
255
+ : selected
256
+ ? mkStyle('primary', undefined, focused ? Attr.Bold : Attr.None)
257
+ : mkStyle('foreground', undefined, Attr.None);
258
+ frame.putText(box.rect.x, y, prefix + opt.label, st, { x: box.rect.x, y, w: box.rect.w, h: 1 });
259
+ y++;
260
+ }
261
+ }
262
+ function paintTable(frame, box) {
263
+ const p = box.node.props;
264
+ const columns = Array.isArray(p['columns'])
265
+ ? p['columns']
266
+ : [];
267
+ const rows = Array.isArray(p['rows']) ? p['rows'] : [];
268
+ const widths = columns.map((c, i) => {
269
+ if (typeof c.width === 'number')
270
+ return c.width;
271
+ let w = stringWidth(c.header);
272
+ for (const r of rows)
273
+ w = Math.max(w, stringWidth(str(r[i])));
274
+ return Math.min(w, 24);
275
+ });
276
+ const gap = 2;
277
+ const renderRow = (cells, y, style) => {
278
+ let x = box.rect.x;
279
+ for (let i = 0; i < columns.length; i++) {
280
+ const w = widths[i] ?? 8;
281
+ frame.putText(x, y, fit(str(cells[i]), w), style, { x, y, w, h: 1 });
282
+ x += w + gap;
283
+ }
284
+ };
285
+ let y = box.rect.y;
286
+ renderRow(columns.map(c => c.header), y, mkStyle('foreground', undefined, Attr.Bold));
287
+ y++;
288
+ const total = widths.reduce((a, b) => a + b + gap, 0);
289
+ frame.putText(box.rect.x, y, '─'.repeat(Math.min(box.rect.w, total)), mkStyle('border', undefined, Attr.None), {
290
+ x: box.rect.x,
291
+ y,
292
+ w: box.rect.w,
293
+ h: 1,
294
+ });
295
+ y++;
296
+ for (const r of rows) {
297
+ renderRow(r, y, mkStyle('foreground', undefined, Attr.None));
298
+ y++;
299
+ }
300
+ }
301
+ function paintTree(frame, box) {
302
+ const p = box.node.props;
303
+ const root = p['root'];
304
+ const lines = [];
305
+ const walk = (n, prefix, isLast, depth) => {
306
+ const branch = depth === 0 ? '' : isLast ? '└─ ' : '├─ ';
307
+ lines.push({ text: prefix + branch + n.label, accent: depth === 0 });
308
+ if (n.expanded !== false && Array.isArray(n.children)) {
309
+ const childPrefix = depth === 0 ? '' : prefix + (isLast ? ' ' : '│ ');
310
+ n.children.forEach((c, i) => walk(c, childPrefix, i === n.children.length - 1, depth + 1));
311
+ }
312
+ };
313
+ if (root)
314
+ walk(root, '', true, 0);
315
+ lines.forEach((l, i) => {
316
+ if (i >= box.rect.h)
317
+ return;
318
+ frame.putText(box.rect.x, box.rect.y + i, l.text, mkStyle(l.accent ? 'foreground' : 'muted', undefined, l.accent ? Attr.Bold : Attr.None), {
319
+ x: box.rect.x,
320
+ y: box.rect.y + i,
321
+ w: box.rect.w,
322
+ h: 1,
323
+ });
324
+ });
325
+ }
326
+ function paintNode(frame, box, ctx) {
327
+ const p = box.node.props;
328
+ const focused = ctx.focusKey !== undefined && box.node.key === ctx.focusKey;
329
+ switch (box.node.type) {
330
+ case 'box':
331
+ case 'panel':
332
+ case 'card':
333
+ paintContainer(frame, box);
334
+ break;
335
+ case 'text':
336
+ paintText(frame, box);
337
+ break;
338
+ case 'banner':
339
+ paintBanner(frame, box);
340
+ break;
341
+ case 'gradientText':
342
+ paintGradientText(frame, box);
343
+ break;
344
+ case 'separator': {
345
+ const ch = str(p['char']) || '─';
346
+ const st = mkStyle(optColor(p['color']) ?? 'border', undefined, Attr.None);
347
+ const label = str(p['label']);
348
+ if (label) {
349
+ const pad = 2;
350
+ const seg = `${ch.repeat(pad)} ${label} `;
351
+ let filled = frame.putText(box.rect.x, box.rect.y, seg, st, box.rect);
352
+ while (filled < box.rect.x + box.rect.w)
353
+ filled = frame.putText(filled, box.rect.y, ch, st, box.rect);
354
+ }
355
+ else {
356
+ line(frame, box, ch.repeat(Math.max(0, box.rect.w)), st);
357
+ }
358
+ break;
359
+ }
360
+ case 'header': {
361
+ line(frame, box, str(p['title']), mkStyle('foreground', undefined, Attr.Bold));
362
+ const badge = str(p['badge']);
363
+ if (badge) {
364
+ const bx = box.rect.x + box.rect.w - stringWidth(` ${badge} `);
365
+ frame.putText(bx, box.rect.y, ` ${badge} `, mkStyle('background', 'primary', Attr.Bold), box.rect);
366
+ }
367
+ const subtitle = str(p['subtitle']);
368
+ if (subtitle)
369
+ frame.putText(box.rect.x, box.rect.y + 1, subtitle, mkStyle('muted', undefined, Attr.Dim), {
370
+ ...box.rect,
371
+ y: box.rect.y + 1,
372
+ });
373
+ break;
374
+ }
375
+ case 'footer': {
376
+ const items = Array.isArray(p['items']) ? p['items'] : [];
377
+ line(frame, box, items.join(' · '), mkStyle(optColor(p['color']) ?? 'muted', undefined, Attr.Dim));
378
+ break;
379
+ }
380
+ case 'spinner': {
381
+ const variant = str(p['variant']) || 'dots';
382
+ const frames = spinnerFrames[variant] ?? spinnerFrames['dots'];
383
+ const glyph = str(p['glyph']) || frames[Math.floor((ctx.elapsedMs ?? (ctx.tick ?? 0) * 80) / 80) % frames.length] || '⠋';
384
+ const label = str(p['label']);
385
+ const st = mkStyle(optColor(p['color']) ?? 'primary', undefined, Attr.None);
386
+ line(frame, box, label ? `${glyph} ${label}` : glyph, st);
387
+ break;
388
+ }
389
+ case 'status': {
390
+ const kind = str(p['kind']) || 'info';
391
+ const [glyph, color] = STATUS_GLYPH[kind] ?? STATUS_GLYPH['info'];
392
+ line(frame, box, `${glyph} ${str(p['label'])}`, mkStyle(color, undefined, Attr.None));
393
+ break;
394
+ }
395
+ case 'badge': {
396
+ const kindColor = {
397
+ ok: 'success',
398
+ warn: 'warning',
399
+ error: 'danger',
400
+ info: 'primary',
401
+ pending: 'muted',
402
+ };
403
+ const color = optColor(p['color']) ?? kindColor[str(p['kind'])] ?? 'primary';
404
+ line(frame, box, ` ${str(p['text'])} `, mkStyle('background', color, Attr.Bold));
405
+ break;
406
+ }
407
+ case 'progressBar': {
408
+ const value = Math.max(0, Math.min(1, num(p['value'])));
409
+ const showPercent = p['showPercent'] !== false;
410
+ const label = str(p['label']);
411
+ const suffix = showPercent ? ` ${String(Math.round(value * 100)).padStart(3)}%` : '';
412
+ const labelPart = label ? label + ' ' : '';
413
+ const barW = Math.max(1, box.rect.w - stringWidth(suffix) - stringWidth(labelPart));
414
+ const filled = Math.round(value * barW);
415
+ let x = box.rect.x;
416
+ if (labelPart)
417
+ x = frame.putText(x, box.rect.y, labelPart, mkStyle('muted', undefined, Attr.None), box.rect);
418
+ const color = optColor(p['color']) ?? 'primary';
419
+ x = frame.putText(x, box.rect.y, '█'.repeat(filled), mkStyle(color, undefined, Attr.None), box.rect);
420
+ x = frame.putText(x, box.rect.y, '░'.repeat(Math.max(0, barW - filled)), mkStyle('border', undefined, Attr.None), box.rect);
421
+ if (suffix)
422
+ frame.putText(x, box.rect.y, suffix, mkStyle('muted', undefined, Attr.Dim), box.rect);
423
+ break;
424
+ }
425
+ case 'list': {
426
+ const items = Array.isArray(p['items']) ? p['items'] : [];
427
+ const ordered = Boolean(p['ordered']);
428
+ const bullet = str(p['bullet']) || '•';
429
+ items.forEach((it, i) => {
430
+ if (i >= box.rect.h)
431
+ return;
432
+ const prefix = ordered ? `${i + 1}. ` : `${bullet} `;
433
+ const x = frame.putText(box.rect.x, box.rect.y + i, prefix, mkStyle('primary', undefined, Attr.None), {
434
+ ...box.rect,
435
+ y: box.rect.y + i,
436
+ });
437
+ frame.putText(x, box.rect.y + i, it, mkStyle('foreground', undefined, Attr.None), {
438
+ ...box.rect,
439
+ y: box.rect.y + i,
440
+ });
441
+ });
442
+ break;
443
+ }
444
+ case 'input':
445
+ case 'password':
446
+ paintInput(frame, box, ctx);
447
+ break;
448
+ case 'select':
449
+ paintSelect(frame, box, ctx);
450
+ break;
451
+ case 'checkbox': {
452
+ const checked = Boolean(p['checked']);
453
+ const glyph = checked ? '◉' : '◯';
454
+ const st = mkStyle(focused ? 'primary' : checked ? 'success' : 'foreground', undefined, focused ? Attr.Bold : Attr.None);
455
+ line(frame, box, `${focused ? '❯ ' : ' '}${glyph} ${str(p['label'])}`, st);
456
+ break;
457
+ }
458
+ case 'confirm': {
459
+ const val = p['value'] === true;
460
+ let x = frame.putText(box.rect.x, box.rect.y, focused ? '❯ ' : ' ', mkStyle('primary', undefined, focused ? Attr.Bold : Attr.None), box.rect);
461
+ x = frame.putText(x, box.rect.y, str(p['message']) + ' ', mkStyle('foreground', undefined, Attr.Bold), box.rect);
462
+ frame.putText(x, box.rect.y, val ? '(Y/n)' : '(y/N)', mkStyle('muted', undefined, Attr.None), box.rect);
463
+ break;
464
+ }
465
+ case 'textarea': {
466
+ const value = str(p['value']);
467
+ const rows = num(p['rows'], 3);
468
+ const lines = wrap(value || str(p['placeholder']), Math.max(1, box.rect.w - 2));
469
+ const barColor = focused ? 'primary' : 'border';
470
+ for (let i = 0; i <= rows; i++) {
471
+ frame.putText(box.rect.x, box.rect.y + i, '│ ', mkStyle(barColor, undefined, Attr.None), {
472
+ ...box.rect,
473
+ y: box.rect.y + i,
474
+ });
475
+ const ln = lines[i] ?? '';
476
+ frame.putText(box.rect.x + 2, box.rect.y + i, ln, mkStyle(value ? 'foreground' : 'muted', undefined, value ? Attr.None : Attr.Dim), {
477
+ ...box.rect,
478
+ y: box.rect.y + i,
479
+ });
480
+ }
481
+ break;
482
+ }
483
+ case 'table':
484
+ paintTable(frame, box);
485
+ break;
486
+ case 'tree':
487
+ paintTree(frame, box);
488
+ break;
489
+ case 'emptyState': {
490
+ const icon = str(p['icon']) || '◌';
491
+ const title = str(p['title']);
492
+ const desc = str(p['description']);
493
+ const hint = str(p['hint']);
494
+ const cx = (s) => box.rect.x + Math.max(0, Math.floor((box.rect.w - stringWidth(s)) / 2));
495
+ let y = box.rect.y;
496
+ frame.putText(cx(icon), y++, icon, mkStyle('muted', undefined, Attr.None), { ...box.rect, y });
497
+ frame.putText(cx(title), y++, title, mkStyle('foreground', undefined, Attr.Bold), { ...box.rect, y });
498
+ if (desc)
499
+ frame.putText(cx(desc), y++, desc, mkStyle('muted', undefined, Attr.Dim), { ...box.rect, y });
500
+ if (hint)
501
+ frame.putText(cx(hint), y++, hint, mkStyle('primary', undefined, Attr.None), { ...box.rect, y });
502
+ break;
503
+ }
504
+ default:
505
+ break;
506
+ }
507
+ }
508
+ class RendererImpl {
509
+ terminal;
510
+ theme;
511
+ differ;
512
+ sync;
513
+ level;
514
+ cursor = createCursor();
515
+ sgr = createSgrBuilder();
516
+ retained;
517
+ scratch;
518
+ _size;
519
+ frameNo = 0;
520
+ constructor(options) {
521
+ this.terminal = options.terminal;
522
+ this.theme = options.theme ?? defaultTheme;
523
+ this.differ = options.differ ?? createDiffer();
524
+ this.sync = options.synchronized ?? true;
525
+ this.level = this.terminal.caps.colors;
526
+ this._size = this.terminal.size();
527
+ this.retained = createFrame(this._size);
528
+ this.scratch = createFrame(this._size);
529
+ }
530
+ get size() {
531
+ return this._size;
532
+ }
533
+ encode(patch) {
534
+ this.sgr.clear();
535
+ let out = '';
536
+ for (const l of patch.lines) {
537
+ for (const run of l.runs) {
538
+ out += this.cursor.moveTo(run.x, l.y);
539
+ for (const cell of run.cells) {
540
+ if (cell.width === 0)
541
+ continue;
542
+ const fg = this.theme.resolve(cell.style.fg, this.level);
543
+ const bg = this.theme.resolve(cell.style.bg, this.level);
544
+ out += this.sgr.transition(fg, bg, cell.style.attrs);
545
+ out += cell.char || ' ';
546
+ }
547
+ }
548
+ }
549
+ out += this.sgr.reset();
550
+ return out;
551
+ }
552
+ render(root, ctx = {}) {
553
+ const t0 = performance.now();
554
+ const boxes = computeLayout(root, this._size);
555
+ const t1 = performance.now();
556
+ this.scratch.clearTo(DEFAULT_STYLE);
557
+ for (const box of boxes)
558
+ paintNode(this.scratch, box, ctx);
559
+ const t2 = performance.now();
560
+ const patch = this.differ.diff(this.retained, this.scratch);
561
+ const t3 = performance.now();
562
+ let bytes = '';
563
+ if (!patch.isEmpty) {
564
+ const body = this.encode(patch);
565
+ bytes = this.sync && this.terminal.caps.synchronizedOutput ? SYNC_BEGIN + body + SYNC_END : body;
566
+ this.terminal.write(bytes);
567
+ }
568
+ const t4 = performance.now();
569
+ const tmp = this.retained;
570
+ this.retained = this.scratch;
571
+ this.scratch = tmp;
572
+ this.frameNo++;
573
+ let changedCells = 0;
574
+ for (const l of patch.lines)
575
+ for (const r of l.runs)
576
+ changedCells += r.cells.length;
577
+ return {
578
+ frame: this.frameNo,
579
+ changedLines: patch.lines.length,
580
+ changedCells,
581
+ bytesWritten: bytes.length,
582
+ layoutMicros: Math.round((t1 - t0) * 1000),
583
+ paintMicros: Math.round((t2 - t1) * 1000),
584
+ diffMicros: Math.round((t3 - t2) * 1000),
585
+ encodeMicros: Math.round((t4 - t3) * 1000),
586
+ };
587
+ }
588
+ invalidateAll() {
589
+ this.retained.invalidate();
590
+ }
591
+ resize(size) {
592
+ this._size = size;
593
+ this.retained = createFrame(size);
594
+ this.scratch = createFrame(size);
595
+ this.retained.invalidate();
596
+ }
597
+ setTheme(theme) {
598
+ this.theme = theme;
599
+ this.invalidateAll();
600
+ }
601
+ debugFrame() {
602
+ const rows = [];
603
+ for (let y = 0; y < this._size.rows; y++) {
604
+ let s = '';
605
+ for (let x = 0; x < this._size.cols; x++) {
606
+ const cell = this.retained.cellAt(x, y);
607
+ if (cell.width === 0)
608
+ continue;
609
+ s += cell.char === '\x00' ? ' ' : cell.char || ' ';
610
+ }
611
+ rows.push(s.replace(/\s+$/u, ''));
612
+ }
613
+ return rows.join('\n');
614
+ }
615
+ }
616
+ export function createRenderer(options) {
617
+ return new RendererImpl(options);
618
+ }
619
+ //# sourceMappingURL=index.js.map