@weasel-js/diagram 1.9.2 → 1.9.4

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.js CHANGED
@@ -1,11 +1,73 @@
1
- import { layered, portOf, outlinePath, diagramNodeOf, forceRelaxation, center, force, tree, portsOf, sizeToBody, measureBody, buildBody } from './chunk-UBM2J3HJ.js';
2
- export { COMPASS, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, force, forceRelaxation, forestOf, graphOrder, isPinned, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, pinnedSet, portOf, portsOf, portsOnOutline, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree } from './chunk-UBM2J3HJ.js';
3
- import { dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, reconcileSpecs, SceneCanvas, fitViewToBounds, AUTO_POSE_DESCRIPTOR, pointAlongPath, translatePoseViaDescriptor, polylineFromPoints, registerNodeShape, effectivePose, usePoseRun, RECT_POSE_DESCRIPTOR, composeAffordanceLayer } from '@weasel-js/core';
1
+ import { insetOf, layered, portsOf, portOf, outlinePath, diagramNodeOf, expandGroupEdges, forceRelaxation, center, force, tree, DIAGRAM_TRAIT_KEY, DEFAULT_GROUP_INSET, sizeToBody, measureBody, buildBody } from './chunk-EDSBULQM.js';
2
+ export { COMPASS, DEFAULT_GROUP_INSET, DEFAULT_NODE_GAP, DEFAULT_PORTS, DEFAULT_RANK_GAP, DIAGRAM_TRAIT_KEY, axesFor, backEdges, bodyOutline, bodyTrait, boxForContent, buildBody, canvasMeasure, contentBox, createDiagramNodes, dataKeyReader, diagramNodeOf, expandGroupEdges, force, forceRelaxation, forestOf, graphOrder, insetOf, isPinned, layered, layoutBody, layoutRowPorts, measureBody, outlinePath, outlinePolyline, packAcross, packClusters, pinnedSet, portOf, portsOf, portsOnOutline, rankGaps, ranksOf, rayHit, seededOrder, settle, sizeToBody, translated, tree, untilSettled } from './chunk-EDSBULQM.js';
3
+ import { dragPanContribution, WeaselProvider, useScene, useSelection, useLatest, reconcileSpecs, SceneCanvas, fitViewToBounds, AUTO_POSE_DESCRIPTOR, translatePoseViaDescriptor, pointAlongPath, polylineFromPoints, registerNodeShape, effectivePose, usePoseRun, RECT_POSE_DESCRIPTOR, commandFill, commandStroke, composeAffordanceLayer } from '@weasel-js/core';
4
4
  import { portCurvePoints } from '@weasel-js/geom';
5
5
  import { AUTO_POSE_DESCRIPTOR as AUTO_POSE_DESCRIPTOR$1 } from '@weasel-js/core/math';
6
6
  import { forwardRef, useEffect, useMemo, useRef, useState, useImperativeHandle, useCallback } from 'react';
7
7
  import { jsx } from 'react/jsx-runtime';
8
8
 
