data-navigator 2.4.0 → 3.0.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,8 +77,8 @@ 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
83
  var TypicallyUnreservedKeys = ["KeyW", "KeyJ", "LeftBracket", "RightBracket", "Slash", "Backslash"];
62
84
  var TypicallyUnreservedKeyPairs = [
@@ -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,26 @@ var NodeElementDefaults = {
187
209
  spatialProperties: void 0
188
210
  }
189
211
  };
212
+ var GenericNavigationRuleCommands = {
213
+ "drill-in": { label: "Enter", description: "Drill in" },
214
+ "drill-out": { label: "Backspace", description: "Drill out" },
215
+ exit: { label: "Esc", description: "Exit" },
216
+ left: { label: "\u2190", description: "Previous data point" },
217
+ right: { label: "\u2192", description: "Next data point" },
218
+ up: { label: "\u2191", description: "Previous data point" },
219
+ down: { label: "\u2193", description: "Next data point" },
220
+ backward: { label: ",", description: "Move backward" },
221
+ forward: { label: ".", description: "Move forward" },
222
+ previous: { label: ";", description: "Previous" },
223
+ next: { label: "'", description: "Next" },
224
+ undo: { label: "Z", description: "Undo" },
225
+ help: { label: "Y", description: "Help" },
226
+ legend: { label: "L", description: "Legend" }
227
+ };
228
+ var commandsTableDefaultColumns = [
229
+ { label: "Command", id: "description" },
230
+ { label: "Key", id: "label" }
231
+ ];
190
232
 
191
233
  // src/utilities.ts
192
234
  var describeNode = (d, descriptionOptions) => {
@@ -201,6 +243,12 @@ var describeNode = (d, descriptionOptions) => {
201
243
  var createValidId = (s) => {
202
244
  return "_" + s.replace(/[^a-zA-Z0-9_-]+/g, "_");
203
245
  };
246
+ function getGenericCommandsFromNavRules(navigationRules) {
247
+ if (!navigationRules) {
248
+ return [];
249
+ }
250
+ return Object.keys(navigationRules).map((navId) => GenericNavigationRuleCommands[navId]).filter(Boolean);
251
+ }
204
252
 
205
253
  // src/structure.ts
206
254
  var structure_default = (options) => {
@@ -277,7 +325,7 @@ var buildNodeStructureFromVegaLite = (options) => {
277
325
  edges[firstChildEdge] = {
278
326
  source: node.id,
279
327
  target: firstChildId,
280
- navigationRules: ["parent", "child"]
328
+ navigationRules: ["drill-out", "drill-in"]
281
329
  };
282
330
  }
283
331
  }
@@ -290,7 +338,7 @@ var buildNodeStructureFromVegaLite = (options) => {
290
338
  edges[parentEdge] = {
291
339
  source: parentId,
292
340
  target: node.id,
293
- navigationRules: ["parent", "child"]
341
+ navigationRules: ["drill-out", "drill-in"]
294
342
  };
295
343
  }
296
344
  }
@@ -491,7 +539,7 @@ var scaffoldDimensions = (options, nodes) => {
491
539
  },
492
540
  navigationRules: dim.navigationRules || {
493
541
  sibling_sibling: rules.length ? [...rules.shift()] : ["previous_" + dim.dimensionKey, "next_" + dim.dimensionKey],
494
- parent_child: ["parent_" + dim.dimensionKey, "child"]
542
+ parent_child: ["drill-out_" + dim.dimensionKey, "drill-in"]
495
543
  }
496
544
  };
