a2uink 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 (134) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc.cjs +21 -0
  3. package/.gitattributes +5 -0
  4. package/.github/copilot-instructions.md +21 -0
  5. package/.github/workflows/ci.yml +31 -0
  6. package/.husky/pre-commit +6 -0
  7. package/.prettierignore +6 -0
  8. package/.prettierrc +7 -0
  9. package/README.md +44 -0
  10. package/dist/binding.d.ts +3 -0
  11. package/dist/binding.js +73 -0
  12. package/dist/catalog.d.ts +6 -0
  13. package/dist/catalog.js +165 -0
  14. package/dist/examples/demo.d.ts +1 -0
  15. package/dist/examples/demo.js +309 -0
  16. package/dist/focus.d.ts +15 -0
  17. package/dist/focus.js +68 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +1 -0
  20. package/dist/renderer.d.ts +6 -0
  21. package/dist/renderer.js +144 -0
  22. package/dist/src/binding.d.ts +8 -0
  23. package/dist/src/binding.js +141 -0
  24. package/dist/src/catalog.d.ts +2 -0
  25. package/dist/src/catalog.js +1 -0
  26. package/dist/src/components/Box.d.ts +6 -0
  27. package/dist/src/components/Box.js +23 -0
  28. package/dist/src/components/Button.d.ts +7 -0
  29. package/dist/src/components/Button.js +71 -0
  30. package/dist/src/components/Chart.d.ts +5 -0
  31. package/dist/src/components/Chart.js +65 -0
  32. package/dist/src/components/Checkbox.d.ts +7 -0
  33. package/dist/src/components/Checkbox.js +51 -0
  34. package/dist/src/components/DateTimeInput.d.ts +1 -0
  35. package/dist/src/components/DateTimeInput.js +1 -0
  36. package/dist/src/components/Divider.d.ts +5 -0
  37. package/dist/src/components/Divider.js +7 -0
  38. package/dist/src/components/Image.d.ts +5 -0
  39. package/dist/src/components/Image.js +8 -0
  40. package/dist/src/components/Input.d.ts +7 -0
  41. package/dist/src/components/Input.js +124 -0
  42. package/dist/src/components/List.d.ts +5 -0
  43. package/dist/src/components/List.js +9 -0
  44. package/dist/src/components/Modal.d.ts +6 -0
  45. package/dist/src/components/Modal.js +13 -0
  46. package/dist/src/components/RadioGroup.d.ts +7 -0
  47. package/dist/src/components/RadioGroup.js +56 -0
  48. package/dist/src/components/Select.d.ts +7 -0
  49. package/dist/src/components/Select.js +66 -0
  50. package/dist/src/components/Slider.d.ts +7 -0
  51. package/dist/src/components/Slider.js +74 -0
  52. package/dist/src/components/Spacer.d.ts +1 -0
  53. package/dist/src/components/Spacer.js +1 -0
  54. package/dist/src/components/Table.d.ts +5 -0
  55. package/dist/src/components/Table.js +14 -0
  56. package/dist/src/components/Tabs.d.ts +7 -0
  57. package/dist/src/components/Tabs.js +56 -0
  58. package/dist/src/components/Text.d.ts +5 -0
  59. package/dist/src/components/Text.js +15 -0
  60. package/dist/src/components/helpers.d.ts +4 -0
  61. package/dist/src/components/helpers.js +39 -0
  62. package/dist/src/components/index.d.ts +16 -0
  63. package/dist/src/components/index.js +15 -0
  64. package/dist/src/components/renderNode.d.ts +4 -0
  65. package/dist/src/components/renderNode.js +61 -0
  66. package/dist/src/components/types.d.ts +7 -0
  67. package/dist/src/components/types.js +1 -0
  68. package/dist/src/components.d.ts +1 -0
  69. package/dist/src/components.js +1 -0
  70. package/dist/src/focus.d.ts +15 -0
  71. package/dist/src/focus.js +68 -0
  72. package/dist/src/index.d.ts +2 -0
  73. package/dist/src/index.js +1 -0
  74. package/dist/src/renderer.d.ts +6 -0
  75. package/dist/src/renderer.js +673 -0
  76. package/dist/src/tree.d.ts +2 -0
  77. package/dist/src/tree.js +47 -0
  78. package/dist/src/types.d.ts +92 -0
  79. package/dist/src/types.js +1 -0
  80. package/dist/tree.d.ts +2 -0
  81. package/dist/tree.js +45 -0
  82. package/dist/types.d.ts +73 -0
  83. package/dist/types.js +1 -0
  84. package/docs/demo/README.md +90 -0
  85. package/docs/demo/app.js +268 -0
  86. package/docs/demo/index.html +14 -0
  87. package/docs/demo/package-lock.json +4512 -0
  88. package/docs/demo/package.json +32 -0
  89. package/docs/demo/src/App.tsx +1403 -0
  90. package/docs/demo/src/main.tsx +9 -0
  91. package/docs/demo/src/setEnv.ts +29 -0
  92. package/docs/demo/src/shims/fs.js +16 -0
  93. package/docs/demo/src/shims/process.js +10 -0
  94. package/docs/demo/src/styles.css +720 -0
  95. package/docs/demo/styles.css +1 -0
  96. package/docs/demo/tsconfig.json +14 -0
  97. package/docs/demo/vite-plugin-node-polyfills/shims/buffer +2 -0
  98. package/docs/demo/vite-plugin-node-polyfills/shims/global +2 -0
  99. package/docs/demo/vite-plugin-node-polyfills/shims/process +10 -0
  100. package/docs/demo/vite.config.js +200 -0
  101. package/docs/overview.md +277 -0
  102. package/examples/demo.d.ts +1 -0
  103. package/examples/demo.js +66 -0
  104. package/examples/demo.ts +315 -0
  105. package/package.json +48 -0
  106. package/src/binding.ts +191 -0
  107. package/src/catalog.ts +2 -0
  108. package/src/components/Box.ts +39 -0
  109. package/src/components/Button.ts +84 -0
  110. package/src/components/Checkbox.ts +66 -0
  111. package/src/components/DateTimeInput.ts +1 -0
  112. package/src/components/Divider.ts +8 -0
  113. package/src/components/Image.ts +15 -0
  114. package/src/components/Input.ts +148 -0
  115. package/src/components/List.ts +15 -0
  116. package/src/components/Modal.ts +21 -0
  117. package/src/components/RadioGroup.ts +77 -0
  118. package/src/components/Select.ts +94 -0
  119. package/src/components/Slider.ts +98 -0
  120. package/src/components/Spacer.ts +1 -0
  121. package/src/components/Table.ts +22 -0
  122. package/src/components/Tabs.ts +82 -0
  123. package/src/components/Text.ts +21 -0
  124. package/src/components/helpers.ts +42 -0
  125. package/src/components/index.ts +16 -0
  126. package/src/components/renderNode.ts +73 -0
  127. package/src/components/types.ts +8 -0
  128. package/src/components.ts +1 -0
  129. package/src/focus.ts +94 -0
  130. package/src/index.ts +12 -0
  131. package/src/renderer.ts +779 -0
  132. package/src/tree.ts +63 -0
  133. package/src/types.ts +110 -0
  134. package/tsconfig.json +16 -0