9
+ function traitOf(node) {
10
+ const data = node.data;
11
+ if (data === null || typeof data !== "object") return null;
12
+ const trait = data[DIAGRAM_TRAIT_KEY];
13
+ return trait !== null && typeof trait === "object" ? trait : null;
14
+ }
15
+ function diagramGroupOf(node) {
16
+ const group = traitOf(node)?.group;
17
+ return group !== null && typeof group === "object" ? group : null;
18
+ }
19
+ function diagramAnchorOf(node) {
20
+ const anchor = traitOf(node)?.anchor;
21
+ return anchor !== null && typeof anchor === "object" && "at" in anchor ? anchor : null;
22
+ }
23
+ function groupDerivePose(opts = {}) {
24
+ const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
25
+ return (node, deps) => {
26
+ const group = diagramGroupOf(node);
27
+ if (group === null) return null;
28
+ let x0 = Infinity;
29
+ let y0 = Infinity;
30
+ let x1 = -Infinity;
31
+ let y1 = -Infinity;
32
+ for (const dep of deps) {
33
+ if (dep === void 0) continue;
34
+ const b = geometry.getBounds(dep.pose);
35
+ x0 = Math.min(x0, b.x);
36
+ y0 = Math.min(y0, b.y);
37
+ x1 = Math.max(x1, b.x + b.width);
38
+ y1 = Math.max(y1, b.y + b.height);
39
+ }
40
+ if (!Number.isFinite(x0)) return null;
41
+ const inset = insetOf(group.inset);
42
+ return geometry.fromBounds(
43
+ {
44
+ x: x0 - inset.left,
45
+ y: y0 - inset.top,
46
+ width: x1 - x0 + inset.left + inset.right,
47
+ height: y1 - y0 + inset.top + inset.bottom
48
+ },
49
+ node.pose
50
+ );
51
+ };
52
+ }
53
+ function anchorDerivePose(opts = {}) {
54
+ const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR;
55
+ return (node, deps) => {
56
+ const anchor = diagramAnchorOf(node);
57
+ const dep = deps[0];
58
+ if (anchor === null || dep === void 0) return null;
59
+ const target = geometry.getBounds(dep.pose);
60
+ const own = geometry.getBounds(node.pose);
61
+ const self = anchor.self ?? anchor.at;
62
+ const x = target.x + anchor.at.u * target.width + (anchor.offset?.x ?? 0) - self.u * own.width;
63
+ const y = target.y + anchor.at.v * target.height + (anchor.offset?.y ?? 0) - self.v * own.height;
64
+ return translatePoseViaDescriptor(node.pose, x - own.x, y - own.y, geometry);
65
+ };
66
+ }
67
+ var DIAGRAM_GROUP = "diagram:group";
68
+ var DIAGRAM_ANCHOR = "diagram:anchor";
69
+ var GROUP_DERIVE_POSE = groupDerivePose();
70
+ var ANCHOR_DERIVE_POSE = anchorDerivePose();
9
71
  var NAMED = { start: 0, mid: 0.5, end: 1 };
