@behindthemusictree/genre-tree-view 5.0.0 → 6.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 CHANGED
@@ -51,7 +51,6 @@ module.exports = __toCommonJS(src_exports);
51
51
  // src/GenreTree.tsx
52
52
  var import_react8 = require("react");
53
53
  var d3 = __toESM(require("d3"), 1);
54
- var import_md6 = require("react-icons/md");
55
54
 
56
55
  // src/NodeHelper.tsx
57
56
  var import_server = __toESM(require("react-dom/server"), 1);
@@ -75,6 +74,9 @@ var MENU_WIDTH = 130;
75
74
  var PER_TREE_ACCENT_DOT = true;
76
75
  var ROOT_TINT_RATIO = 0.08;
77
76
  var POP_SECTOR_TINT_RATIO = 0.8;
77
+ var UNDERGROUND_DARKEN_RATIO = 0.62;
78
+ var UNDERGROUND_SECTOR_FILL_OPACITY = 0.15;
79
+ var SCENE_SECTOR_OUTER_RADIUS = 1e5;
78
80
  var CENTER_NODE_NAME = "Mainstream Pop";
79
81
  var TREE_COLORS = [
80
82
  "#4F46E5",
@@ -156,6 +158,11 @@ function isMainstreamPopLineage(index, nodeId) {
156
158
  const topRoot = index.ancestorsOf(nodeId)[0] ?? index.byId.get(nodeId);
157
159
  return topRoot?.name === CENTER_NODE_NAME;
158
160
  }
161
+ function shadeSurface(hex, ratio) {
162
+ const channel = (offset) => parseInt(hex.slice(offset, offset + 2), 16);
163
+ const toHex = (value) => Math.round(value * ratio).toString(16).padStart(2, "0");
164
+ return `#${toHex(channel(1))}${toHex(channel(3))}${toHex(channel(5))}`;
165
+ }
159
166
  function getGenreNodeStyle(index, nodeId) {
160
167
  if (isMainstreamPopLineage(index, nodeId)) {
161
168
  return { fill: tintSurface("#ffffff", POP_SECTOR_TINT_RATIO), textColor: TEXT_COLOR };
@@ -244,10 +251,13 @@ function calculateRootLabelFontSize(layoutFontSize, zoomScale) {
244
251
  return Math.max(layoutFontSize, ROOT_LABEL_MIN_SCREEN_PX / zoomScale);
245
252
  }
246
253
  var NODE_DOT_MIN_RADIUS = 3;
247
- var NODE_DOT_MAX_RADIUS = 50;
254
+ var NODE_DOT_MAX_RADIUS = 24;
248
255
  var NODE_DOT_LABEL_GAP = 6;
256
+ var NODE_LAYOUT_SLOT_RADIUS = 8;
249
257
  function calculateNodeDotRadius(itemCount, range) {
250
- return NODE_DOT_MIN_RADIUS + logarithmicPosition(itemCount, range) * (NODE_DOT_MAX_RADIUS - NODE_DOT_MIN_RADIUS);
258
+ const minArea = NODE_DOT_MIN_RADIUS ** 2;
259
+ const maxArea = NODE_DOT_MAX_RADIUS ** 2;
260
+ return Math.sqrt(minArea + logarithmicPosition(itemCount, range) * (maxArea - minArea));
251
261
  }
252
262
  var WHEEL_RADIUS = 260;
253
263
  var WHEEL_VIEWPORT_HEIGHT = WHEEL_RADIUS * 2 + MAX_NODE_HEIGHT / 2;
@@ -634,6 +644,61 @@ function renderTree(d3Lib, svgRef, treeData, svgWidth, svgHeight, reparentingNod
634
644
  return svg;
635
645
  }
636
646
 
647
+ // src/FloatingControls.tsx
648
+ var import_md = require("react-icons/md");
649
+
650
+ // src/BusyIndicator.tsx
651
+ var import_jsx_runtime2 = require("react/jsx-runtime");
652
+ function BusyIndicator() {
653
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "gtv-busy", role: "status", "aria-live": "polite", children: [
654
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gtv-busy-ring", "aria-hidden": "true" }),
655
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "gtv-skeleton-sr-only", children: "Updating genre tree\u2026" })
656
+ ] });
657
+ }
658
+
659
+ // src/FloatingControls.tsx
660
+ var import_jsx_runtime3 = require("react/jsx-runtime");
661
+ function FloatingControls({ panZoom, onFit, orientation, leading, busy }) {
662
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
663
+ "div",
664
+ {
665
+ className: [
666
+ "gtv-wheel-floating-controls",
667
+ orientation === "horizontal" && "gtv-wheel-floating-controls--horizontal"
668
+ ].filter(Boolean).join(" "),
669
+ children: [
670
+ busy && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BusyIndicator, {}),
671
+ leading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "gtv-floating-controls-leading", children: leading }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "gtv-zoom-controls", children: [
673
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
674
+ "button",
675
+ {
676
+ type: "button",
677
+ className: ["gtv-zoom-btn", !panZoom.canZoomIn && "gtv-zoom-btn--disabled"].filter(Boolean).join(" "),
678
+ disabled: !panZoom.canZoomIn,
679
+ onClick: panZoom.zoomIn,
680
+ "aria-label": "Zoom in",
681
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_md.MdZoomIn, { className: "gtv-icon", size: 18 })
682
+ }
683
+ ),
684
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
685
+ "button",
686
+ {
687
+ type: "button",
688
+ className: ["gtv-zoom-btn", !panZoom.canZoomOut && "gtv-zoom-btn--disabled"].filter(Boolean).join(" "),
689
+ disabled: !panZoom.canZoomOut,
690
+ onClick: panZoom.zoomOut,
691
+ "aria-label": "Zoom out",
692
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_md.MdZoomOut, { className: "gtv-icon", size: 18 })
693
+ }
694
+ ),
695
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "gtv-zoom-btn", onClick: onFit, "aria-label": "Fit to frame", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_md.MdFitScreen, { className: "gtv-icon", size: 18 }) })
696
+ ] })
697
+ ]
698
+ }
699
+ );
700
+ }
701
+
637
702
  // src/use-pan-zoom.ts
638
703
  var import_react = require("react");
639
704
 
@@ -1270,13 +1335,13 @@ function useTreeIndex(nodes) {
1270
1335
 
1271
1336
  // src/InfoPanel.tsx
1272
1337
  var import_react6 = require("react");
1273
- var import_md4 = require("react-icons/md");
1338
+ var import_md5 = require("react-icons/md");
1274
1339
 
1275
1340
  // src/InfoPanelExpandButton.tsx
1276
- var import_md = require("react-icons/md");
1277
- var import_jsx_runtime2 = require("react/jsx-runtime");
1341
+ var import_md2 = require("react-icons/md");
1342
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1278
1343
  function InfoPanelExpandButton({ expanded, onExpandedChange, style }) {
1279
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1344
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1280
1345
  "button",
1281
1346
  {
1282
1347
  type: "button",
@@ -1284,19 +1349,19 @@ function InfoPanelExpandButton({ expanded, onExpandedChange, style }) {
1284
1349
  style,
1285
1350
  onClick: () => onExpandedChange(!expanded),
1286
1351
  "aria-label": expanded ? "Collapse panel" : "Expand panel",
1287
- children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_md.MdCloseFullscreen, { className: "gtv-icon", size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_md.MdOpenInFull, { className: "gtv-icon", size: 16 })
1352
+ children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_md2.MdCloseFullscreen, { className: "gtv-icon", size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_md2.MdOpenInFull, { className: "gtv-icon", size: 16 })
1288
1353
  }
1289
1354
  );
1290
1355
  }
1291
1356
 
1292
1357
  // src/InfoPanelSection.tsx
1293
1358
  var import_react4 = require("react");
1294
- var import_md2 = require("react-icons/md");
1295
- var import_jsx_runtime3 = require("react/jsx-runtime");
1359
+ var import_md3 = require("react-icons/md");
1360
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1296
1361
  function InfoPanelSection({ title, children }) {
1297
1362
  const [open, setOpen] = (0, import_react4.useState)(true);
1298
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "gtv-info-panel-children", children: [
1299
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1363
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "gtv-info-panel-children", children: [
1364
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1300
1365
  "button",
1301
1366
  {
1302
1367
  type: "button",
@@ -1304,8 +1369,8 @@ function InfoPanelSection({ title, children }) {
1304
1369
  "aria-expanded": open,
1305
1370
  onClick: () => setOpen((isOpen) => !isOpen),
1306
1371
  children: [
1307
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "gtv-info-panel-children-title", children: title }),
1308
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_md2.MdExpandMore, { className: "gtv-icon gtv-info-panel-section-chevron", size: 16 })
1372
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "gtv-info-panel-children-title", children: title }),
1373
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_md3.MdExpandMore, { className: "gtv-icon gtv-info-panel-section-chevron", size: 16 })
1309
1374
  ]
1310
1375
  }
1311
1376
  ),
@@ -1315,10 +1380,10 @@ function InfoPanelSection({ title, children }) {
1315
1380
 
1316
1381
  // src/NodeToolbar.tsx
1317
1382
  var import_react5 = require("react");
1318
- var import_md3 = require("react-icons/md");
1383
+ var import_md4 = require("react-icons/md");
1319
1384
  var import_fa = require("react-icons/fa");
1320
1385
  var import_pi2 = require("react-icons/pi");
1321
- var import_jsx_runtime4 = require("react/jsx-runtime");
1386
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1322
1387
  function NodeToolbar({
1323
1388
  node,
1324
1389
  playingNodeId,
@@ -1339,9 +1404,9 @@ function NodeToolbar({
1339
1404
  const isPlayable = node.itemCount > 0;
1340
1405
  const isThisPlaying = playingNodeId === node.id;
1341
1406
  const playLabel = isThisPlaying && playState === "playing" ? "Pause" : isThisPlaying && playState === "loading" ? "Loading..." : "Play";
1342
- const playIcon = isThisPlaying && playState === "playing" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa.FaPause, { className: "gtv-icon", size: "0.6em" }) : isThisPlaying && playState === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa.FaSpinner, { className: "gtv-icon gtv-icon--spin", size: "0.6em" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa.FaPlay, { className: "gtv-icon", size: "0.6em" });
1343
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: ["gtv-toolbar", className].filter(Boolean).join(" "), children: [
1344
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1407
+ const playIcon = isThisPlaying && playState === "playing" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaPause, { className: "gtv-icon", size: "0.6em" }) : isThisPlaying && playState === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaSpinner, { className: "gtv-icon gtv-icon--spin", size: "0.6em" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaPlay, { className: "gtv-icon", size: "0.6em" });
1408
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: ["gtv-toolbar", className].filter(Boolean).join(" "), children: [
1409
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1345
1410
  "button",
1346
1411
  {
1347
1412
  type: "button",
@@ -1353,8 +1418,8 @@ function NodeToolbar({
1353
1418
  children: playIcon
1354
1419
  }
1355
1420
  ),
1356
- isActionable && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1357
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1421
+ isActionable && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1422
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1358
1423
  "button",
1359
1424
  {
1360
1425
  type: "button",
@@ -1362,13 +1427,13 @@ function NodeToolbar({
1362
1427
  title: "Add sub-genre",
1363
1428
  "aria-label": "Add sub-genre",
1364
1429
  onClick: () => onAddChild?.(node.id),
1365
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa.FaPlus, { className: "gtv-icon", size: "0.6em" })
1430
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaPlus, { className: "gtv-icon", size: "0.6em" })
1366
1431
  }
1367
1432
  ),
1368
1433
  primaryExtraActions.map((action) => {
1369
1434
  const enabled = action.enabled ? action.enabled(node) : true;
1370
1435
  const label = action.label(node);
1371
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1436
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1372
1437
  "button",
1373
1438
  {
1374
1439
  type: "button",
@@ -1382,8 +1447,8 @@ function NodeToolbar({
1382
1447
  action.key
1383
1448
  );
1384
1449
  }),
1385
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gtv-toolbar-overflow", children: [
1386
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1450
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "gtv-toolbar-overflow", children: [
1451
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1387
1452
  "button",
1388
1453
  {
1389
1454
  type: "button",
@@ -1393,11 +1458,11 @@ function NodeToolbar({
1393
1458
  "aria-haspopup": "menu",
1394
1459
  "aria-expanded": menuOpen,
1395
1460
  onClick: () => setMenuOpen((open) => !open),
1396
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_md3.MdMoreVert, { size: "0.6em" })
1461
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_md4.MdMoreVert, { size: "0.6em" })
1397
1462
  }
1398
1463
  ),
1399
- menuOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "gtv-menu-card gtv-toolbar-overflow-menu", children: [
1400
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1464
+ menuOpen && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "gtv-menu-card gtv-toolbar-overflow-menu", children: [
1465
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1401
1466
  "button",
1402
1467
  {
1403
1468
  type: "button",
@@ -1407,12 +1472,12 @@ function NodeToolbar({
1407
1472
  onRenameRequest?.(node);
1408
1473
  },
1409
1474
  children: [
1410
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_md3.MdModeEdit, { className: "gtv-icon", size: 13 }) }),
1411
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-label", children: "Rename" })
1475
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_md4.MdModeEdit, { className: "gtv-icon", size: 13 }) }),
1476
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-label", children: "Rename" })
1412
1477
  ]
1413
1478
  }
1414
1479
  ),
1415
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1480
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1416
1481
  "button",
1417
1482
  {
1418
1483
  type: "button",
@@ -1422,14 +1487,14 @@ function NodeToolbar({
1422
1487
  onReparentRequest?.(node);
1423
1488
  },
1424
1489
  children: [
1425
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_pi2.PiGraphFill, { className: "gtv-icon", size: 13 }) }),
1426
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-label", children: "Change parent" })
1490
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_pi2.PiGraphFill, { className: "gtv-icon", size: 13 }) }),
1491
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-label", children: "Change parent" })
1427
1492
  ]
