@fieldnotes/core 0.38.7 → 0.39.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/dist/index.cjs +505 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -2
- package/dist/index.d.ts +26 -2
- package/dist/index.js +504 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -52,6 +52,7 @@ __export(index_exports, {
|
|
|
52
52
|
createText: () => createText,
|
|
53
53
|
drawHexPath: () => drawHexPath,
|
|
54
54
|
exportImage: () => exportImage,
|
|
55
|
+
exportSvg: () => exportSvg,
|
|
55
56
|
getActiveFormats: () => getActiveFormats,
|
|
56
57
|
getArrowBounds: () => getArrowBounds,
|
|
57
58
|
getArrowControlPoint: () => getArrowControlPoint,
|
|
@@ -1165,7 +1166,6 @@ var DEFAULT_BINDINGS = [
|
|
|
1165
1166
|
["redo", ["mod+y", "mod+shift+z"]],
|
|
1166
1167
|
["select-all", ["mod+a"]],
|
|
1167
1168
|
["copy", ["mod+c"]],
|
|
1168
|
-
["paste", ["mod+v"]],
|
|
1169
1169
|
["duplicate", ["mod+d"]],
|
|
1170
1170
|
["z-forward", ["]"]],
|
|
1171
1171
|
["z-backward", ["["]],
|
|
@@ -1340,6 +1340,7 @@ var KeyboardHandler = class {
|
|
|
1340
1340
|
this.shortcutMap = new ShortcutMap(deps.shortcuts?.bindings);
|
|
1341
1341
|
window.addEventListener("keydown", this.onKeyDown, { signal: deps.abortSignal });
|
|
1342
1342
|
window.addEventListener("keyup", this.onKeyUp, { signal: deps.abortSignal });
|
|
1343
|
+
window.addEventListener("paste", this.onPaste, { signal: deps.abortSignal });
|
|
1343
1344
|
}
|
|
1344
1345
|
shortcutMap;
|
|
1345
1346
|
get shortcuts() {
|
|
@@ -1355,12 +1356,15 @@ var KeyboardHandler = class {
|
|
|
1355
1356
|
zoomToLevel(level) {
|
|
1356
1357
|
this.deps.camera.zoomAt(level, this.viewportCenter());
|
|
1357
1358
|
}
|
|
1359
|
+
shouldHandle(target) {
|
|
1360
|
+
const el = target;
|
|
1361
|
+
if (el?.isContentEditable) return false;
|
|
1362
|
+
const tag = el?.tagName;
|
|
1363
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return false;
|
|
1364
|
+
return this.isInScope();
|
|
1365
|
+
}
|
|
1358
1366
|
onKeyDown = (e) => {
|
|
1359
|
-
|
|
1360
|
-
if (target?.isContentEditable) return;
|
|
1361
|
-
const tag = target?.tagName;
|
|
1362
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
|
|
1363
|
-
if (!this.isInScope()) return;
|
|
1367
|
+
if (!this.shouldHandle(e.target)) return;
|
|
1364
1368
|
if (e.key === " ") {
|
|
1365
1369
|
this.deps.setSpaceHeld(true);
|
|
1366
1370
|
}
|
|
@@ -1382,6 +1386,34 @@ var KeyboardHandler = class {
|
|
|
1382
1386
|
}
|
|
1383
1387
|
}
|
|
1384
1388
|
};
|
|
1389
|
+
onPaste = (e) => {
|
|
1390
|
+
if (!this.shouldHandle(e.target)) return;
|
|
1391
|
+
const items = e.clipboardData?.items;
|
|
1392
|
+
let file = null;
|
|
1393
|
+
if (items) {
|
|
1394
|
+
for (const it of items) {
|
|
1395
|
+
if (it.kind === "file" && it.type.startsWith("image/")) {
|
|
1396
|
+
file = it.getAsFile();
|
|
1397
|
+
break;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (file) {
|
|
1402
|
+
e.preventDefault();
|
|
1403
|
+
const world = this.deps.getLastPointerWorld() ?? this.deps.getCenteredWorld();
|
|
1404
|
+
if (this.deps.onPaste) {
|
|
1405
|
+
this.deps.onPaste(e, world);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
const reader = new FileReader();
|
|
1409
|
+
reader.onload = () => {
|
|
1410
|
+
if (typeof reader.result === "string") this.deps.addImage(reader.result, world);
|
|
1411
|
+
};
|
|
1412
|
+
reader.readAsDataURL(file);
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
this.deps.actions.paste();
|
|
1416
|
+
};
|
|
1385
1417
|
runAction(action, e) {
|
|
1386
1418
|
switch (action) {
|
|
1387
1419
|
case "delete":
|
|
@@ -1530,7 +1562,11 @@ var InputHandler = class {
|
|
|
1530
1562
|
this.spaceHeld = v;
|
|
1531
1563
|
},
|
|
1532
1564
|
getActivePointerCount: () => this.activePointers.size,
|
|
1533
|
-
dispatchToolHover: (e) => this.dispatchToolHover(e)
|
|
1565
|
+
dispatchToolHover: (e) => this.dispatchToolHover(e),
|
|
1566
|
+
addImage: options.addImage ?? (() => ""),
|
|
1567
|
+
getLastPointerWorld: () => this.lastPointerWorld(),
|
|
1568
|
+
getCenteredWorld: options.getCenteredWorld ?? (() => ({ x: 0, y: 0 })),
|
|
1569
|
+
onPaste: options.onPaste
|
|
1534
1570
|
});
|
|
1535
1571
|
this.element.style.touchAction = "none";
|
|
1536
1572
|
if (this.scope === "focus") {
|
|
@@ -2190,12 +2226,12 @@ function smoothToSegments(points) {
|
|
|
2190
2226
|
return [{ start: p0, cp1: { x: mx, y: my }, cp2: { x: mx, y: my }, end: p1 }];
|
|
2191
2227
|
}
|
|
2192
2228
|
const segments = [];
|
|
2193
|
-
const
|
|
2194
|
-
for (let i = 0; i <
|
|
2229
|
+
const n2 = points.length;
|
|
2230
|
+
for (let i = 0; i < n2 - 1; i++) {
|
|
2195
2231
|
const p0 = points[Math.max(0, i - 1)];
|
|
2196
2232
|
const p1 = points[i];
|
|
2197
2233
|
const p2 = points[i + 1];
|
|
2198
|
-
const p3 = points[Math.min(
|
|
2234
|
+
const p3 = points[Math.min(n2 - 1, i + 2)];
|
|
2199
2235
|
if (!p0 || !p1 || !p2 || !p3) continue;
|
|
2200
2236
|
const cp1 = {
|
|
2201
2237
|
x: p1.x + (p2.x - p0.x) / 6,
|
|
@@ -2885,11 +2921,11 @@ function pixelToOffset(x, y, cellSize, orientation) {
|
|
|
2885
2921
|
const offsetY = col % 2 !== 0 ? hexH / 2 : 0;
|
|
2886
2922
|
return { col, row: Math.round((y - offsetY) / hexH) };
|
|
2887
2923
|
}
|
|
2888
|
-
function enumerateHexRing(centerQ, centerR,
|
|
2924
|
+
function enumerateHexRing(centerQ, centerR, n2, orientation, cellSize) {
|
|
2889
2925
|
const cells = [];
|
|
2890
|
-
for (let dq = -
|
|
2891
|
-
const rMin = Math.max(-
|
|
2892
|
-
const rMax = Math.min(
|
|
2926
|
+
for (let dq = -n2; dq <= n2; dq++) {
|
|
2927
|
+
const rMin = Math.max(-n2, -dq - n2);
|
|
2928
|
+
const rMax = Math.min(n2, -dq + n2);
|
|
2893
2929
|
for (let dr = rMin; dr <= rMax; dr++) {
|
|
2894
2930
|
const absQ = centerQ + dq;
|
|
2895
2931
|
const absR = centerR + dr;
|
|
@@ -2910,28 +2946,28 @@ function getHexDistance(a, b, cellSize, orientation) {
|
|
|
2910
2946
|
return Math.max(Math.abs(dq), Math.abs(dr), Math.abs(ds));
|
|
2911
2947
|
}
|
|
2912
2948
|
function getHexCellsInRadius(center2, radiusCells, cellSize, orientation) {
|
|
2913
|
-
const
|
|
2949
|
+
const n2 = Math.round(radiusCells);
|
|
2914
2950
|
const off = pixelToOffset(center2.x, center2.y, cellSize, orientation);
|
|
2915
2951
|
const cube = offsetToCube(off.col, off.row, orientation);
|
|
2916
|
-
if (
|
|
2952
|
+
if (n2 <= 0) {
|
|
2917
2953
|
return [offsetToPixel(off.col, off.row, cellSize, orientation)];
|
|
2918
2954
|
}
|
|
2919
|
-
return enumerateHexRing(cube.q, cube.r,
|
|
2955
|
+
return enumerateHexRing(cube.q, cube.r, n2, orientation, cellSize);
|
|
2920
2956
|
}
|
|
2921
2957
|
function getHexCellsInCone(center2, angle, radiusCells, cellSize, orientation) {
|
|
2922
|
-
const
|
|
2958
|
+
const n2 = Math.round(radiusCells);
|
|
2923
2959
|
const off = pixelToOffset(center2.x, center2.y, cellSize, orientation);
|
|
2924
2960
|
const cube = offsetToCube(off.col, off.row, orientation);
|
|
2925
2961
|
const centerPixel = offsetToPixel(off.col, off.row, cellSize, orientation);
|
|
2926
|
-
if (
|
|
2962
|
+
if (n2 <= 0) return [centerPixel];
|
|
2927
2963
|
const vertexOffset = orientation === "pointy" ? Math.PI / 6 : 0;
|
|
2928
2964
|
const step = Math.PI / 3;
|
|
2929
2965
|
const snappedAngle = Math.round((angle - vertexOffset) / step) * step + vertexOffset;
|
|
2930
2966
|
const halfAngle = Math.PI / 6 + 1e-6;
|
|
2931
2967
|
const cells = [centerPixel];
|
|
2932
|
-
for (let dq = -
|
|
2933
|
-
const rMin = Math.max(-
|
|
2934
|
-
const rMax = Math.min(
|
|
2968
|
+
for (let dq = -n2; dq <= n2; dq++) {
|
|
2969
|
+
const rMin = Math.max(-n2, -dq - n2);
|
|
2970
|
+
const rMax = Math.min(n2, -dq + n2);
|
|
2935
2971
|
for (let dr = rMin; dr <= rMax; dr++) {
|
|
2936
2972
|
if (dq === 0 && dr === 0) continue;
|
|
2937
2973
|
const absQ = cube.q + dq;
|
|
@@ -2955,23 +2991,23 @@ function getHexCellsInCone(center2, angle, radiusCells, cellSize, orientation) {
|
|
|
2955
2991
|
return cells;
|
|
2956
2992
|
}
|
|
2957
2993
|
function getHexCellsInLine(center2, angle, radiusCells, cellSize, orientation) {
|
|
2958
|
-
const
|
|
2994
|
+
const n2 = Math.round(radiusCells);
|
|
2959
2995
|
const off = pixelToOffset(center2.x, center2.y, cellSize, orientation);
|
|
2960
2996
|
const cube = offsetToCube(off.col, off.row, orientation);
|
|
2961
2997
|
const centerPixel = offsetToPixel(off.col, off.row, cellSize, orientation);
|
|
2962
|
-
if (
|
|
2998
|
+
if (n2 <= 0) return [centerPixel];
|
|
2963
2999
|
const vertexOffset = orientation === "pointy" ? Math.PI / 6 : 0;
|
|
2964
3000
|
const step = Math.PI / 3;
|
|
2965
3001
|
const snappedAngle = Math.round((angle - vertexOffset) / step) * step + vertexOffset;
|
|
2966
3002
|
const cos = Math.cos(snappedAngle);
|
|
2967
3003
|
const sin = Math.sin(snappedAngle);
|
|
2968
3004
|
const snapUnit = Math.sqrt(3) * cellSize;
|
|
2969
|
-
const lineLength =
|
|
3005
|
+
const lineLength = n2 * snapUnit;
|
|
2970
3006
|
const halfWidth = snapUnit * 0.5 + 1e-6;
|
|
2971
3007
|
const cells = [];
|
|
2972
|
-
for (let dq = -
|
|
2973
|
-
const rMin = Math.max(-
|
|
2974
|
-
const rMax = Math.min(
|
|
3008
|
+
for (let dq = -n2; dq <= n2; dq++) {
|
|
3009
|
+
const rMin = Math.max(-n2, -dq - n2);
|
|
3010
|
+
const rMax = Math.min(n2, -dq + n2);
|
|
2975
3011
|
for (let dr = rMin; dr <= rMax; dr++) {
|
|
2976
3012
|
const absQ = cube.q + dq;
|
|
2977
3013
|
const absR = cube.r + dr;
|
|
@@ -2993,17 +3029,17 @@ function getHexCellsInLine(center2, angle, radiusCells, cellSize, orientation) {
|
|
|
2993
3029
|
return cells;
|
|
2994
3030
|
}
|
|
2995
3031
|
function getHexCellsInSquare(center2, radiusCells, cellSize, orientation) {
|
|
2996
|
-
const
|
|
3032
|
+
const n2 = Math.round(radiusCells);
|
|
2997
3033
|
const off = pixelToOffset(center2.x, center2.y, cellSize, orientation);
|
|
2998
3034
|
const cube = offsetToCube(off.col, off.row, orientation);
|
|
2999
3035
|
const centerPixel = offsetToPixel(off.col, off.row, cellSize, orientation);
|
|
3000
|
-
if (
|
|
3036
|
+
if (n2 <= 0) return [centerPixel];
|
|
3001
3037
|
const snapUnit = Math.sqrt(3) * cellSize;
|
|
3002
|
-
const halfSide =
|
|
3038
|
+
const halfSide = n2 * snapUnit / 2;
|
|
3003
3039
|
const cells = [];
|
|
3004
|
-
for (let dq = -
|
|
3005
|
-
const rMin = Math.max(-
|
|
3006
|
-
const rMax = Math.min(
|
|
3040
|
+
for (let dq = -n2; dq <= n2; dq++) {
|
|
3041
|
+
const rMin = Math.max(-n2, -dq - n2);
|
|
3042
|
+
const rMax = Math.min(n2, -dq + n2);
|
|
3007
3043
|
for (let dr = rMin; dr <= rMax; dr++) {
|
|
3008
3044
|
const absQ = cube.q + dq;
|
|
3009
3045
|
const absR = cube.r + dr;
|
|
@@ -3190,6 +3226,58 @@ function getSquareGridLines(bounds, cellSize) {
|
|
|
3190
3226
|
}
|
|
3191
3227
|
return { verticals, horizontals };
|
|
3192
3228
|
}
|
|
3229
|
+
function getHexVertices(cx, cy, circumradius, orientation) {
|
|
3230
|
+
const vertices = [];
|
|
3231
|
+
const angleOffset = orientation === "pointy" ? -Math.PI / 2 : 0;
|
|
3232
|
+
for (let i = 0; i < 6; i++) {
|
|
3233
|
+
const angle = Math.PI / 3 * i + angleOffset;
|
|
3234
|
+
vertices.push({
|
|
3235
|
+
x: cx + circumradius * Math.cos(angle),
|
|
3236
|
+
y: cy + circumradius * Math.sin(angle)
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
return vertices;
|
|
3240
|
+
}
|
|
3241
|
+
function getHexCenters(bounds, circumradius, orientation) {
|
|
3242
|
+
if (circumradius <= 0) return [];
|
|
3243
|
+
const centers = [];
|
|
3244
|
+
if (orientation === "pointy") {
|
|
3245
|
+
const hexW = Math.sqrt(3) * circumradius;
|
|
3246
|
+
const hexH = 2 * circumradius;
|
|
3247
|
+
const rowH = hexH * 0.75;
|
|
3248
|
+
const startRow = Math.floor((bounds.minY - circumradius) / rowH);
|
|
3249
|
+
const endRow = Math.ceil((bounds.maxY + circumradius) / rowH);
|
|
3250
|
+
const startCol = Math.floor((bounds.minX - hexW) / hexW);
|
|
3251
|
+
const endCol = Math.ceil((bounds.maxX + hexW) / hexW);
|
|
3252
|
+
for (let row = startRow; row <= endRow; row++) {
|
|
3253
|
+
const offsetX = row % 2 !== 0 ? hexW / 2 : 0;
|
|
3254
|
+
for (let col = startCol; col <= endCol; col++) {
|
|
3255
|
+
centers.push({
|
|
3256
|
+
x: col * hexW + offsetX,
|
|
3257
|
+
y: row * rowH
|
|
3258
|
+
});
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
} else {
|
|
3262
|
+
const hexW = 2 * circumradius;
|
|
3263
|
+
const hexH = Math.sqrt(3) * circumradius;
|
|
3264
|
+
const colW = hexW * 0.75;
|
|
3265
|
+
const startCol = Math.floor((bounds.minX - circumradius) / colW);
|
|
3266
|
+
const endCol = Math.ceil((bounds.maxX + circumradius) / colW);
|
|
3267
|
+
const startRow = Math.floor((bounds.minY - hexH) / hexH);
|
|
3268
|
+
const endRow = Math.ceil((bounds.maxY + hexH) / hexH);
|
|
3269
|
+
for (let col = startCol; col <= endCol; col++) {
|
|
3270
|
+
const offsetY = col % 2 !== 0 ? hexH / 2 : 0;
|
|
3271
|
+
for (let row = startRow; row <= endRow; row++) {
|
|
3272
|
+
centers.push({
|
|
3273
|
+
x: col * colW,
|
|
3274
|
+
y: row * hexH + offsetY
|
|
3275
|
+
});
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
return centers;
|
|
3280
|
+
}
|
|
3193
3281
|
function renderSquareGrid(ctx, bounds, cellSize, strokeColor, strokeWidth, opacity) {
|
|
3194
3282
|
if (cellSize <= 0) return;
|
|
3195
3283
|
const { verticals, horizontals } = getSquareGridLines(bounds, cellSize);
|
|
@@ -3588,6 +3676,8 @@ function createHtmlElement(input) {
|
|
|
3588
3676
|
};
|
|
3589
3677
|
if (input.domId) el.domId = input.domId;
|
|
3590
3678
|
if (input.interactive) el.interactive = input.interactive;
|
|
3679
|
+
if (input.htmlType) el.htmlType = input.htmlType;
|
|
3680
|
+
if (input.data) el.data = input.data;
|
|
3591
3681
|
return el;
|
|
3592
3682
|
}
|
|
3593
3683
|
function createShape(input) {
|
|
@@ -4868,6 +4958,350 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
4868
4958
|
});
|
|
4869
4959
|
}
|
|
4870
4960
|
|
|
4961
|
+
// src/canvas/export-svg.ts
|
|
4962
|
+
var ARROWHEAD_LENGTH2 = 12;
|
|
4963
|
+
var ARROWHEAD_ANGLE2 = Math.PI / 6;
|
|
4964
|
+
var ARROW_LABEL_FONT_SIZE2 = 14;
|
|
4965
|
+
function esc(s) {
|
|
4966
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4967
|
+
}
|
|
4968
|
+
var n = (v) => Number.isFinite(v) ? `${Math.round(v * 1e3) / 1e3}` : "0";
|
|
4969
|
+
function elementCenter(el) {
|
|
4970
|
+
const b = getElementBounds(el);
|
|
4971
|
+
if (!b) return null;
|
|
4972
|
+
return { x: b.x + b.w / 2, y: b.y + b.h / 2 };
|
|
4973
|
+
}
|
|
4974
|
+
function withRotationSvg(el, fragment) {
|
|
4975
|
+
const angle = el.rotation ?? 0;
|
|
4976
|
+
if (!angle || !fragment) return fragment;
|
|
4977
|
+
const c = elementCenter(el);
|
|
4978
|
+
if (!c) return fragment;
|
|
4979
|
+
const deg = angle * 180 / Math.PI;
|
|
4980
|
+
return `<g transform="rotate(${n(deg)} ${n(c.x)} ${n(c.y)})">${fragment}</g>`;
|
|
4981
|
+
}
|
|
4982
|
+
var WIDTH_QUANTUM2 = 0.25;
|
|
4983
|
+
function emitStroke(stroke) {
|
|
4984
|
+
if (stroke.points.length < 2) return "";
|
|
4985
|
+
const data = getStrokeRenderData(stroke);
|
|
4986
|
+
const { x: ox, y: oy } = stroke.position;
|
|
4987
|
+
const byWidth = /* @__PURE__ */ new Map();
|
|
4988
|
+
for (let i = 0; i < data.segments.length; i++) {
|
|
4989
|
+
const seg = data.segments[i];
|
|
4990
|
+
const w = data.widths[i];
|
|
4991
|
+
if (!seg || w === void 0) continue;
|
|
4992
|
+
const q = Math.max(WIDTH_QUANTUM2, Math.round(w / WIDTH_QUANTUM2) * WIDTH_QUANTUM2);
|
|
4993
|
+
let parts = byWidth.get(q);
|
|
4994
|
+
if (!parts) {
|
|
4995
|
+
parts = [];
|
|
4996
|
+
byWidth.set(q, parts);
|
|
4997
|
+
}
|
|
4998
|
+
parts.push(
|
|
4999
|
+
`M${n(ox + seg.start.x)} ${n(oy + seg.start.y)} C${n(ox + seg.cp1.x)} ${n(oy + seg.cp1.y)} ${n(ox + seg.cp2.x)} ${n(oy + seg.cp2.y)} ${n(ox + seg.end.x)} ${n(oy + seg.end.y)}`
|
|
5000
|
+
);
|
|
5001
|
+
}
|
|
5002
|
+
const blend = stroke.blendMode === "multiply" ? ' style="mix-blend-mode:multiply"' : "";
|
|
5003
|
+
let out = "";
|
|
5004
|
+
for (const [width, parts] of byWidth) {
|
|
5005
|
+
out += `<path d="${parts.join(" ")}" fill="none" stroke="${esc(stroke.color)}" stroke-width="${n(width)}" stroke-linecap="round" stroke-linejoin="round" opacity="${n(stroke.opacity)}"${blend} />`;
|
|
5006
|
+
}
|
|
5007
|
+
return out;
|
|
5008
|
+
}
|
|
5009
|
+
function emitShape(shape) {
|
|
5010
|
+
const { x, y } = shape.position;
|
|
5011
|
+
const { w, h } = shape.size;
|
|
5012
|
+
const fill = shape.fillColor !== "none" && shape.shape !== "line" ? esc(shape.fillColor) : "none";
|
|
5013
|
+
const stroke = shape.strokeWidth > 0 ? esc(shape.strokeColor) : "none";
|
|
5014
|
+
const sw = shape.strokeWidth > 0 ? ` stroke-width="${n(shape.strokeWidth)}"` : "";
|
|
5015
|
+
switch (shape.shape) {
|
|
5016
|
+
case "rectangle":
|
|
5017
|
+
return `<rect x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" fill="${fill}" stroke="${stroke}"${sw} />`;
|
|
5018
|
+
case "ellipse":
|
|
5019
|
+
return `<ellipse cx="${n(x + w / 2)}" cy="${n(y + h / 2)}" rx="${n(w / 2)}" ry="${n(h / 2)}" fill="${fill}" stroke="${stroke}"${sw} />`;
|
|
5020
|
+
case "line": {
|
|
5021
|
+
const [a, b] = lineEndpoints(shape);
|
|
5022
|
+
return `<line x1="${n(a.x)}" y1="${n(a.y)}" x2="${n(b.x)}" y2="${n(b.y)}" stroke="${stroke}"${sw} stroke-linecap="round" />`;
|
|
5023
|
+
}
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
function emitArrow(arrow, store) {
|
|
5027
|
+
const geometry = getArrowRenderGeometry(arrow);
|
|
5028
|
+
const { visualFrom: from, visualTo: to } = getVisualEndpoints(arrow, geometry, store);
|
|
5029
|
+
let d;
|
|
5030
|
+
if (arrow.bend !== 0) {
|
|
5031
|
+
const cp = geometry.controlPoint ?? getArrowControlPoint(from, to, arrow.bend);
|
|
5032
|
+
d = `M${n(from.x)} ${n(from.y)} Q${n(cp.x)} ${n(cp.y)} ${n(to.x)} ${n(to.y)}`;
|
|
5033
|
+
} else {
|
|
5034
|
+
d = `M${n(from.x)} ${n(from.y)} L${n(to.x)} ${n(to.y)}`;
|
|
5035
|
+
}
|
|
5036
|
+
const dash = arrow.fromBinding || arrow.toBinding ? ' stroke-dasharray="8 4"' : "";
|
|
5037
|
+
let out = `<path d="${d}" fill="none" stroke="${esc(arrow.color)}" stroke-width="${n(arrow.width)}" stroke-linecap="round"${dash} />`;
|
|
5038
|
+
const angle = geometry.tangentEnd;
|
|
5039
|
+
const p1x = to.x - ARROWHEAD_LENGTH2 * Math.cos(angle - ARROWHEAD_ANGLE2);
|
|
5040
|
+
const p1y = to.y - ARROWHEAD_LENGTH2 * Math.sin(angle - ARROWHEAD_ANGLE2);
|
|
5041
|
+
const p2x = to.x - ARROWHEAD_LENGTH2 * Math.cos(angle + ARROWHEAD_ANGLE2);
|
|
5042
|
+
const p2y = to.y - ARROWHEAD_LENGTH2 * Math.sin(angle + ARROWHEAD_ANGLE2);
|
|
5043
|
+
out += `<polygon points="${n(to.x)},${n(to.y)} ${n(p1x)},${n(p1y)} ${n(p2x)},${n(p2y)}" fill="${esc(arrow.color)}" />`;
|
|
5044
|
+
if (arrow.label && arrow.label.length > 0) {
|
|
5045
|
+
const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
|
|
5046
|
+
const approxW = arrow.label.length * ARROW_LABEL_FONT_SIZE2 * 0.6;
|
|
5047
|
+
const padX = 6;
|
|
5048
|
+
const padY = 4;
|
|
5049
|
+
const lw = approxW + padX * 2;
|
|
5050
|
+
const lh = ARROW_LABEL_FONT_SIZE2 + padY * 2;
|
|
5051
|
+
out += `<rect x="${n(mid.x - lw / 2)}" y="${n(mid.y - lh / 2)}" width="${n(lw)}" height="${n(lh)}" rx="4" fill="rgba(255,255,255,0.9)" />`;
|
|
5052
|
+
out += `<text x="${n(mid.x)}" y="${n(mid.y)}" font-family="system-ui, sans-serif" font-size="${ARROW_LABEL_FONT_SIZE2}" fill="#1a1a1a" text-anchor="middle" dominant-baseline="central">${esc(arrow.label)}</text>`;
|
|
5053
|
+
}
|
|
5054
|
+
return out;
|
|
5055
|
+
}
|
|
5056
|
+
function emitImage(image, dataUri) {
|
|
5057
|
+
const href = dataUri ?? image.src;
|
|
5058
|
+
if (!href) return "";
|
|
5059
|
+
const { x, y } = image.position;
|
|
5060
|
+
const { w, h } = image.size;
|
|
5061
|
+
return `<image href="${esc(href)}" x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" />`;
|
|
5062
|
+
}
|
|
5063
|
+
function emitText(text) {
|
|
5064
|
+
if (!text.text) return "";
|
|
5065
|
+
const pad = 2;
|
|
5066
|
+
let anchor = "start";
|
|
5067
|
+
let textX = text.position.x + pad;
|
|
5068
|
+
if (text.textAlign === "center") {
|
|
5069
|
+
anchor = "middle";
|
|
5070
|
+
textX = text.position.x + text.size.w / 2;
|
|
5071
|
+
} else if (text.textAlign === "right") {
|
|
5072
|
+
anchor = "end";
|
|
5073
|
+
textX = text.position.x + text.size.w - pad;
|
|
5074
|
+
}
|
|
5075
|
+
const lineHeight = text.fontSize * 1.4;
|
|
5076
|
+
const lines = text.text.split("\n");
|
|
5077
|
+
let out = "";
|
|
5078
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5079
|
+
const line = lines[i];
|
|
5080
|
+
if (line === void 0) continue;
|
|
5081
|
+
const y = text.position.y + pad + i * lineHeight;
|
|
5082
|
+
out += `<text x="${n(textX)}" y="${n(y)}" font-family="system-ui, sans-serif" font-size="${n(text.fontSize)}" fill="${esc(text.color)}" text-anchor="${anchor}" dominant-baseline="text-before-edge">${esc(line)}</text>`;
|
|
5083
|
+
}
|
|
5084
|
+
return out;
|
|
5085
|
+
}
|
|
5086
|
+
function emitNote(note, rasterScale) {
|
|
5087
|
+
const { x, y } = note.position;
|
|
5088
|
+
const { w, h } = note.size;
|
|
5089
|
+
if (typeof document === "undefined") return emitNotePlaceholder(note);
|
|
5090
|
+
const canvas = document.createElement("canvas");
|
|
5091
|
+
canvas.width = Math.max(1, Math.ceil(w * rasterScale));
|
|
5092
|
+
canvas.height = Math.max(1, Math.ceil(h * rasterScale));
|
|
5093
|
+
const ctx = canvas.getContext("2d");
|
|
5094
|
+
if (!ctx) return emitNotePlaceholder(note);
|
|
5095
|
+
ctx.scale(rasterScale, rasterScale);
|
|
5096
|
+
ctx.translate(-x, -y);
|
|
5097
|
+
renderNoteOnCanvas(ctx, note);
|
|
5098
|
+
let dataUri;
|
|
5099
|
+
try {
|
|
5100
|
+
dataUri = canvas.toDataURL();
|
|
5101
|
+
} catch {
|
|
5102
|
+
return emitNotePlaceholder(note);
|
|
5103
|
+
}
|
|
5104
|
+
if (!dataUri || !dataUri.startsWith("data:")) return emitNotePlaceholder(note);
|
|
5105
|
+
return `<image href="${esc(dataUri)}" x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" />`;
|
|
5106
|
+
}
|
|
5107
|
+
function emitNotePlaceholder(note) {
|
|
5108
|
+
const { x, y } = note.position;
|
|
5109
|
+
const { w, h } = note.size;
|
|
5110
|
+
return `<rect x="${n(x)}" y="${n(y)}" width="${n(w)}" height="${n(h)}" rx="4" fill="${esc(note.backgroundColor)}" />`;
|
|
5111
|
+
}
|
|
5112
|
+
function emitGrid(grid, bounds) {
|
|
5113
|
+
if (grid.cellSize <= 0) return "";
|
|
5114
|
+
const vb = {
|
|
5115
|
+
minX: bounds.x,
|
|
5116
|
+
minY: bounds.y,
|
|
5117
|
+
maxX: bounds.x + bounds.w,
|
|
5118
|
+
maxY: bounds.y + bounds.h
|
|
5119
|
+
};
|
|
5120
|
+
const stroke = esc(grid.strokeColor);
|
|
5121
|
+
const sw = n(grid.strokeWidth);
|
|
5122
|
+
const op = n(grid.opacity);
|
|
5123
|
+
if (grid.gridType === "hex") {
|
|
5124
|
+
const centers = getHexCenters(vb, grid.cellSize, grid.hexOrientation);
|
|
5125
|
+
let d2 = "";
|
|
5126
|
+
for (const c of centers) {
|
|
5127
|
+
const verts = getHexVertices(c.x, c.y, grid.cellSize, grid.hexOrientation);
|
|
5128
|
+
const first = verts[0];
|
|
5129
|
+
if (!first) continue;
|
|
5130
|
+
d2 += `M${n(first.x)} ${n(first.y)}`;
|
|
5131
|
+
for (let i = 1; i < verts.length; i++) {
|
|
5132
|
+
const v = verts[i];
|
|
5133
|
+
if (v) d2 += `L${n(v.x)} ${n(v.y)}`;
|
|
5134
|
+
}
|
|
5135
|
+
d2 += "Z";
|
|
5136
|
+
}
|
|
5137
|
+
return `<path d="${d2}" fill="none" stroke="${stroke}" stroke-width="${sw}" opacity="${op}" />`;
|
|
5138
|
+
}
|
|
5139
|
+
const { verticals, horizontals } = getSquareGridLines(vb, grid.cellSize);
|
|
5140
|
+
let d = "";
|
|
5141
|
+
for (const gx of verticals) d += `M${n(gx)} ${n(vb.minY)}L${n(gx)} ${n(vb.maxY)}`;
|
|
5142
|
+
for (const gy of horizontals) d += `M${n(vb.minX)} ${n(gy)}L${n(vb.maxX)} ${n(gy)}`;
|
|
5143
|
+
return `<path d="${d}" fill="none" stroke="${stroke}" stroke-width="${sw}" opacity="${op}" />`;
|
|
5144
|
+
}
|
|
5145
|
+
function emitTemplate(template, grid) {
|
|
5146
|
+
if (grid && grid.gridType === "hex") {
|
|
5147
|
+
return emitHexTemplate(template, grid);
|
|
5148
|
+
}
|
|
5149
|
+
return emitGeometricTemplate(template);
|
|
5150
|
+
}
|
|
5151
|
+
function emitGeometricTemplate(t) {
|
|
5152
|
+
const { x: cx, y: cy } = t.position;
|
|
5153
|
+
const r = t.radius;
|
|
5154
|
+
const fill = esc(t.fillColor);
|
|
5155
|
+
const stroke = esc(t.strokeColor);
|
|
5156
|
+
const sw = n(t.strokeWidth);
|
|
5157
|
+
const op = n(t.opacity);
|
|
5158
|
+
const attrs = `fill="${fill}" stroke="${stroke}" stroke-width="${sw}" opacity="${op}"`;
|
|
5159
|
+
switch (t.templateShape) {
|
|
5160
|
+
case "circle":
|
|
5161
|
+
return `<circle cx="${n(cx)}" cy="${n(cy)}" r="${n(r)}" ${attrs} />`;
|
|
5162
|
+
case "square":
|
|
5163
|
+
return `<rect x="${n(cx - r / 2)}" y="${n(cy - r / 2)}" width="${n(r)}" height="${n(r)}" ${attrs} />`;
|
|
5164
|
+
case "cone": {
|
|
5165
|
+
const halfAngle = Math.atan(0.5);
|
|
5166
|
+
const a0 = t.angle - halfAngle;
|
|
5167
|
+
const a1 = t.angle + halfAngle;
|
|
5168
|
+
const p0x = cx + r * Math.cos(a0);
|
|
5169
|
+
const p0y = cy + r * Math.sin(a0);
|
|
5170
|
+
const p1x = cx + r * Math.cos(a1);
|
|
5171
|
+
const p1y = cy + r * Math.sin(a1);
|
|
5172
|
+
const large = a1 - a0 > Math.PI ? 1 : 0;
|
|
5173
|
+
return `<path d="M${n(cx)} ${n(cy)} L${n(p0x)} ${n(p0y)} A${n(r)} ${n(r)} 0 ${large} 1 ${n(p1x)} ${n(p1y)} Z" ${attrs} />`;
|
|
5174
|
+
}
|
|
5175
|
+
case "line": {
|
|
5176
|
+
const halfW = r / 12;
|
|
5177
|
+
const cos = Math.cos(t.angle);
|
|
5178
|
+
const sin = Math.sin(t.angle);
|
|
5179
|
+
const perpX = -sin * halfW;
|
|
5180
|
+
const perpY = cos * halfW;
|
|
5181
|
+
const pts = [
|
|
5182
|
+
[cx + perpX, cy + perpY],
|
|
5183
|
+
[cx + r * cos + perpX, cy + r * sin + perpY],
|
|
5184
|
+
[cx + r * cos - perpX, cy + r * sin - perpY],
|
|
5185
|
+
[cx - perpX, cy - perpY]
|
|
5186
|
+
].map(([px, py]) => `${n(px ?? 0)},${n(py ?? 0)}`).join(" ");
|
|
5187
|
+
return `<polygon points="${pts}" ${attrs} />`;
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
function emitHexTemplate(t, grid) {
|
|
5192
|
+
const cellSize = grid.cellSize;
|
|
5193
|
+
const orientation = grid.hexOrientation;
|
|
5194
|
+
const snapUnit = Math.sqrt(3) * cellSize;
|
|
5195
|
+
const radiusCells = t.radius / snapUnit;
|
|
5196
|
+
const center2 = t.position;
|
|
5197
|
+
let cells;
|
|
5198
|
+
switch (t.templateShape) {
|
|
5199
|
+
case "circle":
|
|
5200
|
+
cells = getHexCellsInRadius(center2, radiusCells, cellSize, orientation);
|
|
5201
|
+
break;
|
|
5202
|
+
case "cone":
|
|
5203
|
+
cells = getHexCellsInCone(center2, t.angle, radiusCells, cellSize, orientation);
|
|
5204
|
+
break;
|
|
5205
|
+
case "line":
|
|
5206
|
+
cells = getHexCellsInLine(center2, t.angle, radiusCells, cellSize, orientation);
|
|
5207
|
+
break;
|
|
5208
|
+
case "square":
|
|
5209
|
+
cells = getHexCellsInSquare(center2, radiusCells, cellSize, orientation);
|
|
5210
|
+
break;
|
|
5211
|
+
}
|
|
5212
|
+
let d = "";
|
|
5213
|
+
for (const cell of cells) {
|
|
5214
|
+
const verts = getHexVertices(cell.x, cell.y, cellSize, orientation);
|
|
5215
|
+
const first = verts[0];
|
|
5216
|
+
if (!first) continue;
|
|
5217
|
+
d += `M${n(first.x)} ${n(first.y)}`;
|
|
5218
|
+
for (let i = 1; i < verts.length; i++) {
|
|
5219
|
+
const v = verts[i];
|
|
5220
|
+
if (v) d += `L${n(v.x)} ${n(v.y)}`;
|
|
5221
|
+
}
|
|
5222
|
+
d += "Z";
|
|
5223
|
+
}
|
|
5224
|
+
return `<path d="${d}" fill="${esc(t.fillColor)}" stroke="${esc(t.strokeColor)}" stroke-width="${n(t.strokeWidth)}" opacity="${n(t.opacity)}" />`;
|
|
5225
|
+
}
|
|
5226
|
+
async function exportSvg(store, options = {}, layerManager) {
|
|
5227
|
+
const padding = options.padding ?? 0;
|
|
5228
|
+
const rasterScale = options.rasterScale ?? 2;
|
|
5229
|
+
const filter = options.filter;
|
|
5230
|
+
const allElements = store.getAll();
|
|
5231
|
+
let visibleElements = layerManager ? allElements.filter((el) => layerManager.isLayerVisible(el.layerId)) : allElements;
|
|
5232
|
+
if (filter) visibleElements = visibleElements.filter(filter);
|
|
5233
|
+
const bounds = computeBounds(visibleElements, padding);
|
|
5234
|
+
if (!bounds) {
|
|
5235
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0" viewBox="0 0 0 0"></svg>`;
|
|
5236
|
+
}
|
|
5237
|
+
const remoteImages = visibleElements.filter(
|
|
5238
|
+
(el) => el.type === "image" && !el.src.startsWith("data:")
|
|
5239
|
+
);
|
|
5240
|
+
const imageCache = await loadImages(remoteImages);
|
|
5241
|
+
const imageDataUris = encodeImages(visibleElements, imageCache, rasterScale);
|
|
5242
|
+
const grids = visibleElements.filter((el) => el.type === "grid");
|
|
5243
|
+
const firstGrid = grids[0];
|
|
5244
|
+
let body = "";
|
|
5245
|
+
if (options.background) {
|
|
5246
|
+
body += `<rect x="${n(bounds.x)}" y="${n(bounds.y)}" width="${n(bounds.w)}" height="${n(bounds.h)}" fill="${esc(options.background)}" />`;
|
|
5247
|
+
}
|
|
5248
|
+
for (const el of visibleElements) {
|
|
5249
|
+
body += emitElement(el, imageDataUris, rasterScale, firstGrid, store);
|
|
5250
|
+
}
|
|
5251
|
+
for (const grid of grids) {
|
|
5252
|
+
body += emitGrid(grid, bounds);
|
|
5253
|
+
}
|
|
5254
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${n(bounds.w)}" height="${n(bounds.h)}" viewBox="${n(bounds.x)} ${n(bounds.y)} ${n(bounds.w)} ${n(bounds.h)}">${body}</svg>`;
|
|
5255
|
+
}
|
|
5256
|
+
function emitElement(el, imageDataUris, rasterScale, firstGrid, store) {
|
|
5257
|
+
switch (el.type) {
|
|
5258
|
+
case "stroke":
|
|
5259
|
+
return withRotationSvg(el, emitStroke(el));
|
|
5260
|
+
case "shape":
|
|
5261
|
+
return withRotationSvg(el, emitShape(el));
|
|
5262
|
+
case "arrow":
|
|
5263
|
+
return emitArrow(el, store);
|
|
5264
|
+
case "image":
|
|
5265
|
+
return withRotationSvg(el, emitImage(el, imageDataUris.get(el.id)));
|
|
5266
|
+
case "text":
|
|
5267
|
+
return withRotationSvg(el, emitText(el));
|
|
5268
|
+
case "note":
|
|
5269
|
+
return withRotationSvg(el, emitNote(el, rasterScale));
|
|
5270
|
+
case "template":
|
|
5271
|
+
return emitTemplate(el, firstGrid);
|
|
5272
|
+
case "grid":
|
|
5273
|
+
return "";
|
|
5274
|
+
case "html":
|
|
5275
|
+
return "";
|
|
5276
|
+
default:
|
|
5277
|
+
return "";
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
function encodeImages(elements, imageCache, rasterScale) {
|
|
5281
|
+
const out = /* @__PURE__ */ new Map();
|
|
5282
|
+
for (const el of elements) {
|
|
5283
|
+
if (el.type !== "image") continue;
|
|
5284
|
+
if (el.src.startsWith("data:")) {
|
|
5285
|
+
out.set(el.id, el.src);
|
|
5286
|
+
continue;
|
|
5287
|
+
}
|
|
5288
|
+
const img = imageCache.get(el.id);
|
|
5289
|
+
if (!img || typeof document === "undefined") continue;
|
|
5290
|
+
const canvas = document.createElement("canvas");
|
|
5291
|
+
canvas.width = Math.max(1, Math.ceil(el.size.w * rasterScale));
|
|
5292
|
+
canvas.height = Math.max(1, Math.ceil(el.size.h * rasterScale));
|
|
5293
|
+
const ctx = canvas.getContext("2d");
|
|
5294
|
+
if (!ctx) continue;
|
|
5295
|
+
try {
|
|
5296
|
+
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
5297
|
+
const uri = canvas.toDataURL();
|
|
5298
|
+
if (uri.startsWith("data:")) out.set(el.id, uri);
|
|
5299
|
+
} catch {
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
return out;
|
|
5303
|
+
}
|
|
5304
|
+
|
|
4871
5305
|
// src/layers/layer-manager.ts
|
|
4872
5306
|
var LayerManager = class {
|
|
4873
5307
|
constructor(store) {
|
|
@@ -6028,13 +6462,13 @@ var SelectionOps = class {
|
|
|
6028
6462
|
if (!first || !last) return;
|
|
6029
6463
|
const c0 = center2(first.bounds);
|
|
6030
6464
|
const cN = center2(last.bounds);
|
|
6031
|
-
const
|
|
6465
|
+
const n2 = sorted.length;
|
|
6032
6466
|
this.deps.recorder.begin();
|
|
6033
6467
|
const moved = [];
|
|
6034
|
-
for (let i = 1; i <
|
|
6468
|
+
for (let i = 1; i < n2 - 1; i++) {
|
|
6035
6469
|
const item = sorted[i];
|
|
6036
6470
|
if (!item || !this.isMovable(item.el)) continue;
|
|
6037
|
-
const target = c0 + i * (cN - c0) / (
|
|
6471
|
+
const target = c0 + i * (cN - c0) / (n2 - 1);
|
|
6038
6472
|
const delta = target - center2(item.bounds);
|
|
6039
6473
|
if (delta === 0) continue;
|
|
6040
6474
|
const [dx, dy] = axis === "horizontal" ? [delta, 0] : [0, delta];
|
|
@@ -6372,7 +6806,10 @@ var Viewport = class {
|
|
|
6372
6806
|
this.getSelectTool()?.selectAtPoint(world, this.toolContext);
|
|
6373
6807
|
this.openContextMenu(screenPos);
|
|
6374
6808
|
},
|
|
6375
|
-
shortcuts: options.shortcuts
|
|
6809
|
+
shortcuts: options.shortcuts,
|
|
6810
|
+
addImage: (src, world) => this.addImage(src, world),
|
|
6811
|
+
getCenteredWorld: () => this.centeredPosition({ w: 300, h: 200 }),
|
|
6812
|
+
onPaste: options.onPaste
|
|
6376
6813
|
});
|
|
6377
6814
|
if (options.contextMenu !== false) {
|
|
6378
6815
|
this.contextMenu = new ContextMenu({
|
|
@@ -6512,6 +6949,7 @@ var Viewport = class {
|
|
|
6512
6949
|
gridController;
|
|
6513
6950
|
interactions;
|
|
6514
6951
|
contextMenu = null;
|
|
6952
|
+
htmlRenderers = /* @__PURE__ */ new Map();
|
|
6515
6953
|
get ctx() {
|
|
6516
6954
|
return this.canvasEl.getContext("2d");
|
|
6517
6955
|
}
|
|
@@ -6553,6 +6991,9 @@ var Viewport = class {
|
|
|
6553
6991
|
async exportImage(options) {
|
|
6554
6992
|
return exportImage(this.store, options, this.layerManager);
|
|
6555
6993
|
}
|
|
6994
|
+
async exportSVG(options) {
|
|
6995
|
+
return exportSvg(this.store, options, this.layerManager);
|
|
6996
|
+
}
|
|
6556
6997
|
loadState(state) {
|
|
6557
6998
|
this.inputHandler.flushPendingHistory();
|
|
6558
6999
|
this.historyRecorder.pause();
|
|
@@ -6566,19 +7007,24 @@ var Viewport = class {
|
|
|
6566
7007
|
this.layerManager.setActiveLayer(state.activeLayerId);
|
|
6567
7008
|
}
|
|
6568
7009
|
this.domNodeManager.reattachHtmlContent(this.store);
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
7010
|
+
for (const el of this.store.getElementsByType("html")) {
|
|
7011
|
+
if (this.domNodeManager.hasContent(el.id)) continue;
|
|
7012
|
+
const factory = el.htmlType ? this.htmlRenderers.get(el.htmlType) : void 0;
|
|
7013
|
+
const rebuilt = factory ? factory(el) : null;
|
|
7014
|
+
if (rebuilt) {
|
|
7015
|
+
this.domNodeManager.storeHtmlContent(el.id, rebuilt);
|
|
7016
|
+
this.domNodeManager.syncDomNode(el);
|
|
7017
|
+
}
|
|
7018
|
+
if (this.onHtmlElementMount) {
|
|
7019
|
+
if (!rebuilt) this.domNodeManager.syncDomNode(el);
|
|
7020
|
+
const node = this.domNodeManager.getNode(el.id);
|
|
7021
|
+
if (node) {
|
|
7022
|
+
this.onHtmlElementMount(el.id, el.domId, node);
|
|
7023
|
+
node.dataset["initialized"] = "true";
|
|
7024
|
+
Object.assign(node.style, {
|
|
7025
|
+
overflow: "hidden",
|
|
7026
|
+
pointerEvents: el.interactive ? "auto" : "none"
|
|
7027
|
+
});
|
|
6582
7028
|
}
|
|
6583
7029
|
}
|
|
6584
7030
|
}
|
|
@@ -6624,12 +7070,14 @@ var Viewport = class {
|
|
|
6624
7070
|
this.requestRender();
|
|
6625
7071
|
return image.id;
|
|
6626
7072
|
}
|
|
6627
|
-
addHtmlElement(dom, position, size = { w: 200, h: 150 }) {
|
|
7073
|
+
addHtmlElement(dom, position, size = { w: 200, h: 150 }, opts) {
|
|
6628
7074
|
const domId = dom.id || void 0;
|
|
6629
7075
|
const el = createHtmlElement({
|
|
6630
7076
|
position,
|
|
6631
7077
|
size,
|
|
6632
7078
|
domId,
|
|
7079
|
+
htmlType: opts?.htmlType,
|
|
7080
|
+
data: opts?.data,
|
|
6633
7081
|
layerId: this.layerManager.activeLayerId
|
|
6634
7082
|
});
|
|
6635
7083
|
this.domNodeManager.storeHtmlContent(el.id, dom);
|
|
@@ -6670,6 +7118,9 @@ var Viewport = class {
|
|
|
6670
7118
|
this.layerManager.removeLayer(id);
|
|
6671
7119
|
this.historyRecorder.commit();
|
|
6672
7120
|
}
|
|
7121
|
+
registerHtmlRenderer(htmlType, factory) {
|
|
7122
|
+
this.htmlRenderers.set(htmlType, factory);
|
|
7123
|
+
}
|
|
6673
7124
|
updateHtmlElement(id, newContent) {
|
|
6674
7125
|
const el = this.store.getById(id);
|
|
6675
7126
|
if (!el) throw new Error(`Element not found: ${id}`);
|
|
@@ -9142,7 +9593,7 @@ var TemplateTool = class {
|
|
|
9142
9593
|
};
|
|
9143
9594
|
|
|
9144
9595
|
// src/index.ts
|
|
9145
|
-
var VERSION = "0.
|
|
9596
|
+
var VERSION = "0.39.0";
|
|
9146
9597
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9147
9598
|
0 && (module.exports = {
|
|
9148
9599
|
ArrowTool,
|
|
@@ -9177,6 +9628,7 @@ var VERSION = "0.38.7";
|
|
|
9177
9628
|
createText,
|
|
9178
9629
|
drawHexPath,
|
|
9179
9630
|
exportImage,
|
|
9631
|
+
exportSvg,
|
|
9180
9632
|
getActiveFormats,
|
|
9181
9633
|
getArrowBounds,
|
|
9182
9634
|
getArrowControlPoint,
|