@safe-ugc-ui/react 0.1.0 → 0.2.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.
package/dist/index.d.ts CHANGED
@@ -64,36 +64,41 @@ declare function UGCContainer({ children, style }: UGCContainerProps): react_jsx
64
64
 
65
65
  interface BoxProps {
66
66
  style?: CSSProperties;
67
+ hoverStyle?: CSSProperties;
67
68
  children?: ReactNode;
68
69
  }
69
- declare function Box({ style, children }: BoxProps): react_jsx_runtime.JSX.Element;
70
+ declare function Box({ style, hoverStyle, children }: BoxProps): react_jsx_runtime.JSX.Element;
70
71
 
71
72
  interface RowProps {
72
73
  style?: CSSProperties;
74
+ hoverStyle?: CSSProperties;
73
75
  children?: ReactNode;
74
76
  }
75
- declare function Row({ style, children }: RowProps): react_jsx_runtime.JSX.Element;
77
+ declare function Row({ style, hoverStyle, children }: RowProps): react_jsx_runtime.JSX.Element;
76
78
 
77
79
  interface ColumnProps {
78
80
  style?: CSSProperties;
81
+ hoverStyle?: CSSProperties;
79
82
  children?: ReactNode;
80
83
  }
81
- declare function Column({ style, children }: ColumnProps): react_jsx_runtime.JSX.Element;
84
+ declare function Column({ style, hoverStyle, children }: ColumnProps): react_jsx_runtime.JSX.Element;
82
85
 
83
86
  interface TextComponentProps {
84
87
  content: string;
85
88
  style?: CSSProperties;
89
+ hoverStyle?: CSSProperties;
86
90
  }
87
91
  /**
88
92
  * Renders text content safely. NEVER uses dangerouslySetInnerHTML.
89
93
  * All content is rendered as text nodes via React's built-in escaping.
90
94
  */
91
- declare function Text({ content, style }: TextComponentProps): react_jsx_runtime.JSX.Element;
95
+ declare function Text({ content, style, hoverStyle }: TextComponentProps): react_jsx_runtime.JSX.Element;
92
96
 
93
97
  interface ImageComponentProps {
94
98
  src: string;
95
99
  alt?: string;
96
100
  style?: CSSProperties;
101
+ hoverStyle?: CSSProperties;
97
102
  }
98
103
  /**
99
104
  * Renders an image. Defense-in-depth: at the render level, we still
@@ -108,32 +113,36 @@ interface ImageComponentProps {
108
113
  *
109
114
  * If the src starts with @assets/, it was not resolved and we render nothing.
110
115
  */
111
- declare function Image({ src, alt, style }: ImageComponentProps): react_jsx_runtime.JSX.Element | null;
116
+ declare function Image({ src, alt, style, hoverStyle }: ImageComponentProps): react_jsx_runtime.JSX.Element | null;
112
117
 
113
118
  interface StackProps {
114
119
  style?: CSSProperties;
120
+ hoverStyle?: CSSProperties;
115
121
  children?: ReactNode;
116
122
  }
117
- declare function Stack({ style, children }: StackProps): react_jsx_runtime.JSX.Element;
123
+ declare function Stack({ style, hoverStyle, children }: StackProps): react_jsx_runtime.JSX.Element;
118
124
 
119
125
  interface GridProps {
120
126
  style?: CSSProperties;
127
+ hoverStyle?: CSSProperties;
121
128
  children?: ReactNode;
122
129
  }
123
- declare function Grid({ style, children }: GridProps): react_jsx_runtime.JSX.Element;
130
+ declare function Grid({ style, hoverStyle, children }: GridProps): react_jsx_runtime.JSX.Element;
124
131
 
125
132
  interface SpacerProps {
126
133
  size?: number | string;
127
134
  style?: CSSProperties;
135
+ hoverStyle?: CSSProperties;
128
136
  }
129
- declare function Spacer({ size, style }: SpacerProps): react_jsx_runtime.JSX.Element;
137
+ declare function Spacer({ size, style, hoverStyle }: SpacerProps): react_jsx_runtime.JSX.Element;
130
138
 
131
139
  interface DividerProps {
132
140
  color?: string;
133
141
  thickness?: number | string;
134
142
  style?: CSSProperties;
143
+ hoverStyle?: CSSProperties;
135
144
  }
136
- declare function Divider({ color, thickness, style }: DividerProps): react_jsx_runtime.JSX.Element;
145
+ declare function Divider({ color, thickness, style, hoverStyle }: DividerProps): react_jsx_runtime.JSX.Element;
137
146
 