1428
1493
  }
1429
1494
  ),
1430
1495
  overflowExtraActions.map((action) => {
1431
1496
  const enabled = action.enabled ? action.enabled(node) : true;
1432
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1497
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1433
1498
  "button",
1434
1499
  {
1435
1500
  type: "button",
@@ -1440,15 +1505,15 @@ function NodeToolbar({
1440
1505
  action.onClick(event, node);
1441
1506
  },
1442
1507
  children: [
1443
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-icon", children: action.icon(node) }),
1444
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-label", children: action.label(node) })
1508
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-icon", children: action.icon(node) }),
1509
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-label", children: action.label(node) })
1445
1510
  ]
1446
1511
  },
1447
1512
  action.key
1448
1513
  );
1449
1514
  }),
1450
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "gtv-menu-divider" }),
1451
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1515
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "gtv-menu-divider" }),
1516
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1452
1517
  "button",
1453
1518
  {
1454
1519
  type: "button",
@@ -1458,8 +1523,8 @@ function NodeToolbar({
1458
1523
  onDeleteRequest?.(node);
1459
1524
  },
1460
1525
  children: [
1461
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_fa.FaTrashAlt, { className: "gtv-icon", size: 13 }) }),
1462
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "gtv-menu-row-label", children: "Delete" })
1526
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_fa.FaTrashAlt, { className: "gtv-icon", size: 13 }) }),
1527
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "gtv-menu-row-label", children: "Delete" })
1463
1528
  ]
1464
1529
  }
1465
1530
  )
@@ -1470,7 +1535,7 @@ function NodeToolbar({
1470
1535
  }
1471
1536
 
1472
1537
  // src/InfoPanel.tsx
1473
- var import_jsx_runtime5 = require("react/jsx-runtime");
1538
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1474
1539
  var MINI_NODE_MIN_DIAMETER = 14;
1475
1540
  var MINI_NODE_MAX_DIAMETER = 32;
1476
1541
  var MINI_NODE_MIN_FONT_SIZE = 10;
@@ -1502,10 +1567,6 @@ function InfoPanel({
1502
1567
  onZoomIn,
1503
1568
  actions,
1504
1569
  readOnly = false,
1505
- editMode = false,
1506
- onEditModeChange,
1507
- renderTitleEditor,
1508
- renderParentsEditor,
1509
1570
  renderDangerZone,
1510
1571
  expanded = false,
1511
1572
  onExpandedChange,
@@ -1516,10 +1577,8 @@ function InfoPanel({
1516
1577
  (0, import_react6.useEffect)(() => {
1517
1578
  panelRef.current?.toggleAttribute("inert", collapsed);
1518
1579
  }, [collapsed]);
1519
- const editing = editMode && !readOnly;
1520
- const shownParent = editing && renderParentsEditor ? null : parentNode;
1521
1580
  const miniTreeRange = getItemCountRange([
1522
- ...shownParent ? [shownParent.node] : [],
1581
+ ...parentNode ? [parentNode.node] : [],
1523
1582
  node,
1524
1583
  ...childNodes.map((child) => child.node)
1525
1584
  ]);
@@ -1533,9 +1592,9 @@ function InfoPanel({
1533
1592
  };
1534
1593
  const miniNodeContent = (target, background, range) => {
1535
1594
  const { diameter, fontSize } = miniNodeSize(target, range);
1536
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1537
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "gtv-info-panel-mini-dot", style: { background, width: diameter, height: diameter } }),
1538
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "gtv-info-panel-mini-label", style: { fontSize }, children: target.name })
1595
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1596
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "gtv-info-panel-mini-dot", style: { background, width: diameter, height: diameter } }),
1597
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "gtv-info-panel-mini-label", style: { fontSize }, children: target.name })
1539
1598
  ] });
1540
1599
  };
1541
1600
  const sortedChildNodes = [...childNodes].sort((a, b) => b.node.itemCount - a.node.itemCount);
@@ -1548,9 +1607,9 @@ function InfoPanel({
1548
1607
  document.addEventListener("keydown", onKeyDown);
1549
1608
  return () => document.removeEventListener("keydown", onKeyDown);
1550
1609
  }, [expanded, onExpandedChange]);
1551
- const dangerZone = editing && renderDangerZone && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "gtv-info-panel-danger-zone", children: renderDangerZone(node) });
1552
- const toolbar = actions ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NodeToolbar, { node, className: "gtv-info-panel-toolbar", ...actions }, node.id) : null;
1553
- const ancestors = ancestorNodes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InfoPanelSection, { title: "Ancestors", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "gtv-info-panel-children-list gtv-info-panel-ancestors-list", children: ancestorNodes.map(({ node: ancestor, fill: fill2 }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1610
+ const dangerZone = !readOnly && renderDangerZone && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gtv-info-panel-danger-zone", children: renderDangerZone(node) });
1611
+ const toolbar = actions ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NodeToolbar, { node, className: "gtv-info-panel-toolbar", ...actions }, node.id) : null;
1612
+ const ancestors = ancestorNodes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(InfoPanelSection, { title: "Ancestors", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: "gtv-info-panel-children-list gtv-info-panel-ancestors-list", children: ancestorNodes.map(({ node: ancestor, fill: fill2 }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1554
1613
  "button",
1555
1614
  {
1556
1615
  type: "button",
@@ -1560,76 +1619,73 @@ function InfoPanel({
1560
1619
  children: miniNodeContent(ancestor, fill2, ancestorRange)
1561
1620
  }
1562
1621
  ) }, ancestor.id)) }) }) : null;
1563
- const family = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1564
- editing && renderParentsEditor && renderParentsEditor(node),
1565
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InfoPanelSection, { title: "Family", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "gtv-info-panel-mini-tree", children: [
1566
- shownParent && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "gtv-info-panel-mini-tree-parent", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1622
+ const family = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(InfoPanelSection, { title: "Family", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gtv-info-panel-mini-tree", children: [
1623
+ parentNode && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gtv-info-panel-mini-tree-parent", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1624
+ "button",
1625
+ {
1626
+ type: "button",
1627
+ className: "gtv-info-panel-child",
1628
+ title: parentNode.node.name,
1629
+ onClick: () => onSelectNode(parentNode.node.id),
1630
+ children: miniNodeContent(parentNode.node, parentNode.fill, miniTreeRange)
1631
+ }
1632
+ ) }),
1633
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1634
+ "div",
1635
+ {
1636
+ className: `gtv-info-panel-mini-tree-node${childNodes.length === 0 ? " gtv-info-panel-mini-tree-node--leaf" : ""}`,
1637
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1638
+ "span",
1639
+ {
1640
+ className: "gtv-info-panel-child gtv-info-panel-mini-tree-current",
1641
+ title: node.name,
1642
+ children: miniNodeContent(node, fill, miniTreeRange)
1643
+ }
1644
+ )
1645
+ }
1646
+ ),
1647
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gtv-info-panel-mini-tree-children", children: [
1648
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "gtv-info-panel-children-title", children: [
1649
+ childNodes.length,
1650
+ " ",
1651
+ childNodes.length <= 1 ? "child" : "children"
1652
+ ] }),
1653
+ childNodes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { className: "gtv-info-panel-children-list", children: sortedChildNodes.map(({ node: child, fill: fill2 }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1567
1654
  "button",
1568
1655
  {
1569
1656
  type: "button",
1570
1657
  className: "gtv-info-panel-child",
1571
- title: shownParent.node.name,
1572
- onClick: () => onSelectNode(shownParent.node.id),
1573
- children: miniNodeContent(shownParent.node, shownParent.fill, miniTreeRange)
1658
+ title: child.name,
1659
+ onClick: () => onSelectNode(child.id),
1660
+ children: miniNodeContent(child, fill2, miniTreeRange)
1574
1661
  }
1575
- ) }),
1576
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1577
- "div",
1578
- {
1579
- className: `gtv-info-panel-mini-tree-node${childNodes.length === 0 ? " gtv-info-panel-mini-tree-node--leaf" : ""}`,
1580
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1581
- "span",
1582
- {
1583
- className: "gtv-info-panel-child gtv-info-panel-mini-tree-current",
1584
- title: node.name,
1585
- children: miniNodeContent(node, fill, miniTreeRange)
1586
- }
1587
- )
1588
- }
1589
- ),
1590
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "gtv-info-panel-mini-tree-children", children: [
1591
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "gtv-info-panel-children-title", children: [
1592
- childNodes.length,
1593
- " ",
1594
- childNodes.length <= 1 ? "child" : "children"
1595
- ] }),
1596
- childNodes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ul", { className: "gtv-info-panel-children-list", children: sortedChildNodes.map(({ node: child, fill: fill2 }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1597
- "button",
1598
- {
1599
- type: "button",
1600
- className: "gtv-info-panel-child",
1601
- title: child.name,
1602
- onClick: () => onSelectNode(child.id),
1603
- children: miniNodeContent(child, fill2, miniTreeRange)
1604
- }
1605
- ) }, child.id)) })
1606
- ] })
1607
- ] }) })
1608
- ] });
1609
- const itemCount = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("dl", { className: "gtv-info-panel-fields", children: [
1610
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dt", { children: itemCountLabel }),
1611
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("dd", { children: node.itemCount })
1662
+ ) }, child.id)) })
1663
+ ] })
1664
+ ] }) }) });
1665
+ const itemCount = /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dl", { className: "gtv-info-panel-fields", children: [
1666
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: itemCountLabel }),
1667
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { children: node.itemCount })
1612
1668
  ] });
1613
- const { primary, aside } = renderInfoPanelBody ? renderInfoPanelBody(node, { toolbar, ancestors, family, itemCount }) : { primary: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1669
+ const { primary, aside } = renderInfoPanelBody ? renderInfoPanelBody(node, { toolbar, ancestors, family, itemCount }) : { primary: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1614
1670
  toolbar,
1615
1671
  ancestors,
1616
1672
  family,
1617
1673
  itemCount
1618
1674
  ] }) };
1619
1675
  const hasAside = aside !== void 0 && aside !== null && aside !== false;
1620
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1676
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1621
1677
  "div",
1622
1678
  {
1623
1679
  className: `gtv-info-panel gtv-info-panel--${side}${expanded ? " gtv-info-panel--expanded" : ""}${expanded && !hasAside ? " gtv-info-panel--expanded-solo" : ""}${collapsed ? " gtv-info-panel--collapsed" : ""}`,
1624
1680
  ref: panelRef,
1625
1681
  children: [
1626
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1682
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1627
1683
  "div",
1628
1684
  {
1629
1685
  className: "gtv-info-panel-header",
1630
1686
  style: { background: fill, color: textColor },
1631
1687
  children: [
1632
- onPrevious && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1688
+ onPrevious && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1633
1689
  "button",
1634
1690
  {
1635
1691
  type: "button",
@@ -1638,21 +1694,21 @@ function InfoPanel({
1638
1694
  onClick: onPrevious,
1639
1695
  "aria-label": previousLabel ? `Previous: ${previousLabel}` : "Previous",
1640
1696
  title: previousLabel,
1641
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_md4.MdArrowBack, { className: "gtv-icon", size: 16 })
1697
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_md5.MdArrowBack, { className: "gtv-icon", size: 16 })
1642
1698
  }
1643
1699
  ),
1644
- editing && renderTitleEditor ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "gtv-info-panel-title gtv-info-panel-title--editor", children: renderTitleEditor(node) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1700
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1645
1701
  "span",
1646
1702
  {
1647
1703
  className: `gtv-info-panel-title${hideTitle ? " gtv-info-panel-title--hidden" : ""}`,
1648
1704
  style: { color: textColor },
1649
1705
  children: [
1650
1706
  node.name,
1651
- node.umbrella && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "gtv-info-panel-umbrella-badge", children: "umbrella" })
1707
+ node.umbrella && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "gtv-info-panel-umbrella-badge", children: "umbrella" })
1652
1708
  ]
1653
1709
  }
1654
1710
  ),
1655
- onZoomIn && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1711
+ onZoomIn && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1656
1712
  "button",
1657
1713
  {
1658
1714
  type: "button",
@@ -1661,22 +1717,11 @@ function InfoPanel({
1661
1717
  onClick: onZoomIn,
1662
1718
  "aria-label": "Zoom in",
1663
1719
  title: "Zoom in",
1664
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_md4.MdZoomIn, { className: "gtv-icon", size: 16 })
1720
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_md5.MdZoomIn, { className: "gtv-icon", size: 16 })
1665
1721
  }
1666
1722
  ),
1667
- onEditModeChange && !readOnly && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1668
- "button",
1669
- {
1670
- type: "button",
1671
- className: "gtv-info-panel-edit",
1672
- style: { color: textColor },
1673
- onClick: () => onEditModeChange(!editMode),
1674
- "aria-pressed": editMode,
1675
- children: editMode ? "Done" : "Edit"
1676
- }
1677
- ),
1678
- onExpandedChange && !hideExpandButton && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InfoPanelExpandButton, { expanded, onExpandedChange, style: { color: textColor } }),
1679
- !hideClose && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1723
+ onExpandedChange && !hideExpandButton && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(InfoPanelExpandButton, { expanded, onExpandedChange, style: { color: textColor } }),
1724
+ !hideClose && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1680
1725
  "button",
1681
1726
  {
1682
1727
  type: "button",
@@ -1684,18 +1729,18 @@ function InfoPanel({
1684
1729
  style: { color: textColor },
1685
1730
  onClick: onClose,
1686
1731
  "aria-label": "Close",
1687
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_md4.MdClose, { className: "gtv-icon", size: 16 })
1732
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_md5.MdClose, { className: "gtv-icon", size: 16 })
1688
1733
  }
