data-navigator 2.4.1 → 3.2.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.js CHANGED
@@ -17,6 +17,28 @@ var __spreadValues = (a, b) => {
17
17
  return a;
18
18
  };
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __accessCheck = (obj, member, msg) => {
21
+ if (!member.has(obj))
22
+ throw TypeError("Cannot " + msg);
23
+ };
24
+ var __privateGet = (obj, member, getter) => {
25
+ __accessCheck(obj, member, "read from private field");
26
+ return getter ? getter.call(obj) : member.get(obj);
27
+ };
28
+ var __privateAdd = (obj, member, value) => {
29
+ if (member.has(obj))
30
+ throw TypeError("Cannot add the same private member more than once");
31
+ member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
32
+ };
33
+ var __privateSet = (obj, member, value, setter) => {
34
+ __accessCheck(obj, member, "write to private field");
35
+ setter ? setter.call(obj, value) : member.set(obj, value);
36
+ return value;
37
+ };
38
+ var __privateMethod = (obj, member, method) => {
39
+ __accessCheck(obj, member, "access private method");
40
+ return method;
41
+ };
20
42
  var __async = (__this, __arguments, generator) => {
21
43
  return new Promise((resolve, reject) => {
22
44
  var fulfilled = (value) => {
@@ -55,12 +77,12 @@ var defaultKeyBindings = {
55
77
  ArrowDown: "down",
56
78
  Period: "forward",
57
79
  Comma: "backward",
58
- Escape: "parent",
59
- Enter: "child"
80
+ Escape: "drill-out",
81
+ Enter: "drill-in"
60
82
  };
61
- var TypicallyUnreservedKeys = ["KeyW", "KeyJ", "LeftBracket", "RightBracket", "Slash", "Backslash"];
83
+ var TypicallyUnreservedKeys = ["KeyW", "KeyJ", "BracketLeft", "BracketRight", "Slash", "Backslash"];
62
84
  var TypicallyUnreservedKeyPairs = [
63
- ["LeftBracket", "RightBracket"],
85
+ ["BracketLeft", "BracketRight"],
64
86
  ["Slash", "Backslash"]
65
87
  ];
66
88
  var GenericFullNavigationRules = {
@@ -80,11 +102,11 @@ var GenericFullNavigationRules = {
80
102
  key: "ArrowDown",
81
103
  direction: "target"
82
104
  },
83
- child: {
105
+ "drill-in": {
84
106
  key: "Enter",
85
107
  direction: "target"
86
108
  },
87
- parent: {
109
+ "drill-out": {
88
110
  key: "Backspace",
89
111
  direction: "source"
90
112
  },
@@ -140,11 +162,11 @@ var GenericLimitedNavigationRules = {
140
162
  key: "ArrowUp",
141
163
  direction: "source"
142
164
  },
143
- child: {
165
+ "drill-in": {
144
166
  key: "Enter",
145
167
  direction: "target"
146
168
  },
147
- parent: {
169
+ "drill-out": {
148
170
  key: "Backspace",
149
171
  direction: "source"
150
172
  },
@@ -187,6 +209,47 @@ var NodeElementDefaults = {
187
209
  spatialProperties: void 0
188
210
  }
189
211
  };
212
+ var KeyLabels = {
213
+ ArrowLeft: "\u2190",
214
+ ArrowRight: "\u2192",
215
+ ArrowUp: "\u2191",
216
+ ArrowDown: "\u2193",
217
+ Enter: "Enter",
218
+ Backspace: "Backspace",
219
+ Escape: "Esc",
220
+ Space: "Space",
221
+ Tab: "Tab",
222
+ BracketLeft: "[",
223
+ BracketRight: "]",
224
+ Backslash: "\\",
225
+ Slash: "/",
226
+ Comma: ",",
227
+ Period: ".",
228
+ Semicolon: ";",
229
+ Quote: "'",
230
+ Minus: "-",
231
+ Equal: "="
232
+ };
233
+ var GenericNavigationRuleCommands = {
234
+ "drill-in": { label: "Enter", description: "Drill in" },
235
+ "drill-out": { label: "Backspace", description: "Drill out" },
236
+ exit: { label: "Esc", description: "Exit" },
237
+ left: { label: "\u2190", description: "Previous data point" },
238
+ right: { label: "\u2192", description: "Next data point" },
239
+ up: { label: "\u2191", description: "Previous data point" },
240
+ down: { label: "\u2193", description: "Next data point" },
241
+ backward: { label: ",", description: "Move backward" },
242
+ forward: { label: ".", description: "Move forward" },
243
+ previous: { label: ";", description: "Previous" },
244
+ next: { label: "'", description: "Next" },
245
+ undo: { label: "Z", description: "Undo" },
246
+ help: { label: "Y", description: "Help" },
247
+ legend: { label: "L", description: "Legend" }
248
+ };
249
+ var commandsTableDefaultColumns = [
250
+ { label: "Command", id: "description" },
251
+ { label: "Key", id: "label" }
252
+ ];
190
253
 
191
254
  // src/utilities.ts
192
255
  var describeNode = (d, descriptionOptions) => {
@@ -201,6 +264,422 @@ var describeNode = (d, descriptionOptions) => {
201
264
  var createValidId = (s) => {
202
265
  return "_" + s.replace(/[^a-zA-Z0-9_-]+/g, "_");
203
266
  };
267
+ var keyLabel = (code) => {
268
+ if (!code)
269
+ return "";
270
+ return KeyLabels[code] || code.replace(/^Key/, "").replace(/^Digit/, "");
271
+ };
272
+ var humanizeRule = (navId) => {
273
+ const text = navId.replace(/[-_]+/g, " ").trim();
274
+ return text.charAt(0).toUpperCase() + text.slice(1);
275
+ };
276
+ function getGenericCommandsFromNavRules(navigationRules, descriptions) {
277
+ if (!navigationRules) {
278
+ return [];
279
+ }
280
+ return Object.keys(navigationRules).map((navId) => {
281
+ const rule = navigationRules[navId];
282
+ const generic = GenericNavigationRuleCommands[navId];
283
+ return {
284
+ label: (rule == null ? void 0 : rule.key) ? keyLabel(rule.key) : (generic == null ? void 0 : generic.label) || "",
285
+ description: (descriptions == null ? void 0 : descriptions[navId]) || (generic == null ? void 0 : generic.description) || humanizeRule(navId),
286
+ command: navId
287
+ };
288
+ });
289
+ }
290
+
291
+ // src/edge-utils.ts
292
+ var makeEdgeCreator = (nodes, edges, useDirectedEdges) => {
293
+ const addEdgeToNode = (nodeId, edgeId) => {
294
+ const node = nodes[nodeId];
295
+ if (!node) {
296
+ console.error(`Building edges. Node ${nodeId} does not exist, so edge ${edgeId} was not added to it.`);
297
+ return;
298
+ }
299
+ if (!node.edges) {
300
+ node.edges = [];
301
+ }
302
+ if (node.edges.indexOf(edgeId) === -1) {
303
+ node.edges.push(edgeId);
304
+ }
305
+ };
306
+ return (source, target, rules, addTo) => {
307
+ if (!source || !target || source === target) {
308
+ return;
309
+ }
310
+ const id = `${source}-${target}`;
311
+ const targetId = !useDirectedEdges ? id : `${target}-${id}`;
312
+ const addToSource = !addTo || addTo === "source";
313
+ const addToTarget = !addTo || addTo === "target";
314
+ const checkEdgeRules = (eId) => {
315
+ if (edges[eId]) {
316
+ (rules || []).forEach((r) => {
317
+ if (edges[eId].navigationRules.indexOf(r) === -1) {
318
+ edges[eId].navigationRules.push(r);
319
+ }
320
+ });
321
+ } else {
322
+ edges[eId] = {
323
+ source,
324
+ target,
325
+ navigationRules: rules ? [...rules] : []
326
+ };
327
+ }
328
+ };
329
+ checkEdgeRules(id);
330
+ if (useDirectedEdges && addToTarget) {
331
+ checkEdgeRules(targetId);
332
+ }
333
+ if (addToSource) {
334
+ addEdgeToNode(source, id);
335
+ }
336
+ if (addToTarget) {
337
+ addEdgeToNode(target, targetId);
338
+ }
339
+ };
340
+ };
341
+ var chain = (createEdge, ids, rules, extents = "terminal") => {
342
+ for (let i = 0; i < ids.length - 1; i++) {
343
+ createEdge(ids[i], ids[i + 1], rules);
344
+ }
345
+ if (extents === "circular" && ids.length > 1) {
346
+ createEdge(ids[ids.length - 1], ids[0], rules);
347
+ }
348
+ };
349
+
350
+ // src/frame.ts
351
+ var prepareFrameOptions = (options) => {
352
+ var _a, _b, _c;
353
+ const f = options.frame;
354
+ const dimCount = ((_b = (_a = options.dimensions) == null ? void 0 : _a.values) == null ? void 0 : _b.length) || 0;
355
+ if (!f || dimCount < 2 || ((_c = options.dimensions.parentOptions) == null ? void 0 : _c.addLevel0)) {
356
+ return options;
357
+ }
358
+ const data = f.data || { id: "data", edges: [], semantics: { label: "Data" } };
359
+ return __spreadProps(__spreadValues({}, options), {
360
+ dimensions: __spreadProps(__spreadValues({}, options.dimensions), {
361
+ parentOptions: __spreadProps(__spreadValues({}, options.dimensions.parentOptions || {}), {
362
+ addLevel0: __spreadProps(__spreadValues({}, data), { edges: data.edges || [] })
363
+ })
364
+ })
365
+ });
366
+ };
367
+ var buildFrame = (options, nodes, edges, topId) => {
368
+ var _a, _b, _c;
369
+ const f = options.frame;
370
+ if (!f)
371
+ return;
372
+ if (!topId || !nodes[topId]) {
373
+ console.error("Building frame. No top data node was found to place under the frame.");
374
+ return;
375
+ }
376
+ const createEdge = makeEdgeCreator(nodes, edges, options.useDirectedEdges);
377
+ const siblingRules = ((_a = f.navigationRules) == null ? void 0 : _a.sibling_sibling) || ["left", "right"];
378
+ const parentRules = ((_b = f.navigationRules) == null ? void 0 : _b.parent_child) || ["drill-out", "drill-in"];
379
+ const extents = ((_c = f.behavior) == null ? void 0 : _c.extents) || "circular";
380
+ const root = f.root;
381
+ nodes[root.id] = __spreadProps(__spreadValues({}, root), { edges: [...root.edges || []], depth: 0, frameRole: "root" });
382
+ const elementIds = (f.elements || []).map((e) => {
383
+ nodes[e.id] = __spreadProps(__spreadValues({}, e), {
384
+ edges: [...e.edges || []],
385
+ depth: 1,
386
+ parentId: root.id,
387
+ frameRole: "element"
388
+ });
389
+ return e.id;
390
+ });
391
+ let dataId = topId;
392
+ if (f.data && f.data.id !== topId) {
393
+ nodes[f.data.id] = __spreadProps(__spreadValues({}, f.data), { edges: [...f.data.edges || []] });
394
+ createEdge(f.data.id, topId, parentRules, "source");
395
+ createEdge(f.data.id, topId, parentRules, "target");
396
+ nodes[topId].parentId = f.data.id;
397
+ dataId = f.data.id;
398
+ }
399
+ const dataNode = nodes[dataId];
400
+ dataNode.frameRole = "data";
401
+ dataNode.depth = 1;
402
+ dataNode.parentId = root.id;
403
+ const children = [...elementIds, dataId];
404
+ createEdge(root.id, children[0], parentRules, "source");
405
+ children.forEach((c) => createEdge(root.id, c, parentRules, "target"));
406
+ chain(createEdge, children, siblingRules, extents);
407
+ return { rootId: root.id, elementIds, dataId, parentRules };
408
+ };
409
+
410
+ // src/spatial-grid.ts
411
+ var buildSpatialGrid = (options, nodes, edges) => {
412
+ var _a, _b, _c;
413
+ const g = options.spatialGrid;
414
+ if (!g)
415
+ return;
416
+ const createEdge = makeEdgeCreator(nodes, edges, options.useDirectedEdges);
417
+ const xRules = ((_a = g.navigationRules) == null ? void 0 : _a.x) || ["left", "right"];
418
+ const yRules = ((_b = g.navigationRules) == null ? void 0 : _b.y) || ["up", "down"];
419
+ const parentRules = ((_c = g.navigationRules) == null ? void 0 : _c.parent_child) || ["drill-out", "drill-in"];
420
+ const extents = g.extents || "terminal";
421
+ const across = g.levelNavigation !== "within";
422
+ const skipEmpty = g.skipEmpty !== false;
423
+ const leafMode = g.leaves || "points";
424
+ const idOf = (d) => d[typeof options.idKey === "function" ? options.idKey(d) : options.idKey];
425
+ const rows = options.data.filter((d) => typeof d[g.xKey] === "number" && typeof d[g.yKey] === "number");
426
+ const ext = (key, given) => {
427
+ if (given)
428
+ return given;
429
+ let min = Infinity;
430
+ let max = -Infinity;
431
+ rows.forEach((d) => {
432
+ min = Math.min(min, d[key]);
433
+ max = Math.max(max, d[key]);
434
+ });
435
+ return [min, max];
436
+ };
437
+ const xExtent = ext(g.xKey, g.xExtent);
438
+ const yExtent = ext(g.yKey, g.yExtent);
439
+ const indexOf = (v, [lo, hi], n) => {
440
+ if (hi === lo)
441
+ return 0;
442
+ return Math.max(0, Math.min(n - 1, Math.floor((v - lo) / (hi - lo) * n)));
443
+ };
444
+ const rootDefaults = { id: "grid", edges: [], semantics: { label: "Grid" } };
445
+ const root = __spreadValues(__spreadValues({}, rootDefaults), g.root || {});
446
+ root.edges = root.edges || [];
447
+ root.depth = 0;
448
+ root.data = __spreadProps(__spreadValues({}, root.data || {}), {
449
+ xKey: g.xKey,
450
+ yKey: g.yKey,
451
+ xExtent,
452
+ yExtent,
453
+ levels: g.levels,
454
+ count: rows.length
455
+ });
456
+ nodes[root.id] = root;
457
+ const cellId = (level, col, row) => g.cellIds ? g.cellIds(level, col, row) : createValidId(`${root.id}_L${level}_${col}_${row}`);
458
+ const cellsByLevel = [];
459
+ g.levels.forEach((n, level) => {
460
+ const cells = {};
461
+ const add = (col, row) => {
462
+ const key = `${col}_${row}`;
463
+ if (cells[key])
464
+ return cells[key];
465
+ const id = cellId(level, col, row);
466
+ const xStep = (xExtent[1] - xExtent[0]) / n;
467
+ const yStep = (yExtent[1] - yExtent[0]) / n;
468
+ let parent = root.id;
469
+ if (level > 0) {
470
+ const pn = g.levels[level - 1];
471
+ const parentCell = cellsByLevel[level - 1][`${Math.floor(col * pn / n)}_${Math.floor(row * pn / n)}`];
472
+ parent = parentCell == null ? void 0 : parentCell.id;
473
+ parentCell == null ? void 0 : parentCell.children.push(id);
474
+ }
475
+ cells[key] = { id, col, row, parent, children: [] };
476
+ nodes[id] = {
477
+ id,
478
+ renderId: id,
479
+ edges: [],
480
+ depth: level + 1,
481
+ parentId: parent,
482
+ data: {
483
+ level,
484
+ col,
485
+ row,
486
+ cellsPerAxis: n,
487
+ xExtent: [xExtent[0] + col * xStep, xExtent[0] + (col + 1) * xStep],
488
+ yExtent: [yExtent[0] + row * yStep, yExtent[0] + (row + 1) * yStep],
489
+ count: 0
490
+ }
491
+ };
492
+ return cells[key];
493
+ };
494
+ if (!skipEmpty) {
495
+ for (let c = 0; c < n; c++)
496
+ for (let r = 0; r < n; r++)
497
+ add(c, r);
498
+ }
499
+ rows.forEach((d) => add(indexOf(d[g.xKey], xExtent, n), indexOf(d[g.yKey], yExtent, n)));
500
+ cellsByLevel.push(cells);
501
+ });
502
+ const finest = cellsByLevel[cellsByLevel.length - 1];
503
+ const fn = g.levels[g.levels.length - 1];
504
+ const leafIds = [];
505
+ const leafParent = {};
506
+ rows.forEach((d) => {
507
+ const id = idOf(d);
508
+ if (!nodes[id])
509
+ return;
510
+ g.levels.forEach((n, level) => {
511
+ const c = cellsByLevel[level][`${indexOf(d[g.xKey], xExtent, n)}_${indexOf(d[g.yKey], yExtent, n)}`];
512
+ nodes[c.id].data.count++;
513
+ });
514
+ const cell = finest[`${indexOf(d[g.xKey], xExtent, fn)}_${indexOf(d[g.yKey], yExtent, fn)}`];
515
+ cell.children.push(id);
516
+ leafParent[id] = cell.id;
517
+ leafIds.push(id);
518
+ nodes[id].parentId = cell.id;
519
+ nodes[id].depth = g.levels.length + 1;
520
+ });
521
+ const topLeftFirst = (a, b) => b.row - a.row || a.col - b.col;
522
+ const rootChildren = Object.values(cellsByLevel[0]).sort(topLeftFirst);
523
+ if (rootChildren.length)
524
+ createEdge(root.id, rootChildren[0].id, parentRules, "source");
525
+ rootChildren.forEach((c) => createEdge(root.id, c.id, parentRules, "target"));
526
+ const pos = (id) => nodes[id].data;
527
+ cellsByLevel.forEach((cells, level) => {
528
+ Object.values(cells).forEach((c) => {
529
+ if (!c.children.length)
530
+ return;
531
+ let first;
532
+ if (level === cellsByLevel.length - 1) {
533
+ first = [...c.children].sort((a, b) => leafOrderX(a, b))[0];
534
+ } else {
535
+ first = [...c.children].sort((a, b) => topLeftFirst(pos(a), pos(b)))[0];
536
+ }
537
+ createEdge(c.id, first, parentRules, "source");
538
+ c.children.forEach((k) => createEdge(c.id, k, parentRules, "target"));
539
+ });
540
+ });
541
+ const linkCells = (ids) => {
542
+ const byRow = {};
543
+ const byCol = {};
544
+ ids.forEach((id) => {
545
+ const p = pos(id);
546
+ (byRow[p.row] = byRow[p.row] || []).push(id);
547
+ (byCol[p.col] = byCol[p.col] || []).push(id);
548
+ });
549
+ Object.values(byRow).forEach(
550
+ (r) => chain(
551
+ createEdge,
552
+ r.sort((a, b) => pos(a).col - pos(b).col),
553
+ xRules,
554
+ extents
555
+ )
556
+ );
557
+ Object.values(byCol).forEach(
558
+ (c) => chain(
559
+ createEdge,
560
+ c.sort((a, b) => pos(b).row - pos(a).row),
561
+ yRules,
562
+ extents
563
+ )
564
+ );
565
+ };
566
+ cellsByLevel.forEach((cells, level) => {
567
+ const list = Object.values(cells);
568
+ if (across || level === 0) {
569
+ linkCells(list.map((c) => c.id));
570
+ } else {
571
+ const groups = {};
572
+ list.forEach((c) => (groups[c.parent] = groups[c.parent] || []).push(c.id));
573
+ Object.values(groups).forEach(linkCells);
574
+ }
575
+ });
576
+ function leafOrderX(a, b) {
577
+ const da = nodes[a].data;
578
+ const db = nodes[b].data;
579
+ return da[g.xKey] - db[g.xKey] || db[g.yKey] - da[g.yKey];
580
+ }
581
+ const leafOrderY = (a, b) => {
582
+ const da = nodes[a].data;
583
+ const db = nodes[b].data;
584
+ return db[g.yKey] - da[g.yKey] || da[g.xKey] - db[g.xKey];
585
+ };
586
+ const linkLeaves = (ids) => {
587
+ if (leafMode === "cells") {
588
+ const byY = {};
589
+ const byX = {};
590
+ ids.forEach((id) => {
591
+ const d = nodes[id].data;
592
+ (byY[d[g.yKey]] = byY[d[g.yKey]] || []).push(id);
593
+ (byX[d[g.xKey]] = byX[d[g.xKey]] || []).push(id);
594
+ });
595
+ Object.values(byY).forEach((r) => chain(createEdge, r.sort(leafOrderX), xRules, extents));
596
+ Object.values(byX).forEach((c) => chain(createEdge, c.sort(leafOrderY), yRules, extents));
597
+ } else {
598
+ chain(createEdge, [...ids].sort(leafOrderX), xRules, extents);
599
+ chain(createEdge, [...ids].sort(leafOrderY), yRules, extents);
600
+ }
601
+ };
602
+ if (across) {
603
+ linkLeaves(leafIds);
604
+ } else {
605
+ Object.values(finest).forEach((c) => linkLeaves(c.children));
606
+ }
607
+ return {
608
+ rootId: root.id,
609
+ levels: g.levels,
610
+ cellsByLevel: cellsByLevel.map((cells) => Object.values(cells).map((c) => c.id)),
611
+ leafIds,
612
+ parentRules
613
+ };
614
+ };
615
+
616
+ // src/hierarchy.ts
617
+ var buildHierarchy = (options, nodes, edges) => {
618
+ var _a, _b;
619
+ const h = options.hierarchy;
620
+ if (!h)
621
+ return;
622
+ const createEdge = makeEdgeCreator(nodes, edges, options.useDirectedEdges);
623
+ const siblingRules = ((_a = h.navigationRules) == null ? void 0 : _a.sibling_sibling) || ["left", "right"];
624
+ const parentRules = ((_b = h.navigationRules) == null ? void 0 : _b.parent_child) || ["drill-out", "drill-in"];
625
+ const extents = h.extents || "circular";
626
+ const idOf = (d) => d[typeof options.idKey === "function" ? options.idKey(d) : options.idKey];
627
+ const children = {};
628
+ let rootId;
629
+ options.data.forEach((d) => {
630
+ const id = idOf(d);
631
+ const parent = d[h.parentKey];
632
+ if (parent === void 0 || parent === null || parent === "") {
633
+ if (rootId) {
634
+ console.error(
635
+ `Building hierarchy. More than one root was found: ${rootId} and ${id}. Using ${rootId}.`
636
+ );
637
+ return;
638
+ }
639
+ rootId = id;
640
+ } else {
641
+ if (!children[parent])
642
+ children[parent] = [];
643
+ children[parent].push(d);
644
+ }
645
+ });
646
+ if (!rootId) {
647
+ console.error("Building hierarchy. No root row was found. One row must have an empty parent.");
648
+ return;
649
+ }
650
+ const byDepth = [];
651
+ const visit = (id, parentId, depth) => {
652
+ const node = nodes[id];
653
+ if (!node) {
654
+ console.error(`Building hierarchy. Row ${id} has no node. Check idKey.`);
655
+ return;
656
+ }
657
+ node.depth = depth;
658
+ if (parentId)
659
+ node.parentId = parentId;
660
+ if (!byDepth[depth])
661
+ byDepth[depth] = [];
662
+ byDepth[depth].push(id);
663
+ let kids = children[id] || [];
664
+ if (h.sortFunction)
665
+ kids = [...kids].sort((a, b) => h.sortFunction(a, b));
666
+ const kidIds = kids.map(idOf);
667
+ node.childCount = kidIds.length;
668
+ if (kidIds.length) {
669
+ createEdge(id, kidIds[0], parentRules, "source");
670
+ kidIds.forEach((k) => createEdge(id, k, parentRules, "target"));
671
+ if (h.levelNavigation !== "across") {
672
+ chain(createEdge, kidIds, siblingRules, extents);
673
+ }
674
+ }
675
+ kidIds.forEach((k) => visit(k, id, depth + 1));
676
+ };
677
+ visit(rootId, void 0, 0);
678
+ if (h.levelNavigation === "across") {
679
+ byDepth.forEach((ids) => chain(createEdge, ids, siblingRules, extents));
680
+ }
681
+ return { rootId, depthCount: byDepth.length, parentRules };
682
+ };
204
683
 
205
684
  // src/structure.ts
206
685
  var structure_default = (options) => {
@@ -277,7 +756,7 @@ var buildNodeStructureFromVegaLite = (options) => {
277
756
  edges[firstChildEdge] = {
278
757
  source: node.id,
279
758
  target: firstChildId,
280
- navigationRules: ["parent", "child"]
759
+ navigationRules: ["drill-out", "drill-in"]
281
760
  };
282
761
  }
283
762
  }
@@ -290,7 +769,7 @@ var buildNodeStructureFromVegaLite = (options) => {
290
769
  edges[parentEdge] = {
291
770
  source: parentId,
292
771
  target: node.id,
293
- navigationRules: ["parent", "child"]
772
+ navigationRules: ["drill-out", "drill-in"]
294
773
  };
295
774
  }
296
775
  }
@@ -491,7 +970,7 @@ var scaffoldDimensions = (options, nodes) => {
491
970
  },
492
971
  navigationRules: dim.navigationRules || {
493
972
  sibling_sibling: rules.length ? [...rules.shift()] : ["previous_" + dim.dimensionKey, "next_" + dim.dimensionKey],
494
- parent_child: ["parent_" + dim.dimensionKey, "child"]
973
+ parent_child: ["drill-out_" + dim.dimensionKey, "drill-in"]
495
974
  }
496
975
  };
497
976
  nodes[id2] = {
@@ -678,9 +1157,9 @@ var scaffoldDimensions = (options, nodes) => {
678
1157
  }
679
1158
  return dimensions;
680
1159
  };
681
- var buildEdges = (options, nodes, dimensions) => {
1160
+ var buildEdges = (options, nodes, dimensions, existingEdges) => {
682
1161
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
683
- let edges = {};
1162
+ let edges = existingEdges || {};
684
1163
  const addEdgeToNode = (nodeId, edgeId) => {
685
1164
  if (nodes[nodeId].edges.indexOf(edgeId) === -1) {
686
1165
  nodes[nodeId].edges.push(edgeId);
@@ -721,7 +1200,7 @@ var buildEdges = (options, nodes, dimensions) => {
721
1200
  let po = ((_d = options.dimensions) == null ? void 0 : _d.parentOptions) || {};
722
1201
  let extents = ((_f = (_e = po.level1Options) == null ? void 0 : _e.behavior) == null ? void 0 : _f.extents) || "terminal";
723
1202
  let level0 = po.addLevel0;
724
- let parentRules = level0 ? ((_h = (_g = po.level1Options) == null ? void 0 : _g.navigationRules) == null ? void 0 : _h.parent_child) || ["parent", "child"] : [];
1203
+ let parentRules = level0 ? ((_h = (_g = po.level1Options) == null ? void 0 : _g.navigationRules) == null ? void 0 : _h.parent_child) || ["drill-out", "drill-in"] : [];
725
1204
  let siblingRules = ((_j = (_i = po.level1Options) == null ? void 0 : _i.navigationRules) == null ? void 0 : _j.sibling_sibling) || ["left", "right"];
726
1205
  let firstLevel1Node = typeof order[0] === "string" ? hasOrder ? nodes[order[0]] : nodes[dimensions[order[0]].nodeId] : order[0];
727
1206
  if (level0) {
@@ -754,7 +1233,7 @@ var buildEdges = (options, nodes, dimensions) => {
754
1233
  createEdge(level1Node.id, nextLevel1Node.id, siblingRules);
755
1234
  }
756
1235
  if (!l && extents === "bridgedCustom") {
757
- createEdge(po.level1Options.behavior.customBridgePost, level1Node.id, siblingRules);
1236
+ createEdge(po.level1Options.behavior.customBridgePrevious, level1Node.id, siblingRules);
758
1237
  }
759
1238
  l++;
760
1239
  });
@@ -941,7 +1420,7 @@ var buildEdges = (options, nodes, dimensions) => {
941
1420
  return edges;
942
1421
  };
943
1422
  var buildRules = (options, edges, dimensions) => {
944
- var _a, _b, _c, _d;
1423
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
945
1424
  let rules = options.navigationRules;
946
1425
  if (!rules) {
947
1426
  let dimKeys = Object.keys(dimensions || {});
@@ -1033,8 +1512,8 @@ var buildRules = (options, edges, dimensions) => {
1033
1512
  if (dimKeys.length) {
1034
1513
  if ((_b = (_a = options.dimensions) == null ? void 0 : _a.parentOptions) == null ? void 0 : _b.addLevel0) {
1035
1514
  let rules2 = ((_d = (_c = options.dimensions.parentOptions.level1Options) == null ? void 0 : _c.navigationRules) == null ? void 0 : _d.parent_child) || [
1036
- "parent",
1037
- "child"
1515
+ "drill-out",
1516
+ "drill-in"
1038
1517
  ];
1039
1518
  checkKeys(rules2[0], rules2[1]);
1040
1519
  }
@@ -1045,6 +1524,20 @@ var buildRules = (options, edges, dimensions) => {
1045
1524
  checkKeys(ss[0], ss[1]);
1046
1525
  });
1047
1526
  }
1527
+ const extraPairs = [
1528
+ (_f = (_e = options.frame) == null ? void 0 : _e.navigationRules) == null ? void 0 : _f.parent_child,
1529
+ (_h = (_g = options.frame) == null ? void 0 : _g.navigationRules) == null ? void 0 : _h.sibling_sibling,
1530
+ (_j = (_i = options.spatialGrid) == null ? void 0 : _i.navigationRules) == null ? void 0 : _j.x,
1531
+ (_l = (_k = options.spatialGrid) == null ? void 0 : _k.navigationRules) == null ? void 0 : _l.y,
1532
+ (_n = (_m = options.spatialGrid) == null ? void 0 : _m.navigationRules) == null ? void 0 : _n.parent_child,
1533
+ (_p = (_o = options.hierarchy) == null ? void 0 : _o.navigationRules) == null ? void 0 : _p.parent_child,
1534
+ (_r = (_q = options.hierarchy) == null ? void 0 : _q.navigationRules) == null ? void 0 : _r.sibling_sibling
1535
+ ];
1536
+ extraPairs.forEach((pair) => {
1537
+ if (pair && !used[pair[0]] && !used[pair[1]]) {
1538
+ checkKeys(pair[0], pair[1]);
1539
+ }
1540
+ });
1048
1541
  Object.keys(edges).forEach((e) => {
1049
1542
  edges[e].navigationRules.forEach((rule) => {
1050
1543
  if (!used[rule]) {
@@ -1079,20 +1572,38 @@ var buildRules = (options, edges, dimensions) => {
1079
1572
  }
1080
1573
  return rules;
1081
1574
  };
1082
- var buildStructure = (options) => {
1083
- if (options.addIds) {
1084
- addSimpleDataIDs(options);
1575
+ var buildStructure = (inputOptions) => {
1576
+ var _a, _b, _c;
1577
+ if (inputOptions.addIds) {
1578
+ addSimpleDataIDs(inputOptions);
1085
1579
  }
1580
+ const options = prepareFrameOptions(inputOptions);
1086
1581
  let nodes = buildNodes(options);
1087
1582
  let dimensions = scaffoldDimensions(options, nodes);
1088
- let edges = buildEdges(options, nodes, dimensions);
1583
+ let edges = {};
1584
+ const spatialGrid = buildSpatialGrid(options, nodes, edges);
1585
+ const hierarchy = buildHierarchy(options, nodes, edges);
1586
+ let frame;
1587
+ if (options.frame) {
1588
+ const dimKeys = Object.keys(dimensions || {});
1589
+ const topId = (spatialGrid == null ? void 0 : spatialGrid.rootId) || (hierarchy == null ? void 0 : hierarchy.rootId) || ((_c = (_b = (_a = options.dimensions) == null ? void 0 : _a.parentOptions) == null ? void 0 : _b.addLevel0) == null ? void 0 : _c.id) || (dimKeys.length === 1 ? dimensions[dimKeys[0]].nodeId : void 0);
1590
+ frame = buildFrame(options, nodes, edges, topId);
1591
+ }
1592
+ edges = buildEdges(options, nodes, dimensions, edges);
1089
1593
  let navigationRules = buildRules(options, edges, dimensions);
1090
- return {
1594
+ const structure = {
1091
1595
  nodes,
1092
1596
  edges,
1093
1597
  dimensions,
1094
1598
  navigationRules
1095
1599
  };
1600
+ if (frame)
1601
+ structure.frame = frame;
1602
+ if (spatialGrid)
1603
+ structure.spatialGrid = spatialGrid;
1604
+ if (hierarchy)
1605
+ structure.hierarchy = hierarchy;
1606
+ return structure;
1096
1607
  };
1097
1608
 
1098
1609
  // src/input.ts
@@ -1190,6 +1701,124 @@ var input_default = (options) => {
1190
1701
  return inputHandler;
1191
1702
  };
1192
1703
 
1704
+ // src/commands-table.ts
1705
+ var _commands, _columns, _kbdColumn, _connected, _render, render_fn;
1706
+ var CommandsTable = class extends HTMLElement {
1707
+ constructor() {
1708
+ super(...arguments);
1709
+ __privateAdd(this, _render);
1710
+ __privateAdd(this, _commands, []);
1711
+ __privateAdd(this, _columns, commandsTableDefaultColumns);
1712
+ __privateAdd(this, _kbdColumn, "label");
1713
+ __privateAdd(this, _connected, false);
1714
+ }
1715
+ static get observedAttributes() {
1716
+ return ["title"];
1717
+ }
1718
+ set commands(value) {
1719
+ __privateSet(this, _commands, value);
1720
+ if (!__privateGet(this, _connected))
1721
+ return;
1722
+ __privateMethod(this, _render, render_fn).call(this);
1723
+ }
1724
+ get commands() {
1725
+ return __privateGet(this, _commands);
1726
+ }
1727
+ set columns(value) {
1728
+ __privateSet(this, _columns, value);
1729
+ if (!__privateGet(this, _connected))
1730
+ return;
1731
+ __privateMethod(this, _render, render_fn).call(this);
1732
+ }
1733
+ get columns() {
1734
+ return __privateGet(this, _columns);
1735
+ }
1736
+ /** Column id whose cells are wrapped in <kbd>. Defaults to 'label'. Set to null to disable. */
1737
+ set kbdColumn(value) {
1738
+ __privateSet(this, _kbdColumn, value);
1739
+ if (!__privateGet(this, _connected))
1740
+ return;
1741
+ __privateMethod(this, _render, render_fn).call(this);
1742
+ }
1743
+ get kbdColumn() {
1744
+ return __privateGet(this, _kbdColumn);
1745
+ }
1746
+ attributeChangedCallback() {
1747
+ if (!__privateGet(this, _connected))
1748
+ return;
1749
+ __privateMethod(this, _render, render_fn).call(this);
1750
+ }
1751
+ connectedCallback() {
1752
+ __privateSet(this, _connected, true);
1753
+ __privateMethod(this, _render, render_fn).call(this);
1754
+ }
1755
+ disconnectedCallback() {
1756
+ __privateSet(this, _connected, false);
1757
+ }
1758
+ };
1759
+ _commands = new WeakMap();
1760
+ _columns = new WeakMap();
1761
+ _kbdColumn = new WeakMap();
1762
+ _connected = new WeakMap();
1763
+ _render = new WeakSet();
1764
+ render_fn = function() {
1765
+ this.replaceChildren();
1766
+ const headerCells = [];
1767
+ for (const column of __privateGet(this, _columns)) {
1768
+ const headerCell = document.createElement("th");
1769
+ headerCell.classList.add("dn-commands-table-th");
1770
+ headerCell.textContent = column.label;
1771
+ headerCells.push(headerCell);
1772
+ }
1773
+ const headerRow = document.createElement("tr");
1774
+ headerRow.classList.add("dn-commands-table-thead-tr");
1775
+ for (const headerCell of headerCells) {
1776
+ headerRow.appendChild(headerCell);
1777
+ }
1778
+ const thead = document.createElement("thead");
1779
+ thead.classList.add("dn-commands-table-thead");
1780
+ thead.appendChild(headerRow);
1781
+ const tbody = document.createElement("tbody");
1782
+ tbody.classList.add("dn-commands-table-tbody");
1783
+ for (const command of __privateGet(this, _commands)) {
1784
+ const rowCells = [];
1785
+ for (const column of __privateGet(this, _columns)) {
1786
+ const cellValue = command[column.id];
1787
+ const rowCell = document.createElement("td");
1788
+ rowCell.classList.add("dn-commands-table-td");
1789
+ if (__privateGet(this, _kbdColumn) !== null && column.id === __privateGet(this, _kbdColumn) && cellValue) {
1790
+ const kbd = document.createElement("kbd");
1791
+ kbd.classList.add("dn-commands-table-td-kbd");
1792
+ kbd.textContent = cellValue;
1793
+ rowCell.append(kbd);
1794
+ } else {
1795
+ rowCell.textContent = cellValue;
1796
+ }
1797
+ rowCells.push(rowCell);
1798
+ }
1799
+ const row = document.createElement("tr");
1800
+ row.classList.add("dn-commands-table-tbody-tr");
1801
+ for (const rowCell of rowCells) {
1802
+ row.appendChild(rowCell);
1803
+ }
1804
+ tbody.appendChild(row);
1805
+ }
1806
+ const table = document.createElement("table");
1807
+ table.classList.add("dn-commands-table");
1808
+ const title = this.getAttribute("title");
1809
+ if (title) {
1810
+ const caption = document.createElement("caption");
1811
+ caption.classList.add("dn-commands-table-caption");
1812
+ caption.textContent = title;
1813
+ table.appendChild(caption);
1814
+ } else {
1815
+ table.setAttribute("aria-label", "Commands table");
1816
+ }
1817
+ table.appendChild(thead);
1818
+ table.appendChild(tbody);
1819
+ this.appendChild(table);
1820
+ };
1821
+
1193
1822
  // src/rendering.ts
1194
1823
  var rendering_default = (options) => {
1195
1824
  const setActiveDescendant = (e) => {
@@ -1215,7 +1844,7 @@ var rendering_default = (options) => {
1215
1844
  defaults.existingElement = options.defaults.existingElement ? __spreadValues(__spreadValues({}, defaults.existingElement), options.defaults.existingElement) : defaults.existingElement;
1216
1845
  }
1217
1846
  renderer.initialize = () => {
1218
- var _a;
1847
+ var _a, _b;
1219
1848
  if (initialized) {
1220
1849
  console.error(
1221
1850
  `The renderer wrapper has already been initialized successfully, RenderingOptions.suffixId is: ${options.suffixId}. No further action was taken.`
@@ -1246,6 +1875,9 @@ var rendering_default = (options) => {
1246
1875
  if (options.root && options.root.height) {
1247
1876
  renderer.wrapper.style.height = options.root.height;
1248
1877
  }
1878
+ if ((_a = options.commandsElement) == null ? void 0 : _a.include) {
1879
+ renderer.initializeCommands(options.commandsElement);
1880
+ }
1249
1881
  if (options.entryButton && options.entryButton.include) {
1250
1882
  renderer.entryButton = document.createElement("button");
1251
1883
  renderer.entryButton.id = "dn-entry-button-" + options.suffixId;
@@ -1260,7 +1892,7 @@ var rendering_default = (options) => {
1260
1892
  renderer.wrapper.appendChild(renderer.entryButton);
1261
1893
  }
1262
1894
  renderer.root.appendChild(renderer.wrapper);
1263
- if ((_a = options.exitElement) == null ? void 0 : _a.include) {
1895
+ if ((_b = options.exitElement) == null ? void 0 : _b.include) {
1264
1896
  renderer.exitElement = document.createElement("div");
1265
1897
  renderer.exitElement.id = "dn-exit-" + options.suffixId;
1266
1898
  renderer.exitElement.classList.add("dn-exit-position");
@@ -1270,17 +1902,17 @@ var rendering_default = (options) => {
1270
1902
  renderer.exitElement.setAttribute("tabindex", "-1");
1271
1903
  renderer.exitElement.style.display = "none";
1272
1904
  renderer.exitElement.addEventListener("focus", (e) => {
1273
- var _a2, _b;
1905
+ var _a2, _b2;
1274
1906
  renderer.exitElement.style.display = "block";
1275
1907
  renderer.clearStructure();
1276
- if ((_b = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b.focus) {
1908
+ if ((_b2 = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b2.focus) {
1277
1909
  options.exitElement.callbacks.focus(e);
1278
1910
  }
1279
1911
  });
1280
1912
  renderer.exitElement.addEventListener("blur", (e) => {
1281
- var _a2, _b;
1913
+ var _a2, _b2;
1282
1914
  renderer.exitElement.style.display = "none";
1283
- if ((_b = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b.blur) {
1915
+ if ((_b2 = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b2.blur) {
1284
1916
  options.exitElement.callbacks.blur(e);
1285
1917
  }
1286
1918
  });
@@ -1337,15 +1969,15 @@ var rendering_default = (options) => {
1337
1969
  const attributes = resolveProp("semantics", "attributes");
1338
1970
  if (typeof attributes === "object") {
1339
1971
  Object.keys(attributes).forEach((attribute) => {
1340
- node.setAttribute(attribute, attributes[attribute]);
1972
+ nodeText.setAttribute(attribute, attributes[attribute]);
1341
1973
  });
1342
1974
  }
1343
1975
  nodeText.setAttribute("role", resolveProp("semantics", "role"));
1344
1976
  nodeText.classList.add("dn-node-text");
1977
+ const label = resolveProp("semantics", "label");
1345
1978
  if (d.showText) {
1346
- nodeText.innerText = d.semantics.label;
1979
+ nodeText.innerText = label;
1347
1980
  }
1348
- const label = resolveProp("semantics", "label");
1349
1981
  if (!label) {
1350
1982
  console.error(
1351
1983
  "Accessibility error: a label must be supplied to every rendered element using semantics.label."
@@ -1393,6 +2025,34 @@ var rendering_default = (options) => {
1393
2025
  }
1394
2026
  });
1395
2027
  };
2028
+ renderer.initializeCommands = (commandOptions) => {
2029
+ const root = document.getElementById(commandOptions == null ? void 0 : commandOptions.rootId);
2030
+ if (!root) {
2031
+ console.error("Commands root element not found in document.");
2032
+ return;
2033
+ }
2034
+ const optionsCommands = commandOptions.commands;
2035
+ const genericCommandsFromRules = getGenericCommandsFromNavRules(
2036
+ commandOptions.navigationRules,
2037
+ commandOptions.descriptions
2038
+ );
2039
+ const commands = optionsCommands ? typeof optionsCommands === "function" ? optionsCommands(genericCommandsFromRules) : optionsCommands : genericCommandsFromRules;
2040
+ if (!customElements.get("dn-commands-table")) {
2041
+ customElements.define("dn-commands-table", CommandsTable);
2042
+ }
2043
+ const commandsElement = document.createElement("dn-commands-table");
2044
+ if (commandOptions == null ? void 0 : commandOptions.title) {
2045
+ commandsElement.setAttribute("title", commandOptions.title);
2046
+ }
2047
+ commandsElement.commands = commands;
2048
+ if (commandOptions.columns) {
2049
+ commandsElement.columns = commandOptions.columns;
2050
+ }
2051
+ root.appendChild(commandsElement);
2052
+ return commandsElement;
2053
+ };
2054
+ renderer.getGenericCommandsFromNavRules = getGenericCommandsFromNavRules;
2055
+ renderer.CommandsTable = CommandsTable;
1396
2056
  return renderer;
1397
2057
  };
1398
2058
 
@@ -1567,7 +2227,7 @@ var searchNodes = (query, structure, describeFn, limit = 10) => {
1567
2227
  return results;
1568
2228
  };
1569
2229
  var text_chat_default = (options) => {
1570
- var _a;
2230
+ var _a, _b, _c, _d;
1571
2231
  const {
1572
2232
  structure,
1573
2233
  container,
@@ -1585,7 +2245,9 @@ var text_chat_default = (options) => {
1585
2245
  if (!rootEl) {
1586
2246
  throw new Error(`textChat: container "${container}" not found`);
1587
2247
  }
1588
- const resolvedEntryPoint = entryPoint || (structure.dimensions ? (_a = structure.dimensions[Object.keys(structure.dimensions)[0]]) == null ? void 0 : _a.nodeId : Object.keys(structure.nodes)[0]);
2248
+ const level0Id = Object.keys(structure.nodes).find((id) => structure.nodes[id].dimensionLevel === 0);
2249
+ const firstDimensionId = structure.dimensions ? (_a = structure.dimensions[Object.keys(structure.dimensions)[0]]) == null ? void 0 : _a.nodeId : void 0;
2250
+ const resolvedEntryPoint = entryPoint || ((_b = structure.frame) == null ? void 0 : _b.rootId) || level0Id || ((_c = structure.spatialGrid) == null ? void 0 : _c.rootId) || ((_d = structure.hierarchy) == null ? void 0 : _d.rootId) || firstDimensionId || Object.keys(structure.nodes)[0];
1589
2251
  const inputHandler = input_default({
1590
2252
  structure,
1591
2253
  navigationRules: structure.navigationRules || {},
@@ -1949,8 +2611,203 @@ var text_chat_default = (options) => {
1949
2611
  };
1950
2612
  };
1951
2613
 
2614
+ // src/geometry.ts
2615
+ function grahamScan(points) {
2616
+ if (points.length < 3)
2617
+ return [...points];
2618
+ let pivot = points[0];
2619
+ for (const p of points) {
2620
+ if (p.y < pivot.y || p.y === pivot.y && p.x < pivot.x)
2621
+ pivot = p;
2622
+ }
2623
+ const sorted = [...points].sort((a, b) => {
2624
+ const ax = a.x - pivot.x, ay = a.y - pivot.y;
2625
+ const bx = b.x - pivot.x, by = b.y - pivot.y;
2626
+ const cross2 = ax * by - ay * bx;
2627
+ if (cross2 !== 0)
2628
+ return -cross2;
2629
+ return ax * ax + ay * ay - (bx * bx + by * by);
2630
+ });
2631
+ const hull = [];
2632
+ for (const p of sorted) {
2633
+ while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
2634
+ hull.pop();
2635
+ }
2636
+ hull.push(p);
2637
+ }
2638
+ return hull;
2639
+ }
2640
+ function cross(O, A, B) {
2641
+ return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
2642
+ }
2643
+ function convexHullPath(points) {
2644
+ if (points.length === 0)
2645
+ return "";
2646
+ if (points.length === 1) {
2647
+ const p = points[0];
2648
+ return `M${p.x},${p.y}Z`;
2649
+ }
2650
+ const hull = grahamScan(points);
2651
+ if (hull.length === 0)
2652
+ return "";
2653
+ const [first, ...rest] = hull;
2654
+ return `M${fmt(first.x)},${fmt(first.y)}` + rest.map((p) => `L${fmt(p.x)},${fmt(p.y)}`).join("") + "Z";
2655
+ }
2656
+ function expandHull(hullPoints, padPx) {
2657
+ if (padPx === 0 || hullPoints.length === 0)
2658
+ return hullPoints;
2659
+ const cx = hullPoints.reduce((s, p) => s + p.x, 0) / hullPoints.length;
2660
+ const cy = hullPoints.reduce((s, p) => s + p.y, 0) / hullPoints.length;
2661
+ return hullPoints.map((p) => {
2662
+ const dx = p.x - cx;
2663
+ const dy = p.y - cy;
2664
+ const dist = Math.hypot(dx, dy);
2665
+ if (dist === 0)
2666
+ return p;
2667
+ return { x: p.x + dx / dist * padPx, y: p.y + dy / dist * padPx };
2668
+ });
2669
+ }
2670
+ function rectCorners(r) {
2671
+ return [
2672
+ { x: r.x, y: r.y },
2673
+ { x: r.x + r.width, y: r.y },
2674
+ { x: r.x + r.width, y: r.y + r.height },
2675
+ { x: r.x, y: r.y + r.height }
2676
+ ];
2677
+ }
2678
+ function padRect(r, pad) {
2679
+ return { x: r.x - pad, y: r.y - pad, width: r.width + pad * 2, height: r.height + pad * 2 };
2680
+ }
2681
+ function convexHullOfRects(rects, padPx = 0) {
2682
+ if (rects.length === 0)
2683
+ return "";
2684
+ const corners = rects.flatMap(rectCorners);
2685
+ const hull = grahamScan(corners);
2686
+ const expanded = padPx > 0 ? expandHull(hull, padPx) : hull;
2687
+ return hullToPath(expanded);
2688
+ }
2689
+ function unionOfRectPaths(rects, padPx = 0) {
2690
+ if (rects.length === 0)
2691
+ return "";
2692
+ return rects.map((r) => {
2693
+ const pr = padPx > 0 ? padRect(r, padPx) : r;
2694
+ const { x, y, width: w, height: h } = pr;
2695
+ return `M${fmt(x)},${fmt(y)}L${fmt(x + w)},${fmt(y)}L${fmt(x + w)},${fmt(y + h)}L${fmt(x)},${fmt(y + h)}Z`;
2696
+ }).join("");
2697
+ }
2698
+ function boundingRectPath(rects, padPx = 0) {
2699
+ if (rects.length === 0)
2700
+ return "";
2701
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
2702
+ for (const r of rects) {
2703
+ minX = Math.min(minX, r.x);
2704
+ minY = Math.min(minY, r.y);
2705
+ maxX = Math.max(maxX, r.x + r.width);
2706
+ maxY = Math.max(maxY, r.y + r.height);
2707
+ }
2708
+ minX -= padPx;
2709
+ minY -= padPx;
2710
+ maxX += padPx;
2711
+ maxY += padPx;
2712
+ return `M${fmt(minX)},${fmt(minY)}L${fmt(maxX)},${fmt(minY)}L${fmt(maxX)},${fmt(maxY)}L${fmt(minX)},${fmt(maxY)}Z`;
2713
+ }
2714
+ function convexHullOfCircles(circles, padPx = 0) {
2715
+ if (circles.length === 0)
2716
+ return "";
2717
+ const SAMPLES = 16;
2718
+ const points = [];
2719
+ for (const { x, y, r } of circles) {
2720
+ const effectiveR = r + padPx;
2721
+ for (let i = 0; i < SAMPLES; i++) {
2722
+ const angle = 2 * Math.PI * i / SAMPLES;
2723
+ points.push({
2724
+ x: x + effectiveR * Math.cos(angle),
2725
+ y: y + effectiveR * Math.sin(angle)
2726
+ });
2727
+ }
2728
+ }
2729
+ return convexHullPath(points);
2730
+ }
2731
+ function offsetLinePath(points, offsetPx) {
2732
+ if (points.length < 2) {
2733
+ if (points.length === 1) {
2734
+ const p = points[0];
2735
+ return convexHullOfCircles([{ x: p.x, y: p.y, r: offsetPx }]);
2736
+ }
2737
+ return "";
2738
+ }
2739
+ const normals = [];
2740
+ for (let i = 0; i < points.length - 1; i++) {
2741
+ const dx = points[i + 1].x - points[i].x;
2742
+ const dy = points[i + 1].y - points[i].y;
2743
+ const len = Math.hypot(dx, dy);
2744
+ normals.push(len === 0 ? { x: 0, y: -1 } : { x: -dy / len, y: dx / len });
2745
+ }
2746
+ const vertexNormals = [];
2747
+ for (let i = 0; i < points.length; i++) {
2748
+ if (i === 0) {
2749
+ vertexNormals.push(normals[0]);
2750
+ } else if (i === points.length - 1) {
2751
+ vertexNormals.push(normals[normals.length - 1]);
2752
+ } else {
2753
+ const n1 = normals[i - 1];
2754
+ const n2 = normals[i];
2755
+ const avg = { x: (n1.x + n2.x) / 2, y: (n1.y + n2.y) / 2 };
2756
+ const len = Math.hypot(avg.x, avg.y);
2757
+ vertexNormals.push(len === 0 ? n1 : { x: avg.x / len, y: avg.y / len });
2758
+ }
2759
+ }
2760
+ const left = points.map((p, i) => ({
2761
+ x: p.x + vertexNormals[i].x * offsetPx,
2762
+ y: p.y + vertexNormals[i].y * offsetPx
2763
+ }));
2764
+ const right = points.map((p, i) => ({
2765
+ x: p.x - vertexNormals[i].x * offsetPx,
2766
+ y: p.y - vertexNormals[i].y * offsetPx
2767
+ })).reverse();
2768
+ const firstLeft = left[0];
2769
+ let d = `M${fmt(firstLeft.x)},${fmt(firstLeft.y)}`;
2770
+ for (let i = 1; i < left.length; i++) {
2771
+ d += `L${fmt(left[i].x)},${fmt(left[i].y)}`;
2772
+ }
2773
+ const lastLeft = left[left.length - 1];
2774
+ const firstRight = right[0];
2775
+ d += arcCap(lastLeft, firstRight, offsetPx);
2776
+ for (let i = 1; i < right.length; i++) {
2777
+ d += `L${fmt(right[i].x)},${fmt(right[i].y)}`;
2778
+ }
2779
+ const lastRight = right[right.length - 1];
2780
+ d += arcCap(lastRight, firstLeft, offsetPx);
2781
+ d += "Z";
2782
+ return d;
2783
+ }
2784
+ function arcCap(from, to, r) {
2785
+ return `A${fmt(r)},${fmt(r)},0,0,0,${fmt(to.x)},${fmt(to.y)}`;
2786
+ }
2787
+ function fmt(n) {
2788
+ return Math.round(n * 100) / 100 + "";
2789
+ }
2790
+ function hullToPath(hull) {
2791
+ if (hull.length === 0)
2792
+ return "";
2793
+ const [first, ...rest] = hull;
2794
+ return `M${fmt(first.x)},${fmt(first.y)}` + rest.map((p) => `L${fmt(p.x)},${fmt(p.y)}`).join("") + "Z";
2795
+ }
2796
+
1952
2797
  // src/index.ts
1953
2798
  var src_default = { structure: structure_default, input: input_default, rendering: rendering_default, textChat: text_chat_default };
1954
2799
  export {
1955
- src_default as default
2800
+ boundingRectPath,
2801
+ convexHullOfCircles,
2802
+ convexHullOfRects,
2803
+ convexHullPath,
2804
+ createValidId,
2805
+ src_default as default,
2806
+ describeNode,
2807
+ expandHull,
2808
+ getGenericCommandsFromNavRules,
2809
+ grahamScan,
2810
+ keyLabel,
2811
+ offsetLinePath,
2812
+ unionOfRectPaths
1956
2813
  };