@behindthemusictree/app-kit-ui 39.0.0 → 40.1.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.
@@ -50,12 +50,12 @@ module.exports = __toCommonJS(genre_tree_exports);
50
50
  // src/genre-tree/GenreTreeView.tsx
51
51
  var import_react19 = require("react");
52
52
  var import_react_query3 = require("@tanstack/react-query");
53
+ var import_lucide_react13 = require("lucide-react");
53
54
  var import_lucide_react14 = require("lucide-react");
54
- var import_lucide_react15 = require("lucide-react");
55
55
  var import_ui21 = require("@behindthemusictree/ui");
56
56
  var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
57
57
  var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
58
- var import_popup5 = require("@behindthemusictree/app-kit/popup");
58
+ var import_popup4 = require("@behindthemusictree/app-kit/popup");
59
59
 
60
60
  // src/genre-tree/playlist-tree/size-metric.ts
61
61
  var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
@@ -69,7 +69,7 @@ var genrePlaylistSize = (genrePlaylist, metric) => {
69
69
  };
70
70
 
71
71
  // src/genre-tree/playlist-tree/toTreeNodes.ts
72
- function toTreeNodes(genrePlaylists, sizeMetric) {
72
+ function toTreeNodes(genrePlaylists, sizeMetric, focusUuids) {
73
73
  return genrePlaylists.map((genrePlaylist) => ({
74
74
  id: genrePlaylist.uuid,
75
75
  parentId: genrePlaylist.parent?.uuid ?? null,
@@ -80,16 +80,37 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
80
80
  side: genrePlaylist.criteria?.side ?? void 0,
81
81
  wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
82
82
  umbrella: genrePlaylist.isUmbrella,
83
- scene: genrePlaylist.scene ?? void 0
83
+ scene: genrePlaylist.scene ?? void 0,
84
+ muted: focusUuids ? !focusUuids.has(genrePlaylist.uuid) : void 0
84
85
  }));
85
86
  }
86
87
 
88
+ // src/genre-tree/playlist-tree/toGenreNodeStyles.ts
89
+ function toGenreNodeStyles(relations, genrePlaylists) {
90
+ const styles = /* @__PURE__ */ new Map();
91
+ if (!relations) return styles;
92
+ const genreUuidByPlaylistUuid = new Map(
93
+ genrePlaylists.flatMap((genrePlaylist) => genrePlaylist.criteria ? [[genrePlaylist.uuid, genrePlaylist.criteria.uuid]] : [])
94
+ );
95
+ const drawn = [relations, relations.parentNode, ...relations.childNodes, ...relations.ancestorNodes];
96
+ for (const entry of drawn) {
97
+ const genreUuid = entry && genreUuidByPlaylistUuid.get(entry.node.id);
98
+ if (entry && genreUuid) styles.set(genreUuid, { fill: entry.fill, textColor: entry.textColor });
99
+ }
100
+ return styles;
101
+ }
102
+
87
103
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
88
104
  var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
89
105
  var import_react3 = require("react");
90
106
  var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
91
107
  var import_player = require("@behindthemusictree/app-kit/player");
108
+
109
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
110
+ var import_react2 = require("react");
111
+ var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
92
112
  var import_popup = require("@behindthemusictree/app-kit/popup");
113
+ var import_transport = require("@behindthemusictree/app-kit/transport");
93
114
 
94
115
  // src/popup/InternalErrorPopup.tsx
95
116
  var import_lucide_react2 = require("lucide-react");
@@ -305,18 +326,15 @@ function InternalErrorPopup({ errorCode, contactEmail, ...rest }) {
305
326
  );
306
327
  }
307
328
 
308
- // src/genre-tree/playlist-tree/TreePerRoot.tsx
309
- var import_transport = require("@behindthemusictree/app-kit/transport");
310
-
311
- // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
312
- var import_react2 = require("react");
313
- var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
329
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
330
+ var import_jsx_runtime5 = require("react/jsx-runtime");
314
331
  function useGenrePlaylistPlayback({
315
332
  scope,
316
333
  genrePlaylists,
317
334
  artist = null
318
335
  }) {
319
336
  const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
337
+ const { showPopup } = (0, import_popup.usePopup)();
320
338
  const origin = trackList?.origin;
321
339
  const playing = (0, import_react2.useMemo)(() => {
322
340
  if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
@@ -328,14 +346,20 @@ function useGenrePlaylistPlayback({
328
346
  }, [origin, artist, genrePlaylists, scope]);
329
347
  const play = (0, import_react2.useCallback)(async (playlist, requested) => {
330
348
  if (artist && !playlist.criteria) return;
331
- const selection = scope === "reference" && playlist.criteria !== null ? requested ?? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
332
- return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
333
- }, [artist, scope, playNewTrackListFromGenrePlaylist]);
349
+ try {
350
+ const selection = scope === "reference" && playlist.criteria !== null ? requested ?? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
351
+ await (selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope));
352
+ } catch (error) {
353
+ console.error("Failed to load genre playlist tracks:", error);
354
+ if (error instanceof import_genre_tree2.TracksFailureAlreadyReportedError) return;
355
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport.AppError ? error.code : import_transport.ErrorCode.CLIENT_UNKNOWN }));
356
+ }
357
+ }, [artist, scope, playNewTrackListFromGenrePlaylist, showPopup]);
334
358
  return (0, import_react2.useMemo)(() => ({ playing, play }), [playing, play]);
335
359
  }
336
360
 
337
361
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
338
- var import_jsx_runtime5 = require("react/jsx-runtime");
362
+ var import_jsx_runtime6 = require("react/jsx-runtime");
339
363
  function GenrePlaylistTreePerRoot({
340
364
  scope,
341
365
  className: className2,
@@ -354,25 +378,16 @@ function GenrePlaylistTreePerRoot({
354
378
  viewportRestore,
355
379
  onViewportChange,
356
380
  playback,
381
+ focusUuids,
357
382
  ...hostPanel
358
383
  }) {
359
384
  const { isPlaying, setIsPlaying } = (0, import_player.usePlayer)();
360
385
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
361
386
  const { playing, play } = playback ?? defaultPlayback;
362
387
  const playingNodeId = playing?.uuid ?? null;
363
- const { showPopup } = (0, import_popup.usePopup)();
364
388
  const nodes = (0, import_react3.useMemo)(
365
- () => genrePlaylistTreePerRoot.map((genrePlaylist) => ({
366
- id: genrePlaylist.uuid,
367
- parentId: genrePlaylist.parent?.uuid ?? null,
368
- name: genrePlaylist.name,
369
- itemCount: (0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist),
370
- actionable: Boolean(genrePlaylist.criteria),
371
- side: genrePlaylist.criteria?.side ?? void 0,
372
- wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
373
- umbrella: genrePlaylist.isUmbrella
374
- })),
375
- [genrePlaylistTreePerRoot]
389
+ () => toTreeNodes(genrePlaylistTreePerRoot, "songs", focusUuids),
390
+ [genrePlaylistTreePerRoot, focusUuids]
376
391
  );
377
392
  const handlePlayPause = (0, import_react3.useCallback)(
378
393
  (nodeId) => {
@@ -385,18 +400,14 @@ function GenrePlaylistTreePerRoot({
385
400
  if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
386
401
  return;
387
402
  }
388
- play(genrePlaylist).catch((error) => {
389
- console.error("Failed to load genre playlist tracks:", error);
390
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InternalErrorPopup, { errorCode: import_transport.ErrorCode.CLIENT_UNKNOWN }));
391
- });
403
+ void play(genrePlaylist);
392
404
  },
393
405
  [
394
406
  genrePlaylistTreePerRoot,
395
407
  playingNodeId,
396
408
  isPlaying,
397
409
  setIsPlaying,
398
- play,
399
- showPopup
410
+ play
400
411
  ]
401
412
  );
402
413
  const handleAddChild = (0, import_react3.useCallback)(
@@ -419,7 +430,7 @@ function GenrePlaylistTreePerRoot({
419
430
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
420
431
  }
421
432
  }, []);
422
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
433
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
423
434
  import_genre_tree_view.GenreTree,
424
435
  {
425
436
  className: className2,
@@ -450,9 +461,7 @@ var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
450
461
  var import_react4 = require("react");
451
462
  var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
452
463
  var import_player2 = require("@behindthemusictree/app-kit/player");
453
- var import_popup2 = require("@behindthemusictree/app-kit/popup");
454
- var import_transport2 = require("@behindthemusictree/app-kit/transport");
455
- var import_jsx_runtime6 = require("react/jsx-runtime");
464
+ var import_jsx_runtime7 = require("react/jsx-runtime");
456
465
  function GenrePlaylistTreeWheel({
457
466
  scope,
458
467
  className: className2,
@@ -473,24 +482,16 @@ function GenrePlaylistTreeWheel({
473
482
  viewportRestore,
474
483
  onViewportChange,
475
484
  playback,
485
+ focusUuids,
476
486
  ...hostPanel
477
487
  }) {
478
488
  const { isPlaying, setIsPlaying } = (0, import_player2.usePlayer)();
479
489
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
480
490
  const { playing, play } = playback ?? defaultPlayback;
481
491
  const playingNodeId = playing?.uuid ?? null;
482
- const { showPopup } = (0, import_popup2.usePopup)();
483
492
  const nodes = (0, import_react4.useMemo)(
484
- () => genrePlaylists.map((genrePlaylist) => ({
485
- id: genrePlaylist.uuid,
486
- parentId: genrePlaylist.parent?.uuid ?? null,
487
- name: genrePlaylist.name,
488
- itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
489
- actionable: Boolean(genrePlaylist.criteria),
490
- side: genrePlaylist.criteria?.side ?? void 0,
491
- wikidataId: genrePlaylist.criteria?.wikidataId ?? null
492
- })),
493
- [genrePlaylists, sizeMetric]
493
+ () => toTreeNodes(genrePlaylists, sizeMetric, focusUuids),
494
+ [genrePlaylists, sizeMetric, focusUuids]
494
495
  );
495
496
  const handlePlayPause = (0, import_react4.useCallback)(
496
497
  (nodeId) => {
@@ -503,18 +504,14 @@ function GenrePlaylistTreeWheel({
503
504
  if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
504
505
  return;
505
506
  }
506
- play(genrePlaylist).catch((error) => {
507
- console.error("Failed to load genre playlist tracks:", error);
508
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(InternalErrorPopup, { errorCode: import_transport2.ErrorCode.CLIENT_UNKNOWN }));
509
- });
507
+ void play(genrePlaylist);
510
508
  },
511
509
  [
512
510
  genrePlaylists,
513
511
  playingNodeId,
514
512
  isPlaying,
515
513
  setIsPlaying,
516
- play,
517
- showPopup
514
+ play
518
515
  ]
519
516
  );
520
517
  const handleAddChild = (0, import_react4.useCallback)(
@@ -537,7 +534,7 @@ function GenrePlaylistTreeWheel({
537
534
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
538
535
  }
539
536
  }, []);
540
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
537
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
541
538
  import_genre_tree_view2.GenreTreeWheel,
542
539
  {
543
540
  className: className2,
@@ -569,7 +566,7 @@ var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
569
566
  var import_react5 = require("react");
570
567
  var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
571
568
  var import_player3 = require("@behindthemusictree/app-kit/player");
572
- var import_jsx_runtime7 = require("react/jsx-runtime");
569
+ var import_jsx_runtime8 = require("react/jsx-runtime");
573
570
  function GenrePlaylistTreeWheelRadialPopCore(props) {
574
571
  const {
575
572
  scope,
@@ -602,7 +599,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
602
599
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
603
600
  const { playing, play } = playback ?? defaultPlayback;
604
601
  const playingNodeId = playing?.uuid ?? null;
605
- const nodes = (0, import_react5.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
602
+ const nodes = (0, import_react5.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric, props.focusUuids), [genrePlaylists, sizeMetric, props.focusUuids]);
606
603
  const handlePlayPause = (0, import_react5.useCallback)(
607
604
  (nodeId) => {
608
605
  const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
@@ -614,9 +611,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
614
611
  if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
615
612
  return;
616
613
  }
617
- play(genrePlaylist).catch((error) => {
618
- console.error("Failed to load genre playlist tracks:", error);
619
- });
614
+ void play(genrePlaylist);
620
615
  },
621
616
  [genrePlaylists, playingNodeId, isPlaying, setIsPlaying, play]
622
617
  );
@@ -656,7 +651,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
656
651
  onSelectedNodeChange,
657
652
  ...hostPanel
658
653
  };
659
- return outline ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
654
+ return outline ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
660
655
  import_genre_tree_view3.GenreTreeWheelRadialPopCore,
661
656
  {
662
657
  ...treeProps,
@@ -673,7 +668,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
673
668
 
674
669
  // src/genre-tree/GenreTreeWheelHandoff.tsx
675
670
  var import_react6 = require("react");
676
- var import_jsx_runtime8 = require("react/jsx-runtime");
671
+ var import_jsx_runtime9 = require("react/jsx-runtime");
677
672
  function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
678
673
  const [revealed, setRevealed] = (0, import_react6.useState)(false);
679
674
  const onRevealedChangeRef = (0, import_react6.useRef)(onRevealedChange);
@@ -692,8 +687,8 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
692
687
  cancelAnimationFrame(innerFrame);
693
688
  };
694
689
  }, []);
695
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative w-full h-full", children: [
696
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
690
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative w-full h-full", children: [
691
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
697
692
  "div",
698
693
  {
699
694
  className: "absolute inset-0 transition-opacity duration-150 ease-out",
@@ -702,7 +697,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
702
697
  children
703
698
  }
704
699
  ),
705
- !revealed && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
700
+ !revealed && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
706
701
  ] });
707
702
  }
708
703
 
@@ -717,7 +712,7 @@ var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
717
712
  // src/genre-tree/search/SearchBox.tsx
718
713
  var import_react7 = require("react");
719
714
  var import_ui3 = require("@behindthemusictree/ui");
720
- var import_jsx_runtime9 = require("react/jsx-runtime");
715
+ var import_jsx_runtime10 = require("react/jsx-runtime");
721
716
  function SearchBox({
722
717
  label,
723
718
  placeholder,
@@ -739,7 +734,7 @@ function SearchBox({
739
734
  groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
740
735
  onPicked?.(groupKey, itemKey);
741
736
  };
742
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
737
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
743
738
  import_ui3.SearchCombobox,
744
739
  {
745
740
  ariaLabel: label,
@@ -771,7 +766,7 @@ var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS
771
766
 
772
767
  // src/genre-tree/search/useArtistSearchGroup.tsx
773
768
  var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
774
- var import_jsx_runtime10 = require("react/jsx-runtime");
769
+ var import_jsx_runtime11 = require("react/jsx-runtime");
775
770
  function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
776
771
  const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
777
772
  if (!enabled) return void 0;
@@ -780,15 +775,15 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
780
775
  label: "Artists",
781
776
  items: search.artists.map((artist) => ({
782
777
  key: artist.uuid,
783
- content: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
784
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "block truncate font-medium", children: [
778
+ content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "block truncate font-medium", children: [
785
780
  artist.name,
786
- artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
781
+ artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
787
782
  " \xB7 ",
788
783
  artist.disambiguation
789
784
  ] })
790
785
  ] }),
791
- artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
786
+ artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
792
787
  "also known as ",
793
788
  artist.matchedAlias
794
789
  ] })
@@ -802,7 +797,7 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
802
797
 
803
798
  // src/genre-tree/search/useSongSearchGroup.tsx
804
799
  var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
805
- var import_jsx_runtime11 = require("react/jsx-runtime");
800
+ var import_jsx_runtime12 = require("react/jsx-runtime");
806
801
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
807
802
  const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
808
803
  if (!enabled) return void 0;
@@ -811,9 +806,9 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
811
806
  label: "Songs",
812
807
  items: search.songs.map((song) => ({
813
808
  key: song.uuid,
814
- content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
815
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "block truncate font-medium", children: song.title }),
816
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
809
+ content: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
810
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block truncate font-medium", children: song.title }),
811
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
817
812
  ] }),
818
813
  onSelect: () => onSelect(song)
819
814
  })),
@@ -824,7 +819,7 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
824
819
 
825
820
  // src/genre-tree/search/useGenreRemoteSearchGroup.tsx
826
821
  var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
827
- var import_jsx_runtime12 = require("react/jsx-runtime");
822
+ var import_jsx_runtime13 = require("react/jsx-runtime");
828
823
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
829
824
  const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
830
825
  const genres = filter ? search.genres.filter(filter) : search.genres;
@@ -833,12 +828,12 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
833
828
  label,
834
829
  items: genres.map((genre) => ({
835
830
  key: genre.uuid,
836
- content: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
837
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "block truncate font-medium", children: [
831
+ content: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
832
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "block truncate font-medium", children: [
838
833
  genre.name,
839
- genre.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
834
+ genre.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
840
835
  ] }),
841
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
836
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
842
837
  ] }),
843
838
  onSelect: () => onSelect(genre)
844
839
  })),
@@ -848,7 +843,7 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
848
843
  }
849
844
 
850
845
  // src/genre-tree/GenreSearch.tsx
851
- var import_jsx_runtime13 = require("react/jsx-runtime");
846
+ var import_jsx_runtime14 = require("react/jsx-runtime");
852
847
  function GenreSearch({
853
848
  genrePlaylists,
854
849
  onSelect,
@@ -877,9 +872,9 @@ function GenreSearch({
877
872
  label: hasExtras ? "Genres" : void 0,
878
873
  items: results.map((genrePlaylist) => ({
879
874
  key: genrePlaylist.uuid,
880
- content: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
875
+ content: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
881
876
  genrePlaylist.name,
882
- genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
877
+ genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
883
878
  ] }),
884
879
  onSelect: () => select(genrePlaylist)
885
880
  }))
@@ -888,7 +883,7 @@ function GenreSearch({
888
883
  (group) => active ? group : { ...group, items: [], status: "", loading: false }
889
884
  )
890
885
  ];
891
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
886
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
892
887
  SearchBox,
893
888
  {
894
889
  label,
@@ -908,14 +903,14 @@ function GenreSearch({
908
903
  clear();
909
904
  onQueryChange?.("");
910
905
  } : void 0,
911
- leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui4.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
906
+ leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui4.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react3.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
912
907
  onFocus
913
908
  }
914
909
  ) });