1689
1734
  )
1690
1735
  ]
1691
1736
  }
1692
1737
  ),
1693
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "gtv-info-panel-body", children: [
1694
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "gtv-info-panel-primary", children: [
1738
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gtv-info-panel-body", children: [
1739
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gtv-info-panel-primary", children: [
1695
1740
  primary,
1696
1741
  expanded && !hasAside && dangerZone
1697
1742
  ] }),
1698
- hasAside && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "gtv-info-panel-aside", children: [
1743
+ hasAside && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gtv-info-panel-aside", children: [
1699
1744
  aside,
1700
1745
  expanded && dangerZone
1701
1746
  ] })
@@ -1708,8 +1753,8 @@ function InfoPanel({
1708
1753
 
1709
1754
  // src/OverlayPanel.tsx
1710
1755
  var import_react7 = require("react");
1711
- var import_md5 = require("react-icons/md");
1712
- var import_jsx_runtime6 = require("react/jsx-runtime");
1756
+ var import_md6 = require("react-icons/md");
1757
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1713
1758
  function OverlayPanel({
1714
1759
  title,
1715
1760
  content,
@@ -1744,21 +1789,21 @@ function OverlayPanel({
1744
1789
  expanded && !extraContent && "gtv-info-panel--expanded-solo",
1745
1790
  collapsed && "gtv-info-panel--collapsed"
1746
1791
  ].filter(Boolean).join(" ");
1747
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className, ref: panelRef, children: [
1748
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "gtv-info-panel-header", children: [
1749
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: `gtv-info-panel-title${hideTitle ? " gtv-info-panel-title--hidden" : ""}`, children: title }),
1750
- onExpandedChange && !hideExpandButton && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(InfoPanelExpandButton, { expanded, onExpandedChange }),
1751
- !hideClose && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "gtv-info-panel-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_md5.MdClose, { className: "gtv-icon", size: 16 }) })
1792
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className, ref: panelRef, children: [
1793
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "gtv-info-panel-header", children: [
1794
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: `gtv-info-panel-title${hideTitle ? " gtv-info-panel-title--hidden" : ""}`, children: title }),
1795
+ onExpandedChange && !hideExpandButton && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(InfoPanelExpandButton, { expanded, onExpandedChange }),
1796
+ !hideClose && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "gtv-info-panel-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_md6.MdClose, { className: "gtv-icon", size: 16 }) })
1752
1797
  ] }),
1753
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "gtv-info-panel-body", children: [
1754
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "gtv-info-panel-aside gtv-overlay-content", children: content }),
1755
- extraContent && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "gtv-info-panel-primary gtv-overlay-content", children: extraContent })
1798
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "gtv-info-panel-body", children: [
1799
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "gtv-info-panel-aside gtv-overlay-content", children: content }),
1800
+ extraContent && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "gtv-info-panel-primary gtv-overlay-content", children: extraContent })
1756
1801
  ] })
1757
1802
  ] });
1758
1803
  }
1759
1804
 
1760
1805
  // src/GenreTree.tsx
1761
- var import_jsx_runtime7 = require("react/jsx-runtime");
1806
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1762
1807
  function GenreTree({
1763
1808
  nodes,
1764
1809
  className,
@@ -1786,10 +1831,6 @@ function GenreTree({
1786
1831
  hideInfoPanelTitle = false,
1787
1832
  itemCountLabel,
1788
1833
  readOnly,
1789
- editMode,
1790
- onEditModeChange,
1791
- renderTitleEditor,
1792
- renderParentsEditor,
1793
1834
  renderDangerZone,
1794
1835
  expanded,
1795
1836
  onExpandedChange,
@@ -1799,7 +1840,9 @@ function GenreTree({
1799
1840
  wheelZoom,
1800
1841
  defaultViewport,
1801
1842
  onViewportChange,
1802
- controlsOrientation = "vertical"
1843
+ controlsOrientation = "vertical",
1844
+ floatingControlsLeading,
1845
+ busy
1803
1846
  }) {
1804
1847
  const index = useTreeIndex(nodes);
1805
1848
  const svgRef = (0, import_react8.useRef)(null);
@@ -1955,7 +1998,7 @@ function GenreTree({
1955
1998
  hasInitialFitRef.current = true;
1956
1999
  panZoom.fitToFrame(elements, { silent: true });
1957
2000
  });
1958
- const svg = /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2001
+ const svg = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1959
2002
  "svg",
1960
2003
  {
1961
2004
  ref: svgRef,
@@ -1965,13 +2008,14 @@ function GenreTree({
1965
2008
  }
1966
2009
  );
1967
2010
  if (!interactive) {
1968
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className, children: svg });
2011
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className, children: svg });
1969
2012
  }
1970
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2013
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1971
2014
  "div",
1972
2015
  {
1973
2016
  ref: viewportRef,
1974
2017
  className,
2018
+ "aria-busy": busy,
1975
2019
  style: {
1976
2020
  position: "relative",
1977
2021
  overflow: "hidden",
@@ -1994,7 +2038,7 @@ function GenreTree({
1994
2038
  },
1995
2039
  onPointerDown: panZoom.handlePointerDown,
1996
2040
  children: [
1997
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2041
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1998
2042
  "div",
1999
2043
  {
2000
2044
  ref: stageRef,
@@ -2008,7 +2052,7 @@ function GenreTree({
2008
2052
  children: svg
2009
2053
  }
2010
2054
  ),
2011
- panel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2055
+ panel && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2012
2056
  InfoPanel,
2013
2057
  {
2014
2058
  node: panel.node,
@@ -2058,10 +2102,6 @@ function GenreTree({
2058
2102
  hideTitle: hideInfoPanelTitle,
2059
2103
  itemCountLabel,
2060
2104
  readOnly,
2061
- editMode,
2062
- onEditModeChange,
2063
- renderTitleEditor,
2064
- renderParentsEditor,
2065
2105
  renderDangerZone,
2066
2106
  expanded,
2067
2107
  onExpandedChange,
@@ -2080,54 +2120,15 @@ function GenreTree({
2080
2120
  } : void 0
2081
2121
  }
2082
2122
  ),
2083
- overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(OverlayPanel, { ...overlayPanel }),
2084
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2085
- "div",
2123
+ overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(OverlayPanel, { ...overlayPanel }),
2124
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2125
+ FloatingControls,
2086
2126
  {
2087
- className: [
2088
- "gtv-wheel-floating-controls",
2089
- controlsOrientation === "horizontal" && "gtv-wheel-floating-controls--horizontal"
2090
- ].filter(Boolean).join(" "),
2091
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gtv-zoom-controls", children: [
2092
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2093
- "button",
2094
- {
2095
- type: "button",
2096
- className: [
2097
- "gtv-zoom-btn",
2098
- !panZoom.canZoomIn && "gtv-zoom-btn--disabled"
2099
- ].filter(Boolean).join(" "),
2100
- disabled: !panZoom.canZoomIn,
2101
- onClick: panZoom.zoomIn,
2102
- "aria-label": "Zoom in",
2103
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_md6.MdZoomIn, { className: "gtv-icon", size: 18 })
2104
- }
2105
- ),
2106
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2107
- "button",
2108
- {
2109
- type: "button",
2110
- className: [
2111
- "gtv-zoom-btn",
2112
- !panZoom.canZoomOut && "gtv-zoom-btn--disabled"
2113
- ].filter(Boolean).join(" "),
2114
- disabled: !panZoom.canZoomOut,
2115
- onClick: panZoom.zoomOut,
2116
- "aria-label": "Zoom out",
2117
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_md6.MdZoomOut, { className: "gtv-icon", size: 18 })
2118
- }
2119
- ),
2120
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2121
- "button",
2122
- {
2123
- type: "button",
2124
- className: "gtv-zoom-btn",
2125
- onClick: () => panZoom.fitToFrame(queryTreeContentElements(svgRef.current)),
2126
- "aria-label": "Fit to frame",
2127
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_md6.MdFitScreen, { className: "gtv-icon", size: 18 })
2128
- }
2129
- )
2130
- ] })
2127
+ panZoom,
2128
+ orientation: controlsOrientation,
2129
+ leading: floatingControlsLeading,
2130
+ busy,
2131
+ onFit: () => panZoom.fitToFrame(queryTreeContentElements(svgRef.current))
2131
2132
  }
2132
2133
  )
2133
2134
  ]
