@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.cjs
CHANGED
|
@@ -2220,8 +2220,8 @@ var IRStyleSchema = import_zod.z.object({
|
|
|
2220
2220
|
var IRNodeStyleSchema = import_zod.z.object({
|
|
2221
2221
|
padding: import_zod.z.string().optional(),
|
|
2222
2222
|
gap: import_zod.z.string().optional(),
|
|
2223
|
-
|
|
2224
|
-
|
|
2223
|
+
justify: import_zod.z.enum(["start", "center", "end", "stretch", "spaceBetween", "spaceAround"]).optional(),
|
|
2224
|
+
align: import_zod.z.enum(["start", "center", "end"]).optional(),
|
|
2225
2225
|
background: import_zod.z.string().optional()
|
|
2226
2226
|
});
|
|
2227
2227
|
var IRMetaSchema = import_zod.z.object({
|
|
@@ -2507,6 +2507,9 @@ ${messages}`);
|
|
|
2507
2507
|
if (layoutParams.gap !== void 0) {
|
|
2508
2508
|
style.gap = String(layoutParams.gap);
|
|
2509
2509
|
}
|
|
2510
|
+
if (layoutParams.justify !== void 0) {
|
|
2511
|
+
style.justify = layoutParams.justify;
|
|
2512
|
+
}
|
|
2510
2513
|
if (layoutParams.align !== void 0) {
|
|
2511
2514
|
style.align = layoutParams.align;
|
|
2512
2515
|
}
|
|
@@ -2586,13 +2589,13 @@ ${messages}`);
|
|
|
2586
2589
|
"Heading",
|
|
2587
2590
|
"Text",
|
|
2588
2591
|
"Label",
|
|
2592
|
+
"Paragraph",
|
|
2589
2593
|
"Image",
|
|
2590
2594
|
"Card",
|
|
2591
2595
|
"Stat",
|
|
2592
2596
|
"Topbar",
|
|
2593
2597
|
"Table",
|
|
2594
2598
|
"Chart",
|
|
2595
|
-
"ChartPlaceholder",
|
|
2596
2599
|
"Textarea",
|
|
2597
2600
|
"Select",
|
|
2598
2601
|
"Checkbox",
|
|
@@ -2927,19 +2930,19 @@ var ICON_SIZES_BY_DENSITY = {
|
|
|
2927
2930
|
comfortable: { xs: 14, sm: 16, md: 20, lg: 28, xl: 36 }
|
|
2928
2931
|
};
|
|
2929
2932
|
var ICON_BUTTON_SIZES_BY_DENSITY = {
|
|
2930
|
-
compact: { sm: 20, md: 24, lg: 32 },
|
|
2931
|
-
normal: { sm: 24, md: 32, lg: 40 },
|
|
2932
|
-
comfortable: { sm: 28, md: 40, lg: 48 }
|
|
2933
|
+
compact: { xs: 16, sm: 20, md: 24, lg: 32, xl: 40 },
|
|
2934
|
+
normal: { xs: 20, sm: 24, md: 32, lg: 40, xl: 48 },
|
|
2935
|
+
comfortable: { xs: 24, sm: 28, md: 40, lg: 48, xl: 56 }
|
|
2933
2936
|
};
|
|
2934
2937
|
var CONTROL_HEIGHTS_BY_DENSITY = {
|
|
2935
|
-
compact: { sm: 28, md: 32, lg: 36 },
|
|
2936
|
-
normal: { sm: 36, md: 40, lg: 48 },
|
|
2937
|
-
comfortable: { sm: 40, md: 48, lg: 56 }
|
|
2938
|
+
compact: { xs: 24, sm: 28, md: 32, lg: 36, xl: 44 },
|
|
2939
|
+
normal: { xs: 28, sm: 36, md: 40, lg: 48, xl: 56 },
|
|
2940
|
+
comfortable: { xs: 32, sm: 40, md: 48, lg: 56, xl: 64 }
|
|
2938
2941
|
};
|
|
2939
2942
|
var ACTION_CONTROL_HEIGHTS_BY_DENSITY = {
|
|
2940
|
-
compact: { sm:
|
|
2941
|
-
normal: { sm:
|
|
2942
|
-
comfortable: { sm:
|
|
2943
|
+
compact: { xs: 24, sm: 28, md: 32, lg: 36, xl: 44 },
|
|
2944
|
+
normal: { xs: 28, sm: 36, md: 40, lg: 48, xl: 56 },
|
|
2945
|
+
comfortable: { xs: 32, sm: 40, md: 48, lg: 56, xl: 64 }
|
|
2943
2946
|
};
|
|
2944
2947
|
var CONTROL_PADDING_BY_DENSITY = {
|
|
2945
2948
|
compact: { none: 0, xs: 4, sm: 8, md: 10, lg: 14, xl: 18 },
|
|
@@ -3153,8 +3156,9 @@ var LayoutEngine = class {
|
|
|
3153
3156
|
}
|
|
3154
3157
|
});
|
|
3155
3158
|
} else {
|
|
3156
|
-
const
|
|
3157
|
-
|
|
3159
|
+
const justify = node.style.justify || "stretch";
|
|
3160
|
+
const crossAlign = node.style.align || "start";
|
|
3161
|
+
if (justify === "stretch") {
|
|
3158
3162
|
let currentX = x;
|
|
3159
3163
|
const childWidth = this.calculateChildWidth(children.length, width, gap);
|
|
3160
3164
|
let stackHeight = 0;
|
|
@@ -3176,43 +3180,44 @@ var LayoutEngine = class {
|
|
|
3176
3180
|
});
|
|
3177
3181
|
} else {
|
|
3178
3182
|
const childWidths = [];
|
|
3183
|
+
const childHeights = [];
|
|
3179
3184
|
const explicitHeightFlags = [];
|
|
3180
|
-
const
|
|
3185
|
+
const flexIndices = /* @__PURE__ */ new Set();
|
|
3181
3186
|
let stackHeight = 0;
|
|
3182
3187
|
children.forEach((childRef, index) => {
|
|
3183
3188
|
const childNode = this.nodes[childRef.ref];
|
|
3184
|
-
let childWidth = this.getIntrinsicComponentWidth(childNode);
|
|
3185
|
-
let childHeight = this.getComponentHeight();
|
|
3186
3189
|
const hasExplicitHeight = childNode?.kind === "component" && !!childNode.props.height;
|
|
3187
3190
|
const hasExplicitWidth = childNode?.kind === "component" && !!childNode.props.width;
|
|
3188
3191
|
const isBlockButton = childNode?.kind === "component" && childNode.componentType === "Button" && !hasExplicitWidth && this.parseBooleanProp(childNode.props.block, false);
|
|
3189
|
-
|
|
3192
|
+
const isFlexContainer = !hasExplicitWidth && childNode?.kind === "container" && !this.containerHasIntrinsicWidth(childNode);
|
|
3193
|
+
let childWidth;
|
|
3194
|
+
if (isBlockButton || isFlexContainer) {
|
|
3190
3195
|
childWidth = 0;
|
|
3191
|
-
|
|
3196
|
+
flexIndices.add(index);
|
|
3192
3197
|
} else if (hasExplicitWidth) {
|
|
3193
3198
|
childWidth = Number(childNode.props.width);
|
|
3194
|
-
}
|
|
3195
|
-
|
|
3196
|
-
childHeight = Number(childNode.props.height);
|
|
3199
|
+
} else {
|
|
3200
|
+
childWidth = this.getIntrinsicWidth(childNode, width);
|
|
3197
3201
|
}
|
|
3198
3202
|
childWidths.push(childWidth);
|
|
3203
|
+
childHeights.push(this.getComponentHeight());
|
|
3199
3204
|
explicitHeightFlags.push(hasExplicitHeight);
|
|
3200
3205
|
});
|
|
3201
3206
|
const totalGapWidth = gap * Math.max(0, children.length - 1);
|
|
3202
|
-
if (
|
|
3207
|
+
if (flexIndices.size > 0) {
|
|
3203
3208
|
const fixedWidth = childWidths.reduce((sum, w, idx) => {
|
|
3204
|
-
return
|
|
3209
|
+
return flexIndices.has(idx) ? sum : sum + w;
|
|
3205
3210
|
}, 0);
|
|
3206
3211
|
const remainingWidth = width - totalGapWidth - fixedWidth;
|
|
3207
|
-
const
|
|
3208
|
-
|
|
3209
|
-
childWidths[
|
|
3212
|
+
const widthPerFlex = Math.max(1, remainingWidth / flexIndices.size);
|
|
3213
|
+
flexIndices.forEach((idx) => {
|
|
3214
|
+
childWidths[idx] = widthPerFlex;
|
|
3210
3215
|
});
|
|
3211
3216
|
}
|
|
3212
3217
|
children.forEach((childRef, index) => {
|
|
3213
3218
|
const childNode = this.nodes[childRef.ref];
|
|
3214
|
-
let childHeight = this.getComponentHeight();
|
|
3215
3219
|
const childWidth = childWidths[index];
|
|
3220
|
+
let childHeight = this.getComponentHeight();
|
|
3216
3221
|
if (explicitHeightFlags[index] && childNode?.kind === "component") {
|
|
3217
3222
|
childHeight = Number(childNode.props.height);
|
|
3218
3223
|
} else if (childNode?.kind === "container") {
|
|
@@ -3220,21 +3225,37 @@ var LayoutEngine = class {
|
|
|
3220
3225
|
} else if (childNode?.kind === "component") {
|
|
3221
3226
|
childHeight = this.getIntrinsicComponentHeight(childNode, childWidth);
|
|
3222
3227
|
}
|
|
3228
|
+
childHeights[index] = childHeight;
|
|
3223
3229
|
stackHeight = Math.max(stackHeight, childHeight);
|
|
3224
3230
|
});
|
|
3225
3231
|
const totalChildWidth = childWidths.reduce((sum, w) => sum + w, 0);
|
|
3226
3232
|
const totalContentWidth = totalChildWidth + totalGapWidth;
|
|
3227
3233
|
let startX = x;
|
|
3228
|
-
|
|
3234
|
+
let dynamicGap = gap;
|
|
3235
|
+
if (justify === "center") {
|
|
3229
3236
|
startX = x + (width - totalContentWidth) / 2;
|
|
3230
|
-
} else if (
|
|
3237
|
+
} else if (justify === "end") {
|
|
3231
3238
|
startX = x + width - totalContentWidth;
|
|
3239
|
+
} else if (justify === "spaceBetween") {
|
|
3240
|
+
startX = x;
|
|
3241
|
+
dynamicGap = children.length > 1 ? (width - totalChildWidth) / (children.length - 1) : 0;
|
|
3242
|
+
} else if (justify === "spaceAround") {
|
|
3243
|
+
const spacing = children.length > 0 ? (width - totalChildWidth) / children.length : 0;
|
|
3244
|
+
startX = x + spacing / 2;
|
|
3245
|
+
dynamicGap = spacing;
|
|
3232
3246
|
}
|
|
3233
3247
|
let currentX = startX;
|
|
3234
3248
|
children.forEach((childRef, index) => {
|
|
3235
3249
|
const childWidth = childWidths[index];
|
|
3236
|
-
|
|
3237
|
-
|
|
3250
|
+
const childHeight = childHeights[index];
|
|
3251
|
+
let childY = y;
|
|
3252
|
+
if (crossAlign === "center") {
|
|
3253
|
+
childY = y + Math.round((stackHeight - childHeight) / 2);
|
|
3254
|
+
} else if (crossAlign === "end") {
|
|
3255
|
+
childY = y + stackHeight - childHeight;
|
|
3256
|
+
}
|
|
3257
|
+
this.calculateNode(childRef.ref, currentX, childY, childWidth, childHeight, "stack");
|
|
3258
|
+
currentX += childWidth + dynamicGap;
|
|
3238
3259
|
});
|
|
3239
3260
|
}
|
|
3240
3261
|
}
|
|
@@ -3594,7 +3615,7 @@ var LayoutEngine = class {
|
|
|
3594
3615
|
wrapTextToLines(text, maxWidth, fontSize) {
|
|
3595
3616
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
3596
3617
|
const paragraphs = normalized.split("\n");
|
|
3597
|
-
const charWidth = fontSize * 0.
|
|
3618
|
+
const charWidth = fontSize * 0.5;
|
|
3598
3619
|
const safeWidth = Math.max(maxWidth, charWidth);
|
|
3599
3620
|
const maxCharsPerLine = Math.max(1, Math.floor(safeWidth / charWidth));
|
|
3600
3621
|
const lines = [];
|
|
@@ -3632,12 +3653,14 @@ var LayoutEngine = class {
|
|
|
3632
3653
|
getIntrinsicComponentHeight(node, availableWidth) {
|
|
3633
3654
|
if (node.kind !== "component") return this.getComponentHeight();
|
|
3634
3655
|
const controlSize = String(node.props.size || "md");
|
|
3656
|
+
const actionControlSize = String(node.props.size || "sm");
|
|
3635
3657
|
const density = this.style.density || "normal";
|
|
3636
3658
|
const inputControlHeight = resolveControlHeight(controlSize, density);
|
|
3637
|
-
const actionControlHeight = resolveActionControlHeight(
|
|
3659
|
+
const actionControlHeight = resolveActionControlHeight(actionControlSize, density);
|
|
3638
3660
|
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;
|
|
3639
3661
|
if (node.componentType === "Image") {
|
|
3640
3662
|
const placeholder = String(node.props.placeholder || "landscape");
|
|
3663
|
+
const isCircle = this.parseBooleanProp(node.props.circle, false);
|
|
3641
3664
|
const aspectRatios = {
|
|
3642
3665
|
landscape: 16 / 9,
|
|
3643
3666
|
portrait: 2 / 3,
|
|
@@ -3645,7 +3668,7 @@ var LayoutEngine = class {
|
|
|
3645
3668
|
icon: 1,
|
|
3646
3669
|
avatar: 1
|
|
3647
3670
|
};
|
|
3648
|
-
const ratio = aspectRatios[placeholder] || 16 / 9;
|
|
3671
|
+
const ratio = isCircle ? 1 : aspectRatios[placeholder] || 16 / 9;
|
|
3649
3672
|
const explicitHeight = Number(node.props.height);
|
|
3650
3673
|
if (!isNaN(explicitHeight) && explicitHeight > 0) {
|
|
3651
3674
|
return explicitHeight;
|
|
@@ -3697,13 +3720,19 @@ var LayoutEngine = class {
|
|
|
3697
3720
|
}
|
|
3698
3721
|
return Math.max(1, Math.ceil(wrappedHeight + verticalPadding * 2));
|
|
3699
3722
|
}
|
|
3700
|
-
if (node.componentType === "Text") {
|
|
3723
|
+
if (node.componentType === "Text" || node.componentType === "Paragraph") {
|
|
3701
3724
|
const content = String(node.props.text || "");
|
|
3702
|
-
const { fontSize, lineHeight } = this.getTextMetricsForDensity();
|
|
3725
|
+
const { fontSize: defaultFontSize, lineHeight } = this.getTextMetricsForDensity();
|
|
3726
|
+
const sizeProp = String(node.props.size || "");
|
|
3727
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
3728
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
3703
3729
|
const lineHeightPx = Math.ceil(fontSize * lineHeight);
|
|
3704
3730
|
const maxWidth = availableWidth && availableWidth > 0 ? availableWidth : 200;
|
|
3705
3731
|
const lines = this.wrapTextToLines(content, maxWidth, fontSize);
|
|
3706
3732
|
const wrappedHeight = Math.max(1, lines.length) * lineHeightPx;
|
|
3733
|
+
if (sizeProp) {
|
|
3734
|
+
return wrappedHeight;
|
|
3735
|
+
}
|
|
3707
3736
|
return Math.max(this.getComponentHeight(), wrappedHeight);
|
|
3708
3737
|
}
|
|
3709
3738
|
if (node.componentType === "Alert") {
|
|
@@ -3732,7 +3761,7 @@ var LayoutEngine = class {
|
|
|
3732
3761
|
if (node.componentType === "Modal") return 300;
|
|
3733
3762
|
if (node.componentType === "Card") return 120;
|
|
3734
3763
|
if (node.componentType === "Stat") return 120;
|
|
3735
|
-
if (node.componentType === "Chart"
|
|
3764
|
+
if (node.componentType === "Chart") return 250;
|
|
3736
3765
|
if (node.componentType === "List") {
|
|
3737
3766
|
const itemsFromProps = String(node.props.items || "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
3738
3767
|
const parsedItemsMock = Number(node.props.itemsMock ?? 4);
|
|
@@ -3743,7 +3772,14 @@ var LayoutEngine = class {
|
|
|
3743
3772
|
const contentHeight = titleHeight + itemCount * itemHeight;
|
|
3744
3773
|
return Math.max(this.getComponentHeight(), contentHeight);
|
|
3745
3774
|
}
|
|
3746
|
-
if (node.componentType === "Topbar")
|
|
3775
|
+
if (node.componentType === "Topbar") {
|
|
3776
|
+
const TOPBAR_HEIGHTS = { sm: 44, md: 56, lg: 72 };
|
|
3777
|
+
return TOPBAR_HEIGHTS[String(node.props.size || "md")] ?? 56;
|
|
3778
|
+
}
|
|
3779
|
+
if (node.componentType === "Tabs") {
|
|
3780
|
+
const TABS_HEIGHTS = { sm: 32, md: 44, lg: 52 };
|
|
3781
|
+
return TABS_HEIGHTS[String(node.props.size || "md")] ?? 44;
|
|
3782
|
+
}
|
|
3747
3783
|
if (node.componentType === "Divider") return 1;
|
|
3748
3784
|
if (node.componentType === "Separate") return this.getSeparateSize(node);
|
|
3749
3785
|
if (node.componentType === "Input" || node.componentType === "Select") {
|
|
@@ -3752,11 +3788,57 @@ var LayoutEngine = class {
|
|
|
3752
3788
|
if (node.componentType === "Button" || node.componentType === "IconButton" || node.componentType === "Link") {
|
|
3753
3789
|
return actionControlHeight + controlLabelOffset;
|
|
3754
3790
|
}
|
|
3791
|
+
if (node.componentType === "Badge" || node.componentType === "Chip") {
|
|
3792
|
+
const BADGE_HEIGHTS = { xs: 28, sm: 36, md: 40, lg: 48, xl: 56 };
|
|
3793
|
+
const badgeSize = String(node.props.size || "md");
|
|
3794
|
+
return BADGE_HEIGHTS[badgeSize] ?? 40;
|
|
3795
|
+
}
|
|
3755
3796
|
return this.getComponentHeight();
|
|
3756
3797
|
}
|
|
3757
3798
|
getControlLabelOffset(label) {
|
|
3758
3799
|
return label.trim().length > 0 ? 18 : 0;
|
|
3759
3800
|
}
|
|
3801
|
+
/**
|
|
3802
|
+
* Returns true when a container's width can be calculated from its children
|
|
3803
|
+
* (i.e. it is a horizontal non-stretch stack). False means the container
|
|
3804
|
+
* behaves like `flex-grow:1` and should absorb remaining space.
|
|
3805
|
+
*/
|
|
3806
|
+
containerHasIntrinsicWidth(node) {
|
|
3807
|
+
if (node.kind !== "container") return false;
|
|
3808
|
+
return node.containerType === "stack" && String(node.params.direction || "vertical") === "horizontal" && (node.style.justify || "stretch") !== "stretch";
|
|
3809
|
+
}
|
|
3810
|
+
/**
|
|
3811
|
+
* Returns the natural (intrinsic) width of any node — component or container.
|
|
3812
|
+
* For horizontal non-stretch containers the width is the sum of their children's
|
|
3813
|
+
* intrinsic widths plus gaps, capped at `availableWidth`. All other containers
|
|
3814
|
+
* are assumed to take the full available width (they stretch or grow).
|
|
3815
|
+
*/
|
|
3816
|
+
getIntrinsicWidth(node, availableWidth) {
|
|
3817
|
+
if (!node) return 120;
|
|
3818
|
+
if (node.kind === "component") {
|
|
3819
|
+
return this.getIntrinsicComponentWidth(node);
|
|
3820
|
+
}
|
|
3821
|
+
if (node.kind === "container") {
|
|
3822
|
+
if (this.containerHasIntrinsicWidth(node)) {
|
|
3823
|
+
const gap = this.resolveSpacing(node.style.gap);
|
|
3824
|
+
const padding = this.resolveSpacing(node.style.padding);
|
|
3825
|
+
const innerAvailable = Math.max(0, availableWidth - padding * 2);
|
|
3826
|
+
const children = node.children ?? [];
|
|
3827
|
+
let total = padding * 2;
|
|
3828
|
+
children.forEach((childRef, idx) => {
|
|
3829
|
+
const child = this.nodes[childRef.ref];
|
|
3830
|
+
total += this.getIntrinsicWidth(child, innerAvailable);
|
|
3831
|
+
if (idx < children.length - 1) total += gap;
|
|
3832
|
+
});
|
|
3833
|
+
return Math.min(total, availableWidth);
|
|
3834
|
+
}
|
|
3835
|
+
return availableWidth;
|
|
3836
|
+
}
|
|
3837
|
+
if (node.kind === "instance") {
|
|
3838
|
+
return availableWidth;
|
|
3839
|
+
}
|
|
3840
|
+
return 120;
|
|
3841
|
+
}
|
|
3760
3842
|
getIntrinsicComponentWidth(node) {
|
|
3761
3843
|
if (!node || node.kind !== "component") {
|
|
3762
3844
|
return 120;
|
|
@@ -3766,7 +3848,7 @@ var LayoutEngine = class {
|
|
|
3766
3848
|
return resolveIconSize(size, this.style.density || "normal");
|
|
3767
3849
|
}
|
|
3768
3850
|
if (node.componentType === "IconButton") {
|
|
3769
|
-
const size = String(node.props.size || "
|
|
3851
|
+
const size = String(node.props.size || "sm");
|
|
3770
3852
|
const density = this.style.density || "normal";
|
|
3771
3853
|
const baseSize = resolveIconButtonSize(size, density);
|
|
3772
3854
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
@@ -3824,7 +3906,13 @@ var LayoutEngine = class {
|
|
|
3824
3906
|
}
|
|
3825
3907
|
if (node.componentType === "Badge" || node.componentType === "Chip") {
|
|
3826
3908
|
const text = String(node.props.text || "");
|
|
3827
|
-
|
|
3909
|
+
const size = String(node.props.size || "md");
|
|
3910
|
+
const BADGE_CHAR_W = { xs: 6, sm: 6.5, md: 7, lg: 7.5, xl: 8.5 };
|
|
3911
|
+
const BADGE_PAD_X = { xs: 5, sm: 6, md: 8, lg: 10, xl: 14 };
|
|
3912
|
+
const customPadding = node.props.padding !== void 0 ? Number(node.props.padding) : void 0;
|
|
3913
|
+
const padX = customPadding !== void 0 && !isNaN(customPadding) ? customPadding : BADGE_PAD_X[size] ?? 8;
|
|
3914
|
+
const charW = BADGE_CHAR_W[size] ?? 7;
|
|
3915
|
+
return Math.max(padX * 4, text.length * charW + padX * 2);
|
|
3828
3916
|
}
|
|
3829
3917
|
return 120;
|
|
3830
3918
|
}
|
|
@@ -4108,34 +4196,358 @@ MockDataGenerator.HEADER_TO_MOCK = {
|
|
|
4108
4196
|
var ColorResolver = class {
|
|
4109
4197
|
constructor() {
|
|
4110
4198
|
this.customColors = {};
|
|
4111
|
-
// Named colors palette
|
|
4199
|
+
// Named colors palette — Full Material Design + utility aliases
|
|
4112
4200
|
this.namedColors = {
|
|
4113
|
-
//
|
|
4201
|
+
// ── Utility ──────────────────────────────────────────────────────────────
|
|
4114
4202
|
white: "#FFFFFF",
|
|
4115
4203
|
black: "#000000",
|
|
4116
|
-
gray: "#
|
|
4117
|
-
|
|
4204
|
+
gray: "#9E9E9E",
|
|
4205
|
+
// alias for grey 500
|
|
4206
|
+
grey: "#9E9E9E",
|
|
4207
|
+
slate: "#607D8B",
|
|
4208
|
+
// alias for blue_grey 500
|
|
4118
4209
|
zinc: "#71717A",
|
|
4119
|
-
//
|
|
4120
|
-
red: "#EF4444",
|
|
4121
|
-
orange: "#F97316",
|
|
4122
|
-
yellow: "#EAB308",
|
|
4123
|
-
lime: "#84CC16",
|
|
4124
|
-
green: "#22C55E",
|
|
4210
|
+
// Extra well-known non-Material names (kept for backward compat)
|
|
4125
4211
|
emerald: "#10B981",
|
|
4126
|
-
teal: "#14B8A6",
|
|
4127
|
-
cyan: "#06B6D4",
|
|
4128
|
-
blue: "#3B82F6",
|
|
4129
|
-
indigo: "#6366F1",
|
|
4130
4212
|
violet: "#8B5CF6",
|
|
4131
|
-
purple: "#A855F7",
|
|
4132
4213
|
fuchsia: "#D946EF",
|
|
4133
|
-
pink: "#EC4899",
|
|
4134
4214
|
rose: "#F43F5E",
|
|
4135
|
-
// Light variants
|
|
4136
|
-
"red-light": "#
|
|
4137
|
-
"blue-light": "#
|
|
4138
|
-
"green-light": "#
|
|
4215
|
+
// Light utility variants
|
|
4216
|
+
"red-light": "#FFCDD2",
|
|
4217
|
+
"blue-light": "#BBDEFB",
|
|
4218
|
+
"green-light": "#C8E6C9",
|
|
4219
|
+
// ── Red ──────────────────────────────────────────────────────────────────
|
|
4220
|
+
red: "#F44336",
|
|
4221
|
+
// 500
|
|
4222
|
+
red_50: "#FFEBEE",
|
|
4223
|
+
red_100: "#FFCDD2",
|
|
4224
|
+
red_200: "#EF9A9A",
|
|
4225
|
+
red_300: "#E57373",
|
|
4226
|
+
red_400: "#EF5350",
|
|
4227
|
+
red_500: "#F44336",
|
|
4228
|
+
red_600: "#E53935",
|
|
4229
|
+
red_700: "#D32F2F",
|
|
4230
|
+
red_800: "#C62828",
|
|
4231
|
+
red_900: "#B71C1C",
|
|
4232
|
+
red_A100: "#FF8A80",
|
|
4233
|
+
red_A200: "#FF5252",
|
|
4234
|
+
red_A400: "#FF1744",
|
|
4235
|
+
red_A700: "#D50000",
|
|
4236
|
+
// ── Pink ─────────────────────────────────────────────────────────────────
|
|
4237
|
+
pink: "#E91E63",
|
|
4238
|
+
// 500
|
|
4239
|
+
pink_50: "#FCE4EC",
|
|
4240
|
+
pink_100: "#F8BBD0",
|
|
4241
|
+
pink_200: "#F48FB1",
|
|
4242
|
+
pink_300: "#F06292",
|
|
4243
|
+
pink_400: "#EC407A",
|
|
4244
|
+
pink_500: "#E91E63",
|
|
4245
|
+
pink_600: "#D81B60",
|
|
4246
|
+
pink_700: "#C2185B",
|
|
4247
|
+
pink_800: "#AD1457",
|
|
4248
|
+
pink_900: "#880E4F",
|
|
4249
|
+
pink_A100: "#FF80AB",
|
|
4250
|
+
pink_A200: "#FF4081",
|
|
4251
|
+
pink_A400: "#F50057",
|
|
4252
|
+
pink_A700: "#C51162",
|
|
4253
|
+
// ── Purple ───────────────────────────────────────────────────────────────
|
|
4254
|
+
purple: "#9C27B0",
|
|
4255
|
+
// 500
|
|
4256
|
+
purple_50: "#F3E5F5",
|
|
4257
|
+
purple_100: "#E1BEE7",
|
|
4258
|
+
purple_200: "#CE93D8",
|
|
4259
|
+
purple_300: "#BA68C8",
|
|
4260
|
+
purple_400: "#AB47BC",
|
|
4261
|
+
purple_500: "#9C27B0",
|
|
4262
|
+
purple_600: "#8E24AA",
|
|
4263
|
+
purple_700: "#7B1FA2",
|
|
4264
|
+
purple_800: "#6A1B9A",
|
|
4265
|
+
purple_900: "#4A148C",
|
|
4266
|
+
purple_A100: "#EA80FC",
|
|
4267
|
+
purple_A200: "#E040FB",
|
|
4268
|
+
purple_A400: "#D500F9",
|
|
4269
|
+
purple_A700: "#AA00FF",
|
|
4270
|
+
// ── Deep Purple ──────────────────────────────────────────────────────────
|
|
4271
|
+
deep_purple: "#673AB7",
|
|
4272
|
+
// 500
|
|
4273
|
+
deep_purple_50: "#EDE7F6",
|
|
4274
|
+
deep_purple_100: "#D1C4E9",
|
|
4275
|
+
deep_purple_200: "#B39DDB",
|
|
4276
|
+
deep_purple_300: "#9575CD",
|
|
4277
|
+
deep_purple_400: "#7E57C2",
|
|
4278
|
+
deep_purple_500: "#673AB7",
|
|
4279
|
+
deep_purple_600: "#5E35B1",
|
|
4280
|
+
deep_purple_700: "#512DA8",
|
|
4281
|
+
deep_purple_800: "#4527A0",
|
|
4282
|
+
deep_purple_900: "#311B92",
|
|
4283
|
+
deep_purple_A100: "#B388FF",
|
|
4284
|
+
deep_purple_A200: "#7C4DFF",
|
|
4285
|
+
deep_purple_A400: "#651FFF",
|
|
4286
|
+
deep_purple_A700: "#6200EA",
|
|
4287
|
+
// ── Indigo ───────────────────────────────────────────────────────────────
|
|
4288
|
+
indigo: "#3F51B5",
|
|
4289
|
+
// 500
|
|
4290
|
+
indigo_50: "#E8EAF6",
|
|
4291
|
+
indigo_100: "#C5CAE9",
|
|
4292
|
+
indigo_200: "#9FA8DA",
|
|
4293
|
+
indigo_300: "#7986CB",
|
|
4294
|
+
indigo_400: "#5C6BC0",
|
|
4295
|
+
indigo_500: "#3F51B5",
|
|
4296
|
+
indigo_600: "#3949AB",
|
|
4297
|
+
indigo_700: "#303F9F",
|
|
4298
|
+
indigo_800: "#283593",
|
|
4299
|
+
indigo_900: "#1A237E",
|
|
4300
|
+
indigo_A100: "#8C9EFF",
|
|
4301
|
+
indigo_A200: "#536DFE",
|
|
4302
|
+
indigo_A400: "#3D5AFE",
|
|
4303
|
+
indigo_A700: "#304FFE",
|
|
4304
|
+
// ── Blue ─────────────────────────────────────────────────────────────────
|
|
4305
|
+
blue: "#2196F3",
|
|
4306
|
+
// 500
|
|
4307
|
+
blue_50: "#E3F2FD",
|
|
4308
|
+
blue_100: "#BBDEFB",
|
|
4309
|
+
blue_200: "#90CAF9",
|
|
4310
|
+
blue_300: "#64B5F6",
|
|
4311
|
+
blue_400: "#42A5F5",
|
|
4312
|
+
blue_500: "#2196F3",
|
|
4313
|
+
blue_600: "#1E88E5",
|
|
4314
|
+
blue_700: "#1976D2",
|
|
4315
|
+
blue_800: "#1565C0",
|
|
4316
|
+
blue_900: "#0D47A1",
|
|
4317
|
+
blue_A100: "#82B1FF",
|
|
4318
|
+
blue_A200: "#448AFF",
|
|
4319
|
+
blue_A400: "#2979FF",
|
|
4320
|
+
blue_A700: "#2962FF",
|
|
4321
|
+
// ── Light Blue ───────────────────────────────────────────────────────────
|
|
4322
|
+
light_blue: "#03A9F4",
|
|
4323
|
+
// 500
|
|
4324
|
+
light_blue_50: "#E1F5FE",
|
|
4325
|
+
light_blue_100: "#B3E5FC",
|
|
4326
|
+
light_blue_200: "#81D4FA",
|
|
4327
|
+
light_blue_300: "#4FC3F7",
|
|
4328
|
+
light_blue_400: "#29B6F6",
|
|
4329
|
+
light_blue_500: "#03A9F4",
|
|
4330
|
+
light_blue_600: "#039BE5",
|
|
4331
|
+
light_blue_700: "#0288D1",
|
|
4332
|
+
light_blue_800: "#0277BD",
|
|
4333
|
+
light_blue_900: "#01579B",
|
|
4334
|
+
light_blue_A100: "#80D8FF",
|
|
4335
|
+
light_blue_A200: "#40C4FF",
|
|
4336
|
+
light_blue_A400: "#00B0FF",
|
|
4337
|
+
light_blue_A700: "#0091EA",
|
|
4338
|
+
// ── Cyan ─────────────────────────────────────────────────────────────────
|
|
4339
|
+
cyan: "#00BCD4",
|
|
4340
|
+
// 500
|
|
4341
|
+
cyan_50: "#E0F7FA",
|
|
4342
|
+
cyan_100: "#B2EBF2",
|
|
4343
|
+
cyan_200: "#80DEEA",
|
|
4344
|
+
cyan_300: "#4DD0E1",
|
|
4345
|
+
cyan_400: "#26C6DA",
|
|
4346
|
+
cyan_500: "#00BCD4",
|
|
4347
|
+
cyan_600: "#00ACC1",
|
|
4348
|
+
cyan_700: "#0097A7",
|
|
4349
|
+
cyan_800: "#00838F",
|
|
4350
|
+
cyan_900: "#006064",
|
|
4351
|
+
cyan_A100: "#84FFFF",
|
|
4352
|
+
cyan_A200: "#18FFFF",
|
|
4353
|
+
cyan_A400: "#00E5FF",
|
|
4354
|
+
cyan_A700: "#00B8D4",
|
|
4355
|
+
// ── Teal ─────────────────────────────────────────────────────────────────
|
|
4356
|
+
teal: "#009688",
|
|
4357
|
+
// 500
|
|
4358
|
+
teal_50: "#E0F2F1",
|
|
4359
|
+
teal_100: "#B2DFDB",
|
|
4360
|
+
teal_200: "#80CBC4",
|
|
4361
|
+
teal_300: "#4DB6AC",
|
|
4362
|
+
teal_400: "#26A69A",
|
|
4363
|
+
teal_500: "#009688",
|
|
4364
|
+
teal_600: "#00897B",
|
|
4365
|
+
teal_700: "#00796B",
|
|
4366
|
+
teal_800: "#00695C",
|
|
4367
|
+
teal_900: "#004D40",
|
|
4368
|
+
teal_A100: "#A7FFEB",
|
|
4369
|
+
teal_A200: "#64FFDA",
|
|
4370
|
+
teal_A400: "#1DE9B6",
|
|
4371
|
+
teal_A700: "#00BFA5",
|
|
4372
|
+
// ── Green ────────────────────────────────────────────────────────────────
|
|
4373
|
+
green: "#4CAF50",
|
|
4374
|
+
// 500
|
|
4375
|
+
green_50: "#E8F5E9",
|
|
4376
|
+
green_100: "#C8E6C9",
|
|
4377
|
+
green_200: "#A5D6A7",
|
|
4378
|
+
green_300: "#81C784",
|
|
4379
|
+
green_400: "#66BB6A",
|
|
4380
|
+
green_500: "#4CAF50",
|
|
4381
|
+
green_600: "#43A047",
|
|
4382
|
+
green_700: "#388E3C",
|
|
4383
|
+
green_800: "#2E7D32",
|
|
4384
|
+
green_900: "#1B5E20",
|
|
4385
|
+
green_A100: "#B9F6CA",
|
|
4386
|
+
green_A200: "#69F0AE",
|
|
4387
|
+
green_A400: "#00E676",
|
|
4388
|
+
green_A700: "#00C853",
|
|
4389
|
+
// ── Light Green ──────────────────────────────────────────────────────────
|
|
4390
|
+
light_green: "#8BC34A",
|
|
4391
|
+
// 500
|
|
4392
|
+
light_green_50: "#F1F8E9",
|
|
4393
|
+
light_green_100: "#DCEDC8",
|
|
4394
|
+
light_green_200: "#C5E1A5",
|
|
4395
|
+
light_green_300: "#AED581",
|
|
4396
|
+
light_green_400: "#9CCC65",
|
|
4397
|
+
light_green_500: "#8BC34A",
|
|
4398
|
+
light_green_600: "#7CB342",
|
|
4399
|
+
light_green_700: "#689F38",
|
|
4400
|
+
light_green_800: "#558B2F",
|
|
4401
|
+
light_green_900: "#33691E",
|
|
4402
|
+
light_green_A100: "#CCFF90",
|
|
4403
|
+
light_green_A200: "#B2FF59",
|
|
4404
|
+
light_green_A400: "#76FF03",
|
|
4405
|
+
light_green_A700: "#64DD17",
|
|
4406
|
+
// ── Lime ─────────────────────────────────────────────────────────────────
|
|
4407
|
+
lime: "#CDDC39",
|
|
4408
|
+
// 500
|
|
4409
|
+
lime_50: "#F9FBE7",
|
|
4410
|
+
lime_100: "#F0F4C3",
|
|
4411
|
+
lime_200: "#E6EE9C",
|
|
4412
|
+
lime_300: "#DCE775",
|
|
4413
|
+
lime_400: "#D4E157",
|
|
4414
|
+
lime_500: "#CDDC39",
|
|
4415
|
+
lime_600: "#C0CA33",
|
|
4416
|
+
lime_700: "#AFB42B",
|
|
4417
|
+
lime_800: "#9E9D24",
|
|
4418
|
+
lime_900: "#827717",
|
|
4419
|
+
lime_A100: "#F4FF81",
|
|
4420
|
+
lime_A200: "#EEFF41",
|
|
4421
|
+
lime_A400: "#C6FF00",
|
|
4422
|
+
lime_A700: "#AEEA00",
|
|
4423
|
+
// ── Yellow ───────────────────────────────────────────────────────────────
|
|
4424
|
+
yellow: "#FFEB3B",
|
|
4425
|
+
// 500
|
|
4426
|
+
yellow_50: "#FFFDE7",
|
|
4427
|
+
yellow_100: "#FFF9C4",
|
|
4428
|
+
yellow_200: "#FFF59D",
|
|
4429
|
+
yellow_300: "#FFF176",
|
|
4430
|
+
yellow_400: "#FFEE58",
|
|
4431
|
+
yellow_500: "#FFEB3B",
|
|
4432
|
+
yellow_600: "#FDD835",
|
|
4433
|
+
yellow_700: "#F9A825",
|
|
4434
|
+
yellow_800: "#F57F17",
|
|
4435
|
+
yellow_900: "#F57F17",
|
|
4436
|
+
yellow_A100: "#FFFF8D",
|
|
4437
|
+
yellow_A200: "#FFFF00",
|
|
4438
|
+
yellow_A400: "#FFEA00",
|
|
4439
|
+
yellow_A700: "#FFD600",
|
|
4440
|
+
// ── Amber ────────────────────────────────────────────────────────────────
|
|
4441
|
+
amber: "#FFC107",
|
|
4442
|
+
// 500
|
|
4443
|
+
amber_50: "#FFF8E1",
|
|
4444
|
+
amber_100: "#FFECB3",
|
|
4445
|
+
amber_200: "#FFE082",
|
|
4446
|
+
amber_300: "#FFD54F",
|
|
4447
|
+
amber_400: "#FFCA28",
|
|
4448
|
+
amber_500: "#FFC107",
|
|
4449
|
+
amber_600: "#FFB300",
|
|
4450
|
+
amber_700: "#FFA000",
|
|
4451
|
+
amber_800: "#FF8F00",
|
|
4452
|
+
amber_900: "#FF6F00",
|
|
4453
|
+
amber_A100: "#FFE57F",
|
|
4454
|
+
amber_A200: "#FFD740",
|
|
4455
|
+
amber_A400: "#FFC400",
|
|
4456
|
+
amber_A700: "#FFAB00",
|
|
4457
|
+
// ── Orange ───────────────────────────────────────────────────────────────
|
|
4458
|
+
orange: "#FF9800",
|
|
4459
|
+
// 500
|
|
4460
|
+
orange_50: "#FFF3E0",
|
|
4461
|
+
orange_100: "#FFE0B2",
|
|
4462
|
+
orange_200: "#FFCC80",
|
|
4463
|
+
orange_300: "#FFB74D",
|
|
4464
|
+
orange_400: "#FFA726",
|
|
4465
|
+
orange_500: "#FF9800",
|
|
4466
|
+
orange_600: "#FB8C00",
|
|
4467
|
+
orange_700: "#F57C00",
|
|
4468
|
+
orange_800: "#EF6C00",
|
|
4469
|
+
orange_900: "#E65100",
|
|
4470
|
+
orange_A100: "#FFD180",
|
|
4471
|
+
orange_A200: "#FFAB40",
|
|
4472
|
+
orange_A400: "#FF9100",
|
|
4473
|
+
orange_A700: "#FF6D00",
|
|
4474
|
+
// ── Deep Orange ──────────────────────────────────────────────────────────
|
|
4475
|
+
deep_orange: "#FF5722",
|
|
4476
|
+
// 500
|
|
4477
|
+
deep_orange_50: "#FBE9E7",
|
|
4478
|
+
deep_orange_100: "#FFCCBC",
|
|
4479
|
+
deep_orange_200: "#FFAB91",
|
|
4480
|
+
deep_orange_300: "#FF8A65",
|
|
4481
|
+
deep_orange_400: "#FF7043",
|
|
4482
|
+
deep_orange_500: "#FF5722",
|
|
4483
|
+
deep_orange_600: "#F4511E",
|
|
4484
|
+
deep_orange_700: "#E64A19",
|
|
4485
|
+
deep_orange_800: "#D84315",
|
|
4486
|
+
deep_orange_900: "#BF360C",
|
|
4487
|
+
deep_orange_A100: "#FF9E80",
|
|
4488
|
+
deep_orange_A200: "#FF6E40",
|
|
4489
|
+
deep_orange_A400: "#FF3D00",
|
|
4490
|
+
deep_orange_A700: "#DD2C00",
|
|
4491
|
+
// ── Brown ────────────────────────────────────────────────────────────────
|
|
4492
|
+
brown: "#795548",
|
|
4493
|
+
// 500
|
|
4494
|
+
brown_50: "#EFEBE9",
|
|
4495
|
+
brown_100: "#D7CCC8",
|
|
4496
|
+
brown_200: "#BCAAA4",
|
|
4497
|
+
brown_300: "#A1887F",
|
|
4498
|
+
brown_400: "#8D6E63",
|
|
4499
|
+
brown_500: "#795548",
|
|
4500
|
+
brown_600: "#6D4C41",
|
|
4501
|
+
brown_700: "#5D4037",
|
|
4502
|
+
brown_800: "#4E342E",
|
|
4503
|
+
brown_900: "#3E2723",
|
|
4504
|
+
// ── Grey ─────────────────────────────────────────────────────────────────
|
|
4505
|
+
grey_50: "#FAFAFA",
|
|
4506
|
+
grey_100: "#F5F5F5",
|
|
4507
|
+
grey_200: "#EEEEEE",
|
|
4508
|
+
grey_300: "#E0E0E0",
|
|
4509
|
+
grey_400: "#BDBDBD",
|
|
4510
|
+
grey_500: "#9E9E9E",
|
|
4511
|
+
grey_600: "#757575",
|
|
4512
|
+
grey_700: "#616161",
|
|
4513
|
+
grey_800: "#424242",
|
|
4514
|
+
grey_900: "#212121",
|
|
4515
|
+
// aliases
|
|
4516
|
+
gray_50: "#FAFAFA",
|
|
4517
|
+
gray_100: "#F5F5F5",
|
|
4518
|
+
gray_200: "#EEEEEE",
|
|
4519
|
+
gray_300: "#E0E0E0",
|
|
4520
|
+
gray_400: "#BDBDBD",
|
|
4521
|
+
gray_500: "#9E9E9E",
|
|
4522
|
+
gray_600: "#757575",
|
|
4523
|
+
gray_700: "#616161",
|
|
4524
|
+
gray_800: "#424242",
|
|
4525
|
+
gray_900: "#212121",
|
|
4526
|
+
// ── Blue Grey ────────────────────────────────────────────────────────────
|
|
4527
|
+
blue_grey: "#607D8B",
|
|
4528
|
+
// 500
|
|
4529
|
+
blue_grey_50: "#ECEFF1",
|
|
4530
|
+
blue_grey_100: "#CFD8DC",
|
|
4531
|
+
blue_grey_200: "#B0BEC5",
|
|
4532
|
+
blue_grey_300: "#90A4AE",
|
|
4533
|
+
blue_grey_400: "#78909C",
|
|
4534
|
+
blue_grey_500: "#607D8B",
|
|
4535
|
+
blue_grey_600: "#546E7A",
|
|
4536
|
+
blue_grey_700: "#455A64",
|
|
4537
|
+
blue_grey_800: "#37474F",
|
|
4538
|
+
blue_grey_900: "#263238",
|
|
4539
|
+
// aliases
|
|
4540
|
+
blue_gray: "#607D8B",
|
|
4541
|
+
blue_gray_50: "#ECEFF1",
|
|
4542
|
+
blue_gray_100: "#CFD8DC",
|
|
4543
|
+
blue_gray_200: "#B0BEC5",
|
|
4544
|
+
blue_gray_300: "#90A4AE",
|
|
4545
|
+
blue_gray_400: "#78909C",
|
|
4546
|
+
blue_gray_500: "#607D8B",
|
|
4547
|
+
blue_gray_600: "#546E7A",
|
|
4548
|
+
blue_gray_700: "#455A64",
|
|
4549
|
+
blue_gray_800: "#37474F",
|
|
4550
|
+
blue_gray_900: "#263238"
|
|
4139
4551
|
};
|
|
4140
4552
|
}
|
|
4141
4553
|
/**
|
|
@@ -4783,7 +5195,8 @@ var SVGRenderer = class {
|
|
|
4783
5195
|
if (node.containerType === "split") {
|
|
4784
5196
|
this.renderSplitDecoration(node, pos, containerGroup);
|
|
4785
5197
|
}
|
|
4786
|
-
|
|
5198
|
+
const isCellContainer = node.meta?.source === "cell";
|
|
5199
|
+
if (node.children.length === 0 && this.options.showDiagnostics && !isCellContainer) {
|
|
4787
5200
|
containerGroup.push(this.renderEmptyContainerDiagnostic(pos, node.containerType));
|
|
4788
5201
|
} else {
|
|
4789
5202
|
node.children.forEach((childRef) => {
|
|
@@ -4830,7 +5243,6 @@ var SVGRenderer = class {
|
|
|
4830
5243
|
case "Table":
|
|
4831
5244
|
return this.renderTable(node, pos);
|
|
4832
5245
|
case "Chart":
|
|
4833
|
-
case "ChartPlaceholder":
|
|
4834
5246
|
return this.renderChartPlaceholder(node, pos);
|
|
4835
5247
|
case "Breadcrumbs":
|
|
4836
5248
|
return this.renderBreadcrumbs(node, pos);
|
|
@@ -4839,6 +5251,8 @@ var SVGRenderer = class {
|
|
|
4839
5251
|
// Text/Content components
|
|
4840
5252
|
case "Text":
|
|
4841
5253
|
return this.renderText(node, pos);
|
|
5254
|
+
case "Paragraph":
|
|
5255
|
+
return this.renderParagraph(node, pos);
|
|
4842
5256
|
case "Label":
|
|
4843
5257
|
return this.renderLabel(node, pos);
|
|
4844
5258
|
case "Code":
|
|
@@ -4918,7 +5332,7 @@ var SVGRenderer = class {
|
|
|
4918
5332
|
renderButton(node, pos) {
|
|
4919
5333
|
const text = String(node.props.text || "Button");
|
|
4920
5334
|
const variant = String(node.props.variant || "default");
|
|
4921
|
-
const size = String(node.props.size || "
|
|
5335
|
+
const size = String(node.props.size || "sm");
|
|
4922
5336
|
const disabled = this.parseBooleanProp(node.props.disabled, false);
|
|
4923
5337
|
const density = this.ir.project.style.density || "normal";
|
|
4924
5338
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
@@ -4993,7 +5407,7 @@ var SVGRenderer = class {
|
|
|
4993
5407
|
renderLink(node, pos) {
|
|
4994
5408
|
const text = String(node.props.text || "Link");
|
|
4995
5409
|
const variant = String(node.props.variant || "primary");
|
|
4996
|
-
const size = String(node.props.size || "
|
|
5410
|
+
const size = String(node.props.size || "sm");
|
|
4997
5411
|
const density = this.ir.project.style.density || "normal";
|
|
4998
5412
|
const fontSize = this.tokens.button.fontSize;
|
|
4999
5413
|
const fontWeight = this.tokens.button.fontWeight;
|
|
@@ -5099,7 +5513,33 @@ var SVGRenderer = class {
|
|
|
5099
5513
|
const variant = String(node.props.variant || "default");
|
|
5100
5514
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
5101
5515
|
const showBorder = this.parseBooleanProp(node.props.border, false);
|
|
5102
|
-
const
|
|
5516
|
+
const bgPropStr = String(node.props.background ?? "");
|
|
5517
|
+
let resolvedBg = null;
|
|
5518
|
+
if (bgPropStr === "true") {
|
|
5519
|
+
resolvedBg = this.renderTheme.cardBg;
|
|
5520
|
+
} else if (bgPropStr && bgPropStr !== "false") {
|
|
5521
|
+
if (bgPropStr.startsWith("#") || bgPropStr.startsWith("rgb")) {
|
|
5522
|
+
resolvedBg = bgPropStr;
|
|
5523
|
+
} else if (this.colorResolver.hasColor(bgPropStr)) {
|
|
5524
|
+
resolvedBg = this.colorResolver.resolveColor(bgPropStr, this.renderTheme.cardBg);
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5527
|
+
const showBackground = resolvedBg !== null;
|
|
5528
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
5529
|
+
let titleColor = this.renderTheme.text;
|
|
5530
|
+
let subtitleColor = this.renderTheme.textMuted;
|
|
5531
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
5532
|
+
let resolvedTitleColor = null;
|
|
5533
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
5534
|
+
resolvedTitleColor = colorPropStr;
|
|
5535
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
5536
|
+
resolvedTitleColor = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
5537
|
+
}
|
|
5538
|
+
if (resolvedTitleColor) {
|
|
5539
|
+
titleColor = resolvedTitleColor;
|
|
5540
|
+
subtitleColor = this.hexToRgba(resolvedTitleColor, 0.65);
|
|
5541
|
+
}
|
|
5542
|
+
}
|
|
5103
5543
|
const radiusMap = {
|
|
5104
5544
|
none: 0,
|
|
5105
5545
|
sm: 4,
|
|
@@ -5111,7 +5551,7 @@ var SVGRenderer = class {
|
|
|
5111
5551
|
const topbar = this.calculateTopbarLayout(node, pos, title, subtitle, actions, user);
|
|
5112
5552
|
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
5113
5553
|
if (showBorder || showBackground) {
|
|
5114
|
-
const bg =
|
|
5554
|
+
const bg = resolvedBg ?? "none";
|
|
5115
5555
|
const stroke = showBorder ? this.renderTheme.border : "none";
|
|
5116
5556
|
svg += `
|
|
5117
5557
|
<rect x="${pos.x}" y="${pos.y}"
|
|
@@ -5127,13 +5567,13 @@ var SVGRenderer = class {
|
|
|
5127
5567
|
font-family="Arial, Helvetica, sans-serif"
|
|
5128
5568
|
font-size="18"
|
|
5129
5569
|
font-weight="600"
|
|
5130
|
-
fill="${
|
|
5570
|
+
fill="${titleColor}">${this.escapeXml(topbar.visibleTitle)}</text>`;
|
|
5131
5571
|
if (topbar.hasSubtitle) {
|
|
5132
5572
|
svg += `
|
|
5133
5573
|
<text x="${topbar.textX}" y="${topbar.subtitleY}"
|
|
5134
5574
|
font-family="Arial, Helvetica, sans-serif"
|
|
5135
5575
|
font-size="13"
|
|
5136
|
-
fill="${
|
|
5576
|
+
fill="${subtitleColor}">${this.escapeXml(topbar.visibleSubtitle)}</text>`;
|
|
5137
5577
|
}
|
|
5138
5578
|
if (topbar.leftIcon) {
|
|
5139
5579
|
svg += `
|
|
@@ -5614,10 +6054,16 @@ var SVGRenderer = class {
|
|
|
5614
6054
|
// ============================================================================
|
|
5615
6055
|
renderText(node, pos) {
|
|
5616
6056
|
const text = String(node.props.text || "Text content");
|
|
5617
|
-
const
|
|
6057
|
+
const sizeProp = String(node.props.size || "");
|
|
6058
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
6059
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
6060
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
5618
6061
|
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
6062
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
6063
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
5619
6064
|
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
5620
|
-
const
|
|
6065
|
+
const totalTextHeight = lines.length * lineHeightPx;
|
|
6066
|
+
const firstLineY = pos.y + Math.round(Math.max(0, (pos.height - totalTextHeight) / 2)) + fontSize;
|
|
5621
6067
|
const tspans = lines.map(
|
|
5622
6068
|
(line, index) => `<tspan x="${pos.x}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
5623
6069
|
).join("");
|
|
@@ -5625,13 +6071,54 @@ var SVGRenderer = class {
|
|
|
5625
6071
|
<text x="${pos.x}" y="${firstLineY}"
|
|
5626
6072
|
font-family="Arial, Helvetica, sans-serif"
|
|
5627
6073
|
font-size="${fontSize}"
|
|
6074
|
+
font-weight="${bold ? "700" : "400"}"
|
|
6075
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
5628
6076
|
fill="${this.renderTheme.text}">${tspans}</text>
|
|
5629
6077
|
</g>`;
|
|
5630
6078
|
}
|
|
6079
|
+
renderParagraph(node, pos) {
|
|
6080
|
+
const text = String(node.props.text || "");
|
|
6081
|
+
const sizeProp = String(node.props.size || "");
|
|
6082
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
6083
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
6084
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
6085
|
+
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
6086
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
6087
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
6088
|
+
const align = String(node.props.align || "left");
|
|
6089
|
+
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
6090
|
+
const totalTextHeight = lines.length * lineHeightPx;
|
|
6091
|
+
const firstLineY = pos.y + Math.round(Math.max(0, (pos.height - totalTextHeight) / 2)) + fontSize;
|
|
6092
|
+
let textX;
|
|
6093
|
+
let textAnchor;
|
|
6094
|
+
if (align === "center") {
|
|
6095
|
+
textX = pos.x + pos.width / 2;
|
|
6096
|
+
textAnchor = "middle";
|
|
6097
|
+
} else if (align === "right") {
|
|
6098
|
+
textX = pos.x + pos.width;
|
|
6099
|
+
textAnchor = "end";
|
|
6100
|
+
} else {
|
|
6101
|
+
textX = pos.x;
|
|
6102
|
+
textAnchor = "start";
|
|
6103
|
+
}
|
|
6104
|
+
const tspans = lines.map(
|
|
6105
|
+
(line, index) => `<tspan x="${textX}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
6106
|
+
).join("");
|
|
6107
|
+
return `<g${this.getDataNodeId(node)}>
|
|
6108
|
+
<text x="${textX}" y="${firstLineY}"
|
|
6109
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6110
|
+
font-size="${fontSize}"
|
|
6111
|
+
font-weight="${bold ? "700" : "400"}"
|
|
6112
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
6113
|
+
fill="${this.renderTheme.text}"
|
|
6114
|
+
text-anchor="${textAnchor}">${tspans}</text>
|
|
6115
|
+
</g>`;
|
|
6116
|
+
}
|
|
5631
6117
|
renderLabel(node, pos) {
|
|
5632
6118
|
const text = String(node.props.text || "Label");
|
|
6119
|
+
const textY = pos.y + Math.round(pos.height / 2) + 4;
|
|
5633
6120
|
return `<g${this.getDataNodeId(node)}>
|
|
5634
|
-
<text x="${pos.x}" y="${
|
|
6121
|
+
<text x="${pos.x}" y="${textY}"
|
|
5635
6122
|
font-family="Arial, Helvetica, sans-serif"
|
|
5636
6123
|
font-size="12"
|
|
5637
6124
|
fill="${this.renderTheme.textMuted}">${this.escapeXml(text)}</text>
|
|
@@ -5869,34 +6356,145 @@ var SVGRenderer = class {
|
|
|
5869
6356
|
const tabs = itemsStr ? itemsStr.split(",").map((t) => t.trim()) : ["Tab 1", "Tab 2", "Tab 3"];
|
|
5870
6357
|
const activeProp = node.props.active ?? 0;
|
|
5871
6358
|
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
5872
|
-
const
|
|
6359
|
+
const variant = String(node.props.variant || "default");
|
|
6360
|
+
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
6361
|
+
const radiusMap = { none: 0, sm: 4, md: 6, lg: 10, full: 20 };
|
|
6362
|
+
const tabRadius = radiusMap[String(node.props.radius || "md")] ?? 6;
|
|
6363
|
+
const sizeMap = { sm: 32, md: 44, lg: 52 };
|
|
6364
|
+
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
6365
|
+
const fontSize = 13;
|
|
6366
|
+
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
6367
|
+
const iconsStr = String(node.props.icons || "");
|
|
6368
|
+
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
6369
|
+
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
6370
|
+
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
5873
6371
|
const tabWidth = pos.width / tabs.length;
|
|
5874
|
-
|
|
5875
|
-
|
|
6372
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
6373
|
+
let textColorOverride = null;
|
|
6374
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
6375
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
6376
|
+
textColorOverride = colorPropStr;
|
|
6377
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
6378
|
+
textColorOverride = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
6379
|
+
}
|
|
6380
|
+
}
|
|
6381
|
+
const activeTextColor = textColorOverride ?? (isFlat ? accentColor : "white");
|
|
6382
|
+
const inactiveTextColor = textColorOverride ? this.hexToRgba(textColorOverride, 0.55) : this.renderTheme.textMuted;
|
|
6383
|
+
const hasRadius = tabRadius > 0;
|
|
6384
|
+
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))}`}` : "";
|
|
6385
|
+
let svg = "";
|
|
6386
|
+
if (hasRadius) {
|
|
6387
|
+
svg += `<defs><clipPath id="${clipId}"><rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" rx="${tabRadius}"/></clipPath></defs>`;
|
|
6388
|
+
}
|
|
6389
|
+
svg += `<g${this.getDataNodeId(node)}>`;
|
|
6390
|
+
if (hasRadius) {
|
|
6391
|
+
svg += `<g clip-path="url(#${clipId})">`;
|
|
6392
|
+
}
|
|
6393
|
+
if (!isFlat) {
|
|
6394
|
+
svg += `
|
|
6395
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${tabHeight}"
|
|
6396
|
+
fill="${this.renderTheme.bg}"/>`;
|
|
6397
|
+
}
|
|
5876
6398
|
tabs.forEach((tab, i) => {
|
|
5877
6399
|
const tabX = pos.x + i * tabWidth;
|
|
5878
6400
|
const isActive = i === activeIndex;
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
6401
|
+
const iconName = iconList[i] || "";
|
|
6402
|
+
const tabIconSvg = iconName ? getIcon(iconName) : null;
|
|
6403
|
+
const iconSize = 14;
|
|
6404
|
+
const iconGap = 4;
|
|
6405
|
+
const charW = fontSize * 0.58;
|
|
6406
|
+
const textEst = tab.length * charW;
|
|
6407
|
+
const totalW = tabIconSvg ? iconSize + iconGap + textEst : textEst;
|
|
6408
|
+
const groupX = tabX + Math.round((tabWidth - totalW) / 2);
|
|
6409
|
+
const iconOffY = pos.y + Math.round((tabHeight - iconSize) / 2);
|
|
6410
|
+
if (isFlat) {
|
|
6411
|
+
if (isActive) {
|
|
6412
|
+
svg += `
|
|
6413
|
+
<rect x="${tabX + 6}" y="${pos.y + tabHeight - 3}"
|
|
6414
|
+
width="${tabWidth - 12}" height="3"
|
|
6415
|
+
rx="1.5"
|
|
6416
|
+
fill="${accentColor}"/>`;
|
|
6417
|
+
}
|
|
6418
|
+
if (tabIconSvg) {
|
|
6419
|
+
svg += `
|
|
6420
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
6421
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
6422
|
+
stroke="${isActive ? accentColor : this.renderTheme.textMuted}"
|
|
6423
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6424
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
6425
|
+
</svg>
|
|
6426
|
+
</g>
|
|
6427
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
6428
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6429
|
+
font-size="${fontSize}"
|
|
6430
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6431
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}">${this.escapeXml(tab)}</text>`;
|
|
6432
|
+
} else {
|
|
6433
|
+
svg += `
|
|
6434
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
6435
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6436
|
+
font-size="${fontSize}"
|
|
6437
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6438
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}"
|
|
5890
6439
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6440
|
+
}
|
|
6441
|
+
} else {
|
|
6442
|
+
svg += `
|
|
6443
|
+
<rect x="${tabX}" y="${pos.y}"
|
|
6444
|
+
width="${tabWidth}" height="${tabHeight}"
|
|
6445
|
+
rx="${!showBorder && hasRadius && isActive ? tabRadius : 0}"
|
|
6446
|
+
fill="${isActive ? accentColor : "transparent"}"
|
|
6447
|
+
${!isActive && showBorder ? `stroke="${this.renderTheme.border}" stroke-width="0.5"` : ""}/>`;
|
|
6448
|
+
if (tabIconSvg) {
|
|
6449
|
+
svg += `
|
|
6450
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
6451
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
6452
|
+
stroke="${isActive ? activeTextColor : this.renderTheme.textMuted}"
|
|
6453
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6454
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
6455
|
+
</svg>
|
|
6456
|
+
</g>
|
|
6457
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
6458
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6459
|
+
font-size="${fontSize}"
|
|
6460
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6461
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}">${this.escapeXml(tab)}</text>`;
|
|
6462
|
+
} else {
|
|
6463
|
+
svg += `
|
|
6464
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
6465
|
+
font-family="Arial, Helvetica, sans-serif"
|
|
6466
|
+
font-size="${fontSize}"
|
|
6467
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
6468
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}"
|
|
6469
|
+
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
5891
6472
|
});
|
|
5892
|
-
|
|
6473
|
+
const contentY = pos.y + tabHeight;
|
|
6474
|
+
const contentH = pos.height - tabHeight;
|
|
6475
|
+
if (contentH >= 20 && !isFlat) {
|
|
6476
|
+
svg += `
|
|
5893
6477
|
<!-- Tab content area -->
|
|
5894
|
-
<rect x="${pos.x}" y="${
|
|
5895
|
-
width="${pos.width}" height="${
|
|
5896
|
-
fill="${this.renderTheme.cardBg}"
|
|
5897
|
-
stroke="${this.renderTheme.border}"
|
|
5898
|
-
|
|
6478
|
+
<rect x="${pos.x}" y="${contentY}"
|
|
6479
|
+
width="${pos.width}" height="${contentH}"
|
|
6480
|
+
fill="${this.renderTheme.cardBg}"
|
|
6481
|
+
${showBorder ? `stroke="${this.renderTheme.border}" stroke-width="1"` : ""}/>`;
|
|
6482
|
+
} else if (isFlat && showBorder) {
|
|
6483
|
+
svg += `
|
|
6484
|
+
<line x1="${pos.x}" y1="${contentY}" x2="${pos.x + pos.width}" y2="${contentY}"
|
|
6485
|
+
stroke="${this.renderTheme.border}" stroke-width="1"/>`;
|
|
6486
|
+
}
|
|
6487
|
+
if (hasRadius) {
|
|
6488
|
+
svg += `
|
|
5899
6489
|
</g>`;
|
|
6490
|
+
if (!isFlat && showBorder) {
|
|
6491
|
+
svg += `
|
|
6492
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}"
|
|
6493
|
+
rx="${tabRadius}" fill="none"
|
|
6494
|
+
stroke="${this.renderTheme.border}" stroke-width="1"/>`;
|
|
6495
|
+
}
|
|
6496
|
+
}
|
|
6497
|
+
svg += "\n </g>";
|
|
5900
6498
|
return svg;
|
|
5901
6499
|
}
|
|
5902
6500
|
renderDivider(node, pos) {
|
|
@@ -5963,15 +6561,20 @@ var SVGRenderer = class {
|
|
|
5963
6561
|
const hasExplicitVariantColor = semanticBase !== void 0 || this.colorResolver.hasColor(variant);
|
|
5964
6562
|
const bgColor = hasExplicitVariantColor ? this.resolveVariantColor(variant, this.renderTheme.primary) : this.renderTheme.border;
|
|
5965
6563
|
const textColor = hasExplicitVariantColor ? "white" : this.renderTheme.text;
|
|
6564
|
+
const size = String(node.props.size || "md");
|
|
6565
|
+
const BADGE_FONT_SIZES = { xs: 9, sm: 11, md: 12, lg: 13, xl: 15 };
|
|
6566
|
+
const fontSize = BADGE_FONT_SIZES[size] ?? this.tokens.badge.fontSize;
|
|
6567
|
+
const customPadding = node.props.padding !== void 0 ? Number(node.props.padding) : void 0;
|
|
6568
|
+
const BADGE_PAD_X = { xs: 5, sm: 6, md: 8, lg: 10, xl: 14 };
|
|
6569
|
+
const paddingX = customPadding !== void 0 && !isNaN(customPadding) ? customPadding : BADGE_PAD_X[size] ?? this.tokens.badge.paddingX;
|
|
5966
6570
|
const badgeRadius = this.tokens.badge.radius === "pill" ? pos.height / 2 : this.tokens.badge.radius;
|
|
5967
|
-
const fontSize = this.tokens.badge.fontSize;
|
|
5968
6571
|
return `<g${this.getDataNodeId(node)}>
|
|
5969
6572
|
<rect x="${pos.x}" y="${pos.y}"
|
|
5970
6573
|
width="${pos.width}" height="${pos.height}"
|
|
5971
6574
|
rx="${badgeRadius}"
|
|
5972
6575
|
fill="${bgColor}"
|
|
5973
6576
|
stroke="none"/>
|
|
5974
|
-
<text x="${pos.x + pos.width / 2}" y="${pos.y + pos.height / 2 +
|
|
6577
|
+
<text x="${pos.x + paddingX + (pos.width - paddingX * 2) / 2}" y="${pos.y + pos.height / 2 + fontSize * 0.35}"
|
|
5975
6578
|
font-family="Arial, Helvetica, sans-serif"
|
|
5976
6579
|
font-size="${fontSize}"
|
|
5977
6580
|
font-weight="600"
|
|
@@ -6180,11 +6783,20 @@ var SVGRenderer = class {
|
|
|
6180
6783
|
return svg;
|
|
6181
6784
|
}
|
|
6182
6785
|
renderImage(node, pos) {
|
|
6183
|
-
const placeholder = String(node.props.
|
|
6786
|
+
const placeholder = String(node.props.type || "landscape").toLowerCase();
|
|
6184
6787
|
const placeholderIcon = String(node.props.icon || "").trim();
|
|
6185
6788
|
const variant = String(node.props.variant || "").trim();
|
|
6186
6789
|
const placeholderIconSvg = placeholder === "icon" && placeholderIcon ? getIcon(placeholderIcon) : null;
|
|
6187
6790
|
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
6791
|
+
const hasExplicitHeight = node.props.height !== void 0 && !isNaN(Number(node.props.height)) && Number(node.props.height) > 0;
|
|
6792
|
+
const isCircle = this.parseBooleanProp(node.props.circle, false);
|
|
6793
|
+
const useClip = isCircle || hasExplicitHeight;
|
|
6794
|
+
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))}`}` : "";
|
|
6795
|
+
const imgCx = pos.x + pos.width / 2;
|
|
6796
|
+
const imgCy = pos.y + pos.height / 2;
|
|
6797
|
+
const imgR = Math.min(pos.width, pos.height) / 2;
|
|
6798
|
+
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>` : "";
|
|
6799
|
+
const clipAttr = useClip ? ` clip-path="url(#${clipId})"` : "";
|
|
6188
6800
|
const aspectRatios = {
|
|
6189
6801
|
landscape: 16 / 9,
|
|
6190
6802
|
portrait: 2 / 3,
|
|
@@ -6193,12 +6805,24 @@ var SVGRenderer = class {
|
|
|
6193
6805
|
avatar: 1
|
|
6194
6806
|
};
|
|
6195
6807
|
const ratio = aspectRatios[placeholder] || 16 / 9;
|
|
6196
|
-
|
|
6197
|
-
let
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6808
|
+
let iconWidth;
|
|
6809
|
+
let iconHeight;
|
|
6810
|
+
if (isCircle) {
|
|
6811
|
+
const diameter = 2 * imgR;
|
|
6812
|
+
iconWidth = diameter;
|
|
6813
|
+
iconHeight = diameter / ratio;
|
|
6814
|
+
if (iconHeight < diameter) {
|
|
6815
|
+
iconHeight = diameter;
|
|
6816
|
+
iconWidth = diameter * ratio;
|
|
6817
|
+
}
|
|
6818
|
+
} else {
|
|
6819
|
+
const maxSize = Math.min(pos.width, pos.height) * 0.8;
|
|
6820
|
+
iconWidth = maxSize;
|
|
6821
|
+
iconHeight = maxSize / ratio;
|
|
6822
|
+
if (iconHeight > pos.height * 0.8) {
|
|
6823
|
+
iconHeight = pos.height * 0.8;
|
|
6824
|
+
iconWidth = iconHeight * ratio;
|
|
6825
|
+
}
|
|
6202
6826
|
}
|
|
6203
6827
|
const offsetX = pos.x + (pos.width - iconWidth) / 2;
|
|
6204
6828
|
const offsetY = pos.y + (pos.height - iconHeight) / 2;
|
|
@@ -6211,17 +6835,18 @@ var SVGRenderer = class {
|
|
|
6211
6835
|
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
6212
6836
|
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
6213
6837
|
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
6214
|
-
|
|
6838
|
+
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"/>`;
|
|
6839
|
+
return `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
6215
6840
|
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${bgColor}" rx="4"/>
|
|
6216
6841
|
<g transform="translate(${iconOffsetX}, ${iconOffsetY})">
|
|
6217
6842
|
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6218
6843
|
${this.extractSvgContent(placeholderIconSvg)}
|
|
6219
6844
|
</svg>
|
|
6220
6845
|
</g>
|
|
6221
|
-
|
|
6846
|
+
${iconBorderShape}
|
|
6222
6847
|
</g>`;
|
|
6223
6848
|
}
|
|
6224
|
-
let svg =
|
|
6849
|
+
let svg = `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
6225
6850
|
<!-- Image Background -->
|
|
6226
6851
|
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" fill="${imageBg}"/>`;
|
|
6227
6852
|
if (["landscape", "portrait", "square"].includes(placeholder)) {
|
|
@@ -6279,10 +6904,10 @@ var SVGRenderer = class {
|
|
|
6279
6904
|
width="${personWidth * 0.6}" height="${personHeight * 0.5}"
|
|
6280
6905
|
fill="#666" rx="3"/>`;
|
|
6281
6906
|
}
|
|
6907
|
+
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"/>`;
|
|
6282
6908
|
svg += `
|
|
6283
6909
|
<!-- Border -->
|
|
6284
|
-
|
|
6285
|
-
fill="none" stroke="${this.renderTheme.border}" stroke-width="1" rx="4"/>
|
|
6910
|
+
${borderShape}
|
|
6286
6911
|
</g>`;
|
|
6287
6912
|
return svg;
|
|
6288
6913
|
}
|
|
@@ -6386,10 +7011,29 @@ var SVGRenderer = class {
|
|
|
6386
7011
|
</g>`;
|
|
6387
7012
|
}
|
|
6388
7013
|
const iconSize = this.getIconSize(size);
|
|
6389
|
-
const
|
|
6390
|
-
const
|
|
7014
|
+
const paddingPx = Math.max(0, Number(node.props.padding || 0));
|
|
7015
|
+
const renderedIconSize = Math.max(4, iconSize - paddingPx * 2);
|
|
7016
|
+
const offsetX = pos.x + (pos.width - renderedIconSize) / 2;
|
|
7017
|
+
const offsetY = pos.y + (pos.height - renderedIconSize) / 2;
|
|
7018
|
+
const isIconCircle = this.parseBooleanProp(node.props.circle, false);
|
|
7019
|
+
if (isIconCircle) {
|
|
7020
|
+
const cx = pos.x + pos.width / 2;
|
|
7021
|
+
const cy = pos.y + pos.height / 2;
|
|
7022
|
+
const r = Math.min(pos.width, pos.height) / 2;
|
|
7023
|
+
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))}`}`;
|
|
7024
|
+
const bgColor = this.hexToRgba(iconColor, 0.12);
|
|
7025
|
+
return `<defs><clipPath id="${iconClipId}"><circle cx="${cx}" cy="${cy}" r="${r}"/></clipPath></defs><g${this.getDataNodeId(node)} clip-path="url(#${iconClipId})">
|
|
7026
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="${bgColor}"/>
|
|
7027
|
+
<g transform="translate(${offsetX}, ${offsetY})">
|
|
7028
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
7029
|
+
${this.extractSvgContent(iconSvg)}
|
|
7030
|
+
</svg>
|
|
7031
|
+
</g>
|
|
7032
|
+
</g>
|
|
7033
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${this.hexToRgba(iconColor, 0.35)}" stroke-width="1"/>`;
|
|
7034
|
+
}
|
|
6391
7035
|
const wrappedSvg = `<g${this.getDataNodeId(node)} transform="translate(${offsetX}, ${offsetY})">
|
|
6392
|
-
<svg width="${
|
|
7036
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
6393
7037
|
${this.extractSvgContent(iconSvg)}
|
|
6394
7038
|
</svg>
|
|
6395
7039
|
</g>`;
|
|
@@ -6398,7 +7042,7 @@ var SVGRenderer = class {
|
|
|
6398
7042
|
renderIconButton(node, pos) {
|
|
6399
7043
|
const iconName = String(node.props.icon || "help-circle");
|
|
6400
7044
|
const variant = String(node.props.variant || "default");
|
|
6401
|
-
const size = String(node.props.size || "
|
|
7045
|
+
const size = String(node.props.size || "sm");
|
|
6402
7046
|
const disabled = String(node.props.disabled || "false") === "true";
|
|
6403
7047
|
const density = this.ir.project.style.density || "normal";
|
|
6404
7048
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
@@ -6526,7 +7170,7 @@ var SVGRenderer = class {
|
|
|
6526
7170
|
wrapTextToLines(text, maxWidth, fontSize) {
|
|
6527
7171
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
6528
7172
|
const paragraphs = normalized.split("\n");
|
|
6529
|
-
const charWidth = fontSize * 0.
|
|
7173
|
+
const charWidth = fontSize * 0.5;
|
|
6530
7174
|
const safeWidth = Math.max(maxWidth || 0, charWidth);
|
|
6531
7175
|
const maxCharsPerLine = Math.max(1, Math.floor(safeWidth / charWidth));
|
|
6532
7176
|
const lines = [];
|
|
@@ -6768,8 +7412,8 @@ var SVGRenderer = class {
|
|
|
6768
7412
|
return false;
|
|
6769
7413
|
}
|
|
6770
7414
|
const direction = String(parent.params.direction || "vertical");
|
|
6771
|
-
const
|
|
6772
|
-
return direction === "horizontal" &&
|
|
7415
|
+
const justify = parent.style.justify || "stretch";
|
|
7416
|
+
return direction === "horizontal" && justify === "stretch";
|
|
6773
7417
|
}
|
|
6774
7418
|
buildParentContainerIndex() {
|
|
6775
7419
|
this.parentContainerByChildId.clear();
|
|
@@ -7641,7 +8285,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7641
8285
|
renderButton(node, pos) {
|
|
7642
8286
|
const text = String(node.props.text || "Button");
|
|
7643
8287
|
const variant = String(node.props.variant || "default");
|
|
7644
|
-
const size = String(node.props.size || "
|
|
8288
|
+
const size = String(node.props.size || "sm");
|
|
7645
8289
|
const density = this.ir.project.style.density || "normal";
|
|
7646
8290
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
7647
8291
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
@@ -7748,7 +8392,7 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
7748
8392
|
renderIconButton(node, pos) {
|
|
7749
8393
|
const iconName = String(node.props.icon || "help-circle");
|
|
7750
8394
|
const variant = String(node.props.variant || "default");
|
|
7751
|
-
const size = String(node.props.size || "
|
|
8395
|
+
const size = String(node.props.size || "sm");
|
|
7752
8396
|
const density = this.ir.project.style.density || "normal";
|
|
7753
8397
|
const labelOffset = this.parseBooleanProp(node.props.labelSpace, false) ? 18 : 0;
|
|
7754
8398
|
const extraPadding = resolveControlHorizontalPadding(String(node.props.padding || "none"), density);
|
|
@@ -8001,26 +8645,58 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8001
8645
|
const variant = String(node.props.variant || "default");
|
|
8002
8646
|
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
8003
8647
|
const topbar = this.calculateTopbarLayout(node, pos, title, subtitle, actions, user);
|
|
8004
|
-
|
|
8648
|
+
const bgPropStr = String(node.props.background ?? "");
|
|
8649
|
+
let sketchTopbarBg = this.renderTheme.cardBg;
|
|
8650
|
+
if (bgPropStr && bgPropStr !== "false" && bgPropStr !== "true") {
|
|
8651
|
+
if (bgPropStr.startsWith("#") || bgPropStr.startsWith("rgb")) {
|
|
8652
|
+
sketchTopbarBg = bgPropStr;
|
|
8653
|
+
} else if (this.colorResolver.hasColor(bgPropStr)) {
|
|
8654
|
+
sketchTopbarBg = this.colorResolver.resolveColor(bgPropStr, this.renderTheme.cardBg);
|
|
8655
|
+
}
|
|
8656
|
+
}
|
|
8657
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
8658
|
+
let titleColor = this.renderTheme.text;
|
|
8659
|
+
let subtitleColor = this.renderTheme.textMuted;
|
|
8660
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
8661
|
+
let resolvedTitleColor = null;
|
|
8662
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
8663
|
+
resolvedTitleColor = colorPropStr;
|
|
8664
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
8665
|
+
resolvedTitleColor = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
8666
|
+
}
|
|
8667
|
+
if (resolvedTitleColor) {
|
|
8668
|
+
titleColor = resolvedTitleColor;
|
|
8669
|
+
subtitleColor = this.hexToRgba(resolvedTitleColor, 0.65);
|
|
8670
|
+
}
|
|
8671
|
+
}
|
|
8672
|
+
const showBorder = this.parseBooleanProp(node.props.border, false);
|
|
8673
|
+
const hasBgProp = bgPropStr === "true" || bgPropStr && bgPropStr !== "false" && bgPropStr !== "";
|
|
8674
|
+
const showBackground = hasBgProp;
|
|
8675
|
+
let svg = `<g${this.getDataNodeId(node)}>`;
|
|
8676
|
+
if (showBorder || showBackground) {
|
|
8677
|
+
const stroke = showBorder ? "#2D3748" : "none";
|
|
8678
|
+
svg += `
|
|
8005
8679
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8006
8680
|
width="${pos.width}" height="${pos.height}"
|
|
8007
|
-
fill="${
|
|
8008
|
-
stroke="
|
|
8681
|
+
fill="${sketchTopbarBg}"
|
|
8682
|
+
stroke="${stroke}"
|
|
8009
8683
|
stroke-width="0.5"
|
|
8010
|
-
filter="url(#sketch-rough)"
|
|
8011
|
-
|
|
8012
|
-
|
|
8684
|
+
filter="url(#sketch-rough)"/>`;
|
|
8685
|
+
}
|
|
8686
|
+
svg += `
|
|
8687
|
+
<!-- Title -->`;
|
|
8688
|
+
svg += `
|
|
8013
8689
|
<text x="${topbar.textX}" y="${topbar.titleY}"
|
|
8014
8690
|
font-family="${this.fontFamily}"
|
|
8015
8691
|
font-size="18"
|
|
8016
8692
|
font-weight="600"
|
|
8017
|
-
fill="${
|
|
8693
|
+
fill="${titleColor}">${this.escapeXml(topbar.visibleTitle)}</text>`;
|
|
8018
8694
|
if (topbar.hasSubtitle) {
|
|
8019
8695
|
svg += `
|
|
8020
8696
|
<text x="${topbar.textX}" y="${topbar.subtitleY}"
|
|
8021
8697
|
font-family="${this.fontFamily}"
|
|
8022
8698
|
font-size="13"
|
|
8023
|
-
fill="${
|
|
8699
|
+
fill="${subtitleColor}">${this.escapeXml(topbar.visibleSubtitle)}</text>`;
|
|
8024
8700
|
}
|
|
8025
8701
|
if (topbar.leftIcon) {
|
|
8026
8702
|
svg += `
|
|
@@ -8100,8 +8776,13 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8100
8776
|
*/
|
|
8101
8777
|
renderText(node, pos) {
|
|
8102
8778
|
const text = String(node.props.text || "Text content");
|
|
8103
|
-
const
|
|
8779
|
+
const sizeProp = String(node.props.size || "");
|
|
8780
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
8781
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
8782
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
8104
8783
|
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
8784
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
8785
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
8105
8786
|
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
8106
8787
|
const firstLineY = pos.y + fontSize;
|
|
8107
8788
|
const tspans = lines.map(
|
|
@@ -8111,9 +8792,48 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8111
8792
|
<text x="${pos.x}" y="${firstLineY}"
|
|
8112
8793
|
font-family="${this.fontFamily}"
|
|
8113
8794
|
font-size="${fontSize}"
|
|
8795
|
+
font-weight="${bold ? "700" : "400"}"
|
|
8796
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
8114
8797
|
fill="${this.renderTheme.text}">${tspans}</text>
|
|
8115
8798
|
</g>`;
|
|
8116
8799
|
}
|
|
8800
|
+
renderParagraph(node, pos) {
|
|
8801
|
+
const text = String(node.props.text || "");
|
|
8802
|
+
const sizeProp = String(node.props.size || "");
|
|
8803
|
+
const defaultFontSize = this.tokens.text.fontSize;
|
|
8804
|
+
const textFontSizeMap = { xs: 10, sm: 12, lg: 16, xl: 20 };
|
|
8805
|
+
const fontSize = textFontSizeMap[sizeProp] ?? defaultFontSize;
|
|
8806
|
+
const lineHeightPx = Math.ceil(fontSize * this.tokens.text.lineHeight);
|
|
8807
|
+
const bold = this.parseBooleanProp(node.props.bold, false);
|
|
8808
|
+
const italic = this.parseBooleanProp(node.props.italic, false);
|
|
8809
|
+
const align = String(node.props.align || "left");
|
|
8810
|
+
const lines = this.wrapTextToLines(text, pos.width, fontSize);
|
|
8811
|
+
const firstLineY = pos.y + fontSize;
|
|
8812
|
+
let textX;
|
|
8813
|
+
let textAnchor;
|
|
8814
|
+
if (align === "center") {
|
|
8815
|
+
textX = pos.x + pos.width / 2;
|
|
8816
|
+
textAnchor = "middle";
|
|
8817
|
+
} else if (align === "right") {
|
|
8818
|
+
textX = pos.x + pos.width;
|
|
8819
|
+
textAnchor = "end";
|
|
8820
|
+
} else {
|
|
8821
|
+
textX = pos.x;
|
|
8822
|
+
textAnchor = "start";
|
|
8823
|
+
}
|
|
8824
|
+
const tspans = lines.map(
|
|
8825
|
+
(line, index) => `<tspan x="${textX}" dy="${index === 0 ? 0 : lineHeightPx}">${this.escapeXml(line)}</tspan>`
|
|
8826
|
+
).join("");
|
|
8827
|
+
return `<g${this.getDataNodeId(node)}>
|
|
8828
|
+
<text x="${textX}" y="${firstLineY}"
|
|
8829
|
+
font-family="${this.fontFamily}"
|
|
8830
|
+
font-size="${fontSize}"
|
|
8831
|
+
font-weight="${bold ? "700" : "400"}"
|
|
8832
|
+
font-style="${italic ? "italic" : "normal"}"
|
|
8833
|
+
fill="${this.renderTheme.text}"
|
|
8834
|
+
text-anchor="${textAnchor}">${tspans}</text>
|
|
8835
|
+
</g>`;
|
|
8836
|
+
}
|
|
8117
8837
|
/**
|
|
8118
8838
|
* Render label with Comic Sans
|
|
8119
8839
|
*/
|
|
@@ -8344,36 +9064,150 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8344
9064
|
renderTabs(node, pos) {
|
|
8345
9065
|
const itemsStr = String(node.props.items || "");
|
|
8346
9066
|
const tabs = itemsStr ? itemsStr.split(",").map((t) => t.trim()) : ["Tab 1", "Tab 2", "Tab 3"];
|
|
8347
|
-
const
|
|
9067
|
+
const activeProp = node.props.active ?? 0;
|
|
9068
|
+
const activeIndex = Number.isFinite(Number(activeProp)) ? Math.max(0, Math.floor(Number(activeProp))) : 0;
|
|
9069
|
+
const variant = String(node.props.variant || "default");
|
|
9070
|
+
const accentColor = variant === "default" ? this.resolveAccentColor() : this.resolveVariantColor(variant, this.resolveAccentColor());
|
|
9071
|
+
const radiusMap = { none: 0, sm: 4, md: 6, lg: 10, full: 20 };
|
|
9072
|
+
const tabRadius = radiusMap[String(node.props.radius || "md")] ?? 6;
|
|
9073
|
+
const sizeMap = { sm: 32, md: 44, lg: 52 };
|
|
9074
|
+
const tabHeight = pos.height > 0 ? pos.height : sizeMap[String(node.props.size || "md")] ?? 44;
|
|
9075
|
+
const fontSize = 13;
|
|
9076
|
+
const textY = pos.y + Math.round(tabHeight / 2) + Math.round(fontSize * 0.4);
|
|
9077
|
+
const iconsStr = String(node.props.icons || "");
|
|
9078
|
+
const iconList = iconsStr ? iconsStr.split(",").map((s) => s.trim()) : [];
|
|
9079
|
+
const isFlat = this.parseBooleanProp(node.props.flat, false);
|
|
9080
|
+
const showBorder = this.parseBooleanProp(node.props.border, true);
|
|
8348
9081
|
const tabWidth = pos.width / tabs.length;
|
|
8349
|
-
|
|
8350
|
-
|
|
9082
|
+
const colorPropStr = String(node.props.color ?? "");
|
|
9083
|
+
let textColorOverride = null;
|
|
9084
|
+
if (colorPropStr && colorPropStr !== "false") {
|
|
9085
|
+
if (colorPropStr.startsWith("#") || colorPropStr.startsWith("rgb")) {
|
|
9086
|
+
textColorOverride = colorPropStr;
|
|
9087
|
+
} else if (this.colorResolver.hasColor(colorPropStr)) {
|
|
9088
|
+
textColorOverride = this.colorResolver.resolveColor(colorPropStr, this.renderTheme.text);
|
|
9089
|
+
}
|
|
9090
|
+
}
|
|
9091
|
+
const activeTextColor = textColorOverride ?? (isFlat ? accentColor : "white");
|
|
9092
|
+
const inactiveTextColor = textColorOverride ? this.hexToRgba(textColorOverride, 0.55) : this.renderTheme.textMuted;
|
|
9093
|
+
const hasRadius = tabRadius > 0;
|
|
9094
|
+
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))}`}` : "";
|
|
9095
|
+
let svg = "";
|
|
9096
|
+
if (hasRadius) {
|
|
9097
|
+
svg += `<defs><clipPath id="${clipId}"><rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}" rx="${tabRadius}"/></clipPath></defs>`;
|
|
9098
|
+
}
|
|
9099
|
+
svg += `<g${this.getDataNodeId(node)}>`;
|
|
9100
|
+
if (hasRadius) {
|
|
9101
|
+
svg += `<g clip-path="url(#${clipId})">`;
|
|
9102
|
+
}
|
|
9103
|
+
if (!isFlat) {
|
|
9104
|
+
svg += `
|
|
9105
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${tabHeight}"
|
|
9106
|
+
fill="${this.renderTheme.bg}"/>`;
|
|
9107
|
+
}
|
|
8351
9108
|
tabs.forEach((tab, i) => {
|
|
8352
9109
|
const tabX = pos.x + i * tabWidth;
|
|
8353
|
-
const isActive = i ===
|
|
8354
|
-
|
|
9110
|
+
const isActive = i === activeIndex;
|
|
9111
|
+
const iconName = iconList[i] || "";
|
|
9112
|
+
const tabIconSvg = iconName ? getIcon(iconName) : null;
|
|
9113
|
+
const iconSize = 14;
|
|
9114
|
+
const iconGap = 4;
|
|
9115
|
+
const charW = fontSize * 0.58;
|
|
9116
|
+
const textEst = tab.length * charW;
|
|
9117
|
+
const totalW = tabIconSvg ? iconSize + iconGap + textEst : textEst;
|
|
9118
|
+
const groupX = tabX + Math.round((tabWidth - totalW) / 2);
|
|
9119
|
+
const iconOffY = pos.y + Math.round((tabHeight - iconSize) / 2);
|
|
9120
|
+
if (isFlat) {
|
|
9121
|
+
if (isActive) {
|
|
9122
|
+
svg += `
|
|
9123
|
+
<rect x="${tabX + 6}" y="${pos.y + tabHeight - 3}"
|
|
9124
|
+
width="${tabWidth - 12}" height="3"
|
|
9125
|
+
rx="1.5"
|
|
9126
|
+
fill="${accentColor}"
|
|
9127
|
+
filter="url(#sketch-rough)"/>`;
|
|
9128
|
+
}
|
|
9129
|
+
if (tabIconSvg) {
|
|
9130
|
+
svg += `
|
|
9131
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
9132
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
9133
|
+
stroke="${isActive ? accentColor : this.renderTheme.textMuted}"
|
|
9134
|
+
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
9135
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
9136
|
+
</svg>
|
|
9137
|
+
</g>
|
|
9138
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
9139
|
+
font-family="${this.fontFamily}"
|
|
9140
|
+
font-size="${fontSize}"
|
|
9141
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
9142
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}">${this.escapeXml(tab)}</text>`;
|
|
9143
|
+
} else {
|
|
9144
|
+
svg += `
|
|
9145
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
9146
|
+
font-family="${this.fontFamily}"
|
|
9147
|
+
font-size="${fontSize}"
|
|
9148
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
9149
|
+
fill="${isActive ? activeTextColor : inactiveTextColor}"
|
|
9150
|
+
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
9151
|
+
}
|
|
9152
|
+
} else {
|
|
9153
|
+
svg += `
|
|
8355
9154
|
<rect x="${tabX}" y="${pos.y}"
|
|
8356
|
-
width="${tabWidth}" height="
|
|
9155
|
+
width="${tabWidth}" height="${tabHeight}"
|
|
9156
|
+
rx="${!showBorder && hasRadius && isActive ? tabRadius : 0}"
|
|
8357
9157
|
fill="${isActive ? accentColor : "transparent"}"
|
|
8358
|
-
stroke="${isActive ? accentColor : "#2D3748"}"
|
|
9158
|
+
stroke="${isActive ? accentColor : showBorder ? "#2D3748" : "none"}"
|
|
8359
9159
|
stroke-width="0.5"
|
|
8360
|
-
filter="url(#sketch-rough)"
|
|
8361
|
-
|
|
9160
|
+
filter="url(#sketch-rough)"/>`;
|
|
9161
|
+
if (tabIconSvg) {
|
|
9162
|
+
svg += `
|
|
9163
|
+
<g transform="translate(${groupX}, ${iconOffY})">
|
|
9164
|
+
<svg width="${iconSize}" height="${iconSize}" viewBox="0 0 24 24" fill="none"
|
|
9165
|
+
stroke="${isActive ? activeTextColor : this.renderTheme.textMuted}"
|
|
9166
|
+
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
9167
|
+
${this.extractSvgContent(tabIconSvg)}
|
|
9168
|
+
</svg>
|
|
9169
|
+
</g>
|
|
9170
|
+
<text x="${groupX + iconSize + iconGap}" y="${textY}"
|
|
8362
9171
|
font-family="${this.fontFamily}"
|
|
8363
|
-
font-size="
|
|
9172
|
+
font-size="${fontSize}"
|
|
9173
|
+
font-weight="${isActive ? "600" : "500"}"
|
|
9174
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}">${this.escapeXml(tab)}</text>`;
|
|
9175
|
+
} else {
|
|
9176
|
+
svg += `
|
|
9177
|
+
<text x="${tabX + tabWidth / 2}" y="${textY}"
|
|
9178
|
+
font-family="${this.fontFamily}"
|
|
9179
|
+
font-size="${fontSize}"
|
|
8364
9180
|
font-weight="${isActive ? "600" : "500"}"
|
|
8365
|
-
fill="${isActive ?
|
|
9181
|
+
fill="${isActive ? activeTextColor : this.renderTheme.text}"
|
|
8366
9182
|
text-anchor="middle">${this.escapeXml(tab)}</text>`;
|
|
9183
|
+
}
|
|
9184
|
+
}
|
|
8367
9185
|
});
|
|
8368
|
-
|
|
9186
|
+
const contentY = pos.y + tabHeight;
|
|
9187
|
+
const contentH = pos.height - tabHeight;
|
|
9188
|
+
if (contentH >= 20 && !isFlat) {
|
|
9189
|
+
svg += `
|
|
8369
9190
|
<!-- Tab content area -->
|
|
8370
|
-
<rect x="${pos.x}" y="${
|
|
8371
|
-
width="${pos.width}" height="${
|
|
9191
|
+
<rect x="${pos.x}" y="${contentY}"
|
|
9192
|
+
width="${pos.width}" height="${contentH}"
|
|
8372
9193
|
fill="${this.renderTheme.cardBg}"
|
|
8373
|
-
stroke="#2D3748"
|
|
8374
|
-
|
|
8375
|
-
|
|
9194
|
+
${showBorder ? 'stroke="#2D3748" stroke-width="0.5" filter="url(#sketch-rough)"' : ""}/>`;
|
|
9195
|
+
} else if (isFlat && showBorder) {
|
|
9196
|
+
svg += `
|
|
9197
|
+
<line x1="${pos.x}" y1="${contentY}" x2="${pos.x + pos.width}" y2="${contentY}"
|
|
9198
|
+
stroke="#2D3748" stroke-width="0.5" filter="url(#sketch-rough)"/>`;
|
|
9199
|
+
}
|
|
9200
|
+
if (hasRadius) {
|
|
9201
|
+
svg += `
|
|
8376
9202
|
</g>`;
|
|
9203
|
+
if (!isFlat && showBorder) {
|
|
9204
|
+
svg += `
|
|
9205
|
+
<rect x="${pos.x}" y="${pos.y}" width="${pos.width}" height="${pos.height}"
|
|
9206
|
+
rx="${tabRadius}" fill="none" stroke="#2D3748" stroke-width="0.5"
|
|
9207
|
+
filter="url(#sketch-rough)"/>`;
|
|
9208
|
+
}
|
|
9209
|
+
}
|
|
9210
|
+
svg += "\n </g>";
|
|
8377
9211
|
return svg;
|
|
8378
9212
|
}
|
|
8379
9213
|
/**
|
|
@@ -8610,11 +9444,20 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8610
9444
|
* Render image with sketch filter
|
|
8611
9445
|
*/
|
|
8612
9446
|
renderImage(node, pos) {
|
|
8613
|
-
const placeholder = String(node.props.
|
|
9447
|
+
const placeholder = String(node.props.type || "landscape").toLowerCase();
|
|
8614
9448
|
const iconType = String(node.props.icon || "").trim();
|
|
8615
9449
|
const variant = String(node.props.variant || "").trim();
|
|
8616
9450
|
const iconSvg = placeholder === "icon" && iconType.length > 0 ? getIcon(iconType) : null;
|
|
8617
9451
|
const imageBg = this.options.theme === "dark" ? "#2A2A2A" : "#E8E8E8";
|
|
9452
|
+
const hasExplicitHeight = node.props.height !== void 0 && !isNaN(Number(node.props.height)) && Number(node.props.height) > 0;
|
|
9453
|
+
const isCircle = this.parseBooleanProp(node.props.circle, false);
|
|
9454
|
+
const useClip = isCircle || hasExplicitHeight;
|
|
9455
|
+
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))}`}` : "";
|
|
9456
|
+
const imgCx = pos.x + pos.width / 2;
|
|
9457
|
+
const imgCy = pos.y + pos.height / 2;
|
|
9458
|
+
const imgR = Math.min(pos.width, pos.height) / 2;
|
|
9459
|
+
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>` : "";
|
|
9460
|
+
const clipAttr = useClip ? ` clip-path="url(#${clipId})"` : "";
|
|
8618
9461
|
if (iconSvg) {
|
|
8619
9462
|
const semanticBase = variant ? this.getSemanticVariantColor(variant) : void 0;
|
|
8620
9463
|
const hasVariant = variant.length > 0 && (semanticBase !== void 0 || this.colorResolver.hasColor(variant));
|
|
@@ -8624,7 +9467,8 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8624
9467
|
const iconSize = Math.max(16, Math.min(pos.width, pos.height) * 0.6);
|
|
8625
9468
|
const iconOffsetX = pos.x + (pos.width - iconSize) / 2;
|
|
8626
9469
|
const iconOffsetY = pos.y + (pos.height - iconSize) / 2;
|
|
8627
|
-
|
|
9470
|
+
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)"/>`;
|
|
9471
|
+
return `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
8628
9472
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8629
9473
|
width="${pos.width}" height="${pos.height}"
|
|
8630
9474
|
fill="${bgColor}"
|
|
@@ -8635,17 +9479,11 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8635
9479
|
${this.extractSvgContent(iconSvg)}
|
|
8636
9480
|
</svg>
|
|
8637
9481
|
</g>
|
|
8638
|
-
|
|
8639
|
-
width="${pos.width}" height="${pos.height}"
|
|
8640
|
-
fill="none"
|
|
8641
|
-
stroke="#2D3748"
|
|
8642
|
-
stroke-width="0.5"
|
|
8643
|
-
rx="4"
|
|
8644
|
-
filter="url(#sketch-rough)"/>
|
|
9482
|
+
${sketchIconBorder}
|
|
8645
9483
|
</g>`;
|
|
8646
9484
|
}
|
|
8647
|
-
|
|
8648
|
-
|
|
9485
|
+
const sketchCircleBorder = isCircle ? `<circle cx="${imgCx}" cy="${imgCy}" r="${imgR}" fill="none" stroke="#2D3748" stroke-width="0.5"/>` : "";
|
|
9486
|
+
return `${defsHtml}<g${this.getDataNodeId(node)}${clipAttr}>
|
|
8649
9487
|
<rect x="${pos.x}" y="${pos.y}"
|
|
8650
9488
|
width="${pos.width}" height="${pos.height}"
|
|
8651
9489
|
fill="${imageBg}"
|
|
@@ -8653,14 +9491,12 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8653
9491
|
stroke-width="0.5"
|
|
8654
9492
|
rx="4"
|
|
8655
9493
|
filter="url(#sketch-rough)"/>
|
|
8656
|
-
|
|
8657
|
-
<!-- Placeholder icon -->
|
|
8658
9494
|
<text x="${pos.x + pos.width / 2}" y="${pos.y + pos.height / 2}"
|
|
8659
9495
|
font-family="${this.fontFamily}"
|
|
8660
9496
|
font-size="24"
|
|
8661
9497
|
fill="#666"
|
|
8662
9498
|
text-anchor="middle">\u{1F5BC}</text>
|
|
8663
|
-
</g
|
|
9499
|
+
</g>${sketchCircleBorder}`;
|
|
8664
9500
|
}
|
|
8665
9501
|
/**
|
|
8666
9502
|
* Render breadcrumbs with Comic Sans
|
|
@@ -8776,20 +9612,33 @@ var SketchSVGRenderer = class extends SVGRenderer {
|
|
|
8776
9612
|
}
|
|
8777
9613
|
const iconSize = this.getIconSize(size);
|
|
8778
9614
|
const iconColor = variant === "default" ? this.resolveTextColor() : this.resolveVariantColor(variant, this.resolveTextColor());
|
|
8779
|
-
const
|
|
8780
|
-
const
|
|
9615
|
+
const paddingPx = Math.max(0, Number(node.props.padding || 0));
|
|
9616
|
+
const renderedIconSize = Math.max(4, iconSize - paddingPx * 2);
|
|
9617
|
+
const offsetX = pos.x + (pos.width - renderedIconSize) / 2;
|
|
9618
|
+
const offsetY = pos.y + (pos.height - renderedIconSize) / 2;
|
|
9619
|
+
const isIconCircle = this.parseBooleanProp(node.props.circle, false);
|
|
9620
|
+
if (isIconCircle) {
|
|
9621
|
+
const cx = pos.x + pos.width / 2;
|
|
9622
|
+
const cy = pos.y + pos.height / 2;
|
|
9623
|
+
const r = Math.min(pos.width, pos.height) / 2;
|
|
9624
|
+
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))}`}`;
|
|
9625
|
+
const bgColor = this.hexToRgba(iconColor, 0.1);
|
|
9626
|
+
return `<defs><clipPath id="${iconClipId}"><circle cx="${cx}" cy="${cy}" r="${r}"/></clipPath></defs><g${this.getDataNodeId(node)} clip-path="url(#${iconClipId})">
|
|
9627
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="${bgColor}"/>
|
|
9628
|
+
<g transform="translate(${offsetX}, ${offsetY})">
|
|
9629
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round">
|
|
9630
|
+
${this.extractSvgContent(iconSvg)}
|
|
9631
|
+
</svg>
|
|
9632
|
+
</g>
|
|
9633
|
+
</g>
|
|
9634
|
+
<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="${this.hexToRgba(iconColor, 0.35)}" stroke-width="0.5"/>`;
|
|
9635
|
+
}
|
|
8781
9636
|
return `<g${this.getDataNodeId(node)} transform="translate(${offsetX}, ${offsetY})">
|
|
8782
|
-
<svg width="${
|
|
9637
|
+
<svg width="${renderedIconSize}" height="${renderedIconSize}" viewBox="0 0 24 24" fill="none" stroke="${iconColor}" stroke-width="0.5" stroke-linecap="round" stroke-linejoin="round">
|
|
8783
9638
|
${this.extractSvgContent(iconSvg)}
|
|
8784
9639
|
</svg>
|
|
8785
9640
|
</g>`;
|
|
8786
9641
|
}
|
|
8787
|
-
/**
|
|
8788
|
-
* Render chart placeholder with sketch filter and Comic Sans
|
|
8789
|
-
*/
|
|
8790
|
-
renderChartPlaceholder(node, pos) {
|
|
8791
|
-
return super.renderChartPlaceholder(node, pos);
|
|
8792
|
-
}
|
|
8793
9642
|
/**
|
|
8794
9643
|
* Helper method to get icon SVG
|
|
8795
9644
|
*/
|