@wire-dsl/engine 0.5.0 → 0.7.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 +1 -1
- package/dist/index.cjs +1013 -164
- package/dist/index.d.cts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +1013 -164
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2174,8 +2174,8 @@ var IRStyleSchema = z.object({
|
|
|
2174
2174
|
var IRNodeStyleSchema = z.object({
|
|
2175
2175
|
padding: z.string().optional(),
|
|
2176
2176
|
gap: z.string().optional(),
|
|
2177
|
-
|
|
2178
|
-
|
|
2177
|
+
justify: z.enum(["start", "center", "end", "stretch", "spaceBetween", "spaceAround"]).optional(),
|
|
2178
|
+
align: z.enum(["start", "center", "end"]).optional(),
|
|
2179
2179
|
background: z.string().optional()
|
|
2180
2180
|
});
|
|
2181
2181
|
var IRMetaSchema = z.object({
|
|
@@ -2461,6 +2461,9 @@ ${messages}`);
|
|
|
2461
2461
|
if (layoutParams.gap !== void 0) {
|
|
2462
2462
|
style.gap = String(layoutParams.gap);
|
|
2463
2463
|
}
|
|
2464
|
+
if (layoutParams.justify !== void 0) {
|
|
2465
|
+
style.justify = layoutParams.justify;
|
|
2466
|
+
}
|
|
2464
2467
|
if (layoutParams.align !== void 0) {
|
|
2465
2468
|
style.align = layoutParams.align;
|
|
2466
2469
|
}
|
|
@@ -2540,13 +2543,13 @@ ${messages}`);
|
|
|
2540
2543
|
"Heading",
|
|
2541
2544
|
"Text",
|
|
2542
2545
|
"Label",
|
|
2546
|
+
"Paragraph",
|
|
2543
2547
|
"Image",
|
|
2544
2548
|
"Card",
|
|
2545
2549
|
"Stat",
|
|
2546
2550
|
"Topbar",
|
|
2547
2551
|
"Table",
|
|
2548
2552
|
"Chart",
|
|
2549
|
-
"ChartPlaceholder",
|
|
2550
2553
|
"Textarea",
|
|
2551
2554
|
"Select",
|
|
2552
2555
|
"Checkbox",
|
|
@@ -2881,19 +2884,19 @@ var ICON_SIZES_BY_DENSITY = {
|
|
|
2881
2884
|
comfortable: { xs: 14, sm: 16, md: 20, lg: 28, xl: 36 }
|
|
2882
2885
|
};
|
|
2883
2886
|
var ICON_BUTTON_SIZES_BY_DENSITY = {
|
|
2884
|
-
compact: { sm: 20, md: 24, lg: 32 },
|
|
2885
|
-
normal: { sm: 24, md: 32, lg: 40 },
|
|
2886
|
-
comfortable: { sm: 28, md: 40, lg: 48 }
|
|
2887
|
+
compact: { xs: 16, sm: 20, md: 24, lg: 32, xl: 40 },
|
|
2888
|
+
normal: { xs: 20, sm: 24, md: 32, lg: 40, xl: 48 },
|
|
2889
|
+
comfortable: { xs: 24, sm: 28, md: 40, lg: 48, xl: 56 }
|
|
2887
2890
|
};
|
|
2888
2891
|
var CONTROL_HEIGHTS_BY_DENSITY = {
|
|
2889
|
-
compact: { sm: 28, md: 32, lg: 36 },
|
|
2890
|
-
normal: { sm: 36, md: 40, lg: 48 },
|
|
2891
|
-
comfortable: { sm: 40, md: 48, lg: 56 }
|
|
2892
|
+
compact: { xs: 24, sm: 28, md: 32, lg: 36, xl: 44 },
|
|
2893
|
+
normal: { xs: 28, sm: 36, md: 40, lg: 48, xl: 56 },
|
|
2894
|
+
comfortable: { xs: 32, sm: 40, md: 48, lg: 56, xl: 64 }
|
|
2892
2895
|
};
|
|
2893
2896
|
var ACTION_CONTROL_HEIGHTS_BY_DENSITY = {
|
|
2894
|
-
compact: { sm:
|
|
2895
|
-
normal: { sm:
|
|
2896
|
-
comfortable: { sm:
|
|
2897
|
+
compact: { xs: 24, sm: 28, md: 32, lg: 36, xl: 44 },
|
|
2898
|
+
normal: { xs: 28, sm: 36, md: 40, lg: 48, xl: 56 },
|
|
2899
|
+
comfortable: { xs: 32, sm: 40, md: 48, lg: 56, xl: 64 }
|
|
2897
2900
|
};
|
|
2898
2901
|
var CONTROL_PADDING_BY_DENSITY = {
|
|
2899
2902
|
compact: { none: 0, xs: 4, sm: 8, md: 10, lg: 14, xl: 18 },
|
|
@@ -3107,8 +3110,9 @@ var LayoutEngine = class {
|
|
|
3107
3110
|
}
|
|
3108
3111
|
});
|
|
3109
3112
|
} else {
|
|
3110
|
-
const
|
|
3111
|
-
|
|
3113
|
+
const justify = node.style.justify || "stretch";
|
|
3114
|
+
const crossAlign = node.style.align || "start";
|
|
3115
|
+
if (justify === "stretch") {
|
|
3112
3116
|
let currentX = x;
|
|
3113
3117
|
const childWidth = this.calculateChildWidth(children.length, width, gap);
|
|
3114
3118
|
let stackHeight = 0;
|
|
@@ -3130,43 +3134,44 @@ var LayoutEngine = class {
|
|
|
3130
3134
|
});
|
|
3131
3135
|
} else {
|
|
3132
3136
|
const childWidths = [];
|
|
3137
|
+
const childHeights = [];
|
|
3133
3138
|
const explicitHeightFlags = [];
|
|
3134
|
-
const
|
|
3139
|
+
const flexIndices = /* @__PURE__ */ new Set();
|
|
3135
3140
|
let stackHeight = 0;
|
|
3136
3141
|
children.forEach((childRef, index) => {
|
|
3137
3142
|
const childNode = this.nodes[childRef.ref];
|
|
3138
|
-
let childWidth = this.getIntrinsicComponentWidth(childNode);
|
|
3139
|
-
let childHeight = this.getComponentHeight();
|
|
3140
3143
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3141
3144
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3142
3145
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
3143
|
-
|
|
3146
|
+
const isFlexContainer = !hasExplicitWidth && childNode?.kind === "container" && !this.containerHasIntrinsicWidth(childNode);
|
|
3147
|
+
let childWidth;
|
|
3148
|
+
if (isBlockButton || isFlexContainer) {
|
|
3144
3149
|
childWidth = 0;
|
|
3145
|
-
|
|
3150
|
+
flexIndices.add(index);
|
|
3146
3151
|
} else if (hasExplicitWidth) {
|
|
3147
3152
|
childWidth = Number(childNode.props.width);
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
childHeight = Number(childNode.props.height);
|
|
3153
|
+
} else {
|
|
3154
|
+
childWidth = this.getIntrinsicWidth(childNode, width);
|
|
3151
3155
|
}
|
|
3152
3156
|
childWidths.push(childWidth);
|
|
3157
|
+
childHeights.push(this.getComponentHeight());
|
|
3153
3158
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3154
3159
|
});
|
|
3155
3160
|
const totalGapWidth = gap * Math.max(0, children.length - 1);
|
|
3156
|
-
if (
|
|
3161
|
+
if (flexIndices.size > 0) {
|
|
3157
3162
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3158
|
-
return
|
|
3163
|
+
return flexIndices.has(idx) ? sum : sum + w;
|
|
3159
3164
|
}, 0);
|
|
3160
3165
|
const remainingWidth = width - totalGapWidth - fixedWidth;
|
|
3161
|
-
const
|
|
3162
|
-
|
|
3163
|
-
childWidths[
|
|
3166
|
+
const widthPerFlex = Math.max(1, remainingWidth / flexIndices.size);
|
|
3167
|
+
flexIndices.forEach((idx) => {
|
|
3168
|
+
childWidths[idx] = widthPerFlex;
|
|
3164
3169
|
});
|
|
3165
3170
|
}
|
|
3166
3171
|
children.forEach((childRef, index) => {
|
|
3167
3172
|
const childNode = this.nodes[childRef.ref];
|
|
3168
|
-
let childHeight = this.getComponentHeight();
|
|
3169
3173
|
const childWidth = childWidths[index];
|
|
3174
|
+
let childHeight = this.getComponentHeight();
|
|
3170
3175
|
if (explicitHeightFlags[index] && childNode?.kind === "component") {
|
|
3171
3176
|
childHeight = Number(childNode.props.height);
|
|
3172
3177
|
} else if (childNode?.kind === "container") {
|
|
@@ -3174,21 +3179,37 @@ var LayoutEngine = class {
|
|
|
3174
3179
|
} else if (childNode?.kind === "component") {
|
|
3175
3180
|
childHeight = this.getIntrinsicComponentHeight(childNode, childWidth);
|
|
3176
3181
|
}
|
|
3182
|
+
childHeights[index] = childHeight;
|
|
3177
3183
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3178
3184
|
});
|
|
3179
3185
|
const totalChildWidth = childWidths.reduce((sum, w) => sum + w, 0);
|
|
3180
3186
|
const totalContentWidth = totalChildWidth + totalGapWidth;
|
|
3181
3187
|
let startX = x;
|
|
3182
|
-
|
|
3188
|
+
let dynamicGap = gap;
|
|
3189
|
+
if (justify === "center") {
|
|
3183
3190
|
startX = x + (width - totalContentWidth) / 2;
|
|
3184
|
-
} else if (
|
|
3191
|
+
} else if (justify === "end") {
|
|
3185
3192
|
startX = x + width - totalContentWidth;
|
|
3193
|
+
} else if (justify === "spaceBetween") {
|
|
3194
|
+
startX = x;
|
|
3195
|
+
dynamicGap = children.length > 1 ? (width - totalChildWidth) / (children.length - 1) : 0;
|
|
3196
|
+
} else if (justify === "spaceAround") {
|
|
3197
|
+
const spacing = children.length > 0 ? (width - totalChildWidth) / children.length : 0;
|
|
3198
|
+
startX = x + spacing / 2;
|
|
3199
|
+
dynamicGap = spacing;
|
|
3186
3200
|
}
|
|
3187
3201
|
let currentX = startX;
|
|
3188
3202
|
children.forEach((childRef, index) => {
|
|
3189
3203
|
const childWidth = childWidths[index];
|
|
3190
|
-
|
|
3191
|
-
|
|
3204
|
+
const childHeight = childHeights[index];
|
|
3205
|
+
let childY = y;
|
|
3206
|
+
if (crossAlign === "center") {
|
|
3207
|
+
childY = y + Math.round((stackHeight - childHeight) / 2);
|
|
3208
|
+
} else if (crossAlign === "end") {
|
|
3209
|
+
childY = y + stackHeight - childHeight;
|
|
3210
|
+
}
|
|
3211
|
+
this.calculateNode(childRef.ref, currentX, childY, childWidth, childHeight, "stack");
|
|
3212
|
+
currentX += childWidth + dynamicGap;
|
|
3192
3213
|
});
|
|
3193
3214
|
}
|
|
3194
3215
|
}
|
|
@@ -3548,7 +3569,7 @@ var LayoutEngine = class {
|
|
|
3548
3569
|
wrapTextToLines(text, maxWidth, fontSize) {
|
|
3549
3570
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
3550
3571
|
const paragraphs = normalized.split("\n");
|
|
3551
|
-
const charWidth = fontSize * 0.
|
|
3572
|
+
const charWidth = fontSize * 0.5;
|
|
3552
3573
|
const safeWidth = Math.max(maxWidth, charWidth);
|
|
3553
3574
|
const maxCharsPerLine = Math.max(1, Math.floor(safeWidth / charWidth));
|
|
3554
3575
|
const lines = [];
|
|
@@ -3586,12 +3607,14 @@ var LayoutEngine = class {
|
|
|
3586
3607
|
getIntrinsicComponentHeight(node, availableWidth) {
|
|
3587
3608
|
if (node.kind !== "component") return this.getComponentHeight();
|
|
3588
3609
|
const controlSize = String(node.props.size || "md");
|
|
3610
|
+
const actionControlSize = String(node.props.size || "sm");
|
|
3589
3611
|
const density = this.style.density || "normal";
|
|
3590
3612
|
const inputControlHeight = resolveControlHeight(controlSize, density);
|
|
3591
|
-
const actionControlHeight = resolveActionControlHeight(
|
|
3613
|
+
const actionControlHeight = resolveActionControlHeight(actionControlSize, density);
|
|
3592
3614
|
const controlLabelOffset = node.componentType === "Input" || node.componentType === "Textarea" || node.componentType === "Select" ? this.getControlLabelOffset(String(node.props.label || "")) : (node.componentType === "Button" || node.componentType === "IconButton") && this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
3593
3615
|
if (node.componentType === "Image") {
|
|
3594
3616
|
const placeholder = String(node.props.placeholder || "landscape");
|
|
3617
|
+
const isCircle = this.parseBooleanProp(node.props.circle, false);
|
|
3595
3618
|
const aspectRatios = {
|
|
3596
3619
|
landscape: 16 / 9,
|
|
3597
3620
|
portrait: 2 / 3,
|
|
@@ -3599,7 +3622,7 @@ var LayoutEngine = class {
|
|
|
3599
3622
|
icon: 1,
|
|
3600
3623
|
avatar: 1
|
|
3601
3624
|
};
|
|
3602
|
-
const ratio = aspectRatios[placeholder] || 16 / 9;
|
|
3625
|
+
const ratio = isCircle ? 1 : aspectRatios[placeholder] || 16 / 9;
|
|
3603
3626
|
const explicitHeight = Number(node.props.height);
|
|
3604
3627
|
if (!isNaN(explicitHeight) && explicitHeight > 0) {
|
|
3605
3628
|
return explicitHeight;
|
|
@@ -3651,13 +3674,19 @@ var LayoutEngine = class {
|
|
|
3651
3674
|
}
|
|
3652
3675
|
return Math.max(1, Math.ceil(wrappedHeight + verticalPadding * 2));
|
|
3653
3676
|
}
|
|
3654
|
-
if (node.componentType === "Text") {
|
|
3677
|
+
if (node.componentType === "Text" || node.componentType === "Paragraph") {
|
|
3655
3678
|
const content = String(node.props.text || "");
|
|
3656
|
-
const { fontSize, lineHeight } = this.getTextMetricsForDensity();
|
|
3679
|
+
const { fontSize: defaultFontSize, lineHeight } = this.getTextMetricsForDensity();
|
|
3680
|
+
const sizeProp = String(node.props.size || "");
|
|
3681
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
3682
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
3657
3683
|
const lineHeightPx = Math.ceil(fontSize * lineHeight);
|
|
3658
3684
|
const maxWidth = availableWidth && availableWidth > 0 ? availableWidth : 200;
|
|
3659
3685
|
const lines = this.wrapTextToLines(content, maxWidth, fontSize);
|
|
3660
3686
|
const wrappedHeight = Math.max(1, lines.length) * lineHeightPx;
|
|
3687
|
+
if (sizeProp) {
|
|
3688
|
+
return wrappedHeight;
|
|
3689
|
+
}
|
|
3661
3690
|
return Math.max(this.getComponentHeight(), wrappedHeight);
|
|
3662
3691
|
}
|
|
3663
3692
|
if (node.componentType === "Alert") {
|
|
@@ -3686,7 +3715,7 @@ var LayoutEngine = class {
|
|
|
3686
3715
|
if (node.componentType === "Modal") return 300;
|
|
3687
3716
|
if (node.componentType === "Card") return 120;
|
|
3688
3717
|
if (node.componentType === "Stat") return 120;
|
|
3689
|
-
if (node.componentType === "Chart"
|
|
3718
|
+
if (node.componentType === "Chart") return 250;
|
|
3690
3719
|
if (node.componentType === "List") {
|
|
3691
3720
|
const itemsFromProps = String(node.props.items || "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
3692
3721
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
@@ -3697,7 +3726,14 @@ var LayoutEngine = class {
|
|
|
3697
3726
|
const contentHeight = titleHeight + itemCount * itemHeight;
|
|
3698
3727
|
return Math.max(this.getComponentHeight(), contentHeight);
|
|
3699
3728
|
}
|
|
3700
|
-
if (node.componentType === "Topbar")
|
|
3729
|
+
if (node.componentType === "Topbar") {
|
|
3730
|
+
const TOPBAR_HEIGHTS = { sm: 44, md: 56, lg: 72 };
|
|
3731
|
+
return TOPBAR_HEIGHTS[String(node.props.size || "md")] ?? 56;
|
|
3732
|
+
}
|
|
3733
|
+
if (node.componentType === "Tabs") {
|
|
3734
|
+
const TABS_HEIGHTS = { sm: 32, md: 44, lg: 52 };
|
|
3735
|
+
return TABS_HEIGHTS[String(node.props.size || "md")] ?? 44;
|
|
3736
|
+
}
|
|
3701
3737
|
if (node.componentType === "Divider") return 1;
|
|
3702
3738
|
if (node.componentType === "Separate") return this.getSeparateSize(node);
|
|
3703
3739
|
if (node.componentType === "Input" || node.componentType === "Select") {
|
|
@@ -3706,11 +3742,57 @@ var LayoutEngine = class {
|
|
|
3706
3742
|
if (node.componentType === "Button" || node.componentType === "IconButton" || node.componentType === "Link") {
|
|
3707
3743
|
return actionControlHeight + controlLabelOffset;
|
|
3708
3744
|
}
|
|
3745
|
+
if (node.componentType === "Badge" || node.componentType === "Chip") {
|
|
3746
|
+
const BADGE_HEIGHTS = { xs: 28, sm: 36, md: 40, lg: 48, xl: 56 };
|
|
3747
|
+
const badgeSize = String(node.props.size || "md");
|
|
3748
|
+
return BADGE_HEIGHTS[badgeSize] ?? 40;
|
|
3749
|
+
}
|
|
3709
3750
|
return this.getComponentHeight();
|
|
3710
3751
|
}
|
|
3711
3752
|
getControlLabelOffset(label) {
|
|
3712
3753
|
return label.trim().length > 0 ? 18 : 0;
|
|
3713
3754
|
}
|
|
3755
|
+
/**
|
|
3756
|
+
* Returns true when a container's width can be calculated from its children
|
|
3757
|
+
* (i.e. it is a horizontal non-stretch stack). False means the container
|
|
3758
|
+
* behaves like `flex-grow:1` and should absorb remaining space.
|
|
3759
|
+
*/
|
|
3760
|
+
containerHasIntrinsicWidth(node) {
|
|
3761
|
+
if (node.kind !== "container") return false;
|
|
3762
|
+
return node.containerType === "stack" && String(node.params.direction || "vertical") === "horizontal" && (node.style.justify || "stretch") !== "stretch";
|
|
3763
|
+
}
|
|
3764
|
+
/**
|
|
3765
|
+
* Returns the natural (intrinsic) width of any node — component or container.
|
|
3766
|
+
* For horizontal non-stretch containers the width is the sum of their children's
|
|
3767
|
+
* intrinsic widths plus gaps, capped at `availableWidth`. All other containers
|
|
3768
|
+
* are assumed to take the full available width (they stretch or grow).
|
|
3769
|
+
*/
|
|
3770
|
+
getIntrinsicWidth(node, availableWidth) {
|
|
3771
|
+
if (!node) return 120;
|
|
3772
|
+
if (node.kind === "component") {
|
|
3773
|
+
return this.getIntrinsicComponentWidth(node);
|
|
3774
|
+
}
|
|
3775
|
+
if (node.kind === "container") {
|
|
3776
|
+
if (this.containerHasIntrinsicWidth(node)) {
|
|
3777
|
+
const gap = this.resolveSpacing(node.style.gap);
|
|
3778
|
+
const padding = this.resolveSpacing(node.style.padding);
|
|
3779
|
+
const innerAvailable = Math.max(0, availableWidth - padding * 2);
|
|
3780
|
+
const children = node.children ?? [];
|
|
3781
|
+
let total = padding * 2;
|
|
3782
|
+
children.forEach((childRef, idx) => {
|
|
3783
|
+
const child = this.nodes[childRef.ref];
|
|
3784
|
+
total += this.getIntrinsicWidth(child, innerAvailable);
|
|
3785
|
+
if (idx < children.length - 1) total += gap;
|
|
3786
|
+
});
|
|
3787
|
+
return Math.min(total, availableWidth);
|
|
3788
|
+
}
|
|
3789
|
+
return availableWidth;
|
|
3790
|
+
}
|
|
3791
|
+
if (node.kind === "instance") {
|
|
3792
|
+
return availableWidth;
|
|
3793
|
+
}
|
|
3794
|
+
return 120;
|
|
3795
|
+
}
|
|
3714
3796
|
getIntrinsicComponentWidth(node) {
|
|
3715
3797
|
if (!node || node.kind !== "component") {
|
|
3716
3798
|
return 120;
|
|
@@ -3720,7 +3802,7 @@ var LayoutEngine = class {
|
|
|
3720
3802
|
return resolveIconSize(size, this.style.density || "normal");
|
|
3721
3803
|
}
|
|
3722
3804
|
if (node.componentType === "IconButton") {
|
|
3723
|
-
const size = String(node.props.size || "
|
|
3805
|
+
const size = String(node.props.size || "sm");
|
|
3724
3806
|
const density = this.style.density || "normal";
|
|
3725
3807
|
const baseSize = resolveIconButtonSize(size, density);
|
|
3726
3808
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
@@ -3778,7 +3860,13 @@ var LayoutEngine = class {
|
|
|
3778
3860
|
}
|
|
3779
3861
|
if (node.componentType === "Badge" || node.componentType === "Chip") {
|
|
3780
3862
|
const text = String(node.props.text || "");
|
|
3781
|
-
|
|
3863
|
+
const size = String(node.props.size || "md");
|
|
3864
|
+
const BADGE_CHAR_W = { xs: 6, sm: 6.5, md: 7, lg: 7.5, xl: 8.5 };
|
|
3865
|
+
const BADGE_PAD_X = { xs: 5, sm: 6, md: 8, lg: 10, xl: 14 };
|
|
3866
|
+
const customPadding = node.props.padding !== void 0 ? Number(node.props.padding) : void 0;
|
|
3867
|
+
const padX = customPadding !== void 0 && !isNaN(customPadding) ? customPadding : BADGE_PAD_X[size] ?? 8;
|
|
3868
|
+
const charW = BADGE_CHAR_W[size] ?? 7;
|
|
3869
|
+
return Math.max(padX * 4, text.length * charW + padX * 2);
|
|
3782
3870
|
}
|
|
3783
3871
|
return 120;
|
|
3784
3872
|
}
|
|
@@ -4062,34 +4150,358 @@ MockDataGenerator.HEADER_TO_MOCK = {
|
|
|
4062
4150
|
var ColorResolver = class {
|
|
4063
4151
|
constructor() {
|
|
4064
4152
|
this.customColors = {};
|
|
4065
|
-
// Named colors palette
|
|
4153
|
+
// Named colors palette — Full Material Design + utility aliases
|
|
4066
4154
|
this.namedColors = {
|
|
4067
|
-
//
|
|
4155
|
+
// ── Utility ──────────────────────────────────────────────────────────────
|
|
4068
4156
|
white: "#FFFFFF",
|
|
4069
4157
|
black: "#000000",
|
|
4070
|
-
gray: "#
|
|
4071
|
-
|
|
4158
|
+
gray: "#9E9E9E",
|
|
4159
|
+
// alias for grey 500
|
|
4160
|
+
grey: "#9E9E9E",
|
|
4161
|
+
slate: "#607D8B",
|
|
4162
|
+
// alias for blue_grey 500
|
|
4072
4163
|
zinc: "#71717A",
|
|
4073
|
-
//
|
|
4074
|
-
red: "#EF4444",
|
|
4075
|
-
orange: "#F97316",
|
|
4076
|
-
yellow: "#EAB308",
|
|
4077
|
-
lime: "#84CC16",
|
|
4078
|
-
green: "#22C55E",
|
|
4164
|
+
// Extra well-known non-Material names (kept for backward compat)
|
|
4079
4165
|
emerald: "#10B981",
|
|
4080
|
-
teal: "#14B8A6",
|
|
4081
|
-
cyan: "#06B6D4",
|
|
4082
|
-
blue: "#3B82F6",
|
|
4083
|
-
indigo: "#6366F1",
|
|
4084
4166
|
violet: "#8B5CF6",
|
|
4085
|
-
purple: "#A855F7",
|
|
4086
4167
|
fuchsia: "#D946EF",
|
|
4087
|
-
pink: "#EC4899",
|
|
4088
4168
|
rose: "#F43F5E",
|
|
4089
|
-
// Light variants
|
|
4090
|
-
"red-light": "#
|
|
4091
|
-
"blue-light": "#
|
|
4092
|
-
"green-light": "#
|
|
4169
|
+
// Light utility variants
|
|
4170
|
+
"red-light": "#FFCDD2",
|
|
4171
|
+
"blue-light": "#BBDEFB",
|
|
4172
|
+
"green-light": "#C8E6C9",
|
|
4173
|
+
// ── Red ──────────────────────────────────────────────────────────────────
|
|
4174
|
+
red: "#F44336",
|
|
4175
|
+
// 500
|
|
4176
|
+
red_50: "#FFEBEE",
|
|
4177
|
+
red_100: "#FFCDD2",
|
|
4178
|
+
red_200: "#EF9A9A",
|
|
4179
|
+
red_300: "#E57373",
|
|
4180
|
+
red_400: "#EF5350",
|
|
4181
|
+
red_500: "#F44336",
|
|
4182
|
+
red_600: "#E53935",
|
|
4183
|
+
red_700: "#D32F2F",
|
|
4184
|
+
red_800: "#C62828",
|
|
4185
|
+
red_900: "#B71C1C",
|
|
4186
|
+
red_A100: "#FF8A80",
|
|
4187
|
+
red_A200: "#FF5252",
|
|
4188
|
+
red_A400: "#FF1744",
|
|
4189
|
+
red_A700: "#D50000",
|
|
4190
|
+
// ── Pink ─────────────────────────────────────────────────────────────────
|
|
4191
|
+
pink: "#E91E63",
|
|
4192
|
+
// 500
|
|
4193
|
+
pink_50: "#FCE4EC",
|
|
4194
|
+
pink_100: "#F8BBD0",
|
|
4195
|
+
pink_200: "#F48FB1",
|
|
4196
|
+
pink_300: "#F06292",
|
|
4197
|
+
pink_400: "#EC407A",
|
|
4198
|
+
pink_500: "#E91E63",
|
|
4199
|
+
pink_600: "#D81B60",
|
|
4200
|
+
pink_700: "#C2185B",
|
|
4201
|
+
pink_800: "#AD1457",
|
|
4202
|
+
pink_900: "#880E4F",
|
|
4203
|
+
pink_A100: "#FF80AB",
|
|
4204
|
+
pink_A200: "#FF4081",
|
|
4205
|
+
pink_A400: "#F50057",
|
|
4206
|
+
pink_A700: "#C51162",
|
|
4207
|
+
// ── Purple ───────────────────────────────────────────────────────────────
|
|
4208
|
+
purple: "#9C27B0",
|
|
4209
|
+
// 500
|
|
4210
|
+
purple_50: "#F3E5F5",
|
|
4211
|
+
purple_100: "#E1BEE7",
|
|
4212
|
+
purple_200: "#CE93D8",
|
|
4213
|
+
purple_300: "#BA68C8",
|
|
4214
|
+
purple_400: "#AB47BC",
|
|
4215
|
+
purple_500: "#9C27B0",
|
|
4216
|
+
purple_600: "#8E24AA",
|
|
4217
|
+
purple_700: "#7B1FA2",
|
|
4218
|
+
purple_800: "#6A1B9A",
|
|
4219
|
+
purple_900: "#4A148C",
|
|
4220
|
+
purple_A100: "#EA80FC",
|
|
4221
|
+
purple_A200: "#E040FB",
|
|
4222
|
+
purple_A400: "#D500F9",
|
|
4223
|
+
purple_A700: "#AA00FF",
|
|
4224
|
+
// ── Deep Purple ──────────────────────────────────────────────────────────
|
|
4225
|
+
deep_purple: "#673AB7",
|
|
4226
|
+
// 500
|
|
4227
|
+
deep_purple_50: "#EDE7F6",
|
|
4228
|
+
deep_purple_100: "#D1C4E9",
|
|
4229
|
+
deep_purple_200: "#B39DDB",
|
|
4230
|
+
deep_purple_300: "#9575CD",
|
|
4231
|
+
deep_purple_400: "#7E57C2",
|
|
4232
|
+
deep_purple_500: "#673AB7",
|
|
4233
|
+
deep_purple_600: "#5E35B1",
|
|
4234
|
+
deep_purple_700: "#512DA8",
|
|
4235
|
+
deep_purple_800: "#4527A0",
|
|
4236
|
+
deep_purple_900: "#311B92",
|
|
4237
|
+
deep_purple_A100: "#B388FF",
|
|
4238
|
+
deep_purple_A200: "#7C4DFF",
|
|
4239
|
+
deep_purple_A400: "#651FFF",
|
|
4240
|
+
deep_purple_A700: "#6200EA",
|
|
4241
|
+
// ── Indigo ───────────────────────────────────────────────────────────────
|
|
4242
|
+
indigo: "#3F51B5",
|
|
4243
|
+
// 500
|
|
4244
|
+
indigo_50: "#E8EAF6",
|
|
4245
|
+
indigo_100: "#C5CAE9",
|
|
4246
|
+
indigo_200: "#9FA8DA",
|
|
4247
|
+
indigo_300: "#7986CB",
|
|
4248
|
+
indigo_400: "#5C6BC0",
|
|
4249
|
+
indigo_500: "#3F51B5",
|
|
4250
|
+
indigo_600: "#3949AB",
|
|
4251
|
+
indigo_700: "#303F9F",
|
|
4252
|
+
indigo_800: "#283593",
|
|
4253
|
+
indigo_900: "#1A237E",
|
|
4254
|
+
indigo_A100: "#8C9EFF",
|
|
4255
|
+
indigo_A200: "#536DFE",
|
|
4256
|
+
indigo_A400: "#3D5AFE",
|
|
4257
|
+
indigo_A700: "#304FFE",
|
|
4258
|
+
// ── Blue ─────────────────────────────────────────────────────────────────
|
|
4259
|
+
blue: "#2196F3",
|
|
4260
|
+
// 500
|
|
4261
|
+
blue_50: "#E3F2FD",
|
|
4262
|
+
blue_100: "#BBDEFB",
|
|
4263
|
+
blue_200: "#90CAF9",
|
|
4264
|
+
blue_300: "#64B5F6",
|
|
4265
|
+
blue_400: "#42A5F5",
|
|
4266
|
+
blue_500: "#2196F3",
|
|
4267
|
+
blue_600: "#1E88E5",
|
|
4268
|
+
blue_700: "#1976D2",
|
|
4269
|
+
blue_800: "#1565C0",
|
|
4270
|
+
blue_900: "#0D47A1",
|
|
4271
|
+
blue_A100: "#82B1FF",
|
|
4272
|
+
blue_A200: "#448AFF",
|
|
4273
|
+
blue_A400: "#2979FF",
|
|
4274
|
+
blue_A700: "#2962FF",
|
|
4275
|
+
// ── Light Blue ───────────────────────────────────────────────────────────
|
|
4276
|
+
light_blue: "#03A9F4",
|
|
4277
|
+
// 500
|
|
4278
|
+
light_blue_50: "#E1F5FE",
|
|
4279
|
+
light_blue_100: "#B3E5FC",
|
|
4280
|
+
light_blue_200: "#81D4FA",
|
|
4281
|
+
light_blue_300: "#4FC3F7",
|
|
4282
|
+
light_blue_400: "#29B6F6",
|
|
4283
|
+
light_blue_500: "#03A9F4",
|
|
4284
|
+
light_blue_600: "#039BE5",
|
|
4285
|
+
light_blue_700: "#0288D1",
|
|
4286
|
+
light_blue_800: "#0277BD",
|
|
4287
|
+
light_blue_900: "#01579B",
|
|
4288
|
+
light_blue_A100: "#80D8FF",
|
|
4289
|
+
light_blue_A200: "#40C4FF",
|
|
4290
|
+
light_blue_A400: "#00B0FF",
|
|
4291
|
+
light_blue_A700: "#0091EA",
|
|
4292
|
+
// ── Cyan ─────────────────────────────────────────────────────────────────
|
|
4293
|
+
cyan: "#00BCD4",
|
|
4294
|
+
// 500
|
|
4295
|
+
cyan_50: "#E0F7FA",
|
|
4296
|
+
cyan_100: "#B2EBF2",
|
|
4297
|
+
cyan_200: "#80DEEA",
|
|
4298
|
+
cyan_300: "#4DD0E1",
|
|
4299
|
+
cyan_400: "#26C6DA",
|
|
4300
|
+
cyan_500: "#00BCD4",
|
|
4301
|
+
cyan_600: "#00ACC1",
|
|
4302
|
+
cyan_700: "#0097A7",
|
|
4303
|
+
cyan_800: "#00838F",
|
|
4304
|
+
cyan_900: "#006064",
|
|
4305
|
+
cyan_A100: "#84FFFF",
|
|
4306
|
+
cyan_A200: "#18FFFF",
|
|
4307
|
+
cyan_A400: "#00E5FF",
|
|
4308
|
+
cyan_A700: "#00B8D4",
|
|
4309
|
+
// ── Teal ─────────────────────────────────────────────────────────────────
|
|
4310
|
+
teal: "#009688",
|
|
4311
|
+
// 500
|
|
4312
|
+
teal_50: "#E0F2F1",
|
|
4313
|
+
teal_100: "#B2DFDB",
|
|
4314
|
+
teal_200: "#80CBC4",
|
|
4315
|
+
teal_300: "#4DB6AC",
|
|
4316
|
+
teal_400: "#26A69A",
|
|
4317
|
+
teal_500: "#009688",
|
|
4318
|
+
teal_600: "#00897B",
|
|
4319
|
+
teal_700: "#00796B",
|
|
4320
|
+
teal_800: "#00695C",
|
|
4321
|
+
teal_900: "#004D40",
|
|
4322
|
+
teal_A100: "#A7FFEB",
|
|
4323
|
+
teal_A200: "#64FFDA",
|
|
4324
|
+
teal_A400: "#1DE9B6",
|
|
4325
|
+
teal_A700: "#00BFA5",
|
|
4326
|
+
// ── Green ────────────────────────────────────────────────────────────────
|
|
4327
|
+
green: "#4CAF50",
|
|
4328
|
+
// 500
|
|
4329
|
+
green_50: "#E8F5E9",
|
|
4330
|
+
green_100: "#C8E6C9",
|
|
4331
|
+
green_200: "#A5D6A7",
|
|
4332
|
+
green_300: "#81C784",
|
|
4333
|
+
green_400: "#66BB6A",
|
|
4334
|
+
green_500: "#4CAF50",
|
|
4335
|
+
green_600: "#43A047",
|
|
4336
|
+
green_700: "#388E3C",
|
|
4337
|
+
green_800: "#2E7D32",
|
|
4338
|
+
green_900: "#1B5E20",
|
|
4339
|
+
green_A100: "#B9F6CA",
|
|
4340
|
+
green_A200: "#69F0AE",
|
|
4341
|
+
green_A400: "#00E676",
|
|
4342
|
+
green_A700: "#00C853",
|
|
4343
|
+
// ── Light Green ──────────────────────────────────────────────────────────
|
|
4344
|
+
light_green: "#8BC34A",
|
|
4345
|
+
// 500
|
|
4346
|
+
light_green_50: "#F1F8E9",
|
|
4347
|
+
light_green_100: "#DCEDC8",
|
|
4348
|
+
light_green_200: "#C5E1A5",
|
|
4349
|
+
light_green_300: "#AED581",
|
|
4350
|
+
light_green_400: "#9CCC65",
|
|
4351
|
+
light_green_500: "#8BC34A",
|
|
4352
|
+
light_green_600: "#7CB342",
|
|
4353
|
+
light_green_700: "#689F38",
|
|
4354
|
+
light_green_800: "#558B2F",
|
|
4355
|
+
light_green_900: "#33691E",
|
|
4356
|
+
light_green_A100: "#CCFF90",
|
|
4357
|
+
light_green_A200: "#B2FF59",
|
|
4358
|
+
light_green_A400: "#76FF03",
|
|
4359
|
+
light_green_A700: "#64DD17",
|
|
4360
|
+
// ── Lime ─────────────────────────────────────────────────────────────────
|
|
4361
|
+
lime: "#CDDC39",
|
|
4362
|
+
// 500
|
|
4363
|
+
lime_50: "#F9FBE7",
|
|
4364
|
+
lime_100: "#F0F4C3",
|
|
4365
|
+
lime_200: "#E6EE9C",
|
|
4366
|
+
lime_300: "#DCE775",
|
|
4367
|
+
lime_400: "#D4E157",
|
|
4368
|
+
lime_500: "#CDDC39",
|
|
4369
|
+
lime_600: "#C0CA33",
|
|
4370
|
+
lime_700: "#AFB42B",
|
|
4371
|
+
lime_800: "#9E9D24",
|
|
4372
|
+
lime_900: "#827717",
|
|
4373
|
+
lime_A100: "#F4FF81",
|
|
4374
|
+
lime_A200: "#EEFF41",
|
|
4375
|
+
lime_A400: "#C6FF00",
|
|
4376
|
+
lime_A700: "#AEEA00",
|
|
4377
|
+
// ── Yellow ───────────────────────────────────────────────────────────────
|
|
4378
|
+
yellow: "#FFEB3B",
|
|
4379
|
+
// 500
|
|
4380
|
+
yellow_50: "#FFFDE7",
|
|
4381
|
+
yellow_100: "#FFF9C4",
|
|
4382
|
+
yellow_200: "#FFF59D",
|
|
4383
|
+
yellow_300: "#FFF176",
|
|
4384
|
+
yellow_400: "#FFEE58",
|
|
4385
|
+
yellow_500: "#FFEB3B",
|
|
4386
|
+
yellow_600: "#FDD835",
|
|
4387
|
+
yellow_700: "#F9A825",
|
|
4388
|
+
yellow_800: "#F57F17",
|
|
4389
|
+
yellow_900: "#F57F17",
|
|
4390
|
+
yellow_A100: "#FFFF8D",
|
|
4391
|
+
yellow_A200: "#FFFF00",
|
|
4392
|
+
yellow_A400: "#FFEA00",
|
|
4393
|
+
yellow_A700: "#FFD600",
|
|
4394
|
+
// ── Amber ────────────────────────────────────────────────────────────────
|
|
4395
|
+
amber: "#FFC107",
|
|
4396
|
+
// 500
|
|
4397
|
+
amber_50: "#FFF8E1",
|
|
4398
|
+
amber_100: "#FFECB3",
|
|
4399
|
+
amber_200: "#FFE082",
|
|
4400
|
+
amber_300: "#FFD54F",
|
|
4401
|
+
amber_400: "#FFCA28",
|
|
4402
|
+
amber_500: "#FFC107",
|
|
4403
|
+
amber_600: "#FFB300",
|
|
4404
|
+
amber_700: "#FFA000",
|
|
4405
|
+
amber_800: "#FF8F00",
|
|
4406
|
+
amber_900: "#FF6F00",
|
|
4407
|
+
amber_A100: "#FFE57F",
|
|
4408
|
+
amber_A200: "#FFD740",
|
|
4409
|
+
amber_A400: "#FFC400",
|
|
4410
|
+
amber_A700: "#FFAB00",
|
|
4411
|
+
// ── Orange ───────────────────────────────────────────────────────────────
|
|
4412
|
+
orange: "#FF9800",
|
|
4413
|
+
// 500
|
|
4414
|
+
orange_50: "#FFF3E0",
|
|
4415
|
+
orange_100: "#FFE0B2",
|
|
4416
|
+
orange_200: "#FFCC80",
|
|
4417
|
+
orange_300: "#FFB74D",
|
|
4418
|
+
orange_400: "#FFA726",
|
|
4419
|
+
orange_500: "#FF9800",
|
|
4420
|
+
orange_600: "#FB8C00",
|
|
4421
|
+
orange_700: "#F57C00",
|
|
4422
|
+
orange_800: "#EF6C00",
|
|
4423
|
+
orange_900: "#E65100",
|
|
4424
|
+
orange_A100: "#FFD180",
|
|
4425
|
+
orange_A200: "#FFAB40",
|
|
4426
|
+
orange_A400: "#FF9100",
|
|
4427
|
+
orange_A700: "#FF6D00",
|
|
4428
|
+
// ── Deep Orange ──────────────────────────────────────────────────────────
|
|
4429
|
+
deep_orange: "#FF5722",
|
|
4430
|
+
// 500
|
|
4431
|
+
deep_orange_50: "#FBE9E7",
|
|
4432
|
+
deep_orange_100: "#FFCCBC",
|
|
4433
|
+
deep_orange_200: "#FFAB91",
|
|
4434
|
+
deep_orange_300: "#FF8A65",
|
|
4435
|
+
deep_orange_400: "#FF7043",
|
|
4436
|
+
deep_orange_500: "#FF5722",
|
|
4437
|
+
deep_orange_600: "#F4511E",
|
|
4438
|
+
deep_orange_700: "#E64A19",
|
|
4439
|
+
deep_orange_800: "#D84315",
|
|
4440
|
+
deep_orange_900: "#BF360C",
|
|
4441
|
+
deep_orange_A100: "#FF9E80",
|
|
4442
|
+
deep_orange_A200: "#FF6E40",
|
|
4443
|
+
deep_orange_A400: "#FF3D00",
|
|
4444
|
+
deep_orange_A700: "#DD2C00",
|
|
4445
|
+
// ── Brown ────────────────────────────────────────────────────────────────
|
|
4446
|
+
brown: "#795548",
|
|
4447
|
+
// 500
|
|
4448
|
+
brown_50: "#EFEBE9",
|
|
4449
|
+
brown_100: "#D7CCC8",
|
|
4450
|
+
brown_200: "#BCAAA4",
|
|
4451
|
+
brown_300: "#A1887F",
|
|
4452
|
+
brown_400: "#8D6E63",
|
|
4453
|
+
brown_500: "#795548",
|
|
4454
|
+
brown_600: "#6D4C41",
|
|
4455
|
+
brown_700: "#5D4037",
|
|
4456
|
+
brown_800: "#4E342E",
|
|
4457
|
+
brown_900: "#3E2723",
|
|
4458
|
+
// ── Grey ─────────────────────────────────────────────────────────────────
|
|
4459
|
+
grey_50: "#FAFAFA",
|
|
4460
|
+
grey_100: "#F5F5F5",
|
|
4461
|
+
grey_200: "#EEEEEE",
|
|
4462
|
+
grey_300: "#E0E0E0",
|
|
4463
|
+
grey_400: "#BDBDBD",
|
|
4464
|
+
grey_500: "#9E9E9E",
|
|
4465
|
+
grey_600: "#757575",
|
|
4466
|
+
grey_700: "#616161",
|
|
4467
|
+
grey_800: "#424242",
|
|
4468
|
+
grey_900: "#212121",
|
|
4469
|
+
// aliases
|
|
4470
|
+
gray_50: "#FAFAFA",
|
|
4471
|
+
gray_100: "#F5F5F5",
|
|
4472
|
+
gray_200: "#EEEEEE",
|
|
4473
|
+
gray_300: "#E0E0E0",
|
|
4474
|
+
gray_400: "#BDBDBD",
|
|
4475
|
+
gray_500: "#9E9E9E",
|
|
4476
|
+
gray_600: "#757575",
|
|
4477
|
+
gray_700: "#616161",
|
|
4478
|
+
gray_800: "#424242",
|
|
4479
|
+
gray_900: "#212121",
|
|
4480
|
+
// ── Blue Grey ────────────────────────────────────────────────────────────
|
|
4481
|
+
blue_grey: "#607D8B",
|
|
4482
|
+
// 500
|
|
4483
|
+
blue_grey_50: "#ECEFF1",
|
|
4484
|
+
blue_grey_100: "#CFD8DC",
|
|
4485
|
+
blue_grey_200: "#B0BEC5",
|
|
4486
|
+
blue_grey_300: "#90A4AE",
|
|
4487
|
+
blue_grey_400: "#78909C",
|
|
4488
|
+
blue_grey_500: "#607D8B",
|
|
4489
|
+
blue_grey_600: "#546E7A",
|
|
4490
|
+
blue_grey_700: "#455A64",
|
|
4491
|
+
blue_grey_800: "#37474F",
|
|
4492
|
+
blue_grey_900: "#263238",
|
|
4493
|
+
// aliases
|
|
4494
|
+
blue_gray: "#607D8B",
|
|
4495
|
+
blue_gray_50: "#ECEFF1",
|
|
4496
|
+
blue_gray_100: "#CFD8DC",
|
|
4497
|
+
blue_gray_200: "#B0BEC5",
|
|
4498
|
+
blue_gray_300: "#90A4AE",
|
|
4499
|
+
blue_gray_400: "#78909C",
|
|
4500
|
+
blue_gray_500: "#607D8B",
|
|
4501
|
+
blue_gray_600: "#546E7A",
|
|
4502
|
+
blue_gray_700: "#455A64",
|
|
4503
|
+
blue_gray_800: "#37474F",
|
|
4504
|
+
blue_gray_900: "#263238"
|
|
4093
4505
|
};
|
|
4094
4506
|
}
|
|
4095
4507
|
/**
|
|
@@ -4737,7 +5149,8 @@ var SVGRenderer = class {
|
|
|
4737
5149
|
if (node.containerType === "split") {
|
|
4738
5150
|
this.renderSplitDecoration(node, pos, containerGroup);
|
|
4739
5151
|
}
|
|
4740
|
-
|
|
5152
|
+
const isCellContainer = node.meta?.source === "cell";
|
|
5153
|
+
if (node.children.length === 0 && this.options.showDiagnostics && !isCellContainer) {
|
|
4741
5154
|
containerGroup.push(this.renderEmptyContainerDiagnostic(pos, node.containerType));
|
|
4742
5155
|
} else {
|
|
4743
5156
|
node.children.forEach((childRef) => {
|
|
@@ -4784,7 +5197,6 @@ var SVGRenderer = class {
|
|
|
4784
5197
|
case "Table":
|
|
4785
5198
|
return this.renderTable(node, pos);
|
|
4786
5199
|
case "Chart":
|
|
4787
|
-
case "ChartPlaceholder":
|
|
4788
5200
|
return this.renderChartPlaceholder(node, pos);
|
|
4789
5201
|
case "Breadcrumbs":
|
|
4790
5202
|
return this.renderBreadcrumbs(node, pos);
|
|
@@ -4793,6 +5205,8 @@ var SVGRenderer = class {
|
|
|
4793
5205
|
// Text/Content components
|
|
4794
5206
|
case "Text":
|
|
4795
5207
|
return this.renderText(node, pos);
|
|
5208
|
+
case "Paragraph":
|
|
5209
|
+
return this.renderParagraph(node, pos);
|
|
4796
5210
|
case "Label":
|
|
4797
5211
|
return this.renderLabel(node, pos);
|
|
4798
5212
|
case "Code":
|
|
@@ -4872,7 +5286,7 @@ var SVGRenderer = class {
|
|
|
4872
5286
|
renderButton(node, pos) {
|
|
4873
5287
|
const text = String(node.props.text || "Button");
|
|
4874
5288
|
const variant = String(node.props.variant || "default");
|
|
4875
|
-
const size = String(node.props.size || "
|
|
5289
|
+
const size = String(node.props.size || "sm");
|
|
4876
5290
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
4877
5291
|
const density = this.ir.project.style.density || "normal";
|
|
4878
5292
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
@@ -4947,7 +5361,7 @@ var SVGRenderer = class {
|
|
|
4947
5361
|
renderLink(node, pos) {
|
|
4948
5362
|
const text = String(node.props.text || "Link");
|
|
4949
5363
|
const variant = String(node.props.variant || "primary");
|
|
4950
|
-
const size = String(node.props.size || "
|
|
5364
|
+
const size = String(node.props.size || "sm");
|
|
4951
5365
|
const density = this.ir.project.style.density || "normal";
|
|
4952
5366
|
const fontSize = this.tokens.button.fontSize;
|
|
4953
5367
|
const fontWeight = this.tokens.button.fontWeight;
|
|
@@ -5053,7 +5467,33 @@ var SVGRenderer = class {
|
|
|
5053
5467
|
const variant = String(node.props.variant || "default");
|
|
5054
5468
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
5055
5469
|
const showBorder = this.parseBooleanProp(node.props.border, false);
|
|
5056
|
-
const
|
|
5470
|
+
const bgPropStr = String(node.props.background ?? "");
|
|
5471
|
+
let resolvedBg = null;
|
|
5472
|
+
if (bgPropStr === "true") {
|
|
5473
|
+
resolvedBg = this.renderTheme.cardBg;
|
|
5474
|
+
} else if (bgPropStr && bgPropStr !== "false") {
|
|
5475
|
+
if (bgPropStr.startsWith("#") || bgPropStr.startsWith("rgb")) {
|
|
5476
|
+
resolvedBg = bgPropStr;
|
|
5477
|
+
} else if (this.colorResolver.hasColor(bgPropStr)) {
|
|
5478
|
+
resolvedBg = this.colorResolver.resolveColor(bgPropStr, this.renderTheme.cardBg);
|
|
5479
|
+
}
|
|
5480
|
+
}
|
|
5481
|
+
const showBackground = resolvedBg !== null;
|
|
5482
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
5483
|
+
let titleColor = this.renderTheme.text;
|
|
5484
|
+
let subtitleColor = this.renderTheme.textMuted;
|
|
5485
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
5486
|
+
let resolvedTitleColor = null;
|
|
5487
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
5488
|
+
resolvedTitleColor = colorPropStr;
|
|
5489
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
5490
|
+
resolvedTitleColor = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
5491
|
+
}
|
|
5492
|
+
if (resolvedTitleColor) {
|
|
5493
|
+
titleColor = resolvedTitleColor;
|
|
5494
|
+
subtitleColor = this.hexToRgba(resolvedTitleColor, 0.65);
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5057
5497
|
const radiusMap = {
|
|
5058
5498
|
none: 0,
|
|
5059
5499
|
sm: 4,
|
|
@@ -5065,7 +5505,7 @@ var SVGRenderer = class {
|
|
|
5065
5505
|
const topbar = this.calculateTopbarLayout(node, pos, title, subtitle, actions, user);
|
|
5066
5506
|
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
5067
5507
|
if (showBorder || showBackground) {
|
|
5068
|
-
const bg =
|
|
5508
|
+
const bg = resolvedBg ?? "none";
|
|
5069
5509
|
const stroke = showBorder ? this.renderTheme.border : "none";
|
|
5070
5510
|
svg += `
|
|
5071
5511
|
<rect x="${pos.x}" y="${pos.y}"
|
|
@@ -5081,13 +5521,13 @@ var SVGRenderer = class {
|
|
|
5081
5521
|
font-family="Arial, Helvetica, sans-serif"
|
|
5082
5522
|
font-size="18"
|
|
5083
5523
|
font-weight="600"
|
|
5084
|
-
fill="${
|
|
5524
|
+
fill="${titleColor}">${this.escapeXml(topbar.visibleTitle)}</text>`;
|
|
5085
5525
|
if (topbar.hasSubtitle) {
|
|
5086
5526
|
svg += `
|
|
5087
5527
|
<text x="${topbar.textX}" y="${topbar.subtitleY}"
|
|
5088
5528
|
font-family="Arial, Helvetica, sans-serif"
|
|
5089
5529
|
font-size="13"
|
|
5090
|
-
fill="${
|
|
5530
|
+
fill="${subtitleColor}">${this.escapeXml(topbar.visibleSubtitle)}</text>`;
|
|
5091
5531
|
}
|
|
5092
5532
|
if (topbar.leftIcon) {
|
|
5093
5533
|
svg += `
|
|
@@ -5568,10 +6008,16 @@ var SVGRenderer = class {
|
|
|
5568
6008
|
// ============================================================================
|
|
5569
6009
|
renderText(node, pos) {
|
|
5570
6010
|
const text = String(node.props.text || "Text content");
|
|
5571
|
-
const
|
|
6011
|
+
const sizeProp = String(node.props.size || "");
|
|
6012
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
6013
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
6014
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
5572
6015
|
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
6016
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
6017
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
5573
6018
|
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
5574
|
-
const
|
|
6019
|
+
const totalTextHeight = lines.length * lineHeightPx;
|
|
6020
|
+
const firstLineY = pos.y + Math.round(Math.max(0, (pos.height - totalTextHeight) / 2)) + fontSize;
|
|
5575
6021
|
const tspans = lines.map(
|
|
5576
6022
|
(line, index) => `<tspan x="${pos.x}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
5577
6023
|
).join("");
|
|
@@ -5579,13 +6025,54 @@ var SVGRenderer = class {
|
|
|
5579
6025
|
<text x="${pos.x}" y="${firstLineY}"
|
|
5580
6026
|
font-family="Arial, Helvetica, sans-serif"
|
|
5581
6027
|
font-size="${fontSize}"
|
|
6028
|
+
font-weight="${bold ? "700" : "400"}"
|
|
6029
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
5582
6030
|
fill="${this.renderTheme.text}">${tspans}</text>
|
|
5583
6031
|
</g>`;
|
|
5584
6032
|
}
|
|
6033
|
+
renderParagraph(node, pos) {
|
|
6034
|
+
const text = String(node.props.text || "");
|
|
6035
|
+
const sizeProp = String(node.props.size || "");
|
|
6036
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
6037
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
6038
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
6039
|
+
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
6040
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
6041
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
6042
|
+
const align = String(node.props.align || "left");
|
|
6043
|
+
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
6044
|
+
const totalTextHeight = lines.length * lineHeightPx;
|
|
6045
|
+
const firstLineY = pos.y + Math.round(Math.max(0, (pos.height - totalTextHeight) / 2)) + fontSize;
|
|
6046
|
+
let textX;
|
|
6047
|
+
let textAnchor;
|
|
6048
|
+
if (align === "center") {
|
|
6049
|
+
textX = pos.x + pos.width / 2;
|
|
6050
|
+
textAnchor = "middle";
|
|
6051
|
+
} else if (align === "right") {
|
|
6052
|
+
textX = pos.x + pos.width;
|
|
6053
|
+
textAnchor = "end";
|
|
6054
|
+
} else {
|
|
6055
|
+
textX = pos.x;
|
|
6056
|
+
textAnchor = "start";
|
|
6057
|
+
}
|
|
6058
|
+
const tspans = lines.map(
|
|
6059
|
+
(line, index) => `<tspan x="${textX}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
6060
|
+
).join("");
|
|
6061
|
+
return `<g${this.getDataNodeId(node)}>
|
|
6062
|
+
<text x="${textX}" y="${firstLineY}"
|
|
6063
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6064
|
+
font-size="${fontSize}"
|
|
6065
|
+
font-weight="${bold ? "700" : "400"}"
|
|
6066
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
6067
|
+
fill="${this.renderTheme.text}"
|
|
6068
|
+
text-anchor="${textAnchor}">${tspans}</text>
|
|
6069
|
+
</g>`;
|
|
6070
|
+
}
|
|
5585
6071
|
renderLabel(node, pos) {
|
|
5586
6072
|
const text = String(node.props.text || "Label");
|
|
6073
|
+
const textY = pos.y + Math.round(pos.height / 2) + 4;
|
|
5587
6074
|
return `<g${this.getDataNodeId(node)}>
|
|
5588
|
-
<text x="${pos.x}" y="${
|
|
6075
|
+
<text x="${pos.x}" y="${textY}"
|
|
5589
6076
|
font-family="Arial, Helvetica, sans-serif"
|
|
5590
6077
|
font-size="12"
|
|
5591
6078
|
fill="${this.renderTheme.textMuted}">${this.escapeXml(text)}</text>
|
|
@@ -5823,34 +6310,145 @@ var SVGRenderer = class {
|
|
|
5823
6310
|
const tabs = itemsStr ? itemsStr.split(",").map((t) => t.trim()) : ["Tab 1", "Tab 2", "Tab 3"];
|
|
5824
6311
|
const activeProp = node.props.active ?? 0;
|
|
5825
6312
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
5826
|
-
const
|
|
6313
|
+
const variant = String(node.props.variant || "default");
|
|
6314
|
+
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
6315
|
+
const radiusMap = { none: 0, sm: 4, md: 6, lg: 10, full: 20 };
|
|
6316
|
+
const tabRadius = radiusMap[String(node.props.radius || "md")] ?? 6;
|
|
6317
|
+
const sizeMap = { sm: 32, md: 44, lg: 52 };
|
|
6318
|
+
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
6319
|
+
const fontSize = 13;
|
|
6320
|
+
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
6321
|
+
const iconsStr = String(node.props.icons || "");
|
|
6322
|
+
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
6323
|
+
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
6324
|
+
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
5827
6325
|
const tabWidth = pos.width / tabs.length;
|
|
5828
|
-
|
|
5829
|
-
|
|
6326
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
6327
|
+
let textColorOverride = null;
|
|
6328
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
6329
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
6330
|
+
textColorOverride = colorPropStr;
|
|
6331
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
6332
|
+
textColorOverride = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
6333
|
+
}
|
|
6334
|
+
}
|
|
6335
|
+
const activeTextColor = textColorOverride ?? (isFlat ? accentColor : "white");
|
|
6336
|
+
const inactiveTextColor = textColorOverride ? this.hexToRgba(textColorOverride, 0.55) : this.renderTheme.textMuted;
|
|
6337
|
+
const hasRadius = tabRadius > 0;
|
|
6338
|
+
const clipId = hasRadius ? `tc${String(node.meta?.nodeId ?? "").replace(/\W/g, "").slice(0, 12) || `${Math.round(Math.abs(pos.x))}x${Math.round(Math.abs(pos.y))}`}` : "";
|
|
6339
|
+
let svg = "";
|
|
6340
|
+
if (hasRadius) {
|
|
6341
|
+
svg += `<defs><clipPath id="${clipId}"><rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" rx="${tabRadius}"/></clipPath></defs>`;
|
|
6342
|
+
}
|
|
6343
|
+
svg += `<g${this.getDataNodeId(node)}>`;
|
|
6344
|
+
if (hasRadius) {
|
|
6345
|
+
svg += `<g clip-path="url(#${clipId})">`;
|
|
6346
|
+
}
|
|
6347
|
+
if (!isFlat) {
|
|
6348
|
+
svg += `
|
|
6349
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${tabHeight}"
|
|
6350
|
+
fill="${this.renderTheme.bg}"/>`;
|
|
6351
|
+
}
|
|
5830
6352
|
tabs.forEach((tab, i) => {
|
|
5831
6353
|
const tabX = pos.x + i * tabWidth;
|
|
5832
6354
|
const isActive = i === activeIndex;
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
6355
|
+
const iconName = iconList[i] || "";
|
|
6356
|
+
const tabIconSvg = iconName ? getIcon(iconName) : null;
|
|
6357
|
+
const iconSize = 14;
|
|
6358
|
+
const iconGap = 4;
|
|
6359
|
+
const charW = fontSize * 0.58;
|
|
6360
|
+
const textEst = tab.length * charW;
|
|
6361
|
+
const totalW = tabIconSvg ? iconSize + iconGap + textEst : textEst;
|
|
6362
|
+
const groupX = tabX + Math.round((tabWidth - totalW) / 2);
|
|
6363
|
+
const iconOffY = pos.y + Math.round((tabHeight - iconSize) / 2);
|
|
6364
|
+
if (isFlat) {
|
|
6365
|
+
if (isActive) {
|
|
6366
|
+
svg += `
|
|
6367
|
+
<rect x="${tabX + 6}" y="${pos.y + tabHeight - 3}"
|
|
6368
|
+
width="${tabWidth - 12}" height="3"
|
|
6369
|
+
rx="1.5"
|
|
6370
|
+
fill="${accentColor}"/>`;
|
|
6371
|
+
}
|
|
6372
|
+
if (tabIconSvg) {
|
|
6373
|
+
svg += `
|
|
6374
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
6375
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
6376
|
+
stroke="${isActive ? accentColor : this.renderTheme.textMuted}"
|
|
6377
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6378
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
6379
|
+
</svg>
|
|
6380
|
+
</g>
|
|
6381
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
6382
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6383
|
+
font-size="${fontSize}"
|
|
6384
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6385
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}">${this.escapeXml(tab)}</text>`;
|
|
6386
|
+
} else {
|
|
6387
|
+
svg += `
|
|
6388
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
6389
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6390
|
+
font-size="${fontSize}"
|
|
6391
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6392
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}"
|
|
5844
6393
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6394
|
+
}
|
|
6395
|
+
} else {
|
|
6396
|
+
svg += `
|
|
6397
|
+
<rect x="${tabX}" y="${pos.y}"
|
|
6398
|
+
width="${tabWidth}" height="${tabHeight}"
|
|
6399
|
+
rx="${!showBorder && hasRadius && isActive ? tabRadius : 0}"
|
|
6400
|
+
fill="${isActive ? accentColor : "transparent"}"
|
|
6401
|
+
${!isActive && showBorder ? `stroke="${this.renderTheme.border}" stroke-width="0.5"` : ""}/>`;
|
|
6402
|
+
if (tabIconSvg) {
|
|
6403
|
+
svg += `
|
|
6404
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
6405
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
6406
|
+
stroke="${isActive ? activeTextColor : this.renderTheme.textMuted}"
|
|
6407
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6408
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
6409
|
+
</svg>
|
|
6410
|
+
</g>
|
|
6411
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
6412
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6413
|
+
font-size="${fontSize}"
|
|
6414
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6415
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}">${this.escapeXml(tab)}</text>`;
|
|
6416
|
+
} else {
|
|
6417
|
+
svg += `
|
|
6418
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
6419
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6420
|
+
font-size="${fontSize}"
|
|
6421
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6422
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}"
|
|
6423
|
+
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
5845
6426
|
});
|
|
5846
|
-
|
|
6427
|
+
const contentY = pos.y + tabHeight;
|
|
6428
|
+
const contentH = pos.height - tabHeight;
|
|
6429
|
+
if (contentH >= 20 && !isFlat) {
|
|
6430
|
+
svg += `
|
|
5847
6431
|
<!-- Tab content area -->
|
|
5848
|
-
<rect x="${pos.x}" y="${
|
|
5849
|
-
width="${pos.width}" height="${
|
|
5850
|
-
fill="${this.renderTheme.cardBg}"
|
|
5851
|
-
stroke="${this.renderTheme.border}"
|
|
5852
|
-
|
|
6432
|
+
<rect x="${pos.x}" y="${contentY}"
|
|
6433
|
+
width="${pos.width}" height="${contentH}"
|
|
6434
|
+
fill="${this.renderTheme.cardBg}"
|
|
6435
|
+
${showBorder ? `stroke="${this.renderTheme.border}" stroke-width="1"` : ""}/>`;
|
|
6436
|
+
} else if (isFlat && showBorder) {
|
|
6437
|
+
svg += `
|
|
6438
|
+
<line x1="${pos.x}" y1="${contentY}" x2="${pos.x + pos.width}" y2="${contentY}"
|
|
6439
|
+
stroke="${this.renderTheme.border}" stroke-width="1"/>`;
|
|
6440
|
+
}
|
|
6441
|
+
if (hasRadius) {
|
|
6442
|
+
svg += `
|
|
5853
6443
|
</g>`;
|
|
6444
|
+
if (!isFlat && showBorder) {
|
|
6445
|
+
svg += `
|
|
6446
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}"
|
|
6447
|
+
rx="${tabRadius}" fill="none"
|
|
6448
|
+
stroke="${this.renderTheme.border}" stroke-width="1"/>`;
|
|
6449
|
+
}
|
|
6450
|
+
}
|
|
6451
|
+
svg += "\n </g>";
|
|
5854
6452
|
return svg;
|
|
5855
6453
|
}
|
|
5856
6454
|
renderDivider(node, pos) {
|
|
@@ -5917,15 +6515,20 @@ var SVGRenderer = class {
|
|
|
5917
6515
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
5918
6516
|
const bgColor = hasExplicitVariantColor ? this.resolveVariantColor(variant, this.renderTheme.primary) : this.renderTheme.border;
|
|
5919
6517
|
const textColor = hasExplicitVariantColor ? "white" : this.renderTheme.text;
|
|
6518
|
+
const size = String(node.props.size || "md");
|
|
6519
|
+
const BADGE_FONT_SIZES = { xs: 9, sm: 11, md: 12, lg: 13, xl: 15 };
|
|
6520
|
+
const fontSize = BADGE_FONT_SIZES[size] ?? this.tokens.badge.fontSize;
|
|
6521
|
+
const customPadding = node.props.padding !== void 0 ? Number(node.props.padding) : void 0;
|
|
6522
|
+
const BADGE_PAD_X = { xs: 5, sm: 6, md: 8, lg: 10, xl: 14 };
|
|
6523
|
+
const paddingX = customPadding !== void 0 && !isNaN(customPadding) ? customPadding : BADGE_PAD_X[size] ?? this.tokens.badge.paddingX;
|
|
5920
6524
|
const badgeRadius = this.tokens.badge.radius === "pill" ? pos.height / 2 : this.tokens.badge.radius;
|
|
5921
|
-
const fontSize = this.tokens.badge.fontSize;
|
|
5922
6525
|
return `<g${this.getDataNodeId(node)}>
|
|
5923
6526
|
<rect x="${pos.x}" y="${pos.y}"
|
|
5924
6527
|
width="${pos.width}" height="${pos.height}"
|
|
5925
6528
|
rx="${badgeRadius}"
|
|
5926
6529
|
fill="${bgColor}"
|
|
5927
6530
|
stroke="none"/>
|
|
5928
|
-
<text x="${pos.x + pos.width / 2}" y="${pos.y + pos.height / 2 +
|
|
6531
|
+
<text x="${pos.x + paddingX + (pos.width - paddingX * 2) / 2}" y="${pos.y + pos.height / 2 + fontSize * 0.35}"
|
|
5929
6532
|
font-family="Arial, Helvetica, sans-serif"
|
|
5930
6533
|
font-size="${fontSize}"
|
|
5931
6534
|
font-weight="600"
|
|
@@ -6134,11 +6737,20 @@ var SVGRenderer = class {
|
|
|
6134
6737
|
return svg;
|
|
6135
6738
|
}
|
|
6136
6739
|
renderImage(node, pos) {
|
|
6137
|
-
const placeholder = String(node.props.
|
|
6740
|
+
const placeholder = String(node.props.type || "landscape").toLowerCase();
|
|
6138
6741
|
const placeholderIcon = String(node.props.icon || "").trim();
|
|
6139
6742
|
const variant = String(node.props.variant || "").trim();
|
|
6140
6743
|
const placeholderIconSvg = placeholder === "icon" && placeholderIcon ? getIcon(placeholderIcon) : null;
|
|
6141
6744
|
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
6745
|
+
const hasExplicitHeight = node.props.height !== void 0 && !isNaN(Number(node.props.height)) && Number(node.props.height) > 0;
|
|
6746
|
+
const isCircle = this.parseBooleanProp(node.props.circle, false);
|
|
6747
|
+
const useClip = isCircle || hasExplicitHeight;
|
|
6748
|
+
const clipId = useClip ? `iclip${String(node.meta?.nodeId ?? "").replace(/\W/g, "").slice(0, 16) || `${Math.round(Math.abs(pos.x))}x${Math.round(Math.abs(pos.y))}`}` : "";
|
|
6749
|
+
const imgCx = pos.x + pos.width / 2;
|
|
6750
|
+
const imgCy = pos.y + pos.height / 2;
|
|
6751
|
+
const imgR = Math.min(pos.width, pos.height) / 2;
|
|
6752
|
+
const defsHtml = useClip ? `<defs><clipPath id="${clipId}">${isCircle ? `<circle cx="${imgCx}" cy="${imgCy}" r="${imgR}"/>` : `<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" rx="4"/>`}</clipPath></defs>` : "";
|
|
6753
|
+
const clipAttr = useClip ? ` clip-path="url(#${clipId})"` : "";
|
|
6142
6754
|
const aspectRatios = {
|
|
6143
6755
|
landscape: 16 / 9,
|
|
6144
6756
|
portrait: 2 / 3,
|
|
@@ -6147,12 +6759,24 @@ var SVGRenderer = class {
|
|
|
6147
6759
|
avatar: 1
|
|
6148
6760
|
};
|
|
6149
6761
|
const ratio = aspectRatios[placeholder] || 16 / 9;
|
|
6150
|
-
|
|
6151
|
-
let
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6762
|
+
let iconWidth;
|
|
6763
|
+
let iconHeight;
|
|
6764
|
+
if (isCircle) {
|
|
6765
|
+
const diameter = 2 * imgR;
|
|
6766
|
+
iconWidth = diameter;
|
|
6767
|
+
iconHeight = diameter / ratio;
|
|
6768
|
+
if (iconHeight < diameter) {
|
|
6769
|
+
iconHeight = diameter;
|
|
6770
|
+
iconWidth = diameter * ratio;
|
|
6771
|
+
}
|
|
6772
|
+
} else {
|
|
6773
|
+
const maxSize = Math.min(pos.width, pos.height) * 0.8;
|
|
6774
|
+
iconWidth = maxSize;
|
|
6775
|
+
iconHeight = maxSize / ratio;
|
|
6776
|
+
if (iconHeight > pos.height * 0.8) {
|
|
6777
|
+
iconHeight = pos.height * 0.8;
|
|
6778
|
+
iconWidth = iconHeight * ratio;
|
|
6779
|
+
}
|
|
6156
6780
|
}
|
|
6157
6781
|
const offsetX = pos.x + (pos.width - iconWidth) / 2;
|
|
6158
6782
|
const offsetY = pos.y + (pos.height - iconHeight) / 2;
|
|
@@ -6165,17 +6789,18 @@ var SVGRenderer = class {
|
|
|
6165
6789
|
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
6166
6790
|
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
6167
6791
|
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
6168
|
-
|
|
6792
|
+
const iconBorderShape = isCircle ? `<circle cx="${imgCx}" cy="${imgCy}" r="${imgR}" fill="none" stroke="${this.renderTheme.border}" stroke-width="1"/>` : `<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="none" stroke="${this.renderTheme.border}" stroke-width="1" rx="4"/>`;
|
|
6793
|
+
return `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
6169
6794
|
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${bgColor}" rx="4"/>
|
|
6170
6795
|
<g transform="translate(${iconOffsetX}, ${iconOffsetY})">
|
|
6171
6796
|
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6172
6797
|
${this.extractSvgContent(placeholderIconSvg)}
|
|
6173
6798
|
</svg>
|
|
6174
6799
|
</g>
|
|
6175
|
-
|
|
6800
|
+
${iconBorderShape}
|
|
6176
6801
|
</g>`;
|
|
6177
6802
|
}
|
|
6178
|
-
let svg =
|
|
6803
|
+
let svg = `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
6179
6804
|
<!-- Image Background -->
|
|
6180
6805
|
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${imageBg}"/>`;
|
|
6181
6806
|
if (["landscape", "portrait", "square"].includes(placeholder)) {
|
|
@@ -6233,10 +6858,10 @@ var SVGRenderer = class {
|
|
|
6233
6858
|
width="${personWidth * 0.6}" height="${personHeight * 0.5}"
|
|
6234
6859
|
fill="#666" rx="3"/>`;
|
|
6235
6860
|
}
|
|
6861
|
+
const borderShape = isCircle ? `<circle cx="${imgCx}" cy="${imgCy}" r="${imgR}" fill="none" stroke="${this.renderTheme.border}" stroke-width="1"/>` : `<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="none" stroke="${this.renderTheme.border}" stroke-width="1" rx="4"/>`;
|
|
6236
6862
|
svg += `
|
|
6237
6863
|
<!-- Border -->
|
|
6238
|
-
|
|
6239
|
-
fill="none" stroke="${this.renderTheme.border}" stroke-width="1" rx="4"/>
|
|
6864
|
+
${borderShape}
|
|
6240
6865
|
</g>`;
|
|
6241
6866
|
return svg;
|
|
6242
6867
|
}
|
|
@@ -6340,10 +6965,29 @@ var SVGRenderer = class {
|
|
|
6340
6965
|
</g>`;
|
|
6341
6966
|
}
|
|
6342
6967
|
const iconSize = this.getIconSize(size);
|
|
6343
|
-
const
|
|
6344
|
-
const
|
|
6968
|
+
const paddingPx = Math.max(0, Number(node.props.padding || 0));
|
|
6969
|
+
const renderedIconSize = Math.max(4, iconSize - paddingPx * 2);
|
|
6970
|
+
const offsetX = pos.x + (pos.width - renderedIconSize) / 2;
|
|
6971
|
+
const offsetY = pos.y + (pos.height - renderedIconSize) / 2;
|
|
6972
|
+
const isIconCircle = this.parseBooleanProp(node.props.circle, false);
|
|
6973
|
+
if (isIconCircle) {
|
|
6974
|
+
const cx = pos.x + pos.width / 2;
|
|
6975
|
+
const cy = pos.y + pos.height / 2;
|
|
6976
|
+
const r = Math.min(pos.width, pos.height) / 2;
|
|
6977
|
+
const iconClipId = `iconclip${String(node.meta?.nodeId ?? "").replace(/\W/g, "").slice(0, 16) || `${Math.round(Math.abs(pos.x))}x${Math.round(Math.abs(pos.y))}`}`;
|
|
6978
|
+
const bgColor = this.hexToRgba(iconColor, 0.12);
|
|
6979
|
+
return `<defs><clipPath id="${iconClipId}"><circle cx="${cx}" cy="${cy}" r="${r}"/></clipPath></defs><g${this.getDataNodeId(node)} clip-path="url(#${iconClipId})">
|
|
6980
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="${bgColor}"/>
|
|
6981
|
+
<g transform="translate(${offsetX}, ${offsetY})">
|
|
6982
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6983
|
+
${this.extractSvgContent(iconSvg)}
|
|
6984
|
+
</svg>
|
|
6985
|
+
</g>
|
|
6986
|
+
</g>
|
|
6987
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${this.hexToRgba(iconColor, 0.35)}" stroke-width="1"/>`;
|
|
6988
|
+
}
|
|
6345
6989
|
const wrappedSvg = `<g${this.getDataNodeId(node)} transform="translate(${offsetX}, ${offsetY})">
|
|
6346
|
-
<svg width="${
|
|
6990
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6347
6991
|
${this.extractSvgContent(iconSvg)}
|
|
6348
6992
|
</svg>
|
|
6349
6993
|
</g>`;
|
|
@@ -6352,7 +6996,7 @@ var SVGRenderer = class {
|
|
|
6352
6996
|
renderIconButton(node, pos) {
|
|
6353
6997
|
const iconName = String(node.props.icon || "help-circle");
|
|
6354
6998
|
const variant = String(node.props.variant || "default");
|
|
6355
|
-
const size = String(node.props.size || "
|
|
6999
|
+
const size = String(node.props.size || "sm");
|
|
6356
7000
|
const disabled = String(node.props.disabled || "false") === "true";
|
|
6357
7001
|
const density = this.ir.project.style.density || "normal";
|
|
6358
7002
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
@@ -6480,7 +7124,7 @@ var SVGRenderer = class {
|
|
|
6480
7124
|
wrapTextToLines(text, maxWidth, fontSize) {
|
|
6481
7125
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
6482
7126
|
const paragraphs = normalized.split("\n");
|
|
6483
|
-
const charWidth = fontSize * 0.
|
|
7127
|
+
const charWidth = fontSize * 0.5;
|
|
6484
7128
|
const safeWidth = Math.max(maxWidth || 0, charWidth);
|
|
6485
7129
|
const maxCharsPerLine = Math.max(1, Math.floor(safeWidth / charWidth));
|
|
6486
7130
|
const lines = [];
|
|
@@ -6722,8 +7366,8 @@ var SVGRenderer = class {
|
|
|
6722
7366
|
return false;
|
|
6723
7367
|
}
|
|
6724
7368
|
const direction = String(parent.params.direction || "vertical");
|
|
6725
|
-
const
|
|
6726
|
-
return direction === "horizontal" &&
|
|
7369
|
+
const justify = parent.style.justify || "stretch";
|
|
7370
|
+
return direction === "horizontal" && justify === "stretch";
|
|
6727
7371
|
}
|
|
6728
7372
|
buildParentContainerIndex() {
|
|
6729
7373
|
this.parentContainerByChildId.clear();
|
|
@@ -7595,7 +8239,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7595
8239
|
renderButton(node, pos) {
|
|
7596
8240
|
const text = String(node.props.text || "Button");
|
|
7597
8241
|
const variant = String(node.props.variant || "default");
|
|
7598
|
-
const size = String(node.props.size || "
|
|
8242
|
+
const size = String(node.props.size || "sm");
|
|
7599
8243
|
const density = this.ir.project.style.density || "normal";
|
|
7600
8244
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
7601
8245
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
@@ -7702,7 +8346,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7702
8346
|
renderIconButton(node, pos) {
|
|
7703
8347
|
const iconName = String(node.props.icon || "help-circle");
|
|
7704
8348
|
const variant = String(node.props.variant || "default");
|
|
7705
|
-
const size = String(node.props.size || "
|
|
8349
|
+
const size = String(node.props.size || "sm");
|
|
7706
8350
|
const density = this.ir.project.style.density || "normal";
|
|
7707
8351
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
7708
8352
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
@@ -7955,26 +8599,58 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7955
8599
|
const variant = String(node.props.variant || "default");
|
|
7956
8600
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
7957
8601
|
const topbar = this.calculateTopbarLayout(node, pos, title, subtitle, actions, user);
|
|
7958
|
-
|
|
8602
|
+
const bgPropStr = String(node.props.background ?? "");
|
|
8603
|
+
let sketchTopbarBg = this.renderTheme.cardBg;
|
|
8604
|
+
if (bgPropStr && bgPropStr !== "false" && bgPropStr !== "true") {
|
|
8605
|
+
if (bgPropStr.startsWith("#") || bgPropStr.startsWith("rgb")) {
|
|
8606
|
+
sketchTopbarBg = bgPropStr;
|
|
8607
|
+
} else if (this.colorResolver.hasColor(bgPropStr)) {
|
|
8608
|
+
sketchTopbarBg = this.colorResolver.resolveColor(bgPropStr, this.renderTheme.cardBg);
|
|
8609
|
+
}
|
|
8610
|
+
}
|
|
8611
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
8612
|
+
let titleColor = this.renderTheme.text;
|
|
8613
|
+
let subtitleColor = this.renderTheme.textMuted;
|
|
8614
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
8615
|
+
let resolvedTitleColor = null;
|
|
8616
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
8617
|
+
resolvedTitleColor = colorPropStr;
|
|
8618
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
8619
|
+
resolvedTitleColor = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
8620
|
+
}
|
|
8621
|
+
if (resolvedTitleColor) {
|
|
8622
|
+
titleColor = resolvedTitleColor;
|
|
8623
|
+
subtitleColor = this.hexToRgba(resolvedTitleColor, 0.65);
|
|
8624
|
+
}
|
|
8625
|
+
}
|
|
8626
|
+
const showBorder = this.parseBooleanProp(node.props.border, false);
|
|
8627
|
+
const hasBgProp = bgPropStr === "true" || bgPropStr && bgPropStr !== "false" && bgPropStr !== "";
|
|
8628
|
+
const showBackground = hasBgProp;
|
|
8629
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
8630
|
+
if (showBorder || showBackground) {
|
|
8631
|
+
const stroke = showBorder ? "#2D3748" : "none";
|
|
8632
|
+
svg += `
|
|
7959
8633
|
<rect x="${pos.x}" y="${pos.y}"
|
|
7960
8634
|
width="${pos.width}" height="${pos.height}"
|
|
7961
|
-
fill="${
|
|
7962
|
-
stroke="
|
|
8635
|
+
fill="${sketchTopbarBg}"
|
|
8636
|
+
stroke="${stroke}"
|
|
7963
8637
|
stroke-width="0.5"
|
|
7964
|
-
filter="url(#sketch-rough)"
|
|
7965
|
-
|
|
7966
|
-
|
|
8638
|
+
filter="url(#sketch-rough)"/>`;
|
|
8639
|
+
}
|
|
8640
|
+
svg += `
|
|
8641
|
+
<!-- Title -->`;
|
|
8642
|
+
svg += `
|
|
7967
8643
|
<text x="${topbar.textX}" y="${topbar.titleY}"
|
|
7968
8644
|
font-family="${this.fontFamily}"
|
|
7969
8645
|
font-size="18"
|
|
7970
8646
|
font-weight="600"
|
|
7971
|
-
fill="${
|
|
8647
|
+
fill="${titleColor}">${this.escapeXml(topbar.visibleTitle)}</text>`;
|
|
7972
8648
|
if (topbar.hasSubtitle) {
|
|
7973
8649
|
svg += `
|
|
7974
8650
|
<text x="${topbar.textX}" y="${topbar.subtitleY}"
|
|
7975
8651
|
font-family="${this.fontFamily}"
|
|
7976
8652
|
font-size="13"
|
|
7977
|
-
fill="${
|
|
8653
|
+
fill="${subtitleColor}">${this.escapeXml(topbar.visibleSubtitle)}</text>`;
|
|
7978
8654
|
}
|
|
7979
8655
|
if (topbar.leftIcon) {
|
|
7980
8656
|
svg += `
|
|
@@ -8054,8 +8730,13 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8054
8730
|
*/
|
|
8055
8731
|
renderText(node, pos) {
|
|
8056
8732
|
const text = String(node.props.text || "Text content");
|
|
8057
|
-
const
|
|
8733
|
+
const sizeProp = String(node.props.size || "");
|
|
8734
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
8735
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
8736
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
8058
8737
|
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
8738
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
8739
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
8059
8740
|
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
8060
8741
|
const firstLineY = pos.y + fontSize;
|
|
8061
8742
|
const tspans = lines.map(
|
|
@@ -8065,9 +8746,48 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8065
8746
|
<text x="${pos.x}" y="${firstLineY}"
|
|
8066
8747
|
font-family="${this.fontFamily}"
|
|
8067
8748
|
font-size="${fontSize}"
|
|
8749
|
+
font-weight="${bold ? "700" : "400"}"
|
|
8750
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
8068
8751
|
fill="${this.renderTheme.text}">${tspans}</text>
|
|
8069
8752
|
</g>`;
|
|
8070
8753
|
}
|
|
8754
|
+
renderParagraph(node, pos) {
|
|
8755
|
+
const text = String(node.props.text || "");
|
|
8756
|
+
const sizeProp = String(node.props.size || "");
|
|
8757
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
8758
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
8759
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
8760
|
+
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
8761
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
8762
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
8763
|
+
const align = String(node.props.align || "left");
|
|
8764
|
+
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
8765
|
+
const firstLineY = pos.y + fontSize;
|
|
8766
|
+
let textX;
|
|
8767
|
+
let textAnchor;
|
|
8768
|
+
if (align === "center") {
|
|
8769
|
+
textX = pos.x + pos.width / 2;
|
|
8770
|
+
textAnchor = "middle";
|
|
8771
|
+
} else if (align === "right") {
|
|
8772
|
+
textX = pos.x + pos.width;
|
|
8773
|
+
textAnchor = "end";
|
|
8774
|
+
} else {
|
|
8775
|
+
textX = pos.x;
|
|
8776
|
+
textAnchor = "start";
|
|
8777
|
+
}
|
|
8778
|
+
const tspans = lines.map(
|
|
8779
|
+
(line, index) => `<tspan x="${textX}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
8780
|
+
).join("");
|
|
8781
|
+
return `<g${this.getDataNodeId(node)}>
|
|
8782
|
+
<text x="${textX}" y="${firstLineY}"
|
|
8783
|
+
font-family="${this.fontFamily}"
|
|
8784
|
+
font-size="${fontSize}"
|
|
8785
|
+
font-weight="${bold ? "700" : "400"}"
|
|
8786
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
8787
|
+
fill="${this.renderTheme.text}"
|
|
8788
|
+
text-anchor="${textAnchor}">${tspans}</text>
|
|
8789
|
+
</g>`;
|
|
8790
|
+
}
|
|
8071
8791
|
/**
|
|
8072
8792
|
* Render label with Comic Sans
|
|
8073
8793
|
*/
|
|
@@ -8298,36 +9018,150 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8298
9018
|
renderTabs(node, pos) {
|
|
8299
9019
|
const itemsStr = String(node.props.items || "");
|
|
8300
9020
|
const tabs = itemsStr ? itemsStr.split(",").map((t) => t.trim()) : ["Tab 1", "Tab 2", "Tab 3"];
|
|
8301
|
-
const
|
|
9021
|
+
const activeProp = node.props.active ?? 0;
|
|
9022
|
+
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
9023
|
+
const variant = String(node.props.variant || "default");
|
|
9024
|
+
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
9025
|
+
const radiusMap = { none: 0, sm: 4, md: 6, lg: 10, full: 20 };
|
|
9026
|
+
const tabRadius = radiusMap[String(node.props.radius || "md")] ?? 6;
|
|
9027
|
+
const sizeMap = { sm: 32, md: 44, lg: 52 };
|
|
9028
|
+
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
9029
|
+
const fontSize = 13;
|
|
9030
|
+
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
9031
|
+
const iconsStr = String(node.props.icons || "");
|
|
9032
|
+
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
9033
|
+
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
9034
|
+
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
8302
9035
|
const tabWidth = pos.width / tabs.length;
|
|
8303
|
-
|
|
8304
|
-
|
|
9036
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
9037
|
+
let textColorOverride = null;
|
|
9038
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
9039
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
9040
|
+
textColorOverride = colorPropStr;
|
|
9041
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
9042
|
+
textColorOverride = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
9043
|
+
}
|
|
9044
|
+
}
|
|
9045
|
+
const activeTextColor = textColorOverride ?? (isFlat ? accentColor : "white");
|
|
9046
|
+
const inactiveTextColor = textColorOverride ? this.hexToRgba(textColorOverride, 0.55) : this.renderTheme.textMuted;
|
|
9047
|
+
const hasRadius = tabRadius > 0;
|
|
9048
|
+
const clipId = hasRadius ? `stc${String(node.meta?.nodeId ?? "").replace(/\W/g, "").slice(0, 12) || `${Math.round(Math.abs(pos.x))}x${Math.round(Math.abs(pos.y))}`}` : "";
|
|
9049
|
+
let svg = "";
|
|
9050
|
+
if (hasRadius) {
|
|
9051
|
+
svg += `<defs><clipPath id="${clipId}"><rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" rx="${tabRadius}"/></clipPath></defs>`;
|
|
9052
|
+
}
|
|
9053
|
+
svg += `<g${this.getDataNodeId(node)}>`;
|
|
9054
|
+
if (hasRadius) {
|
|
9055
|
+
svg += `<g clip-path="url(#${clipId})">`;
|
|
9056
|
+
}
|
|
9057
|
+
if (!isFlat) {
|
|
9058
|
+
svg += `
|
|
9059
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${tabHeight}"
|
|
9060
|
+
fill="${this.renderTheme.bg}"/>`;
|
|
9061
|
+
}
|
|
8305
9062
|
tabs.forEach((tab, i) => {
|
|
8306
9063
|
const tabX = pos.x + i * tabWidth;
|
|
8307
|
-
const isActive = i ===
|
|
8308
|
-
|
|
9064
|
+
const isActive = i === activeIndex;
|
|
9065
|
+
const iconName = iconList[i] || "";
|
|
9066
|
+
const tabIconSvg = iconName ? getIcon(iconName) : null;
|
|
9067
|
+
const iconSize = 14;
|
|
9068
|
+
const iconGap = 4;
|
|
9069
|
+
const charW = fontSize * 0.58;
|
|
9070
|
+
const textEst = tab.length * charW;
|
|
9071
|
+
const totalW = tabIconSvg ? iconSize + iconGap + textEst : textEst;
|
|
9072
|
+
const groupX = tabX + Math.round((tabWidth - totalW) / 2);
|
|
9073
|
+
const iconOffY = pos.y + Math.round((tabHeight - iconSize) / 2);
|
|
9074
|
+
if (isFlat) {
|
|
9075
|
+
if (isActive) {
|
|
9076
|
+
svg += `
|
|
9077
|
+
<rect x="${tabX + 6}" y="${pos.y + tabHeight - 3}"
|
|
9078
|
+
width="${tabWidth - 12}" height="3"
|
|
9079
|
+
rx="1.5"
|
|
9080
|
+
fill="${accentColor}"
|
|
9081
|
+
filter="url(#sketch-rough)"/>`;
|
|
9082
|
+
}
|
|
9083
|
+
if (tabIconSvg) {
|
|
9084
|
+
svg += `
|
|
9085
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
9086
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
9087
|
+
stroke="${isActive ? accentColor : this.renderTheme.textMuted}"
|
|
9088
|
+
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
9089
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
9090
|
+
</svg>
|
|
9091
|
+
</g>
|
|
9092
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
9093
|
+
font-family="${this.fontFamily}"
|
|
9094
|
+
font-size="${fontSize}"
|
|
9095
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
9096
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}">${this.escapeXml(tab)}</text>`;
|
|
9097
|
+
} else {
|
|
9098
|
+
svg += `
|
|
9099
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
9100
|
+
font-family="${this.fontFamily}"
|
|
9101
|
+
font-size="${fontSize}"
|
|
9102
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
9103
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}"
|
|
9104
|
+
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
9105
|
+
}
|
|
9106
|
+
} else {
|
|
9107
|
+
svg += `
|
|
8309
9108
|
<rect x="${tabX}" y="${pos.y}"
|
|
8310
|
-
width="${tabWidth}" height="
|
|
9109
|
+
width="${tabWidth}" height="${tabHeight}"
|
|
9110
|
+
rx="${!showBorder && hasRadius && isActive ? tabRadius : 0}"
|
|
8311
9111
|
fill="${isActive ? accentColor : "transparent"}"
|
|
8312
|
-
stroke="${isActive ? accentColor : "#2D3748"}"
|
|
9112
|
+
stroke="${isActive ? accentColor : showBorder ? "#2D3748" : "none"}"
|
|
8313
9113
|
stroke-width="0.5"
|
|
8314
|
-
filter="url(#sketch-rough)"
|
|
8315
|
-
|
|
9114
|
+
filter="url(#sketch-rough)"/>`;
|
|
9115
|
+
if (tabIconSvg) {
|
|
9116
|
+
svg += `
|
|
9117
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
9118
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
9119
|
+
stroke="${isActive ? activeTextColor : this.renderTheme.textMuted}"
|
|
9120
|
+
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
9121
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
9122
|
+
</svg>
|
|
9123
|
+
</g>
|
|
9124
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
8316
9125
|
font-family="${this.fontFamily}"
|
|
8317
|
-
font-size="
|
|
9126
|
+
font-size="${fontSize}"
|
|
9127
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
9128
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}">${this.escapeXml(tab)}</text>`;
|
|
9129
|
+
} else {
|
|
9130
|
+
svg += `
|
|
9131
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
9132
|
+
font-family="${this.fontFamily}"
|
|
9133
|
+
font-size="${fontSize}"
|
|
8318
9134
|
font-weight="${isActive ? "600" : "500"}"
|
|
8319
|
-
fill="${isActive ?
|
|
9135
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}"
|
|
8320
9136
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
8321
9139
|
});
|
|
8322
|
-
|
|
9140
|
+
const contentY = pos.y + tabHeight;
|
|
9141
|
+
const contentH = pos.height - tabHeight;
|
|
9142
|
+
if (contentH >= 20 && !isFlat) {
|
|
9143
|
+
svg += `
|
|
8323
9144
|
<!-- Tab content area -->
|
|
8324
|
-
<rect x="${pos.x}" y="${
|
|
8325
|
-
width="${pos.width}" height="${
|
|
9145
|
+
<rect x="${pos.x}" y="${contentY}"
|
|
9146
|
+
width="${pos.width}" height="${contentH}"
|
|
8326
9147
|
fill="${this.renderTheme.cardBg}"
|
|
8327
|
-
stroke="#2D3748"
|
|
8328
|
-
|
|
8329
|
-
|
|
9148
|
+
${showBorder ? 'stroke="#2D3748" stroke-width="0.5" filter="url(#sketch-rough)"' : ""}/>`;
|
|
9149
|
+
} else if (isFlat && showBorder) {
|
|
9150
|
+
svg += `
|
|
9151
|
+
<line x1="${pos.x}" y1="${contentY}" x2="${pos.x + pos.width}" y2="${contentY}"
|
|
9152
|
+
stroke="#2D3748" stroke-width="0.5" filter="url(#sketch-rough)"/>`;
|
|
9153
|
+
}
|
|
9154
|
+
if (hasRadius) {
|
|
9155
|
+
svg += `
|
|
8330
9156
|
</g>`;
|
|
9157
|
+
if (!isFlat && showBorder) {
|
|
9158
|
+
svg += `
|
|
9159
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}"
|
|
9160
|
+
rx="${tabRadius}" fill="none" stroke="#2D3748" stroke-width="0.5"
|
|
9161
|
+
filter="url(#sketch-rough)"/>`;
|
|
9162
|
+
}
|
|
9163
|
+
}
|
|
9164
|
+
svg += "\n </g>";
|
|
8331
9165
|
return svg;
|
|
8332
9166
|
}
|
|
8333
9167
|
/**
|
|
@@ -8564,11 +9398,20 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8564
9398
|
* Render image with sketch filter
|
|
8565
9399
|
*/
|
|
8566
9400
|
renderImage(node, pos) {
|
|
8567
|
-
const placeholder = String(node.props.
|
|
9401
|
+
const placeholder = String(node.props.type || "landscape").toLowerCase();
|
|
8568
9402
|
const iconType = String(node.props.icon || "").trim();
|
|
8569
9403
|
const variant = String(node.props.variant || "").trim();
|
|
8570
9404
|
const iconSvg = placeholder === "icon" && iconType.length > 0 ? getIcon(iconType) : null;
|
|
8571
9405
|
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
9406
|
+
const hasExplicitHeight = node.props.height !== void 0 && !isNaN(Number(node.props.height)) && Number(node.props.height) > 0;
|
|
9407
|
+
const isCircle = this.parseBooleanProp(node.props.circle, false);
|
|
9408
|
+
const useClip = isCircle || hasExplicitHeight;
|
|
9409
|
+
const clipId = useClip ? `sclip${String(node.meta?.nodeId ?? "").replace(/\W/g, "").slice(0, 16) || `${Math.round(Math.abs(pos.x))}x${Math.round(Math.abs(pos.y))}`}` : "";
|
|
9410
|
+
const imgCx = pos.x + pos.width / 2;
|
|
9411
|
+
const imgCy = pos.y + pos.height / 2;
|
|
9412
|
+
const imgR = Math.min(pos.width, pos.height) / 2;
|
|
9413
|
+
const defsHtml = useClip ? `<defs><clipPath id="${clipId}">${isCircle ? `<circle cx="${imgCx}" cy="${imgCy}" r="${imgR}"/>` : `<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" rx="4"/>`}</clipPath></defs>` : "";
|
|
9414
|
+
const clipAttr = useClip ? ` clip-path="url(#${clipId})"` : "";
|
|
8572
9415
|
if (iconSvg) {
|
|
8573
9416
|
const semanticBase = variant ? this.getSemanticVariantColor(variant) : void 0;
|
|
8574
9417
|
const hasVariant = variant.length > 0 && (semanticBase !== void 0 || this.colorResolver.hasColor(variant));
|
|
@@ -8578,7 +9421,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8578
9421
|
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
8579
9422
|
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
8580
9423
|
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
8581
|
-
|
|
9424
|
+
const sketchIconBorder = isCircle ? `<circle cx="${imgCx}" cy="${imgCy}" r="${imgR}" fill="none" stroke="#2D3748" stroke-width="0.5" filter="url(#sketch-rough)"/>` : `<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="none" stroke="#2D3748" stroke-width="0.5" rx="4" filter="url(#sketch-rough)"/>`;
|
|
9425
|
+
return `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
8582
9426
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8583
9427
|
width="${pos.width}" height="${pos.height}"
|
|
8584
9428
|
fill="${bgColor}"
|
|
@@ -8589,17 +9433,11 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8589
9433
|
${this.extractSvgContent(iconSvg)}
|
|
8590
9434
|
</svg>
|
|
8591
9435
|
</g>
|
|
8592
|
-
|
|
8593
|
-
width="${pos.width}" height="${pos.height}"
|
|
8594
|
-
fill="none"
|
|
8595
|
-
stroke="#2D3748"
|
|
8596
|
-
stroke-width="0.5"
|
|
8597
|
-
rx="4"
|
|
8598
|
-
filter="url(#sketch-rough)"/>
|
|
9436
|
+
${sketchIconBorder}
|
|
8599
9437
|
</g>`;
|
|
8600
9438
|
}
|
|
8601
|
-
|
|
8602
|
-
|
|
9439
|
+
const sketchCircleBorder = isCircle ? `<circle cx="${imgCx}" cy="${imgCy}" r="${imgR}" fill="none" stroke="#2D3748" stroke-width="0.5"/>` : "";
|
|
9440
|
+
return `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
8603
9441
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8604
9442
|
width="${pos.width}" height="${pos.height}"
|
|
8605
9443
|
fill="${imageBg}"
|
|
@@ -8607,14 +9445,12 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8607
9445
|
stroke-width="0.5"
|
|
8608
9446
|
rx="4"
|
|
8609
9447
|
filter="url(#sketch-rough)"/>
|
|
8610
|
-
|
|
8611
|
-
<!-- Placeholder icon -->
|
|
8612
9448
|
<text x="${pos.x + pos.width / 2}" y="${pos.y + pos.height / 2}"
|
|
8613
9449
|
font-family="${this.fontFamily}"
|
|
8614
9450
|
font-size="24"
|
|
8615
9451
|
fill="#666"
|
|
8616
9452
|
text-anchor="middle">\u{1F5BC}</text>
|
|
8617
|
-
</g
|
|
9453
|
+
</g>${sketchCircleBorder}`;
|
|
8618
9454
|
}
|
|
8619
9455
|
/**
|
|
8620
9456
|
* Render breadcrumbs with Comic Sans
|
|
@@ -8730,20 +9566,33 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8730
9566
|
}
|
|
8731
9567
|
const iconSize = this.getIconSize(size);
|
|
8732
9568
|
const iconColor = variant === "default" ? this.resolveTextColor() : this.resolveVariantColor(variant, this.resolveTextColor());
|
|
8733
|
-
const
|
|
8734
|
-
const
|
|
9569
|
+
const paddingPx = Math.max(0, Number(node.props.padding || 0));
|
|
9570
|
+
const renderedIconSize = Math.max(4, iconSize - paddingPx * 2);
|
|
9571
|
+
const offsetX = pos.x + (pos.width - renderedIconSize) / 2;
|
|
9572
|
+
const offsetY = pos.y + (pos.height - renderedIconSize) / 2;
|
|
9573
|
+
const isIconCircle = this.parseBooleanProp(node.props.circle, false);
|
|
9574
|
+
if (isIconCircle) {
|
|
9575
|
+
const cx = pos.x + pos.width / 2;
|
|
9576
|
+
const cy = pos.y + pos.height / 2;
|
|
9577
|
+
const r = Math.min(pos.width, pos.height) / 2;
|
|
9578
|
+
const iconClipId = `sconclip${String(node.meta?.nodeId ?? "").replace(/\W/g, "").slice(0, 16) || `${Math.round(Math.abs(pos.x))}x${Math.round(Math.abs(pos.y))}`}`;
|
|
9579
|
+
const bgColor = this.hexToRgba(iconColor, 0.1);
|
|
9580
|
+
return `<defs><clipPath id="${iconClipId}"><circle cx="${cx}" cy="${cy}" r="${r}"/></clipPath></defs><g${this.getDataNodeId(node)} clip-path="url(#${iconClipId})">
|
|
9581
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="${bgColor}"/>
|
|
9582
|
+
<g transform="translate(${offsetX}, ${offsetY})">
|
|
9583
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round">
|
|
9584
|
+
${this.extractSvgContent(iconSvg)}
|
|
9585
|
+
</svg>
|
|
9586
|
+
</g>
|
|
9587
|
+
</g>
|
|
9588
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${this.hexToRgba(iconColor, 0.35)}" stroke-width="0.5"/>`;
|
|
9589
|
+
}
|
|
8735
9590
|
return `<g${this.getDataNodeId(node)} transform="translate(${offsetX}, ${offsetY})">
|
|
8736
|
-
<svg width="${
|
|
9591
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8737
9592
|
${this.extractSvgContent(iconSvg)}
|
|
8738
9593
|
</svg>
|
|
8739
9594
|
</g>`;
|
|
8740
9595
|
}
|
|
8741
|
-
/**
|
|
8742
|
-
* Render chart placeholder with sketch filter and Comic Sans
|
|
8743
|
-
*/
|
|
8744
|
-
renderChartPlaceholder(node, pos) {
|
|
8745
|
-
return super.renderChartPlaceholder(node, pos);
|
|
8746
|
-
}
|
|
8747
9596
|
/**
|
|
8748
9597
|
* Helper method to get icon SVG
|
|
8749
9598
|
*/
|