915
910
  }
916
911
 
917
912
  // src/genre-tree/GenreTreeSearch.tsx
918
- var import_jsx_runtime14 = require("react/jsx-runtime");
913
+ var import_jsx_runtime15 = require("react/jsx-runtime");
919
914
  function GenreTreeSearch({
920
915
  artistBaseUrl,
921
916
  songBaseUrl,
@@ -935,7 +930,7 @@ function GenreTreeSearch({
935
930
  const deferredQuery = (0, import_react8.useDeferredValue)(query);
936
931
  const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
937
932
  const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
938
- const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
933
+ const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
939
934
  GenreSearch,
940
935
  {
941
936
  ...props,
@@ -950,7 +945,7 @@ function GenreTreeSearch({
950
945
  extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
951
946
  }
952
947
  );
953
- return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
948
+ return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
954
949
  }
955
950
 
956
951
  // src/genre-tree/GenreDetailPanel.tsx
@@ -970,7 +965,7 @@ var import_ui6 = require("@behindthemusictree/ui");
970
965
  // src/genre-tree/ShareActionButton.tsx
971
966
  var import_lucide_react6 = require("lucide-react");
972
967
  var import_ui5 = require("@behindthemusictree/ui");
973
- var import_popup3 = require("@behindthemusictree/app-kit/popup");
968
+ var import_popup2 = require("@behindthemusictree/app-kit/popup");
974
969
 
975
970
  // src/genre-tree/SharePopup.tsx
976
971
  var import_react10 = require("react");
@@ -981,7 +976,7 @@ var import_si = require("react-icons/si");
981
976
  // src/popup/Toast.tsx
982
977
  var import_react9 = require("react");
983
978
  var import_react_dom2 = require("react-dom");
984
- var import_jsx_runtime15 = require("react/jsx-runtime");
979
+ var import_jsx_runtime16 = require("react/jsx-runtime");
985
980
  function Toast({ message, onClose, durationMs = 2200 }) {
986
981
  const [mounted, setMounted] = (0, import_react9.useState)(false);
987
982
  (0, import_react9.useEffect)(() => {
@@ -991,7 +986,7 @@ function Toast({ message, onClose, durationMs = 2200 }) {
991
986
  }, [durationMs, onClose]);
992
987
  if (!mounted) return null;
993
988
  return (0, import_react_dom2.createPortal)(
994
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "pointer-events-none fixed inset-x-0 bottom-8 z-[60] flex justify-center px-4", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
989
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "pointer-events-none fixed inset-x-0 bottom-8 z-[60] flex justify-center px-4", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
995
990
  "div",
996
991
  {
997
992
  role: "status",
@@ -1006,12 +1001,12 @@ function Toast({ message, onClose, durationMs = 2200 }) {
1006
1001
  }
1007
1002
 
1008
1003
  // src/genre-tree/ShareDestinationButton.tsx
1009
- var import_jsx_runtime16 = require("react/jsx-runtime");
1004
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1010
1005
  var className = "group flex min-w-0 flex-1 flex-col items-center justify-center gap-1.5 rounded-xl px-2 py-2 text-caption text-[var(--color-text)] transition-colors hover:bg-[var(--color-surface-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
1011
1006
  function Contents({ icon, label }) {
1012
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1013
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: icon }),
1014
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "max-w-full truncate", children: label })
1007
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1008
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: icon }),
1009
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "max-w-full truncate", children: label })
1015
1010
  ] });
1016
1011
  }
1017
1012
  function ShareDestinationButton({
@@ -1023,29 +1018,29 @@ function ShareDestinationButton({
1023
1018
  ariaLabel
1024
1019
  }) {
1025
1020
  if (href) {
1026
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Contents, { icon, label }) });
1021
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Contents, { icon, label }) });
1027
1022
  }
1028
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Contents, { icon, label }) });
1023
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Contents, { icon, label }) });
1029
1024
  }
1030
1025
 
1031
1026
  // src/genre-tree/SharePreviewCard.tsx
1032
1027
  var import_lucide_react4 = require("lucide-react");
1033
- var import_jsx_runtime17 = require("react/jsx-runtime");
1028
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1034
1029
  function SharePreviewCard({ target, url }) {
1035
1030
  const Icon = { genre: import_lucide_react4.Music2, artist: import_lucide_react4.UserRound, song: import_lucide_react4.Music }[target.kind];
1036
1031
  const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
1037
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
1038
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
1039
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
1040
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
1041
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
1042
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
1032
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
1033
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
1035
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
1036
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
1037
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
1043
1038
  ] })
1044
1039
  ] }) });
1045
1040
  }
1046
1041
 
1047
1042
  // src/genre-tree/SharePopup.tsx
1048
- var import_jsx_runtime18 = require("react/jsx-runtime");
1043
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1049
1044
  function resolveAbsoluteUrl(url) {
1050
1045
  try {
1051
1046
  const parsed = new URL(url, window.location.href);
@@ -1120,8 +1115,8 @@ function SharePopup({ target, url, onClose }) {
1120
1115
  setShareError("Could not open the share options.");
1121
1116
  }
1122
1117
  };
1123
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1124
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1118
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
1119
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1125
1120
  BasePopup_default,
1126
1121
  {
1127
1122
  title: "Share",
@@ -1136,70 +1131,70 @@ function SharePopup({ target, url, onClose }) {
1136
1131
  titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
1137
1132
  closeButtonClassName: "!h-10 !w-10 !rounded-none !border-0 !bg-transparent !p-0 !text-[var(--color-text)] hover:!bg-transparent hover:!text-[var(--color-text)]",
1138
1133
  contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
1139
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col gap-4", children: [
1140
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
1141
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
1142
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1134
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col gap-4", children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
1136
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
1137
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1143
1138
  ShareDestinationButton,
1144
1139
  {
1145
1140
  label: "Copy Link",
1146
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react5.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1141
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react5.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1147
1142
  onClick: () => void handleCopy(),
1148
1143
  ariaLabel: "Copy link",
1149
1144
  disabled: !absoluteUrl
1150
1145
  }
1151
1146
  ),
1152
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1147
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1153
1148
  ShareDestinationButton,
1154
1149
  {
1155
1150
  label: "WhatsApp",
1156
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_fa.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1151
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_fa.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1157
1152
  href: destinationUrls?.whatsapp,
1158
1153
  ariaLabel: `Share ${target.name} via WhatsApp`,
1159
1154
  disabled: !absoluteUrl
1160
1155
  }
1161
1156
  ),
1162
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1157
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1163
1158
  ShareDestinationButton,
1164
1159
  {
1165
1160
  label: "Gmail",
1166
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1161
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1167
1162
  href: destinationUrls?.gmail,
1168
1163
  ariaLabel: `Email ${target.name} with Gmail`,
1169
1164
  disabled: !absoluteUrl
1170
1165
  }
1171
1166
  ),
1172
- canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1167
+ canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1173
1168
  ShareDestinationButton,
1174
1169
  {
1175
1170
  label: "More",
1176
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1171
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1177
1172
  onClick: () => void handleNativeShare(),
1178
1173
  ariaLabel: "More sharing options",
1179
1174
  disabled: !absoluteUrl
1180
1175
  }
1181
1176
  )
1182
1177
  ] }),
1183
- copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
1184
- shareError && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
1178
+ copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
1179
+ shareError && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
1185
1180
  ] })
1186
1181
  }
1187
1182
  ),
1188
- toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1183
+ toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1189
1184
  ] });
1190
1185
  }
1191
1186
 
1192
1187
  // src/genre-tree/ShareActionButton.tsx