@@ -2137,7 +2138,6 @@ function GenreTree({
2137
2138
 
2138
2139
  // src/GenreTreeWheelBase.tsx
2139
2140
  var import_react9 = require("react");
2140
- var import_md7 = require("react-icons/md");
2141
2141
  var d32 = __toESM(require("d3"), 1);
2142
2142
 
2143
2143
  // src/root-grouping.ts
@@ -2213,7 +2213,7 @@ function computeRotationForSelection(currentRotationDeg, targetAngle, landingAng
2213
2213
  }
2214
2214
 
2215
2215
  // src/GenreTreeWheelBase.tsx
2216
- var import_jsx_runtime8 = require("react/jsx-runtime");
2216
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2217
2217
  function WheelCore({
2218
2218
  nodes,
2219
2219
  className,
@@ -2239,10 +2239,6 @@ function WheelCore({
2239
2239
  hideInfoPanelTitle = false,
2240
2240
  itemCountLabel,
2241
2241
  readOnly,
2242
- editMode,
2243
- onEditModeChange,
2244
- renderTitleEditor,
2245
- renderParentsEditor,
2246
2242
  renderDangerZone,
2247
2243
  expanded,
2248
2244
  onExpandedChange,
@@ -2253,6 +2249,8 @@ function WheelCore({
2253
2249
  defaultViewport,
2254
2250
  onViewportChange,
2255
2251
  controlsOrientation = "vertical",
2252
+ floatingControlsLeading,
2253
+ busy,
2256
2254
  selectedNodeId
2257
2255
  }) {
2258
2256
  const treeOrientation = direction === "left" ? "horizontal-anchored" : "vertical";
@@ -2368,10 +2366,11 @@ function WheelCore({
2368
2366
  );
2369
2367
  showNodeInfo(targetNode, element, viewportRef.current, { notify: false });
2370
2368
  }, [selectedNodeId, effectiveRootId, nodes, panel, showNodeInfo]);
2371
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2369
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2372
2370
  "div",
2373
2371
  {
2374
2372
  ref: viewportRef,
2373
+ "aria-busy": busy,
2375
2374
  className: [
2376
2375
  "gtv-wheel-container",
2377
2376
  direction === "left" && "gtv-wheel-container--left",
@@ -2386,7 +2385,7 @@ function WheelCore({
2386
2385
  },
2387
2386
  onPointerDown: panZoom.handlePointerDown,
2388
2387
  children: [
2389
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2388
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2390
2389
  "div",
2391
2390
  {
2392
2391
  ref: stageRef,
@@ -2399,7 +2398,7 @@ function WheelCore({
2399
2398
  transform: panZoom.transform,
2400
2399
  transformOrigin: "0 0"
2401
2400
  },
2402
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2401
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2403
2402
  "div",
2404
2403
  {
2405
2404
  className: "gtv-wheel-stage",
@@ -2407,7 +2406,7 @@ function WheelCore({
2407
2406
  [chipHalfExtentVar]: `${rootChipHalfExtent}px`
2408
2407
  },
2409
2408
  children: [
2410
- selectedGroup && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "gtv-wheel-tree-anchor", ref: treeAnchorRef, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2409
+ selectedGroup && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "gtv-wheel-tree-anchor", ref: treeAnchorRef, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2411
2410
  GenreTree,
2412
2411
  {
2413
2412
  nodes: selectedGroup.nodes,
@@ -2445,9 +2444,9 @@ function WheelCore({
2445
2444
  },
2446
2445
  selectedGroup.root.id
2447
2446
  ) }),
2448
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "gtv-wheel-circle", ref: wheelCircleRef }),
2449
- centerLabel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "gtv-wheel-center-label", children: centerLabel }),
2450
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2447
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "gtv-wheel-circle", ref: wheelCircleRef }),
2448
+ centerLabel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "gtv-wheel-center-label", children: centerLabel }),
2449
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2451
2450
  "div",
2452
2451
  {
2453
2452
  className: "gtv-wheel",
@@ -2455,7 +2454,7 @@ function WheelCore({
2455
2454
  "--gtv-wheel-rotation": `${rotationDeg}deg`
2456
2455
  },
2457
2456
  children: [
2458
- sectorFillGradient && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2457
+ sectorFillGradient && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2459
2458
  "div",
2460
2459
  {
2461
2460
  className: "gtv-wheel-sector-fill",
@@ -2464,7 +2463,7 @@ function WheelCore({
2464
2463
  }
2465
2464
  }
2466
2465
  ),
2467
- groups.length > 1 && groups.map((group, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2466
+ groups.length > 1 && groups.map((group, index) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2468
2467
  "div",
2469
2468
  {
2470
2469
  className: "gtv-wheel-divider",
@@ -2487,12 +2486,12 @@ function WheelCore({
2487
2486
  rootItemCount,
2488
2487
  rootItemCountRange
2489
2488
  );
2490
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2489
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2491
2490
  "div",
2492
2491
  {
2493
2492
  className: "gtv-wheel-slot",
2494
2493
  style: { "--gtv-chip-angle": `${angle}deg` },
2495
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
2494
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2496
2495
  "button",
2497
2496
  {
2498
2497
  type: "button",
@@ -2528,8 +2527,8 @@ function WheelCore({
2528
2527
  }
2529
2528
  },
2530
2529
  children: [
2531
- PER_TREE_ACCENT_DOT && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "gtv-wheel-chip-dot" }),
2532
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2530
+ PER_TREE_ACCENT_DOT && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "gtv-wheel-chip-dot" }),
2531
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2533
2532
  "span",
2534
2533
  {
2535
2534
  className: "gtv-node-label gtv-node-label--root",
@@ -2552,7 +2551,7 @@ function WheelCore({
2552
2551
  )
2553
2552
  }
2554
2553
  ),
2555
- panel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2554
+ panel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2556
2555
  InfoPanel,
2557
2556
  {
2558
2557
  node: panel.node,
@@ -2599,10 +2598,6 @@ function WheelCore({
2599
2598
  hideTitle: hideInfoPanelTitle,
2600
2599
  itemCountLabel,
2601
2600
  readOnly,
2602
- editMode,
2603
- onEditModeChange,
2604
- renderTitleEditor,
2605
- renderParentsEditor,
2606
2601
  renderDangerZone,
2607
2602
  expanded,
2608
2603
  onExpandedChange,
@@ -2621,57 +2616,18 @@ function WheelCore({
2621
2616
  } : void 0
2622
2617
  }
2623
2618
  ),
2624
- overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(OverlayPanel, { ...overlayPanel }),
2625
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2626
- "div",
2619
+ overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OverlayPanel, { ...overlayPanel }),
2620
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2621
+ FloatingControls,
2627
2622
  {
2628
- className: [
2629
- "gtv-wheel-floating-controls",
2630
- controlsOrientation === "horizontal" && "gtv-wheel-floating-controls--horizontal"
2631
- ].filter(Boolean).join(" "),
2632
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "gtv-zoom-controls", children: [
2633
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2634
- "button",
2635
- {
2636
- type: "button",
2637
- className: [
2638
- "gtv-zoom-btn",
2639
- !panZoom.canZoomIn && "gtv-zoom-btn--disabled"
2640
- ].filter(Boolean).join(" "),
2641
- disabled: !panZoom.canZoomIn,
2642
- onClick: panZoom.zoomIn,
2643
- "aria-label": "Zoom in",
2644
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_md7.MdZoomIn, { className: "gtv-icon", size: 18 })
2645
- }
2646
- ),
2647
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2648
- "button",
2649
- {
2650
- type: "button",
2651
- className: [
2652
- "gtv-zoom-btn",
2653
- !panZoom.canZoomOut && "gtv-zoom-btn--disabled"
2654
- ].filter(Boolean).join(" "),
2655
- disabled: !panZoom.canZoomOut,
2656
- onClick: panZoom.zoomOut,
2657
- "aria-label": "Zoom out",
2658
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_md7.MdZoomOut, { className: "gtv-icon", size: 18 })
2659
- }
2660
- ),
2661
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2662
- "button",
2663
- {
2664
- type: "button",
2665
- className: "gtv-zoom-btn",
2666
- onClick: () => panZoom.fitToFrame([
2667
- wheelCircleRef.current,
2668
- ...queryTreeContentElements(treeAnchorRef.current)
2669
- ]),
2670
- "aria-label": "Fit to frame",
2671
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_md7.MdFitScreen, { className: "gtv-icon", size: 18 })
2672
- }
2673
- )
2674
- ] })
2623
+ panZoom,
2624
+ orientation: controlsOrientation,
2625
+ leading: floatingControlsLeading,
2626
+ busy,
2627
+ onFit: () => panZoom.fitToFrame([
2628
+ wheelCircleRef.current,
2629
+ ...queryTreeContentElements(treeAnchorRef.current)
2630
+ ])
2675
2631
  }
2676
2632
  )
2677
2633
  ]
@@ -2680,20 +2636,19 @@ function WheelCore({
2680
2636
  }
2681
2637
 
2682
2638
  // src/GenreTreeWheel.tsx
2683
- var import_jsx_runtime9 = require("react/jsx-runtime");
2639
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2684
2640
  function GenreTreeWheel(props) {
2685
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(WheelCore, { ...props, direction: "bottom" });
2641
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(WheelCore, { ...props, direction: "bottom" });
2686
2642
  }
2687
2643
 
2688
2644
  // src/GenreTreeWheelRight.tsx
2689
- var import_jsx_runtime10 = require("react/jsx-runtime");
2645
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2690
2646
  function GenreTreeWheelRight(props) {
2691
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(WheelCore, { ...props, direction: "left" });
2647
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WheelCore, { ...props, direction: "left" });
2692
2648
  }
2693
2649
 
2694
2650
  // src/GenreTreeWheelRadialBase.tsx
2695
2651
  var import_react10 = require("react");
2696
- var import_md8 = require("react-icons/md");
2697
2652
  var d33 = __toESM(require("d3"), 1);
2698
2653
 
2699
2654
  // src/defer-click.ts
@@ -2877,6 +2832,7 @@ function pickEmptyLabelSpot(area, width, height, obstacles, prefer = () => true)
2877
2832
  var POP_WEDGE_SPAN_DEGREES = 80;
2878
2833
  var POP_SUBTREE_OUTER_MARGIN = 24;
2879
2834
  var POP_HIERARCHY_ROOT_ABSOLUTE_DEPTH = 1;
2835
+ var DOT_RADIUS_TRANSITION_MS = 250;
2880
2836
  function getRadialDepthRadius(depth, coreRootCircleRadius, depthSpacing) {
2881
2837
  return coreRootCircleRadius + depth * depthSpacing;
2882
2838
  }
@@ -2899,14 +2855,13 @@ function calculatePopSubtreeRadialExtent(hierarchy, coreRootCircleRadius = 0) {
2899
2855
  POP_TREE_DEPTH_RADIAL_SPACING
2900
2856
  ) + MAX_NODE_WIDTH / 2 + POP_SUBTREE_OUTER_MARGIN;
2901
2857
  }
2902
- function computePopRadialLayout(d3Lib, hierarchy, wedgeCenterAngleDegrees, coreRootCircleRadius, wedgeSpanDegrees = POP_WEDGE_SPAN_DEGREES, depthSpacing = POP_TREE_DEPTH_RADIAL_SPACING, itemCountRange) {
2858
+ function computePopRadialLayout(d3Lib, hierarchy, wedgeCenterAngleDegrees, coreRootCircleRadius, wedgeSpanDegrees = POP_WEDGE_SPAN_DEGREES, depthSpacing = POP_TREE_DEPTH_RADIAL_SPACING) {
2903
2859
  return layoutWedge(
2904
2860
  d3Lib,
2905
2861
  hierarchy,
2906
2862
  wedgeCenterAngleDegrees,
2907
2863
  wedgeSpanDegrees,
2908
- popRadiusOfDepth(coreRootCircleRadius, depthSpacing),
2909
- itemCountRange
2864
+ popRadiusOfDepth(coreRootCircleRadius, depthSpacing)
2910
2865
  );
2911
2866
  }
2912
2867
  var MIN_DOT_GAP = 5.6;
@@ -2916,15 +2871,15 @@ function popRadiusOfDepth(coreRootCircleRadius, depthSpacing) {
2916
2871
  function coreRadiusOfDepth(coreRootCircleRadius, depthSpacing) {
2917
2872
  return (depth) => getRadialDepthRadius(depth + 1, coreRootCircleRadius, depthSpacing);
2918
2873
  }
2919
- function measureWedge(_d3Lib, hierarchy, radiusOfDepth, itemCountRange) {
2920
- const dotRadius = (d) => itemCountRange ? calculateNodeDotRadius(d.data.itemCount, itemCountRange) : NODE_DOT_MAX_RADIUS;
2874
+ function measureWedge(_d3Lib, hierarchy, radiusOfDepth) {
2875
+ const dotRadius = () => NODE_LAYOUT_SLOT_RADIUS;
2921
2876
  const offsetFromParent = /* @__PURE__ */ new Map();
2922
2877
  const shiftLevel = (level, by) => ({
2923
2878
  left: { ...level.left, x: level.left.x + by },
2924
2879
  right: { ...level.right, x: level.right.x + by }
2925
2880
  });
2926
2881
  const pack = (node) => {
2927
- const own = { x: 0, dotRadius: dotRadius(node) };
2882
+ const own = { x: 0, dotRadius: dotRadius() };
2928
2883
  if (!node.children) return [{ left: own, right: own }];
2929
2884
  const below = [];
2930
2885
  const childOffsets = [];
@@ -2956,7 +2911,7 @@ function measureWedge(_d3Lib, hierarchy, radiusOfDepth, itemCountRange) {
2956
2911
  let min = Infinity;
2957
2912
  let max = -Infinity;
2958
2913
  hierarchy.each((d) => {
2959
- const inset = (dotRadius(d) + MIN_DOT_GAP / 2) / radiusOfDepth(d.depth);
2914
+ const inset = (dotRadius() + MIN_DOT_GAP / 2) / radiusOfDepth(d.depth);
2960
2915
  min = Math.min(min, d.x - inset);
2961
2916
  max = Math.max(max, d.x + inset);
2962
2917
  });
@@ -2966,13 +2921,44 @@ function measureWedge(_d3Lib, hierarchy, radiusOfDepth, itemCountRange) {
2966
2921
  return max - min;
2967
2922
  }
2968
2923
  var MAX_WEDGE_STRETCH = 1.42;
2969
- function layoutWedge(d3Lib, hierarchy, wedgeCenterAngleDegrees, wedgeSpanDegrees, radiusOfDepth, itemCountRange) {
2970
- const neededRad = measureWedge(d3Lib, hierarchy, radiusOfDepth, itemCountRange);
2924
+ function splitAtBranching(hierarchy) {
2925
+ let branching = hierarchy;
2926
+ const chain = [];
2927
+ while (branching.children?.length === 1) {
2928
+ chain.push(branching);
2929
+ branching = branching.children[0];
2930
+ }
2931
+ const children = branching.children;
2932
+ return children && children.length > 1 ? { chain, branching, children } : null;
2933
+ }
2934
+ function layoutWedge(d3Lib, hierarchy, wedgeCenterAngleDegrees, wedgeSpanDegrees, radiusOfDepth, slack = "center") {
2935
+ const neededRad = measureWedge(d3Lib, hierarchy, radiusOfDepth);
2971
2936
  const wedgeSpanRad = wedgeSpanDegrees * Math.PI / 180;
2972
2937
  const scale = Math.min(MAX_WEDGE_STRETCH, wedgeSpanRad / neededRad);
2973
- const wedgeStartRad = wedgeCenterAngleDegrees * Math.PI / 180 - neededRad * scale / 2;
2938
+ const wedgeCenterRad = wedgeCenterAngleDegrees * Math.PI / 180;
2939
+ const centeredStartRad = wedgeCenterRad - neededRad * scale / 2;
2940
+ const branchingSplit = slack === "between-children" ? splitAtBranching(hierarchy) : null;
2941
+ if (!branchingSplit) {
2942
+ hierarchy.each((d) => {
2943
+ const angleRad = centeredStartRad + d.x * scale;
2944
+ const radius = radiusOfDepth(d.depth);
2945
+ d.x = radius * Math.sin(angleRad);
2946
+ d.y = -radius * Math.cos(angleRad);
2947
+ });
2948
+ return hierarchy;
2949
+ }
2950
+ const { chain, branching, children } = branchingSplit;
2951
+ const gap = Math.max(0, wedgeSpanRad - neededRad * scale) / (children.length - 1);
2952
+ const wedgeStartRad = wedgeCenterRad - wedgeSpanRad / 2;
2953
+ const angleOffsetRad = /* @__PURE__ */ new Map();
2954
+ hierarchy.each((d) => angleOffsetRad.set(d, d.x * scale));
2955
+ children.forEach((child, i) => {
2956
+ child.each((d) => angleOffsetRad.set(d, angleOffsetRad.get(d) + i * gap));
2957
+ });
2958
+ const recenter = gap * (children.length - 1) / 2;
2959
+ [...chain, branching].forEach((d) => angleOffsetRad.set(d, angleOffsetRad.get(d) + recenter));
2974
2960
  hierarchy.each((d) => {
2975
- const angleRad = wedgeStartRad + d.x * scale;
2961
+ const angleRad = wedgeStartRad + angleOffsetRad.get(d);
2976
2962
  const radius = radiusOfDepth(d.depth);
2977
2963
  d.x = radius * Math.sin(angleRad);
2978
2964
  d.y = -radius * Math.cos(angleRad);
@@ -3059,7 +3045,8 @@ function renderPopSubtree(d3Lib, svg, hierarchy, rootColor, reparentingNodeId, r
3059
3045
  rootLinkOrigin,
3060
3046
  isMainstreamSector = false,
3061
3047
  selectedNodeId = null,
3062
- nodeFillOf
3048
+ nodeFillOf,
3049
+ dotRadiusMemory
3063
3050
  } = options;
3064
3051
  const { onReparentTargetSelect, onNodeClick, onNodeDoubleClick, onNodeHover } = callbacks;
3065
3052
  const isForbidden = (d) => reparentForbiddenIds.includes(d.data.id);
@@ -3122,13 +3109,18 @@ function renderPopSubtree(d3Lib, svg, hierarchy, rootColor, reparentingNodeId, r
3122
3109
  );
3123
3110
  d3Lib.select(this).attr("x", hitArea.x0).attr("y", hitArea.y0).attr("width", hitArea.x1 - hitArea.x0).attr("height", hitArea.y1 - hitArea.y0);
3124
3111
  }).attr("fill", "transparent").attr("pointer-events", "all");
3125
- nodes.append("circle").attr("class", "gtv-node-dot").attr("r", (d) => calculateNodeDotRadius(d.data.itemCount, itemCountRange)).attr("fill", nodeFill).attr(
3112
+ const targetDotRadius = (d) => calculateNodeDotRadius(d.data.itemCount, itemCountRange);
3113
+ const animateDots = dotRadiusMemory !== void 0 && !(typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches);
3114
+ const startDotRadius = (d) => (animateDots ? dotRadiusMemory?.get(d.data.id) : void 0) ?? targetDotRadius(d);
3115
+ nodes.append("circle").attr("class", "gtv-node-dot").attr("r", startDotRadius).attr("fill", nodeFill).attr(
3126
3116
  "stroke",
3127
3117
  (d) => isSelected(d) ? ACCENT_COLOR : isMainstreamSector ? TEXT_COLOR : SURFACE_BORDER_COLOR
3128
3118
  ).attr(
3129
3119
  "stroke-width",
3130
3120
  (d) => isSelected(d) ? calculateSelectedNodeBorderWidth(calculateNodeDotRadius(d.data.itemCount, itemCountRange)) : calculateNodeBorderWidth(d.data.itemCount, itemCountRange)
3131
- ).style("pointer-events", "none");
3121
+ ).style("pointer-events", "none").filter((d) => startDotRadius(d) !== targetDotRadius(d)).transition().duration(DOT_RADIUS_TRANSITION_MS).ease(d3Lib.easeCubicOut).attr("r", targetDotRadius);
3122
+ nodes.each((d) => dotRadiusMemory?.set(d.data.id, targetDotRadius(d)));
3123
+ nodes.sort((a, b) => targetDotRadius(b) - targetDotRadius(a));
3132
3124
  nodes.append("text").attr(
3133
3125
  "class",
3134
3126
  "gtv-node-label"
@@ -3251,14 +3243,14 @@ function buildCoreHierarchy(d3Lib, coreNodes) {
3251
3243
  });
3252
3244
  return hierarchy;
3253
3245
  }
3254
- function computeCoreRadialLayout(d3Lib, hierarchy, wedgeCenterAngleDegrees, wedgeSpanDegrees, coreRootCircleRadius, depthSpacing, itemCountRange) {
3246
+ function computeCoreRadialLayout(d3Lib, hierarchy, wedgeCenterAngleDegrees, wedgeSpanDegrees, coreRootCircleRadius, depthSpacing) {
3255
3247
  return layoutWedge(
3256
3248
  d3Lib,
3257
3249
  hierarchy,
3258
3250
  wedgeCenterAngleDegrees,
3259
3251
  wedgeSpanDegrees,
3260
3252
  coreRadiusOfDepth(coreRootCircleRadius, depthSpacing),
3261
- itemCountRange
3253
+ "between-children"
3262
3254
  );
3263
3255
  }
3264
3256
  function calculateCoreSubtreeRadialExtent(hierarchy, depthSpacing, coreRootCircleRadius = 0) {
@@ -3298,7 +3290,7 @@ function splitRootGroupBySide(group) {
3298
3290
  }
3299
3291
 
3300
3292
  // src/GenreTreeWheelRadialBase.tsx
3301
- var import_jsx_runtime11 = require("react/jsx-runtime");
3293
+ var import_jsx_runtime13 = require("react/jsx-runtime");
3302
3294
  var LANDING_ANGLE = 90;
3303
3295
  function computeContinuousAngles(groups, layout, previous) {
3304
3296
  const angles = /* @__PURE__ */ new Map();
@@ -3333,10 +3325,6 @@ function WheelRadialCore({
3333
3325
  hideInfoPanelTitle = false,
3334
3326
  itemCountLabel,
3335
3327
  readOnly,
3336
- editMode,
3337
- onEditModeChange,
3338
- renderTitleEditor,
3339
- renderParentsEditor,
3340
3328
  renderDangerZone,
3341
3329
  expanded,
3342
3330
  onExpandedChange,
@@ -3347,6 +3335,8 @@ function WheelRadialCore({
3347
3335
  defaultViewport,
3348
3336
  onViewportChange,
3349
3337
  controlsOrientation = "vertical",
3338
+ floatingControlsLeading,
3339
+ busy,
3350
3340
  selectedNodeId
3351
3341
  }) {
3352
3342
  const index = useTreeIndex(nodes);
@@ -3505,8 +3495,7 @@ function WheelRadialCore({
3505
3495
  angle,
3506
3496
  span,
3507
3497
  wheelRadius,
3508
- POP_TREE_DEPTH_RADIAL_SPACING,
3509
- wheelItemCountRange
3498
+ POP_TREE_DEPTH_RADIAL_SPACING
3510
3499
  );
3511
3500
  const reparentForbiddenIds = reparentingNodeId ? laidOut.descendants().find((d) => d.data.id === reparentingNodeId)?.descendants().map((d) => d.data.id) ?? [] : [];
3512
3501
  const branchGroup = sectorGroup.append("g").attr("class", "gtv-wheel-core-branch").attr("data-gtv-branch-index", branchIndex);
@@ -3622,10 +3611,11 @@ function WheelRadialCore({
3622
3611
  };
3623
3612
  });
3624
3613
  }, [groups, continuousAngleByRootId, sectorSpanByRootId]);
3625
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3614
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3626
3615
  "div",
3627
3616
  {
3628
3617
  ref: viewportRef,
3618
+ "aria-busy": busy,
3629
3619
  className: [
3630
3620
  "gtv-wheel-container",
3631
3621
  "gtv-wheel-container--radial",
@@ -3639,7 +3629,7 @@ function WheelRadialCore({
3639
3629
  },
3640
3630
  onPointerDown: panZoom.handlePointerDown,
3641
3631
  children: [
3642
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3632
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3643
3633
  "div",
3644
3634
  {
3645
3635
  ref: stageRef,
@@ -3652,9 +3642,9 @@ function WheelRadialCore({
3652
3642
  transform: panZoom.transform,
3653
3643
  transformOrigin: "0 0"
3654
3644
  },
3655
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "gtv-wheel-stage", children: [
3656
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "gtv-wheel-circle", ref: wheelCircleRef }),
3657
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "gtv-wheel", children: sectorFills.map((sector) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3645
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gtv-wheel-stage", children: [
3646
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gtv-wheel-circle", ref: wheelCircleRef }),
3647
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gtv-wheel", children: sectorFills.map((sector) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3658
3648
  "div",
3659
3649
  {
3660
3650
  className: "gtv-wheel-sector",
@@ -3666,7 +3656,7 @@ function WheelRadialCore({
3666
3656
  },
3667
3657
  `sector-${sector.rootId}`
3668
3658
  )) }),
3669
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3659
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3670
3660
  "svg",
3671
3661
  {
3672
3662
  ref: coreSvgRef,
@@ -3675,9 +3665,9 @@ function WheelRadialCore({
3675
3665
  height: wheelRadius * 2
3676
3666
  }
3677
3667
  ),
3678
- centerLabel && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "gtv-wheel-center-label", children: centerLabel }),
3679
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "gtv-wheel", children: [
3680
- dividerAngles.map((angle, index2) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3668
+ centerLabel && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gtv-wheel-center-label", children: centerLabel }),
3669
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "gtv-wheel", children: [
3670
+ dividerAngles.map((angle, index2) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3681
3671
  "div",
3682
3672
  {
3683
3673
  className: "gtv-wheel-divider",
@@ -3700,12 +3690,12 @@ function WheelRadialCore({
3700
3690
  rootItemCount,
3701
3691
  rootItemCountRange
3702
3692
  );
3703
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3693
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3704
3694
  "div",
3705
3695
  {
3706
3696
  className: "gtv-wheel-slot",
3707
3697
  style: { "--gtv-chip-angle": `${angle}deg` },
3708
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
3698
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3709
3699
  "button",
3710
3700
  {
3711
3701
  type: "button",
@@ -3738,8 +3728,8 @@ function WheelRadialCore({
3738
3728
  }
3739
3729
  },
3740
3730
  children: [
3741
- PER_TREE_ACCENT_DOT && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "gtv-wheel-chip-dot" }),
3742
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3731
+ PER_TREE_ACCENT_DOT && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "gtv-wheel-chip-dot" }),
3732
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3743
3733
  "span",
3744
3734
  {
3745
3735
  className: "gtv-node-label gtv-node-label--root",
@@ -3758,7 +3748,7 @@ function WheelRadialCore({
3758
3748
  ] })
3759
3749
  }
3760
3750
  ),
3761
- panel && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3751
+ panel && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3762
3752
  InfoPanel,
3763
3753
  {
3764
3754
  node: panel.node,
@@ -3804,10 +3794,6 @@ function WheelRadialCore({
3804
3794
  hideTitle: hideInfoPanelTitle,
3805
3795
  itemCountLabel,
3806
3796
  readOnly,
3807
- editMode,
3808
- onEditModeChange,
3809
- renderTitleEditor,
3810
- renderParentsEditor,
3811
3797
  renderDangerZone,
3812
3798
  expanded,
3813
3799
  onExpandedChange,
@@ -3826,54 +3812,15 @@ function WheelRadialCore({
3826
3812
  } : void 0
3827
3813
  }
3828
3814
  ),
3829
- overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(OverlayPanel, { ...overlayPanel }),
3830
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3831
- "div",
3815
+ overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(OverlayPanel, { ...overlayPanel }),
3816
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3817
+ FloatingControls,
3832
3818
  {
3833
- className: [
3834
- "gtv-wheel-floating-controls",
3835
- controlsOrientation === "horizontal" && "gtv-wheel-floating-controls--horizontal"
3836
- ].filter(Boolean).join(" "),
3837
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "gtv-zoom-controls", children: [
3838
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3839
- "button",
3840
- {
3841
- type: "button",
3842
- className: [
3843
- "gtv-zoom-btn",
3844
- !panZoom.canZoomIn && "gtv-zoom-btn--disabled"
3845
- ].filter(Boolean).join(" "),
3846
- disabled: !panZoom.canZoomIn,
3847
- onClick: panZoom.zoomIn,
3848
- "aria-label": "Zoom in",
3849
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_md8.MdZoomIn, { className: "gtv-icon", size: 18 })
3850
- }
3851
- ),
3852
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3853
- "button",
3854
- {
3855
- type: "button",
3856
- className: [
3857
- "gtv-zoom-btn",
3858
- !panZoom.canZoomOut && "gtv-zoom-btn--disabled"
3859
- ].filter(Boolean).join(" "),
3860
- disabled: !panZoom.canZoomOut,
3861
- onClick: panZoom.zoomOut,
3862
- "aria-label": "Zoom out",
3863
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_md8.MdZoomOut, { className: "gtv-icon", size: 18 })
3864
- }
3865
- ),
3866
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
3867
- "button",
3868
- {
3869
- type: "button",
3870
- className: "gtv-zoom-btn",
3871
- onClick: () => panZoom.fitToFrame([wheelCircleRef.current, coreSvgRef.current]),
3872
- "aria-label": "Fit to frame",
3873
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_md8.MdFitScreen, { className: "gtv-icon", size: 18 })
3874
- }
3875
- )
3876
- ] })
3819
+ panZoom,
3820
+ orientation: controlsOrientation,
3821
+ leading: floatingControlsLeading,
3822
+ busy,
3823
+ onFit: () => panZoom.fitToFrame([wheelCircleRef.current, coreSvgRef.current])
3877
3824
  }
3878
3825
  )
3879
3826
  ]
@@ -3882,14 +3829,13 @@ function WheelRadialCore({
3882
3829
  }
3883
3830
 
3884
3831
  // src/GenreTreeWheelRadial.tsx
3885
- var import_jsx_runtime12 = require("react/jsx-runtime");
3832
+ var import_jsx_runtime14 = require("react/jsx-runtime");
3886
3833
  function GenreTreeWheelRadial(props) {
3887
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(WheelRadialCore, { ...props });
3834
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(WheelRadialCore, { ...props });
3888
3835
  }
3889
3836
 
3890
3837
  // src/GenreTreeWheelRadialPopCoreBase.tsx
3891
3838
  var import_react13 = require("react");
3892
- var import_md10 = require("react-icons/md");
3893
3839
  var d34 = __toESM(require("d3"), 1);
3894
3840
 
3895
3841
  // src/use-focus-history.ts
@@ -3918,17 +3864,17 @@ function useFocusHistory(focusNodeId, isFocusable) {
3918
3864
 
3919
3865
  // src/WheelBreadcrumb.tsx
3920
3866
  var import_react12 = require("react");
3921
- var import_md9 = require("react-icons/md");
3922
- var import_jsx_runtime13 = require("react/jsx-runtime");
3867
+ var import_md7 = require("react-icons/md");
3868
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3923
3869
  var MAX_VISIBLE_ANCESTORS = 3;
3924
3870
  var ANCESTORS_KEPT_WHEN_COLLAPSED = 2;
3925
3871
  function WheelBreadcrumb({ ancestors, currentName, onHome, onAncestor }) {
3926
3872
  const [expanded, setExpanded] = (0, import_react12.useState)(false);
3927
3873
  const collapsed = !expanded && ancestors.length > MAX_VISIBLE_ANCESTORS;
3928
3874
  const visibleAncestors = collapsed ? ancestors.slice(-ANCESTORS_KEPT_WHEN_COLLAPSED) : ancestors;
3929
- const separator = /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { "aria-hidden": "true", children: "\u203A" });
3930
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("nav", { className: "gtv-wheel-breadcrumb", "aria-label": "Breadcrumb", children: [
3931
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3875
+ const separator = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { "aria-hidden": "true", children: "\u203A" });
3876
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("nav", { className: "gtv-wheel-breadcrumb", "aria-label": "Breadcrumb", children: [
3877
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3932
3878
  "button",
3933
3879
  {
3934
3880
  type: "button",
@@ -3936,12 +3882,12 @@ function WheelBreadcrumb({ ancestors, currentName, onHome, onAncestor }) {
3936
3882
  "aria-label": "All genres",
3937
3883
  title: "All genres",
3938
3884
  onClick: onHome,
3939
- children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_md9.MdHome, { className: "gtv-icon", size: 14 })
3885
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_md7.MdHome, { className: "gtv-icon", size: 14 })
3940
3886
  }
3941
3887
  ),
3942
3888
  separator,
3943
- collapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
3944
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3889
+ collapsed && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
3890
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3945
3891
  "button",
3946
3892
  {
3947
3893
  type: "button",
@@ -3954,11 +3900,11 @@ function WheelBreadcrumb({ ancestors, currentName, onHome, onAncestor }) {
3954
3900
  ),
3955
3901
  separator
3956
3902
  ] }),
3957
- visibleAncestors.map((ancestor) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react12.Fragment, { children: [
3958
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "gtv-wheel-breadcrumb-crumb", onClick: () => onAncestor(ancestor.id), children: ancestor.name }),
3903
+ visibleAncestors.map((ancestor) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react12.Fragment, { children: [
3904
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "gtv-wheel-breadcrumb-crumb", onClick: () => onAncestor(ancestor.id), children: ancestor.name }),
3959
3905
  separator
3960
3906
  ] }, ancestor.id)),
3961
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "gtv-wheel-breadcrumb-current", "aria-current": "page", children: currentName })
3907
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "gtv-wheel-breadcrumb-current", "aria-current": "page", children: currentName })
3962
3908
  ] });
3963
3909
  }
3964
3910
 
@@ -4019,7 +3965,7 @@ function resolvePopCoreFrame(nodes, focusNodeId) {
4019
3965
  }
4020
3966
 
4021
3967
  // src/GenreTreeWheelRadialPopCoreBase.tsx
4022
- var import_jsx_runtime14 = require("react/jsx-runtime");
3968
+ var import_jsx_runtime16 = require("react/jsx-runtime");
4023
3969
  var LANDING_ANGLE2 = 90;
4024
3970
  var CHIP_GAP = 16;
4025
3971
  var sum = (values) => values.reduce((total, value) => total + value, 0);
@@ -4056,10 +4002,6 @@ function WheelRadialPopCoreCore({
4056
4002
  hideInfoPanelTitle = false,
4057
4003
  itemCountLabel,
4058
4004
  readOnly,
4059
- editMode,
4060
- onEditModeChange,
4061
- renderTitleEditor,
4062
- renderParentsEditor,
4063
4005
  renderDangerZone,
4064
4006
  expanded,
4065
4007
  onExpandedChange,
@@ -4070,6 +4012,8 @@ function WheelRadialPopCoreCore({
4070
4012
  defaultViewport,
4071
4013
  onViewportChange,
4072
4014
  controlsOrientation = "vertical",
4015
+ floatingControlsLeading,
4016
+ busy,
4073
4017
  allowWheelRotation = true,
4074
4018
  selectedNodeId,
4075
4019
  focusNodeId: requestedFocusNodeId = null,
@@ -4142,6 +4086,7 @@ function WheelRadialPopCoreCore({
4142
4086
  }
4143
4087
  const viewportRef = (0, import_react13.useRef)(null);
4144
4088
  const stageRef = (0, import_react13.useRef)(null);
4089
+ const dotRadiusMemoryRef = (0, import_react13.useRef)(/* @__PURE__ */ new Map());
4145
4090
  const panZoom = usePanZoom(viewportRef, wheelZoom, stageRef, { defaultViewport, onViewportChange });
4146
4091
  const { panel: openPanel, showNodeInfo, closeNodeInfo } = useNodeInfoPanel({ selectedNodeId, onSelectedNodeChange });
4147
4092
  const isInFrame = (id) => id === focusNodeId || centerSubtreeNodeIds.has(id) || ringRootById.has(id);
@@ -4248,8 +4193,8 @@ function WheelRadialPopCoreCore({
4248
4193
  centerSubtreeHierarchy,
4249
4194
  0,
4250
4195
  POP_TREE_DEPTH_RADIAL_SPACING
4251
- ) : centerChipDiameter / 2,
4252
- [centerSubtreeHierarchy, centerChipDiameter]
4196
+ ) : NODE_LAYOUT_SLOT_RADIUS,
4197
+ [centerSubtreeHierarchy]
4253
4198
  );
4254
4199
  const maxCoreExtentDelta = (0, import_react13.useMemo)(() => {
4255
4200
  let extent = 0;
@@ -4257,11 +4202,7 @@ function WheelRadialPopCoreCore({
4257
4202
  core.forEach((hierarchy) => {
4258
4203
  extent = Math.max(
4259
4204
  extent,
4260
- calculateCoreSubtreeRadialExtent(
4261
- hierarchy,
4262
- POP_TREE_DEPTH_RADIAL_SPACING,
4263
- 0
4264
- )
4205
+ calculateCoreSubtreeRadialExtent(hierarchy, POP_TREE_DEPTH_RADIAL_SPACING, 0)
4265
4206
  );
4266
4207
  });
4267
4208
  });
@@ -4275,7 +4216,7 @@ function WheelRadialPopCoreCore({
4275
4216
  (radius) => new Map(
4276
4217
  groups.map((group) => {
4277
4218
  const { pop, core } = branchHierarchiesByRootId.get(group.root.id);
4278
- const chipWidth = 2 * calculateNodeDotRadius(rootItemCountById.get(group.root.id), wheelItemCountRange);
4219
+ const chipWidth = 2 * NODE_LAYOUT_SLOT_RADIUS;
4279
4220
  return [
4280
4221
  group.root.id,
4281
4222
  {
@@ -4283,16 +4224,14 @@ function WheelRadialPopCoreCore({
4283
4224
  (hierarchy) => measureWedge(
4284
4225
  d34,
4285
4226
  hierarchy,
4286
- popRadiusOfDepth(radius, POP_TREE_DEPTH_RADIAL_SPACING),
4287
- wheelItemCountRange
4227
+ popRadiusOfDepth(radius, POP_TREE_DEPTH_RADIAL_SPACING)
4288
4228
  )
4289
4229
  ),
4290
4230
  core: core.map(
4291
4231
  (hierarchy) => measureWedge(
4292
4232
  d34,
4293
4233
  hierarchy,
4294
- coreRadiusOfDepth(radius, POP_TREE_DEPTH_RADIAL_SPACING),
4295
- wheelItemCountRange
4234
+ coreRadiusOfDepth(radius, POP_TREE_DEPTH_RADIAL_SPACING)
4296
4235
  )
4297
4236
  ),
4298
4237
  chip: 2 * Math.asin(Math.min(1, (chipWidth / 2 + CHIP_GAP) / radius))
@@ -4300,7 +4239,7 @@ function WheelRadialPopCoreCore({
4300
4239
  ];
4301
4240
  })
4302
4241
  ),
4303
- [groups, branchHierarchiesByRootId, rootItemCountById, wheelItemCountRange]
4242
+ [groups, branchHierarchiesByRootId]
4304
4243
  );
4305
4244
  const coreRootCircleRadius = (0, import_react13.useMemo)(
4306
4245
  () => solveRingRadius(
@@ -4418,8 +4357,7 @@ function WheelRadialPopCoreCore({
4418
4357
  angle,
4419
4358
  coreRootCircleRadius,
4420
4359
  span,
4421
- POP_TREE_DEPTH_RADIAL_SPACING,
4422
- wheelItemCountRange
4360
+ POP_TREE_DEPTH_RADIAL_SPACING
4423
4361
  );
4424
4362
  const reparentForbiddenIds = reparentingNodeId ? laidOut.descendants().find((d) => d.data.id === reparentingNodeId)?.descendants().map((d) => d.data.id) ?? [] : [];
4425
4363
  const branchGroup = sectorGroup.append("g").attr("class", "gtv-wheel-pop-branch").attr("data-gtv-branch-index", branchIndex);
@@ -4463,6 +4401,7 @@ function WheelRadialPopCoreCore({
4463
4401
  {
4464
4402
  rootLinkOrigin,
4465
4403
  selectedNodeId: panel?.node.id ?? null,
4404
+ dotRadiusMemory: dotRadiusMemoryRef.current,
4466
4405
  nodeFillOf: (node) => getGenreNodeStyle(index, node.id).fill
4467
4406
  }
4468
4407
  );
@@ -4475,16 +4414,42 @@ function WheelRadialPopCoreCore({
4475
4414
  );
4476
4415
  const sectorGroup = originGroup.append("g").attr("class", "gtv-wheel-core-sector").attr("data-gtv-root-id", rootId);
4477
4416
  branches.forEach(({ hierarchy, angle, span }, branchIndex) => {
4417
+ const undergroundIds = new Set(
4418
+ hierarchy.descendants().filter((d) => d.ancestors().some((a) => a.data.scene === "underground")).map((d) => d.data.id)
4419
+ );
4478
4420
  const laidOut = computeCoreRadialLayout(
4479
4421
  d34,
4480
4422
  hierarchy,
4481
4423
  angle,
4482
4424
  span,
4483
4425
  coreRootCircleRadius,
4484
- POP_TREE_DEPTH_RADIAL_SPACING,
4485
- wheelItemCountRange
4426
+ POP_TREE_DEPTH_RADIAL_SPACING
4486
4427
  );
4487
4428
  const reparentForbiddenIds = reparentingNodeId ? laidOut.descendants().find((d) => d.data.id === reparentingNodeId)?.descendants().map((d) => d.data.id) ?? [] : [];
4429
+ const angleOf = (d) => {
4430
+ const deg = Math.atan2(d.x, -d.y) * 180 / Math.PI;
4431
+ return angle + ((deg - angle + 540) % 360 + 360) % 360 - 180;
4432
+ };
4433
+ const subtreeRange = (d) => {
4434
+ const angles = d.descendants().map(angleOf);
4435
+ return { min: Math.min(...angles), max: Math.max(...angles) };
4436
+ };
4437
+ laidOut.descendants().filter((d) => d.data.scene === "underground" && !d.ancestors().slice(1).some((a) => a.data.scene === "underground")).forEach((ug) => {
4438
+ const mine = subtreeRange(ug);
4439
+ const siblings = ug.parent?.children ?? [];
4440
+ const at = siblings.indexOf(ug);
4441
+ const prev = at > 0 ? siblings[at - 1] : void 0;
4442
+ const next = at >= 0 ? siblings[at + 1] : void 0;
4443
+ const startDeg = prev ? (subtreeRange(prev).max + mine.min) / 2 : angle - span / 2;
4444
+ const endDeg = next ? (mine.max + subtreeRange(next).min) / 2 : angle + span / 2;
4445
+ const arcPath = d34.arc()({
4446
+ innerRadius: coreRootCircleRadius,
4447
+ outerRadius: SCENE_SECTOR_OUTER_RADIUS,
4448
+ startAngle: startDeg * Math.PI / 180,
4449
+ endAngle: endDeg * Math.PI / 180
4450
+ });
4451
+ sectorGroup.append("path").attr("class", "gtv-wheel-scene-sector").attr("d", arcPath).attr("fill", shadeSurface(getGenreNodeColor(index, rootId), UNDERGROUND_DARKEN_RATIO)).attr("fill-opacity", UNDERGROUND_SECTOR_FILL_OPACITY);
4452
+ });
4488
4453
  const branchGroup = sectorGroup.append("g").attr("class", "gtv-wheel-core-branch").attr("data-gtv-branch-index", branchIndex);
4489
4454
  renderPopSubtree(
4490
4455
  d34,
@@ -4527,7 +4492,11 @@ function WheelRadialPopCoreCore({
4527
4492
  isCoreSector: true,
4528
4493
  rootLinkOrigin,
4529
4494
  selectedNodeId: panel?.node.id ?? null,
4530
- nodeFillOf: (node) => getGenreNodeStyle(index, node.id).fill
4495
+ dotRadiusMemory: dotRadiusMemoryRef.current,
4496
+ nodeFillOf: (node) => {
4497
+ const fill = getGenreNodeStyle(index, node.id).fill;
4498
+ return undergroundIds.has(node.id) ? shadeSurface(fill, UNDERGROUND_DARKEN_RATIO) : fill;
4499
+ }
4531
4500
  }
4532
4501
  );
4533
4502
  });
@@ -4582,6 +4551,7 @@ function WheelRadialPopCoreCore({
4582
4551
  skipRootNode: true,
4583
4552
  isMainstreamSector: isCenterMainstream,
4584
4553
  selectedNodeId: panel?.node.id ?? null,
4554
+ dotRadiusMemory: dotRadiusMemoryRef.current,
4585
4555
  nodeFillOf: (node) => getGenreNodeStyle(index, node.id).fill
4586
4556
  }
4587
4557
  );
@@ -4777,10 +4747,11 @@ function WheelRadialPopCoreCore({
4777
4747
  };
4778
4748
  });
4779
4749
  }, [groups, continuousAngleByRootId, sectorSpanByRootId]);
4780
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
4750
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4781
4751
  "div",
4782
4752
  {
4783
4753
  ref: viewportRef,
4754
+ "aria-busy": busy,
4784
4755
  className: [
4785
4756
  "gtv-wheel-container",
4786
4757
  "gtv-wheel-container--radial",
@@ -4795,7 +4766,7 @@ function WheelRadialPopCoreCore({
4795
4766
  },
4796
4767
  onPointerDown: panZoom.handlePointerDown,
4797
4768
  children: [
4798
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
4769
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
4799
4770
  "div",
4800
4771
  {
4801
4772
  ref: stageRef,
@@ -4809,9 +4780,9 @@ function WheelRadialPopCoreCore({
4809
4780
  transformOrigin: "0 0"
4810
4781
  },
4811
4782
  children: [
4812
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gtv-wheel-stage", children: [
4813
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gtv-wheel-circle", ref: wheelCircleRef }),
4814
- isPopExpanded && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4783
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "gtv-wheel-stage", children: [
4784
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "gtv-wheel-circle", ref: wheelCircleRef }),
4785
+ isPopExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4815
4786
  "div",
4816
4787
  {
4817
4788
  className: "gtv-wheel-middle-circle",
@@ -4820,8 +4791,8 @@ function WheelRadialPopCoreCore({
4820
4791
  }
4821
4792
  }
4822
4793
  ),
4823
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gtv-wheel", children: [
4824
- sectorFills.map((sector) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4794
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "gtv-wheel", children: [
4795
+ sectorFills.map((sector) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4825
4796
  "div",
4826
4797
  {
4827
4798
  className: "gtv-wheel-sector",
@@ -4833,8 +4804,8 @@ function WheelRadialPopCoreCore({
4833
4804
  },
4834
4805
  `sector-${sector.rootId}`
4835
4806
  )),
4836
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gtv-wheel-inner-tint" }),
4837
- isPopExpanded && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4807
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "gtv-wheel-inner-tint" }),
4808
+ isPopExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4838
4809
  "div",
4839
4810
  {
4840
4811
  className: "gtv-wheel-middle-tint",
@@ -4844,7 +4815,7 @@ function WheelRadialPopCoreCore({
4844
4815
  }
4845
4816
  )
4846
4817
  ] }),
4847
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4818
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4848
4819
  "svg",
4849
4820
  {
4850
4821
  ref: popSvgRef,
@@ -4853,7 +4824,7 @@ function WheelRadialPopCoreCore({
4853
4824
  height: svgCanvasRadius * 2
4854
4825
  }
4855
4826
  ),
4856
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gtv-wheel-center-node", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4827
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "gtv-wheel-center-node", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4857
4828
  "button",
4858
4829
  {
4859
4830
  ref: centerChipRef,
@@ -4891,8 +4862,8 @@ function WheelRadialPopCoreCore({
4891
4862
  "aria-label": centerNode.name
4892
4863
  }
4893
4864
  ) }) }),
4894
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gtv-wheel", children: [
4895
- dividerAngles.map((angle, index2) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4865
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "gtv-wheel", children: [
4866
+ dividerAngles.map((angle, index2) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4896
4867
  "div",
4897
4868
  {
4898
4869
  className: "gtv-wheel-divider",
@@ -4911,12 +4882,12 @@ function WheelRadialPopCoreCore({
4911
4882
  group.root.id
4912
4883
  );
4913
4884
  const diameter = 2 * calculateNodeDotRadius(rootItemCount, wheelItemCountRange);
4914
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4885
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4915
4886
  "div",
4916
4887
  {
4917
4888
  className: "gtv-wheel-slot",
4918
4889
  style: { "--gtv-chip-angle": `${angle}deg` },
4919
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4890
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "gtv-wheel-chip-anchor", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4920
4891
  "button",
4921
4892
  {
4922
4893
  type: "button",
@@ -4958,7 +4929,7 @@ function WheelRadialPopCoreCore({
4958
4929
  })
4959
4930
  ] })
4960
4931
  ] }),
4961
- regionLabel && index.byId.has(regionLabel.rootId) && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4932
+ regionLabel && index.byId.has(regionLabel.rootId) && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4962
4933
  "div",
4963
4934
  {
4964
4935
  ref: regionLabelRef,
@@ -4976,7 +4947,7 @@ function WheelRadialPopCoreCore({
4976
4947
  ]
4977
4948
  }
4978
4949
  ),
4979
- panel && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4950
+ panel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4980
4951
  InfoPanel,
4981
4952
  {
4982
4953
  node: panel.node,
@@ -5003,10 +4974,6 @@ function WheelRadialPopCoreCore({
5003
4974
  hideTitle: hideInfoPanelTitle,
5004
4975
  itemCountLabel,
5005
4976
  readOnly,
5006
- editMode,
5007
- onEditModeChange,
5008
- renderTitleEditor,
5009
- renderParentsEditor,
5010
4977
  renderDangerZone,
5011
4978
  expanded,
5012
4979
  onExpandedChange,
@@ -5026,8 +4993,8 @@ function WheelRadialPopCoreCore({
5026
4993
  onZoomIn: onFocusChange && panel.node.id !== focusNodeId && isZoomableNode(nodes, panel.node.id) ? () => onFocusChange(panel.node.id, "zoom-in") : void 0
5027
4994
  }
5028
4995
  ),
5029
- overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(OverlayPanel, { ...overlayPanel }),
5030
- onFocusChange && focusNodeId !== null && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
4996
+ overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OverlayPanel, { ...overlayPanel }),
4997
+ onFocusChange && focusNodeId !== null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5031
4998
  WheelBreadcrumb,
5032
4999
  {
5033
5000
  ancestors: index.ancestorsOf(focusNodeId).filter((ancestor) => isZoomableNode(nodes, ancestor.id)),
@@ -5043,56 +5010,17 @@ function WheelRadialPopCoreCore({
5043
5010
  },
5044
5011
  focusNodeId
5045
5012
  ),
5046
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5047
- "div",
5013
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5014
+ FloatingControls,
5048
5015
  {
5049
- className: [
5050
- "gtv-wheel-floating-controls",
5051
- controlsOrientation === "horizontal" && "gtv-wheel-floating-controls--horizontal"
5052
- ].filter(Boolean).join(" "),
5053
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "gtv-zoom-controls", children: [
5054
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5055
- "button",
5056
- {
5057
- type: "button",
5058
- className: [
5059
- "gtv-zoom-btn",
5060
- !panZoom.canZoomIn && "gtv-zoom-btn--disabled"
5061
- ].filter(Boolean).join(" "),
5062
- disabled: !panZoom.canZoomIn,
5063
- onClick: panZoom.zoomIn,
5064
- "aria-label": "Zoom in",
5065
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_md10.MdZoomIn, { className: "gtv-icon", size: 18 })
5066
- }
5067
- ),
5068
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5069
- "button",
5070
- {
5071
- type: "button",
5072
- className: [
5073
- "gtv-zoom-btn",
5074
- !panZoom.canZoomOut && "gtv-zoom-btn--disabled"
5075
- ].filter(Boolean).join(" "),
5076
- disabled: !panZoom.canZoomOut,
5077
- onClick: panZoom.zoomOut,
5078
- "aria-label": "Zoom out",
5079
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_md10.MdZoomOut, { className: "gtv-icon", size: 18 })
5080
- }
5081
- ),
5082
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5083
- "button",
5084
- {
5085
- type: "button",
5086
- className: "gtv-zoom-btn",
5087
- onClick: () => panZoom.fitToFrame([
5088
- wheelCircleRef.current,
5089
- ...queryTreeContentElements(popSvgRef.current)
5090
- ]),
5091
- "aria-label": "Fit to frame",
5092
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_md10.MdFitScreen, { className: "gtv-icon", size: 18 })
5093
- }
5094
- )
5095
- ] })
5016
+ panZoom,
5017
+ orientation: controlsOrientation,
5018
+ leading: floatingControlsLeading,
5019
+ busy,
5020
+ onFit: () => panZoom.fitToFrame([
5021
+ wheelCircleRef.current,
5022
+ ...queryTreeContentElements(popSvgRef.current)
5023
+ ])
5096
5024
  }
5097
5025
  )
5098
5026
  ]
@@ -5101,14 +5029,14 @@ function WheelRadialPopCoreCore({
5101
5029
  }
5102
5030
 
5103
5031
  // src/GenreTreeWheelRadialPopCore.tsx
5104
- var import_jsx_runtime15 = require("react/jsx-runtime");
5032
+ var import_jsx_runtime17 = require("react/jsx-runtime");
5105
5033
  function GenreTreeWheelRadialPopCore(props) {
5106
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(WheelRadialPopCoreCore, { ...props });
5034
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(WheelRadialPopCoreCore, { ...props });
5107
5035
  }
5108
5036
 
5109
5037
  // src/GenreTreeOutline.tsx
5110
5038
  var import_react14 = require("react");
5111
- var import_jsx_runtime16 = require("react/jsx-runtime");
5039
+ var import_jsx_runtime18 = require("react/jsx-runtime");
5112
5040
  function createOutlineStore(initial) {
5113
5041
  let state = initial;
5114
5042
  const listeners = /* @__PURE__ */ new Set();
@@ -5182,9 +5110,9 @@ function OutlineNodeView({
5182
5110
  const { itemCount } = node;
5183
5111
  const children = model.index.childrenByParentId.get(nodeId) ?? [];
5184
5112
  const split = isRoot ? model.ringSplitById.get(nodeId) : void 0;
5185
- const row = /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: ["gtv-outline-row", isSelected && "gtv-outline-row--selected"].filter(Boolean).join(" "), children: [
5186
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "gtv-outline-dot", style: { background: model.styleOf(node).fill } }),
5187
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5113
+ const row = /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: ["gtv-outline-row", isSelected && "gtv-outline-row--selected"].filter(Boolean).join(" "), children: [
5114
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "gtv-outline-dot", style: { background: model.styleOf(node).fill } }),
5115
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5188
5116
  "button",
5189
5117
  {
5190
5118
  type: "button",
@@ -5201,16 +5129,16 @@ function OutlineNodeView({
5201
5129
  children: node.name
5202
5130
  }
5203
5131
  ),
5204
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "gtv-outline-count", children: itemCount })
5132
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "gtv-outline-count", children: itemCount })
5205
5133
  ] });
5206
5134
  const itemProps = { model, store, callbacks };
5207
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { className: "gtv-outline-item", "data-gtv-node-id": nodeId, children: children.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("details", { open: isOpen, children: [
5208
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("summary", { onClick: (event) => handleSummaryClick(event, () => callbacks.toggleOpen(nodeId)), children: row }),
5209
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "gtv-outline-list", children: split ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
5210
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OutlineSection, { rootId: nodeId, kind: "core", branches: split.coreBranches, ...itemProps }),
5211
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OutlineSection, { rootId: nodeId, kind: "pop", branches: split.popBranches, ...itemProps })
5212
- ] }) : children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OutlineNode, { nodeId: child.id, isRoot: false, ...itemProps }, child.id)) })
5213
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "gtv-outline-leaf", children: row }) });
5135
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("li", { className: "gtv-outline-item", "data-gtv-node-id": nodeId, children: children.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("details", { open: isOpen, children: [
5136
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("summary", { onClick: (event) => handleSummaryClick(event, () => callbacks.toggleOpen(nodeId)), children: row }),
5137
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "gtv-outline-list", children: split ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
5138
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OutlineSection, { rootId: nodeId, kind: "core", branches: split.coreBranches, ...itemProps }),
5139
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OutlineSection, { rootId: nodeId, kind: "pop", branches: split.popBranches, ...itemProps })
5140
+ ] }) : children.map((child) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OutlineNode, { nodeId: child.id, isRoot: false, ...itemProps }, child.id)) })
5141
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "gtv-outline-leaf", children: row }) });
5214
5142
  }