138
147
  interface IconProps {
139
148
  name: string;
@@ -141,38 +150,43 @@ interface IconProps {
141
150
  color?: string;
142
151
  iconResolver?: (name: string) => ReactNode;
143
152
  style?: CSSProperties;
153
+ hoverStyle?: CSSProperties;
144
154
  }
145
- declare function Icon({ name, size, color, iconResolver, style }: IconProps): react_jsx_runtime.JSX.Element | null;
155
+ declare function Icon({ name, size, color, iconResolver, style, hoverStyle }: IconProps): react_jsx_runtime.JSX.Element | null;
146
156
 
147
157
  interface ProgressBarProps {
148
158
  value: number;
149
159
  max: number;
150
160
  color?: string;
151
161
  style?: CSSProperties;
162
+ hoverStyle?: CSSProperties;
152
163
  }
153
- declare function ProgressBar({ value, max, color, style }: ProgressBarProps): react_jsx_runtime.JSX.Element;
164
+ declare function ProgressBar({ value, max, color, style, hoverStyle }: ProgressBarProps): react_jsx_runtime.JSX.Element;
154
165
 
155
166
  interface AvatarProps {
156
167
  src: string;
157
168
  alt?: string;
158
169
  size?: number | string;
159
170
  style?: CSSProperties;
171
+ hoverStyle?: CSSProperties;
160
172
  }
161
- declare function Avatar({ src, alt, size, style }: AvatarProps): react_jsx_runtime.JSX.Element;
173
+ declare function Avatar({ src, alt, size, style, hoverStyle }: AvatarProps): react_jsx_runtime.JSX.Element;
162
174
 
163
175
  interface BadgeProps {
164
176
  label: string;
165
177
  color?: string;
166
178
  style?: CSSProperties;
179
+ hoverStyle?: CSSProperties;
167
180
  }
168
- declare function Badge({ label, color, style }: BadgeProps): react_jsx_runtime.JSX.Element;
181
+ declare function Badge({ label, color, style, hoverStyle }: BadgeProps): react_jsx_runtime.JSX.Element;
169
182
 
170
183
  interface ChipProps {
171
184
  label: string;
172
185
  color?: string;
173
186
  style?: CSSProperties;
187
+ hoverStyle?: CSSProperties;
174
188
  }
175
- declare function Chip({ label, color, style }: ChipProps): react_jsx_runtime.JSX.Element;
189
+ declare function Chip({ label, color, style, hoverStyle }: ChipProps): react_jsx_runtime.JSX.Element;
176
190
 
177
191
  interface ButtonProps {
178
192
  label: string;
@@ -180,8 +194,9 @@ interface ButtonProps {
180
194
  onAction?: (type: string, actionId: string) => void;
181
195
  disabled?: boolean;
182
196
  style?: CSSProperties;
197
+ hoverStyle?: CSSProperties;
183
198
  }
184
- declare function Button({ label, action, onAction, disabled, style }: ButtonProps): react_jsx_runtime.JSX.Element;
199
+ declare function Button({ label, action, onAction, disabled, style, hoverStyle }: ButtonProps): react_jsx_runtime.JSX.Element;
185
200
 
186
201
  interface ToggleProps {
187
202
  value: boolean;
@@ -189,8 +204,9 @@ interface ToggleProps {
189
204
  onAction?: (type: string, actionId: string, payload?: unknown) => void;
190
205
  disabled?: boolean;
191
206
  style?: CSSProperties;
207
+ hoverStyle?: CSSProperties;
192
208
  }
193
- declare function Toggle({ value, onToggle, onAction, disabled, style }: ToggleProps): react_jsx_runtime.JSX.Element;
209
+ declare function Toggle({ value, onToggle, onAction, disabled, style, hoverStyle }: ToggleProps): react_jsx_runtime.JSX.Element;
194
210
 
195
211
  /**
196
212
  * @safe-ugc-ui/react --- Node Renderer
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/UGCRenderer.tsx
2
- import { useMemo } from "react";
2
+ import { useMemo as useMemo2 } from "react";
3
3
  import { validate, validateRaw } from "@safe-ugc-ui/validator";
4
4
 
5
5
  // src/UGCContainer.tsx
@@ -90,6 +90,7 @@ function resolveValue(value, state, locals) {
90
90
  }
91
91
 
92
92
  // src/style-mapper.ts
93
+ import { ALLOWED_TRANSITION_PROPERTIES } from "@safe-ugc-ui/types";
93
94
  var DIRECT_MAP_PROPS = [
94
95
  "display",
95
96
  "flexDirection",
@@ -246,8 +247,52 @@ function mapStyle(style, state, locals) {
246
247
  style.backgroundGradient
247
248
  );
248
249
  }
250
+ if (style.transition) {
251
+ const transitionCss = mapTransition(style.transition);
252
+ if (transitionCss) {
253
+ css.transition = transitionCss;
254
+ }
255
+ }
249
256
  return css;
250
257
  }
258
+ var CSS_PROPERTY_NAME_MAP = {
259
+ borderRadiusTopLeft: "border-top-left-radius",
260
+ borderRadiusTopRight: "border-top-right-radius",
261
+ borderRadiusBottomLeft: "border-bottom-left-radius",
262
+ borderRadiusBottomRight: "border-bottom-right-radius"
263
+ };
264
+ function toCssPropertyName(prop) {
265
+ if (prop in CSS_PROPERTY_NAME_MAP) {
266
+ return CSS_PROPERTY_NAME_MAP[prop];
267
+ }
268
+ return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
269
+ }
270
+ function mapTransition(transition) {
271
+ if (!transition) return void 0;
272
+ const items = Array.isArray(transition) ? transition : [transition];
273
+ const parts = [];
274
+ for (const item of items) {
275
+ if (typeof item !== "object" || item === null) continue;
276
+ const t = item;
277
+ const property = t.property;
278
+ const duration = t.duration;
279
+ if (typeof property !== "string" || typeof duration !== "number") continue;
280
+ if (!ALLOWED_TRANSITION_PROPERTIES.includes(property)) {
281
+ continue;
282
+ }
283
+ const ALLOWED_EASINGS = /* @__PURE__ */ new Set(["ease", "linear", "ease-in", "ease-out", "ease-in-out"]);
284
+ const cssProperty = toCssPropertyName(property);
285
+ let part = `${cssProperty} ${duration}ms`;
286
+ if (typeof t.easing === "string" && ALLOWED_EASINGS.has(t.easing)) {
287
+ part += ` ${t.easing}`;
288
+ }
289
+ if (typeof t.delay === "number" && t.delay > 0) {
290
+ part += ` ${t.delay}ms`;
291
+ }
292
+ parts.push(part);
293
+ }
294
+ return parts.length > 0 ? parts.join(", ") : void 0;
295
+ }
251
296
 
252
297
  // src/asset-resolver.ts
253
298
  function resolveAsset(path, assets) {
@@ -258,10 +303,29 @@ function resolveAsset(path, assets) {
258
303
  return void 0;
259
304
  }
260
305
 
306
+ // src/hooks/useHoverStyle.ts
307
+ import { useState, useMemo } from "react";
308
+ function useHoverStyle(baseStyle, hoverStyle) {
309
+ const [hovered, setHovered] = useState(false);
310
+ const mergedStyle = useMemo(() => {
311
+ if (!hoverStyle || !hovered) return baseStyle;
312
+ return { ...baseStyle, ...hoverStyle };
313
+ }, [baseStyle, hoverStyle, hovered]);
314
+ if (!hoverStyle) {
315
+ return { style: baseStyle, onMouseEnter: void 0, onMouseLeave: void 0 };
316
+ }
317
+ return {
318
+ style: mergedStyle,
319
+ onMouseEnter: () => setHovered(true),
320
+ onMouseLeave: () => setHovered(false)
321
+ };
322
+ }
323
+
261
324
  // src/components/Box.tsx
262
325
  import { jsx as jsx2 } from "react/jsx-runtime";
263
- function Box({ style, children }) {
264
- return /* @__PURE__ */ jsx2("div", { style, children });
326
+ function Box({ style, hoverStyle, children }) {
327
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
328
+ return /* @__PURE__ */ jsx2("div", { style: resolvedStyle, onMouseEnter, onMouseLeave, children });
265
329
  }
266
330
 
267
331
  // src/components/Row.tsx
@@ -270,8 +334,9 @@ var rowBase = {
270
334
  display: "flex",
271
335
  flexDirection: "row"
272
336
  };
273
- function Row({ style, children }) {
274
- return /* @__PURE__ */ jsx3("div", { style: { ...rowBase, ...style }, children });
337
+ function Row({ style, hoverStyle, children }) {
338
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
339
+ return /* @__PURE__ */ jsx3("div", { style: { ...rowBase, ...resolvedStyle }, onMouseEnter, onMouseLeave, children });
275
340
  }
276
341
 
277
342
  // src/components/Column.tsx
@@ -280,23 +345,26 @@ var columnBase = {
280
345
  display: "flex",
281
346
  flexDirection: "column"
282
347
  };
283
- function Column({ style, children }) {
284
- return /* @__PURE__ */ jsx4("div", { style: { ...columnBase, ...style }, children });
348
+ function Column({ style, hoverStyle, children }) {
349
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
350
+ return /* @__PURE__ */ jsx4("div", { style: { ...columnBase, ...resolvedStyle }, onMouseEnter, onMouseLeave, children });
285
351
  }
286
352
 
287
353
  // src/components/Text.tsx
288
354
  import { jsx as jsx5 } from "react/jsx-runtime";
289
- function Text({ content, style }) {
290
- return /* @__PURE__ */ jsx5("span", { style, children: content });
355
+ function Text({ content, style, hoverStyle }) {
356
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
357
+ return /* @__PURE__ */ jsx5("span", { style: resolvedStyle, onMouseEnter, onMouseLeave, children: content });
291
358
  }
292
359
 
293
360
  // src/components/Image.tsx
294
361
  import { jsx as jsx6 } from "react/jsx-runtime";
295
- function Image({ src, alt, style }) {
362
+ function Image({ src, alt, style, hoverStyle }) {
363
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
296
364
  if (src.startsWith("@assets/")) {
297
365
  return null;
298
366
  }
299
- return /* @__PURE__ */ jsx6("img", { src, alt: alt ?? "", style });
367
+ return /* @__PURE__ */ jsx6("img", { src, alt: alt ?? "", style: resolvedStyle, onMouseEnter, onMouseLeave });
300
368
  }
301
369
 
302
370
  // src/components/Stack.tsx
@@ -304,8 +372,9 @@ import { jsx as jsx7 } from "react/jsx-runtime";
304
372
  var stackBase = {
305
373
  position: "relative"
306
374
  };
307
- function Stack({ style, children }) {
308
- return /* @__PURE__ */ jsx7("div", { style: { ...stackBase, ...style }, children });
375
+ function Stack({ style, hoverStyle, children }) {
376
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
377
+ return /* @__PURE__ */ jsx7("div", { style: { ...stackBase, ...resolvedStyle }, onMouseEnter, onMouseLeave, children });
309
378
  }
310
379
 
311
380
  // src/components/Grid.tsx
@@ -313,14 +382,16 @@ import { jsx as jsx8 } from "react/jsx-runtime";
313
382
  var gridBase = {
314
383
  display: "grid"
315
384
  };
316
- function Grid({ style, children }) {
317
- return /* @__PURE__ */ jsx8("div", { style: { ...gridBase, ...style }, children });
385
+ function Grid({ style, hoverStyle, children }) {
386
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
387
+ return /* @__PURE__ */ jsx8("div", { style: { ...gridBase, ...resolvedStyle }, onMouseEnter, onMouseLeave, children });
318
388
  }
319
389
 
320
390
  // src/components/Spacer.tsx
321
391
  import { jsx as jsx9 } from "react/jsx-runtime";
322
- function Spacer({ size, style }) {
323
- return /* @__PURE__ */ jsx9("div", { style: { width: size, height: size, flexShrink: 0, ...style } });
392
+ function Spacer({ size, style, hoverStyle }) {
393
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
394
+ return /* @__PURE__ */ jsx9("div", { style: { width: size, height: size, flexShrink: 0, ...resolvedStyle }, onMouseEnter, onMouseLeave });
324
395
  }
325
396
 
326
397
  // src/components/Divider.tsx
@@ -331,22 +402,26 @@ function formatThickness(thickness) {
331
402
  if (/^-?\d+(\.\d+)?$/.test(thickness)) return `${thickness}px`;
332
403
  return thickness;
333
404
  }
334
- function Divider({ color, thickness, style }) {
405
+ function Divider({ color, thickness, style, hoverStyle }) {
406
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
335
407
  return /* @__PURE__ */ jsx10(
336
408
  "div",
337
409
  {
338
410
  style: {
339
411
  borderTop: `${formatThickness(thickness)} solid ${color ?? "#e0e0e0"}`,
340
412
  width: "100%",
341
- ...style
342
- }
413
+ ...resolvedStyle
414
+ },
415
+ onMouseEnter,
416
+ onMouseLeave
343
417
  }
344
418
  );
345
419
  }
346
420
 
347
421
  // src/components/Icon.tsx
348
422
  import { jsx as jsx11 } from "react/jsx-runtime";
349
- function Icon({ name, size, color, iconResolver, style }) {
423
+ function Icon({ name, size, color, iconResolver, style, hoverStyle }) {
424
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
350
425
  if (!iconResolver) {
351
426
  return null;
352
427
  }
@@ -359,8 +434,10 @@ function Icon({ name, size, color, iconResolver, style }) {
359
434
  display: "inline-flex",
360
435
  alignItems: "center",
361
436
  justifyContent: "center",
362
- ...style
437
+ ...resolvedStyle
363
438
  },
439
+ onMouseEnter,
440
+ onMouseLeave,
364
441
  children: iconResolver(name)
365
442
  }
366
443
  );
@@ -368,8 +445,9 @@ function Icon({ name, size, color, iconResolver, style }) {
368
445
 
369
446
  // src/components/ProgressBar.tsx
370
447
  import { jsx as jsx12 } from "react/jsx-runtime";
371
- function ProgressBar({ value, max, color, style }) {
448
+ function ProgressBar({ value, max, color, style, hoverStyle }) {
372
449
  const percentage = max <= 0 ? 0 : Math.min(100, Math.max(0, value / max * 100));
450
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
373
451
  return /* @__PURE__ */ jsx12(
374
452
  "div",
375
453
  {
@@ -377,8 +455,10 @@ function ProgressBar({ value, max, color, style }) {
377
455
  backgroundColor: "#e0e0e0",
378
456
  borderRadius: 4,
379
457
  overflow: "hidden",
380
- ...style
458
+ ...resolvedStyle
381
459
  },
460
+ onMouseEnter,
461
+ onMouseLeave,
382
462
  children: /* @__PURE__ */ jsx12(
383
463
  "div",
384
464
  {
@@ -395,7 +475,8 @@ function ProgressBar({ value, max, color, style }) {
395
475
 
396
476
  // src/components/Avatar.tsx
397
477
  import { jsx as jsx13 } from "react/jsx-runtime";
398
- function Avatar({ src, alt, size, style }) {
478
+ function Avatar({ src, alt, size, style, hoverStyle }) {
479
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
399
480
  return /* @__PURE__ */ jsx13(
400
481
  "img",
401
482
  {
@@ -406,15 +487,18 @@ function Avatar({ src, alt, size, style }) {
406
487
  height: size ?? 40,
407
488
  borderRadius: "50%",
408
489
  objectFit: "cover",
409
- ...style
410
- }
490
+ ...resolvedStyle
491
+ },
492
+ onMouseEnter,
493
+ onMouseLeave
411
494
  }
412
495
  );
413
496
  }
414
497
 
415
498
  // src/components/Badge.tsx
416
499
  import { jsx as jsx14 } from "react/jsx-runtime";
417
- function Badge({ label, color, style }) {
500
+ function Badge({ label, color, style, hoverStyle }) {
501
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
418
502
  return /* @__PURE__ */ jsx14(
419
503
  "span",
420
504
  {
@@ -424,8 +508,10 @@ function Badge({ label, color, style }) {
424
508
  borderRadius: 12,
425
509
  backgroundColor: color ?? "#e0e0e0",
426
510
  fontSize: 12,
427
- ...style
511
+ ...resolvedStyle
428
512
  },
513
+ onMouseEnter,
514
+ onMouseLeave,
429
515
  children: label
430
516
  }
431
517
  );
@@ -433,7 +519,8 @@ function Badge({ label, color, style }) {
433
519
 
434
520
  // src/components/Chip.tsx
435
521
  import { jsx as jsx15 } from "react/jsx-runtime";
436
- function Chip({ label, color, style }) {
522
+ function Chip({ label, color, style, hoverStyle }) {
523
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
437
524
  return /* @__PURE__ */ jsx15(
438
525
  "span",
439
526
  {
@@ -443,8 +530,10 @@ function Chip({ label, color, style }) {
443
530
  borderRadius: 16,
444
531
  border: `1px solid ${color ?? "#e0e0e0"}`,
445
532
  fontSize: 14,
446
- ...style
533
+ ...resolvedStyle
447
534
  },
535
+ onMouseEnter,
536
+ onMouseLeave,
448
537
  children: label
449
538
  }
450
539
  );
@@ -452,7 +541,8 @@ function Chip({ label, color, style }) {
452
541
 
453
542
  // src/components/Button.tsx
454
543
  import { jsx as jsx16 } from "react/jsx-runtime";
455
- function Button({ label, action, onAction, disabled, style }) {
544
+ function Button({ label, action, onAction, disabled, style, hoverStyle }) {
545
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
456
546
  return /* @__PURE__ */ jsx16(
457
547
  "button",
458
548
  {
@@ -463,8 +553,10 @@ function Button({ label, action, onAction, disabled, style }) {
463
553
  borderRadius: 6,
464
554
  border: "1px solid #ccc",
465
555
  cursor: disabled ? "default" : "pointer",
466
- ...style
556
+ ...resolvedStyle
467
557
  },
558
+ onMouseEnter,
559
+ onMouseLeave,
468
560
  children: label
469
561
  }
470
562
  );
@@ -472,7 +564,8 @@ function Button({ label, action, onAction, disabled, style }) {
472
564
 
473
565
  // src/components/Toggle.tsx
474
566
  import { jsx as jsx17 } from "react/jsx-runtime";
475
- function Toggle({ value, onToggle, onAction, disabled, style }) {
567
+ function Toggle({ value, onToggle, onAction, disabled, style, hoverStyle }) {
568
+ const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);
476
569
  return /* @__PURE__ */ jsx17(
477
570
  "button",
478
571
  {
@@ -485,8 +578,10 @@ function Toggle({ value, onToggle, onAction, disabled, style }) {
485
578
  backgroundColor: value ? "#4caf50" : "#e0e0e0",
486
579
  color: value ? "#fff" : "#333",
487
580
  cursor: disabled ? "default" : "pointer",
488
- ...style
581
+ ...resolvedStyle
489
582
  },
583
+ onMouseEnter,
584
+ onMouseLeave,
490
585
  children: value ? "ON" : "OFF"
491
586
  }
492
587
  );
@@ -568,20 +663,22 @@ function renderNode(node, ctx, key) {
568
663
  ctx.limits.overflowAutoCount += overflowDelta;
569
664
  ctx.limits.textBytes += textDelta;
570
665
  const cssStyle = mapStyle(mergedRawStyle, ctx.state, ctx.locals);
666
+ const rawHoverStyle = mergedRawStyle?.hoverStyle;
667
+ const cssHoverStyle = rawHoverStyle ? mapStyle(rawHoverStyle, ctx.state, ctx.locals) : void 0;
571
668
  const childElements = renderChildren(n.children, ctx);
572
669
  switch (n.type) {
573
670
  case "Box":
574
- return /* @__PURE__ */ jsx18(Box, { style: cssStyle, children: childElements }, key);
671
+ return /* @__PURE__ */ jsx18(Box, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
575
672
  case "Row":
576
- return /* @__PURE__ */ jsx18(Row, { style: cssStyle, children: childElements }, key);
673
+ return /* @__PURE__ */ jsx18(Row, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
577
674
  case "Column":
578
- return /* @__PURE__ */ jsx18(Column, { style: cssStyle, children: childElements }, key);
675
+ return /* @__PURE__ */ jsx18(Column, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
579
676
  case "Stack":
580
- return /* @__PURE__ */ jsx18(Stack, { style: cssStyle, children: childElements }, key);
677
+ return /* @__PURE__ */ jsx18(Stack, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
581
678
  case "Grid":
582
- return /* @__PURE__ */ jsx18(Grid, { style: cssStyle, children: childElements }, key);
679
+ return /* @__PURE__ */ jsx18(Grid, { style: cssStyle, hoverStyle: cssHoverStyle, children: childElements }, key);
583
680
  case "Text":
584
- return /* @__PURE__ */ jsx18(Text, { content: resolvedTextContent, style: cssStyle }, key);
681
+ return /* @__PURE__ */ jsx18(Text, { content: resolvedTextContent, style: cssStyle, hoverStyle: cssHoverStyle }, key);
585
682
  case "Image": {
586
683
  let src = rv(n.src);
587
684
  if (typeof src !== "string" || !src) return null;
@@ -592,7 +689,7 @@ function renderNode(node, ctx, key) {
592
689
  if (typeof resolved === "string" && resolved.trim().toLowerCase().startsWith("javascript:")) return null;
593
690
  const resolvedAlt = rv(n.alt);
594
691
  const alt = typeof resolvedAlt === "string" ? resolvedAlt : void 0;
595
- return /* @__PURE__ */ jsx18(Image, { src: resolved, alt, style: cssStyle }, key);
692
+ return /* @__PURE__ */ jsx18(Image, { src: resolved, alt, style: cssStyle, hoverStyle: cssHoverStyle }, key);
596
693
  }
597
694
  case "Avatar": {
598
695
  let src = rv(n.src);
@@ -604,7 +701,7 @@ function renderNode(node, ctx, key) {
604
701
  if (typeof resolved === "string" && resolved.trim().toLowerCase().startsWith("javascript:")) return null;
605
702
  const resolvedSize = rv(n.size);
606
703
  const size = typeof resolvedSize === "number" || typeof resolvedSize === "string" ? resolvedSize : void 0;
607
- return /* @__PURE__ */ jsx18(Avatar, { src: resolved, size, style: cssStyle }, key);
704
+ return /* @__PURE__ */ jsx18(Avatar, { src: resolved, size, style: cssStyle, hoverStyle: cssHoverStyle }, key);
608
705
  }
609
706
  case "Icon": {
610
707
  const nameVal = n.name;
@@ -620,7 +717,8 @@ function renderNode(node, ctx, key) {
620
717
  size,
621
718
  color,
622
719
  iconResolver: ctx.iconResolver,
623
- style: cssStyle
720
+ style: cssStyle,
721
+ hoverStyle: cssHoverStyle
624
722
  },
625
723
  key
626
724
  );
@@ -628,14 +726,14 @@ function renderNode(node, ctx, key) {
628
726
  case "Spacer": {
629
727
  const resolvedSize = rv(n.size);
630
728
  const size = typeof resolvedSize === "number" || typeof resolvedSize === "string" ? resolvedSize : void 0;
631
- return /* @__PURE__ */ jsx18(Spacer, { size, style: cssStyle }, key);
729
+ return /* @__PURE__ */ jsx18(Spacer, { size, style: cssStyle, hoverStyle: cssHoverStyle }, key);
632
730
  }
633
731
  case "Divider": {
634
732
  const resolvedColor = rv(n.color);
635
733
  const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
636
734
  const resolvedThickness = rv(n.thickness);
637
735
  const thickness = typeof resolvedThickness === "number" || typeof resolvedThickness === "string" ? resolvedThickness : void 0;
638
- return /* @__PURE__ */ jsx18(Divider, { color, thickness, style: cssStyle }, key);
736
+ return /* @__PURE__ */ jsx18(Divider, { color, thickness, style: cssStyle, hoverStyle: cssHoverStyle }, key);
639
737
  }
640
738
  case "ProgressBar": {
641
739
  const resolvedValue = rv(n.value);
@@ -644,21 +742,21 @@ function renderNode(node, ctx, key) {
644
742
  const max = typeof resolvedMax === "number" ? resolvedMax : 100;
645
743
  const resolvedColor = rv(n.color);
646
744
  const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
647
- return /* @__PURE__ */ jsx18(ProgressBar, { value, max, color, style: cssStyle }, key);
745
+ return /* @__PURE__ */ jsx18(ProgressBar, { value, max, color, style: cssStyle, hoverStyle: cssHoverStyle }, key);
648
746
  }
649
747
  case "Badge": {
650
748
  const resolvedLabel = rv(n.label);
651
749
  const label = typeof resolvedLabel === "string" ? resolvedLabel : "";
652
750
  const resolvedColor = rv(n.color);
653
751
  const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
654
- return /* @__PURE__ */ jsx18(Badge, { label, color, style: cssStyle }, key);
752
+ return /* @__PURE__ */ jsx18(Badge, { label, color, style: cssStyle, hoverStyle: cssHoverStyle }, key);
655
753
  }
656
754
  case "Chip": {
657
755
  const resolvedLabel = rv(n.label);
658
756
  const label = typeof resolvedLabel === "string" ? resolvedLabel : "";
659
757
  const resolvedColor = rv(n.color);
660
758
  const color = typeof resolvedColor === "string" ? resolvedColor : void 0;
661
- return /* @__PURE__ */ jsx18(Chip, { label, color, style: cssStyle }, key);
759
+ return /* @__PURE__ */ jsx18(Chip, { label, color, style: cssStyle, hoverStyle: cssHoverStyle }, key);
662
760
  }
663
761
  case "Button": {
664
762
  const resolvedLabel = rv(n.label);
@@ -671,7 +769,8 @@ function renderNode(node, ctx, key) {
671
769
  label,
672
770
  action,
673
771
  onAction: ctx.onAction,
674
- style: cssStyle
772
+ style: cssStyle,
773
+ hoverStyle: cssHoverStyle
675
774
  },
676
775
  key
677
776
  );
@@ -687,7 +786,8 @@ function renderNode(node, ctx, key) {
687
786
  value,
688
787
  onToggle,
689
788
  onAction: ctx.onAction,
690
- style: cssStyle
789
+ style: cssStyle,
790
+ hoverStyle: cssHoverStyle
691
791
  },
692
792
  key
693
793
  );
@@ -764,7 +864,7 @@ function UGCRenderer({
764
864
  iconResolver,
765
865
  onAction
766
866
  }) {
767
- const result = useMemo(() => {
867
+ const result = useMemo2(() => {
768
868
  const validationResult = typeof card === "string" ? validateRaw(card) : validate(card);
769
869
  if (!validationResult.valid) {
770
870
  return { valid: false, errors: validationResult.errors };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/UGCRenderer.tsx","../src/UGCContainer.tsx","../src/node-renderer.tsx","../src/state-resolver.ts","../src/style-mapper.ts","../src/asset-resolver.ts","../src/components/Box.tsx","../src/components/Row.tsx","../src/components/Column.tsx","../src/components/Text.tsx","../src/components/Image.tsx","../src/components/Stack.tsx","../src/components/Grid.tsx","../src/components/Spacer.tsx","../src/components/Divider.tsx","../src/components/Icon.tsx","../src/components/ProgressBar.tsx","../src/components/Avatar.tsx","../src/components/Badge.tsx","../src/components/Chip.tsx","../src/components/Button.tsx","../src/components/Toggle.tsx"],"sourcesContent":["/**\n * @safe-ugc-ui/react --- UGC Renderer\n *\n * Top-level component that validates and renders a UGC card.\n *\n * Pipeline:\n * 1. Accept a card (UGCCard object or raw JSON string)\n * 2. Validate via @safe-ugc-ui/validator\n * 3. If invalid, render nothing (or optional error fallback)\n * 4. If valid, wrap in UGCContainer and render the view tree\n *\n * Props:\n * - card: UGCCard object or raw JSON string\n * - viewName: Name of the view to render (defaults to first view)\n * - assets: Mapping of asset keys to actual URLs\n * - state: Optional state override (merged with card.state)\n * - onError: Optional error callback\n * - iconResolver: Optional callback to resolve icon names to ReactNode\n * - onAction: Optional callback for Button/Toggle actions\n */\n\nimport { useMemo } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\nimport { validate, validateRaw } from '@safe-ugc-ui/validator';\nimport type { UGCCard } from '@safe-ugc-ui/types';\n\nimport { UGCContainer } from './UGCContainer.js';\nimport { renderTree } from './node-renderer.js';\nimport type { AssetMap } from './asset-resolver.js';\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface UGCRendererProps {\n /** The UGC card to render --- either a parsed object or a raw JSON string. */\n card: UGCCard | string;\n\n /** Name of the view to render. Defaults to the first view in the card. */\n viewName?: string;\n\n /** Asset map: keys are asset identifiers, values are resolved URLs. */\n assets?: AssetMap;\n\n /** Optional state override. Merged on top of the card's own state. */\n state?: Record<string, unknown>;\n\n /** Optional CSS style for the outer container. */\n containerStyle?: CSSProperties;\n\n /** Optional callback invoked when validation fails or runtime limits are hit. */\n onError?: (errors: Array<{ code: string; message: string; path: string }>) => void;\n\n /** Optional callback to resolve icon names to React elements. */\n iconResolver?: (name: string) => ReactNode;\n\n /** Optional callback for Button/Toggle interaction actions. */\n onAction?: (type: string, actionId: string, payload?: unknown) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Top-level UGC card renderer.\n *\n * Validates the card, then renders the specified view inside a secure\n * UGCContainer. Returns null if validation fails.\n */\nexport function UGCRenderer({\n card,\n viewName,\n assets = {},\n state: stateOverride,\n containerStyle,\n onError,\n iconResolver,\n onAction,\n}: UGCRendererProps) {\n const result = useMemo(() => {\n // 1. Validate\n const validationResult =\n typeof card === 'string' ? validateRaw(card) : validate(card);\n\n if (!validationResult.valid) {\n return { valid: false as const, errors: validationResult.errors };\n }\n\n // 2. Parse card object\n const cardObj: UGCCard =\n typeof card === 'string'\n ? (JSON.parse(card) as UGCCard)\n : card;\n\n // 3. Determine which view to render\n const views = cardObj.views;\n const viewKeys = Object.keys(views);\n const selectedView = viewName && viewName in views\n ? viewName\n : viewKeys[0];\n\n if (!selectedView || !(selectedView in views)) {\n return { valid: false as const, errors: [] };\n }\n\n // 4. Merge state\n const mergedState: Record<string, unknown> = {\n ...(cardObj.state ?? {}),\n ...(stateOverride ?? {}),\n };\n\n // 5. Extract card-level styles\n const cardStyles = cardObj.styles as Record<string, Record<string, unknown>> | undefined;\n\n return {\n valid: true as const,\n rootNode: views[selectedView],\n state: mergedState,\n cardStyles,\n };\n }, [card, viewName, stateOverride]);\n\n // Handle invalid cards\n if (!result.valid) {\n if (onError && result.errors.length > 0) {\n onError(result.errors);\n }\n return null;\n }\n\n // Render the view tree inside the secure container\n return (\n <UGCContainer style={containerStyle}>\n {renderTree(\n result.rootNode,\n result.state,\n assets,\n result.cardStyles,\n iconResolver,\n onAction,\n onError,\n )}\n </UGCContainer>\n );\n}\n","/**\n * @safe-ugc-ui/react — UGC Container\n *\n * Wrapper component that provides security isolation for UGC content.\n *\n * CSS isolation features:\n * - overflow: hidden — prevents content from escaping bounds\n * - isolation: isolate — creates a new stacking context\n * - contain: content — limits layout/paint/style to this subtree\n * - position: relative — establishes positioning context for children\n */\n\nimport type { CSSProperties, ReactNode } from 'react';\n\ninterface UGCContainerProps {\n children?: ReactNode;\n style?: CSSProperties;\n}\n\nconst containerStyle: CSSProperties = {\n overflow: 'hidden',\n isolation: 'isolate',\n contain: 'content',\n position: 'relative',\n};\n\n/**\n * Security isolation wrapper for UGC content.\n * All UGC card renderings should be wrapped in this container.\n */\nexport function UGCContainer({ children, style }: UGCContainerProps) {\n const mergedStyle: CSSProperties = style\n ? { ...containerStyle, ...style }\n : containerStyle;\n\n return <div style={mergedStyle}>{children}</div>;\n}\n","/**\n * @safe-ugc-ui/react --- Node Renderer\n *\n * Recursive renderer that maps UGC node types to React components.\n * Supports all 16 component types, for-loop rendering, $style merge,\n * and runtime limits pre-check.\n *\n * For each node:\n * 1. Pre-check runtime limits (node count, style bytes, overflow, text bytes)\n * 2. Merge $style with inline styles\n * 3. Resolve $ref values in node fields using state-resolver (with locals)\n * 4. Map style fields to React CSSProperties using style-mapper\n * 5. Resolve asset paths for Image/Avatar src using asset-resolver\n * 6. Render the appropriate component\n * 7. Recursively render children (arrays and for-loops)\n */\n\nimport type { ReactNode } from 'react';\nimport {\n MAX_NODE_COUNT,\n MAX_LOOP_ITERATIONS,\n TEXT_CONTENT_TOTAL_MAX_BYTES,\n STYLE_OBJECTS_TOTAL_MAX_BYTES,\n MAX_OVERFLOW_AUTO_COUNT,\n} from '@safe-ugc-ui/types';\n\nimport { resolveRef, resolveValue } from './state-resolver.js';\nimport { mapStyle } from './style-mapper.js';\nimport { resolveAsset } from './asset-resolver.js';\nimport type { AssetMap } from './asset-resolver.js';\nimport { Box } from './components/Box.js';\nimport { Row } from './components/Row.js';\nimport { Column } from './components/Column.js';\nimport { Text } from './components/Text.js';\nimport { Image } from './components/Image.js';\nimport { Stack } from './components/Stack.js';\nimport { Grid } from './components/Grid.js';\nimport { Spacer } from './components/Spacer.js';\nimport { Divider } from './components/Divider.js';\nimport { Icon } from './components/Icon.js';\nimport { ProgressBar } from './components/ProgressBar.js';\nimport { Avatar } from './components/Avatar.js';\nimport { Badge } from './components/Badge.js';\nimport { Chip } from './components/Chip.js';\nimport { Button } from './components/Button.js';\nimport { Toggle } from './components/Toggle.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A generic UGC node shape. We use Record<string, unknown> for flexibility\n * since the validator has already verified the structure before rendering.\n */\ninterface UGCNodeLike {\n type: string;\n style?: Record<string, unknown>;\n children?: unknown;\n condition?: unknown;\n [key: string]: unknown;\n}\n\ninterface ForLoopLike {\n for: string;\n in: string;\n template: unknown;\n}\n\n/**\n * Runtime limits tracking object. Mutated during rendering to\n * track cumulative resource usage across the entire card.\n */\nexport interface RuntimeLimits {\n nodeCount: number;\n textBytes: number;\n styleBytes: number;\n overflowAutoCount: number;\n}\n\nexport interface RenderContext {\n state: Record<string, unknown>;\n assets: AssetMap;\n locals?: Record<string, unknown>;\n cardStyles?: Record<string, Record<string, unknown>>;\n iconResolver?: (name: string) => ReactNode;\n onAction?: (type: string, actionId: string, payload?: unknown) => void;\n onError?: (errors: Array<{ code: string; message: string; path: string }>) => void;\n limits: RuntimeLimits;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isForLoop(obj: unknown): obj is ForLoopLike {\n if (obj == null || typeof obj !== 'object' || Array.isArray(obj)) return false;\n const o = obj as Record<string, unknown>;\n return typeof o.for === 'string' && typeof o.in === 'string' && o.template != null;\n}\n\nfunction utf8ByteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n if (code <= 0x7f) {\n bytes += 1;\n } else if (code <= 0x7ff) {\n bytes += 2;\n } else if (code >= 0xd800 && code <= 0xdbff) {\n // Surrogate pair → 4 UTF-8 bytes\n bytes += 4;\n i++; // skip low surrogate\n } else {\n bytes += 3;\n }\n }\n return bytes;\n}\n\n/**\n * Merge $style from cardStyles with inline style.\n * Returns the merged raw style (unresolved $ref values) and the style\n * without $style key.\n */\nfunction mergeStyleWithCardStyles(\n nodeStyle: Record<string, unknown> | undefined,\n cardStyles: Record<string, Record<string, unknown>> | undefined,\n): Record<string, unknown> | undefined {\n if (!nodeStyle) return undefined;\n\n const rawStyleName = nodeStyle.$style;\n const styleName = typeof rawStyleName === 'string' ? rawStyleName.trim() : rawStyleName;\n if (!styleName || typeof styleName !== 'string' || !cardStyles) {\n // Return style without $style key if present\n if (nodeStyle.$style !== undefined) {\n const { $style: _, ...rest } = nodeStyle;\n return rest;\n }\n return nodeStyle;\n }\n\n const baseStyle = cardStyles[styleName];\n if (!baseStyle) return nodeStyle;\n\n // Merge: base from cardStyles, overridden by inline (excluding $style key)\n const { $style: _, ...inlineWithout$style } = nodeStyle;\n return { ...baseStyle, ...inlineWithout$style };\n}\n\n// ---------------------------------------------------------------------------\n// renderNode --- recursive node renderer\n// ---------------------------------------------------------------------------\n\n/**\n * Render a single UGC node to a React element.\n *\n * Performs runtime limits pre-check BEFORE rendering to prevent\n * DOM pollution when limits are exceeded.\n */\nexport function renderNode(\n node: unknown,\n ctx: RenderContext,\n key: string | number,\n): ReactNode {\n if (node == null || typeof node !== 'object') return null;\n\n const n = node as UGCNodeLike;\n if (!n.type) return null;\n\n // --- Compute all deltas before committing any ---\n const mergedRawStyle = mergeStyleWithCardStyles(n.style, ctx.cardStyles);\n const rv = (val: unknown) => resolveValue(val, ctx.state, ctx.locals);\n\n const styleDelta = mergedRawStyle ? utf8ByteLength(JSON.stringify(mergedRawStyle)) : 0;\n const overflowDelta = mergedRawStyle?.overflow === 'auto' ? 1 : 0;\n\n // Text content: resolve once, reuse in render\n let resolvedTextContent: string | undefined;\n let textDelta = 0;\n if (n.type === 'Text') {\n const raw = rv((n as Record<string, unknown>).content);\n resolvedTextContent = typeof raw === 'string' ? raw : '';\n textDelta = utf8ByteLength(resolvedTextContent);\n }\n\n // --- Batch limit checks (all-or-nothing) ---\n if (ctx.limits.nodeCount + 1 > MAX_NODE_COUNT) {\n ctx.onError?.([{ code: 'RUNTIME_NODE_LIMIT', message: `Node count exceeds maximum of ${MAX_NODE_COUNT}`, path: String(key) }]);\n return null;\n }\n if (ctx.limits.styleBytes + styleDelta > STYLE_OBJECTS_TOTAL_MAX_BYTES) {\n ctx.onError?.([{ code: 'RUNTIME_STYLE_LIMIT', message: `Style bytes exceed maximum of ${STYLE_OBJECTS_TOTAL_MAX_BYTES}`, path: String(key) }]);\n return null;\n }\n if (ctx.limits.overflowAutoCount + overflowDelta > MAX_OVERFLOW_AUTO_COUNT) {\n ctx.onError?.([{ code: 'RUNTIME_OVERFLOW_LIMIT', message: `Overflow auto count exceeds maximum of ${MAX_OVERFLOW_AUTO_COUNT}`, path: String(key) }]);\n return null;\n }\n if (ctx.limits.textBytes + textDelta > TEXT_CONTENT_TOTAL_MAX_BYTES) {\n ctx.onError?.([{ code: 'RUNTIME_TEXT_LIMIT', message: `Text bytes exceed maximum of ${TEXT_CONTENT_TOTAL_MAX_BYTES}`, path: String(key) }]);\n return null;\n }\n\n // --- All checks passed: commit all deltas at once ---\n ctx.limits.nodeCount += 1;\n ctx.limits.styleBytes += styleDelta;\n ctx.limits.overflowAutoCount += overflowDelta;\n ctx.limits.textBytes += textDelta;\n\n // Resolve style to CSS\n const cssStyle = mapStyle(mergedRawStyle, ctx.state, ctx.locals);\n\n // Render children recursively\n const childElements = renderChildren(n.children, ctx);\n\n switch (n.type) {\n case 'Box':\n return <Box key={key} style={cssStyle}>{childElements}</Box>;\n\n case 'Row':\n return <Row key={key} style={cssStyle}>{childElements}</Row>;\n\n case 'Column':\n return <Column key={key} style={cssStyle}>{childElements}</Column>;\n\n case 'Stack':\n return <Stack key={key} style={cssStyle}>{childElements}</Stack>;\n\n case 'Grid':\n return <Grid key={key} style={cssStyle}>{childElements}</Grid>;\n\n case 'Text':\n return <Text key={key} content={resolvedTextContent!} style={cssStyle} />;\n\n case 'Image': {\n let src = rv((n as Record<string, unknown>).src);\n if (typeof src !== 'string' || !src) return null;\n if (!src.startsWith('@assets/')) return null;\n if (src.includes('../')) return null;\n const resolved = resolveAsset(src, ctx.assets);\n if (!resolved) return null;\n if (typeof resolved === 'string' && resolved.trim().toLowerCase().startsWith('javascript:')) return null;\n const resolvedAlt = rv((n as Record<string, unknown>).alt);\n const alt = typeof resolvedAlt === 'string' ? resolvedAlt : undefined;\n return <Image key={key} src={resolved} alt={alt} style={cssStyle} />;\n }\n\n case 'Avatar': {\n let src = rv((n as Record<string, unknown>).src);\n if (typeof src !== 'string' || !src) return null;\n if (!src.startsWith('@assets/')) return null;\n if (src.includes('../')) return null;\n const resolved = resolveAsset(src, ctx.assets);\n if (!resolved) return null;\n if (typeof resolved === 'string' && resolved.trim().toLowerCase().startsWith('javascript:')) return null;\n const resolvedSize = rv((n as Record<string, unknown>).size);\n const size = typeof resolvedSize === 'number' || typeof resolvedSize === 'string'\n ? resolvedSize : undefined;\n return <Avatar key={key} src={resolved} size={size} style={cssStyle} />;\n }\n\n case 'Icon': {\n const nameVal = (n as Record<string, unknown>).name;\n const name = typeof nameVal === 'string' ? nameVal : '';\n const resolvedSize = rv((n as Record<string, unknown>).size);\n const size = typeof resolvedSize === 'number' || typeof resolvedSize === 'string'\n ? resolvedSize : undefined;\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return (\n <Icon\n key={key}\n name={name}\n size={size}\n color={color}\n iconResolver={ctx.iconResolver}\n style={cssStyle}\n />\n );\n }\n\n case 'Spacer': {\n const resolvedSize = rv((n as Record<string, unknown>).size);\n const size = typeof resolvedSize === 'number' || typeof resolvedSize === 'string'\n ? resolvedSize : undefined;\n return <Spacer key={key} size={size} style={cssStyle} />;\n }\n\n case 'Divider': {\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n const resolvedThickness = rv((n as Record<string, unknown>).thickness);\n const thickness = typeof resolvedThickness === 'number' || typeof resolvedThickness === 'string'\n ? resolvedThickness : undefined;\n return <Divider key={key} color={color} thickness={thickness} style={cssStyle} />;\n }\n\n case 'ProgressBar': {\n const resolvedValue = rv((n as Record<string, unknown>).value);\n const value = typeof resolvedValue === 'number' ? resolvedValue : 0;\n const resolvedMax = rv((n as Record<string, unknown>).max);\n const max = typeof resolvedMax === 'number' ? resolvedMax : 100;\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return <ProgressBar key={key} value={value} max={max} color={color} style={cssStyle} />;\n }\n\n case 'Badge': {\n const resolvedLabel = rv((n as Record<string, unknown>).label);\n const label = typeof resolvedLabel === 'string' ? resolvedLabel : '';\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return <Badge key={key} label={label} color={color} style={cssStyle} />;\n }\n\n case 'Chip': {\n const resolvedLabel = rv((n as Record<string, unknown>).label);\n const label = typeof resolvedLabel === 'string' ? resolvedLabel : '';\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return <Chip key={key} label={label} color={color} style={cssStyle} />;\n }\n\n case 'Button': {\n const resolvedLabel = rv((n as Record<string, unknown>).label);\n const label = typeof resolvedLabel === 'string' ? resolvedLabel : '';\n const actionVal = (n as Record<string, unknown>).action;\n const action = typeof actionVal === 'string' ? actionVal : '';\n return (\n <Button\n key={key}\n label={label}\n action={action}\n onAction={ctx.onAction}\n style={cssStyle}\n />\n );\n }\n\n case 'Toggle': {\n const resolvedValue = rv((n as Record<string, unknown>).value);\n const value = typeof resolvedValue === 'boolean' ? resolvedValue : false;\n const onToggleVal = (n as Record<string, unknown>).onToggle;\n const onToggle = typeof onToggleVal === 'string' ? onToggleVal : '';\n return (\n <Toggle\n key={key}\n value={value}\n onToggle={onToggle}\n onAction={ctx.onAction}\n style={cssStyle}\n />\n );\n }\n\n default:\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// renderChildren --- handle children arrays and for-loops\n// ---------------------------------------------------------------------------\n\nfunction renderChildren(\n children: unknown,\n ctx: RenderContext,\n): ReactNode[] | null {\n if (!children) return null;\n\n // Array children\n if (Array.isArray(children)) {\n return children.map((child, index) => renderNode(child, ctx, index));\n }\n\n // For-loop children\n if (isForLoop(children)) {\n return renderForLoop(children, ctx);\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// renderForLoop --- iterate over state array, render template per item\n// ---------------------------------------------------------------------------\n\nfunction renderForLoop(\n loop: ForLoopLike,\n ctx: RenderContext,\n): ReactNode[] {\n // Resolve the source array from state (or locals)\n const source = resolveRef(loop.in, ctx.state, ctx.locals);\n if (source === undefined) {\n // Soft skip: source not found (may be provided at runtime or optional)\n return [];\n }\n if (!Array.isArray(source)) {\n // Hard error: source exists but is not an array (type mismatch)\n ctx.onError?.([{\n code: 'RUNTIME_LOOP_SOURCE_INVALID',\n message: `Loop source \"${loop.in}\" is not an array (got ${typeof source})`,\n path: `for(${loop.for} in ${loop.in})`,\n }]);\n return [];\n }\n\n // Cap iterations\n const maxIter = Math.min(source.length, MAX_LOOP_ITERATIONS);\n const elements: ReactNode[] = [];\n\n for (let i = 0; i < maxIter; i++) {\n const item = source[i];\n const newLocals: Record<string, unknown> = {\n ...ctx.locals,\n [loop.for]: item,\n index: i,\n };\n const childCtx: RenderContext = { ...ctx, locals: newLocals };\n elements.push(renderNode(loop.template, childCtx, i));\n }\n\n return elements;\n}\n\n// ---------------------------------------------------------------------------\n// RenderTree --- render an entire view tree\n// ---------------------------------------------------------------------------\n\n/**\n * Render a full view tree starting from the root node.\n *\n * @param rootNode - The root node of the view\n * @param state - Card state for $ref resolution\n * @param assets - Asset map for @assets/ resolution\n * @param cardStyles - Optional card-level named styles\n * @param iconResolver - Optional icon resolver callback\n * @param onAction - Optional action callback for Button/Toggle\n * @param onError - Optional error callback\n * @returns A React element tree\n */\nexport function renderTree(\n rootNode: unknown,\n state: Record<string, unknown>,\n assets: AssetMap,\n cardStyles?: Record<string, Record<string, unknown>>,\n iconResolver?: (name: string) => ReactNode,\n onAction?: (type: string, actionId: string, payload?: unknown) => void,\n onError?: (errors: Array<{ code: string; message: string; path: string }>) => void,\n): ReactNode {\n const limits: RuntimeLimits = {\n nodeCount: 0,\n textBytes: 0,\n styleBytes: 0,\n overflowAutoCount: 0,\n };\n const ctx: RenderContext = {\n state,\n assets,\n cardStyles,\n iconResolver,\n onAction,\n onError,\n limits,\n };\n return renderNode(rootNode, ctx, 'root');\n}\n","/**\n * @safe-ugc-ui/react — State Resolver\n *\n * Resolves $ref and $expr values from card state.\n *\n * - $ref: looks up a dotted path in the state object\n * - $expr: returns undefined (Phase 2 — expression evaluation not yet implemented)\n * - Literal values pass through unchanged\n */\n\nimport { PROTOTYPE_POLLUTION_SEGMENTS } from '@safe-ugc-ui/types';\n\n/**\n * Parse a $ref path into individual segments.\n * Handles both dot notation and bracket notation:\n * \"items[0].name\" → [\"items\", \"0\", \"name\"]\n * \"data[2][3]\" → [\"data\", \"2\", \"3\"]\n * \"simple.path\" → [\"simple\", \"path\"]\n */\nfunction parseRefSegments(path: string): string[] {\n const segments: string[] = [];\n const dotParts = path.split('.');\n for (const part of dotParts) {\n if (!part) continue;\n // Check for bracket notation: extract base and indices\n const bracketPattern = /\\[(\\d+)\\]/g;\n let match: RegExpExecArray | null;\n\n // Find the base name (before first bracket)\n const firstBracket = part.indexOf('[');\n if (firstBracket > 0) {\n segments.push(part.slice(0, firstBracket));\n } else if (firstBracket === -1) {\n // No brackets at all\n segments.push(part);\n continue;\n }\n\n // Extract all bracket indices\n while ((match = bracketPattern.exec(part)) !== null) {\n segments.push(match[1]);\n }\n\n // If part starts with [ and no base was added\n if (firstBracket === 0) {\n // Edge case: segment is just [0] with no base name\n // This shouldn't normally happen but handle gracefully\n }\n }\n return segments;\n}\n\n/**\n * Resolves a $ref path (e.g. \"$hp\", \"$items[0].name\") from card state.\n *\n * - Strips leading '$' from the path\n * - Parses dot notation and bracket notation into segments\n * - Blocks __proto__, constructor, prototype segments\n * - Max depth of 5 segments per spec\n * - Returns undefined if path doesn't exist\n */\nexport function resolveRef(\n refPath: string,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): unknown {\n const path = refPath.startsWith('$') ? refPath.slice(1) : refPath;\n const segments = parseRefSegments(path);\n\n // Block prototype pollution\n for (const seg of segments) {\n if ((PROTOTYPE_POLLUTION_SEGMENTS as readonly string[]).includes(seg)) {\n return undefined;\n }\n }\n\n // Max depth check\n if (segments.length > 5) return undefined;\n\n // Choose starting object: locals first, then state\n const firstSeg = segments[0];\n let current: unknown;\n if (locals && firstSeg && firstSeg in locals) {\n current = locals;\n } else {\n current = state;\n }\n\n // Traverse\n for (const seg of segments) {\n if (current == null || typeof current !== 'object') return undefined;\n if (Array.isArray(current)) {\n const idx = parseInt(seg, 10);\n if (isNaN(idx)) return undefined;\n current = current[idx];\n } else {\n current = (current as Record<string, unknown>)[seg];\n }\n }\n return current;\n}\n\n/**\n * Resolve a value that might be a literal, $ref, or $expr.\n * - Literal: return as-is\n * - $ref: resolve from state\n * - $expr: return undefined (Phase 2)\n */\nexport function resolveValue(\n value: unknown,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): unknown {\n if (value == null) return value;\n if (typeof value === 'object' && value !== null) {\n if ('$ref' in value && typeof (value as Record<string, unknown>).$ref === 'string') {\n return resolveRef((value as Record<string, unknown>).$ref as string, state, locals);\n }\n if ('$expr' in value) {\n // Phase 2: expression evaluation\n return undefined;\n }\n }\n return value;\n}\n","/**\n * @safe-ugc-ui/react — Style Mapper\n *\n * Maps StyleProps objects from the UGC card schema to React CSSProperties.\n * Uses a whitelist approach: only recognized style properties are mapped.\n *\n * Handles special cases:\n * - border / borderTop/Right/Bottom/Left objects -> CSS shorthand strings\n * - transform object -> CSS transform string\n * - boxShadow object(s) -> CSS box-shadow string\n * - backgroundGradient object -> CSS background string\n * - Dynamic values ($ref/$expr) are resolved via state-resolver\n */\n\nimport type { CSSProperties } from 'react';\nimport { resolveValue } from './state-resolver.js';\n\n// ---------------------------------------------------------------------------\n// Whitelist of style properties that map directly to CSS\n// ---------------------------------------------------------------------------\n\nconst DIRECT_MAP_PROPS = [\n 'display', 'flexDirection', 'justifyContent', 'alignItems', 'alignSelf',\n 'flexWrap', 'flex', 'gap', 'width', 'height', 'minWidth', 'maxWidth',\n 'minHeight', 'maxHeight', 'padding', 'paddingTop', 'paddingRight',\n 'paddingBottom', 'paddingLeft', 'margin', 'marginTop', 'marginRight',\n 'marginBottom', 'marginLeft', 'backgroundColor', 'color', 'borderRadius',\n 'borderRadiusTopLeft', 'borderRadiusTopRight',\n 'borderRadiusBottomLeft', 'borderRadiusBottomRight',\n 'fontSize', 'fontWeight', 'fontStyle', 'textAlign', 'textDecoration',\n 'lineHeight', 'letterSpacing', 'opacity', 'overflow', 'position',\n 'top', 'right', 'bottom', 'left', 'zIndex',\n 'gridTemplateColumns', 'gridTemplateRows', 'gridColumn', 'gridRow',\n] as const;\n\n// ---------------------------------------------------------------------------\n// Forbidden CSS functions (defense-in-depth)\n// ---------------------------------------------------------------------------\n\nconst FORBIDDEN_CSS_FUNCTIONS_LOWER = ['url(', 'var(', 'calc(', 'env(', 'expression('];\n\nfunction containsForbiddenCssFunction(value: string): boolean {\n const lower = value.toLowerCase();\n return FORBIDDEN_CSS_FUNCTIONS_LOWER.some(fn => lower.includes(fn));\n}\n\n// ---------------------------------------------------------------------------\n// Helper: resolve a style value (may be literal, $ref, or $expr)\n// ---------------------------------------------------------------------------\n\nfunction resolveStyleValue(\n value: unknown,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): unknown {\n return resolveValue(value, state, locals);\n}\n\n// ---------------------------------------------------------------------------\n// Helper: transform object -> CSS transform string\n// ---------------------------------------------------------------------------\n\nfunction transformToCss(transform: Record<string, unknown>): string {\n const parts: string[] = [];\n\n if (transform.rotate !== undefined) {\n parts.push(`rotate(${String(transform.rotate)})`);\n }\n if (transform.scale !== undefined) {\n parts.push(`scale(${String(transform.scale)})`);\n }\n if (transform.translateX !== undefined) {\n parts.push(`translateX(${String(transform.translateX)}px)`);\n }\n if (transform.translateY !== undefined) {\n parts.push(`translateY(${String(transform.translateY)}px)`);\n }\n\n return parts.join(' ');\n}\n\n// ---------------------------------------------------------------------------\n// Helper: shadow object(s) -> CSS box-shadow string\n// ---------------------------------------------------------------------------\n\nfunction singleShadowToCss(shadow: Record<string, unknown>): string {\n const offsetX = shadow.offsetX ?? 0;\n const offsetY = shadow.offsetY ?? 0;\n const blur = shadow.blur ?? 0;\n const spread = shadow.spread ?? 0;\n const color = shadow.color ?? '#000';\n return `${String(offsetX)}px ${String(offsetY)}px ${String(blur)}px ${String(spread)}px ${String(color)}`;\n}\n\nfunction shadowToCss(shadow: unknown): string {\n if (Array.isArray(shadow)) {\n return shadow\n .map((s) => singleShadowToCss(s as Record<string, unknown>))\n .join(', ');\n }\n if (typeof shadow === 'object' && shadow !== null) {\n return singleShadowToCss(shadow as Record<string, unknown>);\n }\n return '';\n}\n\n// ---------------------------------------------------------------------------\n// Helper: gradient object -> CSS background string\n// ---------------------------------------------------------------------------\n\nfunction gradientToCss(gradient: Record<string, unknown>): string {\n const stops = gradient.stops as Array<{ color: string; position: string }> | undefined;\n if (!stops || !Array.isArray(stops)) return '';\n\n const stopsStr = stops\n .map((s) => `${s.color} ${s.position}`)\n .join(', ');\n\n if (gradient.type === 'radial') {\n return `radial-gradient(circle, ${stopsStr})`;\n }\n\n // Default to linear\n const direction = (gradient.direction as string) ?? '180deg';\n return `linear-gradient(${direction}, ${stopsStr})`;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: border object -> CSS border shorthand string\n// ---------------------------------------------------------------------------\n\nfunction borderToCss(border: Record<string, unknown>): string {\n const width = border.width ?? 0;\n const style = border.style ?? 'solid';\n const color = border.color ?? '#000';\n return `${String(width)}px ${String(style)} ${String(color)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: map spec alignment values to CSS flexbox values\n// ---------------------------------------------------------------------------\n\nconst FLEX_ALIGNMENT_MAP: Record<string, string> = {\n start: 'flex-start',\n end: 'flex-end',\n};\n\nconst FLEX_ALIGNMENT_PROPS = new Set([\n 'justifyContent',\n 'alignItems',\n 'alignSelf',\n]);\n\n// ---------------------------------------------------------------------------\n// Main: mapStyle\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a UGC StyleProps object to React CSSProperties.\n *\n * @param style - The style object from a UGC node (may contain $ref/$expr values)\n * @param state - The card state for resolving $ref values\n * @param locals - Optional local variables for resolving $ref values (checked before state)\n * @returns A React CSSProperties object\n */\nexport function mapStyle(\n style: Record<string, unknown> | undefined,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): CSSProperties {\n if (!style) return {};\n\n const css: CSSProperties = {};\n\n // Direct-mapped properties (resolve dynamic values)\n for (const prop of DIRECT_MAP_PROPS) {\n if (prop in style) {\n let resolved = resolveStyleValue(style[prop], state, locals);\n if (resolved !== undefined) {\n // Map spec alignment values (start/end) to CSS flexbox values\n if (\n FLEX_ALIGNMENT_PROPS.has(prop) &&\n typeof resolved === 'string' &&\n resolved in FLEX_ALIGNMENT_MAP\n ) {\n resolved = FLEX_ALIGNMENT_MAP[resolved];\n }\n // Block forbidden CSS functions (defense-in-depth)\n if (typeof resolved === 'string' && containsForbiddenCssFunction(resolved)) {\n continue;\n }\n (css as Record<string, unknown>)[prop] = resolved;\n }\n }\n }\n\n // Transform object -> CSS transform string\n if (style.transform && typeof style.transform === 'object') {\n css.transform = transformToCss(style.transform as Record<string, unknown>);\n }\n\n // Border shorthand\n if (style.border && typeof style.border === 'object') {\n css.border = borderToCss(style.border as Record<string, unknown>);\n }\n\n // Border sides (borderTop, borderRight, borderBottom, borderLeft)\n for (const side of ['borderTop', 'borderRight', 'borderBottom', 'borderLeft'] as const) {\n if (style[side] && typeof style[side] === 'object') {\n (css as Record<string, unknown>)[side] = borderToCss(\n style[side] as Record<string, unknown>,\n );\n }\n }\n\n // Box shadow\n if (style.boxShadow) {\n css.boxShadow = shadowToCss(style.boxShadow);\n }\n\n // Background gradient -> CSS background\n if (style.backgroundGradient && typeof style.backgroundGradient === 'object') {\n css.background = gradientToCss(\n style.backgroundGradient as Record<string, unknown>,\n );\n }\n\n return css;\n}\n","/**\n * @safe-ugc-ui/react — Asset Resolver\n *\n * Maps @assets/ paths used by card creators to actual platform-provided URLs.\n * Card authors reference assets as \"@assets/name.png\", and the hosting platform\n * provides a mapping to real URLs (blob:, data:, or CDN URLs).\n */\n\nexport type AssetMap = Record<string, string>;\n\n/**\n * Resolve an asset path to its actual URL.\n *\n * Card creators use `@assets/name.png`; the platform provides actual URLs.\n * Looks up by full path first, then by the key after `@assets/`.\n *\n * @param path - The asset path (e.g. \"@assets/avatar.png\")\n * @param assets - The asset map from the platform\n * @returns The resolved URL, or undefined if not found\n */\nexport function resolveAsset(\n path: string,\n assets: AssetMap,\n): string | undefined {\n if (!path.startsWith('@assets/')) return undefined;\n\n // Try full path match first\n if (path in assets) return assets[path];\n\n // Try key-only match (strip @assets/ prefix)\n const key = path.slice('@assets/'.length);\n if (key in assets) return assets[key];\n\n return undefined;\n}\n","import type { CSSProperties, ReactNode } from 'react';\n\ninterface BoxProps {\n style?: CSSProperties;\n children?: ReactNode;\n}\n\nexport function Box({ style, children }: BoxProps) {\n return <div style={style}>{children}</div>;\n}\n","import type { CSSProperties, ReactNode } from 'react';\n\ninterface RowProps {\n style?: CSSProperties;\n children?: ReactNode;\n}\n\nconst rowBase: CSSProperties = {\n display: 'flex',\n flexDirection: 'row',\n};\n\nexport function Row({ style, children }: RowProps) {\n return <div style={{ ...rowBase, ...style }}>{children}</div>;\n}\n","import type { CSSProperties, ReactNode } from 'react';\n\ninterface ColumnProps {\n style?: CSSProperties;\n children?: ReactNode;\n}\n\nconst columnBase: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n};\n\nexport function Column({ style, children }: ColumnProps) {\n return <div style={{ ...columnBase, ...style }}>{children}</div>;\n}\n","import type { CSSProperties } from 'react';\n\ninterface TextComponentProps {\n content: string;\n style?: CSSProperties;\n}\n\n/**\n * Renders text content safely. NEVER uses dangerouslySetInnerHTML.\n * All content is rendered as text nodes via React's built-in escaping.\n */\nexport function Text({ content, style }: TextComponentProps) {\n return <span style={style}>{content}</span>;\n}\n","import type { CSSProperties } from 'react';\n\ninterface ImageComponentProps {\n src: string;\n alt?: string;\n style?: CSSProperties;\n}\n\n/**\n * Renders an image. Defense-in-depth: at the render level, we still\n * verify the src is a safe URL scheme. In practice, src will already\n * be resolved by asset-resolver before reaching this component.\n *\n * Allowed schemes:\n * - blob: (platform-provided blob URLs)\n * - data: (inline data URIs)\n * - https: (resolved CDN URLs from asset-resolver)\n * - http: (resolved URLs — platform decides)\n *\n * If the src starts with @assets/, it was not resolved and we render nothing.\n */\nexport function Image({ src, alt, style }: ImageComponentProps) {\n // SECURITY: reject unresolved @assets/ paths (should already be resolved)\n if (src.startsWith('@assets/')) {\n return null;\n }\n\n return <img src={src} alt={alt ?? ''} style={style} />;\n}\n","import type { CSSProperties, ReactNode } from 'react';\n\ninterface StackProps {\n style?: CSSProperties;\n children?: ReactNode;\n}\n\nconst stackBase: CSSProperties = {\n position: 'relative',\n};\n\nexport function Stack({ style, children }: StackProps) {\n return <div style={{ ...stackBase, ...style }}>{children}</div>;\n}\n","import type { CSSProperties, ReactNode } from 'react';\n\ninterface GridProps {\n style?: CSSProperties;\n children?: ReactNode;\n}\n\nconst gridBase: CSSProperties = {\n display: 'grid',\n};\n\nexport function Grid({ style, children }: GridProps) {\n return <div style={{ ...gridBase, ...style }}>{children}</div>;\n}\n","import type { CSSProperties } from 'react';\n\ninterface SpacerProps {\n size?: number | string;\n style?: CSSProperties;\n}\n\nexport function Spacer({ size, style }: SpacerProps) {\n return <div style={{ width: size, height: size, flexShrink: 0, ...style }} />;\n}\n","import type { CSSProperties } from 'react';\n\ninterface DividerProps {\n color?: string;\n thickness?: number | string;\n style?: CSSProperties;\n}\n\nfunction formatThickness(thickness: number | string | undefined): string {\n if (thickness == null) return '1px';\n if (typeof thickness === 'number') return `${thickness}px`;\n // Numeric string (e.g. \"2\") → append px\n if (/^-?\\d+(\\.\\d+)?$/.test(thickness)) return `${thickness}px`;\n // Already has unit (e.g. \"2px\", \"1rem\") → use as-is\n return thickness;\n}\n\nexport function Divider({ color, thickness, style }: DividerProps) {\n return (\n <div\n style={{\n borderTop: `${formatThickness(thickness)} solid ${color ?? '#e0e0e0'}`,\n width: '100%',\n ...style,\n }}\n />\n );\n}\n","import type { CSSProperties, ReactNode } from 'react';\n\ninterface IconProps {\n name: string;\n size?: number | string;\n color?: string;\n iconResolver?: (name: string) => ReactNode;\n style?: CSSProperties;\n}\n\nexport function Icon({ name, size, color, iconResolver, style }: IconProps) {\n if (!iconResolver) {\n return null;\n }\n\n return (\n <span\n style={{\n fontSize: size,\n color,\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n ...style,\n }}\n >\n {iconResolver(name)}\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\ninterface ProgressBarProps {\n value: number;\n max: number;\n color?: string;\n style?: CSSProperties;\n}\n\nexport function ProgressBar({ value, max, color, style }: ProgressBarProps) {\n const percentage = max <= 0 ? 0 : Math.min(100, Math.max(0, (value / max) * 100));\n\n return (\n <div\n style={{\n backgroundColor: '#e0e0e0',\n borderRadius: 4,\n overflow: 'hidden',\n ...style,\n }}\n >\n <div\n style={{\n width: `${percentage}%`,\n backgroundColor: color ?? '#4caf50',\n height: 8,\n }}\n />\n </div>\n );\n}\n","import type { CSSProperties } from 'react';\n\ninterface AvatarProps {\n src: string;\n alt?: string;\n size?: number | string;\n style?: CSSProperties;\n}\n\nexport function Avatar({ src, alt, size, style }: AvatarProps) {\n return (\n <img\n src={src}\n alt={alt ?? ''}\n style={{\n width: size ?? 40,\n height: size ?? 40,\n borderRadius: '50%',\n objectFit: 'cover',\n ...style,\n }}\n />\n );\n}\n","import type { CSSProperties } from 'react';\n\ninterface BadgeProps {\n label: string;\n color?: string;\n style?: CSSProperties;\n}\n\nexport function Badge({ label, color, style }: BadgeProps) {\n return (\n <span\n style={{\n display: 'inline-block',\n padding: '2px 8px',\n borderRadius: 12,\n backgroundColor: color ?? '#e0e0e0',\n fontSize: 12,\n ...style,\n }}\n >\n {label}\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\ninterface ChipProps {\n label: string;\n color?: string;\n style?: CSSProperties;\n}\n\nexport function Chip({ label, color, style }: ChipProps) {\n return (\n <span\n style={{\n display: 'inline-block',\n padding: '4px 12px',\n borderRadius: 16,\n border: `1px solid ${color ?? '#e0e0e0'}`,\n fontSize: 14,\n ...style,\n }}\n >\n {label}\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\n\ninterface ButtonProps {\n label: string;\n action: string;\n onAction?: (type: string, actionId: string) => void;\n disabled?: boolean;\n style?: CSSProperties;\n}\n\nexport function Button({ label, action, onAction, disabled, style }: ButtonProps) {\n return (\n <button\n disabled={disabled}\n onClick={() => onAction?.('button', action)}\n style={{\n padding: '8px 16px',\n borderRadius: 6,\n border: '1px solid #ccc',\n cursor: disabled ? 'default' : 'pointer',\n ...style,\n }}\n >\n {label}\n </button>\n );\n}\n","import type { CSSProperties } from 'react';\n\ninterface ToggleProps {\n value: boolean;\n onToggle: string;\n onAction?: (type: string, actionId: string, payload?: unknown) => void;\n disabled?: boolean;\n style?: CSSProperties;\n}\n\nexport function Toggle({ value, onToggle, onAction, disabled, style }: ToggleProps) {\n return (\n <button\n disabled={disabled}\n onClick={() => onAction?.('toggle', onToggle, { value: !value })}\n style={{\n padding: '6px 16px',\n borderRadius: 12,\n border: '1px solid #ccc',\n backgroundColor: value ? '#4caf50' : '#e0e0e0',\n color: value ? '#fff' : '#333',\n cursor: disabled ? 'default' : 'pointer',\n ...style,\n }}\n >\n {value ? 'ON' : 'OFF'}\n </button>\n );\n}\n"],"mappings":"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","names":["jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","_","jsx","containerStyle"]}
1
+ {"version":3,"sources":["../src/UGCRenderer.tsx","../src/UGCContainer.tsx","../src/node-renderer.tsx","../src/state-resolver.ts","../src/style-mapper.ts","../src/asset-resolver.ts","../src/hooks/useHoverStyle.ts","../src/components/Box.tsx","../src/components/Row.tsx","../src/components/Column.tsx","../src/components/Text.tsx","../src/components/Image.tsx","../src/components/Stack.tsx","../src/components/Grid.tsx","../src/components/Spacer.tsx","../src/components/Divider.tsx","../src/components/Icon.tsx","../src/components/ProgressBar.tsx","../src/components/Avatar.tsx","../src/components/Badge.tsx","../src/components/Chip.tsx","../src/components/Button.tsx","../src/components/Toggle.tsx"],"sourcesContent":["/**\n * @safe-ugc-ui/react --- UGC Renderer\n *\n * Top-level component that validates and renders a UGC card.\n *\n * Pipeline:\n * 1. Accept a card (UGCCard object or raw JSON string)\n * 2. Validate via @safe-ugc-ui/validator\n * 3. If invalid, render nothing (or optional error fallback)\n * 4. If valid, wrap in UGCContainer and render the view tree\n *\n * Props:\n * - card: UGCCard object or raw JSON string\n * - viewName: Name of the view to render (defaults to first view)\n * - assets: Mapping of asset keys to actual URLs\n * - state: Optional state override (merged with card.state)\n * - onError: Optional error callback\n * - iconResolver: Optional callback to resolve icon names to ReactNode\n * - onAction: Optional callback for Button/Toggle actions\n */\n\nimport { useMemo } from 'react';\nimport type { CSSProperties, ReactNode } from 'react';\nimport { validate, validateRaw } from '@safe-ugc-ui/validator';\nimport type { UGCCard } from '@safe-ugc-ui/types';\n\nimport { UGCContainer } from './UGCContainer.js';\nimport { renderTree } from './node-renderer.js';\nimport type { AssetMap } from './asset-resolver.js';\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface UGCRendererProps {\n /** The UGC card to render --- either a parsed object or a raw JSON string. */\n card: UGCCard | string;\n\n /** Name of the view to render. Defaults to the first view in the card. */\n viewName?: string;\n\n /** Asset map: keys are asset identifiers, values are resolved URLs. */\n assets?: AssetMap;\n\n /** Optional state override. Merged on top of the card's own state. */\n state?: Record<string, unknown>;\n\n /** Optional CSS style for the outer container. */\n containerStyle?: CSSProperties;\n\n /** Optional callback invoked when validation fails or runtime limits are hit. */\n onError?: (errors: Array<{ code: string; message: string; path: string }>) => void;\n\n /** Optional callback to resolve icon names to React elements. */\n iconResolver?: (name: string) => ReactNode;\n\n /** Optional callback for Button/Toggle interaction actions. */\n onAction?: (type: string, actionId: string, payload?: unknown) => void;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n/**\n * Top-level UGC card renderer.\n *\n * Validates the card, then renders the specified view inside a secure\n * UGCContainer. Returns null if validation fails.\n */\nexport function UGCRenderer({\n card,\n viewName,\n assets = {},\n state: stateOverride,\n containerStyle,\n onError,\n iconResolver,\n onAction,\n}: UGCRendererProps) {\n const result = useMemo(() => {\n // 1. Validate\n const validationResult =\n typeof card === 'string' ? validateRaw(card) : validate(card);\n\n if (!validationResult.valid) {\n return { valid: false as const, errors: validationResult.errors };\n }\n\n // 2. Parse card object\n const cardObj: UGCCard =\n typeof card === 'string'\n ? (JSON.parse(card) as UGCCard)\n : card;\n\n // 3. Determine which view to render\n const views = cardObj.views;\n const viewKeys = Object.keys(views);\n const selectedView = viewName && viewName in views\n ? viewName\n : viewKeys[0];\n\n if (!selectedView || !(selectedView in views)) {\n return { valid: false as const, errors: [] };\n }\n\n // 4. Merge state\n const mergedState: Record<string, unknown> = {\n ...(cardObj.state ?? {}),\n ...(stateOverride ?? {}),\n };\n\n // 5. Extract card-level styles\n const cardStyles = cardObj.styles as Record<string, Record<string, unknown>> | undefined;\n\n return {\n valid: true as const,\n rootNode: views[selectedView],\n state: mergedState,\n cardStyles,\n };\n }, [card, viewName, stateOverride]);\n\n // Handle invalid cards\n if (!result.valid) {\n if (onError && result.errors.length > 0) {\n onError(result.errors);\n }\n return null;\n }\n\n // Render the view tree inside the secure container\n return (\n <UGCContainer style={containerStyle}>\n {renderTree(\n result.rootNode,\n result.state,\n assets,\n result.cardStyles,\n iconResolver,\n onAction,\n onError,\n )}\n </UGCContainer>\n );\n}\n","/**\n * @safe-ugc-ui/react — UGC Container\n *\n * Wrapper component that provides security isolation for UGC content.\n *\n * CSS isolation features:\n * - overflow: hidden — prevents content from escaping bounds\n * - isolation: isolate — creates a new stacking context\n * - contain: content — limits layout/paint/style to this subtree\n * - position: relative — establishes positioning context for children\n */\n\nimport type { CSSProperties, ReactNode } from 'react';\n\ninterface UGCContainerProps {\n children?: ReactNode;\n style?: CSSProperties;\n}\n\nconst containerStyle: CSSProperties = {\n overflow: 'hidden',\n isolation: 'isolate',\n contain: 'content',\n position: 'relative',\n};\n\n/**\n * Security isolation wrapper for UGC content.\n * All UGC card renderings should be wrapped in this container.\n */\nexport function UGCContainer({ children, style }: UGCContainerProps) {\n const mergedStyle: CSSProperties = style\n ? { ...containerStyle, ...style }\n : containerStyle;\n\n return <div style={mergedStyle}>{children}</div>;\n}\n","/**\n * @safe-ugc-ui/react --- Node Renderer\n *\n * Recursive renderer that maps UGC node types to React components.\n * Supports all 16 component types, for-loop rendering, $style merge,\n * and runtime limits pre-check.\n *\n * For each node:\n * 1. Pre-check runtime limits (node count, style bytes, overflow, text bytes)\n * 2. Merge $style with inline styles\n * 3. Resolve $ref values in node fields using state-resolver (with locals)\n * 4. Map style fields to React CSSProperties using style-mapper\n * 5. Resolve asset paths for Image/Avatar src using asset-resolver\n * 6. Render the appropriate component\n * 7. Recursively render children (arrays and for-loops)\n */\n\nimport type { ReactNode } from 'react';\nimport {\n MAX_NODE_COUNT,\n MAX_LOOP_ITERATIONS,\n TEXT_CONTENT_TOTAL_MAX_BYTES,\n STYLE_OBJECTS_TOTAL_MAX_BYTES,\n MAX_OVERFLOW_AUTO_COUNT,\n} from '@safe-ugc-ui/types';\n\nimport { resolveRef, resolveValue } from './state-resolver.js';\nimport { mapStyle } from './style-mapper.js';\nimport { resolveAsset } from './asset-resolver.js';\nimport type { AssetMap } from './asset-resolver.js';\nimport { Box } from './components/Box.js';\nimport { Row } from './components/Row.js';\nimport { Column } from './components/Column.js';\nimport { Text } from './components/Text.js';\nimport { Image } from './components/Image.js';\nimport { Stack } from './components/Stack.js';\nimport { Grid } from './components/Grid.js';\nimport { Spacer } from './components/Spacer.js';\nimport { Divider } from './components/Divider.js';\nimport { Icon } from './components/Icon.js';\nimport { ProgressBar } from './components/ProgressBar.js';\nimport { Avatar } from './components/Avatar.js';\nimport { Badge } from './components/Badge.js';\nimport { Chip } from './components/Chip.js';\nimport { Button } from './components/Button.js';\nimport { Toggle } from './components/Toggle.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A generic UGC node shape. We use Record<string, unknown> for flexibility\n * since the validator has already verified the structure before rendering.\n */\ninterface UGCNodeLike {\n type: string;\n style?: Record<string, unknown>;\n children?: unknown;\n condition?: unknown;\n [key: string]: unknown;\n}\n\ninterface ForLoopLike {\n for: string;\n in: string;\n template: unknown;\n}\n\n/**\n * Runtime limits tracking object. Mutated during rendering to\n * track cumulative resource usage across the entire card.\n */\nexport interface RuntimeLimits {\n nodeCount: number;\n textBytes: number;\n styleBytes: number;\n overflowAutoCount: number;\n}\n\nexport interface RenderContext {\n state: Record<string, unknown>;\n assets: AssetMap;\n locals?: Record<string, unknown>;\n cardStyles?: Record<string, Record<string, unknown>>;\n iconResolver?: (name: string) => ReactNode;\n onAction?: (type: string, actionId: string, payload?: unknown) => void;\n onError?: (errors: Array<{ code: string; message: string; path: string }>) => void;\n limits: RuntimeLimits;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isForLoop(obj: unknown): obj is ForLoopLike {\n if (obj == null || typeof obj !== 'object' || Array.isArray(obj)) return false;\n const o = obj as Record<string, unknown>;\n return typeof o.for === 'string' && typeof o.in === 'string' && o.template != null;\n}\n\nfunction utf8ByteLength(str: string): number {\n let bytes = 0;\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n if (code <= 0x7f) {\n bytes += 1;\n } else if (code <= 0x7ff) {\n bytes += 2;\n } else if (code >= 0xd800 && code <= 0xdbff) {\n // Surrogate pair → 4 UTF-8 bytes\n bytes += 4;\n i++; // skip low surrogate\n } else {\n bytes += 3;\n }\n }\n return bytes;\n}\n\n/**\n * Merge $style from cardStyles with inline style.\n * Returns the merged raw style (unresolved $ref values) and the style\n * without $style key.\n */\nfunction mergeStyleWithCardStyles(\n nodeStyle: Record<string, unknown> | undefined,\n cardStyles: Record<string, Record<string, unknown>> | undefined,\n): Record<string, unknown> | undefined {\n if (!nodeStyle) return undefined;\n\n const rawStyleName = nodeStyle.$style;\n const styleName = typeof rawStyleName === 'string' ? rawStyleName.trim() : rawStyleName;\n if (!styleName || typeof styleName !== 'string' || !cardStyles) {\n // Return style without $style key if present\n if (nodeStyle.$style !== undefined) {\n const { $style: _, ...rest } = nodeStyle;\n return rest;\n }\n return nodeStyle;\n }\n\n const baseStyle = cardStyles[styleName];\n if (!baseStyle) return nodeStyle;\n\n // Merge: base from cardStyles, overridden by inline (excluding $style key)\n const { $style: _, ...inlineWithout$style } = nodeStyle;\n return { ...baseStyle, ...inlineWithout$style };\n}\n\n// ---------------------------------------------------------------------------\n// renderNode --- recursive node renderer\n// ---------------------------------------------------------------------------\n\n/**\n * Render a single UGC node to a React element.\n *\n * Performs runtime limits pre-check BEFORE rendering to prevent\n * DOM pollution when limits are exceeded.\n */\nexport function renderNode(\n node: unknown,\n ctx: RenderContext,\n key: string | number,\n): ReactNode {\n if (node == null || typeof node !== 'object') return null;\n\n const n = node as UGCNodeLike;\n if (!n.type) return null;\n\n // --- Compute all deltas before committing any ---\n const mergedRawStyle = mergeStyleWithCardStyles(n.style, ctx.cardStyles);\n const rv = (val: unknown) => resolveValue(val, ctx.state, ctx.locals);\n\n const styleDelta = mergedRawStyle ? utf8ByteLength(JSON.stringify(mergedRawStyle)) : 0;\n const overflowDelta = mergedRawStyle?.overflow === 'auto' ? 1 : 0;\n\n // Text content: resolve once, reuse in render\n let resolvedTextContent: string | undefined;\n let textDelta = 0;\n if (n.type === 'Text') {\n const raw = rv((n as Record<string, unknown>).content);\n resolvedTextContent = typeof raw === 'string' ? raw : '';\n textDelta = utf8ByteLength(resolvedTextContent);\n }\n\n // --- Batch limit checks (all-or-nothing) ---\n if (ctx.limits.nodeCount + 1 > MAX_NODE_COUNT) {\n ctx.onError?.([{ code: 'RUNTIME_NODE_LIMIT', message: `Node count exceeds maximum of ${MAX_NODE_COUNT}`, path: String(key) }]);\n return null;\n }\n if (ctx.limits.styleBytes + styleDelta > STYLE_OBJECTS_TOTAL_MAX_BYTES) {\n ctx.onError?.([{ code: 'RUNTIME_STYLE_LIMIT', message: `Style bytes exceed maximum of ${STYLE_OBJECTS_TOTAL_MAX_BYTES}`, path: String(key) }]);\n return null;\n }\n if (ctx.limits.overflowAutoCount + overflowDelta > MAX_OVERFLOW_AUTO_COUNT) {\n ctx.onError?.([{ code: 'RUNTIME_OVERFLOW_LIMIT', message: `Overflow auto count exceeds maximum of ${MAX_OVERFLOW_AUTO_COUNT}`, path: String(key) }]);\n return null;\n }\n if (ctx.limits.textBytes + textDelta > TEXT_CONTENT_TOTAL_MAX_BYTES) {\n ctx.onError?.([{ code: 'RUNTIME_TEXT_LIMIT', message: `Text bytes exceed maximum of ${TEXT_CONTENT_TOTAL_MAX_BYTES}`, path: String(key) }]);\n return null;\n }\n\n // --- All checks passed: commit all deltas at once ---\n ctx.limits.nodeCount += 1;\n ctx.limits.styleBytes += styleDelta;\n ctx.limits.overflowAutoCount += overflowDelta;\n ctx.limits.textBytes += textDelta;\n\n // Resolve style to CSS\n const cssStyle = mapStyle(mergedRawStyle, ctx.state, ctx.locals);\n\n // Resolve hoverStyle to CSS (if present)\n const rawHoverStyle = mergedRawStyle?.hoverStyle as Record<string, unknown> | undefined;\n const cssHoverStyle = rawHoverStyle ? mapStyle(rawHoverStyle, ctx.state, ctx.locals) : undefined;\n\n // Render children recursively\n const childElements = renderChildren(n.children, ctx);\n\n switch (n.type) {\n case 'Box':\n return <Box key={key} style={cssStyle} hoverStyle={cssHoverStyle}>{childElements}</Box>;\n\n case 'Row':\n return <Row key={key} style={cssStyle} hoverStyle={cssHoverStyle}>{childElements}</Row>;\n\n case 'Column':\n return <Column key={key} style={cssStyle} hoverStyle={cssHoverStyle}>{childElements}</Column>;\n\n case 'Stack':\n return <Stack key={key} style={cssStyle} hoverStyle={cssHoverStyle}>{childElements}</Stack>;\n\n case 'Grid':\n return <Grid key={key} style={cssStyle} hoverStyle={cssHoverStyle}>{childElements}</Grid>;\n\n case 'Text':\n return <Text key={key} content={resolvedTextContent!} style={cssStyle} hoverStyle={cssHoverStyle} />;\n\n case 'Image': {\n let src = rv((n as Record<string, unknown>).src);\n if (typeof src !== 'string' || !src) return null;\n if (!src.startsWith('@assets/')) return null;\n if (src.includes('../')) return null;\n const resolved = resolveAsset(src, ctx.assets);\n if (!resolved) return null;\n if (typeof resolved === 'string' && resolved.trim().toLowerCase().startsWith('javascript:')) return null;\n const resolvedAlt = rv((n as Record<string, unknown>).alt);\n const alt = typeof resolvedAlt === 'string' ? resolvedAlt : undefined;\n return <Image key={key} src={resolved} alt={alt} style={cssStyle} hoverStyle={cssHoverStyle} />;\n }\n\n case 'Avatar': {\n let src = rv((n as Record<string, unknown>).src);\n if (typeof src !== 'string' || !src) return null;\n if (!src.startsWith('@assets/')) return null;\n if (src.includes('../')) return null;\n const resolved = resolveAsset(src, ctx.assets);\n if (!resolved) return null;\n if (typeof resolved === 'string' && resolved.trim().toLowerCase().startsWith('javascript:')) return null;\n const resolvedSize = rv((n as Record<string, unknown>).size);\n const size = typeof resolvedSize === 'number' || typeof resolvedSize === 'string'\n ? resolvedSize : undefined;\n return <Avatar key={key} src={resolved} size={size} style={cssStyle} hoverStyle={cssHoverStyle} />;\n }\n\n case 'Icon': {\n const nameVal = (n as Record<string, unknown>).name;\n const name = typeof nameVal === 'string' ? nameVal : '';\n const resolvedSize = rv((n as Record<string, unknown>).size);\n const size = typeof resolvedSize === 'number' || typeof resolvedSize === 'string'\n ? resolvedSize : undefined;\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return (\n <Icon\n key={key}\n name={name}\n size={size}\n color={color}\n iconResolver={ctx.iconResolver}\n style={cssStyle}\n hoverStyle={cssHoverStyle}\n />\n );\n }\n\n case 'Spacer': {\n const resolvedSize = rv((n as Record<string, unknown>).size);\n const size = typeof resolvedSize === 'number' || typeof resolvedSize === 'string'\n ? resolvedSize : undefined;\n return <Spacer key={key} size={size} style={cssStyle} hoverStyle={cssHoverStyle} />;\n }\n\n case 'Divider': {\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n const resolvedThickness = rv((n as Record<string, unknown>).thickness);\n const thickness = typeof resolvedThickness === 'number' || typeof resolvedThickness === 'string'\n ? resolvedThickness : undefined;\n return <Divider key={key} color={color} thickness={thickness} style={cssStyle} hoverStyle={cssHoverStyle} />;\n }\n\n case 'ProgressBar': {\n const resolvedValue = rv((n as Record<string, unknown>).value);\n const value = typeof resolvedValue === 'number' ? resolvedValue : 0;\n const resolvedMax = rv((n as Record<string, unknown>).max);\n const max = typeof resolvedMax === 'number' ? resolvedMax : 100;\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return <ProgressBar key={key} value={value} max={max} color={color} style={cssStyle} hoverStyle={cssHoverStyle} />;\n }\n\n case 'Badge': {\n const resolvedLabel = rv((n as Record<string, unknown>).label);\n const label = typeof resolvedLabel === 'string' ? resolvedLabel : '';\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return <Badge key={key} label={label} color={color} style={cssStyle} hoverStyle={cssHoverStyle} />;\n }\n\n case 'Chip': {\n const resolvedLabel = rv((n as Record<string, unknown>).label);\n const label = typeof resolvedLabel === 'string' ? resolvedLabel : '';\n const resolvedColor = rv((n as Record<string, unknown>).color);\n const color = typeof resolvedColor === 'string' ? resolvedColor : undefined;\n return <Chip key={key} label={label} color={color} style={cssStyle} hoverStyle={cssHoverStyle} />;\n }\n\n case 'Button': {\n const resolvedLabel = rv((n as Record<string, unknown>).label);\n const label = typeof resolvedLabel === 'string' ? resolvedLabel : '';\n const actionVal = (n as Record<string, unknown>).action;\n const action = typeof actionVal === 'string' ? actionVal : '';\n return (\n <Button\n key={key}\n label={label}\n action={action}\n onAction={ctx.onAction}\n style={cssStyle}\n hoverStyle={cssHoverStyle}\n />\n );\n }\n\n case 'Toggle': {\n const resolvedValue = rv((n as Record<string, unknown>).value);\n const value = typeof resolvedValue === 'boolean' ? resolvedValue : false;\n const onToggleVal = (n as Record<string, unknown>).onToggle;\n const onToggle = typeof onToggleVal === 'string' ? onToggleVal : '';\n return (\n <Toggle\n key={key}\n value={value}\n onToggle={onToggle}\n onAction={ctx.onAction}\n style={cssStyle}\n hoverStyle={cssHoverStyle}\n />\n );\n }\n\n default:\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// renderChildren --- handle children arrays and for-loops\n// ---------------------------------------------------------------------------\n\nfunction renderChildren(\n children: unknown,\n ctx: RenderContext,\n): ReactNode[] | null {\n if (!children) return null;\n\n // Array children\n if (Array.isArray(children)) {\n return children.map((child, index) => renderNode(child, ctx, index));\n }\n\n // For-loop children\n if (isForLoop(children)) {\n return renderForLoop(children, ctx);\n }\n\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// renderForLoop --- iterate over state array, render template per item\n// ---------------------------------------------------------------------------\n\nfunction renderForLoop(\n loop: ForLoopLike,\n ctx: RenderContext,\n): ReactNode[] {\n // Resolve the source array from state (or locals)\n const source = resolveRef(loop.in, ctx.state, ctx.locals);\n if (source === undefined) {\n // Soft skip: source not found (may be provided at runtime or optional)\n return [];\n }\n if (!Array.isArray(source)) {\n // Hard error: source exists but is not an array (type mismatch)\n ctx.onError?.([{\n code: 'RUNTIME_LOOP_SOURCE_INVALID',\n message: `Loop source \"${loop.in}\" is not an array (got ${typeof source})`,\n path: `for(${loop.for} in ${loop.in})`,\n }]);\n return [];\n }\n\n // Cap iterations\n const maxIter = Math.min(source.length, MAX_LOOP_ITERATIONS);\n const elements: ReactNode[] = [];\n\n for (let i = 0; i < maxIter; i++) {\n const item = source[i];\n const newLocals: Record<string, unknown> = {\n ...ctx.locals,\n [loop.for]: item,\n index: i,\n };\n const childCtx: RenderContext = { ...ctx, locals: newLocals };\n elements.push(renderNode(loop.template, childCtx, i));\n }\n\n return elements;\n}\n\n// ---------------------------------------------------------------------------\n// RenderTree --- render an entire view tree\n// ---------------------------------------------------------------------------\n\n/**\n * Render a full view tree starting from the root node.\n *\n * @param rootNode - The root node of the view\n * @param state - Card state for $ref resolution\n * @param assets - Asset map for @assets/ resolution\n * @param cardStyles - Optional card-level named styles\n * @param iconResolver - Optional icon resolver callback\n * @param onAction - Optional action callback for Button/Toggle\n * @param onError - Optional error callback\n * @returns A React element tree\n */\nexport function renderTree(\n rootNode: unknown,\n state: Record<string, unknown>,\n assets: AssetMap,\n cardStyles?: Record<string, Record<string, unknown>>,\n iconResolver?: (name: string) => ReactNode,\n onAction?: (type: string, actionId: string, payload?: unknown) => void,\n onError?: (errors: Array<{ code: string; message: string; path: string }>) => void,\n): ReactNode {\n const limits: RuntimeLimits = {\n nodeCount: 0,\n textBytes: 0,\n styleBytes: 0,\n overflowAutoCount: 0,\n };\n const ctx: RenderContext = {\n state,\n assets,\n cardStyles,\n iconResolver,\n onAction,\n onError,\n limits,\n };\n return renderNode(rootNode, ctx, 'root');\n}\n","/**\n * @safe-ugc-ui/react — State Resolver\n *\n * Resolves $ref and $expr values from card state.\n *\n * - $ref: looks up a dotted path in the state object\n * - $expr: returns undefined (Phase 2 — expression evaluation not yet implemented)\n * - Literal values pass through unchanged\n */\n\nimport { PROTOTYPE_POLLUTION_SEGMENTS } from '@safe-ugc-ui/types';\n\n/**\n * Parse a $ref path into individual segments.\n * Handles both dot notation and bracket notation:\n * \"items[0].name\" → [\"items\", \"0\", \"name\"]\n * \"data[2][3]\" → [\"data\", \"2\", \"3\"]\n * \"simple.path\" → [\"simple\", \"path\"]\n */\nfunction parseRefSegments(path: string): string[] {\n const segments: string[] = [];\n const dotParts = path.split('.');\n for (const part of dotParts) {\n if (!part) continue;\n // Check for bracket notation: extract base and indices\n const bracketPattern = /\\[(\\d+)\\]/g;\n let match: RegExpExecArray | null;\n\n // Find the base name (before first bracket)\n const firstBracket = part.indexOf('[');\n if (firstBracket > 0) {\n segments.push(part.slice(0, firstBracket));\n } else if (firstBracket === -1) {\n // No brackets at all\n segments.push(part);\n continue;\n }\n\n // Extract all bracket indices\n while ((match = bracketPattern.exec(part)) !== null) {\n segments.push(match[1]);\n }\n\n // If part starts with [ and no base was added\n if (firstBracket === 0) {\n // Edge case: segment is just [0] with no base name\n // This shouldn't normally happen but handle gracefully\n }\n }\n return segments;\n}\n\n/**\n * Resolves a $ref path (e.g. \"$hp\", \"$items[0].name\") from card state.\n *\n * - Strips leading '$' from the path\n * - Parses dot notation and bracket notation into segments\n * - Blocks __proto__, constructor, prototype segments\n * - Max depth of 5 segments per spec\n * - Returns undefined if path doesn't exist\n */\nexport function resolveRef(\n refPath: string,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): unknown {\n const path = refPath.startsWith('$') ? refPath.slice(1) : refPath;\n const segments = parseRefSegments(path);\n\n // Block prototype pollution\n for (const seg of segments) {\n if ((PROTOTYPE_POLLUTION_SEGMENTS as readonly string[]).includes(seg)) {\n return undefined;\n }\n }\n\n // Max depth check\n if (segments.length > 5) return undefined;\n\n // Choose starting object: locals first, then state\n const firstSeg = segments[0];\n let current: unknown;\n if (locals && firstSeg && firstSeg in locals) {\n current = locals;\n } else {\n current = state;\n }\n\n // Traverse\n for (const seg of segments) {\n if (current == null || typeof current !== 'object') return undefined;\n if (Array.isArray(current)) {\n const idx = parseInt(seg, 10);\n if (isNaN(idx)) return undefined;\n current = current[idx];\n } else {\n current = (current as Record<string, unknown>)[seg];\n }\n }\n return current;\n}\n\n/**\n * Resolve a value that might be a literal, $ref, or $expr.\n * - Literal: return as-is\n * - $ref: resolve from state\n * - $expr: return undefined (Phase 2)\n */\nexport function resolveValue(\n value: unknown,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): unknown {\n if (value == null) return value;\n if (typeof value === 'object' && value !== null) {\n if ('$ref' in value && typeof (value as Record<string, unknown>).$ref === 'string') {\n return resolveRef((value as Record<string, unknown>).$ref as string, state, locals);\n }\n if ('$expr' in value) {\n // Phase 2: expression evaluation\n return undefined;\n }\n }\n return value;\n}\n","/**\n * @safe-ugc-ui/react — Style Mapper\n *\n * Maps StyleProps objects from the UGC card schema to React CSSProperties.\n * Uses a whitelist approach: only recognized style properties are mapped.\n *\n * Handles special cases:\n * - border / borderTop/Right/Bottom/Left objects -> CSS shorthand strings\n * - transform object -> CSS transform string\n * - boxShadow object(s) -> CSS box-shadow string\n * - backgroundGradient object -> CSS background string\n * - Dynamic values ($ref/$expr) are resolved via state-resolver\n */\n\nimport type { CSSProperties } from 'react';\nimport { ALLOWED_TRANSITION_PROPERTIES } from '@safe-ugc-ui/types';\nimport { resolveValue } from './state-resolver.js';\n\n// ---------------------------------------------------------------------------\n// Whitelist of style properties that map directly to CSS\n// ---------------------------------------------------------------------------\n\nconst DIRECT_MAP_PROPS = [\n 'display', 'flexDirection', 'justifyContent', 'alignItems', 'alignSelf',\n 'flexWrap', 'flex', 'gap', 'width', 'height', 'minWidth', 'maxWidth',\n 'minHeight', 'maxHeight', 'padding', 'paddingTop', 'paddingRight',\n 'paddingBottom', 'paddingLeft', 'margin', 'marginTop', 'marginRight',\n 'marginBottom', 'marginLeft', 'backgroundColor', 'color', 'borderRadius',\n 'borderRadiusTopLeft', 'borderRadiusTopRight',\n 'borderRadiusBottomLeft', 'borderRadiusBottomRight',\n 'fontSize', 'fontWeight', 'fontStyle', 'textAlign', 'textDecoration',\n 'lineHeight', 'letterSpacing', 'opacity', 'overflow', 'position',\n 'top', 'right', 'bottom', 'left', 'zIndex',\n 'gridTemplateColumns', 'gridTemplateRows', 'gridColumn', 'gridRow',\n] as const;\n\n// ---------------------------------------------------------------------------\n// Forbidden CSS functions (defense-in-depth)\n// ---------------------------------------------------------------------------\n\nconst FORBIDDEN_CSS_FUNCTIONS_LOWER = ['url(', 'var(', 'calc(', 'env(', 'expression('];\n\nfunction containsForbiddenCssFunction(value: string): boolean {\n const lower = value.toLowerCase();\n return FORBIDDEN_CSS_FUNCTIONS_LOWER.some(fn => lower.includes(fn));\n}\n\n// ---------------------------------------------------------------------------\n// Helper: resolve a style value (may be literal, $ref, or $expr)\n// ---------------------------------------------------------------------------\n\nfunction resolveStyleValue(\n value: unknown,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): unknown {\n return resolveValue(value, state, locals);\n}\n\n// ---------------------------------------------------------------------------\n// Helper: transform object -> CSS transform string\n// ---------------------------------------------------------------------------\n\nfunction transformToCss(transform: Record<string, unknown>): string {\n const parts: string[] = [];\n\n if (transform.rotate !== undefined) {\n parts.push(`rotate(${String(transform.rotate)})`);\n }\n if (transform.scale !== undefined) {\n parts.push(`scale(${String(transform.scale)})`);\n }\n if (transform.translateX !== undefined) {\n parts.push(`translateX(${String(transform.translateX)}px)`);\n }\n if (transform.translateY !== undefined) {\n parts.push(`translateY(${String(transform.translateY)}px)`);\n }\n\n return parts.join(' ');\n}\n\n// ---------------------------------------------------------------------------\n// Helper: shadow object(s) -> CSS box-shadow string\n// ---------------------------------------------------------------------------\n\nfunction singleShadowToCss(shadow: Record<string, unknown>): string {\n const offsetX = shadow.offsetX ?? 0;\n const offsetY = shadow.offsetY ?? 0;\n const blur = shadow.blur ?? 0;\n const spread = shadow.spread ?? 0;\n const color = shadow.color ?? '#000';\n return `${String(offsetX)}px ${String(offsetY)}px ${String(blur)}px ${String(spread)}px ${String(color)}`;\n}\n\nfunction shadowToCss(shadow: unknown): string {\n if (Array.isArray(shadow)) {\n return shadow\n .map((s) => singleShadowToCss(s as Record<string, unknown>))\n .join(', ');\n }\n if (typeof shadow === 'object' && shadow !== null) {\n return singleShadowToCss(shadow as Record<string, unknown>);\n }\n return '';\n}\n\n// ---------------------------------------------------------------------------\n// Helper: gradient object -> CSS background string\n// ---------------------------------------------------------------------------\n\nfunction gradientToCss(gradient: Record<string, unknown>): string {\n const stops = gradient.stops as Array<{ color: string; position: string }> | undefined;\n if (!stops || !Array.isArray(stops)) return '';\n\n const stopsStr = stops\n .map((s) => `${s.color} ${s.position}`)\n .join(', ');\n\n if (gradient.type === 'radial') {\n return `radial-gradient(circle, ${stopsStr})`;\n }\n\n // Default to linear\n const direction = (gradient.direction as string) ?? '180deg';\n return `linear-gradient(${direction}, ${stopsStr})`;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: border object -> CSS border shorthand string\n// ---------------------------------------------------------------------------\n\nfunction borderToCss(border: Record<string, unknown>): string {\n const width = border.width ?? 0;\n const style = border.style ?? 'solid';\n const color = border.color ?? '#000';\n return `${String(width)}px ${String(style)} ${String(color)}`;\n}\n\n// ---------------------------------------------------------------------------\n// Helper: map spec alignment values to CSS flexbox values\n// ---------------------------------------------------------------------------\n\nconst FLEX_ALIGNMENT_MAP: Record<string, string> = {\n start: 'flex-start',\n end: 'flex-end',\n};\n\nconst FLEX_ALIGNMENT_PROPS = new Set([\n 'justifyContent',\n 'alignItems',\n 'alignSelf',\n]);\n\n// ---------------------------------------------------------------------------\n// Main: mapStyle\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a UGC StyleProps object to React CSSProperties.\n *\n * @param style - The style object from a UGC node (may contain $ref/$expr values)\n * @param state - The card state for resolving $ref values\n * @param locals - Optional local variables for resolving $ref values (checked before state)\n * @returns A React CSSProperties object\n */\nexport function mapStyle(\n style: Record<string, unknown> | undefined,\n state: Record<string, unknown>,\n locals?: Record<string, unknown>,\n): CSSProperties {\n if (!style) return {};\n\n const css: CSSProperties = {};\n\n // Direct-mapped properties (resolve dynamic values)\n for (const prop of DIRECT_MAP_PROPS) {\n if (prop in style) {\n let resolved = resolveStyleValue(style[prop], state, locals);\n if (resolved !== undefined) {\n // Map spec alignment values (start/end) to CSS flexbox values\n if (\n FLEX_ALIGNMENT_PROPS.has(prop) &&\n typeof resolved === 'string' &&\n resolved in FLEX_ALIGNMENT_MAP\n ) {\n resolved = FLEX_ALIGNMENT_MAP[resolved];\n }\n // Block forbidden CSS functions (defense-in-depth)\n if (typeof resolved === 'string' && containsForbiddenCssFunction(resolved)) {\n continue;\n }\n (css as Record<string, unknown>)[prop] = resolved;\n }\n }\n }\n\n // Transform object -> CSS transform string\n if (style.transform && typeof style.transform === 'object') {\n css.transform = transformToCss(style.transform as Record<string, unknown>);\n }\n\n // Border shorthand\n if (style.border && typeof style.border === 'object') {\n css.border = borderToCss(style.border as Record<string, unknown>);\n }\n\n // Border sides (borderTop, borderRight, borderBottom, borderLeft)\n for (const side of ['borderTop', 'borderRight', 'borderBottom', 'borderLeft'] as const) {\n if (style[side] && typeof style[side] === 'object') {\n (css as Record<string, unknown>)[side] = borderToCss(\n style[side] as Record<string, unknown>,\n );\n }\n }\n\n // Box shadow\n if (style.boxShadow) {\n css.boxShadow = shadowToCss(style.boxShadow);\n }\n\n // Background gradient -> CSS background\n if (style.backgroundGradient && typeof style.backgroundGradient === 'object') {\n css.background = gradientToCss(\n style.backgroundGradient as Record<string, unknown>,\n );\n }\n\n // Transition object(s) -> CSS transition string\n if (style.transition) {\n const transitionCss = mapTransition(style.transition);\n if (transitionCss) {\n css.transition = transitionCss;\n }\n }\n\n return css;\n}\n\n// ---------------------------------------------------------------------------\n// mapTransition — convert structured transition to CSS string\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a TransitionDef or TransitionDef[] to a CSS transition string.\n *\n * @example\n * mapTransition({ property: 'height', duration: 600, easing: 'ease' })\n * // => \"height 600ms ease\"\n *\n * mapTransition([\n * { property: 'height', duration: 600, easing: 'ease' },\n * { property: 'opacity', duration: 300 },\n * ])\n * // => \"height 600ms ease, opacity 300ms\"\n */\n/**\n * Map from SUU camelCase property names to valid CSS property names.\n * Most properties are simple camelCase → kebab-case, but some\n * (like borderRadius directional variants) have non-obvious CSS names.\n */\nconst CSS_PROPERTY_NAME_MAP: Record<string, string> = {\n borderRadiusTopLeft: 'border-top-left-radius',\n borderRadiusTopRight: 'border-top-right-radius',\n borderRadiusBottomLeft: 'border-bottom-left-radius',\n borderRadiusBottomRight: 'border-bottom-right-radius',\n};\n\n/**\n * Convert a SUU camelCase property name to its CSS property name.\n * Uses explicit mapping for irregular names, falls back to\n * generic camelCase → kebab-case conversion.\n */\nfunction toCssPropertyName(prop: string): string {\n if (prop in CSS_PROPERTY_NAME_MAP) {\n return CSS_PROPERTY_NAME_MAP[prop];\n }\n return prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);\n}\n\nexport function mapTransition(transition: unknown): string | undefined {\n if (!transition) return undefined;\n\n const items = Array.isArray(transition) ? transition : [transition];\n const parts: string[] = [];\n\n for (const item of items) {\n if (typeof item !== 'object' || item === null) continue;\n const t = item as Record<string, unknown>;\n const property = t.property;\n const duration = t.duration;\n if (typeof property !== 'string' || typeof duration !== 'number') continue;\n\n // Defense-in-depth: reject properties not in whitelist at render time\n if (!(ALLOWED_TRANSITION_PROPERTIES as readonly string[]).includes(property)) {\n continue;\n }\n\n // Defense-in-depth: reject easing values that aren't in the allowed set\n const ALLOWED_EASINGS = new Set(['ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out']);\n\n // Convert SUU property name to valid CSS property name\n const cssProperty = toCssPropertyName(property);\n\n let part = `${cssProperty} ${duration}ms`;\n if (typeof t.easing === 'string' && ALLOWED_EASINGS.has(t.easing)) {\n part += ` ${t.easing}`;\n }\n if (typeof t.delay === 'number' && t.delay > 0) {\n part += ` ${t.delay}ms`;\n }\n parts.push(part);\n }\n\n return parts.length > 0 ? parts.join(', ') : undefined;\n}\n","/**\n * @safe-ugc-ui/react — Asset Resolver\n *\n * Maps @assets/ paths used by card creators to actual platform-provided URLs.\n * Card authors reference assets as \"@assets/name.png\", and the hosting platform\n * provides a mapping to real URLs (blob:, data:, or CDN URLs).\n */\n\nexport type AssetMap = Record<string, string>;\n\n/**\n * Resolve an asset path to its actual URL.\n *\n * Card creators use `@assets/name.png`; the platform provides actual URLs.\n * Looks up by full path first, then by the key after `@assets/`.\n *\n * @param path - The asset path (e.g. \"@assets/avatar.png\")\n * @param assets - The asset map from the platform\n * @returns The resolved URL, or undefined if not found\n */\nexport function resolveAsset(\n path: string,\n assets: AssetMap,\n): string | undefined {\n if (!path.startsWith('@assets/')) return undefined;\n\n // Try full path match first\n if (path in assets) return assets[path];\n\n // Try key-only match (strip @assets/ prefix)\n const key = path.slice('@assets/'.length);\n if (key in assets) return assets[key];\n\n return undefined;\n}\n","/**\n * @safe-ugc-ui/react — useHoverStyle Hook\n *\n * Manages hover state for components with hoverStyle support.\n * Returns the merged style and mouse event handlers.\n *\n * When hoverStyle is not provided, returns base style with no handlers\n * to avoid unnecessary re-renders.\n */\n\nimport { useState, useMemo, type CSSProperties } from 'react';\n\ninterface UseHoverStyleResult {\n style: CSSProperties | undefined;\n onMouseEnter: (() => void) | undefined;\n onMouseLeave: (() => void) | undefined;\n}\n\nexport function useHoverStyle(\n baseStyle: CSSProperties | undefined,\n hoverStyle: CSSProperties | undefined,\n): UseHoverStyleResult {\n const [hovered, setHovered] = useState(false);\n\n const mergedStyle = useMemo(() => {\n if (!hoverStyle || !hovered) return baseStyle;\n return { ...baseStyle, ...hoverStyle };\n }, [baseStyle, hoverStyle, hovered]);\n\n if (!hoverStyle) {\n return { style: baseStyle, onMouseEnter: undefined, onMouseLeave: undefined };\n }\n\n return {\n style: mergedStyle,\n onMouseEnter: () => setHovered(true),\n onMouseLeave: () => setHovered(false),\n };\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface BoxProps {\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n children?: ReactNode;\n}\n\nexport function Box({ style, hoverStyle, children }: BoxProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return <div style={resolvedStyle} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>{children}</div>;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface RowProps {\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n children?: ReactNode;\n}\n\nconst rowBase: CSSProperties = {\n display: 'flex',\n flexDirection: 'row',\n};\n\nexport function Row({ style, hoverStyle, children }: RowProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return <div style={{ ...rowBase, ...resolvedStyle }} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>{children}</div>;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface ColumnProps {\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n children?: ReactNode;\n}\n\nconst columnBase: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n};\n\nexport function Column({ style, hoverStyle, children }: ColumnProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return <div style={{ ...columnBase, ...resolvedStyle }} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>{children}</div>;\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface TextComponentProps {\n content: string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\n/**\n * Renders text content safely. NEVER uses dangerouslySetInnerHTML.\n * All content is rendered as text nodes via React's built-in escaping.\n */\nexport function Text({ content, style, hoverStyle }: TextComponentProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return <span style={resolvedStyle} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>{content}</span>;\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface ImageComponentProps {\n src: string;\n alt?: string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\n/**\n * Renders an image. Defense-in-depth: at the render level, we still\n * verify the src is a safe URL scheme. In practice, src will already\n * be resolved by asset-resolver before reaching this component.\n *\n * Allowed schemes:\n * - blob: (platform-provided blob URLs)\n * - data: (inline data URIs)\n * - https: (resolved CDN URLs from asset-resolver)\n * - http: (resolved URLs — platform decides)\n *\n * If the src starts with @assets/, it was not resolved and we render nothing.\n */\nexport function Image({ src, alt, style, hoverStyle }: ImageComponentProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n\n // SECURITY: reject unresolved @assets/ paths (should already be resolved)\n if (src.startsWith('@assets/')) {\n return null;\n }\n\n return <img src={src} alt={alt ?? ''} style={resolvedStyle} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface StackProps {\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n children?: ReactNode;\n}\n\nconst stackBase: CSSProperties = {\n position: 'relative',\n};\n\nexport function Stack({ style, hoverStyle, children }: StackProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return <div style={{ ...stackBase, ...resolvedStyle }} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>{children}</div>;\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface GridProps {\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n children?: ReactNode;\n}\n\nconst gridBase: CSSProperties = {\n display: 'grid',\n};\n\nexport function Grid({ style, hoverStyle, children }: GridProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return <div style={{ ...gridBase, ...resolvedStyle }} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>{children}</div>;\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface SpacerProps {\n size?: number | string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function Spacer({ size, style, hoverStyle }: SpacerProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return <div style={{ width: size, height: size, flexShrink: 0, ...resolvedStyle }} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />;\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface DividerProps {\n color?: string;\n thickness?: number | string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nfunction formatThickness(thickness: number | string | undefined): string {\n if (thickness == null) return '1px';\n if (typeof thickness === 'number') return `${thickness}px`;\n // Numeric string (e.g. \"2\") → append px\n if (/^-?\\d+(\\.\\d+)?$/.test(thickness)) return `${thickness}px`;\n // Already has unit (e.g. \"2px\", \"1rem\") → use as-is\n return thickness;\n}\n\nexport function Divider({ color, thickness, style, hoverStyle }: DividerProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return (\n <div\n style={{\n borderTop: `${formatThickness(thickness)} solid ${color ?? '#e0e0e0'}`,\n width: '100%',\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n />\n );\n}\n","import type { CSSProperties, ReactNode } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface IconProps {\n name: string;\n size?: number | string;\n color?: string;\n iconResolver?: (name: string) => ReactNode;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function Icon({ name, size, color, iconResolver, style, hoverStyle }: IconProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n\n if (!iconResolver) {\n return null;\n }\n\n return (\n <span\n style={{\n fontSize: size,\n color,\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {iconResolver(name)}\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface ProgressBarProps {\n value: number;\n max: number;\n color?: string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function ProgressBar({ value, max, color, style, hoverStyle }: ProgressBarProps) {\n const percentage = max <= 0 ? 0 : Math.min(100, Math.max(0, (value / max) * 100));\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n\n return (\n <div\n style={{\n backgroundColor: '#e0e0e0',\n borderRadius: 4,\n overflow: 'hidden',\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n <div\n style={{\n width: `${percentage}%`,\n backgroundColor: color ?? '#4caf50',\n height: 8,\n }}\n />\n </div>\n );\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface AvatarProps {\n src: string;\n alt?: string;\n size?: number | string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function Avatar({ src, alt, size, style, hoverStyle }: AvatarProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return (\n <img\n src={src}\n alt={alt ?? ''}\n style={{\n width: size ?? 40,\n height: size ?? 40,\n borderRadius: '50%',\n objectFit: 'cover',\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n />\n );\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface BadgeProps {\n label: string;\n color?: string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function Badge({ label, color, style, hoverStyle }: BadgeProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return (\n <span\n style={{\n display: 'inline-block',\n padding: '2px 8px',\n borderRadius: 12,\n backgroundColor: color ?? '#e0e0e0',\n fontSize: 12,\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {label}\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface ChipProps {\n label: string;\n color?: string;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function Chip({ label, color, style, hoverStyle }: ChipProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return (\n <span\n style={{\n display: 'inline-block',\n padding: '4px 12px',\n borderRadius: 16,\n border: `1px solid ${color ?? '#e0e0e0'}`,\n fontSize: 14,\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {label}\n </span>\n );\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface ButtonProps {\n label: string;\n action: string;\n onAction?: (type: string, actionId: string) => void;\n disabled?: boolean;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function Button({ label, action, onAction, disabled, style, hoverStyle }: ButtonProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return (\n <button\n disabled={disabled}\n onClick={() => onAction?.('button', action)}\n style={{\n padding: '8px 16px',\n borderRadius: 6,\n border: '1px solid #ccc',\n cursor: disabled ? 'default' : 'pointer',\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {label}\n </button>\n );\n}\n","import type { CSSProperties } from 'react';\nimport { useHoverStyle } from '../hooks/useHoverStyle.js';\n\ninterface ToggleProps {\n value: boolean;\n onToggle: string;\n onAction?: (type: string, actionId: string, payload?: unknown) => void;\n disabled?: boolean;\n style?: CSSProperties;\n hoverStyle?: CSSProperties;\n}\n\nexport function Toggle({ value, onToggle, onAction, disabled, style, hoverStyle }: ToggleProps) {\n const { style: resolvedStyle, onMouseEnter, onMouseLeave } = useHoverStyle(style, hoverStyle);\n return (\n <button\n disabled={disabled}\n onClick={() => onAction?.('toggle', onToggle, { value: !value })}\n style={{\n padding: '6px 16px',\n borderRadius: 12,\n border: '1px solid #ccc',\n backgroundColor: value ? '#4caf50' : '#e0e0e0',\n color: value ? '#fff' : '#333',\n cursor: disabled ? 'default' : 'pointer',\n ...resolvedStyle,\n }}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {value ? 'ON' : 'OFF'}\n </button>\n );\n}\n"],"mappings":"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","names":["useMemo","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","jsx","_","jsx","containerStyle","useMemo"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safe-ugc-ui/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "React renderer for Safe UGC UI cards with sandboxed execution",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,8 +21,8 @@
21
21
  "dist"
22
22
  ],
23
23
  "dependencies": {
24
- "@safe-ugc-ui/types": "0.1.0",
25
- "@safe-ugc-ui/validator": "0.1.0"
24
+ "@safe-ugc-ui/types": "0.2.0",
25
+ "@safe-ugc-ui/validator": "0.2.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": "^18.0.0 || ^19.0.0",