1193
- var import_jsx_runtime19 = require("react/jsx-runtime");
1188
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1194
1189
  function ShareActionButton({ target, url }) {
1195
- const { showPopup, hidePopup } = (0, import_popup3.usePopup)();
1190
+ const { showPopup, hidePopup } = (0, import_popup2.usePopup)();
1196
1191
  const openSharePopup = () => {
1197
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1192
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1198
1193
  };
1199
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1194
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1200
1195
  import_ui5.ActionButton,
1201
1196
  {
1202
- icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react6.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1197
+ icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react6.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1203
1198
  label: "Share",
1204
1199
  "aria-label": `Share ${target.name}`,
1205
1200
  title: `Share ${target.name}`,
@@ -1209,7 +1204,7 @@ function ShareActionButton({ target, url }) {
1209
1204
  }
1210
1205
 
1211
1206
  // src/genre-tree/GenreActionBand.tsx
1212
- var import_jsx_runtime20 = require("react/jsx-runtime");
1207
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1213
1208
  function GenreActionBand({
1214
1209
  node,
1215
1210
  playActions,
@@ -1230,24 +1225,24 @@ function GenreActionBand({
1230
1225
  items.push({
1231
1226
  key: "zoom-in",
1232
1227
  label: "Focus",
1233
- icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1228
+ icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1234
1229
  onClick: onZoomIn
1235
1230
  });
1236
1231
  }
1237
1232
  if (onAddChild) {
1238
- adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
1233
+ adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
1239
1234
  }
1240
1235
  if (onRename) {
1241
- adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
1236
+ adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
1242
1237
  }
1243
1238
  if (onChangeParent) {
1244
- adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
1239
+ adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
1245
1240
  }
1246
1241
  if (onToggleSide) {
1247
1242
  adminItems.push({
1248
1243
  key: "toggle-side",
1249
1244
  label: side === "pop" ? "Make core" : "Make pop",
1250
- icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1245
+ icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1251
1246
  onClick: onToggleSide,
1252
1247
  disabled: !!toggleSideDisabledReason,
1253
1248
  title: toggleSideDisabledReason
@@ -1262,9 +1257,9 @@ function GenreActionBand({
1262
1257
  disabled: action.enabled ? !action.enabled(node) : false
1263
1258
  });
1264
1259
  }
1265
- const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ui6.ActionButton, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
1260
+ const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ui6.ActionButton, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
1266
1261
  if (!playActions && items.length === 0 && adminItems.length === 0 && !(shareTarget && shareUrl)) return null;
1267
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_ui6.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1262
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ui6.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1268
1263
  import_ui6.ActionBand,
1269
1264
  {
1270
1265
  className: "genre-action-band",
@@ -1272,7 +1267,7 @@ function GenreActionBand({
1272
1267
  children: [
1273
1268
  playActions,
1274
1269
  renderItems(items),
1275
- shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1270
+ shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1276
1271
  ]
1277
1272
  }
1278
1273
  ) });
@@ -1296,7 +1291,7 @@ var REGION_GLOBE_PATHS = {
1296
1291
  var REGION_GLOBES = ["africa", "americas", "asia"];
1297
1292
 
1298
1293
  // src/genre-tree/RegionGlobeIcon.tsx
1299
- var import_jsx_runtime21 = require("react/jsx-runtime");
1294
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1300
1295
  var ROTATION_INTERVAL_MS = 3e3;
1301
1296
  function RegionGlobeIcon({ className: className2 }) {
1302
1297
  const [index, setIndex] = (0, import_react11.useState)(0);
@@ -1314,29 +1309,29 @@ function RegionGlobeIcon({ className: className2 }) {
1314
1309
  clearInterval(timer);
1315
1310
  };
1316
1311
  }, []);
1317
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
1312
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
1318
1313
  }
1319
1314
 
1320
1315
  // src/genre-tree/treeMarkPath.ts
1321
1316
  var TREE_MARK_PATH = "M3171 5598 c-5 -13 -5 -24 0 -26 5 -2 9 -16 9 -31 0 -25 -3 -28 -25 -23 l-25 4 0 -116 c0 -64 3 -116 8 -116 10 0 32 51 26 61 -3 5 -1 9 4 9 5 0 15 12 21 28 7 15 26 36 43 47 30 20 88 105 71 105 -4 0 -33 -21 -62 -47 l-54 -47 6 63 c4 35 7 72 7 82 0 10 9 19 23 22 16 3 13 5 -11 6 -25 1 -35 -4 -41 -21z M2893 5485 c8 -76 24 -100 71 -100 l36 0 0 53 c0 58 -15 69 -28 20 -8 -29 -10 -30 -16 -13 -15 43 -42 89 -55 93 -11 4 -13 -6 -8 -53z M2484 5438 c8 -70 49 -169 54 -131 2 12 10 27 18 33 17 15 19 84 3 129 -9 27 -16 31 -46 31 l-36 0 7 -62z M2625 5448 c-7 -22 -24 -121 -31 -179 l-6 -57 -27 11 c-15 5 -32 19 -37 31 -9 21 -88 86 -105 86 -39 0 38 -104 118 -158 17 -12 56 -59 86 -105 30 -45 67 -92 81 -104 14 -13 26 -30 26 -38 0 -13 -9 -11 -45 11 -67 39 -58 10 21 -66 67 -65 96 -110 108 -167 4 -17 13 -34 20 -36 20 -8 66 -186 75 -289 13 -161 -8 -272 -72 -365 -12 -18 -30 -33 -39 -33 -23 0 -53 33 -79 89 -25 54 -69 112 -121 162 -35 33 -38 40 -38 92 0 58 36 172 79 252 25 47 29 125 6 125 -19 0 -30 -22 -50 -100 -9 -35 -24 -78 -32 -94 l-16 -30 -19 24 -20 25 7 -58 c8 -62 0 -127 -16 -127 -19 0 -122 107 -166 172 -52 79 -68 150 -60 273 8 108 17 129 67 149 58 23 97 64 105 109 9 47 -5 49 -63 6 l-42 -30 15 35 c17 41 20 102 5 111 -18 11 -45 -4 -64 -36 -11 -19 -20 -60 -23 -110 -4 -54 -10 -79 -18 -79 -20 0 -27 28 -30 108 -2 40 -6 76 -9 79 -3 4 -6 51 -6 105 0 86 -3 101 -21 119 -27 27 -42 20 -53 -24 -7 -28 -5 -51 7 -90 9 -30 17 -76 17 -103 l0 -49 -29 51 c-29 49 -92 114 -111 114 -6 0 -10 -11 -10 -25 0 -14 5 -25 10 -25 6 0 10 -6 10 -13 0 -7 23 -44 51 -82 61 -84 59 -82 52 -57 -3 14 2 24 15 31 12 6 23 23 25 38 2 16 5 2 6 -29 1 -46 -2 -58 -14 -58 -21 0 -19 -13 10 -70 22 -44 25 -61 25 -165 l0 -116 -47 27 c-65 36 -142 118 -176 187 -15 31 -37 62 -49 68 -21 11 -21 10 -15 -41 l6 -52 -29 6 c-37 7 -39 -7 -4 -31 14 -10 53 -40 86 -68 92 -76 130 -104 163 -119 39 -19 101 -84 109 -116 10 -42 50 -88 217 -245 50 -47 94 -102 134 -167 33 -54 65 -98 71 -98 17 0 45 -79 51 -145 4 -47 0 -76 -20 -135 -14 -41 -39 -121 -56 -177 -18 -57 -35 -103 -40 -103 -4 0 -26 13 -49 30 -31 22 -50 49 -82 117 -99 209 -116 241 -185 337 -123 172 -123 173 -128 311 -5 140 -29 245 -57 245 -12 0 -20 -11 -24 -31 -9 -46 -7 -108 3 -127 14 -26 25 -162 14 -162 -20 0 -160 99 -192 135 -19 22 -42 58 -52 80 -10 23 -27 52 -37 66 -10 13 -26 38 -34 54 -9 17 -23 45 -33 64 -15 29 -16 40 -6 80 31 116 34 139 27 205 -7 57 -11 69 -28 74 -14 3 -32 30 -54 79 -21 46 -39 73 -47 70 -13 -5 -14 -15 -3 -65 l8 -33 -49 25 c-61 31 -93 40 -93 27 0 -13 43 -41 122 -79 47 -22 72 -41 83 -61 19 -38 19 -51 0 -51 -12 0 -15 -15 -16 -72 -1 -79 -12 -134 -22 -108 -3 8 -10 48 -16 89 -14 89 -25 111 -54 111 -38 0 -50 -15 -45 -54 4 -40 2 -42 -26 -21 -29 22 -36 19 -36 -20 0 -39 39 -85 125 -145 38 -27 62 -56 99 -120 45 -79 82 -177 70 -189 -2 -3 -42 3 -87 13 -45 10 -99 21 -119 26 -47 9 -103 51 -125 93 -24 44 -83 203 -94 252 -5 25 -4 59 3 91 14 68 3 140 -23 155 -12 6 -23 8 -27 5 -14 -15 -13 -115 2 -159 39 -116 0 -116 -70 1 -22 36 -31 42 -59 42 -20 0 -35 6 -39 15 -6 16 -56 21 -56 6 0 -5 23 -25 51 -44 53 -36 158 -156 188 -216 10 -20 26 -60 36 -88 11 -29 25 -53 32 -53 8 0 13 -12 13 -30 0 -26 -4 -30 -27 -31 l-28 -1 25 -7 c26 -8 177 -87 196 -102 5 -4 50 -18 100 -29 122 -29 277 -78 300 -95 10 -8 26 -15 34 -15 35 0 160 -166 160 -212 0 -22 -32 -24 -50 -4 -11 14 -100 46 -165 61 -16 3 -68 25 -115 48 -102 49 -163 56 -168 19 -9 -62 58 -104 178 -112 63 -5 103 -14 155 -37 39 -16 91 -38 117 -47 26 -9 74 -35 106 -56 52 -33 65 -48 96 -112 20 -40 39 -95 43 -122 6 -47 6 -48 -18 -41 -33 10 -69 33 -69 45 0 6 -10 10 -23 10 -13 0 -27 4 -32 9 -6 5 -27 14 -47 20 -20 7 -53 23 -73 37 -43 29 -45 29 -45 7 0 -26 80 -96 145 -127 119 -56 274 -185 311 -259 55 -107 104 -410 104 -640 0 -162 -22 -325 -45 -333 -25 -10 -72 25 -168 123 -129 131 -140 148 -182 274 -20 60 -50 142 -66 181 -16 39 -45 131 -65 202 -24 93 -55 173 -104 274 -77 158 -78 166 -32 256 31 64 27 129 -12 181 -31 40 -39 42 -65 18 -51 -49 -55 -120 -11 -208 37 -74 37 -85 3 -85 -36 0 -94 65 -137 154 -18 39 -37 80 -40 92 -3 11 -14 29 -24 40 -10 10 -28 35 -42 54 -13 19 -40 55 -61 80 -20 25 -41 63 -47 85 -6 22 -16 62 -23 89 -20 81 -95 147 -113 100 -18 -47 -6 -92 39 -147 25 -31 45 -61 45 -67 0 -15 -42 -3 -116 31 -94 44 -156 124 -184 235 -9 36 -13 41 -38 40 -39 -2 -144 113 -186 205 -31 66 -33 68 -53 54 -11 -8 -24 -11 -27 -7 -4 4 11 -24 33 -62 39 -67 43 -86 18 -86 -8 0 -8 3 1 9 11 6 9 9 -7 14 -11 3 -25 -1 -31 -7 -7 -10 0 -16 29 -24 39 -12 203 -147 215 -178 13 -34 4 -35 -48 -6 -64 37 -126 51 -126 30 0 -26 55 -67 125 -92 46 -16 80 -36 100 -58 l29 -33 -54 -3 c-79 -5 -203 14 -249 38 -47 24 -70 54 -105 140 -43 105 -115 163 -103 83 3 -21 9 -47 13 -57 4 -11 -15 4 -42 32 -62 64 -84 67 -84 14 0 -41 24 -76 61 -87 16 -6 18 -9 8 -15 -23 -15 15 -45 117 -91 52 -24 93 -48 91 -54 -10 -28 -150 -28 -190 0 -21 15 -47 5 -47 -17 0 -5 -10 -19 -22 -29 -23 -20 -23 -20 112 -14 174 8 208 8 430 0 l185 -7 84 -41 c115 -57 180 -118 225 -212 20 -41 36 -77 36 -80 0 -14 89 -126 150 -190 74 -78 120 -144 120 -175 0 -30 10 -33 -201 52 -94 38 -131 71 -218 195 -27 37 -59 77 -71 88 l-23 20 7 -40 c4 -22 12 -44 17 -49 13 -14 11 -26 -4 -26 -7 0 -29 21 -48 48 -33 45 -34 51 -39 159 -4 89 -9 116 -22 130 -17 16 -18 16 -18 0 0 -9 -7 -17 -16 -17 -14 0 -16 -9 -11 -62 3 -35 8 -74 12 -88 l7 -25 -25 27 c-41 44 -127 118 -137 118 -18 0 -10 -57 12 -85 18 -24 20 -32 10 -57 -10 -28 -9 -30 22 -43 18 -8 54 -19 79 -26 36 -9 60 -24 99 -66 80 -83 80 -83 1 -83 -80 0 -176 21 -236 51 -107 54 -234 169 -252 228 -4 14 -12 29 -17 35 -5 6 -18 25 -28 43 -24 40 -90 64 -116 42 -22 -18 -13 -84 17 -116 13 -13 42 -32 64 -42 22 -10 74 -53 115 -96 l75 -77 -75 5 c-135 8 -243 76 -336 207 -31 44 -65 87 -77 95 -37 26 -34 -9 7 -88 20 -39 34 -72 31 -75 -2 -2 -26 11 -52 31 -90 65 -143 83 -143 49 0 -32 71 -88 150 -117 41 -15 113 -47 159 -71 46 -24 99 -46 118 -50 18 -3 33 -12 33 -18 0 -12 -111 -46 -149 -46 -12 0 -29 -8 -39 -17 -10 -10 -27 -19 -37 -19 -11 -1 -45 -9 -75 -17 -58 -16 -110 -62 -110 -97 0 -39 116 -45 167 -9 11 8 34 31 52 51 28 32 50 43 144 74 145 47 222 48 404 5 70 -17 177 -38 237 -47 128 -19 202 -43 250 -79 18 -14 73 -42 122 -61 164 -66 208 -112 243 -251 36 -141 27 -167 -44 -123 -19 12 -63 23 -111 28 -117 13 -134 25 -275 193 -63 74 -193 151 -266 157 -29 2 -66 -1 -81 -7 -24 -10 -37 -9 -80 8 -66 26 -70 9 -11 -44 72 -65 119 -80 239 -78 60 1 109 -2 117 -8 6 -6 26 -35 42 -64 17 -30 46 -64 64 -77 19 -13 49 -40 67 -61 l34 -39 -79 3 c-57 3 -80 0 -83 -9 -3 -10 -7 -10 -18 -1 -8 6 -11 15 -8 20 3 5 0 9 -5 9 -16 0 -13 -16 6 -36 36 -36 78 -44 227 -44 119 0 152 -3 188 -19 56 -24 95 -64 125 -126 13 -27 35 -68 48 -90 14 -22 25 -53 25 -69 l0 -29 -107 5 c-131 6 -193 28 -290 100 -37 28 -82 56 -98 63 -75 32 -176 123 -277 247 -34 42 -122 98 -134 85 -5 -5 103 -138 184 -226 52 -57 46 -61 -49 -31 -87 28 -173 85 -325 214 -144 123 -189 151 -254 159 -25 2 -39 3 -32 0 6 -3 12 -16 12 -29 0 -17 -4 -23 -12 -19 -7 4 -1 -5 15 -19 15 -14 46 -30 70 -36 23 -6 60 -17 82 -25 l40 -13 -52 -1 c-49 -1 -53 -3 -53 -25 0 -28 28 -36 122 -36 54 0 62 -3 91 -32 l32 -33 -52 -3 c-88 -5 -75 -26 39 -62 130 -41 178 -63 206 -93 l22 -24 -20 -18 c-12 -11 -41 -20 -74 -23 -48 -4 -60 0 -116 32 -118 69 -241 96 -279 62 -13 -12 -40 -16 -104 -16 -48 0 -95 4 -105 9 -9 5 -34 16 -55 25 -20 9 -76 53 -122 97 -47 45 -88 79 -91 76 -12 -12 6 -54 41 -100 l36 -47 -74 0 c-88 0 -112 15 -78 49 26 26 21 39 -34 84 -87 73 -132 32 -50 -45 25 -23 45 -46 45 -50 0 -4 -21 2 -46 12 -45 20 -114 27 -114 12 0 -4 16 -22 35 -40 39 -38 78 -47 255 -61 164 -14 178 -17 172 -47 -2 -13 -2 -30 2 -39 5 -13 9 -13 40 2 19 9 33 20 30 25 -4 5 4 4 15 -4 21 -13 20 -15 -44 -74 -36 -33 -65 -66 -65 -72 0 -21 68 15 142 74 38 30 84 62 103 70 73 30 203 11 312 -46 32 -17 82 -33 111 -37 l53 -6 -28 -30 c-15 -16 -44 -40 -63 -52 -63 -42 -100 -82 -100 -111 0 -25 2 -26 37 -21 28 5 47 17 73 49 65 78 175 183 224 216 34 22 61 32 88 32 43 0 122 -36 152 -69 10 -11 39 -32 62 -46 43 -25 53 -46 36 -72 -11 -17 -200 -86 -232 -85 -28 1 -122 -47 -127 -65 -3 -8 14 -13 54 -15 l58 -3 -65 -44 c-36 -25 -68 -49 -71 -53 -11 -17 -115 -10 -188 12 -105 32 -112 11 -14 -39 l56 -28 -40 -6 c-52 -9 -96 0 -154 29 -31 15 -50 19 -60 13 -15 -10 -16 -10 44 -47 20 -13 37 -27 37 -31 0 -15 -144 -71 -205 -81 -33 -5 -85 -10 -116 -10 -32 0 -63 -6 -75 -14 -18 -13 -15 -15 34 -22 l53 -7 -31 -18 c-18 -12 -30 -27 -30 -40 0 -21 4 -21 61 -16 50 4 75 14 145 57 46 28 112 63 146 77 62 24 191 62 215 63 22 0 -47 -103 -107 -161 -42 -41 -60 -66 -65 -92 -10 -49 0 -60 47 -54 65 9 99 53 124 160 20 83 49 133 94 163 22 15 40 30 40 34 0 4 8 10 18 13 9 3 31 15 49 27 31 21 31 21 26 0 -3 -11 -21 -37 -40 -58 -43 -45 -72 -106 -73 -150 0 -39 18 -49 56 -30 38 18 63 64 75 137 13 82 40 142 89 194 22 24 40 49 40 54 0 6 24 21 53 34 49 22 63 23 237 20 193 -2 278 -14 348 -50 21 -11 44 -27 52 -36 7 -9 32 -27 55 -41 49 -28 138 -147 184 -243 16 -35 42 -114 57 -175 35 -147 31 -268 -14 -436 -17 -63 -37 -166 -45 -227 -12 -94 -21 -127 -54 -195 -22 -46 -52 -94 -67 -108 -15 -14 -46 -44 -68 -67 -23 -24 -47 -43 -53 -43 -7 0 -25 -12 -41 -26 -129 -121 -526 -267 -721 -268 -104 0 -109 -15 -19 -57 75 -35 67 -35 221 -10 125 21 235 27 235 13 0 -16 -66 -72 -85 -72 -24 0 -85 -36 -85 -49 0 -14 12 -19 58 -27 35 -5 42 -4 42 10 0 9 5 17 10 17 28 4 74 20 80 29 9 14 226 119 288 139 63 21 215 43 239 34 32 -12 12 -50 -52 -102 -36 -28 -73 -55 -83 -58 -17 -7 -66 -39 -92 -60 -8 -7 -38 -32 -67 -57 l-52 -45 42 -6 c36 -6 54 -1 122 33 44 22 120 56 169 76 112 45 222 107 289 164 46 40 84 62 107 62 18 0 130 -83 130 -96 0 -8 4 -14 10 -14 5 0 39 -26 75 -59 36 -32 96 -76 132 -97 36 -21 68 -46 71 -56 5 -19 21 -22 59 -12 18 5 20 9 10 19 -7 7 -69 71 -139 143 -147 149 -161 179 -80 168 136 -19 195 -39 378 -128 78 -37 147 -68 152 -68 6 0 15 -7 20 -16 8 -14 14 -15 46 -4 l38 12 -104 57 c-125 69 -148 84 -148 93 0 16 94 7 223 -22 74 -17 146 -30 160 -30 14 0 29 -4 32 -10 7 -11 54 6 115 43 l40 24 -38 1 c-22 1 -68 11 -103 21 -35 11 -107 30 -159 41 -131 31 -295 99 -406 171 -235 153 -355 366 -357 634 -1 211 65 343 285 568 117 119 206 167 368 198 153 29 267 59 328 86 80 35 166 44 264 27 148 -26 167 -38 251 -169 71 -110 136 -165 196 -165 26 0 31 4 31 23 -1 32 -28 62 -87 91 -39 20 -66 45 -118 112 -95 123 -95 135 -5 92 25 -12 77 -31 116 -43 56 -17 76 -19 97 -10 27 11 26 11 -25 29 -113 39 -138 49 -138 57 0 14 91 55 153 68 33 7 75 10 95 7 27 -4 31 -3 17 4 -19 8 -19 9 3 9 12 1 22 7 22 15 0 10 7 12 22 8 12 -3 17 -7 10 -10 -6 -2 -12 -10 -12 -18 0 -8 -6 -14 -12 -15 -27 -2 39 -17 80 -18 29 -1 42 3 42 13 0 20 -19 32 -67 45 -49 13 -53 22 -20 44 49 32 128 39 235 21 56 -9 112 -14 126 -10 26 6 96 69 96 86 0 6 -32 10 -77 10 -84 0 -126 -7 -118 -20 9 -14 -17 -24 -67 -24 -34 -1 -48 3 -48 13 0 8 -7 11 -20 8 -17 -4 -18 -2 -9 14 5 11 14 17 20 14 5 -4 9 0 9 8 0 8 16 35 35 60 35 45 35 46 14 57 -18 10 -23 8 -37 -12 -67 -95 -162 -167 -297 -225 -55 -23 -99 -47 -98 -53 2 -5 -6 -10 -18 -10 -27 0 -23 8 31 71 100 117 116 131 161 155 80 40 80 70 1 41 l-47 -18 36 38 c47 49 59 73 59 120 0 67 -24 80 -82 46 -49 -29 -76 -85 -89 -183 -10 -81 -9 -80 -89 -174 -20 -22 -43 -53 -53 -68 -28 -45 -89 -61 -217 -56 -70 2 -114 8 -118 15 -4 6 -28 13 -54 15 l-46 3 53 71 c51 68 100 104 140 104 50 0 125 73 125 122 0 30 0 31 -45 26 -28 -4 -59 -16 -82 -34 -30 -23 -35 -24 -28 -8 22 51 37 105 31 111 -13 13 -47 -34 -105 -146 -72 -138 -171 -248 -261 -292 -62 -30 -164 -44 -242 -34 -48 7 -50 8 -45 34 14 70 51 144 95 190 42 43 56 51 127 69 44 12 98 24 120 27 99 15 155 57 155 117 l0 31 -51 -5 c-40 -4 -58 -12 -87 -40 -56 -52 -66 -58 -131 -78 l-60 -18 10 27 c6 14 36 62 68 106 70 96 91 136 91 170 0 41 -18 29 -50 -35 -30 -59 -31 -60 -74 -60 -56 0 -71 -18 -101 -115 -23 -75 -43 -114 -72 -145 -8 -8 -41 -61 -73 -117 -35 -59 -75 -113 -95 -128 -37 -29 -177 -85 -210 -85 -35 0 -222 -134 -282 -202 -31 -35 -64 -64 -72 -66 -22 -4 -68 116 -88 223 -20 113 -5 256 37 340 37 76 124 182 195 238 140 110 221 137 416 137 136 0 171 6 186 28 9 15 2 17 -76 18 -47 0 -127 4 -178 9 -167 15 -274 -28 -423 -170 -72 -68 -114 -95 -128 -81 -12 12 17 138 44 194 64 132 229 243 439 298 182 46 319 21 425 -80 68 -65 144 -115 202 -135 119 -41 155 -32 72 17 -88 52 -224 144 -224 152 0 14 51 20 210 22 115 2 160 0 175 -10 19 -12 18 -13 -8 -9 -36 6 -45 -9 -11 -17 56 -15 117 -51 183 -110 66 -59 119 -86 169 -86 54 0 31 59 -35 90 -167 77 -176 82 -158 93 25 13 138 -8 201 -37 27 -13 67 -43 89 -66 50 -53 84 -70 134 -70 37 0 71 19 71 40 0 24 -62 57 -129 70 -40 7 -74 17 -77 22 -3 4 24 8 60 8 36 0 68 -4 71 -10 3 -5 26 -10 51 -10 50 0 57 15 23 46 -24 21 -113 25 -226 10 -50 -7 -65 -6 -77 7 -19 18 -100 37 -160 37 -74 0 -136 12 -136 26 0 17 11 23 53 28 32 4 83 29 97 47 8 10 71 40 99 47 28 7 159 8 309 1 50 -2 92 -1 92 4 0 4 14 7 30 7 23 0 30 -4 30 -20 0 -15 -7 -20 -25 -20 -42 0 -28 -17 30 -39 75 -28 103 -27 117 4 8 18 19 25 38 25 34 0 47 35 23 62 -24 27 -70 31 -190 18 -101 -11 -112 -10 -173 10 -36 12 -104 24 -152 27 -49 3 -88 9 -88 13 0 8 35 26 66 35 12 3 37 21 55 40 19 20 67 58 106 86 57 39 86 52 134 60 71 11 170 58 193 91 34 49 10 51 -66 5 -52 -31 -154 -65 -165 -54 -11 12 9 25 67 47 33 12 60 26 60 30 0 5 15 17 34 28 31 18 67 84 53 98 -15 15 -69 -17 -124 -75 -35 -36 -63 -63 -63 -58 0 4 14 31 30 60 43 77 39 180 -7 151 -39 -24 -56 -62 -63 -141 -7 -87 -27 -127 -98 -200 -50 -52 -120 -103 -139 -103 -8 0 -22 -8 -31 -17 -31 -31 -26 11 12 94 33 74 61 171 53 180 -3 2 -17 1 -32 -3 -38 -10 -65 -74 -75 -185 -9 -97 -31 -147 -79 -176 -31 -20 -81 -71 -81 -83 0 -5 -24 -16 -52 -26 -29 -9 -58 -24 -64 -32 -7 -9 -33 -17 -70 -19 l-59 -4 55 48 c120 105 133 126 66 103 -19 -6 -58 -36 -88 -66 -29 -30 -72 -64 -96 -76 -41 -19 -50 -20 -163 -9 -168 16 -237 52 -196 103 21 27 121 88 143 88 60 0 183 34 234 65 30 18 74 36 97 40 85 14 180 99 131 118 -16 5 -19 13 -14 33 7 25 5 25 -26 19 -52 -11 -83 -31 -122 -76 -54 -63 -94 -86 -197 -109 -52 -12 -102 -24 -111 -26 -28 -8 -22 22 10 44 15 11 35 34 45 51 16 27 16 31 2 31 -8 0 -18 -7 -22 -16 -8 -23 -23 2 -23 40 0 23 -3 27 -12 18 -14 -14 -42 -16 -33 -2 3 6 4 10 1 10 -9 0 -37 -61 -62 -133 -29 -84 -85 -162 -137 -189 -24 -12 -67 -21 -110 -24 -40 -2 -100 -6 -134 -9 l-62 -4 36 72 c20 39 48 84 62 98 29 29 86 122 96 157 9 32 -20 29 -42 -5 -17 -27 -128 -174 -222 -297 -45 -58 -101 -96 -141 -96 -48 0 -9 184 59 284 41 59 113 107 234 156 51 20 141 67 198 102 57 36 118 72 134 80 40 21 155 46 240 53 48 5 82 14 113 32 58 34 150 44 258 28 41 -6 57 -4 75 9 17 12 46 16 111 16 88 0 116 12 57 24 -17 3 -48 6 -69 6 -21 0 -43 6 -49 14 -14 17 -8 22 40 31 30 5 36 10 32 26 -4 13 0 19 12 19 23 0 95 44 129 79 34 35 30 67 -6 55 -13 -4 -31 -7 -41 -6 -11 0 -38 -25 -68 -64 -28 -35 -53 -64 -55 -64 -2 0 7 22 21 50 14 27 25 60 25 74 0 14 5 37 11 53 7 19 7 29 0 31 -6 2 -11 17 -11 33 0 16 -4 29 -10 29 -5 0 -10 -19 -10 -42 0 -50 -37 -158 -74 -222 -28 -48 -90 -86 -141 -86 l-29 0 36 38 c20 20 43 52 52 70 16 31 16 32 -5 32 -25 0 -94 -47 -109 -75 -19 -35 -161 -104 -245 -120 -42 -8 -85 -15 -96 -15 -18 0 -18 1 -2 35 9 19 20 35 25 35 4 0 8 6 8 13 0 7 13 34 30 60 30 48 75 77 182 117 58 21 112 66 176 146 26 31 57 57 84 69 124 52 167 75 177 93 17 33 -14 35 -71 6 -29 -14 -69 -32 -89 -39 l-38 -14 20 51 c11 28 24 59 29 68 10 19 -20 60 -45 60 -18 0 -21 46 -4 52 6 2 14 26 16 53 4 43 2 52 -16 63 -20 11 -21 10 -21 -25 -1 -209 -5 -248 -32 -305 -34 -73 -116 -188 -133 -188 -8 0 -20 -6 -27 -13 -7 -7 -29 -18 -48 -24 -19 -6 -52 -18 -72 -27 -34 -15 -38 -15 -38 -1 0 35 32 73 89 109 60 37 77 51 45 39 -14 -5 -14 -3 -3 14 7 13 9 28 4 39 -5 11 -7 24 -6 29 1 6 -19 -11 -44 -37 -25 -26 -45 -44 -45 -40 0 4 18 34 40 67 30 45 40 69 40 100 0 53 -11 67 -46 63 -22 -2 -34 -13 -53 -47 -14 -25 -26 -40 -29 -33 -2 7 -10 12 -17 12 -10 0 -14 -33 -17 -142 l-4 -143 -63 -127 c-39 -81 -74 -137 -94 -154 -51 -43 -167 -87 -167 -63 0 6 26 38 58 72 32 34 66 73 75 87 18 26 23 100 8 100 -5 0 -22 -19 -38 -42 -15 -23 -52 -60 -81 -82 -31 -25 -61 -59 -74 -85 -37 -75 -125 -156 -220 -201 -46 -22 -101 -43 -121 -47 -32 -5 -37 -3 -37 13 0 36 50 229 70 269 10 23 38 95 61 160 39 116 84 211 117 248 12 13 13 20 4 29 -9 9 -21 5 -49 -18 -58 -46 -166 -272 -249 -519 -37 -110 -73 -207 -80 -216 -8 -8 -14 -21 -14 -28 0 -8 -19 -54 -41 -105 -23 -50 -61 -146 -84 -212 -49 -144 -77 -193 -152 -274 -71 -77 -89 -77 -115 2 -11 33 -27 59 -36 62 -19 6 -21 27 -23 201 -2 197 49 394 151 580 33 61 72 135 86 165 118 253 140 288 273 462 87 112 121 167 121 195 0 37 -25 27 -75 -29 -42 -48 -45 -50 -38 -23 13 50 9 145 -8 156 -19 12 -30 7 -50 -22 -23 -33 -15 -116 12 -124 28 -9 35 -49 12 -73 -10 -11 -36 -45 -58 -76 -22 -31 -44 -56 -48 -56 -12 0 -3 78 17 160 8 36 15 84 16 107 0 23 4 44 9 47 4 3 11 37 14 75 3 39 14 86 26 109 26 50 80 97 94 83 16 -16 59 -13 85 5 30 21 52 66 52 109 0 33 -2 35 -32 35 -59 0 -78 -12 -118 -69 -21 -31 -55 -74 -74 -96 l-35 -40 -1 41 c0 23 7 60 15 83 15 43 92 151 124 173 20 14 55 77 48 85 -3 2 -8 -5 -12 -16 -7 -24 -35 -17 -35 9 0 23 -13 32 -35 24 -9 -4 -12 -9 -7 -13 17 -10 2 -62 -16 -59 -11 2 -30 -24 -61 -87 -34 -68 -46 -84 -49 -67 -2 12 -8 22 -13 22 -5 0 -9 -10 -9 -22 -1 -23 -1 -23 -16 -3 -9 11 -24 37 -34 58 -11 24 -20 34 -23 25 -3 -7 -6 25 -6 72 -1 105 -21 98 -26 -10 -3 -43 -9 -76 -16 -81 -10 -6 -10 -11 0 -23 14 -16 3 -22 -15 -8 -5 4 2 -2 15 -13 51 -41 78 -76 96 -128 26 -70 25 -187 -3 -292 -13 -49 -21 -109 -20 -155 0 -104 -54 -310 -81 -310 -6 0 -11 3 -11 8 -1 44 -24 191 -41 259 l-22 85 27 108 c30 123 34 211 10 231 -20 16 -40 5 -72 -42 -21 -30 -23 -42 -18 -97 3 -36 1 -60 -4 -57 -17 11 -34 81 -34 150 -1 64 2 76 39 140 22 39 45 84 52 100 17 43 16 92 -2 98 -17 7 -17 8 -44 -73 -12 -36 -34 -87 -50 -113 -33 -56 -41 -48 -41 39 0 41 -4 54 -15 54 -11 0 -15 -18 -17 -80 -2 -45 -7 -80 -13 -80 -5 0 -10 4 -10 8 0 5 -8 16 -18 26 -58 58 -70 132 -53 314 7 69 6 92 -3 92 -6 0 -13 -6 -16 -12z m321 -503 c3 -20 4 -39 0 -42 -3 -4 -6 6 -6 20 0 17 -5 27 -15 27 -16 0 -20 12 -8 23 14 14 22 6 29 -28z m-1006 -80 c0 -8 -9 -15 -19 -15 -29 0 -35 18 -9 23 13 2 24 5 26 6 1 0 2 -6 2 -14z m327 -27 c-3 -7 -5 -2 -5 12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m-462 -358 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m-835 -40 c-6 -11 -15 -20 -19 -20 -5 0 -7 9 -4 20 3 11 12 20 20 20 11 0 11 -4 3 -20z m864 -25 c3 -9 0 -15 -9 -15 -9 0 -15 -10 -16 -27 0 -27 -1 -27 -13 7 -10 25 -10 37 -2 42 18 12 33 9 40 -7z m-892 -863 c38 2 68 0 68 -4 0 -5 -33 -8 -73 -8 -49 0 -80 -5 -95 -16 -21 -14 -22 -14 -22 5 0 14 -6 21 -20 21 -11 0 -20 2 -20 4 0 2 -3 12 -6 21 -6 13 1 13 47 -5 40 -16 70 -21 121 -18z m3218 -91 c0 -10 -7 -21 -15 -25 -11 -4 -15 1 -15 19 0 16 6 25 15 25 8 0 15 -9 15 -19z m-3479 -32 c-14 -5 -30 -14 -37 -20 -8 -7 -13 -3 -17 11 -5 18 0 20 36 19 38 0 40 -1 18 -10z m3359 -572 c0 -7 -7 -22 -15 -33 -13 -17 -14 -16 -4 14 11 33 19 41 19 19z m-3345 -198 c7 -22 -7 -39 -33 -39 -10 0 -10 4 0 20 11 17 10 22 -2 30 -12 8 -11 10 7 10 13 0 24 -8 28 -21z m445 -37 c0 -23 -35 -8 -38 16 -3 21 -2 22 17 10 12 -7 21 -19 21 -26z m-310 -647 c-28 -30 -60 -33 -60 -7 0 26 9 32 48 32 l35 0 -23 -25z M3684 5414 c-14 -21 -16 -41 -10 -124 4 -62 2 -100 -3 -100 -9 0 -17 26 -25 84 -1 11 -8 22 -14 24 -9 3 -12 -27 -12 -105 0 -69 -6 -130 -16 -164 -24 -86 -24 -85 -33 -70 -6 9 -10 3 -14 -17 -4 -20 -23 -43 -59 -72 -29 -23 -54 -46 -54 -51 -1 -5 -3 -39 -4 -76 -1 -46 -4 -60 -9 -43 -6 21 -9 15 -20 -35 -16 -74 -29 -99 -97 -197 -35 -49 -54 -86 -54 -105 0 -31 59 -200 60 -171 0 9 6 21 14 25 13 7 13 16 0 68 -17 72 -12 78 75 93 106 19 127 33 145 95 20 70 65 111 186 172 83 42 89 47 75 62 -9 8 -14 23 -11 32 5 14 5 14 -6 1 -7 -8 -41 -25 -75 -37 -54 -19 -61 -19 -56 -5 10 26 43 72 91 126 46 53 77 104 68 114 -6 6 -95 -81 -143 -141 l-30 -38 -7 80 c-4 45 -5 89 -1 98 3 10 31 34 63 53 61 37 147 112 164 144 21 38 -53 8 -126 -52 -98 -80 -100 -81 -92 -46 12 47 54 133 81 166 14 16 25 42 25 56 l-1 27 -25 -30 -26 -30 6 53 c3 28 9 74 12 100 5 40 3 50 -10 55 -10 4 -22 -2 -32 -19z m-79 -654 c0 -125 0 -126 -33 -176 -22 -36 -56 -67 -109 -103 -42 -28 -82 -51 -90 -51 -20 0 -16 22 11 59 13 18 30 46 36 62 7 16 16 29 20 29 5 0 13 33 20 73 16 102 29 149 40 142 6 -4 10 -2 10 3 0 5 13 25 30 43 16 19 30 38 30 42 0 5 8 7 18 5 15 -3 17 -17 17 -128z M2287 5348 c-10 -10 -22 -16 -27 -13 -5 3 -8 -3 -8 -12 1 -10 -2 -37 -6 -59 -6 -32 -4 -43 7 -47 23 -9 67 61 67 105 0 44 -9 51 -33 26z M1853 5256 c4 -22 11 -42 17 -46 7 -4 14 -26 17 -49 3 -22 10 -41 15 -41 5 0 7 12 4 28 -3 15 -8 46 -11 69 -4 23 -16 49 -27 60 -21 18 -21 18 -15 -21z M1780 5267 c0 -47 15 -79 42 -93 l30 -16 -7 34 c-4 18 -12 44 -19 58 -12 28 -46 40 -46 17z M1960 5120 c0 -21 30 -70 49 -80 27 -14 31 -13 31 10 0 11 5 20 10 20 6 0 8 -11 4 -25 -6 -23 -4 -25 30 -25 20 0 36 4 36 8 0 18 -68 72 -109 87 -52 18 -51 18 -51 5z M4127 5076 c-4 -10 -7 -45 -7 -79 0 -42 -6 -70 -18 -88 -56 -85 -73 -107 -106 -138 -20 -19 -36 -39 -36 -43 0 -18 34 -6 62 23 29 28 34 30 84 24 41 -6 59 -3 83 11 37 22 44 37 28 59 -16 21 -72 22 -95 2 -21 -19 -32 -22 -32 -7 0 5 38 35 84 66 77 51 106 84 106 119 0 26 -43 16 -77 -17 l-33 -32 0 47 c0 34 -4 47 -14 47 -8 0 -16 5 -18 12 -3 7 -7 5 -11 -6z M1049 5043 c-8 -10 -9 -14 -2 -10 7 5 28 -18 57 -63 48 -74 62 -86 52 -45 -4 15 -2 25 4 25 15 0 -17 72 -41 93 -27 22 -53 22 -70 0z M3400 4964 c-14 -37 -12 -60 6 -75 14 -11 18 -10 30 6 19 26 18 81 -2 89 -23 9 -24 8 -34 -20z M3910 4933 c-29 -20 -40 -33 -40 -50 0 -33 63 6 75 45 8 26 -2 28 -35 5z M3401 4844 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z M4532 4829 c-8 -12 -9 -18 -3 -14 7 4 11 -8 12 -32 1 -21 4 -51 8 -68 6 -26 8 -22 13 29 5 39 3 67 -5 83 l-12 23 -13 -21z M1017 4820 c-8 -30 16 -52 65 -60 41 -6 45 -5 40 12 -5 20 -68 68 -88 68 -6 0 -14 -9 -17 -20z M2529 4778 c-11 -21 -7 -134 7 -185 8 -30 24 -19 24 17 0 16 7 35 15 44 18 17 20 70 4 115 -13 36 -33 40 -50 9z M830 4767 c0 -24 30 -116 40 -122 11 -7 1 92 -12 118 -10 20 -28 23 -28 4z M4660 4751 c0 -5 -9 -12 -19 -16 -23 -7 -101 -81 -101 -95 0 -26 50 -6 98 38 51 47 67 82 37 82 -8 0 -15 -4 -15 -9z M764 4719 c-7 -13 82 -106 92 -96 11 11 -9 43 -46 75 -27 23 -41 29 -46 21z M1887 4634 c-18 -18 -6 -91 21 -131 79 -118 125 -171 138 -159 10 9 8 18 -9 46 -12 19 -25 37 -29 38 -4 2 -8 25 -8 50 0 60 -15 115 -38 135 -20 19 -65 31 -75 21z M1030 4579 c0 -35 54 -169 68 -169 6 0 12 -8 12 -17 1 -17 2 -17 15 0 11 16 11 26 -5 71 -25 75 -47 114 -70 126 -17 9 -20 8 -20 -11z M697 4553 c19 -54 69 -92 112 -85 36 5 46 34 20 58 -11 9 -22 14 -25 11 -4 -4 -18 3 -32 13 -32 26 -84 28 -75 3z M1150 4486 c0 -36 52 -76 101 -76 47 0 57 29 12 32 -16 1 6 4 47 8 l75 6 -75 2 c-41 1 -85 8 -96 15 -12 6 -31 18 -43 25 -19 12 -21 11 -21 -12z M3909 4427 c-16 -27 -19 -51 -19 -149 0 -111 1 -118 20 -118 15 0 24 14 40 68 39 124 39 215 -1 225 -15 4 -25 -2 -40 -26z M4723 4375 c-30 -13 -33 -18 -33 -60 l0 -45 24 15 c27 18 49 48 60 83 9 26 -4 28 -51 7z M5002 4348 c-5 -7 -23 -40 -40 -73 -32 -65 -106 -146 -156 -172 -17 -9 -51 -21 -75 -28 -46 -11 -63 -28 -38 -38 7 -3 47 2 87 10 58 12 85 13 128 4 96 -19 162 -13 162 15 0 11 -19 14 -82 15 -46 0 -91 4 -101 8 -17 6 -17 8 2 25 11 10 44 25 73 32 34 8 43 13 27 13 -31 1 -44 24 -23 41 8 7 14 21 14 31 0 11 10 36 23 57 33 52 39 72 23 72 -7 0 -18 -6 -24 -12z M3632 4290 c-52 -37 -68 -69 -74 -145 -3 -33 -3 -54 0 -47 3 10 7 9 16 -4 12 -15 18 -14 79 15 69 33 101 66 95 98 -4 22 -37 30 -49 11 -6 -8 -7 10 -3 45 8 68 -2 72 -64 27z M5016 4271 c-11 -12 -16 -24 -12 -28 9 -9 39 22 34 37 -2 7 -10 4 -22 -9z M470 4264 c0 -9 8 -28 18 -42 10 -15 22 -33 27 -40 8 -15 -67 31 -102 62 -13 11 -23 14 -23 8 0 -7 -7 -12 -15 -12 -19 0 -19 -15 2 -50 20 -35 48 -48 118 -56 37 -5 55 -11 55 -20 0 -8 11 -14 25 -14 14 0 25 4 25 10 0 5 -22 43 -50 84 -49 73 -80 100 -80 70z M2382 4102 c-5 -9 -12 -42 -14 -72 -9 -96 18 -125 39 -43 17 66 17 120 -2 127 -8 3 -19 -2 -23 -12z M4235 3850 c36 -39 118 -80 162 -80 30 0 93 41 93 61 0 5 -14 15 -30 24 -25 13 -44 14 -107 6 -43 -5 -80 -6 -83 -1 -3 5 -19 12 -35 16 l-30 7 30 -33z M133 3667 c-19 -41 -15 -59 18 -86 30 -24 135 -55 151 -45 11 7 10 42 -2 49 -5 3 -10 11 -10 17 0 6 7 5 17 -3 12 -10 15 -10 9 -1 -13 22 -84 52 -125 52 -33 0 -40 4 -43 22 -3 21 -4 21 -15 -5z M5265 3631 c-16 -10 -43 -33 -60 -51 l-30 -32 83 4 c77 3 86 6 107 30 35 41 24 68 -29 68 -23 0 -54 -8 -71 -19z M400 3512 c0 -5 -53 -12 -117 -16 -64 -4 -120 -9 -125 -12 -16 -10 -8 -53 13 -65 51 -27 183 0 213 43 13 17 19 18 68 9 29 -5 66 -13 81 -16 22 -6 26 -4 21 9 -5 12 0 16 17 17 13 1 -3 7 -36 14 -106 22 -135 26 -135 17z M5090 3480 c0 -5 9 -10 20 -10 11 0 47 -13 80 -30 57 -29 121 -39 147 -22 22 14 19 45 -7 59 -30 15 -240 18 -240 3z M926 3272 c-12 -11 -6 -94 9 -122 16 -31 39 -47 86 -60 24 -7 29 -5 39 21 19 44 5 94 -34 124 -35 27 -90 47 -100 37z M500 3049 c0 -23 9 -62 20 -85 17 -33 30 -46 70 -62 34 -14 56 -32 70 -54 16 -28 109 -108 125 -108 3 0 5 7 5 15 0 12 7 14 28 9 15 -3 -8 11 -50 32 -42 20 -80 44 -83 53 -4 9 -9 46 -12 84 -5 61 -9 70 -41 98 -34 30 -59 43 -107 55 -24 6 -25 4 -25 -37z M5203 2779 c-18 -11 -35 -29 -38 -40 -7 -22 -20 -25 -32 -6 -8 13 -20 -26 -22 -74 -1 -30 25 -36 35 -7 4 15 9 17 20 7 8 -6 14 -18 14 -26 0 -11 7 -9 27 7 43 33 63 71 63 118 0 49 -14 53 -67 21z M5307 2622 c-34 -32 -86 -72 -115 -88 -51 -29 -61 -46 -29 -52 25 -5 198 81 229 114 15 16 30 41 33 57 7 25 5 27 -24 27 -25 0 -45 -13 -94 -58z M447 2652 c-10 -10 -15 -21 -12 -24 7 -7 75 24 75 34 0 15 -47 8 -63 -10z M263 2600 c-12 -5 -26 -18 -32 -28 -8 -15 -6 -24 13 -41 33 -31 84 -27 160 13 68 36 68 47 1 51 -16 1 -50 5 -75 8 -25 4 -55 3 -67 -3z M996 2540 c-37 -11 -102 -74 -115 -112 l-10 -28 58 0 c48 0 65 5 98 29 49 35 87 86 79 106 -6 17 -62 19 -110 5z M673 2324 c-43 -18 -42 -43 1 -88 33 -35 35 -36 114 -36 70 0 80 2 78 18 0 10 6 16 16 16 13 -1 9 8 -18 40 -19 22 -45 46 -57 53 -29 16 -94 15 -134 -3z M4790 2120 c-9 -17 -5 -20 43 -29 72 -15 166 -19 169 -9 2 5 11 6 21 3 20 -8 23 -35 5 -35 -7 -1 -2 -7 12 -15 29 -17 113 -20 141 -5 24 13 25 36 2 48 -10 5 -58 12 -108 15 -49 3 -117 12 -150 20 -110 28 -124 28 -135 7z M4881 2021 c6 -17 30 -50 53 -75 33 -36 49 -46 74 -46 25 0 32 4 32 19 0 25 -60 77 -99 86 -17 4 -36 15 -41 26 -17 30 -30 23 -19 -10z";
1322
1317
 
1323
1318
  // src/genre-tree/TreeMarkIcon.tsx
1324
- var import_jsx_runtime22 = require("react/jsx-runtime");
1319
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1325
1320
  function TreeMarkIcon({ className: className2 }) {
1326
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
1321
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
1327
1322
  }
1328
1323
 
1329
1324
  // src/genre-tree/GenrePlayActions.tsx
1330
- var import_jsx_runtime23 = require("react/jsx-runtime");
1325
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1331
1326
  function PlayIcon({ kind }) {
1332
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
1333
- kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1334
- kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1335
- kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
1336
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
1337
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
1327
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
1328
+ kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1329
+ kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1330
+ kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
1331
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
1332
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
1338
1333
  ] }),
1339
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react8.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1334
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react8.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1340
1335
  ] });