497
545
  nodes[id2] = {
@@ -604,7 +652,8 @@ var scaffoldDimensions = (options, nodes) => {
604
652
  let node = values[valueKeys[index]];
605
653
  let value = node[s];
606
654
  if (value <= i) {
607
- dimension.divisions[divisionId].values[node.id] = node;
655
+ const leafId = typeof options.idKey === "function" ? options.idKey(node) : node[options.idKey];
656
+ dimension.divisions[divisionId].values[leafId] = node;
608
657
  index++;
609
658
  } else {
610
659
  limit = true;
@@ -616,11 +665,12 @@ var scaffoldDimensions = (options, nodes) => {
616
665
  if (lastDivisionId && index < valueKeys.length) {
617
666
  while (index < valueKeys.length) {
618
667
  let node = values[valueKeys[index]];
619
- dimension.divisions[lastDivisionId].values[node.id] = node;
668
+ const leafId = typeof options.idKey === "function" ? options.idKey(node) : node[options.idKey];
669
+ dimension.divisions[lastDivisionId].values[leafId] = node;
620
670
  index++;
621
671
  }
622
672
  }
623
- delete divisions[s];
673
+ delete divisions[dimension.nodeId];
624
674
  }
625
675
  } else if (typeof ((_e = dimension.operations) == null ? void 0 : _e.sortFunction) === "function") {
626
676
  dimension.divisions = Object.fromEntries(
@@ -719,7 +769,7 @@ var buildEdges = (options, nodes, dimensions) => {
719
769
  let po = ((_d = options.dimensions) == null ? void 0 : _d.parentOptions) || {};
720
770
  let extents = ((_f = (_e = po.level1Options) == null ? void 0 : _e.behavior) == null ? void 0 : _f.extents) || "terminal";
721
771
  let level0 = po.addLevel0;
722
- let parentRules = level0 ? ((_h = (_g = po.level1Options) == null ? void 0 : _g.navigationRules) == null ? void 0 : _h.parent_child) || ["parent", "child"] : [];
772
+ let parentRules = level0 ? ((_h = (_g = po.level1Options) == null ? void 0 : _g.navigationRules) == null ? void 0 : _h.parent_child) || ["drill-out", "drill-in"] : [];
723
773
  let siblingRules = ((_j = (_i = po.level1Options) == null ? void 0 : _i.navigationRules) == null ? void 0 : _j.sibling_sibling) || ["left", "right"];
724
774
  let firstLevel1Node = typeof order[0] === "string" ? hasOrder ? nodes[order[0]] : nodes[dimensions[order[0]].nodeId] : order[0];
725
775
  if (level0) {
@@ -786,6 +836,22 @@ var buildEdges = (options, nodes, dimensions) => {
786
836
  "source"
787
837
  );
788
838
  }
839
+ const findNextNonEmptyDivIdx = (fromIdx) => {
840
+ for (let step = 1; step < divisionKeys.length; step++) {
841
+ const idx = (fromIdx + step) % divisionKeys.length;
842
+ if (Object.keys(dimension.divisions[divisionKeys[idx]].values).length > 0)
843
+ return idx;
844
+ }
845
+ return null;
846
+ };
847
+ const findPrevNonEmptyDivIdx = (fromIdx) => {
848
+ for (let step = 1; step < divisionKeys.length; step++) {
849
+ const idx = (fromIdx - step + divisionKeys.length) % divisionKeys.length;
850
+ if (Object.keys(dimension.divisions[divisionKeys[idx]].values).length > 0)
851
+ return idx;
852
+ }
853
+ return null;
854
+ };
789
855
  let j = 0;
790
856
  divisionKeys.forEach((d) => {
791
857
  let division = dimension.divisions[d];
@@ -837,22 +903,13 @@ var buildEdges = (options, nodes, dimensions) => {
837
903
  dimension.navigationRules.sibling_sibling
838
904
  );
839
905
  } else if (i === valueKeys.length - 1 && extents2 === "bridgedCousins") {
840
- if (j !== divisionKeys.length - 1) {
841
- const targetId = typeof options.idKey === "function" ? options.idKey(
842
- dimension.divisions[divisionKeys[j + 1]].values[valueKeys[0]]
843
- ) : options.idKey;
844
- createEdge(
845
- v[id],
846
- dimension.divisions[divisionKeys[j + 1]].values[valueKeys[0]][targetId],
847
- dimension.navigationRules.sibling_sibling
848
- );
849
- } else {
850
- const targetId = typeof options.idKey === "function" ? options.idKey(dimension.divisions[divisionKeys[0]].values[valueKeys[0]]) : options.idKey;
851
- createEdge(
852
- v[id],
853
- dimension.divisions[divisionKeys[0]].values[valueKeys[0]][targetId],
854
- dimension.navigationRules.sibling_sibling
855
- );
906
+ const nextIdx = findNextNonEmptyDivIdx(j);
907
+ if (nextIdx !== null) {
908
+ const nextDivValues = dimension.divisions[divisionKeys[nextIdx]].values;
909
+ const nextDivValueKeys = Object.keys(nextDivValues);
910
+ const targetDatum = nextDivValues[nextDivValueKeys[0]];
911
+ const targetId = typeof options.idKey === "function" ? options.idKey(targetDatum) : options.idKey;
912
+ createEdge(v[id], targetDatum[targetId], dimension.navigationRules.sibling_sibling);
856
913
  }
857
914
  } else if (i === valueKeys.length - 1 && extents2 === "bridgedCustom") {
858
915
  createEdge(
@@ -869,24 +926,13 @@ var buildEdges = (options, nodes, dimensions) => {
869
926
  );
870
927
  }
871
928
  if (!i && extents2 === "bridgedCousins") {
872
- if (j !== 0) {
873
- const targetId = typeof options.idKey === "function" ? options.idKey(
874
- dimension.divisions[divisionKeys[j - 1]].values[valueKeys[valueKeys.length - 1]]
875
- ) : options.idKey;
876
- createEdge(
877
- dimension.divisions[divisionKeys[j - 1]].values[valueKeys[valueKeys.length - 1]][targetId],
878
- v[id],
879
- dimension.navigationRules.sibling_sibling
880
- );
881
- } else {
882
- const targetId = typeof options.idKey === "function" ? options.idKey(
883
- dimension.divisions[divisionKeys[divisionKeys.length - 1]].values[valueKeys[valueKeys.length - 1]]
884
- ) : options.idKey;
885
- createEdge(
886
- dimension.divisions[divisionKeys[divisionKeys.length - 1]].values[valueKeys[valueKeys.length - 1]][targetId],
887
- v[id],
888
- dimension.navigationRules.sibling_sibling
889
- );
929
+ const prevIdx = findPrevNonEmptyDivIdx(j);
930
+ if (prevIdx !== null) {
931
+ const prevDivValues = dimension.divisions[divisionKeys[prevIdx]].values;
932
+ const prevDivValueKeys = Object.keys(prevDivValues);
933
+ const targetDatum = prevDivValues[prevDivValueKeys[prevDivValueKeys.length - 1]];
934
+ const targetId = typeof options.idKey === "function" ? options.idKey(targetDatum) : options.idKey;
935
+ createEdge(targetDatum[targetId], v[id], dimension.navigationRules.sibling_sibling);
890
936
  }
891
937
  } else if (!i && extents2 === "bridgedCustom") {
892
938
  createEdge(
@@ -1035,8 +1081,8 @@ var buildRules = (options, edges, dimensions) => {
1035
1081
  if (dimKeys.length) {
1036
1082
  if ((_b = (_a = options.dimensions) == null ? void 0 : _a.parentOptions) == null ? void 0 : _b.addLevel0) {
1037
1083
  let rules2 = ((_d = (_c = options.dimensions.parentOptions.level1Options) == null ? void 0 : _c.navigationRules) == null ? void 0 : _d.parent_child) || [
1038
- "parent",
1039
- "child"
1084
+ "drill-out",
1085
+ "drill-in"
1040
1086
  ];
1041
1087
  checkKeys(rules2[0], rules2[1]);
1042
1088
  }
@@ -1192,6 +1238,124 @@ var input_default = (options) => {
1192
1238
  return inputHandler;
1193
1239
  };
1194
1240
 
1241
+ // src/commands-table.ts
1242
+ var _commands, _columns, _kbdColumn, _connected, _render, render_fn;
1243
+ var CommandsTable = class extends HTMLElement {
1244
+ constructor() {
1245
+ super(...arguments);
1246
+ __privateAdd(this, _render);
1247
+ __privateAdd(this, _commands, []);
1248
+ __privateAdd(this, _columns, commandsTableDefaultColumns);
1249
+ __privateAdd(this, _kbdColumn, "label");
1250
+ __privateAdd(this, _connected, false);
1251
+ }
1252
+ static get observedAttributes() {
1253
+ return ["title"];
1254
+ }
1255
+ set commands(value) {
1256
+ __privateSet(this, _commands, value);
1257
+ if (!__privateGet(this, _connected))
1258
+ return;
1259
+ __privateMethod(this, _render, render_fn).call(this);
1260
+ }
1261
+ get commands() {
1262
+ return __privateGet(this, _commands);
1263
+ }
1264
+ set columns(value) {
1265
+ __privateSet(this, _columns, value);
1266
+ if (!__privateGet(this, _connected))
1267
+ return;
1268
+ __privateMethod(this, _render, render_fn).call(this);
1269
+ }
1270
+ get columns() {
1271
+ return __privateGet(this, _columns);
1272
+ }
1273
+ /** Column id whose cells are wrapped in <kbd>. Defaults to 'label'. Set to null to disable. */
1274
+ set kbdColumn(value) {
1275
+ __privateSet(this, _kbdColumn, value);
1276
+ if (!__privateGet(this, _connected))
1277
+ return;
1278
+ __privateMethod(this, _render, render_fn).call(this);
1279
+ }
1280
+ get kbdColumn() {
1281
+ return __privateGet(this, _kbdColumn);
1282
+ }
1283
+ attributeChangedCallback() {
1284
+ if (!__privateGet(this, _connected))
1285
+ return;
1286
+ __privateMethod(this, _render, render_fn).call(this);
1287
+ }
1288
+ connectedCallback() {
1289
+ __privateSet(this, _connected, true);
1290
+ __privateMethod(this, _render, render_fn).call(this);
1291
+ }
1292
+ disconnectedCallback() {
1293
+ __privateSet(this, _connected, false);
1294
+ }
1295
+ };
1296
+ _commands = new WeakMap();
1297
+ _columns = new WeakMap();
1298
+ _kbdColumn = new WeakMap();
1299
+ _connected = new WeakMap();
1300
+ _render = new WeakSet();
1301
+ render_fn = function() {
1302
+ this.replaceChildren();
1303
+ const headerCells = [];
1304
+ for (const column of __privateGet(this, _columns)) {
1305
+ const headerCell = document.createElement("th");
1306
+ headerCell.classList.add("dn-commands-table-th");
1307
+ headerCell.textContent = column.label;
1308
+ headerCells.push(headerCell);
1309
+ }
1310
+ const headerRow = document.createElement("tr");
1311
+ headerRow.classList.add("dn-commands-table-thead-tr");
1312
+ for (const headerCell of headerCells) {
1313
+ headerRow.appendChild(headerCell);
1314
+ }
1315
+ const thead = document.createElement("thead");
1316
+ thead.classList.add("dn-commands-table-thead");
1317
+ thead.appendChild(headerRow);
1318
+ const tbody = document.createElement("tbody");
1319
+ tbody.classList.add("dn-commands-table-tbody");
1320
+ for (const command of __privateGet(this, _commands)) {
1321
+ const rowCells = [];
1322
+ for (const column of __privateGet(this, _columns)) {
1323
+ const cellValue = command[column.id];
1324
+ const rowCell = document.createElement("td");
1325
+ rowCell.classList.add("dn-commands-table-td");
1326
+ if (__privateGet(this, _kbdColumn) !== null && column.id === __privateGet(this, _kbdColumn) && cellValue) {
1327
+ const kbd = document.createElement("kbd");
1328
+ kbd.classList.add("dn-commands-table-td-kbd");
1329
+ kbd.textContent = cellValue;
1330
+ rowCell.append(kbd);
1331
+ } else {
1332
+ rowCell.textContent = cellValue;
1333
+ }
1334
+ rowCells.push(rowCell);
1335
+ }
1336
+ const row = document.createElement("tr");
1337
+ row.classList.add("dn-commands-table-tbody-tr");
1338
+ for (const rowCell of rowCells) {
1339
+ row.appendChild(rowCell);
1340
+ }
1341
+ tbody.appendChild(row);
1342
+ }
1343
+ const table = document.createElement("table");
1344
+ table.classList.add("dn-commands-table");
1345
+ const title = this.getAttribute("title");
1346
+ if (title) {
1347
+ const caption = document.createElement("caption");
1348
+ caption.classList.add("dn-commands-table-caption");
1349
+ caption.textContent = title;
1350
+ table.appendChild(caption);
1351
+ } else {
1352
+ table.setAttribute("aria-label", "Commands table");
1353
+ }
1354
+ table.appendChild(thead);
1355
+ table.appendChild(tbody);
1356
+ this.appendChild(table);
1357
+ };
1358
+
1195
1359
  // src/rendering.ts
1196
1360
  var rendering_default = (options) => {
1197
1361
  const setActiveDescendant = (e) => {
@@ -1217,7 +1381,7 @@ var rendering_default = (options) => {
1217
1381
  defaults.existingElement = options.defaults.existingElement ? __spreadValues(__spreadValues({}, defaults.existingElement), options.defaults.existingElement) : defaults.existingElement;
1218
1382
  }
1219
1383
  renderer.initialize = () => {
1220
- var _a;
1384
+ var _a, _b;
1221
1385
  if (initialized) {
1222
1386
  console.error(
1223
1387
  `The renderer wrapper has already been initialized successfully, RenderingOptions.suffixId is: ${options.suffixId}. No further action was taken.`
@@ -1248,6 +1412,9 @@ var rendering_default = (options) => {
1248
1412
  if (options.root && options.root.height) {
1249
1413
  renderer.wrapper.style.height = options.root.height;
1250
1414
  }
1415
+ if ((_a = options.commandsElement) == null ? void 0 : _a.include) {
1416
+ renderer.initializeCommands(options.commandsElement);
1417
+ }
1251
1418
  if (options.entryButton && options.entryButton.include) {
1252
1419
  renderer.entryButton = document.createElement("button");
1253
1420
  renderer.entryButton.id = "dn-entry-button-" + options.suffixId;
@@ -1262,7 +1429,7 @@ var rendering_default = (options) => {
1262
1429
  renderer.wrapper.appendChild(renderer.entryButton);
1263
1430
  }
1264
1431
  renderer.root.appendChild(renderer.wrapper);
1265
- if ((_a = options.exitElement) == null ? void 0 : _a.include) {
1432
+ if ((_b = options.exitElement) == null ? void 0 : _b.include) {
1266
1433
  renderer.exitElement = document.createElement("div");
1267
1434
  renderer.exitElement.id = "dn-exit-" + options.suffixId;
1268
1435
  renderer.exitElement.classList.add("dn-exit-position");
@@ -1272,17 +1439,17 @@ var rendering_default = (options) => {
1272
1439
  renderer.exitElement.setAttribute("tabindex", "-1");
1273
1440
  renderer.exitElement.style.display = "none";
1274
1441
  renderer.exitElement.addEventListener("focus", (e) => {
1275
- var _a2, _b;
1442
+ var _a2, _b2;
1276
1443
  renderer.exitElement.style.display = "block";
1277
1444
  renderer.clearStructure();
1278
- if ((_b = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b.focus) {
1445
+ if ((_b2 = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b2.focus) {
1279
1446
  options.exitElement.callbacks.focus(e);
1280
1447
  }
1281
1448
  });
1282
1449
  renderer.exitElement.addEventListener("blur", (e) => {
1283
- var _a2, _b;
1450
+ var _a2, _b2;
1284
1451
  renderer.exitElement.style.display = "none";
1285
- if ((_b = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b.blur) {
1452
+ if ((_b2 = (_a2 = options.exitElement) == null ? void 0 : _a2.callbacks) == null ? void 0 : _b2.blur) {
1286
1453
  options.exitElement.callbacks.blur(e);
1287
1454
  }
1288
1455
  });
@@ -1395,6 +1562,31 @@ var rendering_default = (options) => {
1395
1562
  }
1396
1563
  });
1397
1564
  };
1565
+ renderer.initializeCommands = (commandOptions) => {
1566
+ const root = document.getElementById(commandOptions == null ? void 0 : commandOptions.rootId);
1567
+ if (!root) {
1568
+ console.error("Commands root element not found in document.");
1569
+ return;
1570
+ }
1571
+ const optionsCommands = commandOptions.commands;
1572
+ const genericCommandsFromRules = getGenericCommandsFromNavRules(commandOptions.navigationRules);
1573
+ const commands = optionsCommands ? typeof optionsCommands === "function" ? optionsCommands(genericCommandsFromRules) : optionsCommands : genericCommandsFromRules;
1574
+ if (!customElements.get("dn-commands-table")) {
1575
+ customElements.define("dn-commands-table", CommandsTable);
1576
+ }
1577
+ const commandsElement = document.createElement("dn-commands-table");
1578
+ if (commandOptions == null ? void 0 : commandOptions.title) {
1579
+ commandsElement.setAttribute("title", commandOptions.title);
1580
+ }
1581
+ commandsElement.commands = commands;
1582
+ if (commandOptions.columns) {
1583
+ commandsElement.columns = commandOptions.columns;
1584
+ }
1585
+ root.appendChild(commandsElement);
1586
+ return commandsElement;
1587
+ };
1588
+ renderer.getGenericCommandsFromNavRules = getGenericCommandsFromNavRules;
1589
+ renderer.CommandsTable = CommandsTable;
1398
1590
  return renderer;
1399
1591
  };
1400
1592
 
@@ -1484,9 +1676,7 @@ var fuzzyMatch = (input, candidates, labels = {}) => {
1484
1676
  const exactName = candidates.find((c) => c.toLowerCase() === lower);
1485
1677
  if (exactName)
1486
1678
  return { match: exactName, ambiguous: [] };
1487
- const exactLabel = candidates.find(
1488
- (c) => labels[c] && labels[c].toLowerCase() === lower
1489
- );
1679
+ const exactLabel = candidates.find((c) => labels[c] && labels[c].toLowerCase() === lower);
1490
1680
  if (exactLabel)
1491
1681
  return { match: exactLabel, ambiguous: [] };
1492
1682
  const namePrefix = candidates.filter((c) => c.toLowerCase().startsWith(lower));
@@ -1746,8 +1936,12 @@ var text_chat_default = (options) => {
1746
1936
  }
1747
1937
  });
1748
1938
  if (llm) {
1749
- addSystemMessage('Text navigation ready. Type "enter" to begin navigating, "help" for commands, or ask a question about the data.');
1750
- addSystemMessage('Note: AI-generated answers may be inaccurate. You can ask the model to "verify" any answer \u2014 it will attempt to provide a Python script that checks the claim against the dataset. If a claim cannot be verified with code, it should be verified externally.');
1939
+ addSystemMessage(
1940
+ 'Text navigation ready. Type "enter" to begin navigating, "help" for commands, or ask a question about the data.'
1941
+ );
1942
+ addSystemMessage(
1943
+ 'Note: AI-generated answers may be inaccurate. You can ask the model to "verify" any answer \u2014 it will attempt to provide a Python script that checks the claim against the dataset. If a claim cannot be verified with code, it should be verified externally.'
1944
+ );
1751
1945
  } else {
1752
1946
  addSystemMessage('Text navigation ready. Type "enter" to begin or "help" for available commands.');
1753
1947
  }
@@ -1816,9 +2010,7 @@ var text_chat_default = (options) => {
1816
2010
  const node = structure.nodes[currentNodeId];
1817
2011
  const available = getAvailableRules(currentNodeId, node, structure);
1818
2012
  const formatted = available.map((r) => formatRule(r, commandLabels));
1819
- addResponse(
1820
- `Available: ${formatted.join(", ")}, move to <search>.` + interactionSuffix + llmHint
1821
- );
2013
+ addResponse(`Available: ${formatted.join(", ")}, move to <search>.` + interactionSuffix + llmHint);
1822
2014
  }
1823
2015
  return;
1824
2016
  }
@@ -1832,9 +2024,7 @@ var text_chat_default = (options) => {
1832
2024
  onClick(node);
1833
2025
  addResponse(`Clicked: ${describeNode2(node)}`);
1834
2026
  } else {
1835
- addResponse(
1836
- onClick ? "Nothing to click here." : "Click interaction is not enabled for this chart."
1837
- );
2027
+ addResponse(onClick ? "Nothing to click here." : "Click interaction is not enabled for this chart.");
1838
2028
  }
1839
2029
  return;
1840
2030
  }
@@ -1887,7 +2077,9 @@ var text_chat_default = (options) => {
1887
2077
  return;
1888
2078
  }
1889
2079
  const llmHint = llm ? " Enter an API key above to ask questions about the data." : "";
1890
- addResponse('Type "enter" to begin navigating the structure, or "move to <search>" to jump to a node.' + llmHint);
2080
+ addResponse(
2081
+ 'Type "enter" to begin navigating the structure, or "move to <search>" to jump to a node.' + llmHint
2082
+ );
1891
2083
  return;
1892
2084
  }
1893
2085
  const allRules = getAllRuleNames(structure);
@@ -1951,8 +2143,201 @@ var text_chat_default = (options) => {
1951
2143
  };
1952
2144
  };
1953
2145
 
2146
+ // src/geometry.ts
2147
+ function grahamScan(points) {
2148
+ if (points.length < 3)
2149
+ return [...points];
2150
+ let pivot = points[0];
2151
+ for (const p of points) {
2152
+ if (p.y < pivot.y || p.y === pivot.y && p.x < pivot.x)
2153
+ pivot = p;
2154
+ }
2155
+ const sorted = [...points].sort((a, b) => {
2156
+ const ax = a.x - pivot.x, ay = a.y - pivot.y;
2157
+ const bx = b.x - pivot.x, by = b.y - pivot.y;
2158
+ const cross2 = ax * by - ay * bx;
2159
+ if (cross2 !== 0)
2160
+ return -cross2;
2161
+ return ax * ax + ay * ay - (bx * bx + by * by);
2162
+ });
2163
+ const hull = [];
2164
+ for (const p of sorted) {
2165
+ while (hull.length >= 2 && cross(hull[hull.length - 2], hull[hull.length - 1], p) <= 0) {
2166
+ hull.pop();
2167
+ }
2168
+ hull.push(p);
2169
+ }
2170
+ return hull;
2171
+ }
2172
+ function cross(O, A, B) {
2173
+ return (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
2174
+ }
2175
+ function convexHullPath(points) {
2176
+ if (points.length === 0)
2177
+ return "";
2178
+ if (points.length === 1) {
2179
+ const p = points[0];
2180
+ return `M${p.x},${p.y}Z`;
2181
+ }
2182
+ const hull = grahamScan(points);
2183
+ if (hull.length === 0)
2184
+ return "";
2185
+ const [first, ...rest] = hull;
2186
+ return `M${fmt(first.x)},${fmt(first.y)}` + rest.map((p) => `L${fmt(p.x)},${fmt(p.y)}`).join("") + "Z";
2187
+ }
2188
+ function expandHull(hullPoints, padPx) {
2189
+ if (padPx === 0 || hullPoints.length === 0)
2190
+ return hullPoints;
2191
+ const cx = hullPoints.reduce((s, p) => s + p.x, 0) / hullPoints.length;
2192
+ const cy = hullPoints.reduce((s, p) => s + p.y, 0) / hullPoints.length;
2193
+ return hullPoints.map((p) => {
2194
+ const dx = p.x - cx;
2195
+ const dy = p.y - cy;
2196
+ const dist = Math.hypot(dx, dy);
2197
+ if (dist === 0)
2198
+ return p;
2199
+ return { x: p.x + dx / dist * padPx, y: p.y + dy / dist * padPx };
2200
+ });
2201
+ }
2202
+ function rectCorners(r) {
2203
+ return [
2204
+ { x: r.x, y: r.y },
2205
+ { x: r.x + r.width, y: r.y },
2206
+ { x: r.x + r.width, y: r.y + r.height },
2207
+ { x: r.x, y: r.y + r.height }
2208
+ ];
2209
+ }
2210
+ function padRect(r, pad) {
2211
+ return { x: r.x - pad, y: r.y - pad, width: r.width + pad * 2, height: r.height + pad * 2 };
2212
+ }
2213
+ function convexHullOfRects(rects, padPx = 0) {
2214
+ if (rects.length === 0)
2215
+ return "";
2216
+ const corners = rects.flatMap(rectCorners);
2217
+ const hull = grahamScan(corners);
2218
+ const expanded = padPx > 0 ? expandHull(hull, padPx) : hull;
2219
+ return hullToPath(expanded);
2220
+ }
2221
+ function unionOfRectPaths(rects, padPx = 0) {
2222
+ if (rects.length === 0)
2223
+ return "";
2224
+ return rects.map((r) => {
2225
+ const pr = padPx > 0 ? padRect(r, padPx) : r;
2226
+ const { x, y, width: w, height: h } = pr;
2227
+ 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`;
2228
+ }).join("");
2229
+ }
2230
+ function boundingRectPath(rects, padPx = 0) {
2231
+ if (rects.length === 0)
2232
+ return "";
2233
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
2234
+ for (const r of rects) {
2235
+ minX = Math.min(minX, r.x);
2236
+ minY = Math.min(minY, r.y);
2237
+ maxX = Math.max(maxX, r.x + r.width);
2238
+ maxY = Math.max(maxY, r.y + r.height);
2239
+ }
2240
+ minX -= padPx;
2241
+ minY -= padPx;
2242
+ maxX += padPx;
2243
+ maxY += padPx;
2244
+ return `M${fmt(minX)},${fmt(minY)}L${fmt(maxX)},${fmt(minY)}L${fmt(maxX)},${fmt(maxY)}L${fmt(minX)},${fmt(maxY)}Z`;
2245
+ }
2246
+ function convexHullOfCircles(circles, padPx = 0) {
2247
+ if (circles.length === 0)
2248
+ return "";
2249
+ const SAMPLES = 16;
2250
+ const points = [];
2251
+ for (const { x, y, r } of circles) {
2252
+ const effectiveR = r + padPx;
2253
+ for (let i = 0; i < SAMPLES; i++) {
2254
+ const angle = 2 * Math.PI * i / SAMPLES;
2255
+ points.push({
2256
+ x: x + effectiveR * Math.cos(angle),
2257
+ y: y + effectiveR * Math.sin(angle)
2258
+ });
2259
+ }
2260
+ }
2261
+ return convexHullPath(points);
2262
+ }
2263
+ function offsetLinePath(points, offsetPx) {
2264
+ if (points.length < 2) {
2265
+ if (points.length === 1) {
2266
+ const p = points[0];
2267
+ return convexHullOfCircles([{ x: p.x, y: p.y, r: offsetPx }]);
2268
+ }
2269
+ return "";
2270
+ }
2271
+ const normals = [];
2272
+ for (let i = 0; i < points.length - 1; i++) {
2273
+ const dx = points[i + 1].x - points[i].x;
2274
+ const dy = points[i + 1].y - points[i].y;
2275
+ const len = Math.hypot(dx, dy);
2276
+ normals.push(len === 0 ? { x: 0, y: -1 } : { x: -dy / len, y: dx / len });
2277
+ }
2278
+ const vertexNormals = [];
2279
+ for (let i = 0; i < points.length; i++) {
2280
+ if (i === 0) {
2281
+ vertexNormals.push(normals[0]);
2282
+ } else if (i === points.length - 1) {
2283
+ vertexNormals.push(normals[normals.length - 1]);
2284
+ } else {
2285
+ const n1 = normals[i - 1];
2286
+ const n2 = normals[i];
2287
+ const avg = { x: (n1.x + n2.x) / 2, y: (n1.y + n2.y) / 2 };
2288
+ const len = Math.hypot(avg.x, avg.y);
2289
+ vertexNormals.push(len === 0 ? n1 : { x: avg.x / len, y: avg.y / len });
2290
+ }
2291
+ }
2292
+ const left = points.map((p, i) => ({
2293
+ x: p.x + vertexNormals[i].x * offsetPx,
2294
+ y: p.y + vertexNormals[i].y * offsetPx
2295
+ }));
2296
+ const right = points.map((p, i) => ({
2297
+ x: p.x - vertexNormals[i].x * offsetPx,
2298
+ y: p.y - vertexNormals[i].y * offsetPx
2299
+ })).reverse();
2300
+ const firstLeft = left[0];
2301
+ let d = `M${fmt(firstLeft.x)},${fmt(firstLeft.y)}`;
2302
+ for (let i = 1; i < left.length; i++) {
2303
+ d += `L${fmt(left[i].x)},${fmt(left[i].y)}`;
2304
+ }
2305
+ const lastLeft = left[left.length - 1];
2306
+ const firstRight = right[0];
2307
+ d += arcCap(lastLeft, firstRight, offsetPx);
2308
+ for (let i = 1; i < right.length; i++) {
2309
+ d += `L${fmt(right[i].x)},${fmt(right[i].y)}`;
2310
+ }
2311
+ const lastRight = right[right.length - 1];
2312
+ d += arcCap(lastRight, firstLeft, offsetPx);
2313
+ d += "Z";
2314
+ return d;
2315
+ }
2316
+ function arcCap(from, to, r) {
2317
+ return `A${fmt(r)},${fmt(r)},0,0,0,${fmt(to.x)},${fmt(to.y)}`;
2318
+ }
2319
+ function fmt(n) {
2320
+ return Math.round(n * 100) / 100 + "";
2321
+ }
2322
+ function hullToPath(hull) {
2323
+ if (hull.length === 0)
2324
+ return "";
2325
+ const [first, ...rest] = hull;
2326
+ return `M${fmt(first.x)},${fmt(first.y)}` + rest.map((p) => `L${fmt(p.x)},${fmt(p.y)}`).join("") + "Z";
2327
+ }
2328
+
1954
2329
  // src/index.ts
1955
2330
  var src_default = { structure: structure_default, input: input_default, rendering: rendering_default, textChat: text_chat_default };
1956
2331
  export {
1957
- src_default as default
2332
+ boundingRectPath,
2333
+ convexHullOfCircles,
2334
+ convexHullOfRects,
2335
+ convexHullPath,
2336
+ createValidId,
2337
+ src_default as default,
2338
+ describeNode,
2339
+ expandHull,
2340
+ grahamScan,
2341
+ offsetLinePath,
2342
+ unionOfRectPaths
1958
2343
  };