@allternit/ix 0.1.0 → 0.1.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 (103) hide show
  1. package/README.md +6 -6
  2. package/dist/adapters/json-render.d.ts +5 -5
  3. package/dist/adapters/json-render.js +12 -12
  4. package/dist/collab/visual-editor.d.ts +1 -1
  5. package/dist/collab/visual-editor.js +4 -4
  6. package/dist/collab/visual-editor.js.map +1 -1
  7. package/dist/collab/yjs-adapter.d.ts +1 -1
  8. package/dist/collab/yjs-adapter.js +2 -2
  9. package/dist/collab/yjs-adapter.js.map +1 -1
  10. package/dist/collab/yjs-client.js +1 -1
  11. package/dist/collab/yjs-client.js.map +1 -1
  12. package/dist/collab/yjs-types.js +1 -1
  13. package/dist/collab/yjs-types.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/pipeline/llm-to-ix.d.ts +5 -5
  17. package/dist/pipeline/llm-to-ix.d.ts.map +1 -1
  18. package/dist/pipeline/llm-to-ix.js +6 -6
  19. package/dist/react/renderer.d.ts +2 -2
  20. package/dist/react/renderer.js +3 -3
  21. package/dist/react/renderer.js.map +1 -1
  22. package/dist/renderer/renderer.d.ts +1 -1
  23. package/dist/renderer/renderer.js +1 -1
  24. package/dist/runtime/capsule-runtime.d.ts +1 -1
  25. package/dist/runtime/capsule-runtime.js +1 -1
  26. package/dist/sdk/api.d.ts +3 -3
  27. package/dist/sdk/api.js +3 -3
  28. package/dist/sdk/index.d.ts +2 -2
  29. package/dist/sdk/index.js +2 -2
  30. package/dist/sdk/react.d.ts +2 -2
  31. package/dist/sdk/react.js +2 -2
  32. package/dist/state/index.d.ts +1 -1
  33. package/dist/state/index.js +1 -1
  34. package/dist/state/store.d.ts +1 -1
  35. package/dist/state/store.js +3 -3
  36. package/dist/state/store.js.map +1 -1
  37. package/dist/svelte/index.d.ts +1 -1
  38. package/dist/svelte/index.js +1 -1
  39. package/dist/svelte/renderer.d.ts +3 -3
  40. package/dist/svelte/renderer.js +4 -4
  41. package/dist/svelte/renderer.js.map +1 -1
  42. package/dist/types/index.d.ts +1 -1
  43. package/dist/types/index.js +1 -1
  44. package/dist/vue/renderer.d.ts +3 -3
  45. package/dist/vue/renderer.js +5 -5
  46. package/dist/vue/renderer.js.map +1 -1
  47. package/package.json +8 -8
  48. package/dist/adapters/json-render.d.ts.bak +0 -211
  49. package/dist/adapters/json-render.js.bak +0 -576
  50. package/dist/catalog/registry.d.ts.bak +0 -65
  51. package/dist/catalog/registry.js.bak +0 -388
  52. package/dist/collab/capsule-registry.d.ts.bak +0 -114
  53. package/dist/collab/capsule-registry.js.bak +0 -271
  54. package/dist/collab/index.d.ts.bak +0 -21
  55. package/dist/collab/index.js.bak +0 -32
  56. package/dist/collab/visual-editor.d.ts.bak +0 -199
  57. package/dist/collab/visual-editor.js.bak +0 -259
  58. package/dist/collab/yjs-adapter.d.ts.bak +0 -95
  59. package/dist/collab/yjs-adapter.js.bak +0 -282
  60. package/dist/collab/yjs-client.d.ts.bak +0 -101
  61. package/dist/collab/yjs-client.js.bak +0 -193
  62. package/dist/collab/yjs-protocols.d.ts.bak +0 -128
  63. package/dist/collab/yjs-protocols.js.bak +0 -245
  64. package/dist/collab/yjs-types.d.ts.bak +0 -201
  65. package/dist/collab/yjs-types.js.bak +0 -45
  66. package/dist/index.d.ts.bak +0 -38
  67. package/dist/index.js.bak +0 -48
  68. package/dist/pipeline/llm-to-ix.d.ts.bak +0 -249
  69. package/dist/pipeline/llm-to-ix.js.bak +0 -992
  70. package/dist/react/index.d.ts.bak +0 -18
  71. package/dist/react/index.js.bak +0 -18
  72. package/dist/react/renderer.d.ts.bak +0 -47
  73. package/dist/react/renderer.js.bak +0 -420
  74. package/dist/renderer/renderer.d.ts.bak +0 -55
  75. package/dist/renderer/renderer.js.bak +0 -232
  76. package/dist/runtime/capsule-runtime.d.ts.bak +0 -227
  77. package/dist/runtime/capsule-runtime.js.bak +0 -563
  78. package/dist/runtime/policy-gates.d.ts.bak +0 -116
  79. package/dist/runtime/policy-gates.js.bak +0 -317
  80. package/dist/schema/ui-ir.d.ts.bak +0 -26
  81. package/dist/schema/ui-ir.js.bak +0 -51
  82. package/dist/sdk/api.d.ts.bak +0 -101
  83. package/dist/sdk/api.js.bak +0 -177
  84. package/dist/sdk/index.d.ts.bak +0 -9
  85. package/dist/sdk/index.js.bak +0 -10
  86. package/dist/sdk/react.d.ts.bak +0 -63
  87. package/dist/sdk/react.js.bak +0 -161
  88. package/dist/state/index.d.ts.bak +0 -8
  89. package/dist/state/index.js.bak +0 -8
  90. package/dist/state/patch.d.ts.bak +0 -73
  91. package/dist/state/patch.js.bak +0 -437
  92. package/dist/state/store.d.ts.bak +0 -50
  93. package/dist/state/store.js.bak +0 -246
  94. package/dist/svelte/index.d.ts.bak +0 -15
  95. package/dist/svelte/index.js.bak +0 -15
  96. package/dist/svelte/renderer.d.ts.bak +0 -35
  97. package/dist/svelte/renderer.js.bak +0 -317
  98. package/dist/types/index.d.ts.bak +0 -349
  99. package/dist/types/index.js.bak +0 -152
  100. package/dist/vue/index.d.ts.bak +0 -18
  101. package/dist/vue/index.js.bak +0 -18
  102. package/dist/vue/renderer.d.ts.bak +0 -46
  103. package/dist/vue/renderer.js.bak +0 -302