1341
1336
  }
1342
1337
  function playOptions(playlist) {
@@ -1352,13 +1347,13 @@ function playOptions(playlist) {
1352
1347
  function GenrePlayActions({ genrePlaylist, playback }) {
1353
1348
  const { isPlaying, setIsPlaying } = (0, import_player4.usePlayer)();
1354
1349
  const options = playOptions(genrePlaylist);
1355
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: options.map((option) => {
1350
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: options.map((option) => {
1356
1351
  const isCurrent = playback.playing?.uuid === genrePlaylist.uuid && (option.selection === void 0 || playback.playing.selectionKey === "" || playback.playing.selectionKey === (0, import_genre_tree10.genreMusicSelectionKey)(option.selection).join());
1357
1352
  const playing = isCurrent && isPlaying;
1358
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1353
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1359
1354
  import_ui7.ActionButton,
1360
1355
  {
1361
- icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react8.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(PlayIcon, { kind: option.icon }),
1356
+ icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react8.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PlayIcon, { kind: option.icon }),
1362
1357
  label: playing ? "Pause" : option.label,
1363
1358
  caption: String(option.count),
1364
1359
  disabled: option.count === 0,
@@ -1368,9 +1363,7 @@ function GenrePlayActions({ genrePlaylist, playback }) {
1368
1363
  setIsPlaying(!isPlaying);
1369
1364
  return;
1370
1365
  }
1371
- playback.play(genrePlaylist, option.selection).catch((error) => {
1372
- console.error("Failed to load genre playlist tracks:", error);
1373
- });
1366
+ void playback.play(genrePlaylist, option.selection);
1374
1367
  }
1375
1368
  },
1376
1369
  option.key
@@ -1384,7 +1377,7 @@ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
1384
1377
 
1385
1378
  // src/genre-tree/ClampedText.tsx
1386
1379
  var import_react12 = require("react");
1387
- var import_jsx_runtime24 = require("react/jsx-runtime");
1380
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1388
1381
  function ClampedText({ children, className: className2 = "" }) {
1389
1382
  const ref = (0, import_react12.useRef)(null);
1390
1383
  const [expanded, setExpanded] = (0, import_react12.useState)(false);
@@ -1394,9 +1387,9 @@ function ClampedText({ children, className: className2 = "" }) {
1394
1387
  if (!element || expanded) return;
1395
1388
  setOverflows(element.scrollHeight > element.clientHeight);
1396
1389
  }, [children, expanded]);
1397
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
1398
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1399
- (overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1390
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
1391
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1392
+ (overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1400
1393
  "button",
1401
1394
  {
1402
1395
  type: "button",
@@ -1416,13 +1409,13 @@ var import_zod = require("zod");
1416
1409
  var import_fa2 = require("react-icons/fa");
1417
1410
  var import_ui8 = require("@behindthemusictree/ui");
1418
1411
  var import_auth = require("@behindthemusictree/app-kit/auth");
1412
+ var import_transport2 = require("@behindthemusictree/app-kit/transport");
1419
1413
  var import_transport3 = require("@behindthemusictree/app-kit/transport");
1420
1414
  var import_transport4 = require("@behindthemusictree/app-kit/transport");
1421
- var import_transport5 = require("@behindthemusictree/app-kit/transport");
1422
1415
  var import_player5 = require("@behindthemusictree/app-kit/player");
1423
1416
  var import_player6 = require("@behindthemusictree/app-kit/player");
1424
- var import_popup4 = require("@behindthemusictree/app-kit/popup");
1425
- var import_transport6 = require("@behindthemusictree/app-kit/transport");
1417
+ var import_popup3 = require("@behindthemusictree/app-kit/popup");
1418
+ var import_transport5 = require("@behindthemusictree/app-kit/transport");
1426
1419
  var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1427
1420
  var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1428
1421
  var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
@@ -1431,15 +1424,15 @@ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
1431
1424
 
1432
1425
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1433
1426
  var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1434
- var import_jsx_runtime25 = require("react/jsx-runtime");
1427
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1435
1428
  function MusicBrainzRecordingLink({
1436
1429
  song,
1437
1430
  children,
1438
1431
  className: className2 = ""
1439
1432
  }) {
1440
1433
  const url = (0, import_genre_tree11.musicBrainzRecordingUrl)(song);
1441
- if (!url) return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_jsx_runtime25.Fragment, { children });
1442
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1434
+ if (!url) return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children });
1435
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1443
1436
  "a",
1444
1437
  {
1445
1438
  href: url,
@@ -1456,9 +1449,9 @@ function MusicBrainzRecordingLink({
1456
1449
  // src/genre-tree/GenrePlaylistTracks.tsx
1457
1450
  var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
1458
1451
  var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
1459
- var import_jsx_runtime26 = require("react/jsx-runtime");
1452
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1460
1453
  var CATALOGUE_PAGE_SIZE = 100;
1461
- var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1454
+ var SongPageSchema = (0, import_transport3.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1462
1455
  overallTotal: import_zod.z.number().nullable(),
1463
1456
  page: import_zod.z.number().nullable(),
1464
1457
  totalPages: import_zod.z.number().nullable()
@@ -1490,12 +1483,12 @@ function GenrePlaylistTracks({
1490
1483
  const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree16.useTrackList)();
1491
1484
  const { session, sessionRestored } = (0, import_auth.useSession)();
1492
1485
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player5.usePlayer)();
1493
- const { showPopup } = (0, import_popup4.usePopup)();
1486
+ const { showPopup } = (0, import_popup3.usePopup)();
1494
1487
  const trackNoun = (0, import_track_noun.useTrackNoun)();
1495
1488
  const uuid = genrePlaylist?.uuid;
1496
1489
  const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
1497
- const { fetchBound } = (0, import_transport3.usePlaybackFetch)(getBackendBaseUrl);
1498
- const playback = (0, import_transport3.usePlaybackContext)();
1490
+ const { fetchBound } = (0, import_transport2.usePlaybackFetch)(getBackendBaseUrl);
1491
+ const playback = (0, import_transport2.usePlaybackContext)();
1499
1492
  const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
1500
1493
  queryKey: [...scope === "me" ? uuid ? import_genre_tree13.genrePlaylistQueryKeys.me.tracks(uuid) : ["missing-personal-genre-playlist", genreUuid] : artist ? [...import_genre_tree14.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : import_genre_tree14.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
1501
1494
  queryFn: async ({ pageParam }) => {
@@ -1510,7 +1503,7 @@ function GenrePlaylistTracks({
1510
1503
  {},
1511
1504
  { genre: genreUuid, ...(0, import_genre_tree12.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1512
1505
  );
1513
- const parsed = (0, import_transport5.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1506
+ const parsed = (0, import_transport4.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1514
1507
  return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
1515
1508
  },
1516
1509
  initialPageParam: scope === "me" ? 1 : "",
@@ -1542,35 +1535,36 @@ function GenrePlaylistTracks({
1542
1535
  }) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genreName }, scope, { startSongUuid, ...selection && { selection } }) : genrePlaylist ? playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid }) : Promise.reject(new Error("Personal genre has no playlist"));
1543
1536
  started.catch((error) => {
1544
1537
  console.error("Failed to play genre playlist:", error);
1545
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
1538
+ if (error instanceof import_genre_tree16.TracksFailureAlreadyReportedError) return;
1539
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport5.AppError ? error.code : import_transport5.ErrorCode.CLIENT_UNKNOWN }));
1546
1540
  });
1547
1541
  };
1548
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { children: [
1542
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { children: [
1549
1543
  renderTracksToolbar?.(),
1550
- scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { role: "alert", children: [
1544
+ scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("p", { role: "alert", children: [
1551
1545
  "Couldn't load ",
1552
1546
  trackNoun.plural,
1553
1547
  ".",
1554
1548
  " ",
1555
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1556
- ] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1549
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1550
+ ] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1557
1551
  tracks.map((track, index) => {
1558
1552
  const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
1559
1553
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1560
1554
  const isPlaying = isCurrent && playState === import_player6.PlayStates.PLAYING;
1561
- const text = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: TEXT_CELL, children: [
1562
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1563
- track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1564
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_genre_tree17.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1565
- track.album && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1555
+ const text = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: TEXT_CELL, children: [
1556
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1557
+ track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1558
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_genre_tree17.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1559
+ track.album && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1566
1560
  ] });
1567
1561
  const leading = renderSongLeading?.(track, index);
1568
1562
  const actions = renderSongActions?.(track);
1569
1563
  const isUnplayable = (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
1570
1564
  if (isUnplayable) {
1571
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1565
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1572
1566
  leading,
1573
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1567
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1574
1568
  "button",
1575
1569
  {
1576
1570
  type: "button",
@@ -1578,27 +1572,27 @@ function GenrePlaylistTracks({
1578
1572
  "aria-disabled": "true",
1579
1573
  "aria-label": label,
1580
1574
  className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
1581
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_fa2.FaPlay, { size: 10 })
1575
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_fa2.FaPlay, { size: 10 })
1582
1576
  }
1583
1577
  ),
1584
1578
  text,
1585
1579
  actions
1586
1580
  ] }, track.uuid);
1587
1581
  }
1588
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1582
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1589
1583
  "li",
1590
1584
  {
1591
1585
  className: `group flex min-h-8 items-center gap-1 rounded px-1 hover:bg-[var(--color-neutral-100)] ${isCurrent ? "font-medium text-[var(--color-primary)]" : ""}`,
1592
1586
  children: [
1593
1587
  leading,
1594
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1588
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1595
1589
  "button",
1596
1590
  {
1597
1591
  type: "button",
1598
1592
  "aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
1599
1593
  className: `flex h-6 w-6 shrink-0 items-center justify-center rounded-full cursor-pointer group-hover:opacity-100 focus-visible:opacity-100 ${isCurrent && playState !== import_player6.PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
1600
1594
  onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
1601
- children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui8.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_fa2.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_fa2.FaPlay, { size: 10 })
1595
+ children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui8.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_fa2.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_fa2.FaPlay, { size: 10 })
1602
1596
  }
1603
1597
  ),
1604
1598
  text,
@@ -1608,7 +1602,7 @@ function GenrePlaylistTracks({
1608
1602
  track.uuid
1609
1603
  );
1610
1604
  }),
1611
- hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1605
+ hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1612
1606
  ] })
1613
1607
  ] }) });
1614
1608
  }
@@ -1616,7 +1610,7 @@ function GenrePlaylistTracks({
1616
1610
  // src/genre-tree/GenreDetailExtras.tsx
1617
1611
  var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
1618
1612
  var import_si2 = require("react-icons/si");
1619
- var import_jsx_runtime27 = require("react/jsx-runtime");
1613
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1620
1614
  function GenreDetailExtras({
1621
1615
  genreUuid,
1622
1616
  genrePlaylist,
@@ -1642,17 +1636,17 @@ function GenreDetailExtras({
1642
1636
  criteriaOverviewSchema
1643
1637
  );
1644
1638
  if (isPending) {
1645
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
1646
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4" }) }) }),
1639
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui9.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4" }) }) }),
1647
1641
  family
1648
1642
  ] });
1649
1643
  }
1650
- if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children: family });
1644
+ if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_jsx_runtime28.Fragment, { children: family });
1651
1645
  const { summary, summarySourceUrl, essentialSongs } = overview;
1652
1646
  const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
1653
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
1654
- (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
1655
- summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1647
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1648
+ (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui9.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
1649
+ summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1656
1650
  "a",
1657
1651
  {
1658
1652
  href: summarySourceUrl,
@@ -1660,12 +1654,12 @@ function GenreDetailExtras({
1660
1654
  rel: "noopener noreferrer",
1661
1655
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1662
1656
  children: [
1663
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1657
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1664
1658
  "Wikipedia"
1665
1659
  ]
1666
1660
  }
1667
1661
  ),
1668
- wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1662
+ wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1669
1663
  "a",
1670
1664
  {
1671
1665
  href: wikidataUrl,
@@ -1673,17 +1667,17 @@ function GenreDetailExtras({
1673
1667
  rel: "noopener noreferrer",
1674
1668
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1675
1669
  children: [
1676
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1670
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1677
1671
  "Wikidata"
1678
1672
  ]
1679
1673
  }
1680
1674
  )
1681
1675
  ] }) }),
1682
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1676
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui9.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1683
1677
  family,
1684
1678
  renderGenreDetailExtras?.(overview),
1685
- (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
1686
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1679
+ (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui9.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
1680
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1687
1681
  GenrePlaylistTracks,
1688
1682
  {
1689
1683
  genreUuid,
@@ -1701,10 +1695,10 @@ function GenreDetailExtras({
1701
1695
  }
1702
1696
 
1703
1697
  // src/genre-tree/GenreInfoPanelBody.tsx
1704
- var import_jsx_runtime28 = require("react/jsx-runtime");
1698
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1705
1699
  var import_react14 = require("react");
1706
1700
  function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
1707
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
1701
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
1708
1702
  }
1709
1703
  function GenreInfoPanelBody({
1710
1704
  toolbar,
@@ -1721,9 +1715,9 @@ function GenreInfoPanelBody({
1721
1715
  }) {
1722
1716
  const summary = scope === "reference" ? import_genre_tree19.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
1723
1717
  const selection = summary ? (0, import_genre_tree19.defaultGenreMusicSelection)(summary) : void 0;
1724
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1718
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1725
1719
  toolbar,
1726
- genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GenreActionBand, { ...band }),
1720
+ genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenreActionBand, { ...band }),
1727
1721
  /* @__PURE__ */ (0, import_react14.createElement)(
1728
1722
  GenreDetailExtras,
1729
1723
  {
@@ -1743,7 +1737,7 @@ function GenreInfoPanelBody({
1743
1737
  }
1744
1738
 
1745
1739
  // src/genre-tree/GenreDetailPanel.tsx
1746
- var import_jsx_runtime29 = require("react/jsx-runtime");
1740
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1747
1741
  var RELATIVE_ITEM_COUNT = 1;
1748
1742
  function toNode(genre, parentId) {
1749
1743
  return {
@@ -1766,7 +1760,7 @@ function useGenreDetailContent({
1766
1760
  trailing,
1767
1761
  genrePlaylist: hostPlaylist,
1768
1762
  expanded,
1769
- treeRelations
1763
+ treeStyles
1770
1764
  }) {
1771
1765
  const { data: detail, isPending, isError } = (0, import_genre_tree20.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
1772
1766
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
@@ -1774,19 +1768,12 @@ function useGenreDetailContent({
1774
1768
  const view = (0, import_react15.useMemo)(() => {
1775
1769
  if (!detail) return null;
1776
1770
  const rootColor = (0, import_genre_tree_view4.getGenreTreeColor)(detail.root.wikidataId);
1777
- const treeStyles = /* @__PURE__ */ new Map();
1778
- if (treeRelations) {
1779
- const drawn = [treeRelations, treeRelations.parentNode, ...treeRelations.childNodes, ...treeRelations.ancestorNodes];
1780
- for (const entry of drawn) {
1781
- if (entry) treeStyles.set(entry.node.id, { fill: entry.fill, textColor: entry.textColor });
1782
- }
1783
- }
1784
1771
  const relative = (genre, parentId) => ({
1785
1772
  node: toNode(genre, parentId),
1786
- ...treeStyles.get(genre.uuid) ?? { fill: (0, import_genre_tree_view4.tintSurface)(rootColor), textColor: import_genre_tree_view4.TEXT_COLOR }
1773
+ ...treeStyles?.get(genre.uuid) ?? { fill: (0, import_genre_tree_view4.tintSurface)(rootColor), textColor: import_genre_tree_view4.TEXT_COLOR }
1787
1774
  });
1788
1775
  const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
1789
- const own = treeStyles.get(detail.uuid) ?? { fill: rootColor, textColor: (0, import_genre_tree_view4.getTextColorOnFill)(rootColor) };
1776
+ const own = treeStyles?.get(detail.uuid) ?? { fill: rootColor, textColor: (0, import_genre_tree_view4.getTextColorOnFill)(rootColor) };
1790
1777
  return {
1791
1778
  node: {
1792
1779
  id: detail.uuid,
@@ -1803,23 +1790,23 @@ function useGenreDetailContent({
1803
1790
  ({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
1804
1791
  )
1805
1792
  };
1806
- }, [detail, scope, treeRelations]);
1793
+ }, [detail, scope, treeStyles]);
1807
1794
  if (isError) return {
1808
1795
  name: null,
1809
1796
  title: "Genre unavailable",
1810
- content: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
1797
+ content: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
1811
1798
  };
1812
1799
  if (isPending || !detail || !view) return {
1813
1800
  name: null,
1814
1801
  title: "Loading genre\u2026",
1815
- content: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
1816
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.Skeleton, { className: "h-6 w-2/3" }),
1817
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.Skeleton, { className: "h-4 w-full" }),
1818
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.Skeleton, { className: "h-4 w-3/4" })
1802
+ content: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
1803
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.Skeleton, { className: "h-6 w-2/3" }),
1804
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.Skeleton, { className: "h-4 w-full" }),
1805
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.Skeleton, { className: "h-4 w-3/4" })
1819
1806
  ] })
1820
1807
  };
1821
1808
  const selectGenre = (nodeId) => onSelectGenre(import_genre_tree20.GenreUuidSchema.parse(nodeId));
1822
- const family = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1809
+ const family = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1823
1810
  import_genre_tree_view4.GenreNodeFamily,
1824
1811
  {
1825
1812
  node: view.node,
@@ -1830,17 +1817,17 @@ function useGenreDetailContent({
1830
1817
  onSelectNode: selectGenre
1831
1818
  }
1832
1819
  );
1833
- const ancestors = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1820
+ const ancestors = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1834
1821
  return {
1835
1822
  name: detail.name,
1836
1823
  title: detail.name,
1837
1824
  headerAccent: { background: view.fill, color: view.textColor },
1838
1825
  aside: expanded ? ancestors : void 0,
1839
- content: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1840
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1826
+ content: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1827
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1841
1828
  GenreInfoPanelBody,
1842
1829
  {
1843
- family: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1830
+ family: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1844
1831
  family,
1845
1832
  !expanded && ancestors
1846
1833
  ] }),
@@ -1884,7 +1871,7 @@ function GenreDetailPanel({
1884
1871
  expanded,
1885
1872
  band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
1886
1873
  });
1887
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1874
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1888
1875
  import_ui10.DetailPanel,
1889
1876
  {
1890
1877
  side: "left",
@@ -1897,7 +1884,7 @@ function GenreDetailPanel({
1897
1884
  edgeTab,
1898
1885
  aside: panel.aside,
1899
1886
  titleInSearch,
1900
- headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react9.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react9.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
1887
+ headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react9.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react9.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
1901
1888
  children: panel.content
1902
1889
  }
1903
1890
  );
@@ -1906,7 +1893,7 @@ function GenreDetailPanel({
1906
1893
  // src/genre-tree/ChangeParentPopup.tsx
1907
1894
  var import_react16 = require("react");
1908
1895
  var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
1909
- var import_jsx_runtime30 = require("react/jsx-runtime");
1896
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1910
1897
  function ChangeParentPopup({
1911
1898
  genrePlaylist,
1912
1899
  genrePlaylists,
@@ -1941,12 +1928,12 @@ function ChangeParentPopup({
1941
1928
  window.addEventListener("keydown", handleKeyDown);
1942
1929
  return () => window.removeEventListener("keydown", handleKeyDown);
1943
1930
  }, [onClose]);
1944
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
1945
- /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
1931
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
1932
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
1946
1933
  "Current parent: ",
1947
1934
  genrePlaylist.parent?.name ?? "none"
1948
1935
  ] }),
1949
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1936
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1950
1937
  GenreSearch,
1951
1938
  {
1952
1939
  genrePlaylists: candidates,
@@ -1954,7 +1941,7 @@ function ChangeParentPopup({
1954
1941
  placeholder: "Search a new parent\u2026"
1955
1942
  }
1956
1943
  ) }),
1957
- formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
1944
+ formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
1958
1945
  ] });
1959
1946
  }
1960
1947
 
@@ -1967,7 +1954,7 @@ var import_lucide_react10 = require("lucide-react");
1967
1954
  var import_ui11 = require("@behindthemusictree/ui");
1968
1955
  var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
1969
1956
  var import_player7 = require("@behindthemusictree/app-kit/player");
1970
- var import_jsx_runtime31 = require("react/jsx-runtime");
1957
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1971
1958
  function ArtistActionBand({ artist, share }) {
1972
1959
  const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree22.useTrackList)();
1973
1960
  const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
@@ -1983,11 +1970,11 @@ function ArtistActionBand({ artist, share }) {
1983
1970
  console.error("Failed to load artist tracks:", error);
1984
1971
  });
1985
1972
  };
1986
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui11.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_ui11.ActionBand, { children: [
1987
- /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1973
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ui11.ActionBand, { children: [
1974
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1988
1975
  import_ui11.ActionButton,
1989
1976
  {
1990
- icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
1977
+ icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
1991
1978
  label: playing ? "Pause" : "Play",
1992
1979
  caption: String(artist.playableTracksCount),
1993
1980
  disabled: artist.playableTracksCount === 0,
@@ -2006,7 +1993,7 @@ var import_ui13 = require("@behindthemusictree/ui");
2006
1993
  var import_lucide_react11 = require("lucide-react");
2007
1994
  var import_si3 = require("react-icons/si");
2008
1995
  var import_ui12 = require("@behindthemusictree/ui");
2009
- var import_jsx_runtime32 = require("react/jsx-runtime");
1996
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2010
1997
  var LINK_KINDS = {
2011
1998
  official: { label: "Official website", Icon: import_lucide_react11.Globe },
2012
1999
  wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
@@ -2043,7 +2030,7 @@ function artistLinks(artist) {
2043
2030
  return links;
2044
2031
  }
2045
2032
  function ArtistLinks({ links }) {
2046
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui12.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2033
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2047
2034
  }
2048
2035
 
2049
2036
  // src/genre-tree/artist/formatYears.ts
@@ -2054,7 +2041,7 @@ function formatYears(beginYear, endYear, ended) {
2054
2041
  }
2055
2042
 
2056
2043
  // src/genre-tree/artist/ArtistHeader.tsx
2057
- var import_jsx_runtime33 = require("react/jsx-runtime");
2044
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2058
2045
  var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
2059
2046
  function ArtistHeader({ artist }) {
2060
2047
  const fields = [];
@@ -2064,35 +2051,35 @@ function ArtistHeader({ artist }) {
2064
2051
  const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
2065
2052
  if (years) fields.push({ label: "Active", value: years });
2066
2053
  const links = artistLinks(artist);
2067
- if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ArtistLinks, { links }) });
2054
+ if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistLinks, { links }) });
2068
2055
  if (fields.length === 0) return null;
2069
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui13.FieldList, { items: fields });
2056
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui13.FieldList, { items: fields });
2070
2057
  }
2071
2058
 
2072
2059
  // src/genre-tree/artist/ArtistPhoto.tsx
2073
- var import_jsx_runtime34 = require("react/jsx-runtime");
2060
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2074
2061
  function ExternalLink({ href, children }) {
2075
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
2062
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
2076
2063
  }
2077
2064
  function ArtistPhoto({ image, name }) {
2078
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
2079
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
2080
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
2065
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
2066
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
2067
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
2081
2068
  "Photo: ",
2082
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2069
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2083
2070
  ",",
2084
2071
  " ",
2085
- image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2072
+ image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2086
2073
  ] })
2087
2074
  ] });
2088
2075
  }
2089
2076
 
2090
2077
  // src/genre-tree/artist/ArtistMembers.tsx
2091
2078
  var import_ui14 = require("@behindthemusictree/ui");
2092
- var import_jsx_runtime35 = require("react/jsx-runtime");
2079
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2093
2080
  function MembershipList({ title, memberships, renderArtistLink }) {
2094
2081
  if (memberships.length === 0) return null;
2095
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui14.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { children: memberships.map((membership, index) => {
2082
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { children: memberships.map((membership, index) => {
2096
2083
  const details = [
2097
2084
  formatYears(membership.beginYear, membership.endYear, membership.ended),
2098
2085
  ...membership.roles
@@ -2100,9 +2087,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2100
2087
  const detailsText = details.filter(Boolean).join(" \xB7 ");
2101
2088
  return (
2102
2089
  // A member can have several periods in the same group.
2103
- /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
2104
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
2105
- detailsText && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
2091
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
2092
+ detailsText && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2106
2093
  " \u2014 ",
2107
2094
  detailsText
2108
2095
  ] })
@@ -2113,9 +2100,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2113
2100
  function ArtistMembers({ title, memberships, renderArtistLink }) {
2114
2101
  if (memberships.length === 0) return null;
2115
2102
  const isCurrent = (membership) => !membership.ended && membership.endYear === null;
2116
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_ui14.PanelSection, { title, children: [
2117
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2118
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2103
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ui14.PanelSection, { title, children: [
2104
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2105
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2119
2106
  MembershipList,
2120
2107
  {
2121
2108
  title: "Past",
@@ -2128,7 +2115,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
2128
2115
 
2129
2116
  // src/genre-tree/artist/ArtistDiscography.tsx
2130
2117
  var import_ui15 = require("@behindthemusictree/ui");
2131
- var import_jsx_runtime36 = require("react/jsx-runtime");
2118
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2132
2119
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
2133
2120
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
2134
2121
  function discographyGroup({ primaryType, secondaryTypes }) {
@@ -2148,12 +2135,12 @@ function byYear(a, b) {
2148
2135
  function ArtistDiscography({ albums }) {
2149
2136
  if (albums.length === 0) return null;
2150
2137
  const sorted = [...albums].sort(byYear);
2151
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui15.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2138
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui15.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2152
2139
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
2153
2140
  if (groupAlbums.length === 0) return null;
2154
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui15.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2155
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2156
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2141
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui15.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2142
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2143
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2157
2144
  ] }, album.uuid)) }) }, group);
2158
2145
  }) });
2159
2146
  }
@@ -2161,11 +2148,10 @@ function ArtistDiscography({ albums }) {
2161
2148
  // src/genre-tree/artist/ArtistSongs.tsx
2162
2149
  var import_react17 = require("react");
2163
2150
  var import_react_query2 = require("@tanstack/react-query");
2164
- var import_lucide_react12 = require("lucide-react");
2165
2151
  var import_ui16 = require("@behindthemusictree/ui");
2166
2152
  var import_player11 = require("@behindthemusictree/app-kit/player");
2167
2153
  var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2168
- var import_transport7 = require("@behindthemusictree/app-kit/transport");
2154
+ var import_transport6 = require("@behindthemusictree/app-kit/transport");
2169
2155
 
2170
2156
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2171
2157
  var import_player10 = require("@behindthemusictree/app-kit/player");
@@ -2175,7 +2161,7 @@ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
2175
2161
  var import_fa3 = require("react-icons/fa");
2176
2162
  var import_player8 = require("@behindthemusictree/app-kit/player");
2177
2163
  var import_player9 = require("@behindthemusictree/app-kit/player");
2178
- var import_jsx_runtime37 = require("react/jsx-runtime");
2164
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2179
2165
  function TrackPositionPlayPause({
2180
2166
  position,
2181
2167
  uuid,
@@ -2189,14 +2175,14 @@ function TrackPositionPlayPause({
2189
2175
  const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PLAYING;
2190
2176
  const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PAUSED;
2191
2177
  const isCurrentTrackActive = isPlaying || isPaused;
2192
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2178
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2193
2179
  "div",
2194
2180
  {
2195
2181
  className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
2196
2182
  style: { minWidth: "4rem", maxWidth: "4rem" },
2197
2183
  onClick: handlePlayPauseClick,
2198
2184
  children: [
2199
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2185
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2200
2186
  "span",
2201
2187
  {
2202
2188
  className: "track-position-play-pause__bars",
@@ -2204,18 +2190,18 @@ function TrackPositionPlayPause({
2204
2190
  role: "img",
2205
2191
  "aria-label": isPlaying ? "Playing" : "Paused",
2206
2192
  children: [
2207
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" }),
2208
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" }),
2209
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" })
2193
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" }),
2194
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" }),
2195
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" })
2210
2196
  ]
2211
2197
  }
2212
2198
  ) : position }),
2213
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2199
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2214
2200
  "div",
2215
2201
  {
2216
2202
  className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
2217
2203
  "aria-hidden": "true",
2218
- children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fa3.FaPlay, {})
2204
+ children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_fa3.FaPlay, {})
2219
2205
  }
2220
2206
  )
2221
2207
  ]
@@ -2226,7 +2212,7 @@ function TrackPositionPlayPause({
2226
2212
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2227
2213
  var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
2228
2214
  var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
2229
- var import_jsx_runtime38 = require("react/jsx-runtime");
2215
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2230
2216
  function TrackItem({
2231
2217
  track,
2232
2218
  position,
@@ -2250,12 +2236,12 @@ function TrackItem({
2250
2236
  loadTrackForPlayer(track.uuid);
2251
2237
  }
2252
2238
  };
2253
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2239
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
2254
2240
  "div",
2255
2241
  {
2256
2242
  className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
2257
2243
  children: [
2258
- isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2244
+ isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2259
2245
  "div",
2260
2246
  {
2261
2247
  className: "flex items-center justify-center text-lg w-16",
@@ -2263,7 +2249,7 @@ function TrackItem({
2263
2249
  "aria-disabled": "true",
2264
2250
  children: position
2265
2251
  }
2266
- ) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2252
+ ) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2267
2253
  TrackPositionPlayPause,
2268
2254
  {
2269
2255
  position,
@@ -2272,12 +2258,12 @@ function TrackItem({
2272
2258
  isSelected: selectedTrack?.uuid === track.uuid
2273
2259
  }
2274
2260
  ),
2275
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2276
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree24.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2277
- track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree25.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
2261
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2262
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree24.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2263
+ track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree25.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
2278
2264
  ] }),
2279
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
2280
- renderDuration && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2265
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
2266
+ renderDuration && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2281
2267
  "div",
2282
2268
  {
2283
2269
  className: "duration flex text-base w-16 items-center justify-center",
@@ -2285,15 +2271,15 @@ function TrackItem({
2285
2271
  children: renderDuration(track)
2286
2272
  }
2287
2273
  ),
2288
- renderActions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2274
+ renderActions && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2289
2275
  ]
2290
2276
  }
2291
2277
  );
2292
2278
  }
2293
2279
 
2294
2280
  // src/genre-tree/artist/ArtistSongs.tsx
2295
- var import_jsx_runtime39 = require("react/jsx-runtime");
2296
- var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
2281
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2282
+ var SongPageSchema2 = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
2297
2283
  var SORTS = [
2298
2284
  { ordering: "title", label: "Title" },
2299
2285
  { ordering: "-albumYear", label: "Year" }
@@ -2303,7 +2289,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2303
2289
  const [ordering, setOrdering] = (0, import_react17.useState)("title");
2304
2290
  const { playNewTrackListFromArtist } = (0, import_genre_tree26.useTrackList)();
2305
2291
  const { playerTrackObject } = (0, import_player11.usePlayer)();
2306
- const { data, isPending, isError } = (0, import_transport7.usePlaybackQuery)({
2292
+ const { data, isPending, isError } = (0, import_transport6.usePlaybackQuery)({
2307
2293
  getBackendBaseUrl,
2308
2294
  queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
2309
2295
  queryFn: (fetchBound) => fetchBound(import_genre_tree26.libraryEndpoints.reference.songs.list(), true, false, void 0, {
@@ -2325,36 +2311,22 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2325
2311
  event.stopPropagation();
2326
2312
  void play(song.uuid);
2327
2313
  };
2328
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui16.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui16.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
2329
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
2330
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
2331
- "button",
2332
- {
2333
- type: "button",
2334
- onClick: () => void play(),
2335
- className: "flex w-fit items-center gap-1.5 px-3 py-1 text-caption font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
2336
- children: [
2337
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react12.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2338
- "Play all"
2339
- ]
2340
- }
2341
- ),
2342
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2343
- "button",
2344
- {
2345
- type: "button",
2346
- "aria-pressed": ordering === sort.ordering,
2347
- onClick: () => {
2348
- setOrdering(sort.ordering);
2349
- setPage(1);
2350
- },
2351
- className: "px-2.5 py-0.5 text-[var(--color-text)] rounded-full hover:bg-[var(--color-surface-muted)] aria-pressed:bg-gray-900 aria-pressed:text-white",
2352
- children: sort.label
2314
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2315
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2316
+ "button",
2317
+ {
2318
+ type: "button",
2319
+ "aria-pressed": ordering === sort.ordering,
2320
+ onClick: () => {
2321
+ setOrdering(sort.ordering);
2322
+ setPage(1);
2353
2323
  },
2354
- sort.ordering
2355
- )) })
2356
- ] }),
2357
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2324
+ className: "px-2.5 py-0.5 text-[var(--color-text)] rounded-full hover:bg-[var(--color-surface-muted)] aria-pressed:bg-gray-900 aria-pressed:text-white",
2325
+ children: sort.label
2326
+ },
2327
+ sort.ordering
2328
+ )) }) }),
2329
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2358
2330
  TrackItem,
2359
2331
  {
2360
2332
  track: song,
@@ -2363,12 +2335,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2363
2335
  renderSongLink
2364
2336
  }
2365
2337
  ) }, song.uuid)) }),
2366
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui16.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2338
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2367
2339
  ] }) });
2368
2340
  }
2369
2341
 
2370
2342
  // src/genre-tree/artist/useArtistPanel.tsx
2371
- var import_jsx_runtime40 = require("react/jsx-runtime");
2343
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2372
2344
  function useArtistPanel({
2373
2345
  uuid,
2374
2346
  scope,
@@ -2389,26 +2361,26 @@ function useArtistPanel({
2389
2361
  return {
2390
2362
  ...panel,
2391
2363
  title: "Artist",
2392
- content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2393
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2394
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
2395
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2396
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" }),
2397
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-16 mt-2.5" }),
2398
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-16" })
2364
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2365
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2366
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
2367
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2368
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" }),
2369
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-16 mt-2.5" }),
2370
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-16" })
2399
2371
  ] })
2400
2372
  };
2401
2373
  case "not-found":
2402
2374
  return {
2403
2375
  ...panel,
2404
2376
  title: "Artist not found",
2405
- content: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2377
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2406
2378
  };
2407
2379
  case "error":
2408
2380
  return {
2409
2381
  ...panel,
2410
2382
  title: "Artist",
2411
- content: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2383
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2412
2384
  };
2413
2385
  case "success": {
2414
2386
  const { artist } = result;
@@ -2417,19 +2389,19 @@ function useArtistPanel({
2417
2389
  return {
2418
2390
  ...panel,
2419
2391
  title: artist.name,
2420
- content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2421
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2392
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2393
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2422
2394
  ArtistActionBand,
2423
2395
  {
2424
2396
  artist,
2425
- share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2397
+ share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2426
2398
  }
2427
2399
  ),
2428
- artist.image && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
2429
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistHeader, { artist }),
2430
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2431
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2432
- artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2400
+ artist.image && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
2401
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistHeader, { artist }),
2402
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2403
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2404
+ artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2433
2405
  ArtistSongs,
2434
2406
  {
2435
2407
  artist: { uuid: artist.uuid, name: artist.name },
@@ -2439,7 +2411,7 @@ function useArtistPanel({
2439
2411
  }
2440
2412
  )
2441
2413
  ] }),
2442
- extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2414
+ extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2443
2415
  };
2444
2416
  }
2445
2417
  }
@@ -2451,11 +2423,11 @@ var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
2451
2423
 
2452
2424
  // src/genre-tree/song/SongPanelContent.tsx
2453
2425
  var import_react18 = require("react");
2454
- var import_lucide_react13 = require("lucide-react");
2426
+ var import_lucide_react12 = require("lucide-react");
2455
2427
  var import_ui18 = require("@behindthemusictree/ui");
2456
2428
  var import_player12 = require("@behindthemusictree/app-kit/player");
2457
2429
  var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
2458
- var import_jsx_runtime41 = require("react/jsx-runtime");
2430
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2459
2431
  var numberFormat = new Intl.NumberFormat("en");
2460
2432
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2461
2433
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
@@ -2463,27 +2435,27 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2463
2435
  const [open, setOpen] = (0, import_react18.useState)(false);
2464
2436
  const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
2465
2437
  let body = null;
2466
- if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.Skeleton, { className: "h-10", "aria-busy": "true" });
2467
- else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2438
+ if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-10", "aria-busy": "true" });
2439
+ else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2468
2440
  else if (history.status === "success")
2469
- body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("li", { className: "py-1.5 text-body", children: [
2470
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium", children: entry.action }),
2471
- entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2441
+ body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { className: "py-1.5 text-body", children: [
2442
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium", children: entry.action }),
2443
+ entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2472
2444
  " ",
2473
2445
  "\u2014 ",
2474
2446
  entry.oldValue ?? "none",
2475
2447
  " \u2192 ",
2476
2448
  entry.newValue ?? "none"
2477
2449
  ] }) : null,
2478
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2450
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2479
2451
  entry.actorPseudo ?? "Anonymous",
2480
2452
  " \xB7 ",
2481
2453
  dateFormat.format(new Date(entry.createdOn))
2482
2454
  ] })
2483
2455
  ] }, entry.uuid)) });
2484
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2485
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui18.PANEL_LABEL_CLASS}`, children: "History" }),
2486
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "mt-1.5", children: body })
2456
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2457
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui18.PANEL_LABEL_CLASS}`, children: "History" }),
2458
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "mt-1.5", children: body })
2487
2459
  ] }) });
2488
2460
  }
2489
2461
  function SongPanelContent(props) {
@@ -2502,13 +2474,13 @@ function SongPanelContent(props) {
2502
2474
  const genre = song.genre;
2503
2475
  fields.push({
2504
2476
  label: "Genre",
2505
- value: onSelectGenre ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
2506
- index > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
2507
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2477
+ value: onSelectGenre ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
2478
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
2479
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2508
2480
  ] }, genrePlaylist.uuid)) }) : song.genre.name
2509
2481
  });
2510
2482
  } else {
2511
- fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2483
+ fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2512
2484
  }
2513
2485
  if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
2514
2486
  const viewCount = song.youtubeVideo?.viewCount;
@@ -2518,11 +2490,11 @@ function SongPanelContent(props) {
2518
2490
  const links = [];
2519
2491
  if (musicBrainzUrl)
2520
2492
  links.push(
2521
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2493
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2522
2494
  );
2523
2495
  if (song.youtubeVideo)
2524
2496
  links.push(
2525
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2497
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2526
2498
  "a",
2527
2499
  {
2528
2500
  href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
@@ -2537,31 +2509,31 @@ function SongPanelContent(props) {
2537
2509
  if (links.length > 0)
2538
2510
  fields.push({
2539
2511
  label: "Links",
2540
- value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex gap-3", children: links })
2512
+ value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "flex gap-3", children: links })
2541
2513
  });
2542
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2543
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_ui18.ActionBand, { children: [
2544
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2514
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2515
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_ui18.ActionBand, { children: [
2516
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2545
2517
  import_ui18.ActionButton,
2546
2518
  {
2547
- icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react13.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react13.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2519
+ icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react12.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2548
2520
  label: isPlaying ? "Pause" : "Play",
2549
2521
  disabled: !!unplayableReason,
2550
2522
  title: unplayableReason ?? void 0,
2551
2523
  onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
2552
2524
  }
2553
2525
  ),
2554
- shareUrl && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2526
+ shareUrl && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2555
2527
  ] }) }),
2556
- song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2557
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.FieldList, { items: fields }),
2558
- song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2559
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2528
+ song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2529
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.FieldList, { items: fields }),
2530
+ song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2531
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2560
2532
  ] });
2561
2533
  }
2562
2534
 
2563
2535
  // src/genre-tree/song/useSongPanel.tsx
2564
- var import_jsx_runtime42 = require("react/jsx-runtime");
2536
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2565
2537
  function useSongPanel({
2566
2538
  uuid,
2567
2539
  scope,
@@ -2582,24 +2554,24 @@ function useSongPanel({
2582
2554
  return {
2583
2555
  ...panel,
2584
2556
  title: "Song",
2585
- content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2586
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2587
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-4 w-32" }),
2588
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2589
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-4 w-24" })
2557
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2558
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2559
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-4 w-32" }),
2560
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2561
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-4 w-24" })
2590
2562
  ] })
2591
2563
  };
2592
2564
  case "not-found":
2593
2565
  return {
2594
2566
  ...panel,
2595
2567
  title: "Song not found",
2596
- content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2568
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2597
2569
  };
2598
2570
  case "error":
2599
2571
  return {
2600
2572
  ...panel,
2601
2573
  title: "Song",
2602
- content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2574
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2603
2575
  };
2604
2576
  case "success": {
2605
2577
  const { song } = result;
@@ -2607,7 +2579,7 @@ function useSongPanel({
2607
2579
  return {
2608
2580
  ...panel,
2609
2581
  title: song.title,
2610
- content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2582
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2611
2583
  SongPanelContent,
2612
2584
  {
2613
2585
  song,
@@ -2660,12 +2632,12 @@ function useEntityPanel({
2660
2632
  // src/genre-tree/SizeMetricToggle.tsx
2661
2633
  var import_md = require("react-icons/md");
2662
2634
  var import_ui20 = require("@behindthemusictree/ui");
2663
- var import_jsx_runtime43 = require("react/jsx-runtime");
2635
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2664
2636
  function SizeMetricToggle({
2665
2637
  value,
2666
2638
  onChange
2667
2639
  }) {
2668
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2640
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2669
2641
  import_ui20.SegmentedControl,
2670
2642
  {
2671
2643
  variant: "floating",
@@ -2673,15 +2645,15 @@ function SizeMetricToggle({
2673
2645
  value,
2674
2646
  onChange,
2675
2647
  options: [
2676
- { value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
2677
- { value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
2648
+ { value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
2649
+ { value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
2678
2650
  ]
2679
2651
  }
2680
2652
  );
2681
2653
  }
2682
2654
 
2683
2655
  // src/genre-tree/GenreTreeView.tsx
2684
- var import_jsx_runtime44 = require("react/jsx-runtime");
2656
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2685
2657
  var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
2686
2658
  var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
2687
2659
  var noop = () => {
@@ -2773,6 +2745,7 @@ function GenreTreeView({
2773
2745
  isFetching: isTreeFetching,
2774
2746
  genrePlaylistByNodeId,
2775
2747
  visibleGenrePlaylists,
2748
+ focusUuids,
2776
2749
  groupedGenrePlaylistsByRoot,
2777
2750
  selectedGenreUuid,
2778
2751
  selectGenre,
@@ -2832,14 +2805,14 @@ function GenreTreeView({
2832
2805
  const openSongRef = (0, import_react19.useRef)(openSong);
2833
2806
  openSongRef.current = openSong;
2834
2807
  const resolvedRenderSongLink = (0, import_react19.useCallback)(
2835
- (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
2808
+ (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
2836
2809
  [renderSongLink]
2837
2810
  );
2838
2811
  const [expandedSongUuid, setExpandedSongUuid] = (0, import_react19.useState)(null);
2839
2812
  const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react19.useState)(null);
2840
2813
  const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
2841
2814
  const songBaseUrl = getBackendBaseUrl;
2842
- const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
2815
+ const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
2843
2816
  const getGenrePath = (genre) => {
2844
2817
  const path = [];
2845
2818
  let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
@@ -2881,7 +2854,7 @@ function GenreTreeView({
2881
2854
  selectGenrePlaylist(genrePlaylist2);
2882
2855
  }
2883
2856
  });
2884
- const builtInPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : entityPanel;
2857
+ const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
2885
2858
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react19.useState)(false);
2886
2859
  const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react19.useState)(false);
2887
2860
  const isCollapsed = collapsed ?? uncontrolledCollapsed;
@@ -2909,18 +2882,19 @@ function GenreTreeView({
2909
2882
  const obscuredInset = (0, import_ui21.useObscuredInset)(panelRef);
2910
2883
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
2911
2884
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
2912
- const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
2885
+ const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
2913
2886
  const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
2914
2887
  treeNodes,
2915
2888
  !overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
2916
2889
  resolveStyle
2917
2890
  );
2891
+ const treeStyles = (0, import_react19.useMemo)(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
2918
2892
  const [selectionHistory, setSelectionHistory] = (0, import_react19.useState)({ current: selectedGenreUuid, previous: null });
2919
2893
  if (selectionHistory.current !== selectedGenreUuid) {
2920
2894
  setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
2921
2895
  }
2922
2896
  const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
2923
- const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
2897
+ const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
2924
2898
  const reopenPanel = () => {
2925
2899
  setUncontrolledCollapsed(false);
2926
2900
  onCollapsedChange?.(false);
@@ -2960,7 +2934,7 @@ function GenreTreeView({
2960
2934
  selectGenre(uuid);
2961
2935
  },
2962
2936
  expanded: isExpanded,
2963
- treeRelations: relations,
2937
+ treeStyles,
2964
2938
  playback,
2965
2939
  genrePlaylist: selectedGenrePlaylist,
2966
2940
  band: {
@@ -2971,7 +2945,7 @@ function GenreTreeView({
2971
2945
  onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
2972
2946
  onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
2973
2947
  onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
2974
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2948
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2975
2949
  ChangeParentPopup,
2976
2950
  {
2977
2951
  genrePlaylist: { ...genrePlaylist, criteria },
@@ -3005,15 +2979,15 @@ function GenreTreeView({
3005
2979
  const isRotationControlled = controlledAllowWheelRotation !== void 0;
3006
2980
  const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
3007
2981
  let activePanel = null;
3008
- if (overlayPanel) {
2982
+ if (otherPanel) {
3009
2983
  activePanel = {
3010
- title: overlayPanel.title,
3011
- content: overlayPanel.content,
3012
- aside: overlayPanel.extraContent,
3013
- expanded: overlayPanel.expanded ?? false,
3014
- onExpandedChange: overlayPanel.onExpandedChange,
3015
- onClose: overlayPanel.onClose,
3016
- titleInSearch: false
2984
+ title: otherPanel.title,
2985
+ content: otherPanel.content,
2986
+ aside: otherPanel.extraContent,
2987
+ expanded: otherPanel.expanded ?? false,
2988
+ onExpandedChange: otherPanel.onExpandedChange,
2989
+ onClose: otherPanel.onClose,
2990
+ searchName: otherPanel.title
3017
2991
  };
3018
2992
  } else if (selectedGenreUuid) {
3019
2993
  activePanel = {
@@ -3023,38 +2997,28 @@ function GenreTreeView({
3023
2997
  expanded: isExpanded,
3024
2998
  onExpandedChange: changeExpanded,
3025
2999
  onClose: clearSelection,
3026
- titleInSearch: true,
3000
+ searchName: genreContent.name,
3027
3001
  headerAccent: genreContent.headerAccent
3028
3002
  };
3029
- } else if (builtInPanel) {
3030
- activePanel = {
3031
- title: builtInPanel.title,
3032
- content: builtInPanel.content,
3033
- aside: builtInPanel.extraContent,
3034
- expanded: builtInPanel.expanded,
3035
- onExpandedChange: builtInPanel.onExpandedChange,
3036
- onClose: builtInPanel.onClose,
3037
- titleInSearch: true
3038
- };
3039
3003
  }
3040
3004
  const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
3041
3005
  const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
3042
3006
  edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
3043
3007
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
3044
3008
  } : void 0;
3045
- const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3009
+ const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3046
3010
  import_ui21.IconButton,
3047
3011
  {
3048
3012
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
3049
3013
  style: activePanel.headerAccent && { color: "inherit" },
3050
3014
  onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
3051
- children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react15.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react15.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
3015
+ children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react14.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react14.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
3052
3016
  }
3053
3017
  ) : null;
3054
3018
  const renderArtistFilter = () => {
3055
3019
  if (isLoading || !filterArtist) return null;
3056
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
3057
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3020
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
3021
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3058
3022
  "button",
3059
3023
  {
3060
3024
  type: "button",
@@ -3066,7 +3030,7 @@ function GenreTreeView({
3066
3030
  children: filterArtist.name
3067
3031
  }
3068
3032
  ),
3069
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3033
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3070
3034
  "button",
3071
3035
  {
3072
3036
  type: "button",
@@ -3078,8 +3042,8 @@ function GenreTreeView({
3078
3042
  )
3079
3043
  ] });
3080
3044
  };
3081
- const actions = /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
3082
- !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3045
+ const actions = /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
3046
+ !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3083
3047
  GenreTreeSearch,
3084
3048
  {
3085
3049
  artistBaseUrl,
@@ -3102,9 +3066,9 @@ function GenreTreeView({
3102
3066
  if (songUuid) changeSong(null);
3103
3067
  selectGenre(uuid);
3104
3068
  },
3105
- selectedName: selectedGenreUuid ? genreContent.name : typeof builtInPanel?.title === "string" ? builtInPanel.title : null,
3069
+ selectedName: activePanel?.searchName ?? null,
3106
3070
  onClear: activePanel?.onClose,
3107
- showClear: activePanel?.titleInSearch ?? false,
3071
+ showClear: activePanel !== null,
3108
3072
  clearLabel: "Close panel",
3109
3073
  onPrevious: handlePrevious,
3110
3074
  onQueryChange: onSearchQueryChange,
@@ -3112,9 +3076,9 @@ function GenreTreeView({
3112
3076
  extraGroups: extraSearchGroups
3113
3077
  }
3114
3078
  ),
3115
- !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3116
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
3117
- !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3079
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3080
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
3081
+ !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3118
3082
  import_ui21.SegmentedControl,
3119
3083
  {
3120
3084
  "aria-label": "Tree view mode",
@@ -3128,13 +3092,13 @@ function GenreTreeView({
3128
3092
  ]
3129
3093
  }
3130
3094
  ),
3131
- !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3095
+ !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3132
3096
  "div",
3133
3097
  {
3134
3098
  className: "flex items-center gap-1",
3135
3099
  role: "group",
3136
3100
  "aria-label": "Tree display options",
3137
- children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3101
+ children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3138
3102
  import_ui21.Button,
3139
3103
  {
3140
3104
  variant: allowWheelRotation ? "default" : "outline",
@@ -3145,52 +3109,53 @@ function GenreTreeView({
3145
3109
  )
3146
3110
  }
3147
3111
  ),
3148
- !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3112
+ !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3149
3113
  import_ui21.IconTextButton,
3150
3114
  {
3151
- icon: import_lucide_react14.Plus,
3115
+ icon: import_lucide_react13.Plus,
3152
3116
  text: "Add root",
3153
3117
  onClick: () => handleGenreCreationAction(null)
3154
3118
  }
3155
3119
  )
3156
3120
  ] })
3157
3121
  ] });
3158
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
3122
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3159
3123
  "div",
3160
3124
  {
3161
3125
  "data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
3162
3126
  className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
3163
3127
  children: [
3164
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3128
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3165
3129
  "div",
3166
3130
  {
3167
3131
  className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] md:left-[var(--layout-panel-inset)] md:top-[var(--layout-panel-inset)] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
3168
3132
  children: actions
3169
3133
  }
3170
3134
  ),
3171
- !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3135
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3172
3136
  "div",
3173
3137
  {
3174
3138
  className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] md:top-[var(--layout-panel-inset)]" : "top-5"}`,
3175
3139
  children: renderArtistFilter()
3176
3140
  }
3177
3141
  ),
3178
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
3179
- !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
3180
- isLoading ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
3142
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
3143
+ !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
3144
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
3181
3145
  "Couldn't load the genre tree.",
3182
3146
  " ",
3183
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3147
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3184
3148
  ] }) : viewMode === "wheel" ? (
3185
3149
  // The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
3186
3150
  // and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
3187
3151
  // Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
3188
3152
  // The panel's own Zoom in button is hidden: the search bar carries it.
3189
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3153
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3190
3154
  GenrePlaylistTreeWheel,
3191
3155
  {
3192
3156
  scope,
3193
3157
  genrePlaylists: visibleGenrePlaylists,
3158
+ focusUuids,
3194
3159
  handleGenreCreationAction,
3195
3160
  handleGenreRenameAction,
3196
3161
  additionalActions,
@@ -3203,9 +3168,9 @@ function GenreTreeView({
3203
3168
  allowWheelRotation,
3204
3169
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3205
3170
  busy,
3206
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
3171
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
3207
3172
  floatingControlsLeading,
3208
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3173
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3209
3174
  ] }) : void 0,
3210
3175
  defaultViewport,
3211
3176
  viewportRestore,
@@ -3214,11 +3179,12 @@ function GenreTreeView({
3214
3179
  ) }) })
3215
3180
  ) : viewMode === "pop-core" ? (
3216
3181
  // Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
3217
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3182
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3218
3183
  GenrePlaylistTreeWheelRadialPopCore,
3219
3184
  {
3220
3185
  scope,
3221
3186
  genrePlaylists: visibleGenrePlaylists,
3187
+ focusUuids,
3222
3188
  handleGenreCreationAction,
3223
3189
  handleGenreRenameAction,
3224
3190
  additionalActions,
@@ -3231,9 +3197,9 @@ function GenreTreeView({
3231
3197
  allowWheelRotation,
3232
3198
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3233
3199
  busy,
3234
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
3200
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
3235
3201
  floatingControlsLeading,
3236
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3202
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3237
3203
  ] }) : void 0,
3238
3204
  defaultViewport,
3239
3205
  viewportRestore,
@@ -3241,12 +3207,13 @@ function GenreTreeView({
3241
3207
  ...focus
3242
3208
  }
3243
3209
  ) }) })
3244
- ) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3210
+ ) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3245
3211
  GenrePlaylistTreeWheelRadialPopCore,
