@wireweave/core 1.1.0 → 1.2.0-beta.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/renderer.cjs CHANGED
@@ -21,9 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var renderer_exports = {};
22
22
  __export(renderer_exports, {
23
23
  HtmlRenderer: () => HtmlRenderer,
24
- SvgRenderer: () => SvgRenderer,
25
24
  createHtmlRenderer: () => createHtmlRenderer,
26
- createSvgRenderer: () => createSvgRenderer,
27
25
  darkTheme: () => darkTheme,
28
26
  defaultTheme: () => defaultTheme,
29
27
  generateComponentStyles: () => generateComponentStyles,
@@ -34,8 +32,7 @@ __export(renderer_exports, {
34
32
  render: () => render,
35
33
  renderIconSvg: () => renderIconSvg,
36
34
  renderToHtml: () => renderToHtml,
37
- renderToPureSvg: () => renderToPureSvg,
38
- renderToSvg: () => renderToSvg2
35
+ renderToSvg: () => renderToSvg
39
36
  });
40
37
  module.exports = __toCommonJS(renderer_exports);
41
38
 
@@ -92,23 +89,7 @@ function getTheme(name) {
92
89
  return name === "dark" ? darkTheme : defaultTheme;
93
90
  }
94
91
 
95
- // src/renderer/styles-components.ts
96
- function generateComponentStyles(_theme, prefix = "wf") {
97
- const parts = [
98
- generateContainerStyles(prefix),
99
- generateTextStyles(prefix),
100
- generateInputStyles(_theme, prefix),
101
- generateButtonStyles(_theme, prefix),
102
- generateDisplayStyles(_theme, prefix),
103
- generateDataStyles(_theme, prefix),
104
- generateFeedbackStyles(_theme, prefix),
105
- generateOverlayStyles(_theme, prefix),
106
- generateNavigationStyles(_theme, prefix),
107
- generateSemanticMarkerStyles(_theme, prefix),
108
- generateAccessibilityStyles(prefix)
109
- ];
110
- return parts.join("\n\n");
111
- }
92
+ // src/renderer/styles/container.ts
112
93
  function generateContainerStyles(prefix) {
113
94
  return `/* Container Components */
114
95
  .${prefix}-card {
@@ -118,12 +99,17 @@ function generateContainerStyles(prefix) {
118
99
  padding: 16px;
119
100
  }
120
101
 
121
- /* Cards in flex rows should expand equally */
102
+ /* Cards in flex rows: respect explicit width, shrink if needed */
122
103
  .${prefix}-row > .${prefix}-card {
123
- flex: 1 1 0%;
104
+ flex: 0 1 auto;
124
105
  min-width: 0;
125
106
  }
126
107
 
108
+ /* Cards without explicit width should expand to fill space */
109
+ .${prefix}-row > .${prefix}-card-flex {
110
+ flex: 1 1 0%;
111
+ }
112
+
127
113
  .${prefix}-card-title {
128
114
  margin: 0 0 12px 0;
129
115
  font-size: 18px;
@@ -243,6 +229,8 @@ function generateContainerStyles(prefix) {
243
229
  padding: 16px;
244
230
  }`;
245
231
  }
232
+
233
+ // src/renderer/styles/text.ts
246
234
  function generateTextStyles(prefix) {
247
235
  return `/* Text Components */
248
236
  .${prefix}-text {
@@ -277,6 +265,11 @@ function generateTextStyles(prefix) {
277
265
  line-height: 1.25;
278
266
  }
279
267
 
268
+ /* Remove bottom margin when title is in a row (inline with other elements) */
269
+ .${prefix}-row .${prefix}-title {
270
+ margin-bottom: 0;
271
+ }
272
+
280
273
  h1.${prefix}-title { font-size: 36px; }
281
274
  h2.${prefix}-title { font-size: 30px; }
282
275
  h3.${prefix}-title { font-size: 24px; }
@@ -294,6 +287,8 @@ h6.${prefix}-title { font-size: 16px; }
294
287
  opacity: 0.7;
295
288
  }`;
296
289
  }
290
+
291
+ // src/renderer/styles/input.ts
297
292
  function generateInputStyles(_theme, prefix) {
298
293
  return `/* Input Components */
299
294
  .${prefix}-form-field {
@@ -493,6 +488,8 @@ function generateInputStyles(_theme, prefix) {
493
488
  cursor: pointer;
494
489
  }`;
495
490
  }
491
+
492
+ // src/renderer/styles/button.ts
496
493
  function generateButtonStyles(_theme, prefix) {
497
494
  return `/* Button Components */
498
495
  .${prefix}-button {
@@ -620,6 +617,8 @@ function generateButtonStyles(_theme, prefix) {
620
617
  .${prefix}-button.${prefix}-justify-end { justify-content: flex-end; }
621
618
  .${prefix}-button.${prefix}-justify-between { justify-content: space-between; }`;
622
619
  }
620
+
621
+ // src/renderer/styles/display.ts
623
622
  function generateDisplayStyles(_theme, prefix) {
624
623
  return `/* Display Components */
625
624
  .${prefix}-image {
@@ -671,6 +670,30 @@ img.${prefix}-image {
671
670
  font-size: 14px;
672
671
  }
673
672
 
673
+ .${prefix}-placeholder-with-children {
674
+ position: relative;
675
+ }
676
+
677
+ .${prefix}-placeholder-label {
678
+ position: absolute;
679
+ top: 50%;
680
+ left: 50%;
681
+ transform: translate(-50%, -50%);
682
+ z-index: 0;
683
+ pointer-events: none;
684
+ }
685
+
686
+ .${prefix}-placeholder-overlay {
687
+ position: absolute;
688
+ top: 0;
689
+ left: 0;
690
+ right: 0;
691
+ bottom: 0;
692
+ z-index: 1;
693
+ display: flex;
694
+ flex-direction: column;
695
+ }
696
+
674
697
  .${prefix}-avatar {
675
698
  display: inline-flex;
676
699
  align-items: center;
@@ -786,12 +809,12 @@ svg.${prefix}-icon {
786
809
  display: block;
787
810
  }
788
811
 
789
- /* Icon size tokens */
812
+ /* Icon size tokens - matches SVG renderer */
790
813
  svg.${prefix}-icon-xs { width: 12px; height: 12px; }
791
- svg.${prefix}-icon-sm { width: 14px; height: 14px; }
792
- svg.${prefix}-icon-md { width: 16px; height: 16px; }
793
- svg.${prefix}-icon-lg { width: 20px; height: 20px; }
794
- svg.${prefix}-icon-xl { width: 24px; height: 24px; }
814
+ svg.${prefix}-icon-sm { width: 16px; height: 16px; }
815
+ svg.${prefix}-icon-md { width: 20px; height: 20px; }
816
+ svg.${prefix}-icon-lg { width: 24px; height: 24px; }
817
+ svg.${prefix}-icon-xl { width: 32px; height: 32px; }
795
818
 
796
819
  .${prefix}-icon svg {
797
820
  display: block;
@@ -803,6 +826,8 @@ svg.${prefix}-icon-xl { width: 24px; height: 24px; }
803
826
  justify-content: center;
804
827
  }`;
805
828
  }
829
+
830
+ // src/renderer/styles/data.ts
806
831
  function generateDataStyles(_theme, prefix) {
807
832
  return `/* Data Components */
808
833
  .${prefix}-table {
@@ -861,6 +886,8 @@ function generateDataStyles(_theme, prefix) {
861
886
  border-bottom: none;
862
887
  }`;
863
888
  }
889
+
890
+ // src/renderer/styles/feedback.ts
864
891
  function generateFeedbackStyles(_theme, prefix) {
865
892
  return `/* Feedback Components */
866
893
  .${prefix}-alert {
@@ -952,6 +979,8 @@ function generateFeedbackStyles(_theme, prefix) {
952
979
  to { transform: rotate(360deg); }
953
980
  }`;
954
981
  }
982
+
983
+ // src/renderer/styles/overlay.ts
955
984
  function generateOverlayStyles(_theme, prefix) {
956
985
  return `/* Overlay Components */
957
986
  .${prefix}-tooltip-wrapper {
@@ -1050,6 +1079,8 @@ function generateOverlayStyles(_theme, prefix) {
1050
1079
  cursor: not-allowed;
1051
1080
  }`;
1052
1081
  }
1082
+
1083
+ // src/renderer/styles/navigation.ts
1053
1084
  function generateNavigationStyles(_theme, prefix) {
1054
1085
  return `/* Navigation Components */
1055
1086
  .${prefix}-nav {
@@ -1087,6 +1118,27 @@ function generateNavigationStyles(_theme, prefix) {
1087
1118
  cursor: not-allowed;
1088
1119
  }
1089
1120
 
1121
+ .${prefix}-nav-group {
1122
+ display: flex;
1123
+ flex-direction: column;
1124
+ gap: 4px;
1125
+ }
1126
+
1127
+ .${prefix}-nav-group-label {
1128
+ font-size: 11px;
1129
+ font-weight: 500;
1130
+ color: var(--${prefix}-muted);
1131
+ text-transform: uppercase;
1132
+ letter-spacing: 0.05em;
1133
+ padding: 8px 16px 4px;
1134
+ }
1135
+
1136
+ .${prefix}-nav-divider {
1137
+ margin: 8px 0;
1138
+ border: none;
1139
+ border-top: 1px solid var(--${prefix}-border);
1140
+ }
1141
+
1090
1142
  .${prefix}-tabs {
1091
1143
  border-bottom: 1px solid var(--${prefix}-border);
1092
1144
  }
@@ -1141,6 +1193,8 @@ function generateNavigationStyles(_theme, prefix) {
1141
1193
  color: var(--${prefix}-muted);
1142
1194
  }`;
1143
1195
  }
1196
+
1197
+ // src/renderer/styles/semantic.ts
1144
1198
  function generateSemanticMarkerStyles(_theme, prefix) {
1145
1199
  return `/* Semantic Markers */
1146
1200
 
@@ -1227,6 +1281,8 @@ function generateSemanticMarkerStyles(_theme, prefix) {
1227
1281
  color: var(--${prefix}-muted);
1228
1282
  }`;
1229
1283
  }
1284
+
1285
+ // src/renderer/styles/accessibility.ts
1230
1286
  function generateAccessibilityStyles(prefix) {
1231
1287
  return `/* Accessibility Utilities */
1232
1288
  .sr-only {
@@ -1264,6 +1320,24 @@ function generateAccessibilityStyles(prefix) {
1264
1320
  }`;
1265
1321
  }
1266
1322
 
1323
+ // src/renderer/styles-components.ts
1324
+ function generateComponentStyles(_theme, prefix = "wf") {
1325
+ const parts = [
1326
+ generateContainerStyles(prefix),
1327
+ generateTextStyles(prefix),
1328
+ generateInputStyles(_theme, prefix),
1329
+ generateButtonStyles(_theme, prefix),
1330
+ generateDisplayStyles(_theme, prefix),
1331
+ generateDataStyles(_theme, prefix),
1332
+ generateFeedbackStyles(_theme, prefix),
1333
+ generateOverlayStyles(_theme, prefix),
1334
+ generateNavigationStyles(_theme, prefix),
1335
+ generateSemanticMarkerStyles(_theme, prefix),
1336
+ generateAccessibilityStyles(prefix)
1337
+ ];
1338
+ return parts.join("\n\n");
1339
+ }
1340
+
1267
1341
  // src/renderer/styles.ts
1268
1342
  function generateStyles(theme, prefix = "wf") {
1269
1343
  const parts = [
@@ -1304,7 +1378,6 @@ function generateBaseStyles(prefix) {
1304
1378
  font-family: var(--${prefix}-font);
1305
1379
  color: var(--${prefix}-fg);
1306
1380
  background: var(--${prefix}-bg);
1307
- min-height: 100vh;
1308
1381
  box-sizing: border-box;
1309
1382
  position: relative;
1310
1383
  display: flex;
@@ -1315,10 +1388,19 @@ function generateBaseStyles(prefix) {
1315
1388
  overflow: hidden;
1316
1389
  }
1317
1390
 
1391
+ /* Col direct child of page should fill page height */
1392
+ .${prefix}-page > .${prefix}-col {
1393
+ flex: 1;
1394
+ min-height: 0;
1395
+ }
1396
+
1318
1397
  /* Row containing sidebar should fill remaining space */
1319
1398
  .${prefix}-page > .${prefix}-row:has(.${prefix}-sidebar),
1320
- .${prefix}-page > .${prefix}-row:has(.${prefix}-main) {
1399
+ .${prefix}-page > .${prefix}-row:has(.${prefix}-main),
1400
+ .${prefix}-page > .${prefix}-col > .${prefix}-row:has(.${prefix}-sidebar),
1401
+ .${prefix}-page > .${prefix}-col > .${prefix}-row:has(.${prefix}-main) {
1321
1402
  flex: 1;
1403
+ min-height: 0;
1322
1404
  align-items: stretch;
1323
1405
  }
1324
1406
 
@@ -1361,10 +1443,15 @@ function generateGridClasses(_theme, prefix) {
1361
1443
  box-sizing: border-box;
1362
1444
  }
1363
1445
 
1446
+ /* When explicit width is set, don't flex-grow */
1447
+ .${prefix}-row[style*="width:"],
1448
+ .${prefix}-col[style*="width:"] {
1449
+ flex: 0 0 auto;
1450
+ }
1451
+
1364
1452
  `;
1365
1453
  for (let i = 1; i <= 12; i++) {
1366
- const width = (i / 12 * 100).toFixed(4);
1367
- css += `.${prefix}-col-${i} { flex: 0 0 auto; width: ${width}%; }
1454
+ css += `.${prefix}-col-${i} { flex: ${i} 0 0%; min-width: 0; }
1368
1455
  `;
1369
1456
  }
1370
1457
  return css;
@@ -1532,6 +1619,26 @@ function generateLayoutClasses(prefix) {
1532
1619
  .${prefix}-main {
1533
1620
  flex: 1;
1534
1621
  padding: 16px;
1622
+ display: flex;
1623
+ flex-direction: column;
1624
+ min-height: 0;
1625
+ }
1626
+
1627
+ /* Scrollable main content */
1628
+ .${prefix}-main.${prefix}-scroll {
1629
+ overflow-y: auto;
1630
+ overflow-x: hidden;
1631
+ }
1632
+
1633
+ /* Col containing scrollable main needs min-height: 0 for scroll to work */
1634
+ .${prefix}-col:has(> .${prefix}-main.${prefix}-scroll) {
1635
+ min-height: 0;
1636
+ }
1637
+
1638
+ /* Main content should align to top, not stretch to fill */
1639
+ /* But allow explicit flex=1 to override */
1640
+ .${prefix}-main > .${prefix}-col:not(.${prefix}-flex-1) {
1641
+ flex: 0 0 auto;
1535
1642
  }
1536
1643
 
1537
1644
  .${prefix}-footer {
@@ -1548,6 +1655,7 @@ function generateLayoutClasses(prefix) {
1548
1655
  border-right: 1px solid var(--${prefix}-border);
1549
1656
  padding: 16px 16px 16px 20px;
1550
1657
  flex-shrink: 0;
1658
+ align-self: stretch;
1551
1659
  }
1552
1660
 
1553
1661
  .${prefix}-sidebar-right {
@@ -1761,6 +1869,248 @@ function resolveViewport(viewport, device) {
1761
1869
  return DEFAULT_VIEWPORT;
1762
1870
  }
1763
1871
 
1872
+ // src/renderer/html/components.ts
1873
+ function buildClassString(classes) {
1874
+ return classes.filter(Boolean).join(" ");
1875
+ }
1876
+ var SIZE_TOKENS = {
1877
+ icon: { xs: 12, sm: 14, md: 16, lg: 20, xl: 24 },
1878
+ avatar: { xs: 24, sm: 32, md: 40, lg: 48, xl: 64 },
1879
+ spinner: { xs: 12, sm: 16, md: 24, lg: 32, xl: 48 }
1880
+ };
1881
+ function resolveSizeValue(size, componentType, prefix) {
1882
+ if (size === void 0) {
1883
+ return {};
1884
+ }
1885
+ if (typeof size === "string") {
1886
+ const tokens = SIZE_TOKENS[componentType];
1887
+ if (size in tokens) {
1888
+ return { className: `${prefix}-${componentType}-${size}` };
1889
+ }
1890
+ const parsed = parseInt(size, 10);
1891
+ if (!isNaN(parsed)) {
1892
+ return { style: `width: ${parsed}px; height: ${parsed}px;` };
1893
+ }
1894
+ return {};
1895
+ }
1896
+ if (typeof size === "number") {
1897
+ return { style: `width: ${size}px; height: ${size}px;` };
1898
+ }
1899
+ return {};
1900
+ }
1901
+
1902
+ // src/renderer/html/renderers/layout.ts
1903
+ function renderHeader(node, ctx) {
1904
+ const classes = ctx.buildClassString([
1905
+ `${ctx.prefix}-header`,
1906
+ node.border === false ? `${ctx.prefix}-no-border` : void 0,
1907
+ ...ctx.getCommonClasses(node)
1908
+ ]);
1909
+ const styles = ctx.buildCommonStyles(node);
1910
+ const styleAttr = styles ? ` style="${styles}"` : "";
1911
+ const children = ctx.renderChildren(node.children);
1912
+ return `<header class="${classes}"${styleAttr}>
1913
+ ${children}
1914
+ </header>`;
1915
+ }
1916
+ function renderMain(node, ctx) {
1917
+ const classes = ctx.buildClassString([
1918
+ `${ctx.prefix}-main`,
1919
+ node.scroll ? `${ctx.prefix}-scroll` : void 0,
1920
+ ...ctx.getCommonClasses(node)
1921
+ ]);
1922
+ const styles = ctx.buildCommonStyles(node);
1923
+ const styleAttr = styles ? ` style="${styles}"` : "";
1924
+ const children = ctx.renderChildren(node.children);
1925
+ return `<main class="${classes}"${styleAttr}>
1926
+ ${children}
1927
+ </main>`;
1928
+ }
1929
+ function renderFooter(node, ctx) {
1930
+ const classes = ctx.buildClassString([
1931
+ `${ctx.prefix}-footer`,
1932
+ node.border === false ? `${ctx.prefix}-no-border` : void 0,
1933
+ ...ctx.getCommonClasses(node)
1934
+ ]);
1935
+ const styles = ctx.buildCommonStyles(node);
1936
+ const styleAttr = styles ? ` style="${styles}"` : "";
1937
+ const children = ctx.renderChildren(node.children);
1938
+ return `<footer class="${classes}"${styleAttr}>
1939
+ ${children}
1940
+ </footer>`;
1941
+ }
1942
+ function renderSidebar(node, ctx) {
1943
+ const classes = ctx.buildClassString([
1944
+ `${ctx.prefix}-sidebar`,
1945
+ node.position === "right" ? `${ctx.prefix}-sidebar-right` : void 0,
1946
+ ...ctx.getCommonClasses(node)
1947
+ ]);
1948
+ const styles = ctx.buildCommonStyles(node);
1949
+ const styleAttr = styles ? ` style="${styles}"` : "";
1950
+ const children = ctx.renderChildren(node.children);
1951
+ return `<aside class="${classes}"${styleAttr}>
1952
+ ${children}
1953
+ </aside>`;
1954
+ }
1955
+ function renderSection(node, ctx) {
1956
+ const classes = ctx.buildClassString([
1957
+ `${ctx.prefix}-section`,
1958
+ ...ctx.getCommonClasses(node)
1959
+ ]);
1960
+ const styles = ctx.buildCommonStyles(node);
1961
+ const styleAttr = styles ? ` style="${styles}"` : "";
1962
+ const title = node.title ? `<h2 class="${ctx.prefix}-title">${ctx.escapeHtml(node.title)}</h2>
1963
+ ` : "";
1964
+ const children = ctx.renderChildren(node.children);
1965
+ return `<section class="${classes}"${styleAttr}>
1966
+ ${title}${children}
1967
+ </section>`;
1968
+ }
1969
+
1970
+ // src/renderer/html/renderers/grid.ts
1971
+ function renderRow(node, ctx) {
1972
+ const classes = ctx.buildClassString([
1973
+ `${ctx.prefix}-row`,
1974
+ ...ctx.getCommonClasses(node)
1975
+ ]);
1976
+ const styles = ctx.buildCommonStyles(node);
1977
+ const styleAttr = styles ? ` style="${styles}"` : "";
1978
+ const children = ctx.renderChildren(node.children);
1979
+ return `<div class="${classes}"${styleAttr}>
1980
+ ${children}
1981
+ </div>`;
1982
+ }
1983
+ function renderCol(node, ctx) {
1984
+ const classes = ctx.buildClassString([
1985
+ `${ctx.prefix}-col`,
1986
+ node.span ? `${ctx.prefix}-col-${node.span}` : void 0,
1987
+ // Responsive breakpoint classes
1988
+ node.sm ? `${ctx.prefix}-col-sm-${node.sm}` : void 0,
1989
+ node.md ? `${ctx.prefix}-col-md-${node.md}` : void 0,
1990
+ node.lg ? `${ctx.prefix}-col-lg-${node.lg}` : void 0,
1991
+ node.xl ? `${ctx.prefix}-col-xl-${node.xl}` : void 0,
1992
+ ...ctx.getCommonClasses(node)
1993
+ ]);
1994
+ const styles = ctx.buildColStyles(node);
1995
+ const styleAttr = styles ? ` style="${styles}"` : "";
1996
+ const children = ctx.renderChildren(node.children);
1997
+ return `<div class="${classes}"${styleAttr}>
1998
+ ${children}
1999
+ </div>`;
2000
+ }
2001
+
2002
+ // src/renderer/html/renderers/container.ts
2003
+ function renderCard(node, ctx) {
2004
+ const hasExplicitWidth = node.w !== void 0;
2005
+ const classes = ctx.buildClassString([
2006
+ `${ctx.prefix}-card`,
2007
+ !hasExplicitWidth ? `${ctx.prefix}-card-flex` : void 0,
2008
+ node.shadow ? `${ctx.prefix}-card-shadow-${node.shadow}` : void 0,
2009
+ ...ctx.getCommonClasses(node)
2010
+ ]);
2011
+ const styles = ctx.buildCommonStyles(node);
2012
+ const styleAttr = styles ? ` style="${styles}"` : "";
2013
+ const title = node.title ? `<h3 class="${ctx.prefix}-title">${ctx.escapeHtml(node.title)}</h3>
2014
+ ` : "";
2015
+ const children = ctx.renderChildren(node.children);
2016
+ return `<div class="${classes}"${styleAttr}>
2017
+ ${title}${children}
2018
+ </div>`;
2019
+ }
2020
+ function renderModal(node, ctx) {
2021
+ const classes = ctx.buildClassString([
2022
+ `${ctx.prefix}-modal`,
2023
+ ...ctx.getCommonClasses(node)
2024
+ ]);
2025
+ const styles = ctx.buildCommonStyles(node);
2026
+ const styleAttr = styles ? ` style="${styles}"` : "";
2027
+ const title = node.title ? `<h2 class="${ctx.prefix}-title">${ctx.escapeHtml(node.title)}</h2>
2028
+ ` : "";
2029
+ const children = ctx.renderChildren(node.children);
2030
+ return `<div class="${ctx.prefix}-modal-backdrop">
2031
+ <div class="${classes}"${styleAttr} role="dialog" aria-modal="true">
2032
+ ${title}${children}
2033
+ </div>
2034
+ </div>`;
2035
+ }
2036
+ function renderDrawer(node, ctx) {
2037
+ const position = node.position || "left";
2038
+ const classes = ctx.buildClassString([
2039
+ `${ctx.prefix}-drawer`,
2040
+ `${ctx.prefix}-drawer-${position}`,
2041
+ ...ctx.getCommonClasses(node)
2042
+ ]);
2043
+ const styles = ctx.buildCommonStyles(node);
2044
+ const styleAttr = styles ? ` style="${styles}"` : "";
2045
+ const title = node.title ? `<h2 class="${ctx.prefix}-title">${ctx.escapeHtml(node.title)}</h2>
2046
+ ` : "";
2047
+ const children = ctx.renderChildren(node.children);
2048
+ return `<aside class="${classes}"${styleAttr}>
2049
+ ${title}${children}
2050
+ </aside>`;
2051
+ }
2052
+ function renderAccordion(node, ctx) {
2053
+ const classes = ctx.buildClassString([
2054
+ `${ctx.prefix}-accordion`,
2055
+ ...ctx.getCommonClasses(node)
2056
+ ]);
2057
+ const styles = ctx.buildCommonStyles(node);
2058
+ const styleAttr = styles ? ` style="${styles}"` : "";
2059
+ const title = node.title ? `<button class="${ctx.prefix}-accordion-header">${ctx.escapeHtml(node.title)}</button>
2060
+ ` : "";
2061
+ const children = ctx.renderChildren(node.children);
2062
+ return `<div class="${classes}"${styleAttr}>
2063
+ ${title}<div class="${ctx.prefix}-accordion-content">
2064
+ ${children}
2065
+ </div>
2066
+ </div>`;
2067
+ }
2068
+
2069
+ // src/renderer/html/renderers/text.ts
2070
+ function renderText(node, ctx) {
2071
+ const classes = ctx.buildClassString([
2072
+ `${ctx.prefix}-text`,
2073
+ node.size ? `${ctx.prefix}-text-${node.size}` : void 0,
2074
+ node.weight ? `${ctx.prefix}-text-${node.weight}` : void 0,
2075
+ node.align ? `${ctx.prefix}-text-${node.align}` : void 0,
2076
+ node.muted ? `${ctx.prefix}-text-muted` : void 0,
2077
+ ...ctx.getCommonClasses(node)
2078
+ ]);
2079
+ const styles = ctx.buildCommonStyles(node);
2080
+ const styleAttr = styles ? ` style="${styles}"` : "";
2081
+ return `<p class="${classes}"${styleAttr}>${ctx.escapeHtml(node.content)}</p>`;
2082
+ }
2083
+ function renderTitle(node, ctx) {
2084
+ const level = node.level || 1;
2085
+ const tag = `h${level}`;
2086
+ const classes = ctx.buildClassString([
2087
+ `${ctx.prefix}-title`,
2088
+ node.size ? `${ctx.prefix}-text-${node.size}` : void 0,
2089
+ node.align ? `${ctx.prefix}-text-${node.align}` : void 0,
2090
+ ...ctx.getCommonClasses(node)
2091
+ ]);
2092
+ const styles = ctx.buildCommonStyles(node);
2093
+ const styleAttr = styles ? ` style="${styles}"` : "";
2094
+ return `<${tag} class="${classes}"${styleAttr}>${ctx.escapeHtml(node.content)}</${tag}>`;
2095
+ }
2096
+ function renderLink(node, ctx) {
2097
+ const classes = ctx.buildClassString([
2098
+ `${ctx.prefix}-link`,
2099
+ ...ctx.getCommonClasses(node)
2100
+ ]);
2101
+ const styles = ctx.buildCommonStyles(node);
2102
+ const styleAttr = styles ? ` style="${styles}"` : "";
2103
+ const attrs = {
2104
+ class: classes,
2105
+ href: node.href || "#"
2106
+ };
2107
+ if (node.external) {
2108
+ attrs.target = "_blank";
2109
+ attrs.rel = "noopener noreferrer";
2110
+ }
2111
+ return `<a${ctx.buildAttrsString(attrs)}${styleAttr}>${ctx.escapeHtml(node.content)}</a>`;
2112
+ }
2113
+
1764
2114
  // src/icons/lucide-icons.ts
1765
2115
  var lucideIcons = {
1766
2116
  "a-arrow-down": [
@@ -48117,14 +48467,39 @@ var lucideIcons = {
48117
48467
  ]
48118
48468
  ]
48119
48469
  };
48470
+ var iconAliases = {
48471
+ "home": "house",
48472
+ "plus-square": "square-plus",
48473
+ "minus-square": "square-minus",
48474
+ "x-square": "square-x",
48475
+ "check-square": "square-check",
48476
+ "edit": "pencil",
48477
+ "edit-2": "pencil",
48478
+ "edit-3": "pencil-line",
48479
+ "trash": "trash-2",
48480
+ "delete": "trash-2",
48481
+ "close": "x",
48482
+ "menu": "menu",
48483
+ "hamburger": "menu",
48484
+ "dots": "more-horizontal",
48485
+ "dots-vertical": "more-vertical",
48486
+ "cog": "settings",
48487
+ "gear": "settings"
48488
+ };
48120
48489
  function getIconData(name) {
48121
48490
  if (lucideIcons[name]) {
48122
48491
  return lucideIcons[name];
48123
48492
  }
48493
+ if (iconAliases[name] && lucideIcons[iconAliases[name]]) {
48494
+ return lucideIcons[iconAliases[name]];
48495
+ }
48124
48496
  const kebabName = name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
48125
48497
  if (lucideIcons[kebabName]) {
48126
48498
  return lucideIcons[kebabName];
48127
48499
  }
48500
+ if (iconAliases[kebabName] && lucideIcons[iconAliases[kebabName]]) {
48501
+ return lucideIcons[iconAliases[kebabName]];
48502
+ }
48128
48503
  return void 0;
48129
48504
  }
48130
48505
  function renderIconSvg(data, _size = 24, strokeWidth = 2, className = "", styleAttr = "") {
@@ -48135,34 +48510,658 @@ function renderIconSvg(data, _size = 24, strokeWidth = 2, className = "", styleA
48135
48510
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="${strokeWidth}" stroke-linecap="round" stroke-linejoin="round" class="${className}"${styleAttr}>${elements}</svg>`;
48136
48511
  }
48137
48512
 
48138
- // src/renderer/html/components.ts
48139
- function buildClassString(classes) {
48140
- return classes.filter(Boolean).join(" ");
48513
+ // src/renderer/html/renderers/button.ts
48514
+ function renderButton(node, ctx) {
48515
+ const isIconOnly = node.icon && (!node.content.trim() || node.content === "Button");
48516
+ const classes = ctx.buildClassString([
48517
+ `${ctx.prefix}-button`,
48518
+ node.primary ? `${ctx.prefix}-button-primary` : void 0,
48519
+ node.secondary ? `${ctx.prefix}-button-secondary` : void 0,
48520
+ node.outline ? `${ctx.prefix}-button-outline` : void 0,
48521
+ node.ghost ? `${ctx.prefix}-button-ghost` : void 0,
48522
+ node.danger ? `${ctx.prefix}-button-danger` : void 0,
48523
+ node.size ? `${ctx.prefix}-button-${node.size}` : void 0,
48524
+ node.disabled ? `${ctx.prefix}-button-disabled` : void 0,
48525
+ node.loading ? `${ctx.prefix}-button-loading` : void 0,
48526
+ isIconOnly ? `${ctx.prefix}-button-icon-only` : void 0,
48527
+ ...ctx.getCommonClasses(node)
48528
+ ]);
48529
+ const styles = ctx.buildCommonStyles(node);
48530
+ const styleAttr = styles ? ` style="${styles}"` : "";
48531
+ const attrs = {
48532
+ class: classes,
48533
+ disabled: node.disabled
48534
+ };
48535
+ let icon = "";
48536
+ if (node.icon) {
48537
+ const iconData = getIconData(node.icon);
48538
+ if (iconData) {
48539
+ icon = renderIconSvg(iconData, 16, 2, `${ctx.prefix}-icon`);
48540
+ } else {
48541
+ icon = `<span class="${ctx.prefix}-icon">[${ctx.escapeHtml(node.icon)}]</span>`;
48542
+ }
48543
+ }
48544
+ const loading = node.loading ? `<span class="${ctx.prefix}-spinner ${ctx.prefix}-spinner-sm"></span>` : "";
48545
+ const content = isIconOnly ? "" : ctx.escapeHtml(node.content);
48546
+ return `<button${ctx.buildAttrsString(attrs)}${styleAttr}>${loading}${icon}${content}</button>`;
48141
48547
  }
48142
- var SIZE_TOKENS = {
48143
- icon: { xs: 12, sm: 14, md: 16, lg: 20, xl: 24 },
48144
- avatar: { xs: 24, sm: 32, md: 40, lg: 48, xl: 64 },
48145
- spinner: { xs: 12, sm: 16, md: 24, lg: 32, xl: 48 }
48146
- };
48147
- function resolveSizeValue(size, componentType, prefix) {
48148
- if (size === void 0) {
48149
- return {};
48548
+
48549
+ // src/renderer/html/renderers/feedback.ts
48550
+ function renderAlert(node, ctx) {
48551
+ const classes = ctx.buildClassString([
48552
+ `${ctx.prefix}-alert`,
48553
+ node.variant ? `${ctx.prefix}-alert-${node.variant}` : void 0,
48554
+ ...ctx.getCommonClasses(node)
48555
+ ]);
48556
+ const styles = ctx.buildCommonStyles(node);
48557
+ const styleAttr = styles ? ` style="${styles}"` : "";
48558
+ const dismissBtn = node.dismissible ? ` <button class="${ctx.prefix}-alert-close" aria-label="Close">&times;</button>` : "";
48559
+ return `<div class="${classes}"${styleAttr} role="alert">${ctx.escapeHtml(node.content)}${dismissBtn}</div>`;
48560
+ }
48561
+ function renderToast(node, ctx) {
48562
+ const classes = ctx.buildClassString([
48563
+ `${ctx.prefix}-toast`,
48564
+ node.position ? `${ctx.prefix}-toast-${node.position}` : void 0,
48565
+ node.variant ? `${ctx.prefix}-toast-${node.variant}` : void 0,
48566
+ ...ctx.getCommonClasses(node)
48567
+ ]);
48568
+ const styles = ctx.buildCommonStyles(node);
48569
+ const styleAttr = styles ? ` style="${styles}"` : "";
48570
+ return `<div class="${classes}"${styleAttr} role="status">${ctx.escapeHtml(node.content)}</div>`;
48571
+ }
48572
+ function renderProgress(node, ctx) {
48573
+ const classes = ctx.buildClassString([
48574
+ `${ctx.prefix}-progress`,
48575
+ ...ctx.getCommonClasses(node)
48576
+ ]);
48577
+ const styles = ctx.buildCommonStyles(node);
48578
+ const styleAttr = styles ? ` style="${styles}"` : "";
48579
+ const value = node.value || 0;
48580
+ const max = node.max || 100;
48581
+ const percentage = Math.round(value / max * 100);
48582
+ const label = node.label ? `<span class="${ctx.prefix}-progress-label">${ctx.escapeHtml(node.label)}</span>` : "";
48583
+ if (node.indeterminate) {
48584
+ return `<div class="${classes} ${ctx.prefix}-progress-indeterminate"${styleAttr} role="progressbar">${label}</div>`;
48150
48585
  }
48151
- if (typeof size === "string") {
48152
- const tokens = SIZE_TOKENS[componentType];
48153
- if (size in tokens) {
48154
- return { className: `${prefix}-${componentType}-${size}` };
48586
+ return `<div class="${classes}"${styleAttr} role="progressbar" aria-valuenow="${value}" aria-valuemin="0" aria-valuemax="${max}">
48587
+ ${label}
48588
+ <div class="${ctx.prefix}-progress-bar" style="width: ${percentage}%"></div>
48589
+ </div>`;
48590
+ }
48591
+ function renderSpinner(node, ctx) {
48592
+ const sizeResolved = resolveSizeValue(node.size, "spinner", ctx.prefix);
48593
+ const classes = ctx.buildClassString([
48594
+ `${ctx.prefix}-spinner`,
48595
+ sizeResolved.className,
48596
+ ...ctx.getCommonClasses(node)
48597
+ ]);
48598
+ const baseStyles = ctx.buildCommonStyles(node);
48599
+ const sizeStyle = sizeResolved.style || "";
48600
+ const combinedStyles = baseStyles && sizeStyle ? `${baseStyles}; ${sizeStyle}` : baseStyles || sizeStyle;
48601
+ const styleAttr = combinedStyles ? ` style="${combinedStyles}"` : "";
48602
+ const label = node.label || "Loading...";
48603
+ return `<span class="${classes}"${styleAttr} role="status" aria-label="${ctx.escapeHtml(label)}"></span>`;
48604
+ }
48605
+
48606
+ // src/renderer/html/renderers/input.ts
48607
+ function renderInput(node, ctx) {
48608
+ const inputClasses = ctx.buildClassString([
48609
+ `${ctx.prefix}-input`,
48610
+ node.icon ? `${ctx.prefix}-input-with-icon` : void 0,
48611
+ ...ctx.getCommonClasses(node)
48612
+ ]);
48613
+ const styles = ctx.buildCommonStyles(node);
48614
+ const styleAttr = styles ? ` style="${styles}"` : "";
48615
+ const attrs = {
48616
+ class: inputClasses,
48617
+ type: node.inputType || "text",
48618
+ placeholder: node.placeholder,
48619
+ value: node.value,
48620
+ disabled: node.disabled,
48621
+ required: node.required,
48622
+ readonly: node.readonly
48623
+ };
48624
+ const inputElement = `<input${ctx.buildAttrsString(attrs)} />`;
48625
+ if (node.icon) {
48626
+ const iconData = getIconData(node.icon);
48627
+ let iconHtml;
48628
+ if (iconData) {
48629
+ iconHtml = renderIconSvg(iconData, 16, 2, `${ctx.prefix}-input-icon`);
48630
+ } else {
48631
+ iconHtml = `<span class="${ctx.prefix}-input-icon">[${ctx.escapeHtml(node.icon)}]</span>`;
48155
48632
  }
48156
- const parsed = parseInt(size, 10);
48157
- if (!isNaN(parsed)) {
48158
- return { style: `width: ${parsed}px; height: ${parsed}px;` };
48633
+ const wrapperClasses = ctx.buildClassString([`${ctx.prefix}-input-wrapper`]);
48634
+ const wrapper = `<div class="${wrapperClasses}"${styleAttr}>${iconHtml}${inputElement}</div>`;
48635
+ const shouldShowLabel = node.label && !(node.label === "Label" && node.placeholder);
48636
+ if (shouldShowLabel) {
48637
+ return `<label class="${ctx.prefix}-input-label">${ctx.escapeHtml(node.label)}</label>
48638
+ ${wrapper}`;
48159
48639
  }
48160
- return {};
48640
+ return wrapper;
48161
48641
  }
48162
- if (typeof size === "number") {
48163
- return { style: `width: ${size}px; height: ${size}px;` };
48642
+ const input = `<input${ctx.buildAttrsString(attrs)}${styleAttr} />`;
48643
+ const shouldShowLabel2 = node.label && !(node.label === "Label" && node.placeholder);
48644
+ if (shouldShowLabel2) {
48645
+ return `<label class="${ctx.prefix}-input-label">${ctx.escapeHtml(node.label)}</label>
48646
+ ${input}`;
48164
48647
  }
48165
- return {};
48648
+ return input;
48649
+ }
48650
+ function renderTextarea(node, ctx) {
48651
+ const classes = ctx.buildClassString([
48652
+ `${ctx.prefix}-input`,
48653
+ ...ctx.getCommonClasses(node)
48654
+ ]);
48655
+ const styles = ctx.buildCommonStyles(node);
48656
+ const styleAttr = styles ? ` style="${styles}"` : "";
48657
+ const attrs = {
48658
+ class: classes,
48659
+ placeholder: node.placeholder,
48660
+ disabled: node.disabled,
48661
+ required: node.required,
48662
+ rows: node.rows?.toString()
48663
+ };
48664
+ const textarea = `<textarea${ctx.buildAttrsString(attrs)}${styleAttr}>${ctx.escapeHtml(node.value || "")}</textarea>`;
48665
+ if (node.label) {
48666
+ return `<label class="${ctx.prefix}-input-label">${ctx.escapeHtml(node.label)}</label>
48667
+ ${textarea}`;
48668
+ }
48669
+ return textarea;
48670
+ }
48671
+ function renderSelect(node, ctx) {
48672
+ const classes = ctx.buildClassString([
48673
+ `${ctx.prefix}-input`,
48674
+ ...ctx.getCommonClasses(node)
48675
+ ]);
48676
+ const styles = ctx.buildCommonStyles(node);
48677
+ const styleAttr = styles ? ` style="${styles}"` : "";
48678
+ const attrs = {
48679
+ class: classes,
48680
+ disabled: node.disabled,
48681
+ required: node.required
48682
+ };
48683
+ const options = node.options.map((opt) => {
48684
+ if (typeof opt === "string") {
48685
+ const selected2 = opt === node.value ? " selected" : "";
48686
+ return `<option value="${ctx.escapeHtml(opt)}"${selected2}>${ctx.escapeHtml(opt)}</option>`;
48687
+ }
48688
+ const selected = opt.value === node.value ? " selected" : "";
48689
+ return `<option value="${ctx.escapeHtml(opt.value)}"${selected}>${ctx.escapeHtml(opt.label)}</option>`;
48690
+ }).join("\n");
48691
+ const placeholder = node.placeholder ? `<option value="" disabled selected>${ctx.escapeHtml(node.placeholder)}</option>
48692
+ ` : "";
48693
+ const select = `<select${ctx.buildAttrsString(attrs)}${styleAttr}>
48694
+ ${placeholder}${options}
48695
+ </select>`;
48696
+ if (node.label) {
48697
+ return `<label class="${ctx.prefix}-input-label">${ctx.escapeHtml(node.label)}</label>
48698
+ ${select}`;
48699
+ }
48700
+ return select;
48701
+ }
48702
+ function renderCheckbox(node, ctx) {
48703
+ const styles = ctx.buildCommonStyles(node);
48704
+ const styleAttr = styles ? ` style="${styles}"` : "";
48705
+ const attrs = {
48706
+ type: "checkbox",
48707
+ checked: node.checked,
48708
+ disabled: node.disabled
48709
+ };
48710
+ const checkbox = `<input${ctx.buildAttrsString(attrs)} />`;
48711
+ if (node.label) {
48712
+ return `<label class="${ctx.prefix}-checkbox"${styleAttr}>${checkbox}<span>${ctx.escapeHtml(node.label)}</span></label>`;
48713
+ }
48714
+ return checkbox;
48715
+ }
48716
+ function renderRadio(node, ctx) {
48717
+ const styles = ctx.buildCommonStyles(node);
48718
+ const styleAttr = styles ? ` style="${styles}"` : "";
48719
+ const attrs = {
48720
+ type: "radio",
48721
+ name: node.name,
48722
+ checked: node.checked,
48723
+ disabled: node.disabled
48724
+ };
48725
+ const radio = `<input${ctx.buildAttrsString(attrs)} />`;
48726
+ if (node.label) {
48727
+ return `<label class="${ctx.prefix}-radio"${styleAttr}>${radio}<span>${ctx.escapeHtml(node.label)}</span></label>`;
48728
+ }
48729
+ return radio;
48730
+ }
48731
+ function renderSwitch(node, ctx) {
48732
+ const classes = ctx.buildClassString([
48733
+ `${ctx.prefix}-switch`,
48734
+ ...ctx.getCommonClasses(node)
48735
+ ]);
48736
+ const styles = ctx.buildCommonStyles(node);
48737
+ const styleAttr = styles ? ` style="${styles}"` : "";
48738
+ const attrs = {
48739
+ type: "checkbox",
48740
+ role: "switch",
48741
+ checked: node.checked,
48742
+ disabled: node.disabled
48743
+ };
48744
+ const switchEl = `<input${ctx.buildAttrsString(attrs)} />`;
48745
+ if (node.label) {
48746
+ return `<label class="${classes}"${styleAttr}>${switchEl} ${ctx.escapeHtml(node.label)}</label>`;
48747
+ }
48748
+ return `<label class="${classes}"${styleAttr}>${switchEl}</label>`;
48749
+ }
48750
+ function renderSlider(node, ctx) {
48751
+ const classes = ctx.buildClassString([
48752
+ `${ctx.prefix}-slider`,
48753
+ ...ctx.getCommonClasses(node)
48754
+ ]);
48755
+ const styles = ctx.buildCommonStyles(node);
48756
+ const styleAttr = styles ? ` style="${styles}"` : "";
48757
+ const attrs = {
48758
+ class: classes,
48759
+ type: "range",
48760
+ min: node.min?.toString(),
48761
+ max: node.max?.toString(),
48762
+ step: node.step?.toString(),
48763
+ value: node.value?.toString(),
48764
+ disabled: node.disabled
48765
+ };
48766
+ const slider = `<input${ctx.buildAttrsString(attrs)}${styleAttr} />`;
48767
+ if (node.label) {
48768
+ return `<label class="${ctx.prefix}-input-label">${ctx.escapeHtml(node.label)}</label>
48769
+ ${slider}`;
48770
+ }
48771
+ return slider;
48772
+ }
48773
+
48774
+ // src/renderer/html/renderers/display.ts
48775
+ function renderImage(node, ctx) {
48776
+ const classes = ctx.buildClassString([
48777
+ `${ctx.prefix}-image`,
48778
+ ...ctx.getCommonClasses(node)
48779
+ ]);
48780
+ const styles = ctx.buildCommonStyles(node);
48781
+ const styleAttr = styles ? ` style="${styles}"` : "";
48782
+ if (node.src) {
48783
+ const attrs = {
48784
+ class: classes,
48785
+ src: node.src,
48786
+ alt: node.alt || "Image"
48787
+ };
48788
+ const imgStyleAttr = styles ? `; ${styles}` : "";
48789
+ return `<img${ctx.buildAttrsString(attrs)}${imgStyleAttr ? ` style="${imgStyleAttr.slice(2)}"` : ""} />`;
48790
+ }
48791
+ const label = node.alt || "Image";
48792
+ const icon = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`;
48793
+ return `<div class="${classes}"${styleAttr} role="img" aria-label="${ctx.escapeHtml(label)}">${icon}<span>${ctx.escapeHtml(label)}</span></div>`;
48794
+ }
48795
+ function renderPlaceholder(node, ctx) {
48796
+ const classes = ctx.buildClassString([
48797
+ `${ctx.prefix}-placeholder`,
48798
+ node.children && node.children.length > 0 ? `${ctx.prefix}-placeholder-with-children` : void 0,
48799
+ ...ctx.getCommonClasses(node)
48800
+ ]);
48801
+ const styles = ctx.buildCommonStyles(node);
48802
+ const styleAttr = styles ? ` style="${styles}"` : "";
48803
+ const label = node.label ? ctx.escapeHtml(node.label) : "Placeholder";
48804
+ if (node.children && node.children.length > 0) {
48805
+ const childrenHtml = ctx.renderChildren(node.children);
48806
+ return `<div class="${classes}"${styleAttr}>
48807
+ <span class="${ctx.prefix}-placeholder-label">${label}</span>
48808
+ <div class="${ctx.prefix}-placeholder-overlay">${childrenHtml}</div>
48809
+ </div>`;
48810
+ }
48811
+ return `<div class="${classes}"${styleAttr}>${label}</div>`;
48812
+ }
48813
+ function renderAvatar(node, ctx) {
48814
+ const sizeResolved = resolveSizeValue(node.size, "avatar", ctx.prefix);
48815
+ const classes = ctx.buildClassString([
48816
+ `${ctx.prefix}-avatar`,
48817
+ sizeResolved.className,
48818
+ ...ctx.getCommonClasses(node)
48819
+ ]);
48820
+ const baseStyles = ctx.buildCommonStyles(node);
48821
+ const sizeStyle = sizeResolved.style || "";
48822
+ const combinedStyles = baseStyles && sizeStyle ? `${baseStyles}; ${sizeStyle}` : baseStyles || sizeStyle;
48823
+ const styleAttr = combinedStyles ? ` style="${combinedStyles}"` : "";
48824
+ const initials = node.name ? node.name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : "?";
48825
+ return `<div class="${classes}"${styleAttr} role="img" aria-label="${ctx.escapeHtml(node.name || "Avatar")}">${initials}</div>`;
48826
+ }
48827
+ function renderBadge(node, ctx) {
48828
+ if (node.icon) {
48829
+ const iconData = getIconData(node.icon);
48830
+ const classes2 = ctx.buildClassString([
48831
+ `${ctx.prefix}-badge-icon`,
48832
+ node.size ? `${ctx.prefix}-badge-icon-${node.size}` : void 0,
48833
+ node.variant ? `${ctx.prefix}-badge-icon-${node.variant}` : void 0,
48834
+ ...ctx.getCommonClasses(node)
48835
+ ]);
48836
+ const styles2 = ctx.buildCommonStyles(node);
48837
+ const styleAttr2 = styles2 ? ` style="${styles2}"` : "";
48838
+ if (iconData) {
48839
+ const svg = renderIconSvg(iconData, 24, 2, `${ctx.prefix}-icon`);
48840
+ return `<span class="${classes2}"${styleAttr2} aria-label="${ctx.escapeHtml(node.icon)}">${svg}</span>`;
48841
+ }
48842
+ return `<span class="${classes2}"${styleAttr2} aria-label="unknown icon">?</span>`;
48843
+ }
48844
+ const isDot = !node.content || node.content.trim() === "";
48845
+ const classes = ctx.buildClassString([
48846
+ `${ctx.prefix}-badge`,
48847
+ isDot ? `${ctx.prefix}-badge-dot` : void 0,
48848
+ node.variant ? `${ctx.prefix}-badge-${node.variant}` : void 0,
48849
+ node.pill ? `${ctx.prefix}-badge-pill` : void 0,
48850
+ ...ctx.getCommonClasses(node)
48851
+ ]);
48852
+ const styles = ctx.buildCommonStyles(node);
48853
+ const styleAttr = styles ? ` style="${styles}"` : "";
48854
+ return `<span class="${classes}"${styleAttr}>${ctx.escapeHtml(node.content)}</span>`;
48855
+ }
48856
+ function renderIcon(node, ctx) {
48857
+ const iconData = getIconData(node.name);
48858
+ const sizeResolved = resolveSizeValue(node.size, "icon", ctx.prefix);
48859
+ const wrapperClasses = ctx.buildClassString([
48860
+ `${ctx.prefix}-icon-wrapper`,
48861
+ node.muted ? `${ctx.prefix}-text-muted` : void 0,
48862
+ ...ctx.getCommonClasses(node)
48863
+ ]);
48864
+ const baseStyles = ctx.buildCommonStyles(node);
48865
+ if (iconData) {
48866
+ const iconClasses = buildClassString([
48867
+ `${ctx.prefix}-icon`,
48868
+ sizeResolved.className
48869
+ ]);
48870
+ const svgStyleAttr = sizeResolved.style ? ` style="${sizeResolved.style}"` : "";
48871
+ const svg = renderIconSvg(iconData, 24, 2, iconClasses, svgStyleAttr);
48872
+ const wrapperStyleAttr2 = baseStyles ? ` style="${baseStyles}"` : "";
48873
+ return `<span class="${wrapperClasses}"${wrapperStyleAttr2} aria-hidden="true">${svg}</span>`;
48874
+ }
48875
+ const size = sizeResolved.style?.match(/(\d+)px/)?.[1] || "24";
48876
+ const sizeNum = parseInt(size, 10);
48877
+ const placeholderSvg = `<svg class="${ctx.prefix}-icon ${sizeResolved.className || ""}" width="${sizeNum}" height="${sizeNum}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
48878
+ <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-dasharray="4 2" fill="none" opacity="0.5"/>
48879
+ <text x="12" y="16" text-anchor="middle" font-size="10" fill="currentColor" opacity="0.7">?</text>
48880
+ </svg>`;
48881
+ const wrapperStyleAttr = baseStyles ? ` style="${baseStyles}"` : "";
48882
+ return `<span class="${wrapperClasses}"${wrapperStyleAttr} aria-hidden="true" title="Unknown icon: ${ctx.escapeHtml(node.name)}">${placeholderSvg}</span>`;
48883
+ }
48884
+
48885
+ // src/renderer/html/renderers/overlay.ts
48886
+ function renderTooltip(node, ctx) {
48887
+ const classes = ctx.buildClassString([
48888
+ `${ctx.prefix}-tooltip-wrapper`,
48889
+ ...ctx.getCommonClasses(node)
48890
+ ]);
48891
+ const styles = ctx.buildCommonStyles(node);
48892
+ const styleAttr = styles ? ` style="${styles}"` : "";
48893
+ const position = node.position || "top";
48894
+ const children = ctx.renderChildren(node.children);
48895
+ return `<div class="${classes}"${styleAttr}>
48896
+ ${children}
48897
+ <div class="${ctx.prefix}-tooltip ${ctx.prefix}-tooltip-${position}" role="tooltip">${ctx.escapeHtml(node.content)}</div>
48898
+ </div>`;
48899
+ }
48900
+ function renderPopover(node, ctx) {
48901
+ const classes = ctx.buildClassString([
48902
+ `${ctx.prefix}-popover`,
48903
+ ...ctx.getCommonClasses(node)
48904
+ ]);
48905
+ const styles = ctx.buildCommonStyles(node);
48906
+ const styleAttr = styles ? ` style="${styles}"` : "";
48907
+ const title = node.title ? `<div class="${ctx.prefix}-popover-header">${ctx.escapeHtml(node.title)}</div>
48908
+ ` : "";
48909
+ const children = ctx.renderChildren(node.children);
48910
+ return `<div class="${classes}"${styleAttr}>
48911
+ ${title}<div class="${ctx.prefix}-popover-body">
48912
+ ${children}
48913
+ </div>
48914
+ </div>`;
48915
+ }
48916
+ function renderDropdown(node, ctx) {
48917
+ const classes = ctx.buildClassString([
48918
+ `${ctx.prefix}-dropdown`,
48919
+ ...ctx.getCommonClasses(node)
48920
+ ]);
48921
+ const styles = ctx.buildCommonStyles(node);
48922
+ const styleAttr = styles ? ` style="${styles}"` : "";
48923
+ const items = node.items.map((item) => {
48924
+ if ("type" in item && item.type === "divider") {
48925
+ return `<hr class="${ctx.prefix}-divider" />`;
48926
+ }
48927
+ const dropdownItem = item;
48928
+ const itemClasses = ctx.buildClassString([
48929
+ `${ctx.prefix}-dropdown-item`,
48930
+ dropdownItem.danger ? `${ctx.prefix}-dropdown-item-danger` : void 0,
48931
+ dropdownItem.disabled ? `${ctx.prefix}-dropdown-item-disabled` : void 0
48932
+ ]);
48933
+ return `<button class="${itemClasses}"${dropdownItem.disabled ? " disabled" : ""}>${ctx.escapeHtml(dropdownItem.label)}</button>`;
48934
+ }).join("\n");
48935
+ return `<div class="${classes}"${styleAttr}>
48936
+ ${items}
48937
+ </div>`;
48938
+ }
48939
+
48940
+ // src/renderer/html/renderers/navigation.ts
48941
+ function renderIconHtml(iconName, prefix) {
48942
+ return `<span class="${prefix}-icon" data-icon="${iconName}"></span>`;
48943
+ }
48944
+ function renderNavItem(item, ctx) {
48945
+ const linkClasses = ctx.buildClassString([
48946
+ `${ctx.prefix}-nav-link`,
48947
+ item.active ? `${ctx.prefix}-nav-link-active` : void 0,
48948
+ item.disabled ? `${ctx.prefix}-nav-link-disabled` : void 0
48949
+ ]);
48950
+ const iconHtml = item.icon ? renderIconHtml(item.icon, ctx.prefix) + " " : "";
48951
+ return `<a class="${linkClasses}" href="${item.href || "#"}">${iconHtml}${ctx.escapeHtml(item.label)}</a>`;
48952
+ }
48953
+ function renderNavChildren(children, ctx) {
48954
+ return children.map((child) => {
48955
+ if (child.type === "divider") {
48956
+ return `<hr class="${ctx.prefix}-nav-divider" />`;
48957
+ }
48958
+ if (child.type === "group") {
48959
+ const groupItems = child.items.map((item) => {
48960
+ if (item.type === "divider") {
48961
+ return `<hr class="${ctx.prefix}-nav-divider" />`;
48962
+ }
48963
+ return renderNavItem(item, ctx);
48964
+ }).join("\n");
48965
+ return `<div class="${ctx.prefix}-nav-group">
48966
+ <div class="${ctx.prefix}-nav-group-label">${ctx.escapeHtml(child.label)}</div>
48967
+ ${groupItems}
48968
+ </div>`;
48969
+ }
48970
+ if (child.type === "item") {
48971
+ return renderNavItem(child, ctx);
48972
+ }
48973
+ return "";
48974
+ }).join("\n");
48975
+ }
48976
+ function renderNav(node, ctx) {
48977
+ const classes = ctx.buildClassString([
48978
+ `${ctx.prefix}-nav`,
48979
+ node.vertical ? `${ctx.prefix}-nav-vertical` : void 0,
48980
+ ...ctx.getCommonClasses(node)
48981
+ ]);
48982
+ const styles = ctx.buildCommonStyles(node);
48983
+ const styleAttr = styles ? ` style="${styles}"` : "";
48984
+ if (node.children && node.children.length > 0) {
48985
+ const content = renderNavChildren(node.children, ctx);
48986
+ return `<nav class="${classes}"${styleAttr}>
48987
+ ${content}
48988
+ </nav>`;
48989
+ }
48990
+ const items = node.items.map((item) => {
48991
+ if (typeof item === "string") {
48992
+ return `<a class="${ctx.prefix}-nav-link" href="#">${ctx.escapeHtml(item)}</a>`;
48993
+ }
48994
+ const linkClasses = ctx.buildClassString([
48995
+ `${ctx.prefix}-nav-link`,
48996
+ item.active ? `${ctx.prefix}-nav-link-active` : void 0,
48997
+ item.disabled ? `${ctx.prefix}-nav-link-disabled` : void 0
48998
+ ]);
48999
+ const iconHtml = item.icon ? renderIconHtml(item.icon, ctx.prefix) + " " : "";
49000
+ return `<a class="${linkClasses}" href="${item.href || "#"}">${iconHtml}${ctx.escapeHtml(item.label)}</a>`;
49001
+ }).join("\n");
49002
+ return `<nav class="${classes}"${styleAttr}>
49003
+ ${items}
49004
+ </nav>`;
49005
+ }
49006
+ function renderTabs(node, ctx) {
49007
+ const classes = ctx.buildClassString([
49008
+ `${ctx.prefix}-tabs`,
49009
+ ...ctx.getCommonClasses(node)
49010
+ ]);
49011
+ const styles = ctx.buildCommonStyles(node);
49012
+ const styleAttr = styles ? ` style="${styles}"` : "";
49013
+ const tabList = node.items.map((label, idx) => {
49014
+ const isActive = idx === (node.active || 0);
49015
+ const tabClasses = `${ctx.prefix}-tab${isActive ? ` ${ctx.prefix}-tab-active` : ""}`;
49016
+ return `<button class="${tabClasses}" role="tab" aria-selected="${isActive}">${ctx.escapeHtml(label)}</button>`;
49017
+ }).join("\n");
49018
+ return `<div class="${classes}"${styleAttr}>
49019
+ <div class="${ctx.prefix}-tab-list" role="tablist">
49020
+ ${tabList}
49021
+ </div>
49022
+ </div>`;
49023
+ }
49024
+ function renderBreadcrumb(node, ctx) {
49025
+ const classes = ctx.buildClassString([
49026
+ `${ctx.prefix}-breadcrumb`,
49027
+ ...ctx.getCommonClasses(node)
49028
+ ]);
49029
+ const styles = ctx.buildCommonStyles(node);
49030
+ const styleAttr = styles ? ` style="${styles}"` : "";
49031
+ const items = node.items.map((item, idx) => {
49032
+ const isLast = idx === node.items.length - 1;
49033
+ if (typeof item === "string") {
49034
+ return isLast ? `<span class="${ctx.prefix}-breadcrumb-item" aria-current="page">${ctx.escapeHtml(item)}</span>` : `<a class="${ctx.prefix}-breadcrumb-item" href="#">${ctx.escapeHtml(item)}</a>`;
49035
+ }
49036
+ return isLast ? `<span class="${ctx.prefix}-breadcrumb-item" aria-current="page">${ctx.escapeHtml(item.label)}</span>` : `<a class="${ctx.prefix}-breadcrumb-item" href="${item.href || "#"}">${ctx.escapeHtml(item.label)}</a>`;
49037
+ }).join(" / ");
49038
+ return `<nav class="${classes}"${styleAttr} aria-label="Breadcrumb">${items}</nav>`;
49039
+ }
49040
+
49041
+ // src/renderer/html/semantic.ts
49042
+ function renderSemanticMarker(component, variant, prefix) {
49043
+ switch (component) {
49044
+ case "avatar":
49045
+ const avatarSize = variant || "sm";
49046
+ return `<span class="${prefix}-semantic-avatar ${prefix}-semantic-avatar-${avatarSize}" data-semantic="avatar" data-variant="${avatarSize}" aria-hidden="true"></span>`;
49047
+ case "dot":
49048
+ const dotVariant = variant || "default";
49049
+ return `<span class="${prefix}-semantic-dot ${prefix}-semantic-dot-${dotVariant}" data-semantic="dot" data-variant="${dotVariant}" aria-hidden="true"></span>`;
49050
+ case "icon":
49051
+ const iconName = variant || "default";
49052
+ return `<span class="${prefix}-semantic-icon" data-semantic="icon" data-variant="${iconName}" aria-hidden="true">[${iconName}]</span>`;
49053
+ default:
49054
+ return `<span class="${prefix}-semantic-unknown" data-semantic="${component}" data-variant="${variant || ""}">[${component}${variant ? ":" + variant : ""}]</span>`;
49055
+ }
49056
+ }
49057
+ function renderSemanticMarkerWithContent(component, variant, content, prefix, escapeHtml) {
49058
+ switch (component) {
49059
+ case "badge":
49060
+ const badgeVariant = variant || "default";
49061
+ const escapedContent = escapeHtml(content);
49062
+ return `<span class="${prefix}-semantic-badge ${prefix}-semantic-badge-${badgeVariant}" data-semantic="badge" data-variant="${badgeVariant}">${escapedContent}</span>`;
49063
+ default:
49064
+ return renderSemanticMarker(component, variant, prefix) + escapeHtml(content);
49065
+ }
49066
+ }
49067
+ function renderSemanticMarkers(text, prefix, escapeHtml) {
49068
+ const markerPattern = /\[([a-z]+)(?::([a-z0-9-]+))?\](\s*)/gi;
49069
+ let result = "";
49070
+ let lastIndex = 0;
49071
+ let match;
49072
+ while ((match = markerPattern.exec(text)) !== null) {
49073
+ if (match.index > lastIndex) {
49074
+ result += escapeHtml(text.substring(lastIndex, match.index));
49075
+ }
49076
+ const [fullMatch, component, variant] = match;
49077
+ const comp = component.toLowerCase();
49078
+ const varnt = variant?.toLowerCase();
49079
+ if (comp === "badge") {
49080
+ const afterMarker = text.substring(match.index + fullMatch.length);
49081
+ const contentMatch = afterMarker.match(/^([^\n\[]+?)(?=\n|\[|$)/);
49082
+ const badgeContent = contentMatch ? contentMatch[1].trim() : "";
49083
+ result += renderSemanticMarkerWithContent(comp, varnt, badgeContent, prefix, escapeHtml);
49084
+ lastIndex = match.index + fullMatch.length + (contentMatch ? contentMatch[0].length : 0);
49085
+ markerPattern.lastIndex = lastIndex;
49086
+ } else {
49087
+ result += renderSemanticMarker(comp, varnt, prefix);
49088
+ lastIndex = match.index + fullMatch.length;
49089
+ }
49090
+ }
49091
+ if (lastIndex < text.length) {
49092
+ result += escapeHtml(text.substring(lastIndex));
49093
+ }
49094
+ if (lastIndex === 0) {
49095
+ return escapeHtml(text);
49096
+ }
49097
+ return result;
49098
+ }
49099
+ function renderTableCellContent(content, prefix, escapeHtml) {
49100
+ const avatarMatch = content.match(/^\[avatar(?::([a-z0-9-]+))?\]\s*/i);
49101
+ if (avatarMatch) {
49102
+ const avatarVariant = avatarMatch[1]?.toLowerCase();
49103
+ const avatarHtml = renderSemanticMarker("avatar", avatarVariant, prefix);
49104
+ const restContent = content.slice(avatarMatch[0].length);
49105
+ const restHtml = renderSemanticMarkers(restContent, prefix, escapeHtml);
49106
+ const lines = restHtml.split("\n");
49107
+ const textHtml = lines.length > 1 ? lines.map((line) => `<span>${line}</span>`).join("") : restHtml;
49108
+ return `<div class="${prefix}-cell-avatar-layout">${avatarHtml}<div class="${prefix}-cell-avatar-text">${textHtml}</div></div>`;
49109
+ }
49110
+ const withMarkers = renderSemanticMarkers(content, prefix, escapeHtml);
49111
+ return withMarkers.replace(/\n/g, "<br>");
49112
+ }
49113
+
49114
+ // src/renderer/html/renderers/data.ts
49115
+ function renderTable(node, ctx) {
49116
+ const classes = ctx.buildClassString([
49117
+ `${ctx.prefix}-table`,
49118
+ node.striped ? `${ctx.prefix}-table-striped` : void 0,
49119
+ node.bordered ? `${ctx.prefix}-table-bordered` : void 0,
49120
+ node.hover ? `${ctx.prefix}-table-hover` : void 0,
49121
+ ...ctx.getCommonClasses(node)
49122
+ ]);
49123
+ const styles = ctx.buildCommonStyles(node);
49124
+ const styleAttr = styles ? ` style="${styles}"` : "";
49125
+ const thead = `<thead><tr>${node.columns.map((col) => `<th>${ctx.escapeHtml(col)}</th>`).join("")}</tr></thead>`;
49126
+ const tbody = `<tbody>${node.rows.map(
49127
+ (row) => `<tr>${row.map((cell) => {
49128
+ if (typeof cell === "string") {
49129
+ return `<td>${renderTableCellContent(cell, ctx.prefix, ctx.escapeHtml)}</td>`;
49130
+ }
49131
+ return `<td>${ctx.renderNode(cell)}</td>`;
49132
+ }).join("")}</tr>`
49133
+ ).join("")}</tbody>`;
49134
+ return `<table class="${classes}"${styleAttr}>
49135
+ ${thead}
49136
+ ${tbody}
49137
+ </table>`;
49138
+ }
49139
+ function renderList(node, ctx) {
49140
+ const tag = node.ordered ? "ol" : "ul";
49141
+ const classes = ctx.buildClassString([
49142
+ `${ctx.prefix}-list`,
49143
+ node.ordered ? `${ctx.prefix}-list-ordered` : void 0,
49144
+ node.none ? `${ctx.prefix}-list-none` : void 0,
49145
+ ...ctx.getCommonClasses(node)
49146
+ ]);
49147
+ const styles = ctx.buildCommonStyles(node);
49148
+ const styleAttr = styles ? ` style="${styles}"` : "";
49149
+ const items = node.items.map((item) => {
49150
+ if (typeof item === "string") {
49151
+ return `<li class="${ctx.prefix}-list-item">${ctx.escapeHtml(item)}</li>`;
49152
+ }
49153
+ return `<li class="${ctx.prefix}-list-item">${ctx.escapeHtml(item.content)}</li>`;
49154
+ }).join("\n");
49155
+ return `<${tag} class="${classes}"${styleAttr}>
49156
+ ${items}
49157
+ </${tag}>`;
49158
+ }
49159
+
49160
+ // src/renderer/html/renderers/divider.ts
49161
+ function renderDivider(node, ctx) {
49162
+ const styles = ctx.buildCommonStyles(node);
49163
+ const styleAttr = styles ? ` style="${styles}"` : "";
49164
+ return `<hr class="${ctx.prefix}-divider"${styleAttr} />`;
48166
49165
  }
48167
49166
 
48168
49167
  // src/renderer/html/index.ts
@@ -48204,8 +49203,97 @@ function resolveSizeValueToCss(value) {
48204
49203
  return void 0;
48205
49204
  }
48206
49205
  var HtmlRenderer = class extends BaseRenderer {
49206
+ /**
49207
+ * Node type to renderer method mapping
49208
+ */
49209
+ nodeRenderers;
48207
49210
  constructor(options = {}) {
48208
49211
  super(options);
49212
+ this.nodeRenderers = this.createNodeRenderers();
49213
+ }
49214
+ /**
49215
+ * Get render context for external renderer functions
49216
+ */
49217
+ getRenderContext() {
49218
+ return {
49219
+ prefix: this.prefix,
49220
+ escapeHtml: this.escapeHtml.bind(this),
49221
+ buildClassString: this.buildClassString.bind(this),
49222
+ buildAttrsString: this.buildAttrsString.bind(this),
49223
+ buildCommonStyles: this.buildCommonStyles.bind(this),
49224
+ getCommonClasses: this.getCommonClasses.bind(this),
49225
+ renderChildren: this.renderChildren.bind(this),
49226
+ renderNode: this.renderNode.bind(this)
49227
+ };
49228
+ }
49229
+ /**
49230
+ * Get grid render context (extends RenderContext with buildColStyles)
49231
+ */
49232
+ getGridRenderContext() {
49233
+ return {
49234
+ ...this.getRenderContext(),
49235
+ buildColStyles: this.buildColStyles.bind(this)
49236
+ };
49237
+ }
49238
+ /**
49239
+ * Create the node renderer mapping
49240
+ */
49241
+ createNodeRenderers() {
49242
+ return {
49243
+ // Layout nodes
49244
+ Page: (node) => this.renderPage(node),
49245
+ Header: (node) => this.renderHeader(node),
49246
+ Main: (node) => this.renderMain(node),
49247
+ Footer: (node) => this.renderFooter(node),
49248
+ Sidebar: (node) => this.renderSidebar(node),
49249
+ Section: (node) => this.renderSection(node),
49250
+ // Grid nodes
49251
+ Row: (node) => this.renderRow(node),
49252
+ Col: (node) => this.renderCol(node),
49253
+ // Container nodes
49254
+ Card: (node) => this.renderCard(node),
49255
+ Modal: (node) => this.renderModal(node),
49256
+ Drawer: (node) => this.renderDrawer(node),
49257
+ Accordion: (node) => this.renderAccordion(node),
49258
+ // Text nodes
49259
+ Text: (node) => this.renderText(node),
49260
+ Title: (node) => this.renderTitle(node),
49261
+ Link: (node) => this.renderLink(node),
49262
+ // Input nodes
49263
+ Input: (node) => this.renderInput(node),
49264
+ Textarea: (node) => this.renderTextarea(node),
49265
+ Select: (node) => this.renderSelect(node),
49266
+ Checkbox: (node) => this.renderCheckbox(node),
49267
+ Radio: (node) => this.renderRadio(node),
49268
+ Switch: (node) => this.renderSwitch(node),
49269
+ Slider: (node) => this.renderSlider(node),
49270
+ // Button
49271
+ Button: (node) => this.renderButton(node),
49272
+ // Display nodes
49273
+ Image: (node) => this.renderImage(node),
49274
+ Placeholder: (node) => this.renderPlaceholder(node),
49275
+ Avatar: (node) => this.renderAvatar(node),
49276
+ Badge: (node) => this.renderBadge(node),
49277
+ Icon: (node) => this.renderIcon(node),
49278
+ // Data nodes
49279
+ Table: (node) => this.renderTable(node),
49280
+ List: (node) => this.renderList(node),
49281
+ // Feedback nodes
49282
+ Alert: (node) => this.renderAlert(node),
49283
+ Toast: (node) => this.renderToast(node),
49284
+ Progress: (node) => this.renderProgress(node),
49285
+ Spinner: (node) => this.renderSpinner(node),
49286
+ // Overlay nodes
49287
+ Tooltip: (node) => this.renderTooltip(node),
49288
+ Popover: (node) => this.renderPopover(node),
49289
+ Dropdown: (node) => this.renderDropdown(node),
49290
+ // Navigation nodes
49291
+ Nav: (node) => this.renderNav(node),
49292
+ Tabs: (node) => this.renderTabs(node),
49293
+ Breadcrumb: (node) => this.renderBreadcrumb(node),
49294
+ // Other
49295
+ Divider: (node) => this.renderDivider(node)
49296
+ };
48209
49297
  }
48210
49298
  /**
48211
49299
  * Render a page node
@@ -48234,7 +49322,7 @@ var HtmlRenderer = class extends BaseRenderer {
48234
49322
  const title = node.title ? `<title>${this.escapeHtml(node.title)}</title>
48235
49323
  ` : "";
48236
49324
  const commonStyles = this.buildCommonStyles(node);
48237
- const viewportStyle = `width: ${viewport.width}px; height: ${viewport.height}px`;
49325
+ const viewportStyle = `position: relative; width: ${viewport.width}px; height: ${viewport.height}px; overflow: hidden`;
48238
49326
  const combinedStyle = commonStyles ? `${viewportStyle}; ${commonStyles}` : viewportStyle;
48239
49327
  const dataAttrs = `data-viewport-width="${viewport.width}" data-viewport-height="${viewport.height}" data-viewport-label="${viewport.label}"`;
48240
49328
  return `<div class="${classes}" style="${combinedStyle}" ${dataAttrs}>
@@ -48245,104 +49333,11 @@ ${title}${children}
48245
49333
  * Render any AST node
48246
49334
  */
48247
49335
  renderNode(node) {
48248
- switch (node.type) {
48249
- // Layout nodes
48250
- case "Page":
48251
- return this.renderPage(node);
48252
- case "Header":
48253
- return this.renderHeader(node);
48254
- case "Main":
48255
- return this.renderMain(node);
48256
- case "Footer":
48257
- return this.renderFooter(node);
48258
- case "Sidebar":
48259
- return this.renderSidebar(node);
48260
- case "Section":
48261
- return this.renderSection(node);
48262
- // Grid nodes
48263
- case "Row":
48264
- return this.renderRow(node);
48265
- case "Col":
48266
- return this.renderCol(node);
48267
- // Container nodes
48268
- case "Card":
48269
- return this.renderCard(node);
48270
- case "Modal":
48271
- return this.renderModal(node);
48272
- case "Drawer":
48273
- return this.renderDrawer(node);
48274
- case "Accordion":
48275
- return this.renderAccordion(node);
48276
- // Text nodes
48277
- case "Text":
48278
- return this.renderText(node);
48279
- case "Title":
48280
- return this.renderTitle(node);
48281
- case "Link":
48282
- return this.renderLink(node);
48283
- // Input nodes
48284
- case "Input":
48285
- return this.renderInput(node);
48286
- case "Textarea":
48287
- return this.renderTextarea(node);
48288
- case "Select":
48289
- return this.renderSelect(node);
48290
- case "Checkbox":
48291
- return this.renderCheckbox(node);
48292
- case "Radio":
48293
- return this.renderRadio(node);
48294
- case "Switch":
48295
- return this.renderSwitch(node);
48296
- case "Slider":
48297
- return this.renderSlider(node);
48298
- // Button
48299
- case "Button":
48300
- return this.renderButton(node);
48301
- // Display nodes
48302
- case "Image":
48303
- return this.renderImage(node);
48304
- case "Placeholder":
48305
- return this.renderPlaceholder(node);
48306
- case "Avatar":
48307
- return this.renderAvatar(node);
48308
- case "Badge":
48309
- return this.renderBadge(node);
48310
- case "Icon":
48311
- return this.renderIcon(node);
48312
- // Data nodes
48313
- case "Table":
48314
- return this.renderTable(node);
48315
- case "List":
48316
- return this.renderList(node);
48317
- // Feedback nodes
48318
- case "Alert":
48319
- return this.renderAlert(node);
48320
- case "Toast":
48321
- return this.renderToast(node);
48322
- case "Progress":
48323
- return this.renderProgress(node);
48324
- case "Spinner":
48325
- return this.renderSpinner(node);
48326
- // Overlay nodes
48327
- case "Tooltip":
48328
- return this.renderTooltip(node);
48329
- case "Popover":
48330
- return this.renderPopover(node);
48331
- case "Dropdown":
48332
- return this.renderDropdown(node);
48333
- // Navigation nodes
48334
- case "Nav":
48335
- return this.renderNav(node);
48336
- case "Tabs":
48337
- return this.renderTabs(node);
48338
- case "Breadcrumb":
48339
- return this.renderBreadcrumb(node);
48340
- // Other
48341
- case "Divider":
48342
- return this.renderDivider(node);
48343
- default:
48344
- return `<!-- Unknown node type: ${node.type} -->`;
49336
+ const renderer = this.nodeRenderers[node.type];
49337
+ if (renderer) {
49338
+ return renderer(node);
48345
49339
  }
49340
+ return `<!-- Unknown node type: ${node.type} -->`;
48346
49341
  }
48347
49342
  /**
48348
49343
  * Render children nodes
@@ -48385,106 +49380,36 @@ ${title}${children}
48385
49380
  // Layout Node Renderers
48386
49381
  // ===========================================
48387
49382
  renderHeader(node) {
48388
- const classes = this.buildClassString([
48389
- `${this.prefix}-header`,
48390
- node.border === false ? `${this.prefix}-no-border` : void 0,
48391
- ...this.getCommonClasses(node)
48392
- ]);
48393
- const styles = this.buildCommonStyles(node);
48394
- const styleAttr = styles ? ` style="${styles}"` : "";
48395
- const children = this.renderChildren(node.children);
48396
- return `<header class="${classes}"${styleAttr}>
48397
- ${children}
48398
- </header>`;
49383
+ return renderHeader(node, this.getRenderContext());
48399
49384
  }
48400
49385
  renderMain(node) {
48401
- const classes = this.buildClassString([
48402
- `${this.prefix}-main`,
48403
- ...this.getCommonClasses(node)
48404
- ]);
48405
- const styles = this.buildCommonStyles(node);
48406
- const styleAttr = styles ? ` style="${styles}"` : "";
48407
- const children = this.renderChildren(node.children);
48408
- return `<main class="${classes}"${styleAttr}>
48409
- ${children}
48410
- </main>`;
49386
+ return renderMain(node, this.getRenderContext());
48411
49387
  }
48412
49388
  renderFooter(node) {
48413
- const classes = this.buildClassString([
48414
- `${this.prefix}-footer`,
48415
- node.border === false ? `${this.prefix}-no-border` : void 0,
48416
- ...this.getCommonClasses(node)
48417
- ]);
48418
- const styles = this.buildCommonStyles(node);
48419
- const styleAttr = styles ? ` style="${styles}"` : "";
48420
- const children = this.renderChildren(node.children);
48421
- return `<footer class="${classes}"${styleAttr}>
48422
- ${children}
48423
- </footer>`;
49389
+ return renderFooter(node, this.getRenderContext());
48424
49390
  }
48425
49391
  renderSidebar(node) {
48426
- const classes = this.buildClassString([
48427
- `${this.prefix}-sidebar`,
48428
- node.position === "right" ? `${this.prefix}-sidebar-right` : void 0,
48429
- ...this.getCommonClasses(node)
48430
- ]);
48431
- const styles = this.buildCommonStyles(node);
48432
- const styleAttr = styles ? ` style="${styles}"` : "";
48433
- const children = this.renderChildren(node.children);
48434
- return `<aside class="${classes}"${styleAttr}>
48435
- ${children}
48436
- </aside>`;
49392
+ return renderSidebar(node, this.getRenderContext());
48437
49393
  }
48438
49394
  renderSection(node) {
48439
- const classes = this.buildClassString([
48440
- `${this.prefix}-section`,
48441
- ...this.getCommonClasses(node)
48442
- ]);
48443
- const styles = this.buildCommonStyles(node);
48444
- const styleAttr = styles ? ` style="${styles}"` : "";
48445
- const title = node.title ? `<h2 class="${this.prefix}-title">${this.escapeHtml(node.title)}</h2>
48446
- ` : "";
48447
- const children = this.renderChildren(node.children);
48448
- return `<section class="${classes}"${styleAttr}>
48449
- ${title}${children}
48450
- </section>`;
49395
+ return renderSection(node, this.getRenderContext());
48451
49396
  }
48452
49397
  // ===========================================
48453
49398
  // Grid Node Renderers
48454
49399
  // ===========================================
48455
49400
  renderRow(node) {
48456
- const classes = this.buildClassString([
48457
- `${this.prefix}-row`,
48458
- ...this.getCommonClasses(node)
48459
- ]);
48460
- const styles = this.buildCommonStyles(node);
48461
- const styleAttr = styles ? ` style="${styles}"` : "";
48462
- const children = this.renderChildren(node.children);
48463
- return `<div class="${classes}"${styleAttr}>
48464
- ${children}
48465
- </div>`;
49401
+ return renderRow(node, this.getRenderContext());
48466
49402
  }
48467
49403
  renderCol(node) {
48468
- const classes = this.buildClassString([
48469
- `${this.prefix}-col`,
48470
- node.span ? `${this.prefix}-col-${node.span}` : void 0,
48471
- // Responsive breakpoint classes
48472
- node.sm ? `${this.prefix}-col-sm-${node.sm}` : void 0,
48473
- node.md ? `${this.prefix}-col-md-${node.md}` : void 0,
48474
- node.lg ? `${this.prefix}-col-lg-${node.lg}` : void 0,
48475
- node.xl ? `${this.prefix}-col-xl-${node.xl}` : void 0,
48476
- ...this.getCommonClasses(node)
48477
- ]);
48478
- const styles = this.buildColStyles(node);
48479
- const styleAttr = styles ? ` style="${styles}"` : "";
48480
- const children = this.renderChildren(node.children);
48481
- return `<div class="${classes}"${styleAttr}>
48482
- ${children}
48483
- </div>`;
49404
+ return renderCol(node, this.getGridRenderContext());
48484
49405
  }
48485
49406
  /**
48486
49407
  * Build common inline styles for all values
48487
49408
  *
49409
+ * Position values (x, y) for absolute positioning:
49410
+ * - When x or y is specified, element gets position: absolute
49411
+ * - x → left, y → top
49412
+ *
48488
49413
  * Spacing values (p, m, gap) use token system:
48489
49414
  * - number: spacing token (e.g., p=4 → padding: 16px from token table)
48490
49415
  * - ValueWithUnit: direct CSS value (e.g., p=16px → padding: 16px)
@@ -48499,6 +49424,33 @@ ${children}
48499
49424
  */
48500
49425
  buildCommonStyles(props) {
48501
49426
  const styles = [];
49427
+ this.buildPositionStyles(props, styles);
49428
+ this.buildSizeStyles(props, styles);
49429
+ this.buildPaddingStyles(props, styles);
49430
+ this.buildMarginStyles(props, styles);
49431
+ this.buildGapStyles(props, styles);
49432
+ return styles.join("; ");
49433
+ }
49434
+ /**
49435
+ * Build position styles (absolute positioning)
49436
+ */
49437
+ buildPositionStyles(props, styles) {
49438
+ if (props.x !== void 0 || props.y !== void 0) {
49439
+ styles.push("position: absolute");
49440
+ if (props.x !== void 0) {
49441
+ const xValue = resolveSizeValueToCss(props.x);
49442
+ if (xValue) styles.push(`left: ${xValue}`);
49443
+ }
49444
+ if (props.y !== void 0) {
49445
+ const yValue = resolveSizeValueToCss(props.y);
49446
+ if (yValue) styles.push(`top: ${yValue}`);
49447
+ }
49448
+ }
49449
+ }
49450
+ /**
49451
+ * Build size styles (width, height, min/max)
49452
+ */
49453
+ buildSizeStyles(props, styles) {
48502
49454
  const wValue = resolveSizeValueToCss(props.w);
48503
49455
  if (wValue) {
48504
49456
  styles.push(`width: ${wValue}`);
@@ -48524,6 +49476,11 @@ ${children}
48524
49476
  if (maxHValue) {
48525
49477
  styles.push(`max-height: ${maxHValue}`);
48526
49478
  }
49479
+ }
49480
+ /**
49481
+ * Build padding styles (p, px, py, pt, pr, pb, pl)
49482
+ */
49483
+ buildPaddingStyles(props, styles) {
48527
49484
  const pValue = resolveSpacingValue(props.p);
48528
49485
  if (pValue) {
48529
49486
  styles.push(`padding: ${pValue}`);
@@ -48554,6 +49511,11 @@ ${children}
48554
49511
  if (plValue) {
48555
49512
  styles.push(`padding-left: ${plValue}`);
48556
49513
  }
49514
+ }
49515
+ /**
49516
+ * Build margin styles (m, mx, my, mt, mr, mb, ml)
49517
+ */
49518
+ buildMarginStyles(props, styles) {
48557
49519
  const mValue = resolveSpacingValue(props.m);
48558
49520
  if (mValue) {
48559
49521
  styles.push(`margin: ${mValue}`);
@@ -48584,11 +49546,15 @@ ${children}
48584
49546
  if (mlValue) {
48585
49547
  styles.push(`margin-left: ${mlValue}`);
48586
49548
  }
49549
+ }
49550
+ /**
49551
+ * Build gap styles
49552
+ */
49553
+ buildGapStyles(props, styles) {
48587
49554
  const gapValue = resolveSpacingValue(props.gap);
48588
49555
  if (gapValue) {
48589
49556
  styles.push(`gap: ${gapValue}`);
48590
49557
  }
48591
- return styles.join("; ");
48592
49558
  }
48593
49559
  /**
48594
49560
  * Build inline styles for Col node (extends common styles with order)
@@ -48608,1532 +49574,136 @@ ${children}
48608
49574
  // Container Node Renderers
48609
49575
  // ===========================================
48610
49576
  renderCard(node) {
48611
- const classes = this.buildClassString([
48612
- `${this.prefix}-card`,
48613
- node.shadow ? `${this.prefix}-card-shadow-${node.shadow}` : void 0,
48614
- ...this.getCommonClasses(node)
48615
- ]);
48616
- const styles = this.buildCommonStyles(node);
48617
- const styleAttr = styles ? ` style="${styles}"` : "";
48618
- const title = node.title ? `<h3 class="${this.prefix}-title">${this.escapeHtml(node.title)}</h3>
48619
- ` : "";
48620
- const children = this.renderChildren(node.children);
48621
- return `<div class="${classes}"${styleAttr}>
48622
- ${title}${children}
48623
- </div>`;
49577
+ return renderCard(node, this.getRenderContext());
48624
49578
  }
48625
49579
  renderModal(node) {
48626
- const classes = this.buildClassString([
48627
- `${this.prefix}-modal`,
48628
- ...this.getCommonClasses(node)
48629
- ]);
48630
- const styles = this.buildCommonStyles(node);
48631
- const styleAttr = styles ? ` style="${styles}"` : "";
48632
- const title = node.title ? `<h2 class="${this.prefix}-title">${this.escapeHtml(node.title)}</h2>
48633
- ` : "";
48634
- const children = this.renderChildren(node.children);
48635
- return `<div class="${this.prefix}-modal-backdrop">
48636
- <div class="${classes}"${styleAttr} role="dialog" aria-modal="true">
48637
- ${title}${children}
48638
- </div>
48639
- </div>`;
49580
+ return renderModal(node, this.getRenderContext());
48640
49581
  }
48641
49582
  renderDrawer(node) {
48642
- const position = node.position || "left";
48643
- const classes = this.buildClassString([
48644
- `${this.prefix}-drawer`,
48645
- `${this.prefix}-drawer-${position}`,
48646
- ...this.getCommonClasses(node)
48647
- ]);
48648
- const styles = this.buildCommonStyles(node);
48649
- const styleAttr = styles ? ` style="${styles}"` : "";
48650
- const title = node.title ? `<h2 class="${this.prefix}-title">${this.escapeHtml(node.title)}</h2>
48651
- ` : "";
48652
- const children = this.renderChildren(node.children);
48653
- return `<aside class="${classes}"${styleAttr}>
48654
- ${title}${children}
48655
- </aside>`;
49583
+ return renderDrawer(node, this.getRenderContext());
48656
49584
  }
48657
49585
  renderAccordion(node) {
48658
- const classes = this.buildClassString([
48659
- `${this.prefix}-accordion`,
48660
- ...this.getCommonClasses(node)
48661
- ]);
48662
- const styles = this.buildCommonStyles(node);
48663
- const styleAttr = styles ? ` style="${styles}"` : "";
48664
- const title = node.title ? `<button class="${this.prefix}-accordion-header">${this.escapeHtml(node.title)}</button>
48665
- ` : "";
48666
- const children = this.renderChildren(node.children);
48667
- return `<div class="${classes}"${styleAttr}>
48668
- ${title}<div class="${this.prefix}-accordion-content">
48669
- ${children}
48670
- </div>
48671
- </div>`;
49586
+ return renderAccordion(node, this.getRenderContext());
48672
49587
  }
48673
49588
  // ===========================================
48674
49589
  // Text Node Renderers
48675
49590
  // ===========================================
48676
49591
  renderText(node) {
48677
- const classes = this.buildClassString([
48678
- `${this.prefix}-text`,
48679
- node.size ? `${this.prefix}-text-${node.size}` : void 0,
48680
- node.weight ? `${this.prefix}-text-${node.weight}` : void 0,
48681
- node.align ? `${this.prefix}-text-${node.align}` : void 0,
48682
- node.muted ? `${this.prefix}-text-muted` : void 0,
48683
- ...this.getCommonClasses(node)
48684
- ]);
48685
- const styles = this.buildCommonStyles(node);
48686
- const styleAttr = styles ? ` style="${styles}"` : "";
48687
- return `<p class="${classes}"${styleAttr}>${this.escapeHtml(node.content)}</p>`;
49592
+ return renderText(node, this.getRenderContext());
48688
49593
  }
48689
49594
  renderTitle(node) {
48690
- const level = node.level || 1;
48691
- const tag = `h${level}`;
48692
- const classes = this.buildClassString([
48693
- `${this.prefix}-title`,
48694
- node.size ? `${this.prefix}-text-${node.size}` : void 0,
48695
- node.align ? `${this.prefix}-text-${node.align}` : void 0,
48696
- ...this.getCommonClasses(node)
48697
- ]);
48698
- const styles = this.buildCommonStyles(node);
48699
- const styleAttr = styles ? ` style="${styles}"` : "";
48700
- return `<${tag} class="${classes}"${styleAttr}>${this.escapeHtml(node.content)}</${tag}>`;
49595
+ return renderTitle(node, this.getRenderContext());
48701
49596
  }
48702
49597
  renderLink(node) {
48703
- const classes = this.buildClassString([
48704
- `${this.prefix}-link`,
48705
- ...this.getCommonClasses(node)
48706
- ]);
48707
- const styles = this.buildCommonStyles(node);
48708
- const styleAttr = styles ? ` style="${styles}"` : "";
48709
- const attrs = {
48710
- class: classes,
48711
- href: node.href || "#"
48712
- };
48713
- if (node.external) {
48714
- attrs.target = "_blank";
48715
- attrs.rel = "noopener noreferrer";
48716
- }
48717
- return `<a${this.buildAttrsString(attrs)}${styleAttr}>${this.escapeHtml(node.content)}</a>`;
49598
+ return renderLink(node, this.getRenderContext());
48718
49599
  }
48719
49600
  // ===========================================
48720
49601
  // Input Node Renderers
48721
49602
  // ===========================================
48722
49603
  renderInput(node) {
48723
- const inputClasses = this.buildClassString([
48724
- `${this.prefix}-input`,
48725
- node.icon ? `${this.prefix}-input-with-icon` : void 0,
48726
- ...this.getCommonClasses(node)
48727
- ]);
48728
- const styles = this.buildCommonStyles(node);
48729
- const styleAttr = styles ? ` style="${styles}"` : "";
48730
- const attrs = {
48731
- class: inputClasses,
48732
- type: node.inputType || "text",
48733
- placeholder: node.placeholder,
48734
- value: node.value,
48735
- disabled: node.disabled,
48736
- required: node.required,
48737
- readonly: node.readonly
48738
- };
48739
- const inputElement = `<input${this.buildAttrsString(attrs)} />`;
48740
- if (node.icon) {
48741
- const iconData = getIconData(node.icon);
48742
- let iconHtml;
48743
- if (iconData) {
48744
- iconHtml = renderIconSvg(iconData, 16, 2, `${this.prefix}-input-icon`);
48745
- } else {
48746
- iconHtml = `<span class="${this.prefix}-input-icon">[${this.escapeHtml(node.icon)}]</span>`;
48747
- }
48748
- const wrapperClasses = this.buildClassString([`${this.prefix}-input-wrapper`]);
48749
- const wrapper = `<div class="${wrapperClasses}"${styleAttr}>${iconHtml}${inputElement}</div>`;
48750
- if (node.label) {
48751
- return `<label class="${this.prefix}-input-label">${this.escapeHtml(node.label)}</label>
48752
- ${wrapper}`;
48753
- }
48754
- return wrapper;
48755
- }
48756
- const input = `<input${this.buildAttrsString(attrs)}${styleAttr} />`;
48757
- if (node.label) {
48758
- return `<label class="${this.prefix}-input-label">${this.escapeHtml(node.label)}</label>
48759
- ${input}`;
48760
- }
48761
- return input;
49604
+ return renderInput(node, this.getRenderContext());
48762
49605
  }
48763
49606
  renderTextarea(node) {
48764
- const classes = this.buildClassString([
48765
- `${this.prefix}-input`,
48766
- ...this.getCommonClasses(node)
48767
- ]);
48768
- const styles = this.buildCommonStyles(node);
48769
- const styleAttr = styles ? ` style="${styles}"` : "";
48770
- const attrs = {
48771
- class: classes,
48772
- placeholder: node.placeholder,
48773
- disabled: node.disabled,
48774
- required: node.required,
48775
- rows: node.rows?.toString()
48776
- };
48777
- const textarea = `<textarea${this.buildAttrsString(attrs)}${styleAttr}>${this.escapeHtml(node.value || "")}</textarea>`;
48778
- if (node.label) {
48779
- return `<label class="${this.prefix}-input-label">${this.escapeHtml(node.label)}</label>
48780
- ${textarea}`;
48781
- }
48782
- return textarea;
49607
+ return renderTextarea(node, this.getRenderContext());
48783
49608
  }
48784
49609
  renderSelect(node) {
48785
- const classes = this.buildClassString([
48786
- `${this.prefix}-input`,
48787
- ...this.getCommonClasses(node)
48788
- ]);
48789
- const styles = this.buildCommonStyles(node);
48790
- const styleAttr = styles ? ` style="${styles}"` : "";
48791
- const attrs = {
48792
- class: classes,
48793
- disabled: node.disabled,
48794
- required: node.required
48795
- };
48796
- const options = node.options.map((opt) => {
48797
- if (typeof opt === "string") {
48798
- const selected2 = opt === node.value ? " selected" : "";
48799
- return `<option value="${this.escapeHtml(opt)}"${selected2}>${this.escapeHtml(opt)}</option>`;
48800
- }
48801
- const selected = opt.value === node.value ? " selected" : "";
48802
- return `<option value="${this.escapeHtml(opt.value)}"${selected}>${this.escapeHtml(opt.label)}</option>`;
48803
- }).join("\n");
48804
- const placeholder = node.placeholder ? `<option value="" disabled selected>${this.escapeHtml(node.placeholder)}</option>
48805
- ` : "";
48806
- const select = `<select${this.buildAttrsString(attrs)}${styleAttr}>
48807
- ${placeholder}${options}
48808
- </select>`;
48809
- if (node.label) {
48810
- return `<label class="${this.prefix}-input-label">${this.escapeHtml(node.label)}</label>
48811
- ${select}`;
48812
- }
48813
- return select;
49610
+ return renderSelect(node, this.getRenderContext());
48814
49611
  }
48815
49612
  renderCheckbox(node) {
48816
- const styles = this.buildCommonStyles(node);
48817
- const styleAttr = styles ? ` style="${styles}"` : "";
48818
- const attrs = {
48819
- type: "checkbox",
48820
- checked: node.checked,
48821
- disabled: node.disabled
48822
- };
48823
- const checkbox = `<input${this.buildAttrsString(attrs)} />`;
48824
- if (node.label) {
48825
- return `<label class="${this.prefix}-checkbox"${styleAttr}>${checkbox}<span>${this.escapeHtml(node.label)}</span></label>`;
48826
- }
48827
- return checkbox;
49613
+ return renderCheckbox(node, this.getRenderContext());
48828
49614
  }
48829
49615
  renderRadio(node) {
48830
- const styles = this.buildCommonStyles(node);
48831
- const styleAttr = styles ? ` style="${styles}"` : "";
48832
- const attrs = {
48833
- type: "radio",
48834
- name: node.name,
48835
- checked: node.checked,
48836
- disabled: node.disabled
48837
- };
48838
- const radio = `<input${this.buildAttrsString(attrs)} />`;
48839
- if (node.label) {
48840
- return `<label class="${this.prefix}-radio"${styleAttr}>${radio}<span>${this.escapeHtml(node.label)}</span></label>`;
48841
- }
48842
- return radio;
49616
+ return renderRadio(node, this.getRenderContext());
48843
49617
  }
48844
49618
  renderSwitch(node) {
48845
- const classes = this.buildClassString([
48846
- `${this.prefix}-switch`,
48847
- ...this.getCommonClasses(node)
48848
- ]);
48849
- const styles = this.buildCommonStyles(node);
48850
- const styleAttr = styles ? ` style="${styles}"` : "";
48851
- const attrs = {
48852
- type: "checkbox",
48853
- role: "switch",
48854
- checked: node.checked,
48855
- disabled: node.disabled
48856
- };
48857
- const switchEl = `<input${this.buildAttrsString(attrs)} />`;
48858
- if (node.label) {
48859
- return `<label class="${classes}"${styleAttr}>${switchEl} ${this.escapeHtml(node.label)}</label>`;
48860
- }
48861
- return `<label class="${classes}"${styleAttr}>${switchEl}</label>`;
49619
+ return renderSwitch(node, this.getRenderContext());
48862
49620
  }
48863
49621
  renderSlider(node) {
48864
- const classes = this.buildClassString([
48865
- `${this.prefix}-slider`,
48866
- ...this.getCommonClasses(node)
48867
- ]);
48868
- const styles = this.buildCommonStyles(node);
48869
- const styleAttr = styles ? ` style="${styles}"` : "";
48870
- const attrs = {
48871
- class: classes,
48872
- type: "range",
48873
- min: node.min?.toString(),
48874
- max: node.max?.toString(),
48875
- step: node.step?.toString(),
48876
- value: node.value?.toString(),
48877
- disabled: node.disabled
48878
- };
48879
- const slider = `<input${this.buildAttrsString(attrs)}${styleAttr} />`;
48880
- if (node.label) {
48881
- return `<label class="${this.prefix}-input-label">${this.escapeHtml(node.label)}</label>
48882
- ${slider}`;
48883
- }
48884
- return slider;
49622
+ return renderSlider(node, this.getRenderContext());
48885
49623
  }
48886
49624
  // ===========================================
48887
49625
  // Button Renderer
48888
49626
  // ===========================================
48889
49627
  renderButton(node) {
48890
- const isIconOnly = node.icon && !node.content.trim();
48891
- const classes = this.buildClassString([
48892
- `${this.prefix}-button`,
48893
- node.primary ? `${this.prefix}-button-primary` : void 0,
48894
- node.secondary ? `${this.prefix}-button-secondary` : void 0,
48895
- node.outline ? `${this.prefix}-button-outline` : void 0,
48896
- node.ghost ? `${this.prefix}-button-ghost` : void 0,
48897
- node.danger ? `${this.prefix}-button-danger` : void 0,
48898
- node.size ? `${this.prefix}-button-${node.size}` : void 0,
48899
- node.disabled ? `${this.prefix}-button-disabled` : void 0,
48900
- node.loading ? `${this.prefix}-button-loading` : void 0,
48901
- isIconOnly ? `${this.prefix}-button-icon-only` : void 0,
48902
- ...this.getCommonClasses(node)
48903
- ]);
48904
- const styles = this.buildCommonStyles(node);
48905
- const styleAttr = styles ? ` style="${styles}"` : "";
48906
- const attrs = {
48907
- class: classes,
48908
- disabled: node.disabled
48909
- };
48910
- let icon = "";
48911
- if (node.icon) {
48912
- const iconData = getIconData(node.icon);
48913
- if (iconData) {
48914
- icon = renderIconSvg(iconData, 16, 2, `${this.prefix}-icon`);
48915
- } else {
48916
- icon = `<span class="${this.prefix}-icon">[${this.escapeHtml(node.icon)}]</span>`;
48917
- }
48918
- }
48919
- const loading = node.loading ? `<span class="${this.prefix}-spinner ${this.prefix}-spinner-sm"></span>` : "";
48920
- const content = this.escapeHtml(node.content);
48921
- return `<button${this.buildAttrsString(attrs)}${styleAttr}>${loading}${icon}${content}</button>`;
49628
+ return renderButton(node, this.getRenderContext());
48922
49629
  }
48923
49630
  // ===========================================
48924
49631
  // Display Node Renderers
48925
49632
  // ===========================================
48926
49633
  renderImage(node) {
48927
- const classes = this.buildClassString([
48928
- `${this.prefix}-image`,
48929
- ...this.getCommonClasses(node)
48930
- ]);
48931
- const styles = this.buildCommonStyles(node);
48932
- const styleAttr = styles ? ` style="${styles}"` : "";
48933
- if (node.src) {
48934
- const attrs = {
48935
- class: classes,
48936
- src: node.src,
48937
- alt: node.alt || "Image"
48938
- };
48939
- const imgStyleAttr = styles ? `; ${styles}` : "";
48940
- return `<img${this.buildAttrsString(attrs)}${imgStyleAttr ? ` style="${imgStyleAttr.slice(2)}"` : ""} />`;
48941
- }
48942
- const label = node.alt || "Image";
48943
- const icon = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`;
48944
- return `<div class="${classes}"${styleAttr} role="img" aria-label="${this.escapeHtml(label)}">${icon}<span>${this.escapeHtml(label)}</span></div>`;
49634
+ return renderImage(node, this.getRenderContext());
48945
49635
  }
48946
49636
  renderPlaceholder(node) {
48947
- const classes = this.buildClassString([
48948
- `${this.prefix}-placeholder`,
48949
- ...this.getCommonClasses(node)
48950
- ]);
48951
- const styles = this.buildCommonStyles(node);
48952
- const styleAttr = styles ? ` style="${styles}"` : "";
48953
- const label = node.label ? this.escapeHtml(node.label) : "Placeholder";
48954
- return `<div class="${classes}"${styleAttr}>${label}</div>`;
49637
+ return renderPlaceholder(node, this.getRenderContext());
48955
49638
  }
48956
49639
  renderAvatar(node) {
48957
- const sizeResolved = resolveSizeValue(node.size, "avatar", this.prefix);
48958
- const classes = this.buildClassString([
48959
- `${this.prefix}-avatar`,
48960
- sizeResolved.className,
48961
- ...this.getCommonClasses(node)
48962
- ]);
48963
- const baseStyles = this.buildCommonStyles(node);
48964
- const sizeStyle = sizeResolved.style || "";
48965
- const combinedStyles = baseStyles && sizeStyle ? `${baseStyles}; ${sizeStyle}` : baseStyles || sizeStyle;
48966
- const styleAttr = combinedStyles ? ` style="${combinedStyles}"` : "";
48967
- const initials = node.name ? node.name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : "?";
48968
- return `<div class="${classes}"${styleAttr} role="img" aria-label="${this.escapeHtml(node.name || "Avatar")}">${initials}</div>`;
49640
+ return renderAvatar(node, this.getRenderContext());
48969
49641
  }
48970
49642
  renderBadge(node) {
48971
- if (node.icon) {
48972
- const iconData = getIconData(node.icon);
48973
- const classes2 = this.buildClassString([
48974
- `${this.prefix}-badge-icon`,
48975
- node.size ? `${this.prefix}-badge-icon-${node.size}` : void 0,
48976
- node.variant ? `${this.prefix}-badge-icon-${node.variant}` : void 0,
48977
- ...this.getCommonClasses(node)
48978
- ]);
48979
- const styles2 = this.buildCommonStyles(node);
48980
- const styleAttr2 = styles2 ? ` style="${styles2}"` : "";
48981
- if (iconData) {
48982
- const svg = renderIconSvg(iconData, 24, 2, `${this.prefix}-icon`);
48983
- return `<span class="${classes2}"${styleAttr2} aria-label="${this.escapeHtml(node.icon)}">${svg}</span>`;
48984
- }
48985
- return `<span class="${classes2}"${styleAttr2} aria-label="unknown icon">?</span>`;
48986
- }
48987
- const isDot = !node.content || node.content.trim() === "";
48988
- const classes = this.buildClassString([
48989
- `${this.prefix}-badge`,
48990
- isDot ? `${this.prefix}-badge-dot` : void 0,
48991
- node.variant ? `${this.prefix}-badge-${node.variant}` : void 0,
48992
- node.pill ? `${this.prefix}-badge-pill` : void 0,
48993
- ...this.getCommonClasses(node)
48994
- ]);
48995
- const styles = this.buildCommonStyles(node);
48996
- const styleAttr = styles ? ` style="${styles}"` : "";
48997
- return `<span class="${classes}"${styleAttr}>${this.escapeHtml(node.content)}</span>`;
49643
+ return renderBadge(node, this.getRenderContext());
48998
49644
  }
48999
49645
  renderIcon(node) {
49000
- const iconData = getIconData(node.name);
49001
- const sizeResolved = resolveSizeValue(node.size, "icon", this.prefix);
49002
- const wrapperClasses = this.buildClassString([
49003
- `${this.prefix}-icon-wrapper`,
49004
- node.muted ? `${this.prefix}-text-muted` : void 0,
49005
- ...this.getCommonClasses(node)
49006
- ]);
49007
- const baseStyles = this.buildCommonStyles(node);
49008
- if (iconData) {
49009
- const iconClasses = buildClassString([
49010
- `${this.prefix}-icon`,
49011
- sizeResolved.className
49012
- ]);
49013
- const svgStyleAttr = sizeResolved.style ? ` style="${sizeResolved.style}"` : "";
49014
- const svg = renderIconSvg(iconData, 24, 2, iconClasses, svgStyleAttr);
49015
- const wrapperStyleAttr2 = baseStyles ? ` style="${baseStyles}"` : "";
49016
- return `<span class="${wrapperClasses}"${wrapperStyleAttr2} aria-hidden="true">${svg}</span>`;
49017
- }
49018
- const size = sizeResolved.style?.match(/(\d+)px/)?.[1] || "24";
49019
- const sizeNum = parseInt(size, 10);
49020
- const placeholderSvg = `<svg class="${this.prefix}-icon ${sizeResolved.className || ""}" width="${sizeNum}" height="${sizeNum}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
49021
- <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" stroke-dasharray="4 2" fill="none" opacity="0.5"/>
49022
- <text x="12" y="16" text-anchor="middle" font-size="10" fill="currentColor" opacity="0.7">?</text>
49023
- </svg>`;
49024
- const wrapperStyleAttr = baseStyles ? ` style="${baseStyles}"` : "";
49025
- return `<span class="${wrapperClasses}"${wrapperStyleAttr} aria-hidden="true" title="Unknown icon: ${this.escapeHtml(node.name)}">${placeholderSvg}</span>`;
49646
+ return renderIcon(node, this.getRenderContext());
49026
49647
  }
49027
49648
  // ===========================================
49028
49649
  // Data Node Renderers
49029
49650
  // ===========================================
49030
49651
  renderTable(node) {
49031
- const classes = this.buildClassString([
49032
- `${this.prefix}-table`,
49033
- node.striped ? `${this.prefix}-table-striped` : void 0,
49034
- node.bordered ? `${this.prefix}-table-bordered` : void 0,
49035
- node.hover ? `${this.prefix}-table-hover` : void 0,
49036
- ...this.getCommonClasses(node)
49037
- ]);
49038
- const styles = this.buildCommonStyles(node);
49039
- const styleAttr = styles ? ` style="${styles}"` : "";
49040
- const thead = `<thead><tr>${node.columns.map((col) => `<th>${this.escapeHtml(col)}</th>`).join("")}</tr></thead>`;
49041
- const tbody = `<tbody>${node.rows.map(
49042
- (row) => `<tr>${row.map((cell) => {
49043
- if (typeof cell === "string") {
49044
- return `<td>${this.renderTableCellContent(cell)}</td>`;
49045
- }
49046
- return `<td>${this.renderNode(cell)}</td>`;
49047
- }).join("")}</tr>`
49048
- ).join("")}</tbody>`;
49049
- return `<table class="${classes}"${styleAttr}>
49050
- ${thead}
49051
- ${tbody}
49052
- </table>`;
49652
+ return renderTable(node, this.getRenderContext());
49053
49653
  }
49054
49654
  renderList(node) {
49055
- const tag = node.ordered ? "ol" : "ul";
49056
- const classes = this.buildClassString([
49057
- `${this.prefix}-list`,
49058
- node.ordered ? `${this.prefix}-list-ordered` : void 0,
49059
- node.none ? `${this.prefix}-list-none` : void 0,
49060
- ...this.getCommonClasses(node)
49061
- ]);
49062
- const styles = this.buildCommonStyles(node);
49063
- const styleAttr = styles ? ` style="${styles}"` : "";
49064
- const items = node.items.map((item) => {
49065
- if (typeof item === "string") {
49066
- return `<li class="${this.prefix}-list-item">${this.escapeHtml(item)}</li>`;
49067
- }
49068
- return `<li class="${this.prefix}-list-item">${this.escapeHtml(item.content)}</li>`;
49069
- }).join("\n");
49070
- return `<${tag} class="${classes}"${styleAttr}>
49071
- ${items}
49072
- </${tag}>`;
49655
+ return renderList(node, this.getRenderContext());
49073
49656
  }
49074
49657
  // ===========================================
49075
49658
  // Feedback Node Renderers
49076
49659
  // ===========================================
49077
49660
  renderAlert(node) {
49078
- const classes = this.buildClassString([
49079
- `${this.prefix}-alert`,
49080
- node.variant ? `${this.prefix}-alert-${node.variant}` : void 0,
49081
- ...this.getCommonClasses(node)
49082
- ]);
49083
- const styles = this.buildCommonStyles(node);
49084
- const styleAttr = styles ? ` style="${styles}"` : "";
49085
- const dismissBtn = node.dismissible ? ` <button class="${this.prefix}-alert-close" aria-label="Close">&times;</button>` : "";
49086
- return `<div class="${classes}"${styleAttr} role="alert">${this.escapeHtml(node.content)}${dismissBtn}</div>`;
49661
+ return renderAlert(node, this.getRenderContext());
49087
49662
  }
49088
49663
  renderToast(node) {
49089
- const classes = this.buildClassString([
49090
- `${this.prefix}-toast`,
49091
- node.position ? `${this.prefix}-toast-${node.position}` : void 0,
49092
- node.variant ? `${this.prefix}-toast-${node.variant}` : void 0,
49093
- ...this.getCommonClasses(node)
49094
- ]);
49095
- const styles = this.buildCommonStyles(node);
49096
- const styleAttr = styles ? ` style="${styles}"` : "";
49097
- return `<div class="${classes}"${styleAttr} role="status">${this.escapeHtml(node.content)}</div>`;
49664
+ return renderToast(node, this.getRenderContext());
49098
49665
  }
49099
49666
  renderProgress(node) {
49100
- const classes = this.buildClassString([
49101
- `${this.prefix}-progress`,
49102
- ...this.getCommonClasses(node)
49103
- ]);
49104
- const styles = this.buildCommonStyles(node);
49105
- const styleAttr = styles ? ` style="${styles}"` : "";
49106
- const value = node.value || 0;
49107
- const max = node.max || 100;
49108
- const percentage = Math.round(value / max * 100);
49109
- const label = node.label ? `<span class="${this.prefix}-progress-label">${this.escapeHtml(node.label)}</span>` : "";
49110
- if (node.indeterminate) {
49111
- return `<div class="${classes} ${this.prefix}-progress-indeterminate"${styleAttr} role="progressbar">${label}</div>`;
49112
- }
49113
- return `<div class="${classes}"${styleAttr} role="progressbar" aria-valuenow="${value}" aria-valuemin="0" aria-valuemax="${max}">
49114
- ${label}
49115
- <div class="${this.prefix}-progress-bar" style="width: ${percentage}%"></div>
49116
- </div>`;
49667
+ return renderProgress(node, this.getRenderContext());
49117
49668
  }
49118
49669
  renderSpinner(node) {
49119
- const sizeResolved = resolveSizeValue(node.size, "spinner", this.prefix);
49120
- const classes = this.buildClassString([
49121
- `${this.prefix}-spinner`,
49122
- sizeResolved.className,
49123
- ...this.getCommonClasses(node)
49124
- ]);
49125
- const baseStyles = this.buildCommonStyles(node);
49126
- const sizeStyle = sizeResolved.style || "";
49127
- const combinedStyles = baseStyles && sizeStyle ? `${baseStyles}; ${sizeStyle}` : baseStyles || sizeStyle;
49128
- const styleAttr = combinedStyles ? ` style="${combinedStyles}"` : "";
49129
- const label = node.label || "Loading...";
49130
- return `<span class="${classes}"${styleAttr} role="status" aria-label="${this.escapeHtml(label)}"></span>`;
49670
+ return renderSpinner(node, this.getRenderContext());
49131
49671
  }
49132
49672
  // ===========================================
49133
49673
  // Overlay Node Renderers
49134
49674
  // ===========================================
49135
49675
  renderTooltip(node) {
49136
- const classes = this.buildClassString([
49137
- `${this.prefix}-tooltip-wrapper`,
49138
- ...this.getCommonClasses(node)
49139
- ]);
49140
- const styles = this.buildCommonStyles(node);
49141
- const styleAttr = styles ? ` style="${styles}"` : "";
49142
- const position = node.position || "top";
49143
- const children = this.renderChildren(node.children);
49144
- return `<div class="${classes}"${styleAttr}>
49145
- ${children}
49146
- <div class="${this.prefix}-tooltip ${this.prefix}-tooltip-${position}" role="tooltip">${this.escapeHtml(node.content)}</div>
49147
- </div>`;
49676
+ return renderTooltip(node, this.getRenderContext());
49148
49677
  }
49149
49678
  renderPopover(node) {
49150
- const classes = this.buildClassString([
49151
- `${this.prefix}-popover`,
49152
- ...this.getCommonClasses(node)
49153
- ]);
49154
- const styles = this.buildCommonStyles(node);
49155
- const styleAttr = styles ? ` style="${styles}"` : "";
49156
- const title = node.title ? `<div class="${this.prefix}-popover-header">${this.escapeHtml(node.title)}</div>
49157
- ` : "";
49158
- const children = this.renderChildren(node.children);
49159
- return `<div class="${classes}"${styleAttr}>
49160
- ${title}<div class="${this.prefix}-popover-body">
49161
- ${children}
49162
- </div>
49163
- </div>`;
49679
+ return renderPopover(node, this.getRenderContext());
49164
49680
  }
49165
49681
  renderDropdown(node) {
49166
- const classes = this.buildClassString([
49167
- `${this.prefix}-dropdown`,
49168
- ...this.getCommonClasses(node)
49169
- ]);
49170
- const styles = this.buildCommonStyles(node);
49171
- const styleAttr = styles ? ` style="${styles}"` : "";
49172
- const items = node.items.map((item) => {
49173
- if ("type" in item && item.type === "divider") {
49174
- return `<hr class="${this.prefix}-divider" />`;
49175
- }
49176
- const dropdownItem = item;
49177
- const itemClasses = this.buildClassString([
49178
- `${this.prefix}-dropdown-item`,
49179
- dropdownItem.danger ? `${this.prefix}-dropdown-item-danger` : void 0,
49180
- dropdownItem.disabled ? `${this.prefix}-dropdown-item-disabled` : void 0
49181
- ]);
49182
- return `<button class="${itemClasses}"${dropdownItem.disabled ? " disabled" : ""}>${this.escapeHtml(dropdownItem.label)}</button>`;
49183
- }).join("\n");
49184
- return `<div class="${classes}"${styleAttr}>
49185
- ${items}
49186
- </div>`;
49682
+ return renderDropdown(node, this.getRenderContext());
49187
49683
  }
49188
49684
  // ===========================================
49189
49685
  // Navigation Node Renderers
49190
49686
  // ===========================================
49191
49687
  renderNav(node) {
49192
- const classes = this.buildClassString([
49193
- `${this.prefix}-nav`,
49194
- node.vertical ? `${this.prefix}-nav-vertical` : void 0,
49195
- ...this.getCommonClasses(node)
49196
- ]);
49197
- const styles = this.buildCommonStyles(node);
49198
- const styleAttr = styles ? ` style="${styles}"` : "";
49199
- const items = node.items.map((item) => {
49200
- if (typeof item === "string") {
49201
- return `<a class="${this.prefix}-nav-link" href="#">${this.escapeHtml(item)}</a>`;
49202
- }
49203
- const linkClasses = this.buildClassString([
49204
- `${this.prefix}-nav-link`,
49205
- item.active ? `${this.prefix}-nav-link-active` : void 0,
49206
- item.disabled ? `${this.prefix}-nav-link-disabled` : void 0
49207
- ]);
49208
- return `<a class="${linkClasses}" href="${item.href || "#"}">${this.escapeHtml(item.label)}</a>`;
49209
- }).join("\n");
49210
- return `<nav class="${classes}"${styleAttr}>
49211
- ${items}
49212
- </nav>`;
49688
+ return renderNav(node, this.getRenderContext());
49213
49689
  }
49214
49690
  renderTabs(node) {
49215
- const classes = this.buildClassString([
49216
- `${this.prefix}-tabs`,
49217
- ...this.getCommonClasses(node)
49218
- ]);
49219
- const styles = this.buildCommonStyles(node);
49220
- const styleAttr = styles ? ` style="${styles}"` : "";
49221
- const tabList = node.items.map((label, idx) => {
49222
- const isActive = idx === (node.active || 0);
49223
- const tabClasses = `${this.prefix}-tab${isActive ? ` ${this.prefix}-tab-active` : ""}`;
49224
- return `<button class="${tabClasses}" role="tab" aria-selected="${isActive}">${this.escapeHtml(label)}</button>`;
49225
- }).join("\n");
49226
- return `<div class="${classes}"${styleAttr}>
49227
- <div class="${this.prefix}-tab-list" role="tablist">
49228
- ${tabList}
49229
- </div>
49230
- </div>`;
49691
+ return renderTabs(node, this.getRenderContext());
49231
49692
  }
49232
49693
  renderBreadcrumb(node) {
49233
- const classes = this.buildClassString([
49234
- `${this.prefix}-breadcrumb`,
49235
- ...this.getCommonClasses(node)
49236
- ]);
49237
- const styles = this.buildCommonStyles(node);
49238
- const styleAttr = styles ? ` style="${styles}"` : "";
49239
- const items = node.items.map((item, idx) => {
49240
- const isLast = idx === node.items.length - 1;
49241
- if (typeof item === "string") {
49242
- return isLast ? `<span class="${this.prefix}-breadcrumb-item" aria-current="page">${this.escapeHtml(item)}</span>` : `<a class="${this.prefix}-breadcrumb-item" href="#">${this.escapeHtml(item)}</a>`;
49243
- }
49244
- return isLast ? `<span class="${this.prefix}-breadcrumb-item" aria-current="page">${this.escapeHtml(item.label)}</span>` : `<a class="${this.prefix}-breadcrumb-item" href="${item.href || "#"}">${this.escapeHtml(item.label)}</a>`;
49245
- }).join(" / ");
49246
- return `<nav class="${classes}"${styleAttr} aria-label="Breadcrumb">${items}</nav>`;
49694
+ return renderBreadcrumb(node, this.getRenderContext());
49247
49695
  }
49248
49696
  // ===========================================
49249
49697
  // Divider Renderer
49250
49698
  // ===========================================
49251
49699
  renderDivider(node) {
49252
- const styles = this.buildCommonStyles(node);
49253
- const styleAttr = styles ? ` style="${styles}"` : "";
49254
- return `<hr class="${this.prefix}-divider"${styleAttr} />`;
49255
- }
49256
- // ===========================================
49257
- // Semantic Marker Rendering
49258
- // ===========================================
49259
- /**
49260
- * Parse and render semantic markers in text content
49261
- *
49262
- * Semantic markers use the syntax [component:variant] to indicate
49263
- * what a visual element represents. This helps LLMs understand
49264
- * the meaning of placeholder content.
49265
- *
49266
- * Supported markers:
49267
- * - [avatar] or [avatar:size] - User avatar (renders as circle placeholder)
49268
- * - [badge:variant] TEXT - Status badge (TEXT is displayed inside the badge)
49269
- * - [dot:variant] - Status dot (renders as small circle before text)
49270
- * - [icon:name] - Icon placeholder
49271
- *
49272
- * Examples:
49273
- * - "[avatar] John Doe" → renders avatar circle + "John Doe"
49274
- * - "[badge:primary] PRO" → renders badge containing "PRO"
49275
- * - "[dot:success] Active" → renders green dot + "Active"
49276
- */
49277
- renderSemanticMarkers(text) {
49278
- const markerPattern = /\[([a-z]+)(?::([a-z0-9-]+))?\](\s*)/gi;
49279
- let result = "";
49280
- let lastIndex = 0;
49281
- let match;
49282
- while ((match = markerPattern.exec(text)) !== null) {
49283
- if (match.index > lastIndex) {
49284
- result += this.escapeHtml(text.substring(lastIndex, match.index));
49285
- }
49286
- const [fullMatch, component, variant] = match;
49287
- const comp = component.toLowerCase();
49288
- const varnt = variant?.toLowerCase();
49289
- if (comp === "badge") {
49290
- const afterMarker = text.substring(match.index + fullMatch.length);
49291
- const contentMatch = afterMarker.match(/^([^\n\[]+?)(?=\n|\[|$)/);
49292
- const badgeContent = contentMatch ? contentMatch[1].trim() : "";
49293
- result += this.renderSemanticMarkerWithContent(comp, varnt, badgeContent);
49294
- lastIndex = match.index + fullMatch.length + (contentMatch ? contentMatch[0].length : 0);
49295
- markerPattern.lastIndex = lastIndex;
49296
- } else {
49297
- result += this.renderSemanticMarker(comp, varnt);
49298
- lastIndex = match.index + fullMatch.length;
49299
- }
49300
- }
49301
- if (lastIndex < text.length) {
49302
- result += this.escapeHtml(text.substring(lastIndex));
49303
- }
49304
- if (lastIndex === 0) {
49305
- return this.escapeHtml(text);
49306
- }
49307
- return result;
49308
- }
49309
- /**
49310
- * Render a single semantic marker to HTML (without content)
49311
- */
49312
- renderSemanticMarker(component, variant) {
49313
- const prefix = this.prefix;
49314
- switch (component) {
49315
- case "avatar":
49316
- const avatarSize = variant || "sm";
49317
- return `<span class="${prefix}-semantic-avatar ${prefix}-semantic-avatar-${avatarSize}" data-semantic="avatar" data-variant="${avatarSize}" aria-hidden="true"></span>`;
49318
- case "dot":
49319
- const dotVariant = variant || "default";
49320
- return `<span class="${prefix}-semantic-dot ${prefix}-semantic-dot-${dotVariant}" data-semantic="dot" data-variant="${dotVariant}" aria-hidden="true"></span>`;
49321
- case "icon":
49322
- const iconName = variant || "default";
49323
- return `<span class="${prefix}-semantic-icon" data-semantic="icon" data-variant="${iconName}" aria-hidden="true">[${iconName}]</span>`;
49324
- default:
49325
- return `<span class="${prefix}-semantic-unknown" data-semantic="${component}" data-variant="${variant || ""}">[${component}${variant ? ":" + variant : ""}]</span>`;
49326
- }
49327
- }
49328
- /**
49329
- * Render a semantic marker with text content (for badge)
49330
- */
49331
- renderSemanticMarkerWithContent(component, variant, content) {
49332
- const prefix = this.prefix;
49333
- switch (component) {
49334
- case "badge":
49335
- const badgeVariant = variant || "default";
49336
- const escapedContent = this.escapeHtml(content);
49337
- return `<span class="${prefix}-semantic-badge ${prefix}-semantic-badge-${badgeVariant}" data-semantic="badge" data-variant="${badgeVariant}">${escapedContent}</span>`;
49338
- default:
49339
- return this.renderSemanticMarker(component, variant) + this.escapeHtml(content);
49340
- }
49341
- }
49342
- /**
49343
- * Process table cell content with semantic markers and newlines
49344
- *
49345
- * Special handling for avatar + text layout:
49346
- * When content starts with [avatar], wraps in flex container
49347
- * so avatar and text align horizontally, with text stacking vertically
49348
- */
49349
- renderTableCellContent(content) {
49350
- const avatarMatch = content.match(/^\[avatar(?::([a-z0-9-]+))?\]\s*/i);
49351
- if (avatarMatch) {
49352
- const avatarVariant = avatarMatch[1]?.toLowerCase();
49353
- const avatarHtml = this.renderSemanticMarker("avatar", avatarVariant);
49354
- const restContent = content.slice(avatarMatch[0].length);
49355
- const restHtml = this.renderSemanticMarkers(restContent);
49356
- const lines = restHtml.split("\n");
49357
- const textHtml = lines.length > 1 ? lines.map((line) => `<span>${line}</span>`).join("") : restHtml;
49358
- return `<div class="${this.prefix}-cell-avatar-layout">${avatarHtml}<div class="${this.prefix}-cell-avatar-text">${textHtml}</div></div>`;
49359
- }
49360
- const withMarkers = this.renderSemanticMarkers(content);
49361
- return withMarkers.replace(/\n/g, "<br>");
49700
+ return renderDivider(node, this.getRenderContext());
49362
49701
  }
49363
49702
  };
49364
49703
  function createHtmlRenderer(options) {
49365
49704
  return new HtmlRenderer(options);
49366
49705
  }
49367
49706
 
49368
- // src/renderer/svg/index.ts
49369
- var SvgRenderer = class {
49370
- options;
49371
- theme;
49372
- currentX = 0;
49373
- currentY = 0;
49374
- contentWidth = 0;
49375
- constructor(options = {}) {
49376
- this.options = {
49377
- width: options.width ?? 800,
49378
- height: options.height ?? 600,
49379
- scale: options.scale ?? 1,
49380
- background: options.background ?? "#ffffff",
49381
- padding: options.padding ?? 20,
49382
- fontFamily: options.fontFamily ?? "system-ui, -apple-system, sans-serif"
49383
- };
49384
- this.theme = defaultTheme;
49385
- this.contentWidth = this.options.width - this.options.padding * 2;
49386
- }
49387
- /**
49388
- * Render a wireframe document to SVG
49389
- */
49390
- render(doc) {
49391
- this.currentX = this.options.padding;
49392
- this.currentY = this.options.padding;
49393
- const firstPage = doc.children[0];
49394
- let width = this.options.width;
49395
- let height = this.options.height;
49396
- if (firstPage && (firstPage.viewport !== void 0 || firstPage.device !== void 0)) {
49397
- const viewport = resolveViewport(firstPage.viewport, firstPage.device);
49398
- width = viewport.width;
49399
- height = viewport.height;
49400
- this.contentWidth = width - this.options.padding * 2;
49401
- }
49402
- const content = doc.children.map((page) => this.renderPage(page)).join("\n");
49403
- const svg = `<?xml version="1.0" encoding="UTF-8"?>
49404
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">
49405
- <defs>
49406
- ${this.generateDefs()}
49407
- </defs>
49408
- <rect width="100%" height="100%" fill="${this.options.background}"/>
49409
- <g transform="scale(${this.options.scale})">
49410
- ${content}
49411
- </g>
49412
- </svg>`;
49413
- return { svg, width, height };
49414
- }
49415
- /**
49416
- * Generate SVG defs (styles, patterns, etc.)
49417
- */
49418
- generateDefs() {
49419
- return `
49420
- <style>
49421
- text { font-family: ${this.options.fontFamily}; }
49422
- .wf-title { font-weight: 600; }
49423
- .wf-muted { fill: ${this.theme.colors.muted}; }
49424
- </style>
49425
- `;
49426
- }
49427
- /**
49428
- * Render a page node
49429
- */
49430
- renderPage(node) {
49431
- const elements = [];
49432
- if (node.title) {
49433
- elements.push(this.renderPageTitle(node.title));
49434
- }
49435
- for (const child of node.children) {
49436
- elements.push(this.renderNode(child));
49437
- }
49438
- return elements.join("\n");
49439
- }
49440
- /**
49441
- * Render page title
49442
- */
49443
- renderPageTitle(title) {
49444
- const fontSize = 24;
49445
- const y = this.currentY + fontSize;
49446
- this.currentY += fontSize + 16;
49447
- return `<text x="${this.currentX}" y="${y}" font-size="${fontSize}" font-weight="600" fill="${this.theme.colors.foreground}">${this.escapeXml(title)}</text>`;
49448
- }
49449
- /**
49450
- * Render any AST node
49451
- */
49452
- renderNode(node) {
49453
- switch (node.type) {
49454
- // Layout nodes
49455
- case "Row":
49456
- return this.renderRow(node);
49457
- case "Col":
49458
- return this.renderCol(node);
49459
- case "Header":
49460
- return this.renderHeader(node);
49461
- case "Main":
49462
- return this.renderMain(node);
49463
- case "Footer":
49464
- return this.renderFooter(node);
49465
- case "Sidebar":
49466
- return this.renderSidebar(node);
49467
- // Container nodes
49468
- case "Card":
49469
- return this.renderCard(node);
49470
- case "Modal":
49471
- return this.renderModal(node);
49472
- // Text nodes
49473
- case "Text":
49474
- return this.renderText(node);
49475
- case "Title":
49476
- return this.renderTitle(node);
49477
- case "Link":
49478
- return this.renderLink(node);
49479
- // Input nodes
49480
- case "Input":
49481
- return this.renderInput(node);
49482
- case "Textarea":
49483
- return this.renderTextarea(node);
49484
- case "Select":
49485
- return this.renderSelect(node);
49486
- case "Checkbox":
49487
- return this.renderCheckbox(node);
49488
- case "Radio":
49489
- return this.renderRadio(node);
49490
- case "Switch":
49491
- return this.renderSwitch(node);
49492
- // Button
49493
- case "Button":
49494
- return this.renderButton(node);
49495
- // Display nodes
49496
- case "Image":
49497
- return this.renderImage(node);
49498
- case "Placeholder":
49499
- return this.renderPlaceholder(node);
49500
- case "Avatar":
49501
- return this.renderAvatar(node);
49502
- case "Badge":
49503
- return this.renderBadge(node);
49504
- // Data nodes
49505
- case "Table":
49506
- return this.renderTable(node);
49507
- case "List":
49508
- return this.renderList(node);
49509
- // Feedback nodes
49510
- case "Alert":
49511
- return this.renderAlert(node);
49512
- case "Progress":
49513
- return this.renderProgress(node);
49514
- case "Spinner":
49515
- return this.renderSpinner(node);
49516
- // Navigation nodes
49517
- case "Nav":
49518
- return this.renderNav(node);
49519
- case "Tabs":
49520
- return this.renderTabs(node);
49521
- case "Breadcrumb":
49522
- return this.renderBreadcrumb(node);
49523
- default:
49524
- return `<!-- Unsupported: ${node.type} -->`;
49525
- }
49526
- }
49527
- // ===========================================
49528
- // Layout Renderers
49529
- // ===========================================
49530
- renderRow(node) {
49531
- const savedX = this.currentX;
49532
- const savedY = this.currentY;
49533
- const elements = [];
49534
- const totalSpan = node.children.reduce((sum, child) => {
49535
- if ("span" in child && typeof child.span === "number") {
49536
- return sum + child.span;
49537
- }
49538
- return sum + 1;
49539
- }, 0);
49540
- const colWidth = this.contentWidth / Math.max(totalSpan, 1);
49541
- let maxHeight = 0;
49542
- for (const child of node.children) {
49543
- const span = "span" in child && typeof child.span === "number" ? child.span : 1;
49544
- const childWidth = colWidth * span;
49545
- const startY = this.currentY;
49546
- elements.push(this.renderNode(child));
49547
- const childHeight = this.currentY - startY;
49548
- maxHeight = Math.max(maxHeight, childHeight);
49549
- this.currentX += childWidth;
49550
- this.currentY = savedY;
49551
- }
49552
- this.currentX = savedX;
49553
- this.currentY = savedY + maxHeight;
49554
- return elements.join("\n");
49555
- }
49556
- renderCol(node) {
49557
- return node.children.map((child) => this.renderNode(child)).join("\n");
49558
- }
49559
- renderHeader(node) {
49560
- const height = 60;
49561
- const x = this.currentX;
49562
- const y = this.currentY;
49563
- const savedY = this.currentY;
49564
- this.currentY += 16;
49565
- const children = node.children.map((c) => this.renderNode(c)).join("\n");
49566
- this.currentY = savedY + height + 8;
49567
- return `
49568
- <g transform="translate(${x}, ${y})">
49569
- <rect width="${this.contentWidth}" height="${height}" fill="${this.theme.colors.background}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49570
- ${children}
49571
- </g>`;
49572
- }
49573
- renderMain(node) {
49574
- return node.children.map((c) => this.renderNode(c)).join("\n");
49575
- }
49576
- renderFooter(node) {
49577
- const height = 60;
49578
- const x = this.currentX;
49579
- const y = this.currentY;
49580
- const savedY = this.currentY;
49581
- this.currentY += 16;
49582
- const children = node.children.map((c) => this.renderNode(c)).join("\n");
49583
- this.currentY = savedY + height + 8;
49584
- return `
49585
- <g transform="translate(${x}, ${y})">
49586
- <rect width="${this.contentWidth}" height="${height}" fill="${this.theme.colors.background}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49587
- ${children}
49588
- </g>`;
49589
- }
49590
- renderSidebar(node) {
49591
- const width = 200;
49592
- const height = 300;
49593
- const x = this.currentX;
49594
- const y = this.currentY;
49595
- const savedY = this.currentY;
49596
- this.currentY += 16;
49597
- const children = node.children.map((c) => this.renderNode(c)).join("\n");
49598
- this.currentY = savedY + height + 8;
49599
- return `
49600
- <g transform="translate(${x}, ${y})">
49601
- <rect width="${width}" height="${height}" fill="${this.theme.colors.background}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49602
- ${children}
49603
- </g>`;
49604
- }
49605
- // ===========================================
49606
- // Container Renderers
49607
- // ===========================================
49608
- renderCard(node) {
49609
- const width = Math.min(300, this.contentWidth);
49610
- const x = this.currentX;
49611
- const y = this.currentY;
49612
- const savedY = this.currentY;
49613
- this.currentY += 16;
49614
- let titleSvg = "";
49615
- if (node.title) {
49616
- const titleFontSize = 16;
49617
- titleSvg = `<text x="16" y="${titleFontSize + 12}" font-size="${titleFontSize}" font-weight="600" fill="${this.theme.colors.foreground}">${this.escapeXml(node.title)}</text>`;
49618
- this.currentY += titleFontSize + 8;
49619
- }
49620
- const childStartY = this.currentY - savedY;
49621
- const children = node.children.map((c) => this.renderNode(c)).join("\n");
49622
- const contentHeight = Math.max(this.currentY - savedY, 100);
49623
- this.currentY = savedY + contentHeight + 16;
49624
- return `
49625
- <g transform="translate(${x}, ${y})">
49626
- <rect width="${width}" height="${contentHeight}" rx="8" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>
49627
- ${titleSvg}
49628
- <g transform="translate(16, ${childStartY})">
49629
- ${children}
49630
- </g>
49631
- </g>`;
49632
- }
49633
- renderModal(node) {
49634
- const width = 400;
49635
- const height = 300;
49636
- const x = (this.options.width - width) / 2;
49637
- const y = (this.options.height - height) / 2;
49638
- let titleSvg = "";
49639
- if (node.title) {
49640
- titleSvg = `<text x="20" y="30" font-size="18" font-weight="600" fill="${this.theme.colors.foreground}">${this.escapeXml(node.title)}</text>`;
49641
- }
49642
- const savedX = this.currentX;
49643
- const savedY = this.currentY;
49644
- this.currentX = 20;
49645
- this.currentY = 50;
49646
- const children = node.children.map((c) => this.renderNode(c)).join("\n");
49647
- this.currentX = savedX;
49648
- this.currentY = savedY;
49649
- return `
49650
- <g>
49651
- <rect width="100%" height="100%" fill="rgba(0,0,0,0.5)" opacity="0.5"/>
49652
- <g transform="translate(${x}, ${y})">
49653
- <rect width="${width}" height="${height}" rx="8" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>
49654
- ${titleSvg}
49655
- ${children}
49656
- </g>
49657
- </g>`;
49658
- }
49659
- // ===========================================
49660
- // Text Renderers
49661
- // ===========================================
49662
- renderText(node) {
49663
- const fontSize = this.resolveFontSize(node.size);
49664
- const fill = node.muted ? this.theme.colors.muted : this.theme.colors.foreground;
49665
- const fontWeight = node.weight || "normal";
49666
- const y = this.currentY + fontSize;
49667
- this.currentY += fontSize + 8;
49668
- return `<text x="${this.currentX}" y="${y}" font-size="${fontSize}" font-weight="${fontWeight}" fill="${fill}">${this.escapeXml(node.content)}</text>`;
49669
- }
49670
- renderTitle(node) {
49671
- const level = node.level || 1;
49672
- const fontSize = this.getTitleFontSize(level);
49673
- const y = this.currentY + fontSize;
49674
- this.currentY += fontSize + 12;
49675
- return `<text x="${this.currentX}" y="${y}" font-size="${fontSize}" font-weight="600" fill="${this.theme.colors.foreground}">${this.escapeXml(node.content)}</text>`;
49676
- }
49677
- renderLink(node) {
49678
- const fontSize = 14;
49679
- const y = this.currentY + fontSize;
49680
- this.currentY += fontSize + 8;
49681
- return `<text x="${this.currentX}" y="${y}" font-size="${fontSize}" fill="${this.theme.colors.primary}" text-decoration="underline">${this.escapeXml(node.content)}</text>`;
49682
- }
49683
- // ===========================================
49684
- // Input Renderers
49685
- // ===========================================
49686
- renderInput(node) {
49687
- const width = 280;
49688
- const height = 40;
49689
- const x = this.currentX;
49690
- let y = this.currentY;
49691
- let result = "";
49692
- if (node.label) {
49693
- result += `<text x="${x}" y="${y + 14}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.label)}</text>`;
49694
- y += 24;
49695
- }
49696
- const placeholder = node.placeholder || "";
49697
- result += `
49698
- <g transform="translate(${x}, ${y})">
49699
- <rect width="${width}" height="${height}" rx="4" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>
49700
- <text x="12" y="${height / 2 + 5}" font-size="14" fill="${this.theme.colors.muted}">${this.escapeXml(placeholder)}</text>
49701
- </g>`;
49702
- this.currentY = y + height + 12;
49703
- return result;
49704
- }
49705
- renderTextarea(node) {
49706
- const width = 280;
49707
- const height = 100;
49708
- const x = this.currentX;
49709
- let y = this.currentY;
49710
- let result = "";
49711
- if (node.label) {
49712
- result += `<text x="${x}" y="${y + 14}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.label)}</text>`;
49713
- y += 24;
49714
- }
49715
- const placeholder = node.placeholder || "";
49716
- result += `
49717
- <g transform="translate(${x}, ${y})">
49718
- <rect width="${width}" height="${height}" rx="4" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>
49719
- <text x="12" y="24" font-size="14" fill="${this.theme.colors.muted}">${this.escapeXml(placeholder)}</text>
49720
- </g>`;
49721
- this.currentY = y + height + 12;
49722
- return result;
49723
- }
49724
- renderSelect(node) {
49725
- const width = 280;
49726
- const height = 40;
49727
- const x = this.currentX;
49728
- let y = this.currentY;
49729
- let result = "";
49730
- if (node.label) {
49731
- result += `<text x="${x}" y="${y + 14}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.label)}</text>`;
49732
- y += 24;
49733
- }
49734
- const placeholder = node.placeholder || "Select...";
49735
- result += `
49736
- <g transform="translate(${x}, ${y})">
49737
- <rect width="${width}" height="${height}" rx="4" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>
49738
- <text x="12" y="${height / 2 + 5}" font-size="14" fill="${this.theme.colors.muted}">${this.escapeXml(placeholder)}</text>
49739
- <path d="M${width - 24} ${height / 2 - 3} l6 6 l6 -6" fill="none" stroke="${this.theme.colors.muted}" stroke-width="1.5"/>
49740
- </g>`;
49741
- this.currentY = y + height + 12;
49742
- return result;
49743
- }
49744
- renderCheckbox(node) {
49745
- const x = this.currentX;
49746
- const y = this.currentY;
49747
- const size = 18;
49748
- let result = `
49749
- <g transform="translate(${x}, ${y})">
49750
- <rect width="${size}" height="${size}" rx="3" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>`;
49751
- if (node.checked) {
49752
- result += `<path d="M4 9 L7 12 L14 5" fill="none" stroke="${this.theme.colors.foreground}" stroke-width="2"/>`;
49753
- }
49754
- if (node.label) {
49755
- result += `<text x="${size + 8}" y="${size - 3}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.label)}</text>`;
49756
- }
49757
- result += "</g>";
49758
- this.currentY += size + 12;
49759
- return result;
49760
- }
49761
- renderRadio(node) {
49762
- const x = this.currentX;
49763
- const y = this.currentY;
49764
- const size = 18;
49765
- const radius = size / 2;
49766
- let result = `
49767
- <g transform="translate(${x}, ${y})">
49768
- <circle cx="${radius}" cy="${radius}" r="${radius - 1}" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>`;
49769
- if (node.checked) {
49770
- result += `<circle cx="${radius}" cy="${radius}" r="${radius - 5}" fill="${this.theme.colors.foreground}"/>`;
49771
- }
49772
- if (node.label) {
49773
- result += `<text x="${size + 8}" y="${size - 3}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.label)}</text>`;
49774
- }
49775
- result += "</g>";
49776
- this.currentY += size + 12;
49777
- return result;
49778
- }
49779
- renderSwitch(node) {
49780
- const x = this.currentX;
49781
- const y = this.currentY;
49782
- const width = 44;
49783
- const height = 24;
49784
- const radius = height / 2;
49785
- const isOn = node.checked;
49786
- const bgColor = isOn ? this.theme.colors.primary : this.theme.colors.border;
49787
- const knobX = isOn ? width - radius : radius;
49788
- let result = `
49789
- <g transform="translate(${x}, ${y})">
49790
- <rect width="${width}" height="${height}" rx="${radius}" fill="${bgColor}"/>
49791
- <circle cx="${knobX}" cy="${radius}" r="${radius - 3}" fill="white"/>`;
49792
- if (node.label) {
49793
- result += `<text x="${width + 8}" y="${height - 6}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.label)}</text>`;
49794
- }
49795
- result += "</g>";
49796
- this.currentY += height + 12;
49797
- return result;
49798
- }
49799
- // ===========================================
49800
- // Button Renderer
49801
- // ===========================================
49802
- renderButton(node) {
49803
- const content = node.content;
49804
- const hasIcon = !!node.icon;
49805
- const isIconOnly = hasIcon && !content.trim();
49806
- const iconSize = 16;
49807
- const padding = isIconOnly ? 8 : 16;
49808
- let width;
49809
- if (isIconOnly) {
49810
- width = iconSize + padding * 2;
49811
- } else if (hasIcon) {
49812
- width = Math.max(80, content.length * 10 + iconSize + 40);
49813
- } else {
49814
- width = Math.max(80, content.length * 10 + 32);
49815
- }
49816
- const height = 36;
49817
- const x = this.currentX;
49818
- const y = this.currentY;
49819
- let fill = this.theme.colors.primary;
49820
- let textFill = "#ffffff";
49821
- let isOutline = false;
49822
- if (node.secondary) {
49823
- fill = this.theme.colors.secondary;
49824
- } else if (node.outline) {
49825
- fill = "white";
49826
- textFill = this.theme.colors.foreground;
49827
- isOutline = true;
49828
- } else if (node.ghost) {
49829
- fill = "transparent";
49830
- textFill = this.theme.colors.foreground;
49831
- }
49832
- this.currentY += height + 8;
49833
- const strokeAttr = isOutline ? `stroke="${this.theme.colors.border}" stroke-width="1"` : "";
49834
- let iconSvg = "";
49835
- if (hasIcon) {
49836
- const iconData = getIconData(node.icon);
49837
- if (iconData) {
49838
- const iconX = isIconOnly ? (width - iconSize) / 2 : padding;
49839
- const iconY = (height - iconSize) / 2;
49840
- iconSvg = this.renderIconPaths(iconData, iconX, iconY, iconSize, textFill);
49841
- }
49842
- }
49843
- const textX = hasIcon && !isIconOnly ? padding + iconSize + 8 + (width - padding - iconSize - 8 - padding) / 2 : width / 2;
49844
- const textContent = isIconOnly ? "" : `<text x="${textX}" y="${height / 2 + 5}" font-size="14" fill="${textFill}" text-anchor="middle">${this.escapeXml(content)}</text>`;
49845
- return `
49846
- <g transform="translate(${x}, ${y})">
49847
- <rect width="${width}" height="${height}" rx="4" fill="${fill}" ${strokeAttr}/>
49848
- ${iconSvg}
49849
- ${textContent}
49850
- </g>`;
49851
- }
49852
- /**
49853
- * Render icon paths for SVG
49854
- */
49855
- renderIconPaths(data, x, y, size, color) {
49856
- const scale = size / 24;
49857
- const paths = data.map(([tag, attrs]) => {
49858
- const attrStr = Object.entries(attrs).map(([key, value]) => `${key}="${value}"`).join(" ");
49859
- return `<${tag} ${attrStr} />`;
49860
- }).join("");
49861
- return `<g transform="translate(${x}, ${y}) scale(${scale})" fill="none" stroke="${color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${paths}</g>`;
49862
- }
49863
- // ===========================================
49864
- // Display Renderers
49865
- // ===========================================
49866
- renderImage(node) {
49867
- const width = node.w && typeof node.w === "number" ? node.w : 200;
49868
- const height = node.h && typeof node.h === "number" ? node.h : 150;
49869
- const x = this.currentX;
49870
- const y = this.currentY;
49871
- this.currentY += height + 12;
49872
- return `
49873
- <g transform="translate(${x}, ${y})">
49874
- <rect width="${width}" height="${height}" fill="${this.theme.colors.muted}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49875
- <line x1="0" y1="0" x2="${width}" y2="${height}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49876
- <line x1="${width}" y1="0" x2="0" y2="${height}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49877
- <text x="${width / 2}" y="${height / 2 + 5}" font-size="14" fill="${this.theme.colors.foreground}" text-anchor="middle">${this.escapeXml(node.alt || "Image")}</text>
49878
- </g>`;
49879
- }
49880
- renderPlaceholder(node) {
49881
- const width = node.w && typeof node.w === "number" ? node.w : 200;
49882
- const height = node.h && typeof node.h === "number" ? node.h : 100;
49883
- const x = this.currentX;
49884
- const y = this.currentY;
49885
- this.currentY += height + 12;
49886
- return `
49887
- <g transform="translate(${x}, ${y})">
49888
- <rect width="${width}" height="${height}" fill="${this.theme.colors.muted}" stroke="${this.theme.colors.border}" stroke-width="1" stroke-dasharray="4,4"/>
49889
- <text x="${width / 2}" y="${height / 2 + 5}" font-size="14" fill="${this.theme.colors.foreground}" text-anchor="middle">${this.escapeXml(node.label || "Placeholder")}</text>
49890
- </g>`;
49891
- }
49892
- renderAvatar(node) {
49893
- const sizes = { xs: 24, sm: 32, md: 40, lg: 48, xl: 64 };
49894
- const size = sizes[node.size || "md"] || 40;
49895
- const radius = size / 2;
49896
- const x = this.currentX;
49897
- const y = this.currentY;
49898
- const initial = node.name ? node.name.charAt(0).toUpperCase() : "?";
49899
- this.currentY += size + 12;
49900
- return `
49901
- <g transform="translate(${x}, ${y})">
49902
- <circle cx="${radius}" cy="${radius}" r="${radius}" fill="${this.theme.colors.muted}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49903
- <text x="${radius}" y="${radius + 5}" font-size="${size / 2.5}" fill="${this.theme.colors.foreground}" text-anchor="middle">${initial}</text>
49904
- </g>`;
49905
- }
49906
- renderBadge(node) {
49907
- const content = node.content;
49908
- const width = Math.max(24, content.length * 8 + 16);
49909
- const height = 22;
49910
- const x = this.currentX;
49911
- const y = this.currentY;
49912
- const fill = this.theme.colors.muted;
49913
- const textFill = this.theme.colors.foreground;
49914
- this.currentY += height + 8;
49915
- return `
49916
- <g transform="translate(${x}, ${y})">
49917
- <rect width="${width}" height="${height}" rx="11" fill="${fill}" stroke="${this.theme.colors.border}" stroke-width="1"/>
49918
- <text x="${width / 2}" y="${height / 2 + 4}" font-size="12" fill="${textFill}" text-anchor="middle">${this.escapeXml(content)}</text>
49919
- </g>`;
49920
- }
49921
- // ===========================================
49922
- // Data Renderers
49923
- // ===========================================
49924
- renderTable(node) {
49925
- const columns = node.columns || [];
49926
- const rows = node.rows || [];
49927
- const rowCount = rows.length || 3;
49928
- const colWidth = 120;
49929
- const rowHeight = 40;
49930
- const x = this.currentX;
49931
- const y = this.currentY;
49932
- let svg = `<g transform="translate(${x}, ${y})">`;
49933
- svg += `<rect width="${columns.length * colWidth}" height="${rowHeight}" fill="${this.theme.colors.muted}"/>`;
49934
- columns.forEach((col, i) => {
49935
- svg += `<text x="${i * colWidth + 12}" y="${rowHeight / 2 + 5}" font-size="14" font-weight="600">${this.escapeXml(col)}</text>`;
49936
- });
49937
- const displayRowCount = rows.length > 0 ? rows.length : Math.max(rowCount, 3);
49938
- for (let rowIdx = 0; rowIdx < displayRowCount; rowIdx++) {
49939
- const rowY = (rowIdx + 1) * rowHeight;
49940
- svg += `<rect y="${rowY}" width="${columns.length * colWidth}" height="${rowHeight}" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>`;
49941
- columns.forEach((_, colIdx) => {
49942
- const cellContent = rows[rowIdx] && rows[rowIdx][colIdx] ? String(typeof rows[rowIdx][colIdx] === "object" ? "..." : rows[rowIdx][colIdx]) : "\u2014";
49943
- svg += `<text x="${colIdx * colWidth + 12}" y="${rowY + rowHeight / 2 + 5}" font-size="14" fill="${this.theme.colors.muted}">${this.escapeXml(cellContent)}</text>`;
49944
- });
49945
- }
49946
- svg += "</g>";
49947
- this.currentY += (displayRowCount + 1) * rowHeight + 16;
49948
- return svg;
49949
- }
49950
- renderList(node) {
49951
- const x = this.currentX;
49952
- let y = this.currentY;
49953
- const items = node.items || [];
49954
- const ordered = node.ordered || false;
49955
- let svg = `<g transform="translate(${x}, ${y})">`;
49956
- items.forEach((item, idx) => {
49957
- const marker = ordered ? `${idx + 1}.` : "\u2022";
49958
- const content = typeof item === "string" ? item : item.content;
49959
- svg += `<text x="0" y="${idx * 24 + 16}" font-size="14" fill="${this.theme.colors.foreground}">${marker} ${this.escapeXml(content)}</text>`;
49960
- });
49961
- svg += "</g>";
49962
- this.currentY += items.length * 24 + 12;
49963
- return svg;
49964
- }
49965
- // ===========================================
49966
- // Feedback Renderers
49967
- // ===========================================
49968
- renderAlert(node) {
49969
- const width = Math.min(400, this.contentWidth);
49970
- const height = 48;
49971
- const x = this.currentX;
49972
- const y = this.currentY;
49973
- this.currentY += height + 12;
49974
- return `
49975
- <g transform="translate(${x}, ${y})">
49976
- <rect width="${width}" height="${height}" rx="4" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>
49977
- <text x="16" y="${height / 2 + 5}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.content)}</text>
49978
- </g>`;
49979
- }
49980
- renderProgress(node) {
49981
- const width = 200;
49982
- const height = 8;
49983
- const x = this.currentX;
49984
- let y = this.currentY;
49985
- let result = "";
49986
- if (node.label) {
49987
- result += `<text x="${x}" y="${y + 14}" font-size="14" fill="${this.theme.colors.foreground}">${this.escapeXml(node.label)}</text>`;
49988
- y += 24;
49989
- }
49990
- const value = node.value || 0;
49991
- const max = node.max || 100;
49992
- const percent = Math.min(100, Math.max(0, value / max * 100));
49993
- result += `
49994
- <g transform="translate(${x}, ${y})">
49995
- <rect width="${width}" height="${height}" rx="${height / 2}" fill="${this.theme.colors.muted}"/>
49996
- <rect width="${width * percent / 100}" height="${height}" rx="${height / 2}" fill="${this.theme.colors.primary}"/>
49997
- </g>`;
49998
- this.currentY = y + height + 12;
49999
- return result;
50000
- }
50001
- renderSpinner(node) {
50002
- const sizes = { xs: 16, sm: 20, md: 24, lg: 32, xl: 40 };
50003
- const size = sizes[node.size || "md"] || 24;
50004
- const x = this.currentX + size / 2;
50005
- const y = this.currentY + size / 2;
50006
- const radius = size / 2 - 2;
50007
- this.currentY += size + 12;
50008
- return `
50009
- <g transform="translate(${x}, ${y})">
50010
- <circle r="${radius}" fill="none" stroke="${this.theme.colors.muted}" stroke-width="2"/>
50011
- <path d="M0,-${radius} A${radius},${radius} 0 0,1 ${radius},0" fill="none" stroke="${this.theme.colors.primary}" stroke-width="2" stroke-linecap="round"/>
50012
- </g>`;
50013
- }
50014
- // ===========================================
50015
- // Navigation Renderers
50016
- // ===========================================
50017
- renderNav(node) {
50018
- const items = node.items || [];
50019
- const x = this.currentX;
50020
- const y = this.currentY;
50021
- const vertical = node.vertical || false;
50022
- let svg = `<g transform="translate(${x}, ${y})">`;
50023
- if (vertical) {
50024
- items.forEach((item, idx) => {
50025
- const label = typeof item === "string" ? item : item.label;
50026
- const isActive = typeof item === "object" && item.active;
50027
- const fill = isActive ? this.theme.colors.foreground : this.theme.colors.muted;
50028
- svg += `<text x="0" y="${idx * 32 + 16}" font-size="14" fill="${fill}">${this.escapeXml(label)}</text>`;
50029
- });
50030
- this.currentY += items.length * 32 + 12;
50031
- } else {
50032
- let offsetX = 0;
50033
- items.forEach((item) => {
50034
- const label = typeof item === "string" ? item : item.label;
50035
- const isActive = typeof item === "object" && item.active;
50036
- const fill = isActive ? this.theme.colors.foreground : this.theme.colors.muted;
50037
- svg += `<text x="${offsetX}" y="16" font-size="14" fill="${fill}">${this.escapeXml(label)}</text>`;
50038
- offsetX += label.length * 8 + 24;
50039
- });
50040
- this.currentY += 32;
50041
- }
50042
- svg += "</g>";
50043
- return svg;
50044
- }
50045
- renderTabs(node) {
50046
- const items = node.items || [];
50047
- const x = this.currentX;
50048
- const y = this.currentY;
50049
- const tabHeight = 40;
50050
- let svg = `<g transform="translate(${x}, ${y})">`;
50051
- let offsetX = 0;
50052
- items.forEach((item) => {
50053
- const label = typeof item === "string" ? item : item;
50054
- const tabWidth = label.length * 10 + 24;
50055
- svg += `<rect x="${offsetX}" width="${tabWidth}" height="${tabHeight}" fill="white" stroke="${this.theme.colors.border}" stroke-width="1"/>`;
50056
- svg += `<text x="${offsetX + tabWidth / 2}" y="${tabHeight / 2 + 5}" font-size="14" text-anchor="middle">${this.escapeXml(label)}</text>`;
50057
- offsetX += tabWidth;
50058
- });
50059
- svg += "</g>";
50060
- this.currentY += tabHeight + 12;
50061
- return svg;
50062
- }
50063
- renderBreadcrumb(node) {
50064
- const items = node.items || [];
50065
- const separator = "/";
50066
- const x = this.currentX;
50067
- const y = this.currentY;
50068
- let svg = `<g transform="translate(${x}, ${y})">`;
50069
- let offsetX = 0;
50070
- items.forEach((item, idx) => {
50071
- const label = typeof item === "string" ? item : item.label;
50072
- const isLast = idx === items.length - 1;
50073
- const fill = isLast ? this.theme.colors.foreground : this.theme.colors.muted;
50074
- svg += `<text x="${offsetX}" y="16" font-size="14" fill="${fill}">${this.escapeXml(label)}</text>`;
50075
- offsetX += label.length * 8 + 8;
50076
- if (!isLast) {
50077
- svg += `<text x="${offsetX}" y="16" font-size="14" fill="${this.theme.colors.muted}">${separator}</text>`;
50078
- offsetX += 16;
50079
- }
50080
- });
50081
- svg += "</g>";
50082
- this.currentY += 28;
50083
- return svg;
50084
- }
50085
- // ===========================================
50086
- // Utility Methods
50087
- // ===========================================
50088
- getFontSize(size) {
50089
- const sizes = {
50090
- xs: 12,
50091
- sm: 14,
50092
- base: 16,
50093
- md: 16,
50094
- lg: 18,
50095
- xl: 20,
50096
- "2xl": 24,
50097
- "3xl": 30
50098
- };
50099
- return sizes[size] || 16;
50100
- }
50101
- resolveFontSize(size) {
50102
- if (!size) return 16;
50103
- if (typeof size === "string") {
50104
- return this.getFontSize(size);
50105
- }
50106
- if (typeof size === "object" && "value" in size) {
50107
- if (size.unit === "px") return size.value;
50108
- if (size.unit === "rem") return size.value * 16;
50109
- if (size.unit === "em") return size.value * 16;
50110
- return size.value;
50111
- }
50112
- return 16;
50113
- }
50114
- getTitleFontSize(level) {
50115
- const sizes = {
50116
- 1: 32,
50117
- 2: 28,
50118
- 3: 24,
50119
- 4: 20,
50120
- 5: 18,
50121
- 6: 16
50122
- };
50123
- return sizes[level] || 24;
50124
- }
50125
- escapeXml(str) {
50126
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
50127
- }
50128
- };
50129
- function createSvgRenderer(options) {
50130
- return new SvgRenderer(options);
50131
- }
50132
- function renderToSvg(doc, options) {
50133
- const renderer = new SvgRenderer(options);
50134
- return renderer.render(doc);
50135
- }
50136
-
50137
49707
  // src/renderer/index.ts
50138
49708
  function render(document, options = {}) {
50139
49709
  const renderer = createHtmlRenderer(options);
@@ -50170,7 +49740,7 @@ ${html}
50170
49740
  </body>
50171
49741
  </html>`;
50172
49742
  }
50173
- function renderToSvg2(document, options = {}) {
49743
+ function renderToSvg(document, options = {}) {
50174
49744
  const firstPage = document.children[0];
50175
49745
  let width = options.width ?? 800;
50176
49746
  let height = options.height ?? 600;
@@ -50181,15 +49751,22 @@ function renderToSvg2(document, options = {}) {
50181
49751
  height = viewport.height;
50182
49752
  }
50183
49753
  }
50184
- const padding = options.padding ?? 20;
50185
49754
  const background = options.background ?? "#ffffff";
50186
49755
  const { html, css } = render(document, { theme: "light" });
50187
49756
  const svg = `<?xml version="1.0" encoding="UTF-8"?>
50188
49757
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
50189
49758
  viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">
50190
49759
  <rect width="100%" height="100%" fill="${background}"/>
50191
- <foreignObject x="${padding}" y="${padding}" width="${width - padding * 2}" height="${height - padding * 2}">
50192
- <div xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%; overflow: hidden;">
49760
+ <foreignObject x="0" y="0" width="${width}" height="${height}">
49761
+ <div xmlns="http://www.w3.org/1999/xhtml" style="
49762
+ width: ${width}px;
49763
+ height: ${height}px;
49764
+ overflow: hidden;
49765
+ display: flex;
49766
+ justify-content: center;
49767
+ align-items: center;
49768
+ box-sizing: border-box;
49769
+ ">
50193
49770
  <style type="text/css">
50194
49771
  ${css}
50195
49772
  </style>
@@ -50199,15 +49776,10 @@ ${css}
50199
49776
  </svg>`;
50200
49777
  return { svg, width, height };
50201
49778
  }
50202
- function renderToPureSvg(document, options = {}) {
50203
- return renderToSvg(document, options);
50204
- }
50205
49779
  // Annotate the CommonJS export names for ESM import in node:
50206
49780
  0 && (module.exports = {
50207
49781
  HtmlRenderer,
50208
- SvgRenderer,
50209
49782
  createHtmlRenderer,
50210
- createSvgRenderer,
50211
49783
  darkTheme,
50212
49784
  defaultTheme,
50213
49785
  generateComponentStyles,
@@ -50218,7 +49790,6 @@ function renderToPureSvg(document, options = {}) {
50218
49790
  render,
50219
49791
  renderIconSvg,
50220
49792
  renderToHtml,
50221
- renderToPureSvg,
50222
49793
  renderToSvg
50223
49794
  });
50224
49795
  /**