@canvas-harness/core 0.0.2 → 0.0.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/dist/index.cjs +125 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +125 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -227,6 +227,14 @@ type Scene = {
|
|
|
227
227
|
groups: Record<GroupId, Group>;
|
|
228
228
|
camera: CameraState;
|
|
229
229
|
selection: (NodeId | EdgeId)[];
|
|
230
|
+
/**
|
|
231
|
+
* Presentation order for frame-typed nodes. Auto-maintained by the
|
|
232
|
+
* store on `node.add` / `node.remove`; explicitly mutated via
|
|
233
|
+
* `setFrameOrder` (which emits a `frame.reorder` op). Optional for
|
|
234
|
+
* backward compat — missing on older saved scenes is treated as [].
|
|
235
|
+
* See ARCHITECTURE.md §3.7 frames.
|
|
236
|
+
*/
|
|
237
|
+
frameOrder?: NodeId[];
|
|
230
238
|
};
|
|
231
239
|
/**
|
|
232
240
|
* On-the-wire serialized form. Arrays gzip smaller and have predictable iteration order.
|
|
@@ -239,6 +247,7 @@ type SerializedScene = {
|
|
|
239
247
|
groups: Group[];
|
|
240
248
|
camera: CameraState;
|
|
241
249
|
selection: (NodeId | EdgeId)[];
|
|
250
|
+
frameOrder?: NodeId[];
|
|
242
251
|
};
|
|
243
252
|
|
|
244
253
|
/**
|
|
@@ -276,6 +285,10 @@ type Op = {
|
|
|
276
285
|
} | {
|
|
277
286
|
type: 'group.remove';
|
|
278
287
|
group: Group;
|
|
288
|
+
} | {
|
|
289
|
+
type: 'frame.reorder';
|
|
290
|
+
ids: NodeId[];
|
|
291
|
+
prev: NodeId[];
|
|
279
292
|
};
|
|
280
293
|
/**
|
|
281
294
|
* A batch is the atomic unit of mutation (also the unit of undo/redo and sync).
|
|
@@ -1644,6 +1657,26 @@ interface CanvasStore {
|
|
|
1644
1657
|
getEdgeCount(): number;
|
|
1645
1658
|
/** O(1) count. */
|
|
1646
1659
|
getGroupCount(): number;
|
|
1660
|
+
/**
|
|
1661
|
+
* Returns frame nodes (`type === 'frame'`) in presentation order.
|
|
1662
|
+
* Order is auto-maintained on add/remove and can be explicitly
|
|
1663
|
+
* mutated via {@link CanvasStore.setFrameOrder}. Used by present
|
|
1664
|
+
* mode + export to drive slide sequencing.
|
|
1665
|
+
*/
|
|
1666
|
+
getFrames(): Node[];
|
|
1667
|
+
/**
|
|
1668
|
+
* Replace the presentation order of frames. `ids` must be a
|
|
1669
|
+
* permutation of the current frame ids (any unknown ids are
|
|
1670
|
+
* dropped; missing frames are appended to preserve invariants).
|
|
1671
|
+
* Emits a `frame.reorder` op; undoable, syncs over collab.
|
|
1672
|
+
*/
|
|
1673
|
+
setFrameOrder(ids: NodeId[]): void;
|
|
1674
|
+
/**
|
|
1675
|
+
* Geometric containment query: returns all non-frame nodes whose
|
|
1676
|
+
* AABB is fully inside the given frame's AABB. Used to compute
|
|
1677
|
+
* "what's on this slide". Cheap — backed by the spatial index.
|
|
1678
|
+
*/
|
|
1679
|
+
getNodesInFrame(id: NodeId): Node[];
|
|
1647
1680
|
/**
|
|
1648
1681
|
* Spatial query — ids of nodes + edges that intersect a rect or
|
|
1649
1682
|
* contain a point. Backed by a uniform grid for sub-millisecond
|
|
@@ -2055,6 +2088,12 @@ type Renderer = {
|
|
|
2055
2088
|
setBackground(bg: CanvasBackground | undefined): void;
|
|
2056
2089
|
/** Update the selection chrome color. Triggers an interactive repaint. */
|
|
2057
2090
|
setSelectionColor(color: string): void;
|
|
2091
|
+
/**
|
|
2092
|
+
* Toggle frame-node paint. Use during a presentation flow to drop
|
|
2093
|
+
* the slide border + label so only the frame contents are visible.
|
|
2094
|
+
* Triggers a static repaint.
|
|
2095
|
+
*/
|
|
2096
|
+
setHideFrames(hidden: boolean): void;
|
|
2058
2097
|
/** Per-frame timing (FPS, lastMs, avgMs, frames). */
|
|
2059
2098
|
stats(): FrameStats;
|
|
2060
2099
|
/** Number of items the most recent paint actually drew. */
|
package/dist/index.d.ts
CHANGED
|
@@ -227,6 +227,14 @@ type Scene = {
|
|
|
227
227
|
groups: Record<GroupId, Group>;
|
|
228
228
|
camera: CameraState;
|
|
229
229
|
selection: (NodeId | EdgeId)[];
|
|
230
|
+
/**
|
|
231
|
+
* Presentation order for frame-typed nodes. Auto-maintained by the
|
|
232
|
+
* store on `node.add` / `node.remove`; explicitly mutated via
|
|
233
|
+
* `setFrameOrder` (which emits a `frame.reorder` op). Optional for
|
|
234
|
+
* backward compat — missing on older saved scenes is treated as [].
|
|
235
|
+
* See ARCHITECTURE.md §3.7 frames.
|
|
236
|
+
*/
|
|
237
|
+
frameOrder?: NodeId[];
|
|
230
238
|
};
|
|
231
239
|
/**
|
|
232
240
|
* On-the-wire serialized form. Arrays gzip smaller and have predictable iteration order.
|
|
@@ -239,6 +247,7 @@ type SerializedScene = {
|
|
|
239
247
|
groups: Group[];
|
|
240
248
|
camera: CameraState;
|
|
241
249
|
selection: (NodeId | EdgeId)[];
|
|
250
|
+
frameOrder?: NodeId[];
|
|
242
251
|
};
|
|
243
252
|
|
|
244
253
|
/**
|
|
@@ -276,6 +285,10 @@ type Op = {
|
|
|
276
285
|
} | {
|
|
277
286
|
type: 'group.remove';
|
|
278
287
|
group: Group;
|
|
288
|
+
} | {
|
|
289
|
+
type: 'frame.reorder';
|
|
290
|
+
ids: NodeId[];
|
|
291
|
+
prev: NodeId[];
|
|
279
292
|
};
|
|
280
293
|
/**
|
|
281
294
|
* A batch is the atomic unit of mutation (also the unit of undo/redo and sync).
|
|
@@ -1644,6 +1657,26 @@ interface CanvasStore {
|
|
|
1644
1657
|
getEdgeCount(): number;
|
|
1645
1658
|
/** O(1) count. */
|
|
1646
1659
|
getGroupCount(): number;
|
|
1660
|
+
/**
|
|
1661
|
+
* Returns frame nodes (`type === 'frame'`) in presentation order.
|
|
1662
|
+
* Order is auto-maintained on add/remove and can be explicitly
|
|
1663
|
+
* mutated via {@link CanvasStore.setFrameOrder}. Used by present
|
|
1664
|
+
* mode + export to drive slide sequencing.
|
|
1665
|
+
*/
|
|
1666
|
+
getFrames(): Node[];
|
|
1667
|
+
/**
|
|
1668
|
+
* Replace the presentation order of frames. `ids` must be a
|
|
1669
|
+
* permutation of the current frame ids (any unknown ids are
|
|
1670
|
+
* dropped; missing frames are appended to preserve invariants).
|
|
1671
|
+
* Emits a `frame.reorder` op; undoable, syncs over collab.
|
|
1672
|
+
*/
|
|
1673
|
+
setFrameOrder(ids: NodeId[]): void;
|
|
1674
|
+
/**
|
|
1675
|
+
* Geometric containment query: returns all non-frame nodes whose
|
|
1676
|
+
* AABB is fully inside the given frame's AABB. Used to compute
|
|
1677
|
+
* "what's on this slide". Cheap — backed by the spatial index.
|
|
1678
|
+
*/
|
|
1679
|
+
getNodesInFrame(id: NodeId): Node[];
|
|
1647
1680
|
/**
|
|
1648
1681
|
* Spatial query — ids of nodes + edges that intersect a rect or
|
|
1649
1682
|
* contain a point. Backed by a uniform grid for sub-millisecond
|
|
@@ -2055,6 +2088,12 @@ type Renderer = {
|
|
|
2055
2088
|
setBackground(bg: CanvasBackground | undefined): void;
|
|
2056
2089
|
/** Update the selection chrome color. Triggers an interactive repaint. */
|
|
2057
2090
|
setSelectionColor(color: string): void;
|
|
2091
|
+
/**
|
|
2092
|
+
* Toggle frame-node paint. Use during a presentation flow to drop
|
|
2093
|
+
* the slide border + label so only the frame contents are visible.
|
|
2094
|
+
* Triggers a static repaint.
|
|
2095
|
+
*/
|
|
2096
|
+
setHideFrames(hidden: boolean): void;
|
|
2058
2097
|
/** Per-frame timing (FPS, lastMs, avgMs, frames). */
|
|
2059
2098
|
stats(): FrameStats;
|
|
2060
2099
|
/** Number of items the most recent paint actually drew. */
|
package/dist/index.js
CHANGED
|
@@ -3131,6 +3131,8 @@ var inverseOp = (op) => {
|
|
|
3131
3131
|
return { type: "group.remove", group: op.group };
|
|
3132
3132
|
case "group.remove":
|
|
3133
3133
|
return { type: "group.upsert", group: op.group };
|
|
3134
|
+
case "frame.reorder":
|
|
3135
|
+
return { type: "frame.reorder", ids: op.prev, prev: op.ids };
|
|
3134
3136
|
}
|
|
3135
3137
|
};
|
|
3136
3138
|
var inverseBatch = (batch) => {
|
|
@@ -3198,6 +3200,7 @@ var createCanvasStore = (opts = {}) => {
|
|
|
3198
3200
|
const groupIdsAtom = atom("groupIds", []);
|
|
3199
3201
|
const cameraAtom = atom("camera", initial.camera);
|
|
3200
3202
|
const selectionAtom = atom("selection", initial.selection);
|
|
3203
|
+
const frameOrderAtom = atom("frameOrder", initial.frameOrder ?? []);
|
|
3201
3204
|
const interactionAtom = atom("interaction", idleInteractionState());
|
|
3202
3205
|
const localPresenceAtom = atom("presence", emptyPresenceState(clientId));
|
|
3203
3206
|
const remotePresence = /* @__PURE__ */ new Map();
|
|
@@ -3304,6 +3307,9 @@ var createCanvasStore = (opts = {}) => {
|
|
|
3304
3307
|
nodeAtoms.set(op.node.id, a);
|
|
3305
3308
|
nodeIdsAtom.update((ids) => [...ids, op.node.id]);
|
|
3306
3309
|
reindexNode(op.node);
|
|
3310
|
+
if (op.node.type === "frame") {
|
|
3311
|
+
frameOrderAtom.update((ids) => ids.includes(op.node.id) ? ids : [...ids, op.node.id]);
|
|
3312
|
+
}
|
|
3307
3313
|
break;
|
|
3308
3314
|
}
|
|
3309
3315
|
case "node.update": {
|
|
@@ -3328,6 +3334,9 @@ var createCanvasStore = (opts = {}) => {
|
|
|
3328
3334
|
nodeIdsAtom.update((ids) => ids.filter((x) => x !== id));
|
|
3329
3335
|
unindexNode(id);
|
|
3330
3336
|
incidentEdges.delete(id);
|
|
3337
|
+
if (op.node.type === "frame") {
|
|
3338
|
+
frameOrderAtom.update((ids) => ids.filter((x) => x !== id));
|
|
3339
|
+
}
|
|
3331
3340
|
break;
|
|
3332
3341
|
}
|
|
3333
3342
|
case "edge.add": {
|
|
@@ -3377,6 +3386,10 @@ var createCanvasStore = (opts = {}) => {
|
|
|
3377
3386
|
groupIdsAtom.update((ids) => ids.filter((x) => x !== id));
|
|
3378
3387
|
break;
|
|
3379
3388
|
}
|
|
3389
|
+
case "frame.reorder": {
|
|
3390
|
+
frameOrderAtom.set([...op.ids]);
|
|
3391
|
+
break;
|
|
3392
|
+
}
|
|
3380
3393
|
}
|
|
3381
3394
|
};
|
|
3382
3395
|
const enqueueOp = (op) => {
|
|
@@ -3399,6 +3412,7 @@ var createCanvasStore = (opts = {}) => {
|
|
|
3399
3412
|
return prev;
|
|
3400
3413
|
};
|
|
3401
3414
|
const populateInitial = (scene) => {
|
|
3415
|
+
const seededFrameOrder = [];
|
|
3402
3416
|
for (const id of Object.keys(scene.nodes)) {
|
|
3403
3417
|
const node = scene.nodes[id];
|
|
3404
3418
|
if (!node) continue;
|
|
@@ -3406,7 +3420,9 @@ var createCanvasStore = (opts = {}) => {
|
|
|
3406
3420
|
nodeAtoms.set(node.id, a);
|
|
3407
3421
|
nodeIdsAtom.update((ids) => [...ids, node.id]);
|
|
3408
3422
|
reindexNode(node);
|
|
3423
|
+
if (node.type === "frame") seededFrameOrder.push(node.id);
|
|
3409
3424
|
}
|
|
3425
|
+
if (!scene.frameOrder) frameOrderAtom.set(seededFrameOrder);
|
|
3410
3426
|
for (const id of Object.keys(scene.edges)) {
|
|
3411
3427
|
const edge = scene.edges[id];
|
|
3412
3428
|
if (!edge) continue;
|
|
@@ -3708,6 +3724,53 @@ var createCanvasStore = (opts = {}) => {
|
|
|
3708
3724
|
getNodeCount: () => nodeIdsAtom.value.length,
|
|
3709
3725
|
getEdgeCount: () => edgeIdsAtom.value.length,
|
|
3710
3726
|
getGroupCount: () => groupIdsAtom.value.length,
|
|
3727
|
+
getFrames: () => {
|
|
3728
|
+
const out = [];
|
|
3729
|
+
for (const id of frameOrderAtom.value) {
|
|
3730
|
+
const n = nodeAtoms.get(id)?.value;
|
|
3731
|
+
if (n && n.type === "frame") out.push(n);
|
|
3732
|
+
}
|
|
3733
|
+
return out;
|
|
3734
|
+
},
|
|
3735
|
+
setFrameOrder(ids) {
|
|
3736
|
+
const valid = /* @__PURE__ */ new Set();
|
|
3737
|
+
for (const a of nodeAtoms.values()) {
|
|
3738
|
+
if (a.value.type === "frame") valid.add(a.value.id);
|
|
3739
|
+
}
|
|
3740
|
+
const filtered = [];
|
|
3741
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3742
|
+
for (const id of ids) {
|
|
3743
|
+
if (valid.has(id) && !seen.has(id)) {
|
|
3744
|
+
filtered.push(id);
|
|
3745
|
+
seen.add(id);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
for (const id of valid) {
|
|
3749
|
+
if (!seen.has(id)) filtered.push(id);
|
|
3750
|
+
}
|
|
3751
|
+
const prev = [...frameOrderAtom.value];
|
|
3752
|
+
if (filtered.length === prev.length && filtered.every((id, i) => id === prev[i])) {
|
|
3753
|
+
return;
|
|
3754
|
+
}
|
|
3755
|
+
enqueueOp({ type: "frame.reorder", ids: filtered, prev });
|
|
3756
|
+
},
|
|
3757
|
+
getNodesInFrame(id) {
|
|
3758
|
+
const frame = nodeAtoms.get(id)?.value;
|
|
3759
|
+
if (!frame || frame.type !== "frame") return [];
|
|
3760
|
+
const frameAabb = nodeAABB(frame);
|
|
3761
|
+
const candidates = nodeIndex.queryRect(frameAabb);
|
|
3762
|
+
const out = [];
|
|
3763
|
+
for (const cid of candidates) {
|
|
3764
|
+
if (cid === id) continue;
|
|
3765
|
+
const node = nodeAtoms.get(cid)?.value;
|
|
3766
|
+
if (!node || node.type === "frame") continue;
|
|
3767
|
+
const a = nodeAABB(node);
|
|
3768
|
+
if (a.x >= frameAabb.x && a.y >= frameAabb.y && a.x + a.w <= frameAabb.x + frameAabb.w && a.y + a.h <= frameAabb.y + frameAabb.h) {
|
|
3769
|
+
out.push(node);
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
return out;
|
|
3773
|
+
},
|
|
3711
3774
|
getEdgeGeometry(id) {
|
|
3712
3775
|
const edge = edgeAtoms.get(id)?.value;
|
|
3713
3776
|
if (!edge) return void 0;
|
|
@@ -3869,7 +3932,8 @@ var toSerialized = (scene) => ({
|
|
|
3869
3932
|
edges: Object.values(scene.edges),
|
|
3870
3933
|
groups: Object.values(scene.groups),
|
|
3871
3934
|
camera: scene.camera,
|
|
3872
|
-
selection: scene.selection
|
|
3935
|
+
selection: scene.selection,
|
|
3936
|
+
...scene.frameOrder && scene.frameOrder.length > 0 ? { frameOrder: scene.frameOrder } : {}
|
|
3873
3937
|
});
|
|
3874
3938
|
var fromSerialized = (raw) => {
|
|
3875
3939
|
let working = raw;
|
|
@@ -3891,7 +3955,8 @@ var fromSerialized = (raw) => {
|
|
|
3891
3955
|
edges: Object.fromEntries(ser.edges.map((e) => [asEdgeId(e.id), e])),
|
|
3892
3956
|
groups: Object.fromEntries(ser.groups.map((g) => [asGroupId(g.id), g])),
|
|
3893
3957
|
camera: ser.camera,
|
|
3894
|
-
selection: ser.selection
|
|
3958
|
+
selection: ser.selection,
|
|
3959
|
+
...ser.frameOrder ? { frameOrder: ser.frameOrder } : {}
|
|
3895
3960
|
};
|
|
3896
3961
|
};
|
|
3897
3962
|
var storeToJSON = (store) => ({
|
|
@@ -4436,6 +4501,40 @@ var drawMarquee = (ctx, rect, scale, color) => {
|
|
|
4436
4501
|
ctx.restore();
|
|
4437
4502
|
};
|
|
4438
4503
|
|
|
4504
|
+
// src/render/paint-frame.ts
|
|
4505
|
+
var FRAME_BORDER_PX = 1.5;
|
|
4506
|
+
var FRAME_BORDER_COLOR_DEFAULT = "#94a3b8";
|
|
4507
|
+
var FRAME_FILL_DEFAULT = "rgba(148, 163, 184, 0.06)";
|
|
4508
|
+
var FRAME_LABEL_FONT_PX = 12;
|
|
4509
|
+
var FRAME_LABEL_GAP_PX = 6;
|
|
4510
|
+
var FRAME_LABEL_COLOR = "#64748b";
|
|
4511
|
+
var paintFrameNode = (ctx, node, scale, theme) => {
|
|
4512
|
+
if (node.w <= 0 || node.h <= 0) return;
|
|
4513
|
+
const opacity = resolveOpacity(node.style, theme);
|
|
4514
|
+
const needsScope = opacity !== 1;
|
|
4515
|
+
if (needsScope) {
|
|
4516
|
+
ctx.save();
|
|
4517
|
+
ctx.globalAlpha = opacity;
|
|
4518
|
+
}
|
|
4519
|
+
const fill = node.style?.backgroundColor ?? (theme ? theme("frame.background") : void 0) ?? FRAME_FILL_DEFAULT;
|
|
4520
|
+
ctx.fillStyle = fill;
|
|
4521
|
+
ctx.fillRect(0, 0, node.w, node.h);
|
|
4522
|
+
const stroke = resolveColor(node.style, "strokeColor", FRAME_BORDER_COLOR_DEFAULT, theme);
|
|
4523
|
+
ctx.strokeStyle = stroke;
|
|
4524
|
+
ctx.lineWidth = FRAME_BORDER_PX / scale;
|
|
4525
|
+
ctx.setLineDash([]);
|
|
4526
|
+
ctx.strokeRect(0, 0, node.w, node.h);
|
|
4527
|
+
const labelPx = FRAME_LABEL_FONT_PX / scale;
|
|
4528
|
+
const gapPx = FRAME_LABEL_GAP_PX / scale;
|
|
4529
|
+
const label = node.content?.trim() || "Frame";
|
|
4530
|
+
ctx.fillStyle = FRAME_LABEL_COLOR;
|
|
4531
|
+
ctx.textBaseline = "bottom";
|
|
4532
|
+
ctx.textAlign = "left";
|
|
4533
|
+
ctx.font = `500 ${labelPx}px system-ui, -apple-system, sans-serif`;
|
|
4534
|
+
ctx.fillText(label, 0, -gapPx);
|
|
4535
|
+
if (needsScope) ctx.restore();
|
|
4536
|
+
};
|
|
4537
|
+
|
|
4439
4538
|
// src/render/shapes/content-bounds.ts
|
|
4440
4539
|
var SQRT2_INV = 1 / Math.SQRT2;
|
|
4441
4540
|
var contentBounds = (node) => {
|
|
@@ -4509,6 +4608,7 @@ var createRenderer = (opts) => {
|
|
|
4509
4608
|
const interactiveSurface = setupSurface(opts.interactiveCanvas);
|
|
4510
4609
|
let background = opts.background;
|
|
4511
4610
|
let selectionColor = opts.selectionColor ?? DEFAULT_SELECTION_COLOR;
|
|
4611
|
+
let hideFrames = false;
|
|
4512
4612
|
sizeSurface(staticSurface, opts.width, opts.height);
|
|
4513
4613
|
sizeSurface(interactiveSurface, opts.width, opts.height);
|
|
4514
4614
|
let staticDirty = true;
|
|
@@ -4558,7 +4658,18 @@ var createRenderer = (opts) => {
|
|
|
4558
4658
|
const cameraIsMoving = interaction.mode === "panning" || interaction.mode === "zooming";
|
|
4559
4659
|
const movingNodeCount = excludedNodes?.size ?? 0;
|
|
4560
4660
|
const roughEnabled = !cameraIsMoving && movingNodeCount <= ROUGH_MAX_MOVING_NODES && camera.z >= ROUGH_MIN_ZOOM && visible.length <= ROUGH_MAX_NODES;
|
|
4661
|
+
if (!hideFrames) {
|
|
4662
|
+
for (const node of visible) {
|
|
4663
|
+
if (node.type !== "frame") continue;
|
|
4664
|
+
if (excludedNodes?.has(node.id)) continue;
|
|
4665
|
+
drawWithNodeTransform(staticSurface.ctx, node, () => {
|
|
4666
|
+
paintFrameNode(staticSurface.ctx, node, scale, theme);
|
|
4667
|
+
});
|
|
4668
|
+
drawn++;
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4561
4671
|
for (const node of visible) {
|
|
4672
|
+
if (node.type === "frame") continue;
|
|
4562
4673
|
if (excludedNodes?.has(node.id)) continue;
|
|
4563
4674
|
const isEditingThis = editingNodeId === node.id;
|
|
4564
4675
|
if (isDrawablePrimitive(node.type)) {
|
|
@@ -4761,9 +4872,13 @@ var createRenderer = (opts) => {
|
|
|
4761
4872
|
isMoving: true};
|
|
4762
4873
|
const dragRoughEnabled = inDragMap.size <= ROUGH_MAX_MOVING_NODES && camera.z >= ROUGH_MIN_ZOOM;
|
|
4763
4874
|
for (const node of inDragMap.values()) {
|
|
4764
|
-
if (!isDrawablePrimitive(node.type) && node.type !== "text" && node.type !== "image" && node.type !== "icon")
|
|
4875
|
+
if (!isDrawablePrimitive(node.type) && node.type !== "text" && node.type !== "image" && node.type !== "icon" && node.type !== "frame")
|
|
4765
4876
|
continue;
|
|
4766
4877
|
drawWithNodeTransform(ctx, node, () => {
|
|
4878
|
+
if (node.type === "frame") {
|
|
4879
|
+
paintFrameNode(ctx, node, scale, theme);
|
|
4880
|
+
return;
|
|
4881
|
+
}
|
|
4767
4882
|
if (node.type === "image") {
|
|
4768
4883
|
paintImageNode(ctx, node, assetCache, theme);
|
|
4769
4884
|
return;
|
|
@@ -4965,6 +5080,11 @@ var createRenderer = (opts) => {
|
|
|
4965
5080
|
interactiveDirty = true;
|
|
4966
5081
|
loop.requestFrame();
|
|
4967
5082
|
},
|
|
5083
|
+
setHideFrames(hidden) {
|
|
5084
|
+
hideFrames = hidden;
|
|
5085
|
+
staticDirty = true;
|
|
5086
|
+
loop.requestFrame();
|
|
5087
|
+
},
|
|
4968
5088
|
stats: () => loop.stats(),
|
|
4969
5089
|
lastDrawCount: () => lastDrawn,
|
|
4970
5090
|
getOverlaySet: () => [...overlaySet],
|
|
@@ -4995,6 +5115,7 @@ var sceneBounds = (store) => {
|
|
|
4995
5115
|
let maxY = Number.NEGATIVE_INFINITY;
|
|
4996
5116
|
for (const n of nodes) {
|
|
4997
5117
|
if (n.hidden) continue;
|
|
5118
|
+
if (n.type === "frame") continue;
|
|
4998
5119
|
const r = nodeAABB(n);
|
|
4999
5120
|
if (r.x < minX) minX = r.x;
|
|
5000
5121
|
if (r.y < minY) minY = r.y;
|
|
@@ -5025,6 +5146,7 @@ var renderMinimapContent = (ctx, store, mapWidth, mapHeight, opts = {}) => {
|
|
|
5025
5146
|
const defaultColor = opts.defaultNodeColor ?? "#94a3b8";
|
|
5026
5147
|
for (const node of store.getAllNodes()) {
|
|
5027
5148
|
if (node.hidden) continue;
|
|
5149
|
+
if (node.type === "frame") continue;
|
|
5028
5150
|
const r = nodeAABB(node);
|
|
5029
5151
|
const x = offX + (r.x - bx) * scale;
|
|
5030
5152
|
const y = offY + (r.y - by) * scale;
|