sketchmark 0.2.3 → 0.2.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/dist/ast/types.d.ts +2 -1
- package/dist/ast/types.d.ts.map +1 -1
- package/dist/index.cjs +134 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -20
- 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/parser/tokenizer.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 +1 -0
- package/dist/scene/index.d.ts.map +1 -1
- package/dist/sketchmark.iife.js +134 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const KEYWORDS = new Set([
|
|
|
17
17
|
"parallelogram",
|
|
18
18
|
"text",
|
|
19
19
|
"image",
|
|
20
|
+
"icon",
|
|
20
21
|
"group",
|
|
21
22
|
"style",
|
|
22
23
|
"step",
|
|
@@ -230,6 +231,7 @@ const SHAPES = [
|
|
|
230
231
|
"parallelogram",
|
|
231
232
|
"text",
|
|
232
233
|
"image",
|
|
234
|
+
"icon",
|
|
233
235
|
];
|
|
234
236
|
const CHART_TYPES = [
|
|
235
237
|
"bar-chart",
|
|
@@ -420,7 +422,7 @@ function parse(src) {
|
|
|
420
422
|
kind: "node",
|
|
421
423
|
id,
|
|
422
424
|
shape,
|
|
423
|
-
label: props.label || id,
|
|
425
|
+
label: props.label || (shape === "image" || shape === "icon" ? "" : id),
|
|
424
426
|
...(groupId ? { groupId } : {}),
|
|
425
427
|
...(props.width ? { width: parseFloat(props.width) } : {}),
|
|
426
428
|
...(props.height ? { height: parseFloat(props.height) } : {}),
|
|
@@ -429,6 +431,8 @@ function parse(src) {
|
|
|
429
431
|
};
|
|
430
432
|
if (props.url)
|
|
431
433
|
node.imageUrl = props.url;
|
|
434
|
+
if (props.name)
|
|
435
|
+
node.iconName = props.name;
|
|
432
436
|
return node;
|
|
433
437
|
}
|
|
434
438
|
function parseEdge(fromId, connector, rest) {
|
|
@@ -497,7 +501,7 @@ function parse(src) {
|
|
|
497
501
|
j++;
|
|
498
502
|
}
|
|
499
503
|
// Support multiline via literal \n in label string
|
|
500
|
-
const rawLabel = props.label ??
|
|
504
|
+
const rawLabel = props.label ?? "";
|
|
501
505
|
return {
|
|
502
506
|
kind: "note",
|
|
503
507
|
id,
|
|
@@ -1276,6 +1280,7 @@ function buildSceneGraph(ast) {
|
|
|
1276
1280
|
height: n.height,
|
|
1277
1281
|
meta: n.meta,
|
|
1278
1282
|
imageUrl: n.imageUrl,
|
|
1283
|
+
iconName: n.iconName,
|
|
1279
1284
|
x: 0,
|
|
1280
1285
|
y: 0,
|
|
1281
1286
|
w: 0,
|
|
@@ -1502,6 +1507,10 @@ function sizeNode(n) {
|
|
|
1502
1507
|
}
|
|
1503
1508
|
break;
|
|
1504
1509
|
}
|
|
1510
|
+
case "icon":
|
|
1511
|
+
n.w = n.w || 48;
|
|
1512
|
+
n.h = n.h || (n.label !== n.id ? 64 : 48); // extra height for label
|
|
1513
|
+
break;
|
|
1505
1514
|
default:
|
|
1506
1515
|
n.w = n.w || Math.max(MIN_W, Math.min(MAX_W, labelW));
|
|
1507
1516
|
n.h = n.h || 52;
|
|
@@ -1538,9 +1547,8 @@ function sizeTable(t) {
|
|
|
1538
1547
|
const nData = rows.filter((r) => r.kind === "data").length;
|
|
1539
1548
|
t.h = labelH + nHeader * headerH + nData * rowH;
|
|
1540
1549
|
}
|
|
1541
|
-
function sizeChart(
|
|
1542
|
-
|
|
1543
|
-
c.h = c.h || 240;
|
|
1550
|
+
function sizeChart(_c) {
|
|
1551
|
+
// defaults already applied in buildSceneGraph
|
|
1544
1552
|
}
|
|
1545
1553
|
function sizeMarkdown(m) {
|
|
1546
1554
|
const pad = Number(m.style?.padding ?? 16);
|
|
@@ -5098,6 +5106,58 @@ function renderShape$1(rc, n, palette) {
|
|
|
5098
5106
|
}
|
|
5099
5107
|
case "text":
|
|
5100
5108
|
return [];
|
|
5109
|
+
case "icon": {
|
|
5110
|
+
if (n.iconName) {
|
|
5111
|
+
const [prefix, name] = n.iconName.includes(":")
|
|
5112
|
+
? n.iconName.split(":", 2)
|
|
5113
|
+
: ["mdi", n.iconName];
|
|
5114
|
+
const iconColor = s.color
|
|
5115
|
+
? encodeURIComponent(String(s.color))
|
|
5116
|
+
: encodeURIComponent(String(palette.nodeStroke));
|
|
5117
|
+
const iconSize = Math.min(n.w, n.h) - 4;
|
|
5118
|
+
const iconUrl = `https://api.iconify.design/${prefix}/${name}.svg?color=${iconColor}&width=${iconSize}&height=${iconSize}`;
|
|
5119
|
+
const img = document.createElementNS(NS, "image");
|
|
5120
|
+
img.setAttribute("href", iconUrl);
|
|
5121
|
+
img.setAttribute("x", String(n.x + 1));
|
|
5122
|
+
img.setAttribute("y", String(n.y + 1));
|
|
5123
|
+
img.setAttribute("width", String(n.w - 2));
|
|
5124
|
+
img.setAttribute("height", String(n.h - 2));
|
|
5125
|
+
img.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
5126
|
+
if (s.opacity != null)
|
|
5127
|
+
img.setAttribute("opacity", String(s.opacity));
|
|
5128
|
+
// clip-path for rounded corners (same as image)
|
|
5129
|
+
const clipId = `clip-${n.id}`;
|
|
5130
|
+
const defs = document.createElementNS(NS, "defs");
|
|
5131
|
+
const clip = document.createElementNS(NS, "clipPath");
|
|
5132
|
+
clip.setAttribute("id", clipId);
|
|
5133
|
+
const rect = document.createElementNS(NS, "rect");
|
|
5134
|
+
rect.setAttribute("x", String(n.x + 1));
|
|
5135
|
+
rect.setAttribute("y", String(n.y + 1));
|
|
5136
|
+
rect.setAttribute("width", String(n.w - 2));
|
|
5137
|
+
rect.setAttribute("height", String(n.h - 2));
|
|
5138
|
+
rect.setAttribute("rx", "6");
|
|
5139
|
+
clip.appendChild(rect);
|
|
5140
|
+
defs.appendChild(clip);
|
|
5141
|
+
img.setAttribute("clip-path", `url(#${clipId})`);
|
|
5142
|
+
// only draw border when stroke is explicitly set
|
|
5143
|
+
const els = [defs, img];
|
|
5144
|
+
if (s.stroke) {
|
|
5145
|
+
els.push(rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, {
|
|
5146
|
+
...opts,
|
|
5147
|
+
fill: "none",
|
|
5148
|
+
}));
|
|
5149
|
+
}
|
|
5150
|
+
return els;
|
|
5151
|
+
}
|
|
5152
|
+
// fallback: placeholder square
|
|
5153
|
+
return [
|
|
5154
|
+
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, {
|
|
5155
|
+
...opts,
|
|
5156
|
+
fill: "#e0e0e0",
|
|
5157
|
+
stroke: "#999999",
|
|
5158
|
+
}),
|
|
5159
|
+
];
|
|
5160
|
+
}
|
|
5101
5161
|
case "image": {
|
|
5102
5162
|
if (n.imageUrl) {
|
|
5103
5163
|
const img = document.createElementNS(NS, "image");
|
|
@@ -5120,11 +5180,15 @@ function renderShape$1(rc, n, palette) {
|
|
|
5120
5180
|
clip.appendChild(rect);
|
|
5121
5181
|
defs.appendChild(clip);
|
|
5122
5182
|
img.setAttribute("clip-path", `url(#${clipId})`);
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5183
|
+
// only draw border when stroke is explicitly set
|
|
5184
|
+
const els = [defs, img];
|
|
5185
|
+
if (s.stroke) {
|
|
5186
|
+
els.push(rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, {
|
|
5187
|
+
...opts,
|
|
5188
|
+
fill: "none",
|
|
5189
|
+
}));
|
|
5190
|
+
}
|
|
5191
|
+
return els;
|
|
5128
5192
|
}
|
|
5129
5193
|
return [
|
|
5130
5194
|
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, {
|
|
@@ -5368,9 +5432,11 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
5368
5432
|
: verticalAlign === "bottom"
|
|
5369
5433
|
? nodeBodyBottom - blockH / 2
|
|
5370
5434
|
: nodeBodyMid;
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5435
|
+
if (n.label) {
|
|
5436
|
+
ng.appendChild(lines.length > 1
|
|
5437
|
+
? mkMultilineText(lines, textX, textCY, fontSize, fontWeight, textColor, textAnchor, lineHeight, nodeFont, letterSpacing)
|
|
5438
|
+
: mkText(n.label, textX, textCY, fontSize, fontWeight, textColor, textAnchor, nodeFont, letterSpacing));
|
|
5439
|
+
}
|
|
5374
5440
|
if (options.interactive) {
|
|
5375
5441
|
ng.style.cursor = "pointer";
|
|
5376
5442
|
ng.addEventListener("click", () => options.onNodeClick?.(n.id));
|
|
@@ -5562,7 +5628,6 @@ function renderToSVG(sg, container, options = {}) {
|
|
|
5562
5628
|
NoteL.appendChild(ng);
|
|
5563
5629
|
}
|
|
5564
5630
|
svg.appendChild(NoteL);
|
|
5565
|
-
markdownMap(sg);
|
|
5566
5631
|
const MDL = mkGroup('markdown-layer');
|
|
5567
5632
|
for (const m of sg.markdowns) {
|
|
5568
5633
|
const mg = mkGroup(`markdown-${m.id}`, 'mdg');
|
|
@@ -6069,6 +6134,50 @@ function renderShape(rc, ctx, n, palette, R) {
|
|
|
6069
6134
|
break;
|
|
6070
6135
|
case 'text':
|
|
6071
6136
|
break; // no shape drawn
|
|
6137
|
+
case 'icon': {
|
|
6138
|
+
if (n.iconName) {
|
|
6139
|
+
const [prefix, name] = n.iconName.includes(':')
|
|
6140
|
+
? n.iconName.split(':', 2)
|
|
6141
|
+
: ['mdi', n.iconName];
|
|
6142
|
+
const iconColor = s.color
|
|
6143
|
+
? encodeURIComponent(String(s.color))
|
|
6144
|
+
: encodeURIComponent(String(palette.nodeStroke));
|
|
6145
|
+
const iconSize = Math.min(n.w, n.h) - 4;
|
|
6146
|
+
const iconUrl = `https://api.iconify.design/${prefix}/${name}.svg?color=${iconColor}&width=${iconSize}&height=${iconSize}`;
|
|
6147
|
+
const img = new Image();
|
|
6148
|
+
img.crossOrigin = 'anonymous';
|
|
6149
|
+
img.onload = () => {
|
|
6150
|
+
ctx.save();
|
|
6151
|
+
if (s.opacity != null)
|
|
6152
|
+
ctx.globalAlpha = Number(s.opacity);
|
|
6153
|
+
// clip-path for rounded corners (same as image)
|
|
6154
|
+
ctx.beginPath();
|
|
6155
|
+
const r = 6;
|
|
6156
|
+
ctx.moveTo(n.x + r, n.y);
|
|
6157
|
+
ctx.lineTo(n.x + n.w - r, n.y);
|
|
6158
|
+
ctx.quadraticCurveTo(n.x + n.w, n.y, n.x + n.w, n.y + r);
|
|
6159
|
+
ctx.lineTo(n.x + n.w, n.y + n.h - r);
|
|
6160
|
+
ctx.quadraticCurveTo(n.x + n.w, n.y + n.h, n.x + n.w - r, n.y + n.h);
|
|
6161
|
+
ctx.lineTo(n.x + r, n.y + n.h);
|
|
6162
|
+
ctx.quadraticCurveTo(n.x, n.y + n.h, n.x, n.y + n.h - r);
|
|
6163
|
+
ctx.lineTo(n.x, n.y + r);
|
|
6164
|
+
ctx.quadraticCurveTo(n.x, n.y, n.x + r, n.y);
|
|
6165
|
+
ctx.closePath();
|
|
6166
|
+
ctx.clip();
|
|
6167
|
+
ctx.drawImage(img, n.x + 1, n.y + 1, n.w - 2, n.h - 2);
|
|
6168
|
+
ctx.restore();
|
|
6169
|
+
// only draw border when stroke is explicitly set
|
|
6170
|
+
if (s.stroke) {
|
|
6171
|
+
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, { ...opts, fill: 'none' });
|
|
6172
|
+
}
|
|
6173
|
+
};
|
|
6174
|
+
img.src = iconUrl;
|
|
6175
|
+
}
|
|
6176
|
+
else {
|
|
6177
|
+
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, { ...opts, fill: '#e0e0e0', stroke: '#999999' });
|
|
6178
|
+
}
|
|
6179
|
+
return;
|
|
6180
|
+
}
|
|
6072
6181
|
case 'image': {
|
|
6073
6182
|
if (n.imageUrl) {
|
|
6074
6183
|
const img = new Image();
|
|
@@ -6090,7 +6199,10 @@ function renderShape(rc, ctx, n, palette, R) {
|
|
|
6090
6199
|
ctx.clip();
|
|
6091
6200
|
ctx.drawImage(img, n.x + 1, n.y + 1, n.w - 2, n.h - 2);
|
|
6092
6201
|
ctx.restore();
|
|
6093
|
-
|
|
6202
|
+
// only draw border when stroke is explicitly set
|
|
6203
|
+
if (s.stroke) {
|
|
6204
|
+
rc.rectangle(n.x + 1, n.y + 1, n.w - 2, n.h - 2, { ...opts, fill: 'none' });
|
|
6205
|
+
}
|
|
6094
6206
|
};
|
|
6095
6207
|
img.src = n.imageUrl;
|
|
6096
6208
|
}
|
|
@@ -6280,11 +6392,13 @@ function renderToCanvas(sg, canvas, options = {}) {
|
|
|
6280
6392
|
const textCY = vertAlign === 'top' ? nodeBodyTop + blockH / 2
|
|
6281
6393
|
: vertAlign === 'bottom' ? nodeBodyBottom - blockH / 2
|
|
6282
6394
|
: n.y + n.h / 2; // middle (default)
|
|
6283
|
-
if (
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6395
|
+
if (n.label) {
|
|
6396
|
+
if (lines.length > 1) {
|
|
6397
|
+
drawMultilineText(ctx, lines, textX, textCY, fontSize, fontWeight, textColor, textAlign, lineHeight, nodeFont, letterSpacing);
|
|
6398
|
+
}
|
|
6399
|
+
else {
|
|
6400
|
+
drawText(ctx, lines[0] ?? '', textX, textCY, fontSize, fontWeight, textColor, textAlign, nodeFont, letterSpacing);
|
|
6401
|
+
}
|
|
6288
6402
|
}
|
|
6289
6403
|
if (n.style?.opacity != null)
|
|
6290
6404
|
ctx.globalAlpha = 1;
|