sketchmark 1.3.2 → 1.3.5
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 +23 -17
- package/dist/ast/types.d.ts +6 -0
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.cjs +86 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +86 -46
- package/dist/index.js.map +1 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/renderer/canvas/index.d.ts.map +1 -1
- package/dist/renderer/shapes/icon.d.ts.map +1 -1
- package/dist/renderer/shapes/image.d.ts.map +1 -1
- package/dist/renderer/shapes/label-strip.d.ts +6 -0
- package/dist/renderer/shapes/label-strip.d.ts.map +1 -0
- package/dist/renderer/shapes/line.d.ts.map +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/scene/index.d.ts +6 -0
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +86 -46
- package/dist/ui/embed.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -598,6 +598,12 @@ function parse(src, options = {}) {
|
|
|
598
598
|
id,
|
|
599
599
|
shape,
|
|
600
600
|
label: props.label || "",
|
|
601
|
+
...(props["label-dx"] !== undefined
|
|
602
|
+
? { labelDx: parseFloat(props["label-dx"]) }
|
|
603
|
+
: {}),
|
|
604
|
+
...(props["label-dy"] !== undefined
|
|
605
|
+
? { labelDy: parseFloat(props["label-dy"]) }
|
|
606
|
+
: {}),
|
|
601
607
|
...(props.width ? { width: parseFloat(props.width) } : {}),
|
|
602
608
|
...(props.height ? { height: parseFloat(props.height) } : {}),
|
|
603
609
|
...(props.x ? { x: parseFloat(props.x) } : {}),
|
|
@@ -638,6 +644,12 @@ function parse(src, options = {}) {
|
|
|
638
644
|
id,
|
|
639
645
|
shape: "note",
|
|
640
646
|
label: (props.label ?? "").replace(/\\n/g, "\n"),
|
|
647
|
+
...(props["label-dx"] !== undefined
|
|
648
|
+
? { labelDx: parseFloat(props["label-dx"]) }
|
|
649
|
+
: {}),
|
|
650
|
+
...(props["label-dy"] !== undefined
|
|
651
|
+
? { labelDy: parseFloat(props["label-dy"]) }
|
|
652
|
+
: {}),
|
|
641
653
|
theme: props.theme,
|
|
642
654
|
...(meta ? { meta } : {}),
|
|
643
655
|
style: propsToStyle(props),
|
|
@@ -686,6 +698,8 @@ function parse(src, options = {}) {
|
|
|
686
698
|
kind: "group",
|
|
687
699
|
id,
|
|
688
700
|
label: props.label ?? "",
|
|
701
|
+
labelDx: props["label-dx"] !== undefined ? parseFloat(props["label-dx"]) : undefined,
|
|
702
|
+
labelDy: props["label-dy"] !== undefined ? parseFloat(props["label-dy"]) : undefined,
|
|
689
703
|
children: [],
|
|
690
704
|
layout: props.layout,
|
|
691
705
|
columns: props.columns !== undefined ? parseInt(props.columns, 10) : undefined,
|
|
@@ -730,6 +744,8 @@ function parse(src, options = {}) {
|
|
|
730
744
|
to: toTok.value,
|
|
731
745
|
connector: connector,
|
|
732
746
|
label: props.label,
|
|
747
|
+
labelDx: props["label-dx"] !== undefined ? parseFloat(props["label-dx"]) : undefined,
|
|
748
|
+
labelDy: props["label-dy"] !== undefined ? parseFloat(props["label-dy"]) : undefined,
|
|
733
749
|
fromAnchor: props["anchor-from"],
|
|
734
750
|
toAnchor: props["anchor-to"],
|
|
735
751
|
dashed,
|
|
@@ -1231,6 +1247,7 @@ const LAYOUT = {
|
|
|
1231
1247
|
const NODE = {
|
|
1232
1248
|
minW: 90, // minimum auto-sized node width (px)
|
|
1233
1249
|
maxW: 300, // maximum auto-sized node width (px)
|
|
1250
|
+
mediaLabelH: 20, // reserved bottom strip for icon/image/line labels (px)
|
|
1234
1251
|
basePad: 26, // base padding added to label width (px)
|
|
1235
1252
|
};
|
|
1236
1253
|
// ── Shape-specific sizing ──────────────────────────────────
|
|
@@ -3554,6 +3571,8 @@ function buildSceneGraph(ast) {
|
|
|
3554
3571
|
id: n.id,
|
|
3555
3572
|
shape: n.shape,
|
|
3556
3573
|
label: n.label,
|
|
3574
|
+
labelDx: n.labelDx,
|
|
3575
|
+
labelDy: n.labelDy,
|
|
3557
3576
|
style: { ...ast.styles[n.id], ...themeStyle, ...n.style },
|
|
3558
3577
|
groupId: nodeParentById.get(n.id),
|
|
3559
3578
|
width: n.width,
|
|
@@ -3579,6 +3598,8 @@ function buildSceneGraph(ast) {
|
|
|
3579
3598
|
return {
|
|
3580
3599
|
id: g.id,
|
|
3581
3600
|
label: g.label,
|
|
3601
|
+
labelDx: g.labelDx,
|
|
3602
|
+
labelDy: g.labelDy,
|
|
3582
3603
|
parentId: groupParentById.get(g.id),
|
|
3583
3604
|
children: g.children,
|
|
3584
3605
|
layout: (g.layout ?? "column"),
|
|
@@ -3656,6 +3677,8 @@ function buildSceneGraph(ast) {
|
|
|
3656
3677
|
to: e.to,
|
|
3657
3678
|
connector: e.connector,
|
|
3658
3679
|
label: e.label,
|
|
3680
|
+
labelDx: e.labelDx,
|
|
3681
|
+
labelDy: e.labelDy,
|
|
3659
3682
|
fromAnchor: e.fromAnchor,
|
|
3660
3683
|
toAnchor: e.toAnchor,
|
|
3661
3684
|
dashed: e.dashed ?? false,
|
|
@@ -3997,10 +4020,25 @@ const textShape = {
|
|
|
3997
4020
|
},
|
|
3998
4021
|
};
|
|
3999
4022
|
|
|
4023
|
+
const MEDIA_LABEL_SHAPES = new Set(["icon", "image", "line"]);
|
|
4024
|
+
function usesBottomLabelStrip(shape) {
|
|
4025
|
+
return MEDIA_LABEL_SHAPES.has(shape);
|
|
4026
|
+
}
|
|
4027
|
+
function getBottomLabelStripHeight(node) {
|
|
4028
|
+
return usesBottomLabelStrip(node.shape) && node.label ? NODE.mediaLabelH : 0;
|
|
4029
|
+
}
|
|
4030
|
+
function getBottomLabelContentHeight(node) {
|
|
4031
|
+
return node.h - getBottomLabelStripHeight(node);
|
|
4032
|
+
}
|
|
4033
|
+
function getBottomLabelCenterY(node) {
|
|
4034
|
+
const stripH = getBottomLabelStripHeight(node);
|
|
4035
|
+
return stripH > 0 ? node.y + node.h - stripH / 2 : node.y + node.h / 2;
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4000
4038
|
const iconShape = {
|
|
4001
4039
|
size(n, labelW) {
|
|
4002
4040
|
const iconBase = 48;
|
|
4003
|
-
const labelH = n
|
|
4041
|
+
const labelH = getBottomLabelStripHeight(n);
|
|
4004
4042
|
n.w = n.w || Math.max(iconBase, n.label ? labelW : 0);
|
|
4005
4043
|
n.h = n.h || (iconBase + labelH);
|
|
4006
4044
|
},
|
|
@@ -4013,8 +4051,7 @@ const iconShape = {
|
|
|
4013
4051
|
const iconColor = s.color
|
|
4014
4052
|
? encodeURIComponent(String(s.color))
|
|
4015
4053
|
: encodeURIComponent(String(palette.nodeStroke));
|
|
4016
|
-
const
|
|
4017
|
-
const iconAreaH = n.h - labelSpace;
|
|
4054
|
+
const iconAreaH = getBottomLabelContentHeight(n);
|
|
4018
4055
|
const iconSize = Math.min(n.w, iconAreaH) - 4;
|
|
4019
4056
|
const iconUrl = `https://api.iconify.design/${prefix}/${name}.svg?color=${iconColor}&width=${iconSize}&height=${iconSize}`;
|
|
4020
4057
|
const img = document.createElementNS(SVG_NS, "image");
|
|
@@ -4058,8 +4095,7 @@ const iconShape = {
|
|
|
4058
4095
|
const iconColor = s.color
|
|
4059
4096
|
? encodeURIComponent(String(s.color))
|
|
4060
4097
|
: encodeURIComponent(String(palette.nodeStroke));
|
|
4061
|
-
const
|
|
4062
|
-
const iconAreaH = n.h - iconLabelSpace;
|
|
4098
|
+
const iconAreaH = getBottomLabelContentHeight(n);
|
|
4063
4099
|
const iconSize = Math.min(n.w, iconAreaH) - 4;
|
|
4064
4100
|
const iconUrl = `https://api.iconify.design/${prefix}/${name}.svg?color=${iconColor}&width=${iconSize}&height=${iconSize}`;
|
|
4065
4101
|
const img = new Image();
|
|
@@ -4102,21 +4138,21 @@ const imageShape = {
|
|
|
4102
4138
|
const w = n.w || Math.max(MIN_W, Math.min(MAX_W, labelW));
|
|
4103
4139
|
n.w = w;
|
|
4104
4140
|
if (!n.h) {
|
|
4141
|
+
const labelH = getBottomLabelStripHeight(n);
|
|
4105
4142
|
if (labelW > w) {
|
|
4106
4143
|
const fontSize = Number(n.style?.fontSize ?? 14);
|
|
4107
4144
|
const lines = Math.ceil(labelW / (w - 16));
|
|
4108
|
-
n.h = Math.max(52, lines * fontSize * 1.5 +
|
|
4145
|
+
n.h = Math.max(52, lines * fontSize * 1.5 + labelH);
|
|
4109
4146
|
}
|
|
4110
4147
|
else {
|
|
4111
|
-
n.h = 52;
|
|
4148
|
+
n.h = 52 + labelH;
|
|
4112
4149
|
}
|
|
4113
4150
|
}
|
|
4114
4151
|
},
|
|
4115
4152
|
renderSVG(rc, n, _palette, opts) {
|
|
4116
4153
|
const s = n.style ?? {};
|
|
4117
4154
|
if (n.imageUrl) {
|
|
4118
|
-
const
|
|
4119
|
-
const imgAreaH = n.h - imgLabelSpace;
|
|
4155
|
+
const imgAreaH = getBottomLabelContentHeight(n);
|
|
4120
4156
|
const img = document.createElementNS(SVG_NS, "image");
|
|
4121
4157
|
img.setAttribute("href", n.imageUrl);
|
|
4122
4158
|
img.setAttribute("x", String(n.x + 1));
|
|
@@ -4148,8 +4184,7 @@ const imageShape = {
|
|
|
4148
4184
|
renderCanvas(rc, ctx, n, _palette, opts) {
|
|
4149
4185
|
const s = n.style ?? {};
|
|
4150
4186
|
if (n.imageUrl) {
|
|
4151
|
-
const
|
|
4152
|
-
const imgAreaH = n.h - imgLblSpace;
|
|
4187
|
+
const imgAreaH = getBottomLabelContentHeight(n);
|
|
4153
4188
|
const img = new Image();
|
|
4154
4189
|
img.crossOrigin = "anonymous";
|
|
4155
4190
|
img.onload = () => {
|
|
@@ -4326,17 +4361,17 @@ const noteShape = {
|
|
|
4326
4361
|
|
|
4327
4362
|
const lineShape = {
|
|
4328
4363
|
size(n, labelW) {
|
|
4329
|
-
const labelH = n
|
|
4364
|
+
const labelH = getBottomLabelStripHeight(n);
|
|
4330
4365
|
n.w = n.width ?? Math.max(MIN_W, labelW + 20);
|
|
4331
4366
|
n.h = n.height ?? (6 + labelH);
|
|
4332
4367
|
},
|
|
4333
4368
|
renderSVG(rc, n, _palette, opts) {
|
|
4334
|
-
const labelH = n
|
|
4369
|
+
const labelH = getBottomLabelStripHeight(n);
|
|
4335
4370
|
const lineY = n.y + (n.h - labelH) / 2;
|
|
4336
4371
|
return [rc.line(n.x, lineY, n.x + n.w, lineY, opts)];
|
|
4337
4372
|
},
|
|
4338
4373
|
renderCanvas(rc, _ctx, n, _palette, opts) {
|
|
4339
|
-
const labelH = n
|
|
4374
|
+
const labelH = getBottomLabelStripHeight(n);
|
|
4340
4375
|
const lineY = n.y + (n.h - labelH) / 2;
|
|
4341
4376
|
rc.line(n.x, lineY, n.x + n.w, lineY, opts);
|
|
4342
4377
|
},
|
|
@@ -5582,18 +5617,18 @@ function renderRoughChartSVG(rc, c, palette, isDark) {
|
|
|
5582
5617
|
stroke: bgStroke, strokeWidth: Number(s.strokeWidth ?? 1.2),
|
|
5583
5618
|
...(s.strokeDash ? { strokeLineDash: s.strokeDash } : {}),
|
|
5584
5619
|
}));
|
|
5620
|
+
const { px, py, pw, ph, titleH, cx, cy } = chartLayout(c);
|
|
5585
5621
|
// Title
|
|
5586
5622
|
if (c.label) {
|
|
5587
|
-
cg.appendChild(mkT(c.label, c.x + c.w / 2, c.y +
|
|
5623
|
+
cg.appendChild(mkT(c.label, c.x + c.w / 2, c.y + titleH / 2 + 2, cFontSize, cFontWeight, lc, 'middle', cFont));
|
|
5588
5624
|
}
|
|
5589
|
-
const { px, py, pw, ph, cx, cy } = chartLayout(c);
|
|
5590
5625
|
// ── Pie / Donut ──────────────────────────────────────────
|
|
5591
5626
|
if (c.chartType === 'pie' || c.chartType === 'donut') {
|
|
5592
5627
|
const { segments, total } = parsePie(c.data);
|
|
5593
|
-
const r = Math.min(c.w * 0.38, (c.h -
|
|
5628
|
+
const r = Math.min(c.w * 0.38, (c.h - titleH) * 0.44);
|
|
5594
5629
|
const ir = c.chartType === 'donut' ? r * 0.48 : 0;
|
|
5595
5630
|
const legendX = c.x + 8;
|
|
5596
|
-
const legendY = c.y +
|
|
5631
|
+
const legendY = c.y + titleH + 4;
|
|
5597
5632
|
let angle = -Math.PI / 2;
|
|
5598
5633
|
for (const seg of segments) {
|
|
5599
5634
|
const sweep = (seg.value / total) * Math.PI * 2;
|
|
@@ -5631,7 +5666,7 @@ function renderRoughChartSVG(rc, c, palette, isDark) {
|
|
|
5631
5666
|
strokeWidth: 1.2,
|
|
5632
5667
|
}));
|
|
5633
5668
|
});
|
|
5634
|
-
legend(cg, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y +
|
|
5669
|
+
legend(cg, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y + titleH + 4, lc, cFont);
|
|
5635
5670
|
return cg;
|
|
5636
5671
|
}
|
|
5637
5672
|
// ── Bar / Line / Area ─────────────────────────────────────
|
|
@@ -8377,9 +8412,10 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8377
8412
|
}));
|
|
8378
8413
|
// ── Group label typography ──────────────────────────
|
|
8379
8414
|
const gTypo = resolveTypography(gs, { fontSize: GROUP_LABEL.fontSize, fontWeight: GROUP_LABEL.fontWeight, textAlign: "left", padding: GROUP_LABEL.padding }, diagramFont, palette.groupLabel);
|
|
8380
|
-
const gTextX = computeTextX(gTypo, g.x, g.w);
|
|
8415
|
+
const gTextX = computeTextX(gTypo, g.x, g.w) + (g.labelDx ?? 0);
|
|
8416
|
+
const gTextY = g.y + gTypo.padding + (g.labelDy ?? 0);
|
|
8381
8417
|
if (g.label) {
|
|
8382
|
-
gg.appendChild(mkText(g.label, gTextX,
|
|
8418
|
+
gg.appendChild(mkText(g.label, gTextX, gTextY, gTypo.fontSize, gTypo.fontWeight, gTypo.textColor, gTypo.textAnchor, gTypo.font, gTypo.letterSpacing));
|
|
8383
8419
|
}
|
|
8384
8420
|
GL.appendChild(gg);
|
|
8385
8421
|
}
|
|
@@ -8431,8 +8467,8 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8431
8467
|
eg.appendChild(startHead);
|
|
8432
8468
|
}
|
|
8433
8469
|
if (e.label) {
|
|
8434
|
-
const mx = (x1 + x2) / 2 - ny * EDGE.labelOffset;
|
|
8435
|
-
const my = (y1 + y2) / 2 + nx * EDGE.labelOffset;
|
|
8470
|
+
const mx = (x1 + x2) / 2 - ny * EDGE.labelOffset + (e.labelDx ?? 0);
|
|
8471
|
+
const my = (y1 + y2) / 2 + nx * EDGE.labelOffset + (e.labelDy ?? 0);
|
|
8436
8472
|
const tw = Math.max(e.label.length * 7 + 12, 36);
|
|
8437
8473
|
const bg = se("rect");
|
|
8438
8474
|
bg.setAttribute("x", String(mx - tw / 2));
|
|
@@ -8500,7 +8536,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8500
8536
|
// ── Node / text typography ─────────────────────────
|
|
8501
8537
|
const isText = n.shape === "text";
|
|
8502
8538
|
const isNote = n.shape === "note";
|
|
8503
|
-
const
|
|
8539
|
+
const usesBottomStrip = usesBottomLabelStrip(n.shape);
|
|
8504
8540
|
const typo = resolveTypography(n.style, {
|
|
8505
8541
|
fontSize: isText ? 13 : isNote ? 12 : 14,
|
|
8506
8542
|
fontWeight: isText || isNote ? 400 : 500,
|
|
@@ -8518,20 +8554,22 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8518
8554
|
: n.x + typo.padding)
|
|
8519
8555
|
: computeTextX(typo, n.x, n.w);
|
|
8520
8556
|
const fontStr = buildFontStr(typo.fontSize, typo.fontWeight, typo.font);
|
|
8521
|
-
const shouldWrap = !
|
|
8557
|
+
const shouldWrap = !usesBottomStrip && !n.label.includes('\n');
|
|
8522
8558
|
const innerW = shapeInnerTextWidth(n.shape, n.w, typo.padding);
|
|
8523
8559
|
const lines = shouldWrap
|
|
8524
8560
|
? wrapText(n.label, innerW, typo.fontSize, fontStr)
|
|
8525
8561
|
: n.label.split('\n');
|
|
8526
|
-
const textCY =
|
|
8527
|
-
? n
|
|
8562
|
+
const textCY = usesBottomStrip
|
|
8563
|
+
? getBottomLabelCenterY(n)
|
|
8528
8564
|
: isNote
|
|
8529
8565
|
? computeTextCY(typo, n.y, n.h, lines.length, FOLD + typo.padding)
|
|
8530
8566
|
: computeTextCY(typo, n.y, n.h, lines.length);
|
|
8567
|
+
const labelX = textX + (n.labelDx ?? 0);
|
|
8568
|
+
const labelY = textCY + (n.labelDy ?? 0);
|
|
8531
8569
|
if (n.label) {
|
|
8532
8570
|
ng.appendChild(lines.length > 1
|
|
8533
|
-
? mkMultilineText(lines,
|
|
8534
|
-
: mkText(n.label,
|
|
8571
|
+
? mkMultilineText(lines, labelX, labelY, typo.fontSize, typo.fontWeight, typo.textColor, typo.textAnchor, typo.lineHeight, typo.font, typo.letterSpacing)
|
|
8572
|
+
: mkText(n.label, labelX, labelY, typo.fontSize, typo.fontWeight, typo.textColor, typo.textAnchor, typo.font, typo.letterSpacing));
|
|
8535
8573
|
}
|
|
8536
8574
|
if (options.interactive) {
|
|
8537
8575
|
ng.style.cursor = "pointer";
|
|
@@ -8845,6 +8883,7 @@ function drawRoughChartCanvas(rc, ctx, c, pal, R) {
|
|
|
8845
8883
|
strokeWidth: Number(s.strokeWidth ?? 1.2),
|
|
8846
8884
|
...(s.strokeDash ? { strokeLineDash: s.strokeDash } : {}),
|
|
8847
8885
|
});
|
|
8886
|
+
const { px, py, pw, ph, titleH, cx, cy } = chartLayout(c);
|
|
8848
8887
|
// Title
|
|
8849
8888
|
if (c.label) {
|
|
8850
8889
|
ctx.save();
|
|
@@ -8852,17 +8891,16 @@ function drawRoughChartCanvas(rc, ctx, c, pal, R) {
|
|
|
8852
8891
|
ctx.fillStyle = lc;
|
|
8853
8892
|
ctx.textAlign = 'center';
|
|
8854
8893
|
ctx.textBaseline = 'middle';
|
|
8855
|
-
ctx.fillText(c.label, c.x + c.w / 2, c.y +
|
|
8894
|
+
ctx.fillText(c.label, c.x + c.w / 2, c.y + titleH / 2 + 2);
|
|
8856
8895
|
ctx.restore();
|
|
8857
8896
|
}
|
|
8858
|
-
const { px, py, pw, ph, cx, cy } = chartLayout(c);
|
|
8859
8897
|
// ── Pie / Donut ──────────────────────────────────────────
|
|
8860
8898
|
if (c.chartType === 'pie' || c.chartType === 'donut') {
|
|
8861
8899
|
const { segments, total } = parsePie(c.data);
|
|
8862
|
-
const r = Math.min(c.w * 0.38, (c.h -
|
|
8900
|
+
const r = Math.min(c.w * 0.38, (c.h - titleH) * 0.44);
|
|
8863
8901
|
const ir = c.chartType === 'donut' ? r * 0.48 : 0;
|
|
8864
8902
|
const legendX = c.x + 8;
|
|
8865
|
-
const legendY = c.y +
|
|
8903
|
+
const legendY = c.y + titleH + 4;
|
|
8866
8904
|
let angle = -Math.PI / 2;
|
|
8867
8905
|
segments.forEach((seg, i) => {
|
|
8868
8906
|
const sweep = (seg.value / total) * Math.PI * 2;
|
|
@@ -8890,7 +8928,7 @@ function drawRoughChartCanvas(rc, ctx, c, pal, R) {
|
|
|
8890
8928
|
strokeWidth: 1.2,
|
|
8891
8929
|
});
|
|
8892
8930
|
});
|
|
8893
|
-
drawLegend(ctx, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y +
|
|
8931
|
+
drawLegend(ctx, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y + titleH + 4, lc, cFont);
|
|
8894
8932
|
ctx.globalAlpha = 1;
|
|
8895
8933
|
return;
|
|
8896
8934
|
}
|
|
@@ -9125,8 +9163,9 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
9125
9163
|
});
|
|
9126
9164
|
if (g.label) {
|
|
9127
9165
|
const gTypo = resolveTypography(gs, { fontSize: GROUP_LABEL.fontSize, fontWeight: GROUP_LABEL.fontWeight, textAlign: "left", padding: GROUP_LABEL.padding }, diagramFont, palette.groupLabel);
|
|
9128
|
-
const gTextX = computeTextX(gTypo, g.x, g.w);
|
|
9129
|
-
|
|
9166
|
+
const gTextX = computeTextX(gTypo, g.x, g.w) + (g.labelDx ?? 0);
|
|
9167
|
+
const gTextY = g.y + gTypo.padding + 2 + (g.labelDy ?? 0);
|
|
9168
|
+
drawText(ctx, g.label, gTextX, gTextY, gTypo.fontSize, gTypo.fontWeight, gTypo.textColor, gTypo.textAlign, gTypo.font, gTypo.letterSpacing);
|
|
9130
9169
|
}
|
|
9131
9170
|
if (gs.opacity != null)
|
|
9132
9171
|
ctx.globalAlpha = 1;
|
|
@@ -9164,8 +9203,8 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
9164
9203
|
if (arrowAt === 'start' || arrowAt === 'both')
|
|
9165
9204
|
drawArrowHead(rc, x1, y1, Math.atan2(y1 - y2, x1 - x2), ecol, hashStr$3(e.from + 'back'));
|
|
9166
9205
|
if (e.label) {
|
|
9167
|
-
const mx = (x1 + x2) / 2 - ny * EDGE.labelOffset;
|
|
9168
|
-
const my = (y1 + y2) / 2 + nx * EDGE.labelOffset;
|
|
9206
|
+
const mx = (x1 + x2) / 2 - ny * EDGE.labelOffset + (e.labelDx ?? 0);
|
|
9207
|
+
const my = (y1 + y2) / 2 + nx * EDGE.labelOffset + (e.labelDy ?? 0);
|
|
9169
9208
|
// ── Edge label: font, font-size, letter-spacing ──
|
|
9170
9209
|
// always center-anchored (single line)
|
|
9171
9210
|
const eFontSize = Number(e.style?.fontSize ?? EDGE.labelFontSize);
|
|
@@ -9205,7 +9244,7 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
9205
9244
|
// ── Node / text typography ─────────────────────────
|
|
9206
9245
|
const isText = n.shape === 'text';
|
|
9207
9246
|
const isNote = n.shape === 'note';
|
|
9208
|
-
const
|
|
9247
|
+
const usesBottomStrip = usesBottomLabelStrip(n.shape);
|
|
9209
9248
|
const typo = resolveTypography(n.style, {
|
|
9210
9249
|
fontSize: isText ? 13 : isNote ? 12 : 14,
|
|
9211
9250
|
fontWeight: isText || isNote ? 400 : 500,
|
|
@@ -9223,23 +9262,25 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
9223
9262
|
: n.x + typo.padding)
|
|
9224
9263
|
: computeTextX(typo, n.x, n.w);
|
|
9225
9264
|
const fontStr = buildFontStr(typo.fontSize, typo.fontWeight, typo.font);
|
|
9226
|
-
const shouldWrap = !
|
|
9265
|
+
const shouldWrap = !usesBottomStrip && !n.label.includes('\n');
|
|
9227
9266
|
const innerW = shapeInnerTextWidth(n.shape, n.w, typo.padding);
|
|
9228
9267
|
const rawLines = n.label.split('\n');
|
|
9229
9268
|
const lines = shouldWrap && rawLines.length === 1
|
|
9230
9269
|
? wrapText(n.label, innerW, typo.fontSize, fontStr)
|
|
9231
9270
|
: rawLines;
|
|
9232
|
-
const textCY =
|
|
9233
|
-
? n
|
|
9271
|
+
const textCY = usesBottomStrip
|
|
9272
|
+
? getBottomLabelCenterY(n)
|
|
9234
9273
|
: isNote
|
|
9235
9274
|
? computeTextCY(typo, n.y, n.h, lines.length, FOLD + typo.padding)
|
|
9236
9275
|
: computeTextCY(typo, n.y, n.h, lines.length);
|
|
9276
|
+
const labelX = textX + (n.labelDx ?? 0);
|
|
9277
|
+
const labelY = textCY + (n.labelDy ?? 0);
|
|
9237
9278
|
if (n.label) {
|
|
9238
9279
|
if (lines.length > 1) {
|
|
9239
|
-
drawMultilineText(ctx, lines,
|
|
9280
|
+
drawMultilineText(ctx, lines, labelX, labelY, typo.fontSize, typo.fontWeight, typo.textColor, typo.textAlign, typo.lineHeight, typo.font, typo.letterSpacing);
|
|
9240
9281
|
}
|
|
9241
9282
|
else {
|
|
9242
|
-
drawText(ctx, lines[0] ?? '',
|
|
9283
|
+
drawText(ctx, lines[0] ?? '', labelX, labelY, typo.fontSize, typo.fontWeight, typo.textColor, typo.textAlign, typo.font, typo.letterSpacing);
|
|
9243
9284
|
}
|
|
9244
9285
|
}
|
|
9245
9286
|
if (hasTx)
|
|
@@ -9577,7 +9618,7 @@ function buildNodeGuidePath(el) {
|
|
|
9577
9618
|
[x + 1, y + h - 1],
|
|
9578
9619
|
]);
|
|
9579
9620
|
case "line": {
|
|
9580
|
-
const labelH = el.querySelector("text") ?
|
|
9621
|
+
const labelH = el.querySelector("text") ? NODE.mediaLabelH : 0;
|
|
9581
9622
|
const lineY = y + (h - labelH) / 2;
|
|
9582
9623
|
return `M ${x} ${lineY} L ${x + w} ${lineY}`;
|
|
9583
9624
|
}
|
|
@@ -12667,7 +12708,6 @@ const EMBED_CSS = `
|
|
|
12667
12708
|
top: 0;
|
|
12668
12709
|
left: 0;
|
|
12669
12710
|
transform-origin: 0 0;
|
|
12670
|
-
will-change: transform;
|
|
12671
12711
|
}
|
|
12672
12712
|
|
|
12673
12713
|
.skm-embed__error {
|