5215
5143
  var OutlineNode = (0, import_react14.memo)(OutlineNodeView);
5216
5144
  var OutlineSection = (0, import_react14.memo)(function OutlineSection2({
@@ -5224,9 +5152,9 @@ var OutlineSection = (0, import_react14.memo)(function OutlineSection2({
5224
5152
  const key = sectionKey(kind, rootId);
5225
5153
  const isOpen = useOutlineState(store, (s) => s.openIds.has(key));
5226
5154
  if (branches.length === 0) return null;
5227
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("li", { className: "gtv-outline-item", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("details", { open: isOpen, children: [
5228
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("summary", { onClick: (event) => handleSummaryClick(event, () => callbacks.toggleOpen(key)), children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "gtv-outline-section-label", children: kind === "core" ? "Core" : "Pop" }) }),
5229
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "gtv-outline-list", children: branches.map(({ child }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OutlineNode, { nodeId: child.id, isRoot: false, model, store, callbacks }, child.id)) })
5155
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("li", { className: "gtv-outline-item", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("details", { open: isOpen, children: [
5156
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("summary", { onClick: (event) => handleSummaryClick(event, () => callbacks.toggleOpen(key)), children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "gtv-outline-section-label", children: kind === "core" ? "Core" : "Pop" }) }),
5157
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "gtv-outline-list", children: branches.map(({ child }) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OutlineNode, { nodeId: child.id, isRoot: false, model, store, callbacks }, child.id)) })
5230
5158
  ] }) });
5231
5159
  });