@@ -0,0 +1,673 @@
1
+ import React from "react";
2
+ import { Box, render, Text, useInput, useStdout } from "ink";
3
+ import { resolveBoundValue, setBoundValue } from "./binding.js";
4
+ import { FocusProvider, useFocusRegistry } from "./focus.js";
5
+ import { buildResolvedTree } from "./tree.js";
6
+ import { renderNode } from "./catalog.js";
7
+ export function createA2uiInkRenderer(options = {}) {
8
+ const surfaces = new Map();
9
+ let inkInstance = null;
10
+ const updateLocalDataModel = (surfaceId, path, value, node) => {
11
+ const surface = surfaces.get(surfaceId);
12
+ if (!surface) {
13
+ return;
14
+ }
15
+ surface.dataModel = setBoundValue(path, surface.dataModel, node.bindingContext, value);
16
+ renderSurfaces();
17
+ };
18
+ const ensureSurface = (surfaceId) => {
19
+ const existing = surfaces.get(surfaceId);
20
+ if (existing) {
21
+ return existing;
22
+ }
23
+ const created = {
24
+ surfaceId,
25
+ components: {},
26
+ dataModel: {},
27
+ renderReady: false
28
+ };
29
+ surfaces.set(surfaceId, created);
30
+ return created;
31
+ };
32
+ const renderSurfaces = () => {
33
+ var _a, _b;
34
+ const surface = Array.from(surfaces.values()).find((entry) => entry.renderReady && entry.rootComponentId);
35
+ const element = React.createElement(A2uiRoot, {
36
+ surface: surface !== null && surface !== void 0 ? surface : null,
37
+ onUserAction: options.onUserAction,
38
+ onLocalDataModelUpdate: updateLocalDataModel
39
+ });
40
+ if (!inkInstance) {
41
+ const canPatchConsole = typeof console !== "undefined" && typeof console.Console === "function";
42
+ const renderOptions = {
43
+ exitOnCtrlC: (_a = options.exitOnCtrlC) !== null && _a !== void 0 ? _a : true,
44
+ patchConsole: (_b = options.patchConsole) !== null && _b !== void 0 ? _b : canPatchConsole
45
+ };
46
+ if (options.stdin) {
47
+ renderOptions.stdin = options.stdin;
48
+ }
49
+ if (options.stdout) {
50
+ renderOptions.stdout = options.stdout;
51
+ }
52
+ if (options.stderr) {
53
+ renderOptions.stderr = options.stderr;
54
+ }
55
+ inkInstance = render(element, renderOptions);
56
+ }
57
+ else {
58
+ inkInstance.clear();
59
+ inkInstance.rerender(element);
60
+ }
61
+ };
62
+ const handleMessage = (message) => {
63
+ switch (message.type) {
64
+ case "beginRendering": {
65
+ const surface = ensureSurface(message.surfaceId);
66
+ surface.catalogId = message.catalogId;
67
+ surface.renderReady = true;
68
+ renderSurfaces();
69
+ break;
70
+ }
71
+ case "surfaceUpdate": {
72
+ const surface = ensureSurface(message.surfaceId);
73
+ surface.rootComponentId = message.rootComponentId;
74
+ surface.components = message.components.reduce((acc, component) => {
75
+ const normalized = normalizeComponentDef(component);
76
+ if (normalized) {
77
+ acc[normalized.id] = normalized;
78
+ }
79
+ return acc;
80
+ }, {});
81
+ renderSurfaces();
82
+ break;
83
+ }
84
+ case "dataModelUpdate": {
85
+ const surface = ensureSurface(message.surfaceId);
86
+ if (message.dataModel) {
87
+ surface.dataModel = mergeDataModel(surface.dataModel, message.dataModel);
88
+ }
89
+ else if (message.contents) {
90
+ surface.dataModel = applyDataModelUpdate(surface.dataModel, message.path, message.contents);
91
+ }
92
+ renderSurfaces();
93
+ break;
94
+ }
95
+ case "deleteSurface": {
96
+ surfaces.delete(message.surfaceId);
97
+ renderSurfaces();
98
+ break;
99
+ }
100
+ default:
101
+ break;
102
+ }
103
+ };
104
+ const dispose = () => {
105
+ inkInstance === null || inkInstance === void 0 ? void 0 : inkInstance.unmount();
106
+ inkInstance = null;
107
+ };
108
+ return { handleMessage, dispose };
109
+ }
110
+ const A2uiRoot = ({ surface, onUserAction, onLocalDataModelUpdate }) => {
111
+ if (!surface || !surface.rootComponentId) {
112
+ return React.createElement(Text, null, "No surface");
113
+ }
114
+ const tree = buildResolvedTree(surface.components, surface.rootComponentId, surface.dataModel);
115
+ if (!tree) {
116
+ return React.createElement(Text, null, "Invalid surface");
117
+ }
118
+ return React.createElement(FocusProvider, null, React.createElement(FocusInputHandler), React.createElement(RootContainer, null, React.createElement(RenderTree, {
119
+ surfaceId: surface.surfaceId,
120
+ tree,
121
+ dataModel: surface.dataModel,
122
+ onUserAction,
123
+ onLocalDataModelUpdate
124
+ })));
125
+ };
126
+ const FocusInputHandler = () => {
127
+ const focus = useFocusRegistry();
128
+ useInput((input, key) => {
129
+ if (key.tab) {
130
+ if (key.shift) {
131
+ focus.focusPrev();
132
+ }
133
+ else {
134
+ focus.focusNext();
135
+ }
136
+ return;
137
+ }
138
+ focus.handleKey(input, key);
139
+ });
140
+ return null;
141
+ };
142
+ const RootContainer = ({ children }) => {
143
+ var _a, _b;
144
+ const { stdout } = useStdout();
145
+ const width = (_a = stdout === null || stdout === void 0 ? void 0 : stdout.columns) !== null && _a !== void 0 ? _a : undefined;
146
+ const height = (_b = stdout === null || stdout === void 0 ? void 0 : stdout.rows) !== null && _b !== void 0 ? _b : undefined;
147
+ return React.createElement(Box, { width, height, flexDirection: "column" }, children);
148
+ };
149
+ const RenderTree = ({ surfaceId, tree, dataModel, onUserAction, onLocalDataModelUpdate }) => {
150
+ const dispatchAction = (action, node, value) => {
151
+ var _a, _b;
152
+ const context = {
153
+ ...resolveActionContext(action.context, dataModel, node.bindingContext),
154
+ ...(((_a = node.bindingContext) === null || _a === void 0 ? void 0 : _a.index) !== undefined ? { index: node.bindingContext.index } : {}),
155
+ ...(((_b = node.bindingContext) === null || _b === void 0 ? void 0 : _b.item) !== undefined ? { item: node.bindingContext.item } : {})
156
+ };
157
+ onUserAction === null || onUserAction === void 0 ? void 0 : onUserAction({
158
+ type: "userAction",
159
+ surfaceId,
160
+ componentId: node.id,
161
+ actionId: action.actionId,
162
+ context,
163
+ value
164
+ });
165
+ };
166
+ const updateLocalDataModel = (path, value, node) => {
167
+ onLocalDataModelUpdate === null || onLocalDataModelUpdate === void 0 ? void 0 : onLocalDataModelUpdate(surfaceId, path, value, node);
168
+ };
169
+ return renderNode(tree, { dispatchAction, updateLocalDataModel });
170
+ };
171
+ function resolveActionContext(context, dataModel, bindingContext) {
172
+ if (!context) {
173
+ return {};
174
+ }
175
+ const resolved = {};
176
+ for (const [key, value] of Object.entries(context)) {
177
+ if (isBoundValueLike(value)) {
178
+ resolved[key] = resolveBoundValue(value, dataModel, bindingContext);
179
+ }
180
+ else {
181
+ resolved[key] = value;
182
+ }
183
+ }
184
+ return resolved;
185
+ }
186
+ function isBoundValueLike(value) {
187
+ if (!value || typeof value !== "object") {
188
+ return false;
189
+ }
190
+ return ("path" in value ||
191
+ "literalString" in value ||
192
+ "literalNumber" in value ||
193
+ "literalBoolean" in value ||
194
+ "literalObject" in value ||
195
+ "literalArray" in value);
196
+ }
197
+ function mergeDataModel(base, update) {
198
+ const result = { ...base };
199
+ for (const [key, value] of Object.entries(update)) {
200
+ if (isPlainObject(value) && isPlainObject(result[key])) {
201
+ result[key] = mergeDataModel(result[key], value);
202
+ }
203
+ else {
204
+ result[key] = value;
205
+ }
206
+ }
207
+ return result;
208
+ }
209
+ function isPlainObject(value) {
210
+ return !!value && typeof value === "object" && !Array.isArray(value);
211
+ }
212
+ function normalizeComponentDef(component) {
213
+ if (!component || typeof component !== "object") {
214
+ return null;
215
+ }
216
+ const record = component;
217
+ if (typeof record.id !== "string") {
218
+ return null;
219
+ }
220
+ if (typeof record.type === "string") {
221
+ const normalizedProps = normalizeBoundValuesDeep(record.props);
222
+ if (normalizedProps) {
223
+ if (normalizedProps.action && !normalizedProps.onPress && record.type === "Button") {
224
+ normalizedProps.onPress = normalizeAction(normalizedProps.action);
225
+ delete normalizedProps.action;
226
+ }
227
+ if (normalizedProps.action && !normalizedProps.onChange && (record.type === "Checkbox" || record.type === "CheckBox")) {
228
+ normalizedProps.onChange = normalizeAction(normalizedProps.action);
229
+ delete normalizedProps.action;
230
+ }
231
+ }
232
+ return {
233
+ id: record.id,
234
+ type: record.type,
235
+ props: normalizedProps,
236
+ children: normalizeChildren(record.children)
237
+ };
238
+ }
239
+ if (record.component && typeof record.component === "object") {
240
+ const [type, props] = extractComponentType(record.component);
241
+ if (!type) {
242
+ return null;
243
+ }
244
+ const { normalizedType, normalizedProps, children } = normalizeComponentShape(type, props);
245
+ if (!normalizedType) {
246
+ return null;
247
+ }
248
+ return {
249
+ id: record.id,
250
+ type: normalizedType,
251
+ props: normalizedProps,
252
+ children
253
+ };
254
+ }
255
+ return null;
256
+ }
257
+ function extractComponentType(componentObject) {
258
+ var _a, _b, _c;
259
+ if (typeof componentObject.type === "string") {
260
+ return [componentObject.type, (_a = componentObject.props) !== null && _a !== void 0 ? _a : {}];
261
+ }
262
+ const entries = Object.entries(componentObject);
263
+ if (entries.length === 1) {
264
+ const [type, props] = entries[0];
265
+ return [type, (_b = props) !== null && _b !== void 0 ? _b : {}];
266
+ }
267
+ const candidate = entries.find(([key, value]) => typeof key === "string" && typeof value === "object");
268
+ if (candidate) {
269
+ return [candidate[0], (_c = candidate[1]) !== null && _c !== void 0 ? _c : {}];
270
+ }
271
+ return [null, {}];
272
+ }
273
+ function normalizeComponentShape(type, props) {
274
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
275
+ const nextProps = normalizeBoundValuesDeep({ ...(props !== null && props !== void 0 ? props : {}) });
276
+ let children = undefined;
277
+ if (nextProps.children) {
278
+ children = normalizeChildren(nextProps.children);
279
+ delete nextProps.children;
280
+ }
281
+ if (nextProps.child) {
282
+ children = normalizeChildren({ explicitList: [nextProps.child] });
283
+ delete nextProps.child;
284
+ }
285
+ if (nextProps.action && !nextProps.onPress && type === "Button") {
286
+ nextProps.onPress = normalizeAction(nextProps.action);
287
+ delete nextProps.action;
288
+ }
289
+ if (nextProps.action && !nextProps.onChange && (type === "Checkbox" || type === "CheckBox")) {
290
+ nextProps.onChange = normalizeAction(nextProps.action);
291
+ delete nextProps.action;
292
+ }
293
+ if (type === "Text" && nextProps.usageHint && nextProps.bold === undefined) {
294
+ const hint = String(nextProps.usageHint).toLowerCase();
295
+ if (hint === "h1" || hint === "h2" || hint === "h3") {
296
+ nextProps.bold = true;
297
+ }
298
+ }
299
+ if (type === "Row" || type === "Column") {
300
+ const direction = type === "Row" ? "row" : "column";
301
+ const alignItems = mapAlignment(nextProps.alignment);
302
+ const justifyContent = mapDistribution(nextProps.distribution);
303
+ return {
304
+ normalizedType: "Box",
305
+ normalizedProps: {
306
+ ...nextProps,
307
+ direction,
308
+ alignItems,
309
+ justifyContent
310
+ },
311
+ children
312
+ };
313
+ }
314
+ if (type === "Card") {
315
+ return {
316
+ normalizedType: "Box",
317
+ normalizedProps: {
318
+ direction: "column",
319
+ borderStyle: "round",
320
+ padding: 1,
321
+ ...nextProps
322
+ },
323
+ children
324
+ };
325
+ }
326
+ if (type === "Divider") {
327
+ return {
328
+ normalizedType: "Divider",
329
+ normalizedProps: nextProps,
330
+ children: undefined
331
+ };
332
+ }
333
+ if (type === "Icon") {
334
+ return {
335
+ normalizedType: "Text",
336
+ normalizedProps: {
337
+ text: resolveIconText(nextProps.name)
338
+ },
339
+ children: undefined
340
+ };
341
+ }
342
+ if (type === "Image") {
343
+ return {
344
+ normalizedType: "Image",
345
+ normalizedProps: nextProps,
346
+ children: undefined
347
+ };
348
+ }
349
+ if (type === "TextField") {
350
+ const textValue = (_a = nextProps.value) !== null && _a !== void 0 ? _a : nextProps.text;
351
+ return {
352
+ normalizedType: "TextField",
353
+ normalizedProps: {
354
+ label: nextProps.label,
355
+ value: textValue,
356
+ placeholder: (_b = nextProps.placeholder) !== null && _b !== void 0 ? _b : resolveLiteralString(nextProps.label),
357
+ onChange: nextProps.onChange,
358
+ onSubmit: nextProps.onSubmit
359
+ },
360
+ children: undefined
361
+ };
362
+ }
363
+ if (type === "CheckBox") {
364
+ return {
365
+ normalizedType: "Checkbox",
366
+ normalizedProps: {
367
+ label: nextProps.label,
368
+ checked: (_c = nextProps.checked) !== null && _c !== void 0 ? _c : nextProps.value,
369
+ onChange: (_d = nextProps.onChange) !== null && _d !== void 0 ? _d : normalizeAction(nextProps.action)
370
+ },
371
+ children: undefined
372
+ };
373
+ }
374
+ if (type === "MultipleChoice") {
375
+ return {
376
+ normalizedType: "MultipleChoice",
377
+ normalizedProps: {
378
+ label: nextProps.label,
379
+ items: normalizeMultipleChoiceOptions((_e = nextProps.options) !== null && _e !== void 0 ? _e : []),
380
+ onSelect: normalizeAction(nextProps.action)
381
+ },
382
+ children: undefined
383
+ };
384
+ }
385
+ if (type === "Slider") {
386
+ return {
387
+ normalizedType: "Slider",
388
+ normalizedProps: {
389
+ label: nextProps.label,
390
+ min: (_f = nextProps.min) !== null && _f !== void 0 ? _f : nextProps.minValue,
391
+ max: (_g = nextProps.max) !== null && _g !== void 0 ? _g : nextProps.maxValue,
392
+ step: nextProps.step,
393
+ value: nextProps.value,
394
+ onChange: (_h = nextProps.onChange) !== null && _h !== void 0 ? _h : normalizeAction(nextProps.action)
395
+ },
396
+ children: undefined
397
+ };
398
+ }
399
+ if (type === "Tabs") {
400
+ const tabItems = Array.isArray(nextProps.tabItems) ? nextProps.tabItems : [];
401
+ const tabLabels = tabItems.map((item) => { var _a; return (_a = resolveLiteralString(item === null || item === void 0 ? void 0 : item.title)) !== null && _a !== void 0 ? _a : "Tab"; });
402
+ if (!children && tabItems.length) {
403
+ const childIds = tabItems
404
+ .map((item) => item === null || item === void 0 ? void 0 : item.child)
405
+ .filter((entry) => typeof entry === "string");
406
+ if (childIds.length) {
407
+ children = { explicitList: childIds };
408
+ }
409
+ }
410
+ return {
411
+ normalizedType: "Tabs",
412
+ normalizedProps: {
413
+ tabs: tabLabels,
414
+ selectedIndex: (_j = nextProps.selectedIndex) !== null && _j !== void 0 ? _j : 0,
415
+ onChange: (_k = nextProps.onChange) !== null && _k !== void 0 ? _k : normalizeAction(nextProps.action)
416
+ },
417
+ children
418
+ };
419
+ }
420
+ if (type === "Modal") {
421
+ return {
422
+ normalizedType: "Modal",
423
+ normalizedProps: nextProps,
424
+ children
425
+ };
426
+ }
427
+ return {
428
+ normalizedType: type,
429
+ normalizedProps: nextProps,
430
+ children
431
+ };
432
+ }
433
+ function normalizeChildren(children) {
434
+ if (!children || typeof children !== "object")
435
+ return children;
436
+ const record = children;
437
+ if (Array.isArray(record.explicitList)) {
438
+ return { explicitList: record.explicitList };
439
+ }
440
+ if (record.template && typeof record.template === "object") {
441
+ const template = record.template;
442
+ const dataBinding = template.dataBinding;
443
+ return {
444
+ template: {
445
+ componentId: template.componentId,
446
+ dataBinding: typeof dataBinding === "string" ? dataBinding : dataBinding
447
+ }
448
+ };
449
+ }
450
+ return children;
451
+ }
452
+ function normalizeAction(action) {
453
+ if (!action || typeof action !== "object")
454
+ return undefined;
455
+ const record = action;
456
+ if (record.actionId && typeof record.actionId === "string") {
457
+ return { actionId: record.actionId, context: record.context };
458
+ }
459
+ if (record.name && typeof record.name === "string") {
460
+ return {
461
+ actionId: record.name,
462
+ context: normalizeActionContext(record.context)
463
+ };
464
+ }
465
+ return undefined;
466
+ }
467
+ function normalizeActionContext(context) {
468
+ if (!context)
469
+ return undefined;
470
+ if (Array.isArray(context)) {
471
+ const result = {};
472
+ context.forEach((entry) => {
473
+ var _a, _b;
474
+ if (!entry || typeof entry !== "object")
475
+ return;
476
+ const record = entry;
477
+ if (!record.key)
478
+ return;
479
+ result[String(record.key)] = (_b = (_a = record.value) !== null && _a !== void 0 ? _a : record.literal) !== null && _b !== void 0 ? _b : record;
480
+ });
481
+ return result;
482
+ }
483
+ if (typeof context === "object") {
484
+ return context;
485
+ }
486
+ return undefined;
487
+ }
488
+ function normalizeBoundValuesDeep(value) {
489
+ if (!value || typeof value !== "object") {
490
+ return value;
491
+ }
492
+ if (Array.isArray(value)) {
493
+ return value.map((item) => normalizeBoundValuesDeep(item));
494
+ }
495
+ if (isBoundValueLike(value)) {
496
+ return value;
497
+ }
498
+ const result = {};
499
+ Object.entries(value).forEach(([key, entry]) => {
500
+ result[key] = normalizeBoundValuesDeep(entry);
501
+ });
502
+ return result;
503
+ }
504
+ function resolveLiteralString(value) {
505
+ if (!value || typeof value !== "object")
506
+ return undefined;
507
+ const record = value;
508
+ if (typeof record.literalString === "string") {
509
+ return record.literalString;
510
+ }
511
+ return undefined;
512
+ }
513
+ function normalizeMultipleChoiceOptions(options) {
514
+ if (!Array.isArray(options)) {
515
+ return options;
516
+ }
517
+ return options.map((option) => {
518
+ var _a;
519
+ if (!option || typeof option !== "object") {
520
+ return option;
521
+ }
522
+ const record = option;
523
+ const label = (_a = resolveLiteralString(record.label)) !== null && _a !== void 0 ? _a : record.label;
524
+ return {
525
+ ...record,
526
+ label
527
+ };
528
+ });
529
+ }
530
+ function resolveIconText(nameBinding) {
531
+ var _a;
532
+ const map = {
533
+ flight: "✈",
534
+ airplane: "✈",
535
+ plane: "✈",
536
+ calendar: "📅",
537
+ time: "⏰",
538
+ favorite: "★",
539
+ star: "★"
540
+ };
541
+ if (!nameBinding || typeof nameBinding !== "object") {
542
+ return { literalString: "•" };
543
+ }
544
+ const record = nameBinding;
545
+ if (typeof record.literalString === "string") {
546
+ const key = record.literalString.toLowerCase();
547
+ return { literalString: (_a = map[key]) !== null && _a !== void 0 ? _a : record.literalString };
548
+ }
549
+ if (typeof record.path === "string") {
550
+ return { path: record.path };
551
+ }
552
+ return { literalString: "•" };
553
+ }
554
+ function mapAlignment(alignment) {
555
+ if (!alignment)
556
+ return undefined;
557
+ const normalized = alignment.toLowerCase();
558
+ switch (normalized) {
559
+ case "start":
560
+ case "flexstart":
561
+ case "flex-start":
562
+ return "flex-start";
563
+ case "end":
564
+ case "flexend":
565
+ case "flex-end":
566
+ return "flex-end";
567
+ case "center":
568
+ return "center";
569
+ case "stretch":
570
+ return "stretch";
571
+ default:
572
+ return undefined;
573
+ }
574
+ }
575
+ function mapDistribution(distribution) {
576
+ if (!distribution)
577
+ return undefined;
578
+ const normalized = distribution.toLowerCase();
579
+ switch (normalized) {
580
+ case "spacebetween":
581
+ case "space-between":
582
+ return "space-between";
583
+ case "spacearound":
584
+ case "space-around":
585
+ return "space-around";
586
+ case "center":
587
+ return "center";
588
+ case "start":
589
+ case "flexstart":
590
+ case "flex-start":
591
+ return "flex-start";
592
+ case "end":
593
+ case "flexend":
594
+ case "flex-end":
595
+ return "flex-end";
596
+ default:
597
+ return undefined;
598
+ }
599
+ }
600
+ function applyDataModelUpdate(existing, path, contents) {
601
+ var _a;
602
+ const next = { ...(existing !== null && existing !== void 0 ? existing : {}) };
603
+ const updatedValue = buildValueFromContents(contents);
604
+ const parts = normalizePath(path);
605
+ if (!parts.length) {
606
+ if (isPlainObject(updatedValue)) {
607
+ return { ...next, ...updatedValue };
608
+ }
609
+ return (_a = updatedValue) !== null && _a !== void 0 ? _a : next;
610
+ }
611
+ let cursor = next;
612
+ for (let i = 0; i < parts.length - 1; i += 1) {
613
+ const key = parts[i];
614
+ if (!cursor[key] || typeof cursor[key] !== "object") {
615
+ cursor[key] = {};
616
+ }
617
+ cursor = cursor[key];
618
+ }
619
+ cursor[parts[parts.length - 1]] = updatedValue;
620
+ return next;
621
+ }
622
+ function normalizePath(path) {
623
+ if (!path || path === "/") {
624
+ return [];
625
+ }
626
+ const trimmed = String(path).replace(/^\/+/, "");
627
+ if (!trimmed) {
628
+ return [];
629
+ }
630
+ return trimmed.split("/").filter(Boolean);
631
+ }
632
+ function buildValueFromContents(contents) {
633
+ if (!Array.isArray(contents)) {
634
+ return contents;
635
+ }
636
+ const result = {};
637
+ contents.forEach((entry) => {
638
+ if (!entry || typeof entry !== "object") {
639
+ return;
640
+ }
641
+ result[entry.key] = extractTypedValue(entry);
642
+ });
643
+ return result;
644
+ }
645
+ function extractTypedValue(entry) {
646
+ var _a, _b;
647
+ if ("valueString" in entry)
648
+ return entry.valueString;
649
+ if ("valueNumber" in entry)
650
+ return entry.valueNumber;
651
+ if ("valueBoolean" in entry)
652
+ return entry.valueBoolean;
653
+ if ("valueNull" in entry)
654
+ return null;
655
+ if ("valueMap" in entry)
656
+ return buildValueFromContents((_a = entry.valueMap) !== null && _a !== void 0 ? _a : []);
657
+ if ("valueArray" in entry)
658
+ return normalizeValueArray((_b = entry.valueArray) !== null && _b !== void 0 ? _b : []);
659
+ if ("value" in entry)
660
+ return entry.value;
661
+ return undefined;
662
+ }
663
+ function normalizeValueArray(valueArray) {
664
+ return valueArray.map((item) => {
665
+ if (item && typeof item === "object" && "key" in item) {
666
+ return extractTypedValue(item);
667
+ }
668
+ if (item && typeof item === "object" && ("valueString" in item || "valueNumber" in item || "valueBoolean" in item || "valueMap" in item || "valueArray" in item || "valueNull" in item)) {
669
+ return extractTypedValue(item);
670
+ }
671
+ return item;
672
+ });
673
+ }
@@ -0,0 +1,2 @@
1
+ import type { ComponentDef, ResolvedNode } from "./types.js";
2
+ export declare function buildResolvedTree(components: Record<string, ComponentDef>, rootId: string, dataModel: Record<string, unknown>): ResolvedNode | null;