@wire-dsl/engine 0.4.1 → 0.5.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/README.md CHANGED
@@ -92,7 +92,7 @@ validateIR(ir); // Throws if invalid
92
92
 
93
93
  **Display**: Divider, Badge, Link, Alert
94
94
 
95
- **Info**: StatCard, Code, ChartPlaceholder
95
+ **Info**: Stat, Code, ChartPlaceholder
96
96
 
97
97
  **Feedback**: Modal, Spinner
98
98
 
package/dist/index.cjs CHANGED
@@ -2588,7 +2588,7 @@ ${messages}`);
2588
2588
  "Label",
2589
2589
  "Image",
2590
2590
  "Card",
2591
- "StatCard",
2591
+ "Stat",
2592
2592
  "Topbar",
2593
2593
  "Table",
2594
2594
  "Chart",
@@ -3731,7 +3731,7 @@ var LayoutEngine = class {
3731
3731
  if (node.componentType === "Textarea") return 100 + controlLabelOffset;
3732
3732
  if (node.componentType === "Modal") return 300;
3733
3733
  if (node.componentType === "Card") return 120;
3734
- if (node.componentType === "StatCard") return 120;
3734
+ if (node.componentType === "Stat") return 120;
3735
3735
  if (node.componentType === "Chart" || node.componentType === "ChartPlaceholder") return 250;
3736
3736
  if (node.componentType === "List") {
3737
3737
  const itemsFromProps = String(node.props.items || "").split(",").map((item) => item.trim()).filter(Boolean);
@@ -3782,7 +3782,10 @@ var LayoutEngine = class {
3782
3782
  const density = this.style.density || "normal";
3783
3783
  const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
3784
3784
  const textWidth = this.estimateTextWidth(text, fontSize);
3785
- return Math.max(60, Math.ceil(textWidth + (paddingX + extraPadding) * 2));
3785
+ const iconName = String(node.props.icon || "").trim();
3786
+ const iconSize = iconName ? Math.round(fontSize * 1.1) : 0;
3787
+ const iconGap = iconName ? 8 : 0;
3788
+ return Math.max(60, Math.ceil(textWidth + iconSize + iconGap + (paddingX + extraPadding) * 2));
3786
3789
  }
3787
3790
  if (node.componentType === "Label" || node.componentType === "Text") {
3788
3791
  const text = String(node.props.text || "");
@@ -3813,7 +3816,7 @@ var LayoutEngine = class {
3813
3816
  if (node.componentType === "Table") {
3814
3817
  return 400;
3815
3818
  }
3816
- if (node.componentType === "StatCard" || node.componentType === "Card") {
3819
+ if (node.componentType === "Stat" || node.componentType === "Card") {
3817
3820
  return 280;
3818
3821
  }
3819
3822
  if (node.componentType === "SidebarMenu") {
@@ -4869,8 +4872,8 @@ var SVGRenderer = class {
4869
4872
  return this.renderModal(node, pos);
4870
4873
  case "List":
4871
4874
  return this.renderList(node, pos);
4872
- case "StatCard":
4873
- return this.renderStatCard(node, pos);
4875
+ case "Stat":
4876
+ return this.renderStat(node, pos);
4874
4877
  case "Image":
4875
4878
  return this.renderImage(node, pos);
4876
4879
  // Icon components
@@ -4916,6 +4919,7 @@ var SVGRenderer = class {
4916
4919
  const text = String(node.props.text || "Button");
4917
4920
  const variant = String(node.props.variant || "default");
4918
4921
  const size = String(node.props.size || "md");
4922
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
4919
4923
  const density = this.ir.project.style.density || "normal";
4920
4924
  const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
4921
4925
  const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
@@ -4961,7 +4965,7 @@ var SVGRenderer = class {
4961
4965
  textX = pos.x + buttonWidth / 2;
4962
4966
  textAnchor = "middle";
4963
4967
  }
4964
- let svg = `<g${this.getDataNodeId(node)}>
4968
+ let svg = `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
4965
4969
  <rect x="${pos.x}" y="${buttonY}"
4966
4970
  width="${buttonWidth}" height="${buttonHeight}"
4967
4971
  rx="${radius}"
@@ -5027,6 +5031,7 @@ var SVGRenderer = class {
5027
5031
  const placeholder = String(node.props.placeholder || "");
5028
5032
  const iconLeftName = String(node.props.iconLeft || "").trim();
5029
5033
  const iconRightName = String(node.props.iconRight || "").trim();
5034
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5030
5035
  const radius = this.tokens.input.radius;
5031
5036
  const fontSize = this.tokens.input.fontSize;
5032
5037
  const paddingX = this.tokens.input.paddingX;
@@ -5043,7 +5048,7 @@ var SVGRenderer = class {
5043
5048
  const textX = pos.x + (iconLeftSvg ? leftOffset : paddingX);
5044
5049
  const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
5045
5050
  const iconCenterY = controlY + (controlHeight - iconSize) / 2;
5046
- let svg = `<g${this.getDataNodeId(node)}>`;
5051
+ let svg = `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>`;
5047
5052
  if (label) {
5048
5053
  svg += `