5232
5160
  function GenreTreeOutline(props) {
@@ -5243,10 +5171,6 @@ function GenreTreeOutline(props) {
5243
5171
  hideInfoPanelTitle = false,
5244
5172
  itemCountLabel,
5245
5173
  readOnly,
5246
- editMode,
5247
- onEditModeChange,
5248
- renderTitleEditor,
5249
- renderParentsEditor,
5250
5174
  renderDangerZone,
5251
5175
  expanded,
5252
5176
  onExpandedChange,
@@ -5347,12 +5271,12 @@ function GenreTreeOutline(props) {
5347
5271
  }, [selectedNodeId, store]);
5348
5272
  const selectedNode = selectedId === null ? void 0 : index.byId.get(selectedId);
5349
5273
  const itemProps = { model, store, callbacks };
5350
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: ["gtv-outline", selectedNode && "gtv-outline--panel-open", className].filter(Boolean).join(" "), children: [
5351
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "gtv-outline-scroll", ref: scrollRef, children: [
5352
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "gtv-outline-list gtv-outline-list--roots", children: [model.centerRoot, ...model.ringRoots].map((root) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OutlineNode, { nodeId: root.id, isRoot: true, ...itemProps }, root.id)) }),
5353
- model.detachedRoots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("ul", { className: "gtv-outline-list gtv-outline-list--roots gtv-outline-list--detached", children: model.detachedRoots.map((root) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OutlineNode, { nodeId: root.id, isRoot: true, ...itemProps }, root.id)) })
5274
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: ["gtv-outline", selectedNode && "gtv-outline--panel-open", className].filter(Boolean).join(" "), children: [
5275
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "gtv-outline-scroll", ref: scrollRef, children: [
5276
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "gtv-outline-list gtv-outline-list--roots", children: [model.centerRoot, ...model.ringRoots].map((root) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OutlineNode, { nodeId: root.id, isRoot: true, ...itemProps }, root.id)) }),
5277
+ model.detachedRoots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("ul", { className: "gtv-outline-list gtv-outline-list--roots gtv-outline-list--detached", children: model.detachedRoots.map((root) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OutlineNode, { nodeId: root.id, isRoot: true, ...itemProps }, root.id)) })
5354
5278
  ] }),
