sketchmark 1.2.1 → 1.3.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 +48 -68
- package/dist/animation/index.d.ts.map +1 -1
- package/dist/ast/types.d.ts +3 -0
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/index.cjs +112 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +112 -24
- 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/svg/index.d.ts.map +1 -1
- package/dist/scene/index.d.ts +3 -0
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +112 -24
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -377,6 +377,7 @@ function parse(src, options = {}) {
|
|
|
377
377
|
const ast = {
|
|
378
378
|
kind: "diagram",
|
|
379
379
|
layout: "column",
|
|
380
|
+
style: {},
|
|
380
381
|
nodes: [],
|
|
381
382
|
edges: [],
|
|
382
383
|
groups: [],
|
|
@@ -433,6 +434,54 @@ function parse(src, options = {}) {
|
|
|
433
434
|
}
|
|
434
435
|
return props;
|
|
435
436
|
}
|
|
437
|
+
function parseConfigValue(value) {
|
|
438
|
+
if (value === "true" || value === "on")
|
|
439
|
+
return true;
|
|
440
|
+
if (value === "false" || value === "off")
|
|
441
|
+
return false;
|
|
442
|
+
const numeric = Number(value);
|
|
443
|
+
return Number.isNaN(numeric) ? value : numeric;
|
|
444
|
+
}
|
|
445
|
+
function applyRootProps(props) {
|
|
446
|
+
const styleProps = propsToStyle(props);
|
|
447
|
+
const styleKeys = new Set([
|
|
448
|
+
"fill",
|
|
449
|
+
"stroke",
|
|
450
|
+
"stroke-width",
|
|
451
|
+
"color",
|
|
452
|
+
"opacity",
|
|
453
|
+
"font-size",
|
|
454
|
+
"font-weight",
|
|
455
|
+
"font",
|
|
456
|
+
"dash",
|
|
457
|
+
"stroke-dash",
|
|
458
|
+
"padding",
|
|
459
|
+
"text-align",
|
|
460
|
+
"vertical-align",
|
|
461
|
+
"line-height",
|
|
462
|
+
"letter-spacing",
|
|
463
|
+
]);
|
|
464
|
+
ast.style = { ...(ast.style ?? {}), ...styleProps };
|
|
465
|
+
if (props.layout) {
|
|
466
|
+
ast.layout = props.layout;
|
|
467
|
+
}
|
|
468
|
+
if (props.width !== undefined) {
|
|
469
|
+
ast.width = parseFloat(props.width);
|
|
470
|
+
}
|
|
471
|
+
if (props.height !== undefined) {
|
|
472
|
+
ast.height = parseFloat(props.height);
|
|
473
|
+
}
|
|
474
|
+
if (props.font !== undefined) {
|
|
475
|
+
ast.config.font = parseConfigValue(props.font);
|
|
476
|
+
}
|
|
477
|
+
for (const [key, value] of Object.entries(props)) {
|
|
478
|
+
if (key === "layout" || key === "width" || key === "height")
|
|
479
|
+
continue;
|
|
480
|
+
if (styleKeys.has(key))
|
|
481
|
+
continue;
|
|
482
|
+
ast.config[key] = parseConfigValue(value);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
436
485
|
function parseGroupProps(toks, startIndex) {
|
|
437
486
|
const props = {};
|
|
438
487
|
const itemIds = [];
|
|
@@ -914,8 +963,12 @@ function parse(src, options = {}) {
|
|
|
914
963
|
};
|
|
915
964
|
}
|
|
916
965
|
skipNL();
|
|
917
|
-
if (cur().value === "diagram")
|
|
966
|
+
if (cur().value === "diagram") {
|
|
918
967
|
skip();
|
|
968
|
+
const toks = lineTokens();
|
|
969
|
+
const props = parseSimpleProps(toks, 0);
|
|
970
|
+
applyRootProps(props);
|
|
971
|
+
}
|
|
919
972
|
skipNL();
|
|
920
973
|
while (cur().type !== "EOF" && cur().value !== "end") {
|
|
921
974
|
skipNL();
|
|
@@ -926,7 +979,7 @@ function parse(src, options = {}) {
|
|
|
926
979
|
continue;
|
|
927
980
|
}
|
|
928
981
|
if (v === "diagram") {
|
|
929
|
-
|
|
982
|
+
lineTokens();
|
|
930
983
|
continue;
|
|
931
984
|
}
|
|
932
985
|
if (v === "end")
|
|
@@ -936,10 +989,7 @@ function parse(src, options = {}) {
|
|
|
936
989
|
continue;
|
|
937
990
|
}
|
|
938
991
|
if (v === "layout") {
|
|
939
|
-
|
|
940
|
-
ast.layout = cur().value ?? "column";
|
|
941
|
-
skip();
|
|
942
|
-
continue;
|
|
992
|
+
throw new ParseError(`Root layout must be declared on the diagram line, e.g. diagram layout=absolute`, t.line, t.col);
|
|
943
993
|
}
|
|
944
994
|
if (v === "title") {
|
|
945
995
|
skip();
|
|
@@ -963,15 +1013,7 @@ function parse(src, options = {}) {
|
|
|
963
1013
|
continue;
|
|
964
1014
|
}
|
|
965
1015
|
if (v === "config") {
|
|
966
|
-
|
|
967
|
-
const key = cur().value;
|
|
968
|
-
skip();
|
|
969
|
-
if (cur().type === "EQUALS")
|
|
970
|
-
skip();
|
|
971
|
-
const value = cur().value;
|
|
972
|
-
skip();
|
|
973
|
-
ast.config[key] = value;
|
|
974
|
-
continue;
|
|
1016
|
+
throw new ParseError(`Root config must be declared on the diagram line, e.g. diagram gap=40 margin=0 tts=true`, t.line, t.col);
|
|
975
1017
|
}
|
|
976
1018
|
if (v === "style") {
|
|
977
1019
|
skip();
|
|
@@ -3624,6 +3666,7 @@ function buildSceneGraph(ast) {
|
|
|
3624
3666
|
title: ast.title,
|
|
3625
3667
|
description: ast.description,
|
|
3626
3668
|
layout: ast.layout,
|
|
3669
|
+
style: ast.style ?? {},
|
|
3627
3670
|
nodes,
|
|
3628
3671
|
edges,
|
|
3629
3672
|
groups,
|
|
@@ -3636,6 +3679,8 @@ function buildSceneGraph(ast) {
|
|
|
3636
3679
|
rootOrder: ast.rootOrder ?? [],
|
|
3637
3680
|
width: 0,
|
|
3638
3681
|
height: 0,
|
|
3682
|
+
fixedWidth: ast.width,
|
|
3683
|
+
fixedHeight: ast.height,
|
|
3639
3684
|
};
|
|
3640
3685
|
}
|
|
3641
3686
|
// ── Helpers ───────────────────────────────────────────────
|
|
@@ -5212,8 +5257,10 @@ function computeBounds(sg, margin) {
|
|
|
5212
5257
|
...sg.charts.map((c) => c.y + c.h),
|
|
5213
5258
|
...sg.markdowns.map((m) => m.y + m.h),
|
|
5214
5259
|
];
|
|
5215
|
-
|
|
5216
|
-
|
|
5260
|
+
const autoWidth = (allX.length ? Math.max(...allX) : 400) + margin;
|
|
5261
|
+
const autoHeight = (allY.length ? Math.max(...allY) : 300) + margin;
|
|
5262
|
+
sg.width = sg.fixedWidth ?? autoWidth;
|
|
5263
|
+
sg.height = sg.fixedHeight ?? autoHeight;
|
|
5217
5264
|
}
|
|
5218
5265
|
// ── Public entry point ────────────────────────────────────
|
|
5219
5266
|
function layout(sg) {
|
|
@@ -8249,7 +8296,7 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8249
8296
|
const palette = resolvePalette(themeName);
|
|
8250
8297
|
// ── Diagram-level font ──────────────────────────────────
|
|
8251
8298
|
const diagramFont = (() => {
|
|
8252
|
-
const raw = String(sg.config["font"] ?? "");
|
|
8299
|
+
const raw = String(sg.style?.font ?? sg.config["font"] ?? "");
|
|
8253
8300
|
if (raw) {
|
|
8254
8301
|
loadFont(raw);
|
|
8255
8302
|
return resolveFont(raw);
|
|
@@ -8279,8 +8326,22 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
8279
8326
|
bgRect.setAttribute("y", "0");
|
|
8280
8327
|
bgRect.setAttribute("width", String(sg.width));
|
|
8281
8328
|
bgRect.setAttribute("height", String(sg.height));
|
|
8282
|
-
bgRect.setAttribute("fill", palette.background);
|
|
8329
|
+
bgRect.setAttribute("fill", String(sg.style?.fill ?? palette.background));
|
|
8283
8330
|
svg.appendChild(bgRect);
|
|
8331
|
+
const rootStroke = sg.style?.stroke;
|
|
8332
|
+
const rootStrokeWidth = Number(sg.style?.strokeWidth ?? 0);
|
|
8333
|
+
if (rootStroke && rootStroke !== "none" && rootStrokeWidth > 0) {
|
|
8334
|
+
const frame = se("rect");
|
|
8335
|
+
const inset = rootStrokeWidth / 2;
|
|
8336
|
+
frame.setAttribute("x", String(inset));
|
|
8337
|
+
frame.setAttribute("y", String(inset));
|
|
8338
|
+
frame.setAttribute("width", String(Math.max(0, sg.width - rootStrokeWidth)));
|
|
8339
|
+
frame.setAttribute("height", String(Math.max(0, sg.height - rootStrokeWidth)));
|
|
8340
|
+
frame.setAttribute("fill", "none");
|
|
8341
|
+
frame.setAttribute("stroke", String(rootStroke));
|
|
8342
|
+
frame.setAttribute("stroke-width", String(rootStrokeWidth));
|
|
8343
|
+
svg.appendChild(frame);
|
|
8344
|
+
}
|
|
8284
8345
|
}
|
|
8285
8346
|
const rc = rough.svg(svg);
|
|
8286
8347
|
// ── Title ────────────────────────────────────────────────
|
|
@@ -9008,7 +9069,7 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
9008
9069
|
const palette = resolvePalette(themeName);
|
|
9009
9070
|
// ── Diagram-level font ───────────────────────────────────
|
|
9010
9071
|
const diagramFont = (() => {
|
|
9011
|
-
const raw = String(sg.config['font'] ?? '');
|
|
9072
|
+
const raw = String(sg.style?.font ?? sg.config['font'] ?? '');
|
|
9012
9073
|
if (raw) {
|
|
9013
9074
|
loadFont(raw);
|
|
9014
9075
|
return resolveFont(raw);
|
|
@@ -9017,8 +9078,18 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
9017
9078
|
})();
|
|
9018
9079
|
// ── Background ───────────────────────────────────────────
|
|
9019
9080
|
if (!options.transparent) {
|
|
9020
|
-
ctx.fillStyle = options.background ?? palette.background;
|
|
9081
|
+
ctx.fillStyle = options.background ?? String(sg.style?.fill ?? palette.background);
|
|
9021
9082
|
ctx.fillRect(0, 0, sg.width, sg.height);
|
|
9083
|
+
const rootStroke = sg.style?.stroke;
|
|
9084
|
+
const rootStrokeWidth = Number(sg.style?.strokeWidth ?? 0);
|
|
9085
|
+
if (rootStroke && rootStroke !== 'none' && rootStrokeWidth > 0) {
|
|
9086
|
+
const inset = rootStrokeWidth / 2;
|
|
9087
|
+
ctx.save();
|
|
9088
|
+
ctx.strokeStyle = String(rootStroke);
|
|
9089
|
+
ctx.lineWidth = rootStrokeWidth;
|
|
9090
|
+
ctx.strokeRect(inset, inset, Math.max(0, sg.width - rootStrokeWidth), Math.max(0, sg.height - rootStrokeWidth));
|
|
9091
|
+
ctx.restore();
|
|
9092
|
+
}
|
|
9022
9093
|
}
|
|
9023
9094
|
else {
|
|
9024
9095
|
ctx.clearRect(0, 0, sg.width, sg.height);
|
|
@@ -10068,6 +10139,10 @@ class AnimationController {
|
|
|
10068
10139
|
if (!el.id.startsWith("group-")) {
|
|
10069
10140
|
const ids = new Set([el.id]);
|
|
10070
10141
|
this._relatedElementIdsByPrimaryId.get(el.id)?.forEach((id) => ids.add(id));
|
|
10142
|
+
this.svg.querySelectorAll(POSITIONABLE_SELECTOR).forEach((candidate) => {
|
|
10143
|
+
if (candidate.dataset.animationParent === target)
|
|
10144
|
+
ids.add(candidate.id);
|
|
10145
|
+
});
|
|
10071
10146
|
return Array.from(ids)
|
|
10072
10147
|
.map((id) => getEl(this.svg, id))
|
|
10073
10148
|
.filter((candidate) => candidate != null);
|
|
@@ -10268,6 +10343,7 @@ class AnimationController {
|
|
|
10268
10343
|
el.classList.remove("hl", "faded", "hidden");
|
|
10269
10344
|
el.style.opacity = el.style.filter = "";
|
|
10270
10345
|
if (this.drawTargetNodes.has(el.id)) {
|
|
10346
|
+
hideDrawEl(el);
|
|
10271
10347
|
prepareNodeForDraw(el);
|
|
10272
10348
|
}
|
|
10273
10349
|
else {
|
|
@@ -10766,6 +10842,15 @@ class AnimationController {
|
|
|
10766
10842
|
_doColor(target, color) {
|
|
10767
10843
|
if (!color)
|
|
10768
10844
|
return;
|
|
10845
|
+
const applyTextColor = (root) => {
|
|
10846
|
+
root.querySelectorAll("text").forEach((t) => {
|
|
10847
|
+
t.style.fill = color;
|
|
10848
|
+
const existingStyle = t.getAttribute("style") ?? "";
|
|
10849
|
+
const nextStyle = `${existingStyle.replace(/(?:^|;)\s*fill\s*:[^;]*/g, "").trim().replace(/;?$/, ";")}fill:${color};`;
|
|
10850
|
+
t.setAttribute("style", nextStyle);
|
|
10851
|
+
t.setAttribute("fill", color);
|
|
10852
|
+
});
|
|
10853
|
+
};
|
|
10769
10854
|
for (const el of this._resolveCascadeTargets(target)) {
|
|
10770
10855
|
if (parseEdgeTarget(target)) {
|
|
10771
10856
|
el.querySelectorAll("path, line, polyline").forEach((p) => {
|
|
@@ -10788,11 +10873,14 @@ class AnimationController {
|
|
|
10788
10873
|
hit = true;
|
|
10789
10874
|
});
|
|
10790
10875
|
if (!hit) {
|
|
10791
|
-
el
|
|
10792
|
-
t.style.fill = color;
|
|
10793
|
-
});
|
|
10876
|
+
applyTextColor(el);
|
|
10794
10877
|
}
|
|
10795
10878
|
}
|
|
10879
|
+
this.svg.querySelectorAll(`${POSITIONABLE_SELECTOR}[data-animation-parent]`).forEach((el) => {
|
|
10880
|
+
if (el.dataset.animationParent === target) {
|
|
10881
|
+
applyTextColor(el);
|
|
10882
|
+
}
|
|
10883
|
+
});
|
|
10796
10884
|
}
|
|
10797
10885
|
// ── narration ───────────────────────────────────────────
|
|
10798
10886
|
_initCaption() {
|