sdui-web 0.5.0 → 0.6.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.
@@ -0,0 +1,935 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/react/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SduiRenderer: () => SduiRenderer
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/react/SduiRenderer.tsx
28
+ var import_react = require("react");
29
+
30
+ // src/utils.ts
31
+ function resolveColor(color, theme) {
32
+ if (!color) return void 0;
33
+ if (theme === "dark") {
34
+ return color.dark ?? color.light;
35
+ }
36
+ return color.light ?? color.dark;
37
+ }
38
+ function resolveSize(size) {
39
+ if (!size || !size.unit) return void 0;
40
+ switch (size.unit) {
41
+ case "dp":
42
+ return `${size.value ?? 0}px`;
43
+ case "match_parent":
44
+ return "100%";
45
+ case "wrap_content":
46
+ return "fit-content";
47
+ default:
48
+ return void 0;
49
+ }
50
+ }
51
+ function resolveSpacing(spacing) {
52
+ if (!spacing) return {};
53
+ return {
54
+ top: spacing.top != null ? `${spacing.top}px` : void 0,
55
+ bottom: spacing.bottom != null ? `${spacing.bottom}px` : void 0,
56
+ left: spacing.start != null ? `${spacing.start}px` : void 0,
57
+ right: spacing.end != null ? `${spacing.end}px` : void 0
58
+ };
59
+ }
60
+
61
+ // src/modifiers.ts
62
+ function applyModifier(element, modifier, theme) {
63
+ if (!modifier) return;
64
+ const s = element.style;
65
+ if (modifier.alignment) {
66
+ switch (modifier.alignment) {
67
+ // Single-axis (Column / Row children)
68
+ case "center":
69
+ case "center_horizontally":
70
+ case "center_vertically":
71
+ s.alignSelf = "center";
72
+ break;
73
+ case "start":
74
+ case "top":
75
+ s.alignSelf = "flex-start";
76
+ break;
77
+ case "end":
78
+ case "bottom":
79
+ s.alignSelf = "flex-end";
80
+ break;
81
+ // Two-axis (Box children — grid layout)
82
+ case "top_start":
83
+ s.alignSelf = "start";
84
+ s.justifySelf = "start";
85
+ break;
86
+ case "top_center":
87
+ s.alignSelf = "start";
88
+ s.justifySelf = "center";
89
+ break;
90
+ case "top_end":
91
+ s.alignSelf = "start";
92
+ s.justifySelf = "end";
93
+ break;
94
+ case "center_start":
95
+ s.alignSelf = "center";
96
+ s.justifySelf = "start";
97
+ break;
98
+ case "center_end":
99
+ s.alignSelf = "center";
100
+ s.justifySelf = "end";
101
+ break;
102
+ case "bottom_start":
103
+ s.alignSelf = "end";
104
+ s.justifySelf = "start";
105
+ break;
106
+ case "bottom_center":
107
+ s.alignSelf = "end";
108
+ s.justifySelf = "center";
109
+ break;
110
+ case "bottom_end":
111
+ s.alignSelf = "end";
112
+ s.justifySelf = "end";
113
+ break;
114
+ }
115
+ }
116
+ if (modifier.weight != null) {
117
+ s.flex = `${modifier.weight}`;
118
+ s.minWidth = "0";
119
+ s.minHeight = "0";
120
+ }
121
+ if (modifier.action) {
122
+ const action = modifier.action;
123
+ s.cursor = "pointer";
124
+ element.addEventListener("click", (e) => {
125
+ e.stopPropagation();
126
+ element.dispatchEvent(new CustomEvent("sdui:action", {
127
+ bubbles: true,
128
+ detail: { type: action.type, params: action.params }
129
+ }));
130
+ });
131
+ }
132
+ const mod = modifier;
133
+ const hasAspectRatio = "aspectRatio" in mod && mod.aspectRatio != null;
134
+ const hasWidth = "width" in mod && mod.width;
135
+ const hasHeight = "height" in mod && mod.height;
136
+ if (hasWidth) {
137
+ const w = resolveSize(mod.width);
138
+ if (w) s.width = w;
139
+ }
140
+ if (hasHeight && !(hasAspectRatio && hasWidth)) {
141
+ const h = resolveSize(mod.height);
142
+ if (h) s.height = h;
143
+ }
144
+ if (hasAspectRatio) {
145
+ s.aspectRatio = `${mod.aspectRatio}`;
146
+ }
147
+ if ("padding" in mod && mod.padding) {
148
+ const p = resolveSpacing(mod.padding);
149
+ if (p.top) s.paddingTop = p.top;
150
+ if (p.bottom) s.paddingBottom = p.bottom;
151
+ if (p.left) s.paddingLeft = p.left;
152
+ if (p.right) s.paddingRight = p.right;
153
+ }
154
+ if ("margin" in mod && mod.margin) {
155
+ const m = resolveSpacing(mod.margin);
156
+ if (m.top) s.marginTop = m.top;
157
+ if (m.bottom) s.marginBottom = m.bottom;
158
+ if (m.left) s.marginLeft = m.left;
159
+ if (m.right) s.marginRight = m.right;
160
+ }
161
+ if ("backgroundColor" in mod && mod.backgroundColor) {
162
+ applyBackground(s, mod.backgroundColor, theme);
163
+ }
164
+ }
165
+ function applyBackground(s, bg, theme) {
166
+ if (!bg.colors || bg.colors.length === 0) return;
167
+ const colors = bg.colors.map((c) => resolveColor(c, theme)).filter(Boolean);
168
+ if (colors.length === 0) return;
169
+ switch (bg.type) {
170
+ case "single":
171
+ s.backgroundColor = colors[0];
172
+ break;
173
+ case "vertical_gradient":
174
+ s.background = `linear-gradient(to bottom, ${colors.join(", ")})`;
175
+ break;
176
+ case "horizontal_gradient":
177
+ s.background = `linear-gradient(to right, ${colors.join(", ")})`;
178
+ break;
179
+ case "linear_gradient":
180
+ s.background = `linear-gradient(${colors.join(", ")})`;
181
+ break;
182
+ default:
183
+ if (colors.length === 1) {
184
+ s.backgroundColor = colors[0];
185
+ }
186
+ break;
187
+ }
188
+ }
189
+
190
+ // src/components/text.ts
191
+ function renderText(component, theme) {
192
+ const props = component.properties;
193
+ const el = document.createElement("span");
194
+ el.textContent = props?.text ?? "";
195
+ if (props?.fontSize != null) {
196
+ el.style.fontSize = `${props.fontSize}px`;
197
+ }
198
+ if (props?.color) {
199
+ const c = resolveColor(props.color, theme);
200
+ if (c) el.style.color = c;
201
+ }
202
+ if (props?.fontWeight) {
203
+ switch (props.fontWeight) {
204
+ case "bold":
205
+ el.style.fontWeight = "700";
206
+ break;
207
+ case "semi_bold":
208
+ el.style.fontWeight = "600";
209
+ break;
210
+ case "medium":
211
+ el.style.fontWeight = "500";
212
+ break;
213
+ case "normal":
214
+ el.style.fontWeight = "400";
215
+ break;
216
+ }
217
+ }
218
+ if (props?.textAlign) {
219
+ el.style.display = "block";
220
+ switch (props.textAlign) {
221
+ case "left":
222
+ case "start":
223
+ el.style.textAlign = "left";
224
+ break;
225
+ case "right":
226
+ case "end":
227
+ el.style.textAlign = "right";
228
+ break;
229
+ case "center":
230
+ el.style.textAlign = "center";
231
+ break;
232
+ case "justify":
233
+ el.style.textAlign = "justify";
234
+ break;
235
+ }
236
+ }
237
+ if (props?.overflow === "ellipsis") {
238
+ el.style.textOverflow = "ellipsis";
239
+ el.style.overflow = "hidden";
240
+ el.style.whiteSpace = "nowrap";
241
+ }
242
+ if (props?.maxLines != null) {
243
+ el.style.display = "-webkit-box";
244
+ el.style.webkitLineClamp = `${props.maxLines}`;
245
+ el.style["-webkit-box-orient"] = "vertical";
246
+ el.style.overflow = "hidden";
247
+ }
248
+ if (props?.minLines != null) {
249
+ el.style.minHeight = `${props.minLines * (props.fontSize ?? 16) * 1.4}px`;
250
+ }
251
+ applyModifier(el, props?.modifier, theme);
252
+ return el;
253
+ }
254
+
255
+ // src/components/image.ts
256
+ function renderImage(component, theme) {
257
+ const props = component.properties;
258
+ const el = document.createElement("img");
259
+ el.style.display = "block";
260
+ el.style.boxSizing = "border-box";
261
+ if (props?.image) {
262
+ el.src = props.image;
263
+ }
264
+ if (props?.contentDescription) {
265
+ el.alt = props.contentDescription;
266
+ }
267
+ switch (props?.scaleType) {
268
+ case "crop":
269
+ el.style.objectFit = "cover";
270
+ break;
271
+ case "fill_bounds":
272
+ el.style.objectFit = "fill";
273
+ break;
274
+ case "fill_width":
275
+ el.style.objectFit = "cover";
276
+ el.style.width = "100%";
277
+ break;
278
+ case "fill_height":
279
+ el.style.objectFit = "cover";
280
+ el.style.height = "100%";
281
+ break;
282
+ case "none":
283
+ el.style.objectFit = "none";
284
+ break;
285
+ default:
286
+ el.style.objectFit = "contain";
287
+ break;
288
+ }
289
+ if (props?.tintColor) {
290
+ const c = resolveColor(props.tintColor, theme);
291
+ if (c) {
292
+ el.style.filter = `opacity(0.5) drop-shadow(0 0 0 ${c})`;
293
+ }
294
+ }
295
+ if (props?.aspectRatio != null) {
296
+ el.style.aspectRatio = `${props.aspectRatio}`;
297
+ }
298
+ applyModifier(el, props?.modifier, theme);
299
+ return el;
300
+ }
301
+
302
+ // src/components/box.ts
303
+ function renderBox(component, theme) {
304
+ const props = component.properties;
305
+ const el = document.createElement("div");
306
+ el.style.display = "grid";
307
+ el.style.position = "relative";
308
+ if (props?.alignment) {
309
+ switch (props.alignment) {
310
+ case "top_start":
311
+ el.style.justifyItems = "start";
312
+ el.style.alignItems = "start";
313
+ break;
314
+ case "top_center":
315
+ el.style.justifyItems = "center";
316
+ el.style.alignItems = "start";
317
+ break;
318
+ case "top_end":
319
+ el.style.justifyItems = "end";
320
+ el.style.alignItems = "start";
321
+ break;
322
+ case "center_start":
323
+ el.style.justifyItems = "start";
324
+ el.style.alignItems = "center";
325
+ break;
326
+ case "center":
327
+ el.style.justifyItems = "center";
328
+ el.style.alignItems = "center";
329
+ break;
330
+ case "center_end":
331
+ el.style.justifyItems = "end";
332
+ el.style.alignItems = "center";
333
+ break;
334
+ case "bottom_start":
335
+ el.style.justifyItems = "start";
336
+ el.style.alignItems = "end";
337
+ break;
338
+ case "bottom_center":
339
+ el.style.justifyItems = "center";
340
+ el.style.alignItems = "end";
341
+ break;
342
+ case "bottom_end":
343
+ el.style.justifyItems = "end";
344
+ el.style.alignItems = "end";
345
+ break;
346
+ }
347
+ }
348
+ applyModifier(el, props?.modifier, theme);
349
+ return el;
350
+ }
351
+
352
+ // src/components/column.ts
353
+ function renderColumn(component, theme) {
354
+ const props = component.properties;
355
+ const el = document.createElement("div");
356
+ el.style.display = "flex";
357
+ el.style.flexDirection = "column";
358
+ if (props?.verticalArrangement) {
359
+ const arr = props.verticalArrangement;
360
+ switch (arr.type) {
361
+ case "top":
362
+ el.style.justifyContent = "flex-start";
363
+ break;
364
+ case "center":
365
+ el.style.justifyContent = "center";
366
+ break;
367
+ case "bottom":
368
+ el.style.justifyContent = "flex-end";
369
+ break;
370
+ case "space_between":
371
+ el.style.justifyContent = "space-between";
372
+ break;
373
+ case "space_evenly":
374
+ el.style.justifyContent = "space-evenly";
375
+ break;
376
+ case "space_around":
377
+ el.style.justifyContent = "space-around";
378
+ break;
379
+ case "spaced_by":
380
+ el.style.justifyContent = "flex-start";
381
+ if (arr.spacing != null) {
382
+ el.style.gap = `${arr.spacing}px`;
383
+ }
384
+ break;
385
+ }
386
+ }
387
+ if (props?.horizontalAlignment) {
388
+ switch (props.horizontalAlignment) {
389
+ case "start":
390
+ el.style.alignItems = "flex-start";
391
+ break;
392
+ case "end":
393
+ el.style.alignItems = "flex-end";
394
+ break;
395
+ case "center_horizontally":
396
+ el.style.alignItems = "center";
397
+ break;
398
+ }
399
+ }
400
+ if (props?.canScroll) {
401
+ el.style.overflowY = "auto";
402
+ }
403
+ applyModifier(el, props?.modifier, theme);
404
+ return el;
405
+ }
406
+
407
+ // src/components/row.ts
408
+ function renderRow(component, theme) {
409
+ const props = component.properties;
410
+ const el = document.createElement("div");
411
+ el.style.display = "flex";
412
+ el.style.flexDirection = "row";
413
+ if (props?.horizontalArrangement) {
414
+ const arr = props.horizontalArrangement;
415
+ switch (arr.type) {
416
+ case "start":
417
+ el.style.justifyContent = "flex-start";
418
+ break;
419
+ case "center":
420
+ el.style.justifyContent = "center";
421
+ break;
422
+ case "end":
423
+ el.style.justifyContent = "flex-end";
424
+ break;
425
+ case "space_between":
426
+ el.style.justifyContent = "space-between";
427
+ break;
428
+ case "space_evenly":
429
+ el.style.justifyContent = "space-evenly";
430
+ break;
431
+ case "space_around":
432
+ el.style.justifyContent = "space-around";
433
+ break;
434
+ case "spaced_by":
435
+ el.style.justifyContent = "flex-start";
436
+ if (arr.spacing != null) {
437
+ el.style.gap = `${arr.spacing}px`;
438
+ }
439
+ break;
440
+ }
441
+ }
442
+ if (props?.verticalAlignment) {
443
+ switch (props.verticalAlignment) {
444
+ case "top":
445
+ el.style.alignItems = "flex-start";
446
+ break;
447
+ case "bottom":
448
+ el.style.alignItems = "flex-end";
449
+ break;
450
+ case "center_vertically":
451
+ el.style.alignItems = "center";
452
+ break;
453
+ }
454
+ }
455
+ if (props?.canScroll) {
456
+ el.style.overflowX = "auto";
457
+ }
458
+ applyModifier(el, props?.modifier, theme);
459
+ return el;
460
+ }
461
+
462
+ // src/components/lazy-column.ts
463
+ function renderLazyColumn(component, theme) {
464
+ const props = component.properties;
465
+ const el = document.createElement("div");
466
+ el.style.display = "flex";
467
+ el.style.flexDirection = "column";
468
+ el.style.overflowY = "auto";
469
+ if (props?.verticalArrangement) {
470
+ const arr = props.verticalArrangement;
471
+ switch (arr.type) {
472
+ case "top":
473
+ el.style.justifyContent = "flex-start";
474
+ break;
475
+ case "center":
476
+ el.style.justifyContent = "center";
477
+ break;
478
+ case "bottom":
479
+ el.style.justifyContent = "flex-end";
480
+ break;
481
+ case "space_between":
482
+ el.style.justifyContent = "space-between";
483
+ break;
484
+ case "space_evenly":
485
+ el.style.justifyContent = "space-evenly";
486
+ break;
487
+ case "space_around":
488
+ el.style.justifyContent = "space-around";
489
+ break;
490
+ case "spaced_by":
491
+ el.style.justifyContent = "flex-start";
492
+ if (arr.spacing != null) {
493
+ el.style.gap = `${arr.spacing}px`;
494
+ }
495
+ break;
496
+ }
497
+ }
498
+ applyModifier(el, props?.modifier, theme);
499
+ return el;
500
+ }
501
+
502
+ // src/components/index.ts
503
+ var renderers = {
504
+ text: renderText,
505
+ image: renderImage,
506
+ box: renderBox,
507
+ column: renderColumn,
508
+ row: renderRow,
509
+ lazy_column: renderLazyColumn
510
+ };
511
+ function getRenderer(type) {
512
+ return renderers[type];
513
+ }
514
+
515
+ // src/render.ts
516
+ function renderComponent(component, theme) {
517
+ const renderer = getRenderer(component.type);
518
+ if (!renderer) {
519
+ console.warn(`[sdui] Unknown component type: "${component.type}"`);
520
+ const el2 = document.createElement("div");
521
+ el2.dataset.sduiUnknown = component.type;
522
+ return el2;
523
+ }
524
+ const el = renderer(component, theme);
525
+ el.dataset.sduiType = component.type;
526
+ if (component.__builderId) {
527
+ el.dataset.sduiId = component.__builderId;
528
+ }
529
+ if (component.children && component.children.length > 0) {
530
+ for (const child of component.children) {
531
+ el.appendChild(renderComponent(child, theme));
532
+ }
533
+ }
534
+ return el;
535
+ }
536
+
537
+ // src/component-meta.ts
538
+ function sizeModelProperties() {
539
+ return [
540
+ { name: "value", label: "Value", type: "number" },
541
+ { name: "unit", label: "Unit", type: "enum", enumValues: ["dp", "match_parent", "wrap_content"] }
542
+ ];
543
+ }
544
+ function spacingModelProperties() {
545
+ return [
546
+ { name: "top", label: "Top", type: "number" },
547
+ { name: "bottom", label: "Bottom", type: "number" },
548
+ { name: "start", label: "Start", type: "number" },
549
+ { name: "end", label: "End", type: "number" }
550
+ ];
551
+ }
552
+ function colorModelProperties() {
553
+ return [
554
+ { name: "light", label: "Light", type: "color" },
555
+ { name: "dark", label: "Dark", type: "color" }
556
+ ];
557
+ }
558
+ function actionModelProperties() {
559
+ return [
560
+ { name: "type", label: "Action Type", type: "string" },
561
+ { name: "params", label: "Parameters", type: "object" }
562
+ ];
563
+ }
564
+ function backgroundColorModelProperties() {
565
+ return [
566
+ {
567
+ name: "colors",
568
+ label: "Colors",
569
+ type: "object",
570
+ isArray: true,
571
+ properties: colorModelProperties()
572
+ },
573
+ {
574
+ name: "type",
575
+ label: "Background Type",
576
+ type: "enum",
577
+ enumValues: ["single", "vertical_gradient", "horizontal_gradient", "linear_gradient"]
578
+ }
579
+ ];
580
+ }
581
+ function modifierBaseProperties() {
582
+ return [
583
+ {
584
+ name: "alignment",
585
+ label: "Alignment",
586
+ type: "enum",
587
+ enumValues: ["start", "end", "center", "center_horizontally", "center_vertically", "top", "bottom", "top_start", "top_center", "top_end", "center_start", "center_end", "bottom_start", "bottom_center", "bottom_end"]
588
+ },
589
+ { name: "weight", label: "Weight", type: "number" },
590
+ { name: "action", label: "Action", type: "object", properties: actionModelProperties() }
591
+ ];
592
+ }
593
+ function textModifierDescriptor() {
594
+ return {
595
+ name: "modifier",
596
+ label: "Modifier",
597
+ type: "object",
598
+ properties: [
599
+ ...modifierBaseProperties(),
600
+ { name: "width", label: "Width", type: "object", properties: sizeModelProperties() },
601
+ { name: "margin", label: "Margin", type: "object", properties: spacingModelProperties() }
602
+ ]
603
+ };
604
+ }
605
+ function imageModifierDescriptor() {
606
+ return {
607
+ name: "modifier",
608
+ label: "Modifier",
609
+ type: "object",
610
+ properties: [
611
+ ...modifierBaseProperties(),
612
+ { name: "width", label: "Width", type: "object", properties: sizeModelProperties() },
613
+ { name: "height", label: "Height", type: "object", properties: sizeModelProperties() },
614
+ { name: "padding", label: "Padding", type: "object", properties: spacingModelProperties() },
615
+ { name: "margin", label: "Margin", type: "object", properties: spacingModelProperties() }
616
+ ]
617
+ };
618
+ }
619
+ function containerModifierDescriptor() {
620
+ return {
621
+ name: "modifier",
622
+ label: "Modifier",
623
+ type: "object",
624
+ properties: [
625
+ ...modifierBaseProperties(),
626
+ { name: "backgroundColor", label: "Background Color", type: "object", properties: backgroundColorModelProperties() },
627
+ { name: "width", label: "Width", type: "object", properties: sizeModelProperties() },
628
+ { name: "height", label: "Height", type: "object", properties: sizeModelProperties() },
629
+ { name: "padding", label: "Padding", type: "object", properties: spacingModelProperties() },
630
+ { name: "margin", label: "Margin", type: "object", properties: spacingModelProperties() },
631
+ { name: "aspectRatio", label: "Aspect Ratio", type: "number" }
632
+ ]
633
+ };
634
+ }
635
+ function verticalArrangementDescriptor() {
636
+ return {
637
+ name: "verticalArrangement",
638
+ label: "Vertical Arrangement",
639
+ type: "object",
640
+ properties: [
641
+ {
642
+ name: "type",
643
+ label: "Type",
644
+ type: "enum",
645
+ enumValues: ["top", "center", "bottom", "space_evenly", "space_between", "space_around", "spaced_by"]
646
+ },
647
+ { name: "spacing", label: "Spacing", type: "number" }
648
+ ]
649
+ };
650
+ }
651
+ function horizontalArrangementDescriptor() {
652
+ return {
653
+ name: "horizontalArrangement",
654
+ label: "Horizontal Arrangement",
655
+ type: "object",
656
+ properties: [
657
+ {
658
+ name: "type",
659
+ label: "Type",
660
+ type: "enum",
661
+ enumValues: ["start", "center", "end", "space_between", "space_evenly", "space_around", "spaced_by"]
662
+ },
663
+ { name: "spacing", label: "Spacing", type: "number" }
664
+ ]
665
+ };
666
+ }
667
+ var COMPONENT_DEFINITIONS = [
668
+ {
669
+ type: "text",
670
+ label: "Text",
671
+ acceptsChildren: false,
672
+ properties: [
673
+ { name: "text", label: "Text", type: "string", required: true, defaultValue: "Text" },
674
+ { name: "fontSize", label: "Font Size", type: "number" },
675
+ { name: "color", label: "Color", type: "object", properties: colorModelProperties() },
676
+ { name: "fontWeight", label: "Font Weight", type: "enum", enumValues: ["bold", "medium", "normal", "semi_bold"] },
677
+ { name: "textAlign", label: "Text Align", type: "enum", enumValues: ["left", "right", "center", "justify", "start", "end", "unspecified"] },
678
+ { name: "overflow", label: "Overflow", type: "enum", enumValues: ["none", "ellipsis"] },
679
+ { name: "maxLines", label: "Max Lines", type: "number" },
680
+ { name: "minLines", label: "Min Lines", type: "number", defaultValue: 1 },
681
+ textModifierDescriptor()
682
+ ]
683
+ },
684
+ {
685
+ type: "image",
686
+ label: "Image",
687
+ acceptsChildren: false,
688
+ properties: [
689
+ { name: "image", label: "Image URL", type: "string", required: true },
690
+ { name: "scaleType", label: "Scale Type", type: "enum", enumValues: ["fit", "crop", "fill_height", "fill_width", "fill_bounds", "inside", "none"] },
691
+ { name: "contentDescription", label: "Content Description", type: "string" },
692
+ { name: "aspectRatio", label: "Aspect Ratio", type: "number" },
693
+ { name: "tintColor", label: "Tint Color", type: "object", properties: colorModelProperties() },
694
+ imageModifierDescriptor()
695
+ ]
696
+ },
697
+ {
698
+ type: "box",
699
+ label: "Box",
700
+ acceptsChildren: true,
701
+ properties: [
702
+ containerModifierDescriptor(),
703
+ {
704
+ name: "alignment",
705
+ label: "Alignment",
706
+ type: "enum",
707
+ enumValues: ["top_start", "top_center", "top_end", "center_start", "center", "center_end", "bottom_start", "bottom_center", "bottom_end"]
708
+ }
709
+ ]
710
+ },
711
+ {
712
+ type: "column",
713
+ label: "Column",
714
+ acceptsChildren: true,
715
+ properties: [
716
+ containerModifierDescriptor(),
717
+ verticalArrangementDescriptor(),
718
+ { name: "horizontalAlignment", label: "Horizontal Alignment", type: "enum", enumValues: ["start", "end", "center_horizontally"] },
719
+ { name: "canScroll", label: "Can Scroll", type: "boolean" }
720
+ ]
721
+ },
722
+ {
723
+ type: "row",
724
+ label: "Row",
725
+ acceptsChildren: true,
726
+ properties: [
727
+ containerModifierDescriptor(),
728
+ { name: "verticalAlignment", label: "Vertical Alignment", type: "enum", enumValues: ["top", "bottom", "center_vertically"] },
729
+ horizontalArrangementDescriptor(),
730
+ { name: "canScroll", label: "Can Scroll", type: "boolean" }
731
+ ]
732
+ },
733
+ {
734
+ type: "lazy_column",
735
+ label: "Lazy Column",
736
+ acceptsChildren: true,
737
+ properties: [
738
+ containerModifierDescriptor(),
739
+ verticalArrangementDescriptor()
740
+ ]
741
+ }
742
+ ];
743
+
744
+ // src/index.ts
745
+ function render(json, container, options) {
746
+ const screen = JSON.parse(json);
747
+ const theme = options?.theme ?? "light";
748
+ const root = renderComponent(screen.root, theme);
749
+ container.innerHTML = "";
750
+ container.appendChild(root);
751
+ }
752
+
753
+ // src/react/SduiRenderer.tsx
754
+ var import_jsx_runtime = require("react/jsx-runtime");
755
+ function SduiRenderer({
756
+ json,
757
+ theme = "light",
758
+ deviceWidth,
759
+ deviceHeight,
760
+ filePreviewMap,
761
+ selectedId,
762
+ onClickNode,
763
+ onClickOutside,
764
+ className,
765
+ style
766
+ }) {
767
+ const renderRef = (0, import_react.useRef)(null);
768
+ const wrapperRef = (0, import_react.useRef)(null);
769
+ const [containerSize, setContainerSize] = (0, import_react.useState)({ w: 0, h: 0 });
770
+ const [error, setError] = (0, import_react.useState)(null);
771
+ (0, import_react.useEffect)(() => {
772
+ const el = wrapperRef.current;
773
+ if (!el) return;
774
+ const ro = new ResizeObserver((entries) => {
775
+ const { width, height } = entries[0].contentRect;
776
+ setContainerSize({ w: width, h: height });
777
+ });
778
+ ro.observe(el);
779
+ return () => ro.disconnect();
780
+ }, []);
781
+ (0, import_react.useEffect)(() => {
782
+ const el = renderRef.current;
783
+ if (!el) return;
784
+ try {
785
+ let renderJson = json;
786
+ if (filePreviewMap) {
787
+ for (const [fileName, blobUrl] of Object.entries(filePreviewMap)) {
788
+ renderJson = renderJson.replaceAll(JSON.stringify(fileName), JSON.stringify(blobUrl));
789
+ }
790
+ }
791
+ JSON.parse(renderJson);
792
+ setError(null);
793
+ render(renderJson, el, { theme });
794
+ } catch (err) {
795
+ setError(err instanceof Error ? err.message : "Render error");
796
+ el.innerHTML = "";
797
+ }
798
+ }, [json, theme, filePreviewMap]);
799
+ (0, import_react.useEffect)(() => {
800
+ const el = renderRef.current;
801
+ if (!el) return;
802
+ el.querySelectorAll("[data-sdui-selected]").forEach((node) => {
803
+ node.removeAttribute("data-sdui-selected");
804
+ });
805
+ if (selectedId) {
806
+ const selected = el.querySelector(`[data-sdui-id="${selectedId}"]`);
807
+ if (selected) {
808
+ selected.setAttribute("data-sdui-selected", "true");
809
+ }
810
+ }
811
+ }, [selectedId, json]);
812
+ (0, import_react.useEffect)(() => {
813
+ const el = renderRef.current;
814
+ if (!el || !onClickNode) return;
815
+ function handleClick(e) {
816
+ e.stopPropagation();
817
+ const target = e.target.closest("[data-sdui-id]");
818
+ if (target) {
819
+ onClickNode(target.dataset.sduiId);
820
+ }
821
+ }
822
+ el.addEventListener("click", handleClick);
823
+ return () => el.removeEventListener("click", handleClick);
824
+ }, [onClickNode]);
825
+ const padding = 48;
826
+ const totalH = deviceHeight + 10;
827
+ const scaleW = containerSize.w > 0 && deviceWidth > containerSize.w - padding ? (containerSize.w - padding) / deviceWidth : 1;
828
+ const scaleH = containerSize.h > 0 && totalH > containerSize.h - padding ? (containerSize.h - padding) / totalH : 1;
829
+ const scale = Math.min(scaleW, scaleH, 1);
830
+ let rootScrollable = false;
831
+ try {
832
+ const parsed = JSON.parse(json);
833
+ const root = parsed.root;
834
+ if (root) {
835
+ rootScrollable = root.type === "lazy_column" || root.properties?.canScroll === true;
836
+ }
837
+ } catch {
838
+ }
839
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
840
+ "div",
841
+ {
842
+ ref: wrapperRef,
843
+ className,
844
+ style: {
845
+ flex: 1,
846
+ overflow: "hidden",
847
+ display: "flex",
848
+ justifyContent: "center",
849
+ alignItems: "flex-start",
850
+ padding: "24px 16px",
851
+ ...style
852
+ },
853
+ onClick: onClickOutside,
854
+ children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
856
+ "div",
857
+ {
858
+ onClick: (e) => e.stopPropagation(),
859
+ style: {
860
+ position: "relative",
861
+ alignSelf: "flex-start",
862
+ width: `${deviceWidth}px`,
863
+ transform: scale < 1 ? `scale(${scale})` : void 0,
864
+ transformOrigin: "top center"
865
+ },
866
+ children: [
867
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
868
+ "div",
869
+ {
870
+ style: {
871
+ borderRadius: "36px",
872
+ padding: "3px",
873
+ background: "linear-gradient(145deg, rgba(255,255,255,0.12), rgba(255,255,255,0.04))",
874
+ boxShadow: "0 24px 80px rgba(0,0,0,0.5), 0 8px 32px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.06)"
875
+ },
876
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { borderRadius: "33px", overflow: "hidden", background: "#1c1c1e" }, children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "8px 24px", height: "28px" }, children: [
878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "11px", fontWeight: 600, color: "rgba(255,255,255,0.7)", fontFamily: "system-ui" }, children: "9:41" }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
880
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "10", viewBox: "0 0 14 10", fill: "none", children: [
881
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "0", y: "6", width: "2.5", height: "4", rx: "0.5", fill: "rgba(255,255,255,0.5)" }),
882
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3.8", y: "4", width: "2.5", height: "6", rx: "0.5", fill: "rgba(255,255,255,0.5)" }),
883
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "7.6", y: "2", width: "2.5", height: "8", rx: "0.5", fill: "rgba(255,255,255,0.5)" }),
884
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "11.4", y: "0", width: "2.5", height: "10", rx: "0.5", fill: "rgba(255,255,255,0.5)" })
885
+ ] }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "20", height: "10", viewBox: "0 0 20 10", fill: "none", children: [
887
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "0.5", y: "0.5", width: "16", height: "9", rx: "2", stroke: "rgba(255,255,255,0.4)", strokeWidth: "1" }),
888
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "17", y: "3", width: "2", height: "4", rx: "0.5", fill: "rgba(255,255,255,0.3)" }),
889
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "1.5", y: "1.5", width: "14", height: "7", rx: "1", fill: "rgba(255,255,255,0.5)" })
890
+ ] })
891
+ ] })
892
+ ] }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", justifyContent: "center", marginBottom: "4px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: "120px", height: "28px", background: "#000", borderRadius: "14px" } }) }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
895
+ "div",
896
+ {
897
+ ref: renderRef,
898
+ style: {
899
+ background: theme === "dark" ? "#121212" : "#ffffff",
900
+ color: theme === "dark" ? "#ffffff" : "#000000",
901
+ height: `${deviceHeight - 60}px`,
902
+ borderBottomLeftRadius: "33px",
903
+ borderBottomRightRadius: "33px",
904
+ overflow: rootScrollable ? "auto" : "hidden"
905
+ }
906
+ }
907
+ )
908
+ ] })
909
+ }
910
+ ),
911
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", justifyContent: "center", marginTop: "-18px", position: "relative", zIndex: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: "120px", height: "4px", background: "rgba(255,255,255,0.2)", borderRadius: "2px" } }) })
912
+ ]
913
+ }
914
+ ),
915
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
916
+ position: "absolute",
917
+ bottom: "24px",
918
+ left: "50%",
919
+ transform: "translateX(-50%)",
920
+ width: "100%",
921
+ maxWidth: "375px",
922
+ padding: "12px",
923
+ borderRadius: "8px",
924
+ background: "rgba(239,68,68,0.1)",
925
+ border: "1px solid rgba(239,68,68,0.2)"
926
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: "13px", fontFamily: "monospace", color: "#ef4444", wordBreak: "break-all" }, children: error }) })
927
+ ]
928
+ }
929
+ );
930
+ }
931
+ // Annotate the CommonJS export names for ESM import in node:
932
+ 0 && (module.exports = {
933
+ SduiRenderer
934
+ });
935
+ //# sourceMappingURL=index.cjs.map