10
72
  function diagramLabelOf(node) {
11
73
  const data = node.data;
@@ -41,6 +103,23 @@ function labelDerivePose(opts = {}) {
41
103
  var DIAGRAM_LABEL = "diagram:label";
42
104
  var LABEL_DERIVE_POSE = labelDerivePose();
43
105
 
106
+ // src/selfLoop.ts
107
+ var dot = (a, b) => a.x * b.x + a.y * b.y;
108
+ function selfLoop(ports, standoff) {
109
+ const facing = ports.filter((p) => p.normal !== null);
110
+ const most = (dir, among) => among.reduce(
111
+ (best, p) => best === void 0 || dot(p.normal, dir) > dot(best.normal, dir) ? p : best,
112
+ void 0
113
+ );
114
+ const from = most({ x: 1, y: 0 }, facing);
115
+ if (from === void 0) return null;
116
+ const to = most({ x: 0, y: -1 }, facing.filter((p) => Math.abs(dot(p.normal, from.normal)) < 0.5));
117
+ if (to === void 0) return null;
118
+ const out = { x: from.point.x + from.normal.x * standoff, y: from.point.y + from.normal.y * standoff };
119
+ const reach = dot({ x: to.point.x - out.x, y: to.point.y - out.y }, to.normal) + standoff;
120
+ return { from, to, via: { x: out.x + to.normal.x * reach, y: out.y + to.normal.y * reach } };
121
+ }
122
+
44
123
  // src/edge.ts
45
124
  var straight = (req) => [req.from.point, ...req.waypoints, req.to.point];
46
125
  var orthogonal = (req) => {
@@ -61,12 +140,15 @@ var orthogonal = (req) => {
61
140
  out.push(b);
62
141
  continue;
63
142
  }
143
+ const prev = out.length > 1 ? out[out.length - 2] : null;
64
144
  if (leaveHorizontal) {
65
145
  const mid = (a.x + b.x) / 2;
66
- out.push({ x: mid, y: a.y }, { x: mid, y: b.y }, b);
146
+ if (prev !== null && (mid - a.x) * (a.x - prev.x) < 0) out.push({ x: a.x, y: b.y }, b);
147
+ else out.push({ x: mid, y: a.y }, { x: mid, y: b.y }, b);
67
148
  } else {
68
149
  const mid = (a.y + b.y) / 2;
69
- out.push({ x: a.x, y: mid }, { x: b.x, y: mid }, b);
150
+ if (prev !== null && (mid - a.y) * (a.y - prev.y) < 0) out.push({ x: b.x, y: a.y }, b);
151
+ else out.push({ x: a.x, y: mid }, { x: b.x, y: mid }, b);
70
152
  }
71
153
  }
72
154
  return out;
@@ -136,17 +218,24 @@ function edgeDerivePath(opts = {}) {
136
218
  const b = geometry.getBounds(d.pose);
137
219
  return { x: b.x + b.width / 2, y: b.y + b.height / 2 };
138
220
  };
221
+ const standoffOf = (d) => {
222
+ const b = geometry.getBounds(d.pose);
223
+ return Math.min(b.width, b.height) / 2;
224
+ };
139
225
  return (node, deps) => {
140
226
  const edge = diagramEdgeOf(node);
141
227
  if (edge === null) return null;
142
228
  const [fromDep, toDep] = deps;
143
229
  if (fromDep === void 0 || toDep === void 0) return null;
144
- const from = resolveEnd(edge.from, fromDep, centerOf(toDep), portOpts, allPorts);
145
- const to = resolveEnd(edge.to, toDep, centerOf(fromDep), portOpts, allPorts);
230
+ const loop = fromDep.node.id === toDep.node.id && edge.from.port === void 0 && edge.to.port === void 0 ? selfLoop(allPorts(fromDep), opts.selfLoopStandoff ?? standoffOf(fromDep)) : null;
231
+ const from = loop?.from ?? resolveEnd(edge.from, fromDep, centerOf(toDep), portOpts, allPorts);
232
+ const to = loop?.to ?? resolveEnd(edge.to, toDep, centerOf(fromDep), portOpts, allPorts);
146
233
  if (from === void 0 || to === void 0) return null;
234
+ const authored = edge.waypoints ?? [];
235
+ const waypoints = loop !== null && authored.length === 0 ? [loop.via] : authored;
147
236
  const routers = opts.routers ?? ROUTERS;
148
237
  const router = edge.router !== void 0 && routers[edge.router] || straight;
149
- const points = router({ from, to, waypoints: edge.waypoints ?? [] });
238
+ const points = router({ from, to, waypoints });
150
239
  return points.length < 2 ? null : polylineFromPoints(points);
151
240
  };
152
241
  }
@@ -161,6 +250,8 @@ function withDiagramRegistry(registry = {}) {
161
250
  },
162
251
  derivePose: {
163
252
  [DIAGRAM_LABEL]: LABEL_DERIVE_POSE,
253
+ [DIAGRAM_GROUP]: GROUP_DERIVE_POSE,
254
+ [DIAGRAM_ANCHOR]: ANCHOR_DERIVE_POSE,
164
255
  ...registry.derivePose
165
256
  }
166
257
  };
@@ -398,11 +489,64 @@ function diagramPorts(opts) {
398
489
  contribution: createDiagramContribution(opts)
399
490
  };
400
491
  }