3246
3212
  {
3247
3213
  outline: true,
3248
3214
  scope,
3249
3215
  genrePlaylists: visibleGenrePlaylists,
3216
+ focusUuids,
3250
3217
  handleGenreCreationAction,
3251
3218
  handleGenreRenameAction,
3252
3219
  additionalActions,
@@ -3257,13 +3224,13 @@ function GenreTreeView({
3257
3224
  selectedNodeId,
3258
3225
  readOnly
3259
3226
  }
3260
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
3227
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
3261
3228
  ([uuid, genrePlaylistTreePerRoot]) => {
3262
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3229
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3263
3230
  "div",
3264
3231
  {
3265
3232
  className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
3266
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3233
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3267
3234
  GenrePlaylistTreePerRoot,
3268
3235
  {
3269
3236
  scope,
@@ -3276,6 +3243,7 @@ function GenreTreeView({
3276
3243
  onNodeHover: handleNodeHover,
3277
3244
  ...hostPanel,
3278
3245
  playback,
3246
+ focusUuids,
3279
3247
  selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
3280
3248
  readOnly,
3281
3249
  wheelZoom: "modifier",
@@ -3289,13 +3257,13 @@ function GenreTreeView({
3289
3257
  );
3290
3258
  }
3291
3259
  ) }),
3292
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3260
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3293
3261
  import_ui21.DetailPanel,
3294
3262
  {
3295
3263
  ref: panelRef,
3296
3264
  side: "left",
3297
3265
  title: activePanel?.title ?? "",
3298
- titleInSearch: activePanel?.titleInSearch,
3266
+ titleInSearch: !isLoading && !isError,
3299
3267
  headerAccent: activePanel?.headerAccent,
3300
3268
  headerActions: expandButton,
3301
3269
  onClose: activePanel?.onClose ?? noop,
@@ -3304,8 +3272,7 @@ function GenreTreeView({
3304
3272
  onExpandedChange: activePanel?.onExpandedChange,
3305
3273
  aside: activePanel?.aside,
3306
3274
  edgeTab: detailTab,
3307
- headerClassName: activePanel?.titleInSearch ? void 0 : "pt-search-clear",
3308
- children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
3275
+ children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
3309
3276
  activePanel.content,
3310
3277
  activePanel.aside
3311
3278
  ] }) : activePanel?.content
@@ -3322,11 +3289,11 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
3322
3289
 
3323
3290
  // src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
3324
3291
  var import_react20 = require("react");
3325
- var import_lucide_react16 = require("lucide-react");
3292
+ var import_lucide_react15 = require("lucide-react");
3326
3293
  var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
3327
3294
  var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
3328
3295
  var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
3329
- var import_jsx_runtime45 = require("react/jsx-runtime");
3296
+ var import_jsx_runtime46 = require("react/jsx-runtime");
3330
3297
  var useBrowserLayoutEffect = typeof window === "undefined" ? import_react20.useEffect : import_react20.useLayoutEffect;
3331
3298
  function TrackListSidebar({
3332
3299
  className: className2 = "",
@@ -3428,25 +3395,25 @@ function TrackListSidebar({
3428
3395
  }
3429
3396
  };
3430
3397
  const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
3431
- return trackList ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3398
+ return trackList ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3432
3399
  "div",
3433
3400
  {
3434
3401
  "data-theme": "dark",
3435
3402
  className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
3436
3403
  style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
3437
3404
  children: [
3438
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3439
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3440
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "origin flex text-xl", children: [
3441
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3442
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3405
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3406
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3407
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "origin flex text-xl", children: [
3408
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3409
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3443
3410
  ] }),
3444
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3411
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3445
3412
  trackList.origin.type === import_genre_tree32.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree32.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
3446
3413
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3447
3414
  ] })
3448
3415
  ] }),
3449
- (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3416
+ (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3450
3417
  "button",
3451
3418
  {
3452
3419
  type: "button",
@@ -3455,11 +3422,11 @@ function TrackListSidebar({
3455
3422
  "aria-expanded": !isCollapsed,
3456
3423
  className: `ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-md hover:bg-[var(--color-surface-muted)] ${isCollapsed ? "text-[var(--color-text-muted)]" : "text-white"}`,
3457
3424
  onClick: isCollapsed ? onExpand : onMinimize,
3458
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react16.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3425
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react15.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3459
3426
  }
3460
3427
  )
3461
3428
  ] }),
3462
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3429
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3463
3430
  "ul",
3464
3431
  {
3465
3432
  ref: trackListRef,
@@ -3469,7 +3436,7 @@ function TrackListSidebar({
3469
3436
  onPointerDown: markUserScrollIntent,
3470
3437
  className: layout === "inline" ? "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0" : "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0",
3471
3438
  children: [
3472
- trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3439
+ trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3473
3440
  TrackItem,
3474
3441
  {
3475
3442
  track,
@@ -3480,7 +3447,7 @@ function TrackListSidebar({
3480
3447
  renderSongLink
3481
3448
  }
3482
3449
  ) }, track.uuid)),
3483
- hasMore && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3450
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3484
3451
  ]
3485
3452
  }
3486
3453
  )