5049
5054
  <text x="${pos.x + paddingX}" y="${this.getControlLabelBaselineY(pos.y)}"
@@ -5681,6 +5686,7 @@ var SVGRenderer = class {
5681
5686
  const placeholder = String(node.props.placeholder || "Select...");
5682
5687
  const iconLeftName = String(node.props.iconLeft || "").trim();
5683
5688
  const iconRightName = String(node.props.iconRight || "").trim();
5689
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5684
5690
  const labelOffset = this.getControlLabelOffset(label);
5685
5691
  const controlY = pos.y + labelOffset;
5686
5692
  const controlHeight = Math.max(16, pos.height - labelOffset);
@@ -5694,7 +5700,7 @@ var SVGRenderer = class {
5694
5700
  const chevronWidth = 20;
5695
5701
  const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
5696
5702
  const iconCenterY = controlY + (controlHeight - iconSize) / 2;
5697
- let svg = `<g${this.getDataNodeId(node)}>`;
5703
+ let svg = `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>`;
5698
5704
  if (label) {
5699
5705
  svg += `
5700
5706
  <text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
@@ -5743,10 +5749,11 @@ var SVGRenderer = class {
5743
5749
  renderCheckbox(node, pos) {
5744
5750
  const label = String(node.props.label || "Checkbox");
5745
5751
  const checked = String(node.props.checked || "false").toLowerCase() === "true";
5752
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5746
5753
  const controlColor = this.resolveControlColor();
5747
5754
  const checkboxSize = 18;
5748
5755
  const checkboxY = pos.y + pos.height / 2 - checkboxSize / 2;
5749
- return `<g${this.getDataNodeId(node)}>
5756
+ return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
5750
5757
  <rect x="${pos.x}" y="${checkboxY}"
5751
5758
  width="${checkboxSize}" height="${checkboxSize}"
5752
5759
  rx="4"
@@ -5767,10 +5774,11 @@ var SVGRenderer = class {
5767
5774
  renderRadio(node, pos) {
5768
5775
  const label = String(node.props.label || "Radio");
5769
5776
  const checked = String(node.props.checked || "false").toLowerCase() === "true";
5777
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5770
5778
  const controlColor = this.resolveControlColor();
5771
5779
  const radioSize = 16;
5772
5780
  const radioY = pos.y + pos.height / 2 - radioSize / 2;
5773
- return `<g${this.getDataNodeId(node)}>
5781
+ return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
5774
5782
  <circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
5775
5783
  r="${radioSize / 2}"
5776
5784
  fill="${this.renderTheme.cardBg}"
@@ -5788,11 +5796,12 @@ var SVGRenderer = class {
5788
5796
  renderToggle(node, pos) {
5789
5797
  const label = String(node.props.label || "Toggle");
5790
5798
  const enabled = String(node.props.enabled || "false").toLowerCase() === "true";
5799
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5791
5800
  const controlColor = this.resolveControlColor();
5792
5801
  const toggleWidth = 40;
5793
5802
  const toggleHeight = 20;
5794
5803
  const toggleY = pos.y + pos.height / 2 - toggleHeight / 2;
5795
- return `<g${this.getDataNodeId(node)}>
5804
+ return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
5796
5805
  <rect x="${pos.x}" y="${toggleY}"
5797
5806
  width="${toggleWidth}" height="${toggleHeight}"
5798
5807
  rx="10"
@@ -6088,7 +6097,7 @@ var SVGRenderer = class {
6088
6097
  text-anchor="middle">${node.componentType}</text>
6089
6098
  </g>`;
6090
6099
  }
6091
- renderStatCard(node, pos) {
6100
+ renderStat(node, pos) {
6092
6101
  const title = String(node.props.title || "Metric");
6093
6102
  const value = String(node.props.value || "0");
6094
6103
  const rawCaption = String(node.props.caption || "");
@@ -6096,7 +6105,9 @@ var SVGRenderer = class {
6096
6105
  const hasCaption = caption.trim().length > 0;
6097
6106
  const iconName = String(node.props.icon || "").trim();
6098
6107
  const iconSvg = iconName ? getIcon(iconName) : null;
6099
- const accentColor = this.resolveAccentColor();
6108
+ const variant = String(node.props.variant || "default");
6109
+ const baseAccent = this.resolveAccentColor();
6110
+ const accentColor = variant !== "default" ? this.resolveVariantColor(variant, baseAccent) : baseAccent;
6100
6111
  const padding = this.resolveSpacing(node.style.padding) || 16;
6101
6112
  const innerX = pos.x + padding;
6102
6113
  const innerY = pos.y + padding;
@@ -6120,7 +6131,7 @@ var SVGRenderer = class {
6120
6131
  const visibleTitle = this.truncateTextToWidth(title, titleMaxWidth, titleSize);
6121
6132
  const visibleCaption = hasCaption ? this.truncateTextToWidth(caption, Math.max(20, innerWidth), captionSize) : "";
6122
6133
  let svg = `<g${this.getDataNodeId(node)}>
6123
- <!-- StatCard Background -->
6134
+ <!-- Stat Background -->
6124
6135
  <rect x="${pos.x}" y="${pos.y}"
6125
6136
  width="${pos.width}" height="${pos.height}"
6126
6137
  rx="8"
@@ -7383,9 +7394,9 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
7383
7394
  return svg;
7384
7395
  }
7385
7396
  /**
7386
- * Render StatCard with gray blocks instead of values
7397
+ * Render Stat with gray blocks instead of values
7387
7398
  */
7388
- renderStatCard(node, pos) {
7399
+ renderStat(node, pos) {
7389
7400
  const hasIcon = String(node.props.icon || "").trim().length > 0;
7390
7401
  const hasCaption = String(node.props.caption || "").trim().length > 0;
7391
7402
  const iconSize = 20;
@@ -8512,7 +8523,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
8512
8523
  /**
8513
8524
  * Render stat card with sketch filter and Comic Sans
8514
8525
  */
8515
- renderStatCard(node, pos) {
8526
+ renderStat(node, pos) {
8516
8527
  const title = String(node.props.title || "Metric");
8517
8528
  const value = String(node.props.value || "0");
8518
8529
  const rawCaption = String(node.props.caption || "");
@@ -8520,7 +8531,9 @@ var SketchSVGRenderer = class extends SVGRenderer {
8520
8531
  const hasCaption = caption.trim().length > 0;
8521
8532
  const iconName = String(node.props.icon || "").trim();
8522
8533
  const iconSvg = iconName ? getIcon(iconName) : null;
8523
- const accentColor = this.resolveAccentColor();
8534
+ const variant = String(node.props.variant || "default");
8535
+ const baseAccent = this.resolveAccentColor();
8536
+ const accentColor = variant !== "default" ? this.resolveVariantColor(variant, baseAccent) : baseAccent;
8524
8537
  const padding = this.resolveSpacing(node.style.padding);
8525
8538
  const innerX = pos.x + padding;
8526
8539
  const innerY = pos.y + padding;
@@ -8543,7 +8556,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
8543
8556
  const visibleTitle = this.truncateTextToWidth(title, titleMaxWidth, titleSize);
8544
8557
  const visibleCaption = hasCaption ? this.truncateTextToWidth(caption, Math.max(20, pos.width - padding * 2), captionSize) : "";
8545
8558
  let svg = `<g${this.getDataNodeId(node)}>
8546
- <!-- StatCard Background -->
8559
+ <!-- Stat Background -->
8547
8560
  <rect x="${pos.x}" y="${pos.y}"
8548
8561
  width="${pos.width}" height="${pos.height}"
8549
8562
  rx="8"
package/dist/index.d.cts CHANGED
@@ -706,7 +706,7 @@ declare class SVGRenderer {
706
706
  protected renderModal(node: IRComponentNode, pos: any): string;
707
707
  protected renderList(node: IRComponentNode, pos: any): string;
708
708
  protected renderGenericComponent(node: IRComponentNode, pos: any): string;
709
- protected renderStatCard(node: IRComponentNode, pos: any): string;
709
+ protected renderStat(node: IRComponentNode, pos: any): string;
710
710
  protected renderImage(node: IRComponentNode, pos: any): string;
711
711
  protected renderBreadcrumbs(node: IRComponentNode, pos: any): string;
712
712
  protected renderSidebarMenu(node: IRComponentNode, pos: any): string;
@@ -892,9 +892,9 @@ declare class SkeletonSVGRenderer extends SVGRenderer {
892
892
  */
893
893
  protected renderTopbar(node: IRComponentNode, pos: any): string;
894
894
  /**
895
- * Render StatCard with gray blocks instead of values
895
+ * Render Stat with gray blocks instead of values
896
896
  */
897
- protected renderStatCard(node: IRComponentNode, pos: any): string;
897
+ protected renderStat(node: IRComponentNode, pos: any): string;
898
898
  /**
899
899
  * Render icon as gray square instead of hiding it
900
900
  */
@@ -1037,7 +1037,7 @@ declare class SketchSVGRenderer extends SVGRenderer {
1037
1037
  /**
1038
1038
  * Render stat card with sketch filter and Comic Sans
1039
1039
  */
1040
- protected renderStatCard(node: IRComponentNode, pos: any): string;
1040
+ protected renderStat(node: IRComponentNode, pos: any): string;
1041
1041
  /**
1042
1042
  * Render image with sketch filter
1043
1043
  */
package/dist/index.d.ts CHANGED
@@ -706,7 +706,7 @@ declare class SVGRenderer {
706
706
  protected renderModal(node: IRComponentNode, pos: any): string;
707
707
  protected renderList(node: IRComponentNode, pos: any): string;
708
708
  protected renderGenericComponent(node: IRComponentNode, pos: any): string;
709
- protected renderStatCard(node: IRComponentNode, pos: any): string;
709
+ protected renderStat(node: IRComponentNode, pos: any): string;
710
710
  protected renderImage(node: IRComponentNode, pos: any): string;
711
711
  protected renderBreadcrumbs(node: IRComponentNode, pos: any): string;
712
712
  protected renderSidebarMenu(node: IRComponentNode, pos: any): string;
@@ -892,9 +892,9 @@ declare class SkeletonSVGRenderer extends SVGRenderer {
892
892
  */
893
893
  protected renderTopbar(node: IRComponentNode, pos: any): string;
894
894
  /**
895
- * Render StatCard with gray blocks instead of values
895
+ * Render Stat with gray blocks instead of values
896
896
  */
897
- protected renderStatCard(node: IRComponentNode, pos: any): string;
897
+ protected renderStat(node: IRComponentNode, pos: any): string;
898
898
  /**
899
899
  * Render icon as gray square instead of hiding it
900
900
  */
@@ -1037,7 +1037,7 @@ declare class SketchSVGRenderer extends SVGRenderer {
1037
1037
  /**
1038
1038
  * Render stat card with sketch filter and Comic Sans
1039
1039
  */
1040
- protected renderStatCard(node: IRComponentNode, pos: any): string;
1040
+ protected renderStat(node: IRComponentNode, pos: any): string;
1041
1041
  /**
1042
1042
  * Render image with sketch filter
1043
1043
  */
package/dist/index.js CHANGED
@@ -2542,7 +2542,7 @@ ${messages}`);
2542
2542
  "Label",
2543
2543
  "Image",
2544
2544
  "Card",
2545
- "StatCard",
2545
+ "Stat",
2546
2546
  "Topbar",
2547
2547
  "Table",
2548
2548
  "Chart",
@@ -3685,7 +3685,7 @@ var LayoutEngine = class {
3685
3685
  if (node.componentType === "Textarea") return 100 + controlLabelOffset;
3686
3686
  if (node.componentType === "Modal") return 300;
3687
3687
  if (node.componentType === "Card") return 120;
3688
- if (node.componentType === "StatCard") return 120;
3688
+ if (node.componentType === "Stat") return 120;
3689
3689
  if (node.componentType === "Chart" || node.componentType === "ChartPlaceholder") return 250;
3690
3690
  if (node.componentType === "List") {
3691
3691
  const itemsFromProps = String(node.props.items || "").split(",").map((item) => item.trim()).filter(Boolean);
@@ -3736,7 +3736,10 @@ var LayoutEngine = class {
3736
3736
  const density = this.style.density || "normal";
3737
3737
  const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
3738
3738
  const textWidth = this.estimateTextWidth(text, fontSize);
3739
- return Math.max(60, Math.ceil(textWidth + (paddingX + extraPadding) * 2));
3739
+ const iconName = String(node.props.icon || "").trim();
3740
+ const iconSize = iconName ? Math.round(fontSize * 1.1) : 0;
3741
+ const iconGap = iconName ? 8 : 0;
3742
+ return Math.max(60, Math.ceil(textWidth + iconSize + iconGap + (paddingX + extraPadding) * 2));
3740
3743
  }
3741
3744
  if (node.componentType === "Label" || node.componentType === "Text") {
3742
3745
  const text = String(node.props.text || "");
@@ -3767,7 +3770,7 @@ var LayoutEngine = class {
3767
3770
  if (node.componentType === "Table") {
3768
3771
  return 400;
3769
3772
  }
3770
- if (node.componentType === "StatCard" || node.componentType === "Card") {
3773
+ if (node.componentType === "Stat" || node.componentType === "Card") {
3771
3774
  return 280;
3772
3775
  }
3773
3776
  if (node.componentType === "SidebarMenu") {
@@ -4823,8 +4826,8 @@ var SVGRenderer = class {
4823
4826
  return this.renderModal(node, pos);
4824
4827
  case "List":
4825
4828
  return this.renderList(node, pos);
4826
- case "StatCard":
4827
- return this.renderStatCard(node, pos);
4829
+ case "Stat":
4830
+ return this.renderStat(node, pos);
4828
4831
  case "Image":
4829
4832
  return this.renderImage(node, pos);
4830
4833
  // Icon components
@@ -4870,6 +4873,7 @@ var SVGRenderer = class {
4870
4873
  const text = String(node.props.text || "Button");
4871
4874
  const variant = String(node.props.variant || "default");
4872
4875
  const size = String(node.props.size || "md");
4876
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
4873
4877
  const density = this.ir.project.style.density || "normal";
4874
4878
  const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
4875
4879
  const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
@@ -4915,7 +4919,7 @@ var SVGRenderer = class {
4915
4919
  textX = pos.x + buttonWidth / 2;
4916
4920
  textAnchor = "middle";
4917
4921
  }
4918
- let svg = `<g${this.getDataNodeId(node)}>
4922
+ let svg = `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
4919
4923
  <rect x="${pos.x}" y="${buttonY}"
4920
4924
  width="${buttonWidth}" height="${buttonHeight}"
4921
4925
  rx="${radius}"
@@ -4981,6 +4985,7 @@ var SVGRenderer = class {
4981
4985
  const placeholder = String(node.props.placeholder || "");
4982
4986
  const iconLeftName = String(node.props.iconLeft || "").trim();
4983
4987
  const iconRightName = String(node.props.iconRight || "").trim();
4988
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
4984
4989
  const radius = this.tokens.input.radius;
4985
4990
  const fontSize = this.tokens.input.fontSize;
4986
4991
  const paddingX = this.tokens.input.paddingX;
@@ -4997,7 +5002,7 @@ var SVGRenderer = class {
4997
5002
  const textX = pos.x + (iconLeftSvg ? leftOffset : paddingX);
4998
5003
  const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
4999
5004
  const iconCenterY = controlY + (controlHeight - iconSize) / 2;
5000
- let svg = `<g${this.getDataNodeId(node)}>`;
5005
+ let svg = `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>`;
5001
5006
  if (label) {
5002
5007
  svg += `
5003
5008
  <text x="${pos.x + paddingX}" y="${this.getControlLabelBaselineY(pos.y)}"
@@ -5635,6 +5640,7 @@ var SVGRenderer = class {
5635
5640
  const placeholder = String(node.props.placeholder || "Select...");
5636
5641
  const iconLeftName = String(node.props.iconLeft || "").trim();
5637
5642
  const iconRightName = String(node.props.iconRight || "").trim();
5643
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5638
5644
  const labelOffset = this.getControlLabelOffset(label);
5639
5645
  const controlY = pos.y + labelOffset;
5640
5646
  const controlHeight = Math.max(16, pos.height - labelOffset);
@@ -5648,7 +5654,7 @@ var SVGRenderer = class {
5648
5654
  const chevronWidth = 20;
5649
5655
  const iconColor = this.hexToRgba(this.resolveMutedColor(), 0.8);
5650
5656
  const iconCenterY = controlY + (controlHeight - iconSize) / 2;
5651
- let svg = `<g${this.getDataNodeId(node)}>`;
5657
+ let svg = `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>`;
5652
5658
  if (label) {
5653
5659
  svg += `
5654
5660
  <text x="${pos.x}" y="${this.getControlLabelBaselineY(pos.y)}"
@@ -5697,10 +5703,11 @@ var SVGRenderer = class {
5697
5703
  renderCheckbox(node, pos) {
5698
5704
  const label = String(node.props.label || "Checkbox");
5699
5705
  const checked = String(node.props.checked || "false").toLowerCase() === "true";
5706
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5700
5707
  const controlColor = this.resolveControlColor();
5701
5708
  const checkboxSize = 18;
5702
5709
  const checkboxY = pos.y + pos.height / 2 - checkboxSize / 2;
5703
- return `<g${this.getDataNodeId(node)}>
5710
+ return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
5704
5711
  <rect x="${pos.x}" y="${checkboxY}"
5705
5712
  width="${checkboxSize}" height="${checkboxSize}"
5706
5713
  rx="4"
@@ -5721,10 +5728,11 @@ var SVGRenderer = class {
5721
5728
  renderRadio(node, pos) {
5722
5729
  const label = String(node.props.label || "Radio");
5723
5730
  const checked = String(node.props.checked || "false").toLowerCase() === "true";
5731
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5724
5732
  const controlColor = this.resolveControlColor();
5725
5733
  const radioSize = 16;
5726
5734
  const radioY = pos.y + pos.height / 2 - radioSize / 2;
5727
- return `<g${this.getDataNodeId(node)}>
5735
+ return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
5728
5736
  <circle cx="${pos.x + radioSize / 2}" cy="${radioY + radioSize / 2}"
5729
5737
  r="${radioSize / 2}"
5730
5738
  fill="${this.renderTheme.cardBg}"
@@ -5742,11 +5750,12 @@ var SVGRenderer = class {
5742
5750
  renderToggle(node, pos) {
5743
5751
  const label = String(node.props.label || "Toggle");
5744
5752
  const enabled = String(node.props.enabled || "false").toLowerCase() === "true";
5753
+ const disabled = this.parseBooleanProp(node.props.disabled, false);
5745
5754
  const controlColor = this.resolveControlColor();
5746
5755
  const toggleWidth = 40;
5747
5756
  const toggleHeight = 20;
5748
5757
  const toggleY = pos.y + pos.height / 2 - toggleHeight / 2;
5749
- return `<g${this.getDataNodeId(node)}>
5758
+ return `<g${this.getDataNodeId(node)}${disabled ? ' opacity="0.45"' : ""}>
5750
5759
  <rect x="${pos.x}" y="${toggleY}"
5751
5760
  width="${toggleWidth}" height="${toggleHeight}"
5752
5761
  rx="10"
@@ -6042,7 +6051,7 @@ var SVGRenderer = class {
6042
6051
  text-anchor="middle">${node.componentType}</text>
6043
6052
  </g>`;
6044
6053
  }
6045
- renderStatCard(node, pos) {
6054
+ renderStat(node, pos) {
6046
6055
  const title = String(node.props.title || "Metric");
6047
6056
  const value = String(node.props.value || "0");
6048
6057
  const rawCaption = String(node.props.caption || "");
@@ -6050,7 +6059,9 @@ var SVGRenderer = class {
6050
6059
  const hasCaption = caption.trim().length > 0;
6051
6060
  const iconName = String(node.props.icon || "").trim();
6052
6061
  const iconSvg = iconName ? getIcon(iconName) : null;
6053
- const accentColor = this.resolveAccentColor();
6062
+ const variant = String(node.props.variant || "default");
6063
+ const baseAccent = this.resolveAccentColor();
6064
+ const accentColor = variant !== "default" ? this.resolveVariantColor(variant, baseAccent) : baseAccent;
6054
6065
  const padding = this.resolveSpacing(node.style.padding) || 16;
6055
6066
  const innerX = pos.x + padding;
6056
6067
  const innerY = pos.y + padding;
@@ -6074,7 +6085,7 @@ var SVGRenderer = class {
6074
6085
  const visibleTitle = this.truncateTextToWidth(title, titleMaxWidth, titleSize);
6075
6086
  const visibleCaption = hasCaption ? this.truncateTextToWidth(caption, Math.max(20, innerWidth), captionSize) : "";
6076
6087
  let svg = `<g${this.getDataNodeId(node)}>
6077
- <!-- StatCard Background -->
6088
+ <!-- Stat Background -->
6078
6089
  <rect x="${pos.x}" y="${pos.y}"
6079
6090
  width="${pos.width}" height="${pos.height}"
6080
6091
  rx="8"
@@ -7337,9 +7348,9 @@ var SkeletonSVGRenderer = class extends SVGRenderer {
7337
7348
  return svg;
7338
7349
  }
7339
7350
  /**
7340
- * Render StatCard with gray blocks instead of values
7351
+ * Render Stat with gray blocks instead of values
7341
7352
  */
7342
- renderStatCard(node, pos) {
7353
+ renderStat(node, pos) {
7343
7354
  const hasIcon = String(node.props.icon || "").trim().length > 0;
7344
7355
  const hasCaption = String(node.props.caption || "").trim().length > 0;
7345
7356
  const iconSize = 20;
@@ -8466,7 +8477,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
8466
8477
  /**
8467
8478
  * Render stat card with sketch filter and Comic Sans
8468
8479
  */
8469
- renderStatCard(node, pos) {
8480
+ renderStat(node, pos) {
8470
8481
  const title = String(node.props.title || "Metric");
8471
8482
  const value = String(node.props.value || "0");
8472
8483
  const rawCaption = String(node.props.caption || "");
@@ -8474,7 +8485,9 @@ var SketchSVGRenderer = class extends SVGRenderer {
8474
8485
  const hasCaption = caption.trim().length > 0;
8475
8486
  const iconName = String(node.props.icon || "").trim();
8476
8487
  const iconSvg = iconName ? getIcon(iconName) : null;
8477
- const accentColor = this.resolveAccentColor();
8488
+ const variant = String(node.props.variant || "default");
8489
+ const baseAccent = this.resolveAccentColor();
8490
+ const accentColor = variant !== "default" ? this.resolveVariantColor(variant, baseAccent) : baseAccent;
8478
8491
  const padding = this.resolveSpacing(node.style.padding);
8479
8492
  const innerX = pos.x + padding;
8480
8493
  const innerY = pos.y + padding;
@@ -8497,7 +8510,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
8497
8510
  const visibleTitle = this.truncateTextToWidth(title, titleMaxWidth, titleSize);
8498
8511
  const visibleCaption = hasCaption ? this.truncateTextToWidth(caption, Math.max(20, pos.width - padding * 2), captionSize) : "";
8499
8512
  let svg = `<g${this.getDataNodeId(node)}>
8500
- <!-- StatCard Background -->
8513
+ <!-- Stat Background -->
8501
8514
  <rect x="${pos.x}" y="${pos.y}"
8502
8515
  width="${pos.width}" height="${pos.height}"
8503
8516
  rx="8"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wire-dsl/engine",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "WireDSL engine - Parser, IR generator, layout engine, and SVG renderer (browser-safe, pure JS/TS)",
5
5
  "type": "module",
6
6
  "exports": {
@@ -32,7 +32,7 @@
32
32
  "dependencies": {
33
33
  "chevrotain": "11.1.1",
34
34
  "zod": "4.3.6",
35
- "@wire-dsl/language-support": "0.4.0"
35
+ "@wire-dsl/language-support": "0.5.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "25.2.0",