492
+
493
+ // src/groupTree.ts
494
+ function nestGroups(declared, isNode) {
495
+ const groupIds = new Set(declared.map((g) => g.id));
496
+ const parentOf = /* @__PURE__ */ new Map();
497
+ const claimedNodes = /* @__PURE__ */ new Set();
498
+ const direct = /* @__PURE__ */ new Map();
499
+ const isAncestorOrSelf = (candidate, of) => {
500
+ for (let at = of; at !== void 0; at = parentOf.get(at)) {
501
+ if (at === candidate) return true;
502
+ }
503
+ return false;
504
+ };
505
+ for (const group of declared) {
506
+ const own = [];
507
+ for (const id of group.members) {
508
+ if (isNode(id)) {
509
+ if (claimedNodes.has(id)) continue;
510
+ claimedNodes.add(id);
511
+ own.push(id);
512
+ } else if (groupIds.has(id) && !parentOf.has(id) && !isAncestorOrSelf(id, group.id)) {
513
+ parentOf.set(id, group.id);
514
+ own.push(id);
515
+ }
516
+ }
517
+ direct.set(group.id, own);
518
+ }
519
+ const flat = /* @__PURE__ */ new Map();
520
+ const flatten = (id) => {
521
+ const known = flat.get(id);
522
+ if (known !== void 0) return known;
523
+ const out = [];
524
+ for (const member of direct.get(id)) {
525
+ if (groupIds.has(member)) out.push(...flatten(member));
526
+ else out.push(member);
527
+ }
528
+ flat.set(id, out);
529
+ return out;
530
+ };
531
+ const live = new Set(declared.filter((g) => flatten(g.id).length > 0).map((g) => g.id));
532
+ return declared.filter((g) => live.has(g.id)).map((g) => {
533
+ const parent = parentOf.get(g.id);
534
+ return {
535
+ id: g.id,
536
+ direct: direct.get(g.id).filter((id) => !groupIds.has(id) || live.has(id)),
537
+ members: flat.get(g.id),
538
+ ...parent !== void 0 ? { parent } : {}
539
+ };
540
+ });
541
+ }
542
+
543
+ // src/graph.ts
401
544
  function buildGraph(source, opts = {}) {
402
545
  const geometry = opts.geometry ?? AUTO_POSE_DESCRIPTOR$1;
403
546
  const nodes = [];
404
547
  const byId = /* @__PURE__ */ new Map();
405
548
  const candidates = [];
549
+ const groupNodes = [];
406
550
  for (const { node, pose } of source()) {
407
551
  if (diagramEdgeOf(node) !== null) {
408
552
  const deps = node.dependsOn;
@@ -413,6 +557,15 @@ function buildGraph(source, opts = {}) {
413
557
  }
414
558
  const trait = diagramNodeOf(node, opts.read);
415
559
  if (trait === null) continue;
560
+ if (trait.group !== void 0) {
561
+ const deps = node.dependsOn;
562
+ groupNodes.push({
563
+ id: node.id,
564
+ members: Array.isArray(deps) ? deps : [],
565
+ inset: insetOf(trait.group.inset)
566
+ });
567
+ continue;
568
+ }
416
569
  const entry = {
417
570
  id: node.id,
418
571
  bounds: geometry.getBounds(pose),
@@ -421,7 +574,21 @@ function buildGraph(source, opts = {}) {
421
574
  nodes.push(entry);
422
575
  byId.set(entry.id, entry);
423
576
  }
424
- const edges = candidates.filter((e) => byId.has(e.from) && byId.has(e.to));
577
+ const insets = new Map(groupNodes.map((g) => [g.id, g.inset]));
578
+ const live = nestGroups(groupNodes, (id) => byId.has(id)).map((g) => {
579
+ for (const id of g.direct) {
580
+ const node = byId.get(id);
581
+ if (node !== void 0) node.group = g.id;
582
+ }
583
+ return {
584
+ id: g.id,
585
+ members: g.members,
586
+ inset: insets.get(g.id),
587
+ ...g.parent !== void 0 ? { parent: g.parent } : {}
588
+ };
589
+ });
590
+ const known = (id) => byId.has(id) || live.some((g) => g.id === id);
591
+ const edges = expandGroupEdges(candidates.filter((e) => known(e.from) && known(e.to)), live);
425
592
  const out = /* @__PURE__ */ new Map();
426
593
  const into = /* @__PURE__ */ new Map();
427
594
  for (const edge of edges) {
@@ -432,6 +599,7 @@ function buildGraph(source, opts = {}) {
432
599
  return {
433
600
  nodes,
434
601
  edges,
602
+ groups: live,
435
603
  node: (id) => byId.get(id),
436
604
  outgoing: (id) => out.get(id) ?? NONE,
437
605
  incoming: (id) => into.get(id) ?? NONE
@@ -443,17 +611,106 @@ function push(index, key, edge) {
443
611
  else list.push(edge);
444
612
  }
445
613
 
614
+ // src/dataGroups.ts
615
+ function resolveGroups(groups, ids, nodeIds, opts) {
616
+ for (const group of groups) {
617
+ if (ids.has(group.id)) throw new Error(`diagramScene: group id "${group.id}" is already a node or group id`);
618
+ ids.add(group.id);
619
+ }
620
+ const declared = new Map(groups.map((g) => [g.id, g]));
621
+ const nested = nestGroups(groups, (id) => nodeIds.has(id));
622
+ const depthOf = /* @__PURE__ */ new Map();
623
+ const byId = new Map(nested.map((g) => [g.id, g]));
624
+ const depth = (id) => {
625
+ const parent = byId.get(id).parent;
626
+ let d = depthOf.get(id);
627
+ if (d === void 0) depthOf.set(id, d = parent === void 0 ? 0 : depth(parent) + 1);
628
+ return d;
629
+ };
630
+ return nested.map((g, i) => ({ g, i })).sort((a, b) => depth(a.g.id) - depth(b.g.id) || a.i - b.i).map(({ g }) => {
631
+ const group = declared.get(g.id);
632
+ const pad = group.padding ?? opts.padding;
633
+ const title = group.label ? opts.title(group.label) : null;
634
+ const inset = { top: pad + (title ? title.height + pad / 2 : 0), right: pad, bottom: pad, left: pad };
635
+ return {
636
+ group,
637
+ direct: g.direct,
638
+ members: g.members,
639
+ inset,
640
+ pad,
641
+ title,
642
+ ...g.parent !== void 0 ? { parent: g.parent } : {}
643
+ };
644
+ });
645
+ }
646
+ function groupSpecs(groups, placedAt, styleOf) {
647
+ const byId = new Map(groups.map((g) => [g.group.id, g]));
648
+ const boxes = /* @__PURE__ */ new Map();
649
+ const boxOf = (id) => {
650
+ const known = boxes.get(id);
651
+ if (known !== void 0) return known;
652
+ const g = byId.get(id);
653
+ const rs = g.direct.map((m) => byId.has(m) ? boxOf(m) : placedAt.get(m));
654
+ const x = Math.min(...rs.map((r) => r.x)) - g.inset.left;
655
+ const y = Math.min(...rs.map((r) => r.y)) - g.inset.top;
656
+ const box = {
657
+ x,
658
+ y,
659
+ width: Math.max(...rs.map((r) => r.x + r.width)) + g.inset.right - x,
660
+ height: Math.max(...rs.map((r) => r.y + r.height)) + g.inset.bottom - y
661
+ };
662
+ boxes.set(id, box);
663
+ return box;
664
+ };
665
+ const specs = [];
666
+ for (const g of groups) {
667
+ const { style, text } = styleOf(g.group);
668
+ const { x, y, width, height } = boxOf(g.group.id);
669
+ specs.push({
670
+ id: g.group.id,
671
+ kind: "leaf",
672
+ layer: "main",
673
+ pose: { x, y, width, height },
674
+ data: {
675
+ diagram: { outline: "rect", group: { inset: g.inset } },
676
+ fill: { color: style.fill },
677
+ stroke: {
678
+ paint: { color: style.stroke },
679
+ width: style.strokeWidth ?? 1,
680
+ ...style.dash ? { dash: style.dash } : {}
681
+ }
682
+ },
683
+ dependsOn: g.direct,
684
+ derivePose: GROUP_DERIVE_POSE
685
+ });
686
+ if (g.title === null || !g.group.label) continue;
687
+ const anchor = { at: { u: 0, v: 0 }, offset: { x: g.pad, y: g.pad } };
688
+ specs.push({
689
+ id: `${g.group.id}/label`,
690
+ kind: "leaf",
691
+ layer: "main",
692
+ pickable: false,
693
+ pose: { x: x + g.pad, y: y + g.pad, width: g.title.width, height: g.title.height },
694
+ data: { diagram: { anchor }, text: g.group.label, style: text, fill: { color: style.text } },
695
+ dependsOn: [g.group.id],
696
+ derivePose: ANCHOR_DERIVE_POSE
697
+ });
698
+ }
699
+ return specs;
700
+ }
701
+
446
702
  // src/fromData.ts
447
703
  var FONT = { fontFamily: "sans-serif", fontSize: 12 };
448
704
  var NODE = { fill: "#16222c", stroke: "#7ba7c7", text: "#dbe7f2" };
449
705
  var EDGE = { stroke: "#7ba7c7" };
706
+ var GROUP = { fill: "rgba(123, 167, 199, 0.08)", stroke: "#7ba7c7", text: "#9fbdd6", strokeWidth: 1, dash: [4, 3] };
450
707
  var LABEL_PLACEMENT = { at: "mid", offset: 9 };
451
708
  var LINE = 1.4;
452
709
  var GLYPH = 0.6;
453
710
  var MIN_WIDTH = 64;
454
711
  var LAYOUTS = { layered, tree, force };
455
712
  var edgeIdOf = (e) => `edge:${e.from}${e.fromPort ? `.${e.fromPort}` : ""}->${e.to}${e.toPort ? `.${e.toPort}` : ""}${e.label ? `:${e.label}` : ""}`;
456
- function graphOf(nodes, edges) {
713
+ function graphOf(nodes, edges, groups = []) {
457
714
  const byId = new Map(nodes.map((n) => [n.id, n]));
458
715
  const out = /* @__PURE__ */ new Map();
459
716
  const inc = /* @__PURE__ */ new Map();
@@ -464,6 +721,7 @@ function graphOf(nodes, edges) {
464
721
  return {
465
722
  nodes,
466
723
  edges,
724
+ groups,
467
725
  node: (id) => byId.get(id),
468
726
  outgoing: (id) => out.get(id) ?? [],
469
727
  incoming: (id) => inc.get(id) ?? []
@@ -485,6 +743,12 @@ function diagramScene(data, opts = {}) {
485
743
  if (ids.has(n.id)) throw new Error(`diagramScene: node id "${n.id}" appears more than once`);
486
744
  ids.add(n.id);
487
745
  }
746
+ const nodeIds = new Set(ids);
747
+ const titleStyle = { ...font, fontSize: font.fontSize - 1 };
748
+ const groups = resolveGroups(data.groups ?? [], ids, nodeIds, {
749
+ padding: opts.groupPadding ?? DEFAULT_GROUP_INSET,
750
+ title: (text) => measure(text, titleStyle)
751
+ });
488
752
  const seen = /* @__PURE__ */ new Set();
489
753
  const edges = data.edges.filter((e) => {
490
754
  const id = edgeIdOf(e);
@@ -507,13 +771,39 @@ function diagramScene(data, opts = {}) {
507
771
  );
508
772
  return { id: n.id, bounds: pose, pinned: n.pinned === true };
509
773
  });
510
- const graph = graphOf(seeds, edges.map((e) => ({ id: edgeIdOf(e), from: e.from, to: e.to })));
774
+ const graphGroups = groups.map((g) => ({
775
+ id: g.group.id,
776
+ members: g.members,
777
+ inset: g.inset,
778
+ ...g.parent !== void 0 ? { parent: g.parent } : {}
779
+ }));
780
+ const seedOf = new Map(seeds.map((n) => [n.id, n]));
781
+ for (const g of groups) {
782
+ for (const m of g.direct) {
783
+ const seed = seedOf.get(m);
784
+ if (seed !== void 0) seed.group = g.group.id;
785
+ }
786
+ }
787
+ const graph = graphOf(
788
+ seeds,
789
+ expandGroupEdges(edges.map((e) => ({ id: edgeIdOf(e), from: e.from, to: e.to })), graphGroups),
790
+ graphGroups
791
+ );
511
792
  const layout = typeof opts.layout === "function" ? opts.layout : opts.layout === "none" ? null : LAYOUTS[opts.layout ?? "layered"];
512
793
  const moved = layout === null ? /* @__PURE__ */ new Map() : layout(graph, { order: "barycenter", ...opts.layoutOptions });
513
794
  const specs = [];
514
- for (const [i, n] of data.nodes.entries()) {
795
+ const placedAt = new Map(data.nodes.map((n, i) => {
515
796
  const seed = seeds[i].bounds;
516
797
  const at = moved.get(n.id) ?? seed;
798
+ return [n.id, { x: at.x, y: at.y, width: seed.width, height: seed.height }];
799
+ }));
800
+ specs.push(...groupSpecs(groups, placedAt, (g) => ({
801
+ style: opts.groupStyle?.(g) ?? GROUP,
802
+ text: styleOf(titleStyle)
803
+ })));
804
+ for (const [i, n] of data.nodes.entries()) {
805
+ const seed = seeds[i].bounds;
806
+ const at = placedAt.get(n.id);
517
807
  const style = opts.nodeStyle?.(n) ?? NODE;
518
808
  const textData = (text, s) => ({ text, style: styleOf(s), fill: { color: style.text } });
519
809
  const built = buildBody(
@@ -829,11 +1119,14 @@ function diagramShape(opts = {}) {
829
1119
  const outline = outlineOf(node);
830
1120
  if (outline === void 0) return [];
831
1121
  const d = node.data;
832
- const fill = d.fill === void 0 ? opts.defaultFill : d.fill;
833
- const stroke = d.stroke === void 0 ? opts.defaultStroke : d.stroke;
1122
+ const box = geometry.getBounds(pose);
1123
+ const declaredFill = d.fill === void 0 ? opts.defaultFill : d.fill;
1124
+ const declaredStroke = d.stroke === void 0 ? opts.defaultStroke : d.stroke;
1125
+ const fill = declaredFill && commandFill(declaredFill, box);
1126
+ const stroke = declaredStroke && commandStroke(declaredStroke, box);
834
1127
  return [{
835
1128
  kind: "path",
836
- path: outlinePath(outline, geometry.getBounds(pose)),
1129
+ path: outlinePath(outline, box),
837
1130
  ...fill ? { fill } : {},
838
1131
  ...stroke ? { stroke } : {}
839
1132
  }];
@@ -874,6 +1167,7 @@ function Inner({
874
1167
  onSelect,
875
1168
  onMove,
876
1169
  onConnect,
1170
+ enableKeybindings,
877
1171
  canConnect,
878
1172
  portOptions,
879
1173
  live: liveOpts,
@@ -972,6 +1266,7 @@ function Inner({
972
1266
  scene,
973
1267
  selection,
974
1268
  features: onMove ? ["view", "pick", "move"] : ["view", "pick"],
1269
+ enableKeybindings: enableKeybindings ?? (onMove !== void 0 || onConnect !== void 0),
975
1270
  ambient,
976
1271
  onClick,
977
1272
  width,
@@ -985,7 +1280,7 @@ function Inner({
985
1280
  );
986
1281
  }
987
1282
  function fitDiagram(specs, size, fit = {}) {
988
- const boxes = specs.filter((s) => s.kind === "container").map((s) => s.pose);
1283
+ const boxes = specs.filter((s) => s.kind === "container" || diagramGroupOf(s) !== null).map((s) => s.pose);
989
1284
  if (boxes.length === 0) return void 0;
990
1285
  const x = Math.min(...boxes.map((b) => b.x));
991
1286
  const y = Math.min(...boxes.map((b) => b.y));
@@ -1003,6 +1298,6 @@ function fitDiagram(specs, size, fit = {}) {
1003
1298
  });
1004
1299
  }
1005
1300
 
1006
- export { CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_EDGE, DIAGRAM_LABEL, DiagramView, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, LABEL_DERIVE_POSE, LAYOUTS2 as LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramEdgeOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
1301
+ export { ANCHOR_DERIVE_POSE, CONNECT_ACTION_ID, DEFAULT_EDGE_STROKE, DIAGRAM_ANCHOR, DIAGRAM_EDGE, DIAGRAM_GROUP, DIAGRAM_LABEL, DiagramView, EASED_LAYOUTS, EDGE_DERIVE_PATH, GRAB_PORT_ACTION_ID, GROUP_DERIVE_POSE, LABEL_DERIVE_POSE, LAYOUTS2 as LAYOUTS, LAYOUT_ACTION_ID, PORT_AFFORDANCE_KIND, PORT_LAYER_ID, ROUTERS, anchorDerivePose, applyLayout, bezier, buildGraph, commitEdgeToScene, connectBinding, createConnectAction, createDiagramContribution, createLayoutAction, createPortAffordance, defaultCanConnect, diagramAnchorOf, diagramEdgeOf, diagramGroupOf, diagramLabelOf, diagramPorts, diagramScene, diagramShape, easedProducer, edgeDerivePath, edgeIdOf, forceProducer, grabPortAction, grabPortBindings, groupDerivePose, labelDerivePose, layoutPoses, orthogonal, portLayer, portScratchOf, registerDiagramShape, resolveEnd, sceneParticipants, selfLoop, straight, useLiveLayout, useMirroredSelection, withDiagramRegistry };
1007
1302
  //# sourceMappingURL=index.js.map
1008
1303
  //# sourceMappingURL=index.js.map