@@ -3497,10 +3464,10 @@ var import_genre_tree34 = require("@behindthemusictree/app-kit/genre-tree");
3497
3464
  var import_ui22 = require("@behindthemusictree/ui");
3498
3465
 
3499
3466
  // src/genre-tree/PlayerControls.tsx
3500
- var import_lucide_react17 = require("lucide-react");
3467
+ var import_lucide_react16 = require("lucide-react");
3501
3468
  var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
3502
3469
  var import_player13 = require("@behindthemusictree/app-kit/player");
3503
- var import_jsx_runtime46 = require("react/jsx-runtime");
3470
+ var import_jsx_runtime47 = require("react/jsx-runtime");
3504
3471
  function PlayerControls() {
3505
3472
  const {
3506
3473
  duration,
@@ -3519,15 +3486,15 @@ function PlayerControls() {
3519
3486
  const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
3520
3487
  const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
3521
3488
  const isPlaying = playState === import_player13.PlayStates.PLAYING;
3522
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3489
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
3523
3490
  "section",
3524
3491
  {
3525
3492
  "aria-label": "Player controls",
3526
3493
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3527
3494
  children: [
3528
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3529
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree33.formatTime)(safeCurrentTime) }),
3530
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3495
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3496
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree33.formatTime)(safeCurrentTime) }),
3497
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3531
3498
  "input",