@@ -1,992 +0,0 @@
1
- /**
2
- * LLM-to-IX Pipeline
3
- *
4
- * Converts LLM-generated UI descriptions to A2R-IX format.
5
- *
6
- * Supports multiple input formats:
7
- * - Natural language descriptions
8
- * - Vercel Labs json-render
9
- * - JSX-like pseudo-code
10
- * - Raw component trees
11
- * - Markdown UI descriptions
12
- *
13
- * @example
14
- * ```typescript
15
- * import { llmToIxPipeline } from '@allternit/ix/pipeline';
16
- *
17
- * const result = llmToIxPipeline({
18
- * input: llmOutput,
19
- * options: {
20
- * applyStyling: true,
21
- * theme: 'default',
22
- * generateIds: true,
23
- * }
24
- * });
25
- *
26
- * console.log(result.tree); // IxRenderTree
27
- * ```
28
- */
29
- import { jsonRenderAdapter } from '../adapters/json-render';
30
- /**
31
- * Convert LLM output to A2R-IX
32
- */
33
- export function convertLLMToIX(input, options = {}) {
34
- const warnings = [];
35
- const inferredState = [];
36
- // Detect input format
37
- const format = detectInputFormat(input);
38
- switch (format) {
39
- case 'json-render':
40
- return convertFromJsonRender(input, options);
41
- case 'jsx-like':
42
- return convertFromJSXLike(input, options);
43
- case 'component-tree':
44
- return convertFromComponentTree(input, options);
45
- case 'natural-language':
46
- return convertFromNaturalLanguage(input, options);
47
- default:
48
- warnings.push('Unknown input format, attempting generic conversion');
49
- return convertGeneric(input, options);
50
- }
51
- }
52
- /**
53
- * Detect input format
54
- */
55
- function detectInputFormat(input) {
56
- if (!input)
57
- return 'unknown';
58
- // Check for json-render
59
- if (jsonRenderAdapter.isValidSchema(input)) {
60
- return 'json-render';
61
- }
62
- // Check for JSX-like (string with JSX syntax)
63
- if (typeof input === 'string') {
64
- const trimmed = input.trim();
65
- if (trimmed.startsWith('<') && trimmed.endsWith('>')) {
66
- return 'jsx-like';
67
- }
68
- if (trimmed.length >= 40 && !trimmed.startsWith('{')) {
69
- return 'natural-language';
70
- }
71
- }
72
- // Check for component tree object (including empty object)
73
- if (typeof input === 'object' && input !== null && !Array.isArray(input)) {
74
- const obj = input;
75
- if (obj.components || obj.type || obj.root || Object.keys(obj).length === 0) {
76
- return 'component-tree';
77
- }
78
- }
79
- return 'unknown';
80
- }
81
- /**
82
- * Convert from json-render format
83
- */
84
- function convertFromJsonRender(schema, options) {
85
- const warnings = [];
86
- const inferredState = [];
87
- const ui = jsonRenderAdapter.fromJsonRender(schema);
88
- // Add IDs if needed
89
- if (options.generateIds) {
90
- addComponentIds(ui.components);
91
- }
92
- // Infer state from bindings
93
- if (options.inferState) {
94
- inferStateFromBindings(ui, inferredState);
95
- }
96
- return {
97
- ui,
98
- warnings,
99
- inferredState,
100
- confidence: 0.95,
101
- };
102
- }
103
- /**
104
- * Convert from JSX-like string
105
- */
106
- function convertFromJSXLike(jsxString, options) {
107
- const warnings = [];
108
- const inferredState = [];
109
- try {
110
- const parsed = parseJSXLike(jsxString);
111
- // Check if parsing produced any components
112
- if (parsed.length === 0) {
113
- warnings.push('No valid JSX components found');
114
- return {
115
- ui: createEmptyUI(options.version),
116
- warnings,
117
- inferredState,
118
- confidence: 0.3,
119
- };
120
- }
121
- const components = parsed.map((node) => jsxNodeToComponent(node));
122
- const ui = {
123
- version: options.version || '1.0.0',
124
- components,
125
- state: { variables: [] },
126
- actions: [],
127
- };
128
- // Infer state
129
- if (options.inferState) {
130
- inferStateFromBindings(ui, inferredState);
131
- }
132
- return {
133
- ui,
134
- warnings,
135
- inferredState,
136
- confidence: 0.85,
137
- };
138
- }
139
- catch (error) {
140
- warnings.push(`Failed to parse JSX: ${error}`);
141
- return {
142
- ui: createEmptyUI(options.version),
143
- warnings,
144
- inferredState,
145
- confidence: 0.3,
146
- };
147
- }
148
- }
149
- /**
150
- * Parse JSX-like string to AST
151
- */
152
- function parseJSXLike(jsx) {
153
- // Simple JSX parser - in production, use a proper parser
154
- const nodes = [];
155
- const tagRegex = /<(\w+)([^>]*)>([\s\S]*?)<\/\1>|<(\w+)([^>]*)\/>/g;
156
- let match;
157
- while ((match = tagRegex.exec(jsx)) !== null) {
158
- const type = (match[1] || match[4]) ?? 'div';
159
- const propsString = match[2] || match[5] || '';
160
- const children = match[3]?.trim();
161
- const props = parseProps(propsString);
162
- const childNodes = children ? parseJSXLike(children) : [];
163
- nodes.push({
164
- type,
165
- props,
166
- children: childNodes,
167
- text: childNodes.length === 0 ? children : undefined,
168
- });
169
- }
170
- return nodes;
171
- }
172
- /**
173
- * Parse props string
174
- */
175
- function parseProps(propsString) {
176
- const props = {};
177
- // Support $ prefix for bindings: $value={path}
178
- const propRegex = /(\$?\w+)={([^}]*)}|(\$?\w+)="([^"]*)"|(\$?\w+)='([^']*)'|(\$?\w+)/g;
179
- let match;
180
- while ((match = propRegex.exec(propsString)) !== null) {
181
- const key = (match[1] || match[3] || match[5] || match[7]) ?? '';
182
- if (!key)
183
- continue;
184
- let value = true; // Boolean prop
185
- if (match[2]) {
186
- // Expression: prop={value}
187
- try {
188
- value = JSON.parse(match[2]);
189
- }
190
- catch {
191
- value = match[2]; // Raw string for expressions like user.name
192
- }
193
- }
194
- else if (match[4]) {
195
- // Double quoted: prop="value"
196
- value = match[4];
197
- }
198
- else if (match[6]) {
199
- // Single quoted: prop='value'
200
- value = match[6];
201
- }
202
- props[key] = value;
203
- }
204
- return props;
205
- }
206
- /**
207
- * Convert JSX node to UIComponent
208
- */
209
- function jsxNodeToComponent(node, parentId = 'root') {
210
- const id = generateId();
211
- const component = {
212
- id,
213
- type: node.type,
214
- props: {},
215
- };
216
- // Process props
217
- const bindings = [];
218
- const events = [];
219
- for (const [key, value] of Object.entries(node.props)) {
220
- // Check for binding (starts with $)
221
- if (key.startsWith('$')) {
222
- const propName = key.slice(1);
223
- // Handle both string values and object expressions
224
- const statePath = typeof value === 'string'
225
- ? value
226
- : value?.$expr || String(value);
227
- bindings.push({
228
- prop: propName,
229
- statePath,
230
- direction: 'one-way',
231
- });
232
- }
233
- // Check for event handler
234
- else if (key.startsWith('on')) {
235
- events.push({
236
- event: key,
237
- action: value,
238
- });
239
- }
240
- // Regular prop
241
- else {
242
- component.props[key] = value;
243
- }
244
- }
245
- if (bindings.length > 0) {
246
- component.bindings = bindings;
247
- }
248
- if (events.length > 0) {
249
- component.events = events;
250
- }
251
- // Process children
252
- if (node.children.length > 0) {
253
- component.children = node.children.map((child) => jsxNodeToComponent(child, id));
254
- }
255
- else if (node.text) {
256
- component.props.children = node.text;
257
- }
258
- return component;
259
- }
260
- /**
261
- * Convert from component tree object
262
- */
263
- function convertFromComponentTree(tree, options) {
264
- const warnings = [];
265
- const inferredState = [];
266
- try {
267
- // Handle empty object
268
- if (Object.keys(tree).length === 0) {
269
- return {
270
- ui: createEmptyUI(options.version),
271
- warnings: ['Empty input provided'],
272
- inferredState,
273
- confidence: 0.1,
274
- };
275
- }
276
- const components = tree.components
277
- ? tree.components.map((c) => convertTreeNode(c))
278
- : tree.type
279
- ? [convertTreeNode(tree)]
280
- : [];
281
- const ui = {
282
- version: options.version || '1.0.0',
283
- components,
284
- state: { variables: [] },
285
- actions: [],
286
- };
287
- return {
288
- ui,
289
- warnings,
290
- inferredState,
291
- confidence: 0.8,
292
- };
293
- }
294
- catch (error) {
295
- warnings.push(`Failed to convert tree: ${error}`);
296
- return {
297
- ui: createEmptyUI(options.version),
298
- warnings,
299
- inferredState,
300
- confidence: 0.4,
301
- };
302
- }
303
- }
304
- /**
305
- * Convert tree node to component
306
- */
307
- function convertTreeNode(node) {
308
- const component = {
309
- id: generateId(),
310
- type: node.type || 'Box',
311
- props: node.props || {},
312
- };
313
- if (node.children) {
314
- component.children = node.children.map((c) => convertTreeNode(c));
315
- }
316
- if (node.bindings) {
317
- component.bindings = node.bindings;
318
- }
319
- if (node.events) {
320
- component.events = node.events;
321
- }
322
- return component;
323
- }
324
- /**
325
- * Convert from natural language
326
- */
327
- function convertFromNaturalLanguage(description, options) {
328
- const warnings = [];
329
- const inferredState = [];
330
- warnings.push('Natural language conversion requires LLM assistance');
331
- // This would typically call an LLM to generate the UI
332
- // For now, return a placeholder
333
- const ui = {
334
- version: options.version || '1.0.0',
335
- components: [
336
- {
337
- id: generateId(),
338
- type: 'Text',
339
- props: { children: 'Natural language conversion not yet implemented' },
340
- },
341
- ],
342
- state: { variables: [] },
343
- actions: [],
344
- };
345
- return {
346
- ui,
347
- warnings,
348
- inferredState,
349
- confidence: 0.1,
350
- };
351
- }
352
- /**
353
- * Generic conversion fallback
354
- */
355
- function convertGeneric(input, options) {
356
- const warnings = ['Using generic conversion'];
357
- const inferredState = [];
358
- // Try to coerce to component structure
359
- let components = [];
360
- if (Array.isArray(input)) {
361
- components = input.map((item, i) => ({
362
- id: `comp-${i}`,
363
- type: item?.type || 'Box',
364
- props: item?.props || {},
365
- }));
366
- }
367
- else if (typeof input === 'object' && input !== null) {
368
- components = [{
369
- id: generateId(),
370
- type: input.type || 'Box',
371
- props: input.props || {},
372
- }];
373
- }
374
- const ui = {
375
- version: options.version || '1.0.0',
376
- components,
377
- state: { variables: [] },
378
- actions: [],
379
- };
380
- return {
381
- ui,
382
- warnings,
383
- inferredState,
384
- confidence: 0.5,
385
- };
386
- }
387
- /**
388
- * Add component IDs
389
- */
390
- function addComponentIds(components, prefix = 'comp') {
391
- components.forEach((comp, i) => {
392
- if (!comp.id) {
393
- comp.id = `${prefix}-${i}`;
394
- }
395
- if (comp.children) {
396
- addComponentIds(comp.children, comp.id);
397
- }
398
- });
399
- }
400
- /**
401
- * Infer state from bindings
402
- */
403
- function inferStateFromBindings(ui, inferredState) {
404
- const statePaths = new Set();
405
- function scanComponent(comp) {
406
- comp.bindings?.forEach((binding) => {
407
- statePaths.add(binding.statePath);
408
- });
409
- comp.children?.forEach(scanComponent);
410
- }
411
- ui.components.forEach(scanComponent);
412
- statePaths.forEach((path) => {
413
- const existing = ui.state.variables.find((v) => v.path === path);
414
- if (!existing) {
415
- inferredState.push({
416
- path,
417
- type: 'any',
418
- });
419
- }
420
- });
421
- }
422
- /**
423
- * Generate unique ID
424
- */
425
- function generateId() {
426
- return `comp_${Math.random().toString(36).substr(2, 9)}`;
427
- }
428
- /**
429
- * Create empty UI
430
- */
431
- function createEmptyUI(version) {
432
- return {
433
- version: version || '1.0.0',
434
- components: [],
435
- state: { variables: [] },
436
- actions: [],
437
- };
438
- }
439
- /**
440
- * A2R-IX Built-in Themes
441
- */
442
- const IX_THEMES = {
443
- default: {
444
- name: 'default',
445
- colors: {
446
- primary: '#3B82F6',
447
- secondary: '#6B7280',
448
- success: '#10B981',
449
- warning: '#F59E0B',
450
- error: '#EF4444',
451
- background: '#FFFFFF',
452
- surface: '#F9FAFB',
453
- text: '#111827',
454
- textMuted: '#6B7280',
455
- border: '#E5E7EB',
456
- },
457
- spacing: {
458
- xs: 4,
459
- sm: 8,
460
- md: 16,
461
- lg: 24,
462
- xl: 32,
463
- },
464
- typography: {
465
- fontFamily: 'system-ui, -apple-system, sans-serif',
466
- fontSize: {
467
- xs: '12px',
468
- sm: '14px',
469
- md: '16px',
470
- lg: '18px',
471
- xl: '24px',
472
- },
473
- fontWeight: {
474
- normal: 400,
475
- medium: 500,
476
- semibold: 600,
477
- bold: 700,
478
- },
479
- },
480
- borderRadius: {
481
- sm: 4,
482
- md: 8,
483
- lg: 12,
484
- full: 9999,
485
- },
486
- shadows: {
487
- sm: '0 1px 2px rgba(0, 0, 0, 0.05)',
488
- md: '0 4px 6px rgba(0, 0, 0, 0.1)',
489
- lg: '0 10px 15px rgba(0, 0, 0, 0.1)',
490
- },
491
- },
492
- minimal: {
493
- name: 'minimal',
494
- colors: {
495
- primary: '#000000',
496
- secondary: '#666666',
497
- success: '#00AA00',
498
- warning: '#FFAA00',
499
- error: '#FF0000',
500
- background: '#FFFFFF',
501
- surface: '#FFFFFF',
502
- text: '#000000',
503
- textMuted: '#666666',
504
- border: '#DDDDDD',
505
- },
506
- spacing: {
507
- xs: 4,
508
- sm: 8,
509
- md: 12,
510
- lg: 16,
511
- xl: 24,
512
- },
513
- typography: {
514
- fontFamily: 'system-ui, -apple-system, sans-serif',
515
- fontSize: {
516
- xs: '11px',
517
- sm: '13px',
518
- md: '15px',
519
- lg: '17px',
520
- xl: '22px',
521
- },
522
- fontWeight: {
523
- normal: 400,
524
- medium: 500,
525
- semibold: 600,
526
- bold: 700,
527
- },
528
- },
529
- borderRadius: {
530
- sm: 0,
531
- md: 0,
532
- lg: 0,
533
- full: 9999,
534
- },
535
- shadows: {
536
- sm: 'none',
537
- md: 'none',
538
- lg: 'none',
539
- },
540
- },
541
- compact: {
542
- name: 'compact',
543
- colors: {
544
- primary: '#2563EB',
545
- secondary: '#475569',
546
- success: '#059669',
547
- warning: '#D97706',
548
- error: '#DC2626',
549
- background: '#FAFAFA',
550
- surface: '#FFFFFF',
551
- text: '#18181B',
552
- textMuted: '#71717A',
553
- border: '#E4E4E7',
554
- },
555
- spacing: {
556
- xs: 2,
557
- sm: 4,
558
- md: 8,
559
- lg: 12,
560
- xl: 16,
561
- },
562
- typography: {
563
- fontFamily: 'system-ui, -apple-system, sans-serif',
564
- fontSize: {
565
- xs: '10px',
566
- sm: '12px',
567
- md: '14px',
568
- lg: '16px',
569
- xl: '20px',
570
- },
571
- fontWeight: {
572
- normal: 400,
573
- medium: 500,
574
- semibold: 600,
575
- bold: 700,
576
- },
577
- },
578
- borderRadius: {
579
- sm: 2,
580
- md: 4,
581
- lg: 6,
582
- full: 9999,
583
- },
584
- shadows: {
585
- sm: '0 1px 1px rgba(0, 0, 0, 0.03)',
586
- md: '0 2px 4px rgba(0, 0, 0, 0.06)',
587
- lg: '0 4px 8px rgba(0, 0, 0, 0.08)',
588
- },
589
- },
590
- };
591
- /**
592
- * Get theme by name or return custom theme
593
- */
594
- function getTheme(theme) {
595
- if (typeof theme === 'string') {
596
- return IX_THEMES[theme] || IX_THEMES.default;
597
- }
598
- return theme;
599
- }
600
- /**
601
- * Apply A2R-IX styling to component tree
602
- */
603
- function applyStylingToTree(components, theme) {
604
- return components.map((comp) => applyStylingToNode(comp, theme));
605
- }
606
- /**
607
- * Apply A2R-IX styling to a single node
608
- */
609
- function applyStylingToNode(comp, theme) {
610
- const baseStyle = {};
611
- let className = '';
612
- // Apply component-specific default styles based on type
613
- const componentType = comp.type.toLowerCase();
614
- // Layout components
615
- if (componentType === 'box' || componentType === 'stack' || componentType === 'flex') {
616
- baseStyle.display = 'flex';
617
- baseStyle.flexDirection = comp.props.flexDirection || 'column';
618
- baseStyle.gap = theme.spacing.sm;
619
- baseStyle.padding = theme.spacing.md;
620
- }
621
- if (componentType === 'stack') {
622
- const direction = comp.props.direction;
623
- baseStyle.flexDirection = direction === 'horizontal' ? 'row' : 'column';
624
- baseStyle.alignItems = comp.props.align || 'stretch';
625
- baseStyle.justifyContent = comp.props.justify || 'flex-start';
626
- baseStyle.gap = comp.props.spacing || theme.spacing.sm;
627
- }
628
- // Typography components
629
- if (componentType === 'text') {
630
- baseStyle.fontSize = getFontSize(comp.props.variant, theme);
631
- baseStyle.color = theme.colors.text;
632
- baseStyle.fontFamily = theme.typography.fontFamily;
633
- baseStyle.lineHeight = 1.5;
634
- }
635
- if (componentType === 'heading') {
636
- const level = comp.props.level || 1;
637
- baseStyle.fontSize = level === 1 ? theme.typography.fontSize.xl :
638
- level === 2 ? theme.typography.fontSize.lg :
639
- theme.typography.fontSize.md;
640
- baseStyle.fontWeight = theme.typography.fontWeight.semibold;
641
- baseStyle.color = theme.colors.text;
642
- baseStyle.marginBottom = theme.spacing.md;
643
- }
644
- if (componentType === 'paragraph') {
645
- baseStyle.fontSize = theme.typography.fontSize.md;
646
- baseStyle.color = theme.colors.text;
647
- baseStyle.lineHeight = 1.6;
648
- baseStyle.marginBottom = theme.spacing.md;
649
- }
650
- // Input components
651
- if (componentType === 'button') {
652
- const variant = comp.props.variant || 'primary';
653
- baseStyle.padding = `${theme.spacing.sm} ${theme.spacing.md}`;
654
- baseStyle.borderRadius = theme.borderRadius.md;
655
- baseStyle.fontSize = theme.typography.fontSize.sm;
656
- baseStyle.fontWeight = theme.typography.fontWeight.medium;
657
- baseStyle.cursor = 'pointer';
658
- baseStyle.border = 'none';
659
- baseStyle.transition = 'all 0.2s ease';
660
- if (variant === 'primary') {
661
- baseStyle.backgroundColor = theme.colors.primary;
662
- baseStyle.color = '#FFFFFF';
663
- }
664
- else if (variant === 'secondary') {
665
- baseStyle.backgroundColor = theme.colors.surface;
666
- baseStyle.color = theme.colors.text;
667
- baseStyle.border = `1px solid ${theme.colors.border}`;
668
- }
669
- else if (variant === 'ghost') {
670
- baseStyle.backgroundColor = 'transparent';
671
- baseStyle.color = theme.colors.primary;
672
- }
673
- else if (variant === 'danger') {
674
- baseStyle.backgroundColor = theme.colors.error;
675
- baseStyle.color = '#FFFFFF';
676
- }
677
- }
678
- if (componentType === 'input' || componentType === 'textarea') {
679
- baseStyle.padding = `${theme.spacing.sm} ${theme.spacing.md}`;
680
- baseStyle.borderRadius = theme.borderRadius.md;
681
- baseStyle.border = `1px solid ${theme.colors.border}`;
682
- baseStyle.fontSize = theme.typography.fontSize.sm;
683
- baseStyle.fontFamily = theme.typography.fontFamily;
684
- baseStyle.outline = 'none';
685
- baseStyle.transition = 'border-color 0.2s ease';
686
- baseStyle.width = '100%';
687
- baseStyle.boxSizing = 'border-box';
688
- }
689
- // Display components
690
- if (componentType === 'card') {
691
- baseStyle.backgroundColor = theme.colors.surface;
692
- baseStyle.borderRadius = comp.props.borderRadius !== undefined
693
- ? `${comp.props.borderRadius}px`
694
- : `${theme.borderRadius.lg}px`;
695
- baseStyle.padding = comp.props.padding !== undefined
696
- ? `${comp.props.padding}px`
697
- : `${theme.spacing.lg}px`;
698
- const elevation = comp.props.elevation || 1;
699
- baseStyle.boxShadow = elevation === 0 ? 'none' :
700
- elevation === 1 ? theme.shadows.sm :
701
- elevation === 2 ? theme.shadows.md :
702
- theme.shadows.lg;
703
- }
704
- if (componentType === 'badge') {
705
- const variant = comp.props.variant || 'default';
706
- baseStyle.display = 'inline-flex';
707
- baseStyle.alignItems = 'center';
708
- baseStyle.padding = `${theme.spacing.xs} ${theme.spacing.sm}`;
709
- baseStyle.borderRadius = theme.borderRadius.full;
710
- baseStyle.fontSize = theme.typography.fontSize.xs;
711
- baseStyle.fontWeight = theme.typography.fontWeight.medium;
712
- const variantColors = {
713
- default: { bg: theme.colors.surface, color: theme.colors.text },
714
- success: { bg: '#D1FAE5', color: '#065F46' },
715
- warning: { bg: '#FEF3C7', color: '#92400E' },
716
- error: { bg: '#FEE2E2', color: '#991B1B' },
717
- info: { bg: '#DBEAFE', color: '#1E40AF' },
718
- };
719
- const colors = variantColors[variant] || variantColors.default;
720
- baseStyle.backgroundColor = colors.bg;
721
- baseStyle.color = colors.color;
722
- }
723
- if (componentType === 'image') {
724
- baseStyle.maxWidth = '100%';
725
- baseStyle.height = 'auto';
726
- baseStyle.objectFit = comp.props.objectFit || 'cover';
727
- baseStyle.borderRadius = theme.borderRadius.md;
728
- }
729
- // Merge with component-specific styles
730
- const mergedStyle = { ...baseStyle, ...(comp.style || {}) };
731
- // Handle style props
732
- if (comp.props.backgroundColor) {
733
- mergedStyle.backgroundColor = comp.props.backgroundColor;
734
- }
735
- if (comp.props.borderRadius) {
736
- mergedStyle.borderRadius = `${comp.props.borderRadius}px`;
737
- }
738
- if (comp.props.padding) {
739
- mergedStyle.padding = `${comp.props.padding}px`;
740
- }
741
- if (comp.props.margin) {
742
- mergedStyle.margin = `${comp.props.margin}px`;
743
- }
744
- if (comp.props.width) {
745
- mergedStyle.width = comp.props.width;
746
- }
747
- if (comp.props.height) {
748
- mergedStyle.height = comp.props.height;
749
- }
750
- // Build className from props
751
- const classNameParts = [];
752
- if (comp.props.className) {
753
- classNameParts.push(comp.props.className);
754
- }
755
- className = classNameParts.join(' ') || undefined;
756
- // Process children
757
- const children = comp.children
758
- ? comp.children.map((child) => applyStylingToNode(child, theme))
759
- : undefined;
760
- return {
761
- id: comp.id,
762
- type: comp.type,
763
- props: comp.props,
764
- style: mergedStyle,
765
- className,
766
- children,
767
- bindings: comp.bindings,
768
- events: comp.events,
769
- condition: comp.condition,
770
- repeat: comp.repeat,
771
- };
772
- }
773
- /**
774
- * Get font size based on variant
775
- */
776
- function getFontSize(variant, theme) {
777
- if (!variant)
778
- return theme.typography.fontSize.md;
779
- const sizeMap = {
780
- heading: theme.typography.fontSize.lg,
781
- body: theme.typography.fontSize.md,
782
- caption: theme.typography.fontSize.xs,
783
- label: theme.typography.fontSize.sm,
784
- };
785
- return sizeMap[variant] || theme.typography.fontSize.md;
786
- }
787
- /**
788
- * Parse markdown UI description to component tree
789
- */
790
- function parseMarkdownToComponents(markdown) {
791
- const components = [];
792
- const lines = markdown.split('\n').filter((line) => line.trim());
793
- const currentList = null;
794
- const listItems = [];
795
- for (const line of lines) {
796
- const trimmed = line.trim();
797
- // Heading
798
- if (trimmed.startsWith('#')) {
799
- const level = trimmed.match(/^#+/)?.[0].length || 1;
800
- const text = trimmed.replace(/^#+\s*/, '');
801
- components.push({
802
- id: generateId(),
803
- type: 'Heading',
804
- props: { level, children: text },
805
- });
806
- }
807
- // Paragraph
808
- else if (!trimmed.startsWith('-') && !trimmed.startsWith('*') && !trimmed.startsWith('>')) {
809
- components.push({
810
- id: generateId(),
811
- type: 'Text',
812
- props: { children: trimmed, variant: 'body' },
813
- });
814
- }
815
- // List item
816
- else if (trimmed.startsWith('-') || trimmed.startsWith('*')) {
817
- const text = trimmed.replace(/^[-*]\s*/, '');
818
- listItems.push({
819
- id: generateId(),
820
- type: 'Text',
821
- props: { children: text },
822
- });
823
- }
824
- // Blockquote
825
- else if (trimmed.startsWith('>')) {
826
- const text = trimmed.replace(/^>\s*/, '');
827
- components.push({
828
- id: generateId(),
829
- type: 'Text',
830
- props: { children: text, variant: 'caption', style: { fontStyle: 'italic', borderLeft: '3px solid #E5E7EB', paddingLeft: '12px' } },
831
- });
832
- }
833
- }
834
- // If we have list items, wrap them in a Stack
835
- if (listItems.length > 0) {
836
- components.push({
837
- id: generateId(),
838
- type: 'Stack',
839
- props: { direction: 'vertical', spacing: 4 },
840
- children: listItems,
841
- });
842
- }
843
- return components;
844
- }
845
- /**
846
- * LLM-to-IX Pipeline
847
- *
848
- * Main pipeline function that:
849
- * 1. Parses LLM output (JSON or markdown)
850
- * 2. Transforms to IX component tree
851
- * 3. Applies A2R-IX styling
852
- * 4. Returns IxRenderTree
853
- */
854
- export function llmToIxPipeline(options) {
855
- const startTime = Date.now();
856
- const pipelineOptions = options.options || {};
857
- // Parse input
858
- const parseStart = Date.now();
859
- const format = detectInputFormat(options.input);
860
- let convertResult;
861
- // Handle markdown input
862
- if (format === 'natural-language' && typeof options.input === 'string') {
863
- // Check if it looks like markdown
864
- if (options.input.includes('#') || options.input.includes('- ') || options.input.includes('> ')) {
865
- const components = parseMarkdownToComponents(options.input);
866
- const ui = {
867
- version: pipelineOptions.version || '1.0.0',
868
- components,
869
- state: { variables: [] },
870
- actions: [],
871
- };
872
- convertResult = {
873
- ui,
874
- warnings: ['Parsed from markdown'],
875
- inferredState: [],
876
- confidence: 0.7,
877
- };
878
- }
879
- else {
880
- convertResult = convertFromNaturalLanguage(options.input, {
881
- version: pipelineOptions.version,
882
- generateIds: pipelineOptions.generateIds,
883
- validate: pipelineOptions.validate,
884
- inferState: pipelineOptions.inferState,
885
- });
886
- }
887
- }
888
- else {
889
- convertResult = convertLLMToIX(options.input, {
890
- version: pipelineOptions.version,
891
- generateIds: pipelineOptions.generateIds,
892
- validate: pipelineOptions.validate,
893
- inferState: pipelineOptions.inferState,
894
- });
895
- }
896
- const parseTime = Date.now() - parseStart;
897
- // Transform to render tree
898
- const transformStart = Date.now();
899
- const theme = pipelineOptions.theme ? getTheme(pipelineOptions.theme) : IX_THEMES.default;
900
- const styledComponents = pipelineOptions.applyStyling !== false
901
- ? applyStylingToTree(convertResult.ui.components, theme)
902
- : convertResult.ui.components.map((comp) => ({
903
- id: comp.id,
904
- type: comp.type,
905
- props: comp.props,
906
- style: comp.style || {},
907
- className: comp.className,
908
- children: comp.children?.map((child) => ({
909
- id: child.id,
910
- type: child.type,
911
- props: child.props,
912
- style: child.style || {},
913
- className: child.className,
914
- children: child.children?.map((c) => ({
915
- id: c.id,
916
- type: c.type,
917
- props: c.props,
918
- style: c.style || {},
919
- className: c.className,
920
- })),
921
- bindings: child.bindings,
922
- events: child.events,
923
- })),
924
- bindings: comp.bindings,
925
- events: comp.events,
926
- condition: comp.condition,
927
- repeat: comp.repeat,
928
- }));
929
- const transformTime = Date.now() - transformStart;
930
- // Apply styling
931
- const styleStart = Date.now();
932
- const styles = {};
933
- if (pipelineOptions.applyStyling !== false) {
934
- // Generate CSS classes for components
935
- styledComponents.forEach((comp) => {
936
- styles[comp.id] = comp.style;
937
- });
938
- }
939
- const styleTime = Date.now() - styleStart;
940
- const totalTime = Date.now() - startTime;
941
- // Build render tree
942
- const tree = {
943
- version: convertResult.ui.version,
944
- components: styledComponents,
945
- state: convertResult.ui.state,
946
- actions: convertResult.ui.actions,
947
- theme: pipelineOptions.applyStyling !== false ? theme : undefined,
948
- styles: pipelineOptions.applyStyling !== false ? styles : undefined,
949
- metadata: {
950
- generatedAt: new Date().toISOString(),
951
- },
952
- };
953
- return {
954
- tree,
955
- uiRoot: convertResult.ui,
956
- warnings: convertResult.warnings,
957
- inferredState: convertResult.inferredState,
958
- confidence: convertResult.confidence,
959
- metrics: {
960
- parseTime,
961
- transformTime,
962
- styleTime,
963
- totalTime,
964
- },
965
- };
966
- }
967
- /**
968
- * LLM-to-IX Pipeline API
969
- */
970
- export const llmToIX = {
971
- /**
972
- * Convert LLM output to IX format
973
- */
974
- convert: convertLLMToIX,
975
- /**
976
- * Detect input format
977
- */
978
- detectFormat: detectInputFormat,
979
- /**
980
- * Check if input is valid json-render
981
- */
982
- isJsonRender: (input) => jsonRenderAdapter.isValidSchema(input),
983
- /**
984
- * Quick convert from json-render
985
- */
986
- fromJsonRender: (schema, options) => convertFromJsonRender(schema, options || {}),
987
- /**
988
- * Quick convert from JSX-like
989
- */
990
- fromJSX: (jsx, options) => convertFromJSXLike(jsx, options || {}),
991
- };
992
- //# sourceMappingURL=llm-to-ix.js.map