@data-navigator/bokeh-wrapper 0.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.cjs ADDED
@@ -0,0 +1,781 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
+ var __export = (target, all) => {
25
+ for (var name in all)
26
+ __defProp(target, name, { get: all[name], enumerable: true });
27
+ };
28
+ var __copyProps = (to, from, except, desc) => {
29
+ if (from && typeof from === "object" || typeof from === "function") {
30
+ for (let key of __getOwnPropNames(from))
31
+ if (!__hasOwnProp.call(to, key) && key !== except)
32
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
33
+ }
34
+ return to;
35
+ };
36
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
37
+ // If the importer is in node compatibility mode or this is not an ESM
38
+ // file that has been converted to a CommonJS file using a Babel-
39
+ // compatible transform (i.e. "__esModule" has not been set), then set
40
+ // "default" to the CommonJS "module.exports" for node compatibility.
41
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
42
+ mod
43
+ ));
44
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
45
+
46
+ // src/index.ts
47
+ var src_exports = {};
48
+ __export(src_exports, {
49
+ addDataNavigator: () => addDataNavigator,
50
+ buildChartDescription: () => buildChartDescription,
51
+ buildNodeLabel: () => buildNodeLabel,
52
+ prepareNodeSemantics: () => prepareNodeSemantics
53
+ });
54
+ module.exports = __toCommonJS(src_exports);
55
+ var import_data_navigator = __toESM(require("data-navigator"), 1);
56
+
57
+ // src/structure-builder.ts
58
+ function resolveEl(ref) {
59
+ if (typeof ref === "string")
60
+ return document.getElementById(ref);
61
+ return ref instanceof HTMLElement ? ref : null;
62
+ }
63
+ function safeId(s) {
64
+ return String(s).replace(/\s+/g, "-").replace(/[^\w-]/g, "");
65
+ }
66
+ function isNumericField(data, field) {
67
+ return data.some((d) => d[field] != null && !isNaN(Number(d[field])));
68
+ }
69
+ function hasMultiplePerX(data, xField) {
70
+ var _a;
71
+ const counts = /* @__PURE__ */ new Map();
72
+ for (const d of data) {
73
+ const v = d[xField];
74
+ counts.set(v, ((_a = counts.get(v)) != null ? _a : 0) + 1);
75
+ }
76
+ return Array.from(counts.values()).some((c) => c > 1);
77
+ }
78
+ function resolveChartType(type, data, xField, yField, groupField) {
79
+ if (type && type !== "auto")
80
+ return type;
81
+ if (!data.length)
82
+ return "bar";
83
+ if (groupField && xField && yField) {
84
+ return hasMultiplePerX(data, xField) ? "stacked_bar" : "multiline";
85
+ }
86
+ if (xField && yField) {
87
+ if (isNumericField(data, xField) && isNumericField(data, yField))
88
+ return "cartesian";
89
+ if (!isNumericField(data, xField))
90
+ return "bar";
91
+ return "line";
92
+ }
93
+ if (xField) {
94
+ return isNumericField(data, xField) ? "line" : "bar";
95
+ }
96
+ return "bar";
97
+ }
98
+ var exitEdge = {
99
+ edgeId: "dn-exit",
100
+ edge: {
101
+ source: (_d, c) => c,
102
+ target: () => "",
103
+ navigationRules: ["exit"]
104
+ }
105
+ };
106
+ var baseNavRules = {
107
+ // Primary (x-axis) navigation
108
+ left: { key: "ArrowLeft", direction: "source" },
109
+ right: { key: "ArrowRight", direction: "target" },
110
+ // Secondary encoding (y-axis / group) navigation
111
+ up: { key: "ArrowUp", direction: "source" },
112
+ down: { key: "ArrowDown", direction: "target" },
113
+ // Tertiary encoding navigation ([ / ])
114
+ backward: { key: "BracketLeft", direction: "source" },
115
+ forward: { key: "BracketRight", direction: "target" },
116
+ // Hierarchy traversal
117
+ child: { key: "Enter", direction: "target" },
118
+ parent: { key: "Backspace", direction: "source" },
119
+ undo: { key: "Delete", direction: "source" },
120
+ // Encoding-specific parent shortcuts (W = x-axis parent, J = y-axis parent, \ = tertiary parent)
121
+ xParent: { key: "KeyW", direction: "source" },
122
+ yParent: { key: "KeyJ", direction: "source" },
123
+ zParent: { key: "Backslash", direction: "source" },
124
+ // Exit and help
125
+ exit: { key: "Escape", direction: "target" },
126
+ help: { key: "KeyY", direction: "target" }
127
+ };
128
+ function buildFlatStructure(data, xField, idField) {
129
+ const sorted = xField ? [...data].sort((a, b) => {
130
+ const av = a[xField];
131
+ const bv = b[xField];
132
+ if (typeof av === "number" && typeof bv === "number")
133
+ return av - bv;
134
+ return String(av) < String(bv) ? -1 : 1;
135
+ }) : [...data];
136
+ const ids = sorted.map(
137
+ (d, i) => idField && d[idField] != null ? safeId(d[idField]) : `dn-item-${i}`
138
+ );
139
+ const augmented = sorted.map((d, i) => __spreadProps(__spreadValues({}, d), { _dnId: ids[i] }));
140
+ return {
141
+ data: augmented,
142
+ idKey: "_dnId",
143
+ navigationRules: baseNavRules,
144
+ genericEdges: [
145
+ {
146
+ edgeId: "dn-right",
147
+ edge: {
148
+ source: (_d, c) => c,
149
+ target: (_d, c) => {
150
+ const i = ids.indexOf(c);
151
+ return ids[(i + 1) % ids.length];
152
+ },
153
+ navigationRules: ["right"]
154
+ }
155
+ },
156
+ {
157
+ edgeId: "dn-left",
158
+ edge: {
159
+ source: (_d, c) => {
160
+ const i = ids.indexOf(c);
161
+ return ids[(i - 1 + ids.length) % ids.length];
162
+ },
163
+ target: (_d, c) => c,
164
+ navigationRules: ["left"]
165
+ }
166
+ },
167
+ exitEdge
168
+ ]
169
+ };
170
+ }
171
+ function buildCrosslineStructure(data, xField, groupField) {
172
+ const augNavRules = __spreadValues({}, baseNavRules);
173
+ augNavRules.up = { key: "ArrowUp", direction: "target" };
174
+ augNavRules.down = { key: "ArrowDown", direction: "source" };
175
+ return {
176
+ data,
177
+ idKey: "id",
178
+ addIds: true,
179
+ navigationRules: augNavRules,
180
+ dimensions: {
181
+ values: [
182
+ {
183
+ dimensionKey: xField,
184
+ type: "categorical",
185
+ behavior: {
186
+ extents: "circular",
187
+ childmostNavigation: "across"
188
+ },
189
+ // left/right siblings at this dimension level; W drills back up
190
+ navigationRules: {
191
+ sibling_sibling: ["left", "right"],
192
+ parent_child: ["xParent", "child"]
193
+ }
194
+ },
195
+ {
196
+ dimensionKey: groupField,
197
+ type: "categorical",
198
+ behavior: {
199
+ extents: "circular",
200
+ childmostNavigation: "across"
201
+ },
202
+ // up/down siblings at this dimension level; J drills back up
203
+ navigationRules: {
204
+ sibling_sibling: ["up", "down"],
205
+ parent_child: ["yParent", "child"]
206
+ }
207
+ }
208
+ ]
209
+ },
210
+ genericEdges: [exitEdge]
211
+ };
212
+ }
213
+ function buildCartesianStructure(data, xField, yField, idField) {
214
+ const augmented = data.map((d, i) => {
215
+ const baseId = idField && d[idField] != null ? safeId(String(d[idField])) : d["id"] != null ? safeId(String(d["id"])) : `dn-pt-${i}`;
216
+ return __spreadProps(__spreadValues({}, d), { id: baseId });
217
+ });
218
+ const autoSubdivs = (_key, values) => Math.min(12, Math.max(3, Math.ceil(Math.sqrt(Object.keys(values).length))));
219
+ const augNavRules = __spreadValues({}, baseNavRules);
220
+ augNavRules.up = { key: "ArrowUp", direction: "target" };
221
+ augNavRules.down = { key: "ArrowDown", direction: "source" };
222
+ return {
223
+ data: augmented,
224
+ idKey: "id",
225
+ navigationRules: augNavRules,
226
+ dimensions: {
227
+ values: [
228
+ {
229
+ dimensionKey: xField,
230
+ type: "numerical",
231
+ behavior: {
232
+ extents: "terminal"
233
+ },
234
+ operations: { createNumericalSubdivisions: autoSubdivs },
235
+ navigationRules: {
236
+ sibling_sibling: ["left", "right"],
237
+ parent_child: ["xParent", "child"]
238
+ }
239
+ },
240
+ {
241
+ dimensionKey: yField,
242
+ type: "numerical",
243
+ behavior: {
244
+ extents: "terminal"
245
+ },
246
+ operations: {
247
+ createNumericalSubdivisions: autoSubdivs
248
+ // sortFunction: (a:any,b:any) => {
249
+ // console.log("sorting dim",yField,a,b)
250
+ // return b[yField] - a[yField]
251
+ // }
252
+ },
253
+ // divisionOptions: {
254
+ // sortFunction: (a:any,b:any) => {
255
+ // console.log("sorting div",yField,a,b)
256
+ // return b[yField] - a[yField]
257
+ // }
258
+ // },
259
+ navigationRules: {
260
+ sibling_sibling: ["down", "up"],
261
+ parent_child: ["yParent", "child"]
262
+ }
263
+ }
264
+ ]
265
+ },
266
+ genericEdges: [exitEdge]
267
+ };
268
+ }
269
+ function buildCartesianGroupedStructure(data, xField, yField, groupField, idField) {
270
+ const augmented = data.map((d, i) => {
271
+ const baseId = idField && d[idField] != null ? safeId(String(d[idField])) : d["id"] != null ? safeId(String(d["id"])) : `dn-pt-${i}`;
272
+ return __spreadProps(__spreadValues({}, d), { id: baseId });
273
+ });
274
+ const autoSubdivs = (_key, values) => Math.min(12, Math.max(3, Math.ceil(Math.sqrt(Object.keys(values).length))));
275
+ const augNavRules = __spreadValues({}, baseNavRules);
276
+ augNavRules.up = { key: "ArrowUp", direction: "target" };
277
+ augNavRules.down = { key: "ArrowDown", direction: "source" };
278
+ return {
279
+ data: augmented,
280
+ idKey: "id",
281
+ navigationRules: augNavRules,
282
+ dimensions: {
283
+ values: [
284
+ {
285
+ dimensionKey: xField,
286
+ type: "numerical",
287
+ behavior: { extents: "terminal" },
288
+ operations: { createNumericalSubdivisions: autoSubdivs },
289
+ navigationRules: {
290
+ sibling_sibling: ["left", "right"],
291
+ parent_child: ["xParent", "child"]
292
+ }
293
+ },
294
+ {
295
+ dimensionKey: yField,
296
+ type: "numerical",
297
+ behavior: { extents: "terminal" },
298
+ operations: { createNumericalSubdivisions: autoSubdivs },
299
+ navigationRules: {
300
+ sibling_sibling: ["down", "up"],
301
+ parent_child: ["yParent", "child"]
302
+ }
303
+ },
304
+ {
305
+ dimensionKey: groupField,
306
+ type: "categorical",
307
+ behavior: { extents: "circular" },
308
+ navigationRules: {
309
+ sibling_sibling: ["backward", "forward"],
310
+ parent_child: ["zParent", "child"]
311
+ }
312
+ }
313
+ ]
314
+ },
315
+ genericEdges: [exitEdge]
316
+ };
317
+ }
318
+ function buildDimensionStructure(data, dimensionKey, idField, compositeKey, compressSparseDivisions) {
319
+ let idKey;
320
+ let augmented = data;
321
+ if (compositeKey) {
322
+ augmented = data.map((d) => __spreadProps(__spreadValues({}, d), {
323
+ _dnId: `${safeId(d[dimensionKey])}-${safeId(d[compositeKey])}`
324
+ }));
325
+ idKey = "_dnId";
326
+ } else if (idField) {
327
+ idKey = idField;
328
+ } else {
329
+ idKey = dimensionKey;
330
+ }
331
+ return {
332
+ data: augmented,
333
+ idKey,
334
+ navigationRules: baseNavRules,
335
+ dimensions: {
336
+ values: [
337
+ {
338
+ dimensionKey,
339
+ type: "categorical",
340
+ behavior: { extents: "circular" },
341
+ // Explicitly name rules so edge tags match keys in baseNavRules.
342
+ // Without this, data-navigator auto-generates 'parent_<dimensionKey>'
343
+ // (e.g. 'parent_city'), which won't match the 'parent' rule.
344
+ operations: {
345
+ compressSparseDivisions: compressSparseDivisions || false
346
+ },
347
+ navigationRules: {
348
+ sibling_sibling: ["left", "right"],
349
+ parent_child: ["parent", "child"]
350
+ }
351
+ }
352
+ ]
353
+ },
354
+ genericEdges: [exitEdge]
355
+ };
356
+ }
357
+ var humanChartType = {
358
+ bar: "bar chart",
359
+ hbar: "horizontal bar chart",
360
+ scatter: "scatter plot",
361
+ cartesian: "scatter plot",
362
+ line: "line chart",
363
+ multiline: "multi-line chart",
364
+ crossline: "cross-navigable multi-line chart",
365
+ stacked_bar: "stacked bar chart"
366
+ };
367
+ function buildChartDescription(options, dimensionCount = 1) {
368
+ var _a;
369
+ const { data, type, xField, yField, groupField, title } = options;
370
+ const chartType = resolveChartType(type, data, xField, yField, groupField);
371
+ const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
372
+ const parts = [];
373
+ if (title) {
374
+ parts.push(title);
375
+ } else if (xField && yField) {
376
+ parts.push(`${cap(xField)} and ${cap(yField)}`);
377
+ } else if (xField) {
378
+ parts.push(cap(xField));
379
+ }
380
+ parts.push((_a = humanChartType[chartType]) != null ? _a : chartType);
381
+ if (xField) {
382
+ const axisType = isNumericField(data, xField) ? "numerical" : "categorical";
383
+ parts.push(`${xField} along x axis (${axisType})`);
384
+ }
385
+ if (yField) {
386
+ const axisType = isNumericField(data, yField) ? "numerical" : "categorical";
387
+ parts.push(`${yField} along y axis (${axisType})`);
388
+ }
389
+ if (groupField) {
390
+ parts.push(`grouped by ${groupField}`);
391
+ }
392
+ const total = data.length;
393
+ if (dimensionCount > 1) {
394
+ parts.push(`contains ${dimensionCount} dimensions and ${total} total data points`);
395
+ } else {
396
+ let divisionKey;
397
+ if (chartType === "bar" || chartType === "hbar")
398
+ divisionKey = xField;
399
+ else if (chartType === "stacked_bar")
400
+ divisionKey = xField;
401
+ else if (chartType === "multiline")
402
+ divisionKey = groupField;
403
+ if (divisionKey) {
404
+ const divisions = new Set(data.map((d) => d[divisionKey])).size;
405
+ if (divisions !== total) {
406
+ parts.push(`contains ${divisions} divisions and ${total} total data points`);
407
+ } else {
408
+ parts.push(`contains ${total} total data points`);
409
+ }
410
+ } else {
411
+ parts.push(`contains ${total} total data points`);
412
+ }
413
+ }
414
+ return parts.map(cap).join(". ") + ".";
415
+ }
416
+ function buildStructureOptions(options) {
417
+ var _a;
418
+ const { data, type, xField, yField, groupField, idField, structureOptions = {} } = options;
419
+ const chartType = resolveChartType(type, data, xField, yField, groupField);
420
+ let base;
421
+ switch (chartType) {
422
+ case "scatter":
423
+ case "line":
424
+ base = buildFlatStructure(data, xField, idField);
425
+ break;
426
+ case "bar":
427
+ case "hbar": {
428
+ const catField = xField || Object.keys((_a = data[0]) != null ? _a : {}).find((k) => !isNumericField(data, k)) || "x";
429
+ base = buildDimensionStructure(data, catField, idField, "", options.compressSparseDivisions);
430
+ break;
431
+ }
432
+ case "stacked_bar": {
433
+ const stackX = xField || "x";
434
+ const stackGroup = groupField || "group";
435
+ base = buildCrosslineStructure(data, stackX, stackGroup);
436
+ break;
437
+ }
438
+ case "multiline": {
439
+ const lineGroup = groupField || "series";
440
+ base = buildDimensionStructure(data, lineGroup, idField, xField, options.compressSparseDivisions);
441
+ break;
442
+ }
443
+ case "cartesian": {
444
+ const cartX = xField || "x";
445
+ const cartY = yField || "y";
446
+ if (groupField) {
447
+ base = buildCartesianGroupedStructure(data, cartX, cartY, groupField, idField);
448
+ } else {
449
+ base = buildCartesianStructure(data, cartX, cartY, idField);
450
+ }
451
+ break;
452
+ }
453
+ case "crossline": {
454
+ const crossX = xField || "x";
455
+ const crossGroup = groupField || "series";
456
+ base = buildCrosslineStructure(data, crossX, crossGroup);
457
+ break;
458
+ }
459
+ default:
460
+ base = buildFlatStructure(data, xField, idField);
461
+ }
462
+ return __spreadValues(__spreadValues({}, base), structureOptions);
463
+ }
464
+ function buildCommandLabels(options) {
465
+ const { data, type, xField, yField, groupField, commandLabels = {} } = options;
466
+ const chartType = resolveChartType(type, data, xField, yField, groupField);
467
+ const auto = {};
468
+ switch (chartType) {
469
+ case "bar":
470
+ case "hbar":
471
+ auto.left = `Move to previous ${xField != null ? xField : "category"}`;
472
+ auto.right = `Move to next ${xField != null ? xField : "category"}`;
473
+ auto.child = `Drill into ${xField != null ? xField : "category"} detail`;
474
+ auto.parent = "Go back up";
475
+ auto.undo = "Go back up";
476
+ break;
477
+ case "scatter":
478
+ case "line":
479
+ auto.left = `Move to previous ${xField != null ? xField : "point"}`;
480
+ auto.right = `Move to next ${xField != null ? xField : "point"}`;
481
+ break;
482
+ case "stacked_bar":
483
+ auto.left = `Move to previous ${xField != null ? xField : "category"}`;
484
+ auto.right = `Move to next ${xField != null ? xField : "category"}`;
485
+ auto.up = `Move to previous ${groupField != null ? groupField : "group"}`;
486
+ auto.down = `Move to next ${groupField != null ? groupField : "group"}`;
487
+ auto.child = "Drill in";
488
+ auto.xParent = `Go up to ${xField != null ? xField : "category"} level`;
489
+ auto.yParent = `Go up to ${groupField != null ? groupField : "group"} level`;
490
+ break;
491
+ case "multiline":
492
+ auto.left = `Move to previous ${groupField != null ? groupField : "series"}`;
493
+ auto.right = `Move to next ${groupField != null ? groupField : "series"}`;
494
+ auto.child = `Drill into ${groupField != null ? groupField : "series"} data`;
495
+ auto.parent = "Go back up";
496
+ auto.undo = "Go back up";
497
+ break;
498
+ case "cartesian":
499
+ auto.left = `Move to previous ${xField != null ? xField : "x"} range`;
500
+ auto.right = `Move to next ${xField != null ? xField : "x"} range`;
501
+ auto.up = `Move to higher ${yField != null ? yField : "y"} range`;
502
+ auto.down = `Move to lower ${yField != null ? yField : "y"} range`;
503
+ auto.child = "Drill in";
504
+ auto.xParent = `Go up to ${xField != null ? xField : "x"} level`;
505
+ auto.yParent = `Go up to ${yField != null ? yField : "y"} level`;
506
+ if (groupField) {
507
+ auto.backward = `Move to previous ${groupField} group`;
508
+ auto.forward = `Move to next ${groupField} group`;
509
+ auto.zParent = `Go up to ${groupField} level`;
510
+ }
511
+ break;
512
+ case "crossline":
513
+ auto.left = `Move to previous ${xField != null ? xField : "x-axis point"}`;
514
+ auto.right = `Move to next ${xField != null ? xField : "x-axis point"}`;
515
+ auto.up = `Move to previous ${groupField != null ? groupField : "series"}`;
516
+ auto.down = `Move to next ${groupField != null ? groupField : "series"}`;
517
+ auto.child = "Drill in";
518
+ auto.xParent = `Go up to ${xField != null ? xField : "x-axis"} level`;
519
+ auto.yParent = `Go up to ${groupField != null ? groupField : "series"} level`;
520
+ break;
521
+ }
522
+ return __spreadValues(__spreadValues({}, auto), commandLabels);
523
+ }
524
+ function buildNodeLabel(node) {
525
+ const data = node.data;
526
+ if (!data)
527
+ return String(node.id);
528
+ if (typeof data.dimensionKey === "string" && data.divisions != null) {
529
+ const divCount = Object.keys(data.divisions).length;
530
+ return `${data.dimensionKey} dimension. Contains ${divCount} division${divCount !== 1 ? "s" : ""}.`;
531
+ }
532
+ if (data.values != null && typeof data.values === "object" && !Array.isArray(data.values)) {
533
+ const dimKey = node.derivedNode;
534
+ const dimValue = dimKey ? data[dimKey] : null;
535
+ if (dimValue != null) {
536
+ const childCount = Object.keys(data.values).length;
537
+ return `${dimValue}. Contains ${childCount} data point${childCount !== 1 ? "s" : ""}.`;
538
+ }
539
+ return String(node.id);
540
+ }
541
+ const parts = Object.entries(data).filter(
542
+ ([k, v]) => !k.startsWith("_") && v != null && typeof v !== "object" && typeof v !== "function"
543
+ ).map(([k, v]) => `${k}: ${v}`);
544
+ return parts.length > 0 ? parts.join(". ") + "." : String(node.id);
545
+ }
546
+ function prepareNodeSemantics(structure) {
547
+ var _a, _b;
548
+ for (const node of Object.values(structure.nodes)) {
549
+ const n = node;
550
+ if (!((_a = n.semantics) == null ? void 0 : _a.label)) {
551
+ n.semantics = __spreadProps(__spreadValues({}, (_b = n.semantics) != null ? _b : {}), { label: buildNodeLabel(n) });
552
+ }
553
+ }
554
+ }
555
+
556
+ // src/index.ts
557
+ function resolveChatContainer(options, plotEl) {
558
+ var _a, _b;
559
+ if (options.chatContainer) {
560
+ const el = resolveEl(options.chatContainer);
561
+ if (el)
562
+ return { el, owned: false };
563
+ }
564
+ const div = document.createElement("div");
565
+ div.className = "dn-bokeh-wrapper";
566
+ (_b = (_a = plotEl.parentElement) == null ? void 0 : _a.insertBefore(div, plotEl.nextSibling)) != null ? _b : document.body.appendChild(div);
567
+ return { el: div, owned: true };
568
+ }
569
+ function setupKeyboardMode(structure, plotEl, options, entryPoint) {
570
+ var _a, _b;
571
+ const { onNavigate, onExit, renderingOptions = {} } = options;
572
+ const width = plotEl.clientWidth || 400;
573
+ const height = plotEl.clientHeight || 300;
574
+ let current = null;
575
+ let previous = null;
576
+ const resolvedEntry = entryPoint != null ? entryPoint : Object.keys(structure.nodes)[0];
577
+ const suffixId = `dn-bokeh-${Math.random().toString(36).slice(2, 6)}`;
578
+ const rendering = import_data_navigator.default.rendering({
579
+ elementData: structure.nodes,
580
+ defaults: __spreadValues({
581
+ cssClass: "dn-node",
582
+ spatialProperties: { x: 0, y: 0, width, height }
583
+ }, renderingOptions.defaults),
584
+ suffixId,
585
+ root: {
586
+ id: typeof options.plotContainer === "string" ? options.plotContainer : options.plotContainer.id || "dn-bokeh-root",
587
+ description: "Accessible data navigation",
588
+ width: "100%",
589
+ height: 0
590
+ },
591
+ entryButton: { include: true, callbacks: { click: enter } },
592
+ exitElement: { include: true }
593
+ });
594
+ rendering.initialize();
595
+ if (rendering.entryButton) {
596
+ rendering.entryButton.addEventListener("keydown", (e) => {
597
+ if (e.key === "Enter" || e.key === " ") {
598
+ e.preventDefault();
599
+ if (!current)
600
+ enter();
601
+ }
602
+ });
603
+ }
604
+ const input = import_data_navigator.default.input({
605
+ structure,
606
+ navigationRules: (_a = structure.navigationRules) != null ? _a : {},
607
+ entryPoint: resolvedEntry,
608
+ exitPoint: (_b = rendering.exitElement) == null ? void 0 : _b.id
609
+ });
610
+ function enter() {
611
+ const node = input.enter();
612
+ if (!node)
613
+ return;
614
+ navigate(node);
615
+ }
616
+ function navigate(node) {
617
+ if (!node.renderId)
618
+ node.renderId = node.id;
619
+ if (!node.spatialProperties) {
620
+ node.spatialProperties = { x: 0, y: 0, width, height };
621
+ }
622
+ if (previous)
623
+ rendering.remove(previous);
624
+ const el = rendering.render({ renderId: node.renderId, datum: node });
625
+ if (!el)
626
+ return;
627
+ el.style.width = "100%";
628
+ el.style.height = "100%";
629
+ el.style.top = "0";
630
+ el.style.left = "0";
631
+ el.addEventListener("keydown", (e) => {
632
+ const direction = input.keydownValidator(e);
633
+ if (direction) {
634
+ e.preventDefault();
635
+ if (direction === "exit") {
636
+ if (rendering.exitElement) {
637
+ rendering.exitElement.style.display = "block";
638
+ input.focus(rendering.exitElement.id);
639
+ }
640
+ } else {
641
+ const next = input.move(current, direction);
642
+ if (next)
643
+ navigate(next);
644
+ }
645
+ }
646
+ });
647
+ el.addEventListener("focus", () => {
648
+ if (onNavigate)
649
+ onNavigate(node);
650
+ });
651
+ input.focus(node.renderId);
652
+ previous = current;
653
+ current = node.id;
654
+ }
655
+ if (rendering.exitElement) {
656
+ rendering.exitElement.addEventListener("focus", () => {
657
+ if (current)
658
+ rendering.remove(current);
659
+ current = null;
660
+ if (onExit)
661
+ onExit();
662
+ });
663
+ }
664
+ return {
665
+ destroy() {
666
+ var _a2, _b2, _c, _d;
667
+ try {
668
+ (_b2 = (_a2 = rendering.wrapper) == null ? void 0 : _a2.remove) == null ? void 0 : _b2.call(_a2);
669
+ } catch (_) {
670
+ }
671
+ try {
672
+ (_d = (_c = rendering.exitElement) == null ? void 0 : _c.remove) == null ? void 0 : _d.call(_c);
673
+ } catch (_) {
674
+ }
675
+ },
676
+ getCurrentNode() {
677
+ var _a2;
678
+ return current ? (_a2 = structure.nodes[current]) != null ? _a2 : null : null;
679
+ }
680
+ };
681
+ }
682
+ function addDataNavigator(options) {
683
+ var _a, _b, _c, _d, _e;
684
+ const { mode = "text", onNavigate, onExit, onClick, onHover, llm } = options;
685
+ const plotEl = resolveEl(options.plotContainer);
686
+ if (!plotEl) {
687
+ throw new Error(
688
+ `@data-navigator/bokeh-wrapper: plotContainer "${options.plotContainer}" not found.`
689
+ );
690
+ }
691
+ const didSetInert = mode === "text";
692
+ if (didSetInert) {
693
+ plotEl.setAttribute("inert", "true");
694
+ }
695
+ const structOpts = buildStructureOptions(options);
696
+ const dimCount = (_c = (_b = (_a = structOpts.dimensions) == null ? void 0 : _a.values) == null ? void 0 : _b.length) != null ? _c : 0;
697
+ const resolveDescription = () => {
698
+ if (typeof options.describeRoot === "function")
699
+ return options.describeRoot(options);
700
+ if (typeof options.describeRoot === "string")
701
+ return options.describeRoot;
702
+ return buildChartDescription(options, dimCount);
703
+ };
704
+ if (dimCount > 1) {
705
+ const dims = structOpts.dimensions;
706
+ if (!dims.parentOptions)
707
+ dims.parentOptions = {};
708
+ if (!dims.parentOptions.addLevel0) {
709
+ const plotId = typeof options.plotContainer === "string" ? options.plotContainer.replace(/[^a-zA-Z0-9_-]/g, "") : "dn";
710
+ dims.parentOptions.addLevel0 = {
711
+ id: `${plotId}-chart-root`,
712
+ edges: [],
713
+ semantics: { label: resolveDescription() }
714
+ };
715
+ }
716
+ }
717
+ const structure = import_data_navigator.default.structure(structOpts);
718
+ if (dimCount === 1 && structure.dimensions) {
719
+ const desc = resolveDescription();
720
+ for (const dim of Object.values(structure.dimensions)) {
721
+ const node = structure.nodes[dim.nodeId];
722
+ if (node)
723
+ node.semantics = { label: desc };
724
+ }
725
+ }
726
+ prepareNodeSemantics(structure);
727
+ const level0Node = Object.values(structure.nodes).find((n) => n.dimensionLevel === 0);
728
+ const resolvedEntryPoint = (_e = level0Node == null ? void 0 : level0Node.id) != null ? _e : structure.dimensions ? (_d = structure.dimensions[Object.keys(structure.dimensions)[0]]) == null ? void 0 : _d.nodeId : void 0;
729
+ const cleanups = [];
730
+ let textChatInstance = null;
731
+ if (mode === "text" || mode === "both") {
732
+ const { el: chatEl, owned } = resolveChatContainer(options, plotEl);
733
+ textChatInstance = import_data_navigator.default.textChat({
734
+ structure,
735
+ container: chatEl,
736
+ entryPoint: resolvedEntryPoint,
737
+ data: options.data,
738
+ commandLabels: buildCommandLabels(options),
739
+ onNavigate,
740
+ onExit,
741
+ onClick,
742
+ onHover,
743
+ llm
744
+ });
745
+ if (owned) {
746
+ cleanups.push(() => {
747
+ var _a2;
748
+ try {
749
+ (_a2 = chatEl.parentElement) == null ? void 0 : _a2.removeChild(chatEl);
750
+ } catch (_) {
751
+ }
752
+ });
753
+ }
754
+ }
755
+ let keyboardMode = null;
756
+ if (mode === "keyboard" || mode === "both") {
757
+ keyboardMode = setupKeyboardMode(structure, plotEl, options, resolvedEntryPoint);
758
+ cleanups.push(() => keyboardMode == null ? void 0 : keyboardMode.destroy());
759
+ }
760
+ return {
761
+ destroy() {
762
+ textChatInstance == null ? void 0 : textChatInstance.destroy();
763
+ for (const cleanup of cleanups)
764
+ cleanup();
765
+ if (didSetInert)
766
+ plotEl.removeAttribute("inert");
767
+ },
768
+ getCurrentNode() {
769
+ var _a2, _b2;
770
+ return (_b2 = (_a2 = textChatInstance == null ? void 0 : textChatInstance.getCurrentNode()) != null ? _a2 : keyboardMode == null ? void 0 : keyboardMode.getCurrentNode()) != null ? _b2 : null;
771
+ },
772
+ structure
773
+ };
774
+ }
775
+ // Annotate the CommonJS export names for ESM import in node:
776
+ 0 && (module.exports = {
777
+ addDataNavigator,
778
+ buildChartDescription,
779
+ buildNodeLabel,
780
+ prepareNodeSemantics
781
+ });