3532
3499
  {
3533
3500
  "aria-label": "Seek",
@@ -3541,11 +3508,11 @@ function PlayerControls() {
3541
3508
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3542
3509
  }
3543
3510
  ),
3544
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3511
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3545
3512
  ] }),
3546
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3547
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3548
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3513
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3514
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3515
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3549
3516
  "button",
3550
3517
  {
3551
3518
  type: "button",
@@ -3554,10 +3521,10 @@ function PlayerControls() {
3554
3521
  disabled: !isReady || !hasPreviousTrack,
3555
3522
  onClick: playPreviousTrack,
3556
3523
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
3557
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3524
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react16.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3558
3525
  }
3559
3526
  ),
3560
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3527
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3561
3528
  "button",
3562
3529
  {
3563
3530
  type: "button",
@@ -3566,10 +3533,10 @@ function PlayerControls() {
3566
3533
  disabled: !isReady,
3567
3534
  onClick: handlePlayPauseAction,
3568
3535
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-white text-zinc-950 hover:bg-zinc-200 disabled:cursor-not-allowed disabled:opacity-40",
3569
- children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3536
+ children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react16.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react16.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3570
3537
  }
3571
3538
  ),
3572
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3539
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3573
3540
  "button",
3574
3541
  {
3575
3542
  type: "button",
@@ -3578,14 +3545,14 @@ function PlayerControls() {
3578
3545
  disabled: !isReady || !hasNextTrack,
3579
3546
  onClick: playNextTrack,
3580
3547
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
3581
- children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3548
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react16.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3582
3549
  }
3583
3550
  )
