sketchmark 0.2.1 → 0.2.3
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 +4 -4
- package/dist/ast/types.d.ts +1 -5
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/index.cjs +259 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +259 -120
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.ts.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/canvas/roughChartCanvas.d.ts.map +1 -1
- package/dist/renderer/roughChart.d.ts +1 -1
- package/dist/renderer/svg/index.d.ts.map +1 -1
- package/dist/renderer/svg/roughChartSVG.d.ts.map +1 -1
- package/dist/scene/index.d.ts +1 -1
- package/dist/sketchmark.iife.js +259 -120
- package/package.json +1 -1
package/dist/sketchmark.iife.js
CHANGED
|
@@ -262,10 +262,6 @@ var AIDiagram = (function (exports) {
|
|
|
262
262
|
s.color = p.color;
|
|
263
263
|
if (p.opacity)
|
|
264
264
|
s.opacity = parseFloat(p.opacity);
|
|
265
|
-
if (p.radius)
|
|
266
|
-
s.radius = parseFloat(p.radius);
|
|
267
|
-
if (p.shadow)
|
|
268
|
-
s.shadow = p.shadow === "true";
|
|
269
265
|
if (p["font-size"])
|
|
270
266
|
s.fontSize = parseFloat(p["font-size"]);
|
|
271
267
|
if (p["font-weight"])
|
|
@@ -282,8 +278,9 @@ var AIDiagram = (function (exports) {
|
|
|
282
278
|
s.letterSpacing = parseFloat(p["letter-spacing"]);
|
|
283
279
|
if (p.font)
|
|
284
280
|
s.font = p.font;
|
|
285
|
-
|
|
286
|
-
|
|
281
|
+
const dashVal = p["dash"] || p["stroke-dash"];
|
|
282
|
+
if (dashVal) {
|
|
283
|
+
const parts = dashVal
|
|
287
284
|
.split(",")
|
|
288
285
|
.map(Number)
|
|
289
286
|
.filter((n) => !isNaN(n));
|
|
@@ -854,7 +851,7 @@ var AIDiagram = (function (exports) {
|
|
|
854
851
|
kind: "chart",
|
|
855
852
|
id,
|
|
856
853
|
chartType: chartType.replace("-chart", ""),
|
|
857
|
-
|
|
854
|
+
label: props.label ?? props.title,
|
|
858
855
|
data: { headers, rows },
|
|
859
856
|
width: props.width ? parseFloat(props.width) : undefined,
|
|
860
857
|
height: props.height ? parseFloat(props.height) : undefined,
|
|
@@ -1346,7 +1343,7 @@ var AIDiagram = (function (exports) {
|
|
|
1346
1343
|
return {
|
|
1347
1344
|
id: c.id,
|
|
1348
1345
|
chartType: c.chartType,
|
|
1349
|
-
|
|
1346
|
+
label: c.label,
|
|
1350
1347
|
data: c.data,
|
|
1351
1348
|
style: { ...ast.styles[c.id], ...themeStyle, ...c.style },
|
|
1352
1349
|
x: 0,
|
|
@@ -1490,13 +1487,22 @@ var AIDiagram = (function (exports) {
|
|
|
1490
1487
|
n.h = n.h || 50;
|
|
1491
1488
|
break;
|
|
1492
1489
|
case "text": {
|
|
1493
|
-
// read fontSize from style if set, otherwise use default
|
|
1494
1490
|
const fontSize = Number(n.style?.fontSize ?? 13);
|
|
1495
1491
|
const charWidth = fontSize * 0.55;
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1492
|
+
const pad = Number(n.style?.padding ?? 8) * 2;
|
|
1493
|
+
if (n.width) {
|
|
1494
|
+
// User set width → word-wrap within it
|
|
1495
|
+
const approxLines = Math.ceil((n.label.length * charWidth) / (n.width - pad));
|
|
1496
|
+
n.w = n.width;
|
|
1497
|
+
n.h = n.height ?? Math.max(24, approxLines * fontSize * 1.5 + pad);
|
|
1498
|
+
}
|
|
1499
|
+
else {
|
|
1500
|
+
// Auto-size to content
|
|
1501
|
+
const lines = n.label.split("\\n");
|
|
1502
|
+
const longest = lines.reduce((a, b) => (a.length > b.length ? a : b), "");
|
|
1503
|
+
n.w = Math.max(MIN_W, Math.round(longest.length * charWidth + pad));
|
|
1504
|
+
n.h = n.height ?? Math.max(24, lines.length * fontSize * 1.5 + pad);
|
|
1505
|
+
}
|
|
1500
1506
|
break;
|
|
1501
1507
|
}
|
|
1502
1508
|
default:
|
|
@@ -1713,17 +1719,16 @@ var AIDiagram = (function (exports) {
|
|
|
1713
1719
|
if (layout === "row") {
|
|
1714
1720
|
const ws = kids.map((r) => iW(r, nm, gm, tm, ntm, cm, mdm));
|
|
1715
1721
|
const hs = kids.map((r) => iH(r, nm, gm, tm, ntm, cm, mdm));
|
|
1716
|
-
const maxH = Math.max(...hs);
|
|
1717
1722
|
const { start, gaps } = distribute(ws, contentW, gap, justify);
|
|
1718
1723
|
let x = contentX + start;
|
|
1719
1724
|
for (let i = 0; i < kids.length; i++) {
|
|
1720
1725
|
let y;
|
|
1721
1726
|
switch (align) {
|
|
1722
1727
|
case "center":
|
|
1723
|
-
y = contentY + (
|
|
1728
|
+
y = contentY + (contentH - hs[i]) / 2;
|
|
1724
1729
|
break;
|
|
1725
1730
|
case "end":
|
|
1726
|
-
y = contentY +
|
|
1731
|
+
y = contentY + contentH - hs[i];
|
|
1727
1732
|
break;
|
|
1728
1733
|
default:
|
|
1729
1734
|
y = contentY;
|
|
@@ -1744,17 +1749,16 @@ var AIDiagram = (function (exports) {
|
|
|
1744
1749
|
// column (default)
|
|
1745
1750
|
const ws = kids.map((r) => iW(r, nm, gm, tm, ntm, cm, mdm));
|
|
1746
1751
|
const hs = kids.map((r) => iH(r, nm, gm, tm, ntm, cm, mdm));
|
|
1747
|
-
const maxW = Math.max(...ws);
|
|
1748
1752
|
const { start, gaps } = distribute(hs, contentH, gap, justify);
|
|
1749
1753
|
let y = contentY + start;
|
|
1750
1754
|
for (let i = 0; i < kids.length; i++) {
|
|
1751
1755
|
let x;
|
|
1752
1756
|
switch (align) {
|
|
1753
1757
|
case "center":
|
|
1754
|
-
x = contentX + (
|
|
1758
|
+
x = contentX + (contentW - ws[i]) / 2;
|
|
1755
1759
|
break;
|
|
1756
1760
|
case "end":
|
|
1757
|
-
x = contentX +
|
|
1761
|
+
x = contentX + contentW - ws[i];
|
|
1758
1762
|
break;
|
|
1759
1763
|
default:
|
|
1760
1764
|
x = contentX;
|
|
@@ -2074,7 +2078,7 @@ var AIDiagram = (function (exports) {
|
|
|
2074
2078
|
'#7F77DD', '#D4537E', '#639922', '#E24B4A',
|
|
2075
2079
|
];
|
|
2076
2080
|
function chartLayout(c) {
|
|
2077
|
-
const titleH = c.
|
|
2081
|
+
const titleH = c.label ? 24 : 8;
|
|
2078
2082
|
const padL = 44, padR = 12, padB = 28, padT = 6;
|
|
2079
2083
|
const pw = c.w - padL - padR;
|
|
2080
2084
|
const ph = c.h - titleH - padT - padB;
|
|
@@ -2178,13 +2182,13 @@ var AIDiagram = (function (exports) {
|
|
|
2178
2182
|
g.setAttribute('class', cls);
|
|
2179
2183
|
return g;
|
|
2180
2184
|
}
|
|
2181
|
-
function mkT(txt, x, y, sz = 10, wt = 400, col = '#4a2e10', anchor = 'middle') {
|
|
2185
|
+
function mkT(txt, x, y, sz = 10, wt = 400, col = '#4a2e10', anchor = 'middle', font = 'system-ui, sans-serif') {
|
|
2182
2186
|
const t = se$1('text');
|
|
2183
2187
|
t.setAttribute('x', String(x));
|
|
2184
2188
|
t.setAttribute('y', String(y));
|
|
2185
2189
|
t.setAttribute('text-anchor', anchor);
|
|
2186
2190
|
t.setAttribute('dominant-baseline', 'middle');
|
|
2187
|
-
t.setAttribute('font-family',
|
|
2191
|
+
t.setAttribute('font-family', font);
|
|
2188
2192
|
t.setAttribute('font-size', String(sz));
|
|
2189
2193
|
t.setAttribute('font-weight', String(wt));
|
|
2190
2194
|
t.setAttribute('fill', col);
|
|
@@ -2200,7 +2204,7 @@ var AIDiagram = (function (exports) {
|
|
|
2200
2204
|
}
|
|
2201
2205
|
const BASE = { roughness: 1.2, bowing: 0.7 };
|
|
2202
2206
|
// ── Axes ───────────────────────────────────────────────────
|
|
2203
|
-
function drawAxes$1(rc, g, c, px, py, pw, ph, allY, labelCol) {
|
|
2207
|
+
function drawAxes$1(rc, g, c, px, py, pw, ph, allY, labelCol, font = 'system-ui, sans-serif') {
|
|
2204
2208
|
// Y axis
|
|
2205
2209
|
g.appendChild(rc.line(px, py, px, py + ph, {
|
|
2206
2210
|
roughness: 0.4, seed: hashStr$4(c.id + 'ya'), stroke: labelCol, strokeWidth: 1,
|
|
@@ -2219,7 +2223,7 @@ var AIDiagram = (function (exports) {
|
|
|
2219
2223
|
g.appendChild(rc.line(px - 3, ty, px, ty, {
|
|
2220
2224
|
roughness: 0.2, seed: hashStr$4(c.id + 'yt' + tick), stroke: labelCol, strokeWidth: 0.7,
|
|
2221
2225
|
}));
|
|
2222
|
-
g.appendChild(mkT(fmtNum$1(tick), px - 5, ty, 9, 400, labelCol, 'end'));
|
|
2226
|
+
g.appendChild(mkT(fmtNum$1(tick), px - 5, ty, 9, 400, labelCol, 'end', font));
|
|
2223
2227
|
}
|
|
2224
2228
|
}
|
|
2225
2229
|
function fmtNum$1(v) {
|
|
@@ -2228,7 +2232,7 @@ var AIDiagram = (function (exports) {
|
|
|
2228
2232
|
return String(v);
|
|
2229
2233
|
}
|
|
2230
2234
|
// ── Legend row ─────────────────────────────────────────────
|
|
2231
|
-
function legend(g, labels, colors, x, y, labelCol) {
|
|
2235
|
+
function legend(g, labels, colors, x, y, labelCol, font = 'system-ui, sans-serif') {
|
|
2232
2236
|
labels.forEach((lbl, i) => {
|
|
2233
2237
|
const dot = se$1('rect');
|
|
2234
2238
|
dot.setAttribute('x', String(x));
|
|
@@ -2238,7 +2242,7 @@ var AIDiagram = (function (exports) {
|
|
|
2238
2242
|
dot.setAttribute('fill', colors[i % colors.length]);
|
|
2239
2243
|
dot.setAttribute('rx', '1');
|
|
2240
2244
|
g.appendChild(dot);
|
|
2241
|
-
g.appendChild(mkT(lbl, x + 12, y + i * 14 + 4, 9, 400, labelCol, 'start'));
|
|
2245
|
+
g.appendChild(mkT(lbl, x + 12, y + i * 14 + 4, 9, 400, labelCol, 'start', font));
|
|
2242
2246
|
});
|
|
2243
2247
|
}
|
|
2244
2248
|
// ── Public entry ───────────────────────────────────────────
|
|
@@ -2249,6 +2253,11 @@ var AIDiagram = (function (exports) {
|
|
|
2249
2253
|
const bgFill = String(s.fill ?? palette.nodeFill);
|
|
2250
2254
|
const bgStroke = String(s.stroke ?? (isDark ? '#5a4a30' : '#c8b898'));
|
|
2251
2255
|
const lc = String(s.color ?? palette.titleText);
|
|
2256
|
+
const cFont = String(s.font ? `${s.font}, system-ui, sans-serif` : 'system-ui, sans-serif');
|
|
2257
|
+
const cFontSize = Number(s.fontSize ?? 12);
|
|
2258
|
+
const cFontWeight = s.fontWeight ?? 600;
|
|
2259
|
+
if (s.opacity != null)
|
|
2260
|
+
cg.setAttribute('opacity', String(s.opacity));
|
|
2252
2261
|
// Background box
|
|
2253
2262
|
cg.appendChild(rc.rectangle(c.x, c.y, c.w, c.h, {
|
|
2254
2263
|
...BASE, seed: hashStr$4(c.id),
|
|
@@ -2257,17 +2266,17 @@ var AIDiagram = (function (exports) {
|
|
|
2257
2266
|
...(s.strokeDash ? { strokeLineDash: s.strokeDash } : {}),
|
|
2258
2267
|
}));
|
|
2259
2268
|
// Title
|
|
2260
|
-
if (c.
|
|
2261
|
-
cg.appendChild(mkT(c.
|
|
2269
|
+
if (c.label) {
|
|
2270
|
+
cg.appendChild(mkT(c.label, c.x + c.w / 2, c.y + 14, cFontSize, cFontWeight, lc, 'middle', cFont));
|
|
2262
2271
|
}
|
|
2263
2272
|
const { px, py, pw, ph, cx, cy } = chartLayout(c);
|
|
2264
2273
|
// ── Pie / Donut ──────────────────────────────────────────
|
|
2265
2274
|
if (c.chartType === 'pie' || c.chartType === 'donut') {
|
|
2266
2275
|
const { segments, total } = parsePie(c.data);
|
|
2267
|
-
const r = Math.min(c.w * 0.38, (c.h - (c.
|
|
2276
|
+
const r = Math.min(c.w * 0.38, (c.h - (c.label ? 24 : 8)) * 0.44);
|
|
2268
2277
|
const ir = c.chartType === 'donut' ? r * 0.48 : 0;
|
|
2269
2278
|
const legendX = c.x + 8;
|
|
2270
|
-
const legendY = c.y + (c.
|
|
2279
|
+
const legendY = c.y + (c.label ? 28 : 12);
|
|
2271
2280
|
let angle = -Math.PI / 2;
|
|
2272
2281
|
for (const seg of segments) {
|
|
2273
2282
|
const sweep = (seg.value / total) * Math.PI * 2;
|
|
@@ -2284,7 +2293,7 @@ var AIDiagram = (function (exports) {
|
|
|
2284
2293
|
angle += sweep;
|
|
2285
2294
|
}
|
|
2286
2295
|
// Mini legend on left
|
|
2287
|
-
legend(cg, segments.map(s => `${s.label} ${Math.round(s.value / total * 100)}%`), segments.map(s => s.color), legendX, legendY, lc);
|
|
2296
|
+
legend(cg, segments.map(s => `${s.label} ${Math.round(s.value / total * 100)}%`), segments.map(s => s.color), legendX, legendY, lc, cFont);
|
|
2288
2297
|
return cg;
|
|
2289
2298
|
}
|
|
2290
2299
|
// ── Scatter ───────────────────────────────────────────────
|
|
@@ -2305,7 +2314,7 @@ var AIDiagram = (function (exports) {
|
|
|
2305
2314
|
strokeWidth: 1.2,
|
|
2306
2315
|
}));
|
|
2307
2316
|
});
|
|
2308
|
-
legend(cg, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y + (c.
|
|
2317
|
+
legend(cg, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y + (c.label ? 28 : 12), lc, cFont);
|
|
2309
2318
|
return cg;
|
|
2310
2319
|
}
|
|
2311
2320
|
// ── Bar / Line / Area ─────────────────────────────────────
|
|
@@ -2314,10 +2323,10 @@ var AIDiagram = (function (exports) {
|
|
|
2314
2323
|
const toY = makeValueToY(allY, py, ph);
|
|
2315
2324
|
const baseline = toY(0);
|
|
2316
2325
|
const n = labels.length;
|
|
2317
|
-
drawAxes$1(rc, cg, c, px, py, pw, ph, allY, lc);
|
|
2326
|
+
drawAxes$1(rc, cg, c, px, py, pw, ph, allY, lc, cFont);
|
|
2318
2327
|
// X labels
|
|
2319
2328
|
labels.forEach((lbl, i) => {
|
|
2320
|
-
cg.appendChild(mkT(lbl, px + (i + 0.5) * (pw / n), py + ph + 14, 9, 400, lc));
|
|
2329
|
+
cg.appendChild(mkT(lbl, px + (i + 0.5) * (pw / n), py + ph + 14, 9, 400, lc, 'middle', cFont));
|
|
2321
2330
|
});
|
|
2322
2331
|
if (c.chartType === 'bar') {
|
|
2323
2332
|
const groupW = pw / n;
|
|
@@ -2388,7 +2397,7 @@ var AIDiagram = (function (exports) {
|
|
|
2388
2397
|
}
|
|
2389
2398
|
// Multi-series legend
|
|
2390
2399
|
if (series.length > 1) {
|
|
2391
|
-
legend(cg, series.map(s => s.name), series.map(s => s.color), px, py - 2, lc);
|
|
2400
|
+
legend(cg, series.map(s => s.name), series.map(s => s.color), px, py - 2, lc, cFont);
|
|
2392
2401
|
}
|
|
2393
2402
|
return cg;
|
|
2394
2403
|
}
|
|
@@ -4854,6 +4863,14 @@ var AIDiagram = (function (exports) {
|
|
|
4854
4863
|
return h;
|
|
4855
4864
|
}
|
|
4856
4865
|
const BASE_ROUGH = { roughness: 1.3, bowing: 0.7 };
|
|
4866
|
+
/** Darken a CSS hex colour by `amount` (0–1). Falls back to input for non-hex. */
|
|
4867
|
+
function darkenHex$1(hex, amount = 0.12) {
|
|
4868
|
+
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
|
|
4869
|
+
if (!m)
|
|
4870
|
+
return hex;
|
|
4871
|
+
const d = (v) => Math.max(0, Math.round(parseInt(v, 16) * (1 - amount)));
|
|
4872
|
+
return `#${d(m[1]).toString(16).padStart(2, "0")}${d(m[2]).toString(16).padStart(2, "0")}${d(m[3]).toString(16).padStart(2, "0")}`;
|
|
4873
|
+
}
|
|
4857
4874
|
// ── Small helper: load + resolve font from style or fall back ─────────────
|
|
4858
4875
|
function resolveStyleFont$1(style, fallback) {
|
|
4859
4876
|
const raw = String(style["font"] ?? "");
|
|
@@ -5024,6 +5041,7 @@ var AIDiagram = (function (exports) {
|
|
|
5024
5041
|
fillStyle: "solid",
|
|
5025
5042
|
stroke,
|
|
5026
5043
|
strokeWidth: Number(s.strokeWidth ?? 1.9),
|
|
5044
|
+
...(s.strokeDash ? { strokeLineDash: s.strokeDash } : {}),
|
|
5027
5045
|
};
|
|
5028
5046
|
const cx = n.x + n.w / 2, cy = n.y + n.h / 2;
|
|
5029
5047
|
const hw = n.w / 2 - 2;
|
|
@@ -5206,6 +5224,8 @@ var AIDiagram = (function (exports) {
|
|
|
5206
5224
|
continue;
|
|
5207
5225
|
const gs = g.style ?? {};
|
|
5208
5226
|
const gg = mkGroup(`group-${g.id}`, "gg");
|
|
5227
|
+
if (gs.opacity != null)
|
|
5228
|
+
gg.setAttribute("opacity", String(gs.opacity));
|
|
5209
5229
|
gg.appendChild(rc.rectangle(g.x, g.y, g.w, g.h, {
|
|
5210
5230
|
...BASE_ROUGH,
|
|
5211
5231
|
roughness: 1.7,
|
|
@@ -5218,14 +5238,26 @@ var AIDiagram = (function (exports) {
|
|
|
5218
5238
|
strokeLineDash: gs.strokeDash ?? palette.groupDash,
|
|
5219
5239
|
}));
|
|
5220
5240
|
// ── Group label typography ──────────────────────────
|
|
5221
|
-
// supports: font, font-size, letter-spacing
|
|
5222
|
-
// always left-anchored (single line)
|
|
5223
5241
|
const gLabelColor = gs.color ? String(gs.color) : palette.groupLabel;
|
|
5224
5242
|
const gFontSize = Number(gs.fontSize ?? 12);
|
|
5243
|
+
const gFontWeight = gs.fontWeight ?? 500;
|
|
5225
5244
|
const gFont = resolveStyleFont$1(gs, diagramFont);
|
|
5226
5245
|
const gLetterSpacing = gs.letterSpacing;
|
|
5246
|
+
const gPad = Number(gs.padding ?? 14);
|
|
5247
|
+
const gTextAlign = String(gs.textAlign ?? "left");
|
|
5248
|
+
const gAnchorMap = {
|
|
5249
|
+
left: "start",
|
|
5250
|
+
center: "middle",
|
|
5251
|
+
right: "end",
|
|
5252
|
+
};
|
|
5253
|
+
const gAnchor = gAnchorMap[gTextAlign] ?? "start";
|
|
5254
|
+
const gTextX = gTextAlign === "right"
|
|
5255
|
+
? g.x + g.w - gPad
|
|
5256
|
+
: gTextAlign === "center"
|
|
5257
|
+
? g.x + g.w / 2
|
|
5258
|
+
: g.x + gPad;
|
|
5227
5259
|
if (g.label) {
|
|
5228
|
-
gg.appendChild(mkText(g.label,
|
|
5260
|
+
gg.appendChild(mkText(g.label, gTextX, g.y + gPad, gFontSize, gFontWeight, gLabelColor, gAnchor, gFont, gLetterSpacing));
|
|
5229
5261
|
}
|
|
5230
5262
|
GL.appendChild(gg);
|
|
5231
5263
|
}
|
|
@@ -5246,6 +5278,8 @@ var AIDiagram = (function (exports) {
|
|
|
5246
5278
|
const [x1, y1] = getConnPoint$1(src, dstCX, dstCY);
|
|
5247
5279
|
const [x2, y2] = getConnPoint$1(dst, srcCX, srcCY);
|
|
5248
5280
|
const eg = mkGroup(`edge-${e.from}-${e.to}`, "eg");
|
|
5281
|
+
if (e.style?.opacity != null)
|
|
5282
|
+
eg.setAttribute("opacity", String(e.style.opacity));
|
|
5249
5283
|
const len = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) || 1;
|
|
5250
5284
|
const nx = (x2 - x1) / len, ny = (y2 - y1) / len;
|
|
5251
5285
|
const ecol = String(e.style?.stroke ?? palette.edgeStroke);
|
|
@@ -5286,7 +5320,9 @@ var AIDiagram = (function (exports) {
|
|
|
5286
5320
|
const eFontSize = Number(e.style?.fontSize ?? 11);
|
|
5287
5321
|
const eFont = resolveStyleFont$1(e.style ?? {}, diagramFont);
|
|
5288
5322
|
const eLetterSpacing = e.style?.letterSpacing;
|
|
5289
|
-
|
|
5323
|
+
const eFontWeight = e.style?.fontWeight ?? 400;
|
|
5324
|
+
const eLabelColor = String(e.style?.color ?? palette.edgeLabelText);
|
|
5325
|
+
eg.appendChild(mkText(e.label, mx, my, eFontSize, eFontWeight, eLabelColor, "middle", eFont, eLetterSpacing));
|
|
5290
5326
|
}
|
|
5291
5327
|
EL.appendChild(eg);
|
|
5292
5328
|
}
|
|
@@ -5295,6 +5331,8 @@ var AIDiagram = (function (exports) {
|
|
|
5295
5331
|
const NL = mkGroup("node-layer");
|
|
5296
5332
|
for (const n of sg.nodes) {
|
|
5297
5333
|
const ng = mkGroup(`node-${n.id}`, "ng");
|
|
5334
|
+
if (n.style?.opacity != null)
|
|
5335
|
+
ng.setAttribute("opacity", String(n.style.opacity));
|
|
5298
5336
|
renderShape$1(rc, n, palette).forEach((s) => ng.appendChild(s));
|
|
5299
5337
|
// ── Node / text typography ─────────────────────────
|
|
5300
5338
|
// supports: font, font-size, letter-spacing, text-align, line-height
|
|
@@ -5313,18 +5351,19 @@ var AIDiagram = (function (exports) {
|
|
|
5313
5351
|
// line-height is a multiplier (e.g. 1.4 = 140% of font-size)
|
|
5314
5352
|
const lineHeight = Number(n.style?.lineHeight ?? 1.3) * fontSize;
|
|
5315
5353
|
const letterSpacing = n.style?.letterSpacing;
|
|
5354
|
+
const pad = Number(n.style?.padding ?? 8);
|
|
5316
5355
|
// x shifts for left / right alignment
|
|
5317
5356
|
const textX = textAlign === "left"
|
|
5318
|
-
? n.x +
|
|
5357
|
+
? n.x + pad
|
|
5319
5358
|
: textAlign === "right"
|
|
5320
|
-
? n.x + n.w -
|
|
5359
|
+
? n.x + n.w - pad
|
|
5321
5360
|
: n.x + n.w / 2;
|
|
5322
5361
|
const lines = n.shape === 'text' && !n.label.includes('\n')
|
|
5323
|
-
? wrapText$1(n.label, n.w -
|
|
5362
|
+
? wrapText$1(n.label, n.w - pad * 2, fontSize)
|
|
5324
5363
|
: n.label.split('\n');
|
|
5325
5364
|
const verticalAlign = String(n.style?.verticalAlign ?? "middle");
|
|
5326
|
-
const nodeBodyTop = n.y +
|
|
5327
|
-
const nodeBodyBottom = n.y + n.h -
|
|
5365
|
+
const nodeBodyTop = n.y + pad;
|
|
5366
|
+
const nodeBodyBottom = n.y + n.h - pad;
|
|
5328
5367
|
const nodeBodyMid = n.y + n.h / 2;
|
|
5329
5368
|
const blockH = (lines.length - 1) * lineHeight;
|
|
5330
5369
|
const textCY = verticalAlign === "top"
|
|
@@ -5356,15 +5395,19 @@ var AIDiagram = (function (exports) {
|
|
|
5356
5395
|
const fill = String(gs.fill ?? palette.tableFill);
|
|
5357
5396
|
const strk = String(gs.stroke ?? palette.tableStroke);
|
|
5358
5397
|
const textCol = String(gs.color ?? palette.tableText);
|
|
5359
|
-
const hdrFill = palette.tableHeaderFill;
|
|
5398
|
+
const hdrFill = gs.fill ? darkenHex$1(fill, 0.08) : palette.tableHeaderFill;
|
|
5360
5399
|
const hdrText = String(gs.color ?? palette.tableHeaderText);
|
|
5361
5400
|
const divCol = palette.tableDivider;
|
|
5362
5401
|
const pad = t.labelH;
|
|
5402
|
+
const tStrokeWidth = Number(gs.strokeWidth ?? 1.5);
|
|
5403
|
+
const tFontWeight = gs.fontWeight ?? 500;
|
|
5363
5404
|
// ── Table-level font (applies to label + all cells) ─
|
|
5364
5405
|
// supports: font, font-size, letter-spacing
|
|
5365
5406
|
const tFontSize = Number(gs.fontSize ?? 12);
|
|
5366
5407
|
const tFont = resolveStyleFont$1(gs, diagramFont);
|
|
5367
5408
|
const tLetterSpacing = gs.letterSpacing;
|
|
5409
|
+
if (gs.opacity != null)
|
|
5410
|
+
tg.setAttribute("opacity", String(gs.opacity));
|
|
5368
5411
|
// outer border
|
|
5369
5412
|
tg.appendChild(rc.rectangle(t.x, t.y, t.w, t.h, {
|
|
5370
5413
|
...BASE_ROUGH,
|
|
@@ -5372,7 +5415,8 @@ var AIDiagram = (function (exports) {
|
|
|
5372
5415
|
fill,
|
|
5373
5416
|
fillStyle: "solid",
|
|
5374
5417
|
stroke: strk,
|
|
5375
|
-
strokeWidth:
|
|
5418
|
+
strokeWidth: tStrokeWidth,
|
|
5419
|
+
...(gs.strokeDash ? { strokeLineDash: gs.strokeDash } : {}),
|
|
5376
5420
|
}));
|
|
5377
5421
|
// label strip separator
|
|
5378
5422
|
tg.appendChild(rc.line(t.x, t.y + pad, t.x + t.w, t.y + pad, {
|
|
@@ -5381,8 +5425,8 @@ var AIDiagram = (function (exports) {
|
|
|
5381
5425
|
stroke: strk,
|
|
5382
5426
|
strokeWidth: 1,
|
|
5383
5427
|
}));
|
|
5384
|
-
// ── Table label: font, font-size, letter-spacing (always left) ──
|
|
5385
|
-
tg.appendChild(mkText(t.label, t.x + 10, t.y + pad / 2, tFontSize,
|
|
5428
|
+
// ── Table label: font, font-size, font-weight, letter-spacing (always left) ──
|
|
5429
|
+
tg.appendChild(mkText(t.label, t.x + 10, t.y + pad / 2, tFontSize, tFontWeight, textCol, "start", tFont, tLetterSpacing));
|
|
5386
5430
|
// rows
|
|
5387
5431
|
let rowY = t.y + pad;
|
|
5388
5432
|
for (const row of t.rows) {
|
|
@@ -5411,7 +5455,7 @@ var AIDiagram = (function (exports) {
|
|
|
5411
5455
|
right: "end",
|
|
5412
5456
|
};
|
|
5413
5457
|
const cellAnchor = cellAnchorMap[cellAlignProp] ?? "middle";
|
|
5414
|
-
const cellFw = row.kind === "header" ? 600 : 400;
|
|
5458
|
+
const cellFw = row.kind === "header" ? 600 : (gs.fontWeight ?? 400);
|
|
5415
5459
|
const cellColor = row.kind === "header" ? hdrText : textCol;
|
|
5416
5460
|
let cx = t.x;
|
|
5417
5461
|
row.cells.forEach((cell, i) => {
|
|
@@ -5450,27 +5494,31 @@ var AIDiagram = (function (exports) {
|
|
|
5450
5494
|
const gs = n.style ?? {};
|
|
5451
5495
|
const fill = String(gs.fill ?? palette.noteFill);
|
|
5452
5496
|
const strk = String(gs.stroke ?? palette.noteStroke);
|
|
5497
|
+
const nStrokeWidth = Number(gs.strokeWidth ?? 1.2);
|
|
5453
5498
|
const fold = 14;
|
|
5454
5499
|
const { x, y, w, h } = n;
|
|
5500
|
+
if (gs.opacity != null)
|
|
5501
|
+
ng.setAttribute("opacity", String(gs.opacity));
|
|
5455
5502
|
// ── Note typography ─────────────────────────────────
|
|
5456
|
-
// supports: font, font-size, letter-spacing, text-align, line-height
|
|
5457
5503
|
const nFontSize = Number(gs.fontSize ?? 12);
|
|
5504
|
+
const nFontWeight = gs.fontWeight ?? 400;
|
|
5458
5505
|
const nFont = resolveStyleFont$1(gs, diagramFont);
|
|
5459
5506
|
const nLetterSpacing = gs.letterSpacing;
|
|
5460
5507
|
const nLineHeight = Number(gs.lineHeight ?? 1.4) * nFontSize;
|
|
5461
5508
|
const nTextAlign = String(gs.textAlign ?? "left");
|
|
5509
|
+
const nPad = Number(gs.padding ?? 12);
|
|
5462
5510
|
const nAnchorMap = {
|
|
5463
5511
|
left: "start",
|
|
5464
5512
|
center: "middle",
|
|
5465
5513
|
right: "end",
|
|
5466
5514
|
};
|
|
5467
5515
|
const nAnchor = nAnchorMap[nTextAlign] ?? "start";
|
|
5468
|
-
// x position for the text block (pad from left, with alignment)
|
|
5469
5516
|
const nTextX = nTextAlign === "right"
|
|
5470
|
-
? x + w - fold -
|
|
5517
|
+
? x + w - fold - nPad
|
|
5471
5518
|
: nTextAlign === "center"
|
|
5472
5519
|
? x + (w - fold) / 2
|
|
5473
|
-
: x +
|
|
5520
|
+
: x + nPad;
|
|
5521
|
+
const nFoldPad = fold + nPad; // text starts below fold + user padding
|
|
5474
5522
|
ng.appendChild(rc.polygon([
|
|
5475
5523
|
[x, y],
|
|
5476
5524
|
[x + w - fold, y],
|
|
@@ -5483,7 +5531,8 @@ var AIDiagram = (function (exports) {
|
|
|
5483
5531
|
fill,
|
|
5484
5532
|
fillStyle: "solid",
|
|
5485
5533
|
stroke: strk,
|
|
5486
|
-
strokeWidth:
|
|
5534
|
+
strokeWidth: nStrokeWidth,
|
|
5535
|
+
...(gs.strokeDash ? { strokeLineDash: gs.strokeDash } : {}),
|
|
5487
5536
|
}));
|
|
5488
5537
|
ng.appendChild(rc.polygon([
|
|
5489
5538
|
[x + w - fold, y],
|
|
@@ -5495,11 +5544,11 @@ var AIDiagram = (function (exports) {
|
|
|
5495
5544
|
fill: palette.noteFold,
|
|
5496
5545
|
fillStyle: "solid",
|
|
5497
5546
|
stroke: strk,
|
|
5498
|
-
strokeWidth: 0.8,
|
|
5547
|
+
strokeWidth: Math.min(nStrokeWidth, 0.8),
|
|
5499
5548
|
}));
|
|
5500
5549
|
const nVerticalAlign = String(gs.verticalAlign ?? "top");
|
|
5501
|
-
const bodyTop = y +
|
|
5502
|
-
const bodyBottom = y + h -
|
|
5550
|
+
const bodyTop = y + nFoldPad;
|
|
5551
|
+
const bodyBottom = y + h - nPad;
|
|
5503
5552
|
const bodyMid = (bodyTop + bodyBottom) / 2;
|
|
5504
5553
|
const blockH = (n.lines.length - 1) * nLineHeight;
|
|
5505
5554
|
const blockCY = nVerticalAlign === "bottom"
|
|
@@ -5507,13 +5556,11 @@ var AIDiagram = (function (exports) {
|
|
|
5507
5556
|
: nVerticalAlign === "middle"
|
|
5508
5557
|
? bodyMid
|
|
5509
5558
|
: bodyTop + blockH / 2;
|
|
5510
|
-
// multiline: use mkMultilineText so line-height is respected
|
|
5511
5559
|
if (n.lines.length > 1) {
|
|
5512
|
-
|
|
5513
|
-
ng.appendChild(mkMultilineText(n.lines, nTextX, blockCY, nFontSize, 400, String(gs.color ?? palette.noteText), nAnchor, nLineHeight, nFont, nLetterSpacing));
|
|
5560
|
+
ng.appendChild(mkMultilineText(n.lines, nTextX, blockCY, nFontSize, nFontWeight, String(gs.color ?? palette.noteText), nAnchor, nLineHeight, nFont, nLetterSpacing));
|
|
5514
5561
|
}
|
|
5515
5562
|
else {
|
|
5516
|
-
ng.appendChild(mkText(n.lines[0] ?? "", nTextX, blockCY, nFontSize,
|
|
5563
|
+
ng.appendChild(mkText(n.lines[0] ?? "", nTextX, blockCY, nFontSize, nFontWeight, String(gs.color ?? palette.noteText), nAnchor, nFont, nLetterSpacing));
|
|
5517
5564
|
}
|
|
5518
5565
|
NoteL.appendChild(ng);
|
|
5519
5566
|
}
|
|
@@ -5522,13 +5569,27 @@ var AIDiagram = (function (exports) {
|
|
|
5522
5569
|
const MDL = mkGroup('markdown-layer');
|
|
5523
5570
|
for (const m of sg.markdowns) {
|
|
5524
5571
|
const mg = mkGroup(`markdown-${m.id}`, 'mdg');
|
|
5525
|
-
const
|
|
5526
|
-
const
|
|
5527
|
-
const
|
|
5572
|
+
const gs = m.style ?? {};
|
|
5573
|
+
const mFont = resolveStyleFont$1(gs, diagramFont);
|
|
5574
|
+
const baseColor = String(gs.color ?? palette.nodeText);
|
|
5575
|
+
const textAlign = String(gs.textAlign ?? 'left');
|
|
5528
5576
|
const anchor = textAlign === 'right' ? 'end'
|
|
5529
5577
|
: textAlign === 'center' ? 'middle'
|
|
5530
5578
|
: 'start';
|
|
5531
|
-
const PAD = Number(
|
|
5579
|
+
const PAD = Number(gs.padding ?? 16);
|
|
5580
|
+
const mLetterSpacing = gs.letterSpacing;
|
|
5581
|
+
if (gs.opacity != null)
|
|
5582
|
+
mg.setAttribute('opacity', String(gs.opacity));
|
|
5583
|
+
// Background + border
|
|
5584
|
+
if (gs.fill || gs.stroke) {
|
|
5585
|
+
mg.appendChild(rc.rectangle(m.x, m.y, m.w, m.h, {
|
|
5586
|
+
...BASE_ROUGH, seed: hashStr$3(m.id),
|
|
5587
|
+
fill: String(gs.fill ?? 'none'), fillStyle: 'solid',
|
|
5588
|
+
stroke: String(gs.stroke ?? 'none'),
|
|
5589
|
+
strokeWidth: Number(gs.strokeWidth ?? 1.2),
|
|
5590
|
+
...(gs.strokeDash ? { strokeLineDash: gs.strokeDash } : {}),
|
|
5591
|
+
}));
|
|
5592
|
+
}
|
|
5532
5593
|
const textX = textAlign === 'right' ? m.x + m.w - PAD
|
|
5533
5594
|
: textAlign === 'center' ? m.x + m.w / 2
|
|
5534
5595
|
: m.x + PAD;
|
|
@@ -5551,6 +5612,8 @@ var AIDiagram = (function (exports) {
|
|
|
5551
5612
|
t.setAttribute('fill', baseColor);
|
|
5552
5613
|
t.setAttribute('pointer-events', 'none');
|
|
5553
5614
|
t.setAttribute('user-select', 'none');
|
|
5615
|
+
if (mLetterSpacing != null)
|
|
5616
|
+
t.setAttribute('letter-spacing', String(mLetterSpacing));
|
|
5554
5617
|
for (const run of line.runs) {
|
|
5555
5618
|
const span = se('tspan');
|
|
5556
5619
|
span.textContent = run.text;
|
|
@@ -5640,7 +5703,7 @@ var AIDiagram = (function (exports) {
|
|
|
5640
5703
|
});
|
|
5641
5704
|
}
|
|
5642
5705
|
// ── Axes ───────────────────────────────────────────────────
|
|
5643
|
-
function drawAxes(rc, ctx, c, px, py, pw, ph, allY, labelCol, R) {
|
|
5706
|
+
function drawAxes(rc, ctx, c, px, py, pw, ph, allY, labelCol, R, font = 'system-ui, sans-serif') {
|
|
5644
5707
|
const toY = makeValueToY(allY, py, ph);
|
|
5645
5708
|
const baseline = toY(0);
|
|
5646
5709
|
// Y axis
|
|
@@ -5654,7 +5717,7 @@ var AIDiagram = (function (exports) {
|
|
|
5654
5717
|
continue;
|
|
5655
5718
|
rc.line(px - 3, ty, px, ty, { roughness: 0.2, seed: hashStr$2(c.id + 'yt' + tick), stroke: labelCol, strokeWidth: 0.7 });
|
|
5656
5719
|
ctx.save();
|
|
5657
|
-
ctx.font =
|
|
5720
|
+
ctx.font = `400 9px ${font}`;
|
|
5658
5721
|
ctx.fillStyle = labelCol;
|
|
5659
5722
|
ctx.textAlign = 'right';
|
|
5660
5723
|
ctx.textBaseline = 'middle';
|
|
@@ -5663,9 +5726,9 @@ var AIDiagram = (function (exports) {
|
|
|
5663
5726
|
}
|
|
5664
5727
|
}
|
|
5665
5728
|
// ── Legend ─────────────────────────────────────────────────
|
|
5666
|
-
function drawLegend(ctx, labels, colors, x, y, labelCol) {
|
|
5729
|
+
function drawLegend(ctx, labels, colors, x, y, labelCol, font = 'system-ui, sans-serif') {
|
|
5667
5730
|
ctx.save();
|
|
5668
|
-
ctx.font =
|
|
5731
|
+
ctx.font = `400 9px ${font}`;
|
|
5669
5732
|
ctx.textAlign = 'left';
|
|
5670
5733
|
ctx.textBaseline = 'middle';
|
|
5671
5734
|
labels.forEach((lbl, i) => {
|
|
@@ -5683,6 +5746,11 @@ var AIDiagram = (function (exports) {
|
|
|
5683
5746
|
const bgFill = String(s.fill ?? pal.nodeFill);
|
|
5684
5747
|
const bgStroke = String(s.stroke ?? (pal.nodeStroke === 'none' ? '#c8b898' : pal.nodeStroke));
|
|
5685
5748
|
const lc = String(s.color ?? pal.labelText);
|
|
5749
|
+
const cFont = String(s.font ? `${s.font}, system-ui, sans-serif` : 'system-ui, sans-serif');
|
|
5750
|
+
const cFontSize = Number(s.fontSize ?? 12);
|
|
5751
|
+
const cFontWeight = s.fontWeight ?? 600;
|
|
5752
|
+
if (s.opacity != null)
|
|
5753
|
+
ctx.globalAlpha = Number(s.opacity);
|
|
5686
5754
|
// Background
|
|
5687
5755
|
rc.rectangle(c.x, c.y, c.w, c.h, {
|
|
5688
5756
|
...R, seed: hashStr$2(c.id),
|
|
@@ -5693,30 +5761,31 @@ var AIDiagram = (function (exports) {
|
|
|
5693
5761
|
...(s.strokeDash ? { strokeLineDash: s.strokeDash } : {}),
|
|
5694
5762
|
});
|
|
5695
5763
|
// Title
|
|
5696
|
-
if (c.
|
|
5764
|
+
if (c.label) {
|
|
5697
5765
|
ctx.save();
|
|
5698
|
-
ctx.font =
|
|
5766
|
+
ctx.font = `${cFontWeight} ${cFontSize}px ${cFont}`;
|
|
5699
5767
|
ctx.fillStyle = lc;
|
|
5700
5768
|
ctx.textAlign = 'center';
|
|
5701
5769
|
ctx.textBaseline = 'middle';
|
|
5702
|
-
ctx.fillText(c.
|
|
5770
|
+
ctx.fillText(c.label, c.x + c.w / 2, c.y + 14);
|
|
5703
5771
|
ctx.restore();
|
|
5704
5772
|
}
|
|
5705
5773
|
const { px, py, pw, ph, cx, cy } = chartLayout(c);
|
|
5706
5774
|
// ── Pie / Donut ──────────────────────────────────────────
|
|
5707
5775
|
if (c.chartType === 'pie' || c.chartType === 'donut') {
|
|
5708
5776
|
const { segments, total } = parsePie(c.data);
|
|
5709
|
-
const r = Math.min(c.w * 0.38, (c.h - (c.
|
|
5777
|
+
const r = Math.min(c.w * 0.38, (c.h - (c.label ? 24 : 8)) * 0.44);
|
|
5710
5778
|
const ir = c.chartType === 'donut' ? r * 0.48 : 0;
|
|
5711
5779
|
const legendX = c.x + 8;
|
|
5712
|
-
const legendY = c.y + (c.
|
|
5780
|
+
const legendY = c.y + (c.label ? 28 : 12);
|
|
5713
5781
|
let angle = -Math.PI / 2;
|
|
5714
5782
|
segments.forEach((seg, i) => {
|
|
5715
5783
|
const sweep = (seg.value / total) * Math.PI * 2;
|
|
5716
5784
|
drawPieArc(rc, ctx, cx, cy, r, ir, angle, angle + sweep, seg.color, hashStr$2(c.id + seg.label + i));
|
|
5717
5785
|
angle += sweep;
|
|
5718
5786
|
});
|
|
5719
|
-
drawLegend(ctx, segments.map(s => `${s.label} ${Math.round(s.value / total * 100)}%`), segments.map(s => s.color), legendX, legendY, lc);
|
|
5787
|
+
drawLegend(ctx, segments.map(s => `${s.label} ${Math.round(s.value / total * 100)}%`), segments.map(s => s.color), legendX, legendY, lc, cFont);
|
|
5788
|
+
ctx.globalAlpha = 1;
|
|
5720
5789
|
return;
|
|
5721
5790
|
}
|
|
5722
5791
|
// ── Scatter ───────────────────────────────────────────────
|
|
@@ -5736,7 +5805,8 @@ var AIDiagram = (function (exports) {
|
|
|
5736
5805
|
strokeWidth: 1.2,
|
|
5737
5806
|
});
|
|
5738
5807
|
});
|
|
5739
|
-
drawLegend(ctx, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y + (c.
|
|
5808
|
+
drawLegend(ctx, pts.map(p => p.label), CHART_COLORS, c.x + 8, c.y + (c.label ? 28 : 12), lc, cFont);
|
|
5809
|
+
ctx.globalAlpha = 1;
|
|
5740
5810
|
return;
|
|
5741
5811
|
}
|
|
5742
5812
|
// ── Bar / Line / Area ─────────────────────────────────────
|
|
@@ -5745,10 +5815,10 @@ var AIDiagram = (function (exports) {
|
|
|
5745
5815
|
const toY = makeValueToY(allY, py, ph);
|
|
5746
5816
|
const baseline = toY(0);
|
|
5747
5817
|
const n = labels.length;
|
|
5748
|
-
drawAxes(rc, ctx, c, px, py, pw, ph, allY, lc, R);
|
|
5818
|
+
drawAxes(rc, ctx, c, px, py, pw, ph, allY, lc, R, cFont);
|
|
5749
5819
|
// X labels
|
|
5750
5820
|
ctx.save();
|
|
5751
|
-
ctx.font =
|
|
5821
|
+
ctx.font = `400 9px ${cFont}`;
|
|
5752
5822
|
ctx.fillStyle = lc;
|
|
5753
5823
|
ctx.textAlign = 'center';
|
|
5754
5824
|
ctx.textBaseline = 'top';
|
|
@@ -5825,8 +5895,9 @@ var AIDiagram = (function (exports) {
|
|
|
5825
5895
|
}
|
|
5826
5896
|
// Multi-series legend
|
|
5827
5897
|
if (series.length > 1) {
|
|
5828
|
-
drawLegend(ctx, series.map(s => s.name), series.map(s => s.color), px, py - 2, lc);
|
|
5898
|
+
drawLegend(ctx, series.map(s => s.name), series.map(s => s.color), px, py - 2, lc, cFont);
|
|
5829
5899
|
}
|
|
5900
|
+
ctx.globalAlpha = 1;
|
|
5830
5901
|
}
|
|
5831
5902
|
|
|
5832
5903
|
// ============================================================
|
|
@@ -5839,6 +5910,14 @@ var AIDiagram = (function (exports) {
|
|
|
5839
5910
|
h = ((h * 33) ^ s.charCodeAt(i)) & 0xffff;
|
|
5840
5911
|
return h;
|
|
5841
5912
|
}
|
|
5913
|
+
/** Darken a CSS hex colour by `amount` (0–1). Falls back to input for non-hex. */
|
|
5914
|
+
function darkenHex(hex, amount = 0.12) {
|
|
5915
|
+
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
|
|
5916
|
+
if (!m)
|
|
5917
|
+
return hex;
|
|
5918
|
+
const d = (v) => Math.max(0, Math.round(parseInt(v, 16) * (1 - amount)));
|
|
5919
|
+
return `#${d(m[1]).toString(16).padStart(2, "0")}${d(m[2]).toString(16).padStart(2, "0")}${d(m[3]).toString(16).padStart(2, "0")}`;
|
|
5920
|
+
}
|
|
5842
5921
|
// ── Small helper: load + resolve font from a style map ────────────────────
|
|
5843
5922
|
function resolveStyleFont(style, fallback) {
|
|
5844
5923
|
const raw = String(style['font'] ?? '');
|
|
@@ -5956,6 +6035,7 @@ var AIDiagram = (function (exports) {
|
|
|
5956
6035
|
...R, seed: hashStr$1(n.id),
|
|
5957
6036
|
fill, fillStyle: 'solid',
|
|
5958
6037
|
stroke, strokeWidth: Number(s.strokeWidth ?? 1.9),
|
|
6038
|
+
...(s.strokeDash ? { strokeLineDash: s.strokeDash } : {}),
|
|
5959
6039
|
};
|
|
5960
6040
|
const cx = n.x + n.w / 2, cy = n.y + n.h / 2;
|
|
5961
6041
|
const hw = n.w / 2 - 2;
|
|
@@ -6091,6 +6171,8 @@ var AIDiagram = (function (exports) {
|
|
|
6091
6171
|
if (!g.w)
|
|
6092
6172
|
continue;
|
|
6093
6173
|
const gs = g.style ?? {};
|
|
6174
|
+
if (gs.opacity != null)
|
|
6175
|
+
ctx.globalAlpha = Number(gs.opacity);
|
|
6094
6176
|
rc.rectangle(g.x, g.y, g.w, g.h, {
|
|
6095
6177
|
...R, roughness: 1.7, bowing: 0.4, seed: hashStr$1(g.id),
|
|
6096
6178
|
fill: String(gs.fill ?? palette.groupFill),
|
|
@@ -6099,16 +6181,21 @@ var AIDiagram = (function (exports) {
|
|
|
6099
6181
|
strokeWidth: Number(gs.strokeWidth ?? 1.2),
|
|
6100
6182
|
strokeLineDash: gs.strokeDash ?? palette.groupDash,
|
|
6101
6183
|
});
|
|
6102
|
-
// ── Group label ──────────────────────────────────────
|
|
6103
|
-
// Only render when label has content — empty label = no reserved space
|
|
6104
|
-
// supports: font, font-size, letter-spacing (always left-anchored)
|
|
6105
6184
|
if (g.label) {
|
|
6106
6185
|
const gFontSize = Number(gs.fontSize ?? 12);
|
|
6186
|
+
const gFontWeight = gs.fontWeight ?? 500;
|
|
6107
6187
|
const gFont = resolveStyleFont(gs, diagramFont);
|
|
6108
6188
|
const gLetterSpacing = gs.letterSpacing;
|
|
6109
6189
|
const gLabelColor = gs.color ? String(gs.color) : palette.groupLabel;
|
|
6110
|
-
|
|
6190
|
+
const gPad = Number(gs.padding ?? 14);
|
|
6191
|
+
const gTextAlign = String(gs.textAlign ?? 'left');
|
|
6192
|
+
const gTextX = gTextAlign === 'right' ? g.x + g.w - gPad
|
|
6193
|
+
: gTextAlign === 'center' ? g.x + g.w / 2
|
|
6194
|
+
: g.x + gPad;
|
|
6195
|
+
drawText(ctx, g.label, gTextX, g.y + gPad + 2, gFontSize, gFontWeight, gLabelColor, gTextAlign, gFont, gLetterSpacing);
|
|
6111
6196
|
}
|
|
6197
|
+
if (gs.opacity != null)
|
|
6198
|
+
ctx.globalAlpha = 1;
|
|
6112
6199
|
}
|
|
6113
6200
|
// ── Edges ─────────────────────────────────────────────────
|
|
6114
6201
|
for (const e of sg.edges) {
|
|
@@ -6120,6 +6207,8 @@ var AIDiagram = (function (exports) {
|
|
|
6120
6207
|
const srcCX = src.x + src.w / 2, srcCY = src.y + src.h / 2;
|
|
6121
6208
|
const [x1, y1] = getConnPoint(src, dstCX, dstCY);
|
|
6122
6209
|
const [x2, y2] = getConnPoint(dst, srcCX, srcCY);
|
|
6210
|
+
if (e.style?.opacity != null)
|
|
6211
|
+
ctx.globalAlpha = Number(e.style.opacity);
|
|
6123
6212
|
const ecol = String(e.style?.stroke ?? palette.edgeStroke);
|
|
6124
6213
|
const { arrowAt, dashed } = connMeta(e.connector);
|
|
6125
6214
|
const len = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) || 1;
|
|
@@ -6148,17 +6237,22 @@ var AIDiagram = (function (exports) {
|
|
|
6148
6237
|
const eFontSize = Number(e.style?.fontSize ?? 11);
|
|
6149
6238
|
const eFont = resolveStyleFont(e.style ?? {}, diagramFont);
|
|
6150
6239
|
const eLetterSpacing = e.style?.letterSpacing;
|
|
6240
|
+
const eFontWeight = e.style?.fontWeight ?? 400;
|
|
6241
|
+
const eLabelColor = String(e.style?.color ?? palette.edgeLabelText);
|
|
6151
6242
|
ctx.save();
|
|
6152
|
-
ctx.font =
|
|
6243
|
+
ctx.font = `${eFontWeight} ${eFontSize}px ${eFont}`;
|
|
6153
6244
|
const tw = ctx.measureText(e.label).width + 12;
|
|
6154
6245
|
ctx.restore();
|
|
6155
6246
|
ctx.fillStyle = palette.edgeLabelBg;
|
|
6156
6247
|
ctx.fillRect(mx - tw / 2, my - 8, tw, 15);
|
|
6157
|
-
drawText(ctx, e.label, mx, my + 3, eFontSize,
|
|
6248
|
+
drawText(ctx, e.label, mx, my + 3, eFontSize, eFontWeight, eLabelColor, 'center', eFont, eLetterSpacing);
|
|
6158
6249
|
}
|
|
6250
|
+
ctx.globalAlpha = 1;
|
|
6159
6251
|
}
|
|
6160
6252
|
// ── Nodes ─────────────────────────────────────────────────
|
|
6161
6253
|
for (const n of sg.nodes) {
|
|
6254
|
+
if (n.style?.opacity != null)
|
|
6255
|
+
ctx.globalAlpha = Number(n.style.opacity);
|
|
6162
6256
|
renderShape(rc, ctx, n, palette, R);
|
|
6163
6257
|
// ── Node / text typography ─────────────────────────
|
|
6164
6258
|
// supports: font, font-size, letter-spacing, text-align,
|
|
@@ -6172,18 +6266,19 @@ var AIDiagram = (function (exports) {
|
|
|
6172
6266
|
const lineHeight = Number(n.style?.lineHeight ?? 1.3) * fontSize;
|
|
6173
6267
|
const letterSpacing = n.style?.letterSpacing;
|
|
6174
6268
|
const vertAlign = String(n.style?.verticalAlign ?? 'middle');
|
|
6269
|
+
const pad = Number(n.style?.padding ?? 8);
|
|
6175
6270
|
// x shifts for left/right alignment
|
|
6176
|
-
const textX = textAlign === 'left' ? n.x +
|
|
6177
|
-
: textAlign === 'right' ? n.x + n.w -
|
|
6271
|
+
const textX = textAlign === 'left' ? n.x + pad
|
|
6272
|
+
: textAlign === 'right' ? n.x + n.w - pad
|
|
6178
6273
|
: n.x + n.w / 2;
|
|
6179
6274
|
// word-wrap for text shape; explicit \n for all others
|
|
6180
6275
|
const rawLines = n.label.split('\n');
|
|
6181
6276
|
const lines = n.shape === 'text' && rawLines.length === 1
|
|
6182
|
-
? wrapText(n.label, n.w -
|
|
6277
|
+
? wrapText(n.label, n.w - pad * 2, fontSize)
|
|
6183
6278
|
: rawLines;
|
|
6184
6279
|
// vertical-align: compute textCY from top/middle/bottom
|
|
6185
|
-
const nodeBodyTop = n.y +
|
|
6186
|
-
const nodeBodyBottom = n.y + n.h -
|
|
6280
|
+
const nodeBodyTop = n.y + pad;
|
|
6281
|
+
const nodeBodyBottom = n.y + n.h - pad;
|
|
6187
6282
|
const blockH = (lines.length - 1) * lineHeight;
|
|
6188
6283
|
const textCY = vertAlign === 'top' ? nodeBodyTop + blockH / 2
|
|
6189
6284
|
: vertAlign === 'bottom' ? nodeBodyBottom - blockH / 2
|
|
@@ -6194,6 +6289,8 @@ var AIDiagram = (function (exports) {
|
|
|
6194
6289
|
else {
|
|
6195
6290
|
drawText(ctx, lines[0] ?? '', textX, textCY, fontSize, fontWeight, textColor, textAlign, nodeFont, letterSpacing);
|
|
6196
6291
|
}
|
|
6292
|
+
if (n.style?.opacity != null)
|
|
6293
|
+
ctx.globalAlpha = 1;
|
|
6197
6294
|
}
|
|
6198
6295
|
// ── Tables ────────────────────────────────────────────────
|
|
6199
6296
|
for (const t of sg.tables) {
|
|
@@ -6203,25 +6300,28 @@ var AIDiagram = (function (exports) {
|
|
|
6203
6300
|
const textCol = String(gs.color ?? palette.tableText);
|
|
6204
6301
|
const pad = t.labelH;
|
|
6205
6302
|
// ── Table-level font ────────────────────────────────
|
|
6206
|
-
// supports: font, font-size, letter-spacing
|
|
6207
|
-
// cells also support text-align
|
|
6208
6303
|
const tFontSize = Number(gs.fontSize ?? 12);
|
|
6209
6304
|
const tFont = resolveStyleFont(gs, diagramFont);
|
|
6210
6305
|
const tLetterSpacing = gs.letterSpacing;
|
|
6306
|
+
const tStrokeWidth = Number(gs.strokeWidth ?? 1.5);
|
|
6307
|
+
const tFontWeight = gs.fontWeight ?? 500;
|
|
6308
|
+
if (gs.opacity != null)
|
|
6309
|
+
ctx.globalAlpha = Number(gs.opacity);
|
|
6211
6310
|
rc.rectangle(t.x, t.y, t.w, t.h, {
|
|
6212
6311
|
...R, seed: hashStr$1(t.id),
|
|
6213
|
-
fill, fillStyle: 'solid', stroke: strk, strokeWidth:
|
|
6312
|
+
fill, fillStyle: 'solid', stroke: strk, strokeWidth: tStrokeWidth,
|
|
6313
|
+
...(gs.strokeDash ? { strokeLineDash: gs.strokeDash } : {}),
|
|
6214
6314
|
});
|
|
6215
6315
|
rc.line(t.x, t.y + pad, t.x + t.w, t.y + pad, {
|
|
6216
6316
|
roughness: 0.6, seed: hashStr$1(t.id + 'l'), stroke: strk, strokeWidth: 1,
|
|
6217
6317
|
});
|
|
6218
6318
|
// ── Table label: always left-anchored ───────────────
|
|
6219
|
-
drawText(ctx, t.label, t.x + 10, t.y + pad / 2, tFontSize,
|
|
6319
|
+
drawText(ctx, t.label, t.x + 10, t.y + pad / 2, tFontSize, tFontWeight, textCol, 'left', tFont, tLetterSpacing);
|
|
6220
6320
|
let rowY = t.y + pad;
|
|
6221
6321
|
for (const row of t.rows) {
|
|
6222
6322
|
const rh = row.kind === 'header' ? t.headerH : t.rowH;
|
|
6223
6323
|
if (row.kind === 'header') {
|
|
6224
|
-
ctx.fillStyle = palette.tableHeaderFill;
|
|
6324
|
+
ctx.fillStyle = gs.fill ? darkenHex(fill, 0.08) : palette.tableHeaderFill;
|
|
6225
6325
|
ctx.fillRect(t.x + 1, rowY + 1, t.w - 2, rh - 1);
|
|
6226
6326
|
}
|
|
6227
6327
|
rc.line(t.x, rowY + rh, t.x + t.w, rowY + rh, {
|
|
@@ -6234,7 +6334,7 @@ var AIDiagram = (function (exports) {
|
|
|
6234
6334
|
const cellAlignProp = (row.kind === 'header'
|
|
6235
6335
|
? 'center'
|
|
6236
6336
|
: String(gs.textAlign ?? 'center'));
|
|
6237
|
-
const cellFw = row.kind === 'header' ? 600 : 400;
|
|
6337
|
+
const cellFw = row.kind === 'header' ? 600 : (gs.fontWeight ?? 400);
|
|
6238
6338
|
const cellColor = row.kind === 'header'
|
|
6239
6339
|
? String(gs.color ?? palette.tableHeaderText)
|
|
6240
6340
|
: textCol;
|
|
@@ -6255,63 +6355,87 @@ var AIDiagram = (function (exports) {
|
|
|
6255
6355
|
});
|
|
6256
6356
|
rowY += rh;
|
|
6257
6357
|
}
|
|
6358
|
+
ctx.globalAlpha = 1;
|
|
6258
6359
|
}
|
|
6259
6360
|
// ── Notes ─────────────────────────────────────────────────
|
|
6260
6361
|
for (const n of sg.notes) {
|
|
6261
6362
|
const gs = n.style ?? {};
|
|
6262
6363
|
const fill = String(gs.fill ?? palette.noteFill);
|
|
6263
6364
|
const strk = String(gs.stroke ?? palette.noteStroke);
|
|
6365
|
+
const nStrokeWidth = Number(gs.strokeWidth ?? 1.2);
|
|
6264
6366
|
const fold = 14;
|
|
6265
6367
|
const { x, y, w, h } = n;
|
|
6368
|
+
if (gs.opacity != null)
|
|
6369
|
+
ctx.globalAlpha = Number(gs.opacity);
|
|
6266
6370
|
rc.polygon([
|
|
6267
6371
|
[x, y],
|
|
6268
6372
|
[x + w - fold, y],
|
|
6269
6373
|
[x + w, y + fold],
|
|
6270
6374
|
[x + w, y + h],
|
|
6271
6375
|
[x, y + h],
|
|
6272
|
-
], { ...R, seed: hashStr$1(n.id), fill, fillStyle: 'solid', stroke: strk,
|
|
6376
|
+
], { ...R, seed: hashStr$1(n.id), fill, fillStyle: 'solid', stroke: strk,
|
|
6377
|
+
strokeWidth: nStrokeWidth,
|
|
6378
|
+
...(gs.strokeDash ? { strokeLineDash: gs.strokeDash } : {}),
|
|
6379
|
+
});
|
|
6273
6380
|
rc.polygon([
|
|
6274
6381
|
[x + w - fold, y],
|
|
6275
6382
|
[x + w, y + fold],
|
|
6276
6383
|
[x + w - fold, y + fold],
|
|
6277
6384
|
], { roughness: 0.4, seed: hashStr$1(n.id + 'f'),
|
|
6278
|
-
fill: palette.noteFold, fillStyle: 'solid', stroke: strk,
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
// vertical-align, line-height
|
|
6385
|
+
fill: palette.noteFold, fillStyle: 'solid', stroke: strk,
|
|
6386
|
+
strokeWidth: Math.min(nStrokeWidth, 0.8),
|
|
6387
|
+
});
|
|
6282
6388
|
const nFontSize = Number(gs.fontSize ?? 12);
|
|
6389
|
+
const nFontWeight = gs.fontWeight ?? 400;
|
|
6283
6390
|
const nFont = resolveStyleFont(gs, diagramFont);
|
|
6284
6391
|
const nLetterSpacing = gs.letterSpacing;
|
|
6285
6392
|
const nLineHeight = Number(gs.lineHeight ?? 1.4) * nFontSize;
|
|
6286
6393
|
const nTextAlign = String(gs.textAlign ?? 'left');
|
|
6287
6394
|
const nVertAlign = String(gs.verticalAlign ?? 'top');
|
|
6288
6395
|
const nColor = String(gs.color ?? palette.noteText);
|
|
6289
|
-
const
|
|
6396
|
+
const nPad = Number(gs.padding ?? 12);
|
|
6397
|
+
const nTextX = nTextAlign === 'right' ? x + w - fold - nPad
|
|
6290
6398
|
: nTextAlign === 'center' ? x + (w - fold) / 2
|
|
6291
|
-
: x +
|
|
6292
|
-
|
|
6293
|
-
const bodyTop = y +
|
|
6294
|
-
const bodyBottom = y + h -
|
|
6399
|
+
: x + nPad;
|
|
6400
|
+
const nFoldPad = fold + nPad;
|
|
6401
|
+
const bodyTop = y + nFoldPad;
|
|
6402
|
+
const bodyBottom = y + h - nPad;
|
|
6295
6403
|
const blockH = (n.lines.length - 1) * nLineHeight;
|
|
6296
6404
|
const blockCY = nVertAlign === 'bottom' ? bodyBottom - blockH / 2
|
|
6297
6405
|
: nVertAlign === 'middle' ? (bodyTop + bodyBottom) / 2
|
|
6298
|
-
: bodyTop + blockH / 2;
|
|
6406
|
+
: bodyTop + blockH / 2;
|
|
6299
6407
|
if (n.lines.length > 1) {
|
|
6300
|
-
drawMultilineText(ctx, n.lines, nTextX, blockCY, nFontSize,
|
|
6408
|
+
drawMultilineText(ctx, n.lines, nTextX, blockCY, nFontSize, nFontWeight, nColor, nTextAlign, nLineHeight, nFont, nLetterSpacing);
|
|
6301
6409
|
}
|
|
6302
6410
|
else {
|
|
6303
|
-
drawText(ctx, n.lines[0] ?? '', nTextX, blockCY, nFontSize,
|
|
6411
|
+
drawText(ctx, n.lines[0] ?? '', nTextX, blockCY, nFontSize, nFontWeight, nColor, nTextAlign, nFont, nLetterSpacing);
|
|
6304
6412
|
}
|
|
6413
|
+
if (gs.opacity != null)
|
|
6414
|
+
ctx.globalAlpha = 1;
|
|
6305
6415
|
}
|
|
6306
6416
|
// ── Markdown blocks ────────────────────────────────────────
|
|
6307
6417
|
// Renders prose with Markdown headings and bold/italic inline spans.
|
|
6308
6418
|
// Canvas has no native bold-within-a-run, so each run is drawn
|
|
6309
6419
|
// individually with its own ctx.font setting.
|
|
6310
6420
|
for (const m of (sg.markdowns ?? [])) {
|
|
6311
|
-
const
|
|
6312
|
-
const
|
|
6313
|
-
const
|
|
6314
|
-
const
|
|
6421
|
+
const gs = m.style ?? {};
|
|
6422
|
+
const mFont = resolveStyleFont(gs, diagramFont);
|
|
6423
|
+
const baseColor = String(gs.color ?? palette.nodeText);
|
|
6424
|
+
const textAlign = String(gs.textAlign ?? 'left');
|
|
6425
|
+
const PAD = Number(gs.padding ?? 16);
|
|
6426
|
+
const mLetterSpacing = gs.letterSpacing;
|
|
6427
|
+
if (gs.opacity != null)
|
|
6428
|
+
ctx.globalAlpha = Number(gs.opacity);
|
|
6429
|
+
// Background + border
|
|
6430
|
+
if (gs.fill || gs.stroke) {
|
|
6431
|
+
rc.rectangle(m.x, m.y, m.w, m.h, {
|
|
6432
|
+
...R, seed: hashStr$1(m.id),
|
|
6433
|
+
fill: String(gs.fill ?? 'none'), fillStyle: 'solid',
|
|
6434
|
+
stroke: String(gs.stroke ?? 'none'),
|
|
6435
|
+
strokeWidth: Number(gs.strokeWidth ?? 1.2),
|
|
6436
|
+
...(gs.strokeDash ? { strokeLineDash: gs.strokeDash } : {}),
|
|
6437
|
+
});
|
|
6438
|
+
}
|
|
6315
6439
|
const anchorX = textAlign === 'right' ? m.x + m.w - PAD
|
|
6316
6440
|
: textAlign === 'center' ? m.x + m.w / 2
|
|
6317
6441
|
: m.x + PAD;
|
|
@@ -6329,14 +6453,29 @@ var AIDiagram = (function (exports) {
|
|
|
6329
6453
|
ctx.save();
|
|
6330
6454
|
ctx.textBaseline = 'middle';
|
|
6331
6455
|
ctx.fillStyle = baseColor;
|
|
6456
|
+
const ls = mLetterSpacing ?? 0;
|
|
6457
|
+
// measure run width including letter-spacing
|
|
6458
|
+
const runW = (run) => {
|
|
6459
|
+
return ctx.measureText(run.text).width + ls * run.text.length;
|
|
6460
|
+
};
|
|
6461
|
+
const drawRun = (run, rx) => {
|
|
6462
|
+
if (ls) {
|
|
6463
|
+
for (const ch of run.text) {
|
|
6464
|
+
ctx.fillText(ch, rx, lineY);
|
|
6465
|
+
rx += ctx.measureText(ch).width + ls;
|
|
6466
|
+
}
|
|
6467
|
+
}
|
|
6468
|
+
else {
|
|
6469
|
+
ctx.fillText(run.text, rx, lineY);
|
|
6470
|
+
}
|
|
6471
|
+
};
|
|
6332
6472
|
if (textAlign === 'center' || textAlign === 'right') {
|
|
6333
|
-
// Measure full line width first
|
|
6334
6473
|
let totalW = 0;
|
|
6335
6474
|
for (const run of line.runs) {
|
|
6336
6475
|
const runStyle = run.italic ? 'italic ' : '';
|
|
6337
6476
|
const runWeight = run.bold ? 700 : fontWeight;
|
|
6338
6477
|
ctx.font = `${runStyle}${runWeight} ${fontSize}px ${mFont}`;
|
|
6339
|
-
totalW +=
|
|
6478
|
+
totalW += runW(run);
|
|
6340
6479
|
}
|
|
6341
6480
|
let runX = textAlign === 'center' ? anchorX - totalW / 2 : anchorX - totalW;
|
|
6342
6481
|
ctx.textAlign = 'left';
|
|
@@ -6344,25 +6483,25 @@ var AIDiagram = (function (exports) {
|
|
|
6344
6483
|
const runStyle = run.italic ? 'italic ' : '';
|
|
6345
6484
|
const runWeight = run.bold ? 700 : fontWeight;
|
|
6346
6485
|
ctx.font = `${runStyle}${runWeight} ${fontSize}px ${mFont}`;
|
|
6347
|
-
|
|
6348
|
-
runX +=
|
|
6486
|
+
drawRun(run, runX);
|
|
6487
|
+
runX += runW(run);
|
|
6349
6488
|
}
|
|
6350
6489
|
}
|
|
6351
6490
|
else {
|
|
6352
|
-
// left-aligned — draw runs left to right from anchorX
|
|
6353
6491
|
let runX = anchorX;
|
|
6354
6492
|
ctx.textAlign = 'left';
|
|
6355
6493
|
for (const run of line.runs) {
|
|
6356
6494
|
const runStyle = run.italic ? 'italic ' : '';
|
|
6357
6495
|
const runWeight = run.bold ? 700 : fontWeight;
|
|
6358
6496
|
ctx.font = `${runStyle}${runWeight} ${fontSize}px ${mFont}`;
|
|
6359
|
-
|
|
6360
|
-
runX +=
|
|
6497
|
+
drawRun(run, runX);
|
|
6498
|
+
runX += runW(run);
|
|
6361
6499
|
}
|
|
6362
6500
|
}
|
|
6363
6501
|
ctx.restore();
|
|
6364
6502
|
y += LINE_SPACING[line.kind];
|
|
6365
6503
|
}
|
|
6504
|
+
ctx.globalAlpha = 1;
|
|
6366
6505
|
}
|
|
6367
6506
|
// ── Charts ────────────────────────────────────────────────
|
|
6368
6507
|
for (const c of sg.charts) {
|