5355
- selectedNode && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5279
+ selectedNode && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5356
5280
  InfoPanel,
5357
5281
  {
5358
5282
  node: selectedNode,
@@ -5373,10 +5297,6 @@ function GenreTreeOutline(props) {
5373
5297
  hideTitle: hideInfoPanelTitle,
5374
5298
  itemCountLabel,
5375
5299
  readOnly,
5376
- editMode,
5377
- onEditModeChange,
5378
- renderTitleEditor,
5379
- renderParentsEditor,
5380
5300
  renderDangerZone,
5381
5301
  expanded,
5382
5302
  onExpandedChange,
@@ -5395,13 +5315,13 @@ function GenreTreeOutline(props) {
5395
5315
  } : void 0
5396
5316
  }
5397
5317
  ),
5398
- overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OverlayPanel, { ...overlayPanel })
5318
+ overlayPanel && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(OverlayPanel, { ...overlayPanel })
5399
5319
  ] });
5400
5320
  }
5401
5321
 
5402
5322
  // src/GenreTreeSkeleton.tsx
5403
5323
  var import_react15 = require("react");
5404
- var import_jsx_runtime17 = require("react/jsx-runtime");
5324
+ var import_jsx_runtime19 = require("react/jsx-runtime");
5405
5325
  var CARD_FILL = "#F4F4F5";