3584
3551
  ] }),
3585
- /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
3586
- volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3587
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "sr-only", children: "Volume" }),
3588
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3552
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
3553
+ volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react16.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react16.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3554
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sr-only", children: "Volume" }),
3555
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3589
3556
  "input",
3590
3557
  {
3591
3558
  "aria-label": "Volume",
@@ -3607,7 +3574,7 @@ function PlayerControls() {
3607
3574
  }
3608
3575
 
3609
3576
  // src/genre-tree/PlayerPanel.tsx
3610
- var import_jsx_runtime47 = require("react/jsx-runtime");
3577
+ var import_jsx_runtime48 = require("react/jsx-runtime");
3611
3578
  var PANEL_ID = "player-panel";
3612
3579
  var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
3613
3580
  function PlayerPanel({
@@ -3647,7 +3614,7 @@ function PlayerPanel({
3647
3614
  onClick: () => setIsExtendRequested(true),
3648
3615
  expanded: false
3649
3616
  } : null;
3650
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
3617
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3651
3618
  "div",
3652
3619
  {
3653
3620
  id: PANEL_ID,
@@ -3660,7 +3627,7 @@ function PlayerPanel({
3660
3627
  ),
3661
3628
  style: isExtended ? { width: extendedWidth } : void 0,
3662
3629
  children: [
3663
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3630
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3664
3631
  import_ui22.PanelEdgeTab,
3665
3632
  {
3666
3633
  side: "right",
@@ -3670,7 +3637,7 @@ function PlayerPanel({
3670
3637
  className: "absolute right-full top-1/2 -translate-y-1/2"
3671
3638
  }
3672
3639
  ),
3673
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
3640
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3674
3641
  "div",
3675
3642
  {
3676
3643
  className: (0, import_tailwind_merge.twMerge)(
@@ -3678,20 +3645,20 @@ function PlayerPanel({
3678
3645
  isExtended ? "flex-row" : "flex-col"
3679
3646
  ),
3680
3647
  children: [
3681
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3682
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3683
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "relative w-full shrink-0", children: [
3684
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3685
- playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3648
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3649
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3650
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "relative w-full shrink-0", children: [
3651
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3652
+ playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3686
3653
  playerTrackObject.loadError,
3687
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3654
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3688
3655
  void retryPlayback();
3689
3656
  }, children: "Retry" })
3690
3657
  ] })
3691
3658
  ] }),
3692
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(PlayerControls, {})
3659
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PlayerControls, {})
3693
3660
  ] }),
3694
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3661
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3695
3662
  TrackListSidebar,
3696
3663
  {
3697
3664
  layout: "inline",
@@ -3718,11 +3685,11 @@ function PlayerPanel({
3718
3685
  var import_uuid = require("uuid");
3719
3686
  var import_ai = require("react-icons/ai");
3720
3687
  var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
3721
- var import_jsx_runtime48 = require("react/jsx-runtime");
3688
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3722
3689
  function Rating({ rating, handleChange }) {
3723
3690
  const instanceId = (0, import_uuid.v4)();
3724
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
3725
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3691
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
3692
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3726
3693
  "input",
3727
3694
  {
3728
3695
  id: `star-${instanceId}-${i}`,
@@ -3734,16 +3701,16 @@ function Rating({ rating, handleChange }) {
3734
3701
  onChange: handleChange
3735
3702
  }
3736
3703
  ),
3737
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex", children: [
3738
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3704
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex", children: [
3705
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3739
3706
  import_ai.AiOutlineStar,
3740
3707
  {
3741
3708
  className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree35.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
3742
3709
  }
3743
3710
  ),
3744
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "flex-grow w-8" }),
3711
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-grow w-8" }),
3745
3712
  " "
3746
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3713
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3747
3714
  import_ai.AiFillStar,
3748
3715
  {
3749
3716
  className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
@@ -3755,7 +3722,7 @@ function Rating({ rating, handleChange }) {
3755
3722
  // src/genre-tree/EntityDetailPanel.tsx
3756
3723
  var import_react22 = require("react");
3757
3724
  var import_ui23 = require("@behindthemusictree/ui");
3758
- var import_jsx_runtime49 = require("react/jsx-runtime");
3725
+ var import_jsx_runtime50 = require("react/jsx-runtime");
3759
3726
  function EntityDetailPanel({
3760
3727
  scope,
3761
3728
  getBackendBaseUrl,
@@ -3778,8 +3745,8 @@ function EntityDetailPanel({
3778
3745
  getBackendBaseUrl,
3779
3746
  getArtistBackendBaseUrl,
3780
3747
  getShareUrl,
3781
- renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
3782
- renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
3748
+ renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
3749
+ renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
3783
3750
  song: {
3784
3751
  uuid: songUuid,
3785
3752
  expanded: songUuid !== null && expandedSongUuid === songUuid,
@@ -3795,7 +3762,7 @@ function EntityDetailPanel({
3795
3762
  ...navigation
3796
3763
  });
3797
3764
  if (!panel) return null;
3798
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3765
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3799
3766
  import_ui23.DetailPanel,
3800
3767
  {
3801
3768
  side: "left",
@@ -3807,7 +3774,7 @@ function EntityDetailPanel({
3807
3774
  onExpandedChange: panel.onExpandedChange,
3808
3775
  aside: panel.extraContent,
3809
3776
  edgeTab,
3810
- children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
3777
+ children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
3811
3778
  panel.content,
3812
3779
  panel.extraContent
3813
3780
  ] }) : panel.content