5406
5326
  var CARD_BORDER_COLOR = "#E4E4E7";
5407
5327
  var CONNECTOR_COLOR2 = "#D4D4D8";
@@ -5506,8 +5426,8 @@ function SkeletonCard({
5506
5426
  card,
5507
5427
  rootAccent
5508
5428
  }) {
5509
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
5510
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5429
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
5430
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5511
5431
  "rect",
5512
5432
  {
5513
5433
  x: card.x,
@@ -5521,7 +5441,7 @@ function SkeletonCard({
5521
5441
  strokeWidth: rootAccent ? 1.5 : 1
5522
5442
  }
5523
5443
  ),
5524
- rootAccent && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5444
+ rootAccent && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5525
5445
  "circle",
5526
5446
  {
5527
5447
  cx: card.x + 18,
@@ -5539,9 +5459,9 @@ function GenreTreeSkeleton() {
5539
5459
  const animationName = `genre-tree-skeleton-sweep-${uid}`;
5540
5460
  const shimmerBandClass = `genre-tree-skeleton-shimmer-band-${uid}`;
5541
5461
  const sweepDistance = VIEWBOX_WIDTH + SHIMMER_BAND_WIDTH;
5542
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "gtv-skeleton", children: [
5543
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "gtv-skeleton-sr-only", children: "Loading genre tree\u2026" }),
5544
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
5462
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "gtv-skeleton", children: [
5463
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "gtv-skeleton-sr-only", children: "Loading genre tree\u2026" }),
5464
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
5545
5465
  "svg",
5546
5466
  {
5547
5467
  viewBox: `0 0 ${VIEWBOX_WIDTH} ${VIEWBOX_HEIGHT}`,
@@ -5550,7 +5470,7 @@ function GenreTreeSkeleton() {
5550
5470
  className: "gtv-skeleton-svg",
5551
5471
  "aria-hidden": "true",
5552
5472
  children: [
5553
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("style", { children: `
5473
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("style", { children: `
5554
5474
  @keyframes ${animationName} {
5555
5475
  from { transform: translateX(0); }
5556
5476
  to { transform: translateX(${sweepDistance}px); }
@@ -5564,9 +5484,9 @@ function GenreTreeSkeleton() {
5564
5484
  }
5565
5485
  }
5566
5486
  ` }),
5567
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("defs", { children: [
5568
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "1", y2: "0", children: [
5569
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5487
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("defs", { children: [
5488
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "1", y2: "0", children: [
5489
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5570
5490
  "stop",
5571
5491
  {
5572
5492
  offset: "0%",
@@ -5574,7 +5494,7 @@ function GenreTreeSkeleton() {
5574
5494
  stopOpacity: 0
5575
5495
  }
5576
5496
  ),
5577
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5497
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5578
5498
  "stop",
5579
5499
  {
5580
5500
  offset: "50%",
@@ -5582,7 +5502,7 @@ function GenreTreeSkeleton() {
5582
5502
  stopOpacity: 0.9
5583
5503
  }
5584
5504
  ),
5585
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5505
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5586
5506
  "stop",
5587
5507
  {
5588
5508
  offset: "100%",
@@ -5591,8 +5511,8 @@ function GenreTreeSkeleton() {
5591
5511
  }
5592
5512
  )
5593
5513
  ] }),
5594
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("mask", { id: maskId, children: [
5595
- ALL_LINKS.map((link, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5514
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("mask", { id: maskId, children: [
5515
+ ALL_LINKS.map((link, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5596
5516
  "path",
5597
5517
  {
5598
5518
  d: connectorPath(link.from, link.to),
@@ -5602,7 +5522,7 @@ function GenreTreeSkeleton() {
5602
5522
  },
5603
5523
  `link-mask-${i}`
5604
5524
  )),
5605
- ALL_CARDS.map(({ card }, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5525
+ ALL_CARDS.map(({ card }, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5606
5526
  "rect",
5607
5527
  {
5608
5528
  x: card.x,
@@ -5617,7 +5537,7 @@ function GenreTreeSkeleton() {
5617
5537
  ))
5618
5538
  ] })
5619
5539
  ] }),
5620
- ALL_LINKS.map((link, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5540
+ ALL_LINKS.map((link, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5621
5541
  "path",
5622
5542
  {
5623
5543
  d: connectorPath(link.from, link.to),
@@ -5627,8 +5547,8 @@ function GenreTreeSkeleton() {
5627
5547
  },
5628
5548
  `link-${i}`
5629
5549
  )),
5630
- ALL_CARDS.map(({ card, rootAccent }, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SkeletonCard, { card, rootAccent }, `card-${i}`)),
5631
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("g", { mask: `url(#${maskId})`, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5550
+ ALL_CARDS.map(({ card, rootAccent }, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SkeletonCard, { card, rootAccent }, `card-${i}`)),
5551
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("g", { mask: `url(#${maskId})`, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5632
5552
  "rect",
5633
5553
  {
5634
5554
  className: shimmerBandClass,
@@ -5647,7 +5567,7 @@ function GenreTreeSkeleton() {
5647
5567
 
5648
5568
  // src/GenreTreeWheelSkeleton.tsx
5649
5569
  var import_react16 = require("react");
5650
- var import_jsx_runtime18 = require("react/jsx-runtime");
5570
+ var import_jsx_runtime20 = require("react/jsx-runtime");
5651
5571
  var SHAPE_COLOR = "#D4D4D8";
5652
5572
  var SECTOR_FILL_OPACITY = 0.22;
5653
5573
  var INNER_TINT_OPACITY = 0.55;
@@ -5792,8 +5712,8 @@ function GenreTreeWheelSkeleton() {
5792
5712
  sharedStartTime ??= animation.startTime ?? document.timeline.currentTime;
5793
5713
  animation.startTime = sharedStartTime;
5794
5714
  }, []);
5795
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "gtv-wheel-skeleton", children: [
5796
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5715
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "gtv-wheel-skeleton", children: [
5716
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5797
5717
  "div",
5798
5718
  {
5799
5719
  className: "gtv-wheel-skeleton-sector-layer",
@@ -5801,8 +5721,8 @@ function GenreTreeWheelSkeleton() {
5801
5721
  "aria-hidden": "true"
5802
5722
  }
5803
5723
  ),
5804
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "gtv-skeleton-sr-only", children: "Loading genre tree\u2026" }),
5805
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
5724
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "gtv-skeleton-sr-only", children: "Loading genre tree\u2026" }),
5725
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5806
5726
  "svg",
5807
5727
  {
5808
5728
  viewBox: `${CANVAS_ORIGIN} ${CANVAS_ORIGIN} ${CANVAS_SIZE} ${CANVAS_SIZE}`,
@@ -5810,14 +5730,14 @@ function GenreTreeWheelSkeleton() {
5810
5730
  className: "gtv-wheel-skeleton-svg",
5811
5731
  "aria-hidden": "true",
5812
5732
  children: [
5813
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("defs", { children: [
5814
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("radialGradient", { id: mainstreamId, children: [
5815
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("stop", { offset: "0%", stopColor: "#FFFFFF", stopOpacity: 0.45 }),
5816
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("stop", { offset: "65%", stopColor: "#FFFFFF", stopOpacity: 0.15 }),
5817
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("stop", { offset: "100%", stopColor: "#FFFFFF", stopOpacity: 0 })
5733
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("defs", { children: [
5734
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("radialGradient", { id: mainstreamId, children: [
5735
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("stop", { offset: "0%", stopColor: "#FFFFFF", stopOpacity: 0.45 }),
5736
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("stop", { offset: "65%", stopColor: "#FFFFFF", stopOpacity: 0.15 }),
5737
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("stop", { offset: "100%", stopColor: "#FFFFFF", stopOpacity: 0 })
5818
5738
  ] }),
5819
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("g", { id: shapesId, children: [
5820
- LINKS.map((link, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
5739
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("g", { id: shapesId, children: [
5740
+ LINKS.map((link, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5821
5741
  "path",
5822
5742
  {
5823
5743
  d: link.d,
@@ -5829,21 +5749,21 @@ function GenreTreeWheelSkeleton() {
5829
5749
  },
5830
5750
  `link-${i}`
5831
5751
  )),
5832
- DOTS.map((dot, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: dot.point.x, cy: dot.point.y, r: dot.r, fill: "currentColor" }, `dot-${i}`))
5752
+ DOTS.map((dot, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: dot.point.x, cy: dot.point.y, r: dot.r, fill: "currentColor" }, `dot-${i}`))
5833
5753
  ] })
5834
5754
  ] }),
5835
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: 0, cy: 0, r: RING_RADIUS, fill: "#FFFFFF", fillOpacity: INNER_TINT_OPACITY }),
5836
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("circle", { cx: 0, cy: 0, r: MAINSTREAM_RADIUS, fill: `url(#${mainstreamId})` }),
5837
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("use", { href: `#${shapesId}`, color: SHAPE_COLOR })
5755
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: 0, cy: 0, r: RING_RADIUS, fill: "#FFFFFF", fillOpacity: INNER_TINT_OPACITY }),
5756
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("circle", { cx: 0, cy: 0, r: MAINSTREAM_RADIUS, fill: `url(#${mainstreamId})` }),
5757
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("use", { href: `#${shapesId}`, color: SHAPE_COLOR })
5838
5758
  ]
5839
5759
  }
5840
5760
  ),
5841
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "gtv-wheel-skeleton-shimmer", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: wedgeRef, className: "gtv-wheel-skeleton-shimmer-wedge" }) })
5761
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "gtv-wheel-skeleton-shimmer", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { ref: wedgeRef, className: "gtv-wheel-skeleton-shimmer-wedge" }) })
5842
5762
  ] });
5843
5763
  }
5844
5764
 
5845
5765
  // src/GenreTreeOutlineSkeleton.tsx
5846
- var import_jsx_runtime19 = require("react/jsx-runtime");
5766
+ var import_jsx_runtime21 = require("react/jsx-runtime");
5847
5767
  var ROWS = [
5848
5768
  { depth: 0, width: 38 },
5849
5769
  { depth: 0, width: 26 },
@@ -5855,9 +5775,9 @@ var ROWS = [
5855
5775
  { depth: 0, width: 22 }
5856
5776
  ];
5857
5777
  function GenreTreeOutlineSkeleton() {
5858
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "gtv-skeleton gtv-outline-skeleton", children: [
5859
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "gtv-skeleton-sr-only", children: "Loading genre tree\u2026" }),
5860
- ROWS.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
5778
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "gtv-skeleton gtv-outline-skeleton", children: [
5779
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "gtv-skeleton-sr-only", children: "Loading genre tree\u2026" }),
5780
+ ROWS.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5861
5781
  "div",
5862
5782
  {
5863
5783
  className: "gtv-outline-skeleton-row",
@@ -5870,13 +5790,13 @@ function GenreTreeOutlineSkeleton() {
5870
5790
  }
5871
5791
 
5872
5792
  // src/GenreTreeViewSkeleton.tsx
5873
- var import_jsx_runtime20 = require("react/jsx-runtime");
5793
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5874
5794
  function GenreTreeViewSkeleton({ viewMode }) {
5875
5795
  if (viewMode === "wheel" || viewMode === "pop-core") {
5876
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "gtv-view-skeleton-wheel-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GenreTreeWheelSkeleton, {}) });
5796
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "gtv-view-skeleton-wheel-wrapper", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenreTreeWheelSkeleton, {}) });
5877
5797
  }
5878
- if (viewMode === "outline") return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GenreTreeOutlineSkeleton, {});
5879
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(GenreTreeSkeleton, {});
5798
+ if (viewMode === "outline") return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenreTreeOutlineSkeleton, {});
5799
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GenreTreeSkeleton, {});
5880
5800
  }
5881
5801
  // Annotate the CommonJS export names for ESM import in node:
5882
5802
  0 && (module.exports = {