@behindthemusictree/app-kit-ui 39.0.0 → 40.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,7 +55,7 @@ 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,7 +80,8 @@ 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
 
@@ -89,7 +90,12 @@ var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
89
90
  var import_react3 = require("react");
90
91
  var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
91
92
  var import_player = require("@behindthemusictree/app-kit/player");
93
+
94
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
95
+ var import_react2 = require("react");
96
+ var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
92
97
  var import_popup = require("@behindthemusictree/app-kit/popup");
98
+ var import_transport = require("@behindthemusictree/app-kit/transport");
93
99
 
94
100
  // src/popup/InternalErrorPopup.tsx
95
101
  var import_lucide_react2 = require("lucide-react");
@@ -305,18 +311,15 @@ function InternalErrorPopup({ errorCode, contactEmail, ...rest }) {
305
311
  );
306
312
  }
307
313
 
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");
314
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
315
+ var import_jsx_runtime5 = require("react/jsx-runtime");
314
316
  function useGenrePlaylistPlayback({
315
317
  scope,
316
318
  genrePlaylists,
317
319
  artist = null
318
320
  }) {
319
321
  const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
322
+ const { showPopup } = (0, import_popup.usePopup)();
320
323
  const origin = trackList?.origin;
321
324
  const playing = (0, import_react2.useMemo)(() => {
322
325
  if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
@@ -328,14 +331,20 @@ function useGenrePlaylistPlayback({
328
331
  }, [origin, artist, genrePlaylists, scope]);
329
332
  const play = (0, import_react2.useCallback)(async (playlist, requested) => {
330
333
  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]);
334
+ try {
335
+ const selection = scope === "reference" && playlist.criteria !== null ? requested ?? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
336
+ await (selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope));
337
+ } catch (error) {
338
+ console.error("Failed to load genre playlist tracks:", error);
339
+ if (error instanceof import_genre_tree2.TracksFailureAlreadyReportedError) return;
340
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport.AppError ? error.code : import_transport.ErrorCode.CLIENT_UNKNOWN }));
341
+ }
342
+ }, [artist, scope, playNewTrackListFromGenrePlaylist, showPopup]);
334
343
  return (0, import_react2.useMemo)(() => ({ playing, play }), [playing, play]);
335
344
  }
336
345
 
337
346
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
338
- var import_jsx_runtime5 = require("react/jsx-runtime");
347
+ var import_jsx_runtime6 = require("react/jsx-runtime");
339
348
  function GenrePlaylistTreePerRoot({
340
349
  scope,
341
350
  className: className2,
@@ -354,25 +363,16 @@ function GenrePlaylistTreePerRoot({
354
363
  viewportRestore,
355
364
  onViewportChange,
356
365
  playback,
366
+ focusUuids,
357
367
  ...hostPanel
358
368
  }) {
359
369
  const { isPlaying, setIsPlaying } = (0, import_player.usePlayer)();
360
370
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
361
371
  const { playing, play } = playback ?? defaultPlayback;
362
372
  const playingNodeId = playing?.uuid ?? null;
363
- const { showPopup } = (0, import_popup.usePopup)();
364
373
  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]
374
+ () => toTreeNodes(genrePlaylistTreePerRoot, "songs", focusUuids),
375
+ [genrePlaylistTreePerRoot, focusUuids]
376
376
  );
377
377
  const handlePlayPause = (0, import_react3.useCallback)(
378
378
  (nodeId) => {
@@ -385,18 +385,14 @@ function GenrePlaylistTreePerRoot({
385
385
  if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
386
386
  return;
387
387
  }
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
- });
388
+ void play(genrePlaylist);
392
389
  },
393
390
  [
394
391
  genrePlaylistTreePerRoot,
395
392
  playingNodeId,
396
393
  isPlaying,
397
394
  setIsPlaying,
398
- play,
399
- showPopup
395
+ play
400
396
  ]
401
397
  );
402
398
  const handleAddChild = (0, import_react3.useCallback)(
@@ -419,7 +415,7 @@ function GenrePlaylistTreePerRoot({
419
415
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
420
416
  }
421
417
  }, []);
422
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
418
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
423
419
  import_genre_tree_view.GenreTree,
424
420
  {
425
421
  className: className2,
@@ -450,9 +446,7 @@ var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
450
446
  var import_react4 = require("react");
451
447
  var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
452
448
  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");
449
+ var import_jsx_runtime7 = require("react/jsx-runtime");
456
450
  function GenrePlaylistTreeWheel({
457
451
  scope,
458
452
  className: className2,
@@ -473,24 +467,16 @@ function GenrePlaylistTreeWheel({
473
467
  viewportRestore,
474
468
  onViewportChange,
475
469
  playback,
470
+ focusUuids,
476
471
  ...hostPanel
477
472
  }) {
478
473
  const { isPlaying, setIsPlaying } = (0, import_player2.usePlayer)();
479
474
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
480
475
  const { playing, play } = playback ?? defaultPlayback;
481
476
  const playingNodeId = playing?.uuid ?? null;
482
- const { showPopup } = (0, import_popup2.usePopup)();
483
477
  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]
478
+ () => toTreeNodes(genrePlaylists, sizeMetric, focusUuids),
479
+ [genrePlaylists, sizeMetric, focusUuids]
494
480
  );
495
481
  const handlePlayPause = (0, import_react4.useCallback)(
496
482
  (nodeId) => {
@@ -503,18 +489,14 @@ function GenrePlaylistTreeWheel({
503
489
  if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
504
490
  return;
505
491
  }
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
- });
492
+ void play(genrePlaylist);
510
493
  },
511
494
  [
512
495
  genrePlaylists,
513
496
  playingNodeId,
514
497
  isPlaying,
515
498
  setIsPlaying,
516
- play,
517
- showPopup
499
+ play
518
500
  ]
519
501
  );
520
502
  const handleAddChild = (0, import_react4.useCallback)(
@@ -537,7 +519,7 @@ function GenrePlaylistTreeWheel({
537
519
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
538
520
  }
539
521
  }, []);
540
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
522
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
541
523
  import_genre_tree_view2.GenreTreeWheel,
542
524
  {
543
525
  className: className2,
@@ -569,7 +551,7 @@ var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
569
551
  var import_react5 = require("react");
570
552
  var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
571
553
  var import_player3 = require("@behindthemusictree/app-kit/player");
572
- var import_jsx_runtime7 = require("react/jsx-runtime");
554
+ var import_jsx_runtime8 = require("react/jsx-runtime");
573
555
  function GenrePlaylistTreeWheelRadialPopCore(props) {
574
556
  const {
575
557
  scope,
@@ -602,7 +584,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
602
584
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
603
585
  const { playing, play } = playback ?? defaultPlayback;
604
586
  const playingNodeId = playing?.uuid ?? null;
605
- const nodes = (0, import_react5.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
587
+ const nodes = (0, import_react5.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric, props.focusUuids), [genrePlaylists, sizeMetric, props.focusUuids]);
606
588
  const handlePlayPause = (0, import_react5.useCallback)(
607
589
  (nodeId) => {
608
590
  const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
@@ -614,9 +596,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
614
596
  if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
615
597
  return;
616
598
  }
617
- play(genrePlaylist).catch((error) => {
618
- console.error("Failed to load genre playlist tracks:", error);
619
- });
599
+ void play(genrePlaylist);
620
600
  },
621
601
  [genrePlaylists, playingNodeId, isPlaying, setIsPlaying, play]
622
602
  );
@@ -656,7 +636,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
656
636
  onSelectedNodeChange,
657
637
  ...hostPanel
658
638
  };
659
- return outline ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
639
+ return outline ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
660
640
  import_genre_tree_view3.GenreTreeWheelRadialPopCore,
661
641
  {
662
642
  ...treeProps,
@@ -673,7 +653,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
673
653
 
674
654
  // src/genre-tree/GenreTreeWheelHandoff.tsx
675
655
  var import_react6 = require("react");
676
- var import_jsx_runtime8 = require("react/jsx-runtime");
656
+ var import_jsx_runtime9 = require("react/jsx-runtime");
677
657
  function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
678
658
  const [revealed, setRevealed] = (0, import_react6.useState)(false);
679
659
  const onRevealedChangeRef = (0, import_react6.useRef)(onRevealedChange);
@@ -692,8 +672,8 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
692
672
  cancelAnimationFrame(innerFrame);
693
673
  };
694
674
  }, []);
695
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative w-full h-full", children: [
696
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
675
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative w-full h-full", children: [
676
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
697
677
  "div",
698
678
  {
699
679
  className: "absolute inset-0 transition-opacity duration-150 ease-out",
@@ -702,7 +682,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
702
682
  children
703
683
  }
704
684
  ),
705
- !revealed && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
685
+ !revealed && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
706
686
  ] });
707
687
  }
708
688
 
@@ -717,7 +697,7 @@ var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
717
697
  // src/genre-tree/search/SearchBox.tsx
718
698
  var import_react7 = require("react");
719
699
  var import_ui3 = require("@behindthemusictree/ui");
720
- var import_jsx_runtime9 = require("react/jsx-runtime");
700
+ var import_jsx_runtime10 = require("react/jsx-runtime");
721
701
  function SearchBox({
722
702
  label,
723
703
  placeholder,
@@ -739,7 +719,7 @@ function SearchBox({
739
719
  groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
740
720
  onPicked?.(groupKey, itemKey);
741
721
  };
742
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
722
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
743
723
  import_ui3.SearchCombobox,
744
724
  {
745
725
  ariaLabel: label,
@@ -771,7 +751,7 @@ var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS
771
751
 
772
752
  // src/genre-tree/search/useArtistSearchGroup.tsx
773
753
  var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
774
- var import_jsx_runtime10 = require("react/jsx-runtime");
754
+ var import_jsx_runtime11 = require("react/jsx-runtime");
775
755
  function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
776
756
  const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
777
757
  if (!enabled) return void 0;
@@ -780,15 +760,15 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
780
760
  label: "Artists",
781
761
  items: search.artists.map((artist) => ({
782
762
  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: [
763
+ content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
764
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "block truncate font-medium", children: [
785
765
  artist.name,
786
- artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
766
+ artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
787
767
  " \xB7 ",
788
768
  artist.disambiguation
789
769
  ] })
790
770
  ] }),
791
- artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
771
+ artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
792
772
  "also known as ",
793
773
  artist.matchedAlias
794
774
  ] })
@@ -802,7 +782,7 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
802
782
 
803
783
  // src/genre-tree/search/useSongSearchGroup.tsx
804
784
  var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
805
- var import_jsx_runtime11 = require("react/jsx-runtime");
785
+ var import_jsx_runtime12 = require("react/jsx-runtime");
806
786
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
807
787
  const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
808
788
  if (!enabled) return void 0;
@@ -811,9 +791,9 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
811
791
  label: "Songs",
812
792
  items: search.songs.map((song) => ({
813
793
  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 ") })
794
+ content: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
795
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block truncate font-medium", children: song.title }),
796
+ /* @__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
797
  ] }),
818
798
  onSelect: () => onSelect(song)
819
799
  })),
@@ -824,7 +804,7 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
824
804
 
825
805
  // src/genre-tree/search/useGenreRemoteSearchGroup.tsx
826
806
  var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
827
- var import_jsx_runtime12 = require("react/jsx-runtime");
807
+ var import_jsx_runtime13 = require("react/jsx-runtime");
828
808
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
829
809
  const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
830
810
  const genres = filter ? search.genres.filter(filter) : search.genres;
@@ -833,12 +813,12 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
833
813
  label,
834
814
  items: genres.map((genre) => ({
835
815
  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: [
816
+ content: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
817
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "block truncate font-medium", children: [
838
818
  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" })
819
+ 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
820
  ] }),
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}` })
821
+ /* @__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
822
  ] }),
843
823
  onSelect: () => onSelect(genre)
844
824
  })),
@@ -848,7 +828,7 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
848
828
  }
849
829
 
850
830
  // src/genre-tree/GenreSearch.tsx
851
- var import_jsx_runtime13 = require("react/jsx-runtime");
831
+ var import_jsx_runtime14 = require("react/jsx-runtime");
852
832
  function GenreSearch({
853
833
  genrePlaylists,
854
834
  onSelect,
@@ -877,9 +857,9 @@ function GenreSearch({
877
857
  label: hasExtras ? "Genres" : void 0,
878
858
  items: results.map((genrePlaylist) => ({
879
859
  key: genrePlaylist.uuid,
880
- content: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
860
+ content: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
881
861
  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" })
862
+ genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
883
863
  ] }),
884
864
  onSelect: () => select(genrePlaylist)
885
865
  }))
@@ -888,7 +868,7 @@ function GenreSearch({
888
868
  (group) => active ? group : { ...group, items: [], status: "", loading: false }
889
869
  )
890
870
  ];
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)(
871
+ 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
872
  SearchBox,
893
873
  {
894
874
  label,
@@ -908,14 +888,14 @@ function GenreSearch({
908
888
  clear();
909
889
  onQueryChange?.("");
910
890
  } : 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" }) }),
891
+ 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
892
  onFocus
913
893
  }
914
894
  ) });
915
895
  }
916
896
 
917
897
  // src/genre-tree/GenreTreeSearch.tsx
918
- var import_jsx_runtime14 = require("react/jsx-runtime");
898
+ var import_jsx_runtime15 = require("react/jsx-runtime");
919
899
  function GenreTreeSearch({
920
900
  artistBaseUrl,
921
901
  songBaseUrl,
@@ -935,7 +915,7 @@ function GenreTreeSearch({
935
915
  const deferredQuery = (0, import_react8.useDeferredValue)(query);
936
916
  const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
937
917
  const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
938
- const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
918
+ const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
939
919
  GenreSearch,
940
920
  {
941
921
  ...props,
@@ -950,7 +930,7 @@ function GenreTreeSearch({
950
930
  extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
951
931
  }
952
932
  );
953
- return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
933
+ return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
954
934
  }
955
935
 
956
936
  // src/genre-tree/GenreDetailPanel.tsx
@@ -970,7 +950,7 @@ var import_ui6 = require("@behindthemusictree/ui");
970
950
  // src/genre-tree/ShareActionButton.tsx
971
951
  var import_lucide_react6 = require("lucide-react");
972
952
  var import_ui5 = require("@behindthemusictree/ui");
973
- var import_popup3 = require("@behindthemusictree/app-kit/popup");
953
+ var import_popup2 = require("@behindthemusictree/app-kit/popup");
974
954
 
975
955
  // src/genre-tree/SharePopup.tsx
976
956
  var import_react10 = require("react");
@@ -981,7 +961,7 @@ var import_si = require("react-icons/si");
981
961
  // src/popup/Toast.tsx
982
962
  var import_react9 = require("react");
983
963
  var import_react_dom2 = require("react-dom");
984
- var import_jsx_runtime15 = require("react/jsx-runtime");
964
+ var import_jsx_runtime16 = require("react/jsx-runtime");
985
965
  function Toast({ message, onClose, durationMs = 2200 }) {
986
966
  const [mounted, setMounted] = (0, import_react9.useState)(false);
987
967
  (0, import_react9.useEffect)(() => {
@@ -991,7 +971,7 @@ function Toast({ message, onClose, durationMs = 2200 }) {
991
971
  }, [durationMs, onClose]);
992
972
  if (!mounted) return null;
993
973
  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)(
974
+ /* @__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
975
  "div",
996
976
  {
997
977
  role: "status",
@@ -1006,12 +986,12 @@ function Toast({ message, onClose, durationMs = 2200 }) {
1006
986
  }
1007
987
 
1008
988
  // src/genre-tree/ShareDestinationButton.tsx
1009
- var import_jsx_runtime16 = require("react/jsx-runtime");
989
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1010
990
  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
991
  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 })
992
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
993
+ /* @__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 }),
994
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "max-w-full truncate", children: label })
1015
995
  ] });
1016
996
  }
1017
997
  function ShareDestinationButton({
@@ -1023,29 +1003,29 @@ function ShareDestinationButton({
1023
1003
  ariaLabel
1024
1004
  }) {
1025
1005
  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 }) });
1006
+ 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
1007
  }
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 }) });
1008
+ 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
1009
  }
1030
1010
 
1031
1011
  // src/genre-tree/SharePreviewCard.tsx
1032
1012
  var import_lucide_react4 = require("lucide-react");
1033
- var import_jsx_runtime17 = require("react/jsx-runtime");
1013
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1034
1014
  function SharePreviewCard({ target, url }) {
1035
1015
  const Icon = { genre: import_lucide_react4.Music2, artist: import_lucide_react4.UserRound, song: import_lucide_react4.Music }[target.kind];
1036
1016
  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 })
1017
+ 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: [
1018
+ /* @__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" }) }),
1019
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
1020
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
1021
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
1022
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
1043
1023
  ] })
1044
1024
  ] }) });
1045
1025
  }
1046
1026
 
1047
1027
  // src/genre-tree/SharePopup.tsx
1048
- var import_jsx_runtime18 = require("react/jsx-runtime");
1028
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1049
1029
  function resolveAbsoluteUrl(url) {
1050
1030
  try {
1051
1031
  const parsed = new URL(url, window.location.href);
@@ -1120,8 +1100,8 @@ function SharePopup({ target, url, onClose }) {
1120
1100
  setShareError("Could not open the share options.");
1121
1101
  }
1122
1102
  };
1123
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1124
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1103
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
1104
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1125
1105
  BasePopup_default,
1126
1106
  {
1127
1107
  title: "Share",
@@ -1136,70 +1116,70 @@ function SharePopup({ target, url, onClose }) {
1136
1116
  titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
1137
1117
  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
1118
  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)(
1119
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col gap-4", children: [
1120
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
1121
+ /* @__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: [
1122
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1143
1123
  ShareDestinationButton,
1144
1124
  {
1145
1125
  label: "Copy Link",
1146
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react5.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1126
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react5.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1147
1127
  onClick: () => void handleCopy(),
1148
1128
  ariaLabel: "Copy link",
1149
1129
  disabled: !absoluteUrl
1150
1130
  }
1151
1131
  ),
1152
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1132
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1153
1133
  ShareDestinationButton,
1154
1134
  {
1155
1135
  label: "WhatsApp",
1156
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_fa.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1136
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_fa.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1157
1137
  href: destinationUrls?.whatsapp,
1158
1138
  ariaLabel: `Share ${target.name} via WhatsApp`,
1159
1139
  disabled: !absoluteUrl
1160
1140
  }
1161
1141
  ),
1162
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1142
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1163
1143
  ShareDestinationButton,
1164
1144
  {
1165
1145
  label: "Gmail",
1166
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1146
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1167
1147
  href: destinationUrls?.gmail,
1168
1148
  ariaLabel: `Email ${target.name} with Gmail`,
1169
1149
  disabled: !absoluteUrl
1170
1150
  }
1171
1151
  ),
1172
- canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1152
+ canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1173
1153
  ShareDestinationButton,
1174
1154
  {
1175
1155
  label: "More",
1176
- icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1156
+ icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1177
1157
  onClick: () => void handleNativeShare(),
1178
1158
  ariaLabel: "More sharing options",
1179
1159
  disabled: !absoluteUrl
1180
1160
  }
1181
1161
  )
1182
1162
  ] }),
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 })
1163
+ 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." }),
1164
+ shareError && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
1185
1165
  ] })
1186
1166
  }
1187
1167
  ),
1188
- toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1168
+ toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1189
1169
  ] });
1190
1170
  }
1191
1171
 
1192
1172
  // src/genre-tree/ShareActionButton.tsx
1193
- var import_jsx_runtime19 = require("react/jsx-runtime");
1173
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1194
1174
  function ShareActionButton({ target, url }) {
1195
- const { showPopup, hidePopup } = (0, import_popup3.usePopup)();
1175
+ const { showPopup, hidePopup } = (0, import_popup2.usePopup)();
1196
1176
  const openSharePopup = () => {
1197
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1177
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1198
1178
  };
1199
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1200
1180
  import_ui5.ActionButton,
1201
1181
  {
1202
- icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react6.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1182
+ icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react6.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1203
1183
  label: "Share",
1204
1184
  "aria-label": `Share ${target.name}`,
1205
1185
  title: `Share ${target.name}`,
@@ -1209,7 +1189,7 @@ function ShareActionButton({ target, url }) {
1209
1189
  }
1210
1190
 
1211
1191
  // src/genre-tree/GenreActionBand.tsx
1212
- var import_jsx_runtime20 = require("react/jsx-runtime");
1192
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1213
1193
  function GenreActionBand({
1214
1194
  node,
1215
1195
  playActions,
@@ -1230,24 +1210,24 @@ function GenreActionBand({
1230
1210
  items.push({
1231
1211
  key: "zoom-in",
1232
1212
  label: "Focus",
1233
- icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1213
+ icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1234
1214
  onClick: onZoomIn
1235
1215
  });
1236
1216
  }
1237
1217
  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 });
1218
+ 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
1219
  }
1240
1220
  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 });
1221
+ 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
1222
  }
1243
1223
  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 });
1224
+ 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
1225
  }
1246
1226
  if (onToggleSide) {
1247
1227
  adminItems.push({
1248
1228
  key: "toggle-side",
1249
1229
  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" }),
1230
+ icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1251
1231
  onClick: onToggleSide,
1252
1232
  disabled: !!toggleSideDisabledReason,
1253
1233
  title: toggleSideDisabledReason
@@ -1262,9 +1242,9 @@ function GenreActionBand({
1262
1242
  disabled: action.enabled ? !action.enabled(node) : false
1263
1243
  });
1264
1244
  }
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));
1245
+ 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
1246
  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)(
1247
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_ui6.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1268
1248
  import_ui6.ActionBand,
1269
1249
  {
1270
1250
  className: "genre-action-band",
@@ -1272,7 +1252,7 @@ function GenreActionBand({
1272
1252
  children: [
1273
1253
  playActions,
1274
1254
  renderItems(items),
1275
- shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1255
+ shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1276
1256
  ]
1277
1257
  }
1278
1258
  ) });
@@ -1296,7 +1276,7 @@ var REGION_GLOBE_PATHS = {
1296
1276
  var REGION_GLOBES = ["africa", "americas", "asia"];
1297
1277
 
1298
1278
  // src/genre-tree/RegionGlobeIcon.tsx
1299
- var import_jsx_runtime21 = require("react/jsx-runtime");
1279
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1300
1280
  var ROTATION_INTERVAL_MS = 3e3;
1301
1281
  function RegionGlobeIcon({ className: className2 }) {
1302
1282
  const [index, setIndex] = (0, import_react11.useState)(0);
@@ -1314,29 +1294,29 @@ function RegionGlobeIcon({ className: className2 }) {
1314
1294
  clearInterval(timer);
1315
1295
  };
1316
1296
  }, []);
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]] }) });
1297
+ 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
1298
  }
1319
1299
 
1320
1300
  // src/genre-tree/treeMarkPath.ts
1321
1301
  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
1302
 
1323
1303
  // src/genre-tree/TreeMarkIcon.tsx
1324
- var import_jsx_runtime22 = require("react/jsx-runtime");
1304
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1325
1305
  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 }) });
1306
+ 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
1307
  }
1328
1308
 
1329
1309
  // src/genre-tree/GenrePlayActions.tsx
1330
- var import_jsx_runtime23 = require("react/jsx-runtime");
1310
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1331
1311
  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%)]" })
1312
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
1313
+ kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1314
+ kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1315
+ kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
1316
+ /* @__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)]" }),
1317
+ /* @__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
1318
  ] }),
1339
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react8.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1319
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react8.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1340
1320
  ] });
1341
1321
  }
1342
1322
  function playOptions(playlist) {
@@ -1352,13 +1332,13 @@ function playOptions(playlist) {
1352
1332
  function GenrePlayActions({ genrePlaylist, playback }) {
1353
1333
  const { isPlaying, setIsPlaying } = (0, import_player4.usePlayer)();
1354
1334
  const options = playOptions(genrePlaylist);
1355
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: options.map((option) => {
1335
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: options.map((option) => {
1356
1336
  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
1337
  const playing = isCurrent && isPlaying;
1358
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1338
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1359
1339
  import_ui7.ActionButton,
1360
1340
  {
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 }),
1341
+ 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
1342
  label: playing ? "Pause" : option.label,
1363
1343
  caption: String(option.count),
1364
1344
  disabled: option.count === 0,
@@ -1368,9 +1348,7 @@ function GenrePlayActions({ genrePlaylist, playback }) {
1368
1348
  setIsPlaying(!isPlaying);
1369
1349
  return;
1370
1350
  }
1371
- playback.play(genrePlaylist, option.selection).catch((error) => {
1372
- console.error("Failed to load genre playlist tracks:", error);
1373
- });
1351
+ void playback.play(genrePlaylist, option.selection);
1374
1352
  }
1375
1353
  },
1376
1354
  option.key
@@ -1384,7 +1362,7 @@ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
1384
1362
 
1385
1363
  // src/genre-tree/ClampedText.tsx
1386
1364
  var import_react12 = require("react");
1387
- var import_jsx_runtime24 = require("react/jsx-runtime");
1365
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1388
1366
  function ClampedText({ children, className: className2 = "" }) {
1389
1367
  const ref = (0, import_react12.useRef)(null);
1390
1368
  const [expanded, setExpanded] = (0, import_react12.useState)(false);
@@ -1394,9 +1372,9 @@ function ClampedText({ children, className: className2 = "" }) {
1394
1372
  if (!element || expanded) return;
1395
1373
  setOverflows(element.scrollHeight > element.clientHeight);
1396
1374
  }, [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)(
1375
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
1376
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1377
+ (overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1400
1378
  "button",
1401
1379
  {
1402
1380
  type: "button",
@@ -1416,13 +1394,13 @@ var import_zod = require("zod");
1416
1394
  var import_fa2 = require("react-icons/fa");
1417
1395
  var import_ui8 = require("@behindthemusictree/ui");
1418
1396
  var import_auth = require("@behindthemusictree/app-kit/auth");
1397
+ var import_transport2 = require("@behindthemusictree/app-kit/transport");
1419
1398
  var import_transport3 = require("@behindthemusictree/app-kit/transport");
1420
1399
  var import_transport4 = require("@behindthemusictree/app-kit/transport");
1421
- var import_transport5 = require("@behindthemusictree/app-kit/transport");
1422
1400
  var import_player5 = require("@behindthemusictree/app-kit/player");
1423
1401
  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");
1402
+ var import_popup3 = require("@behindthemusictree/app-kit/popup");
1403
+ var import_transport5 = require("@behindthemusictree/app-kit/transport");
1426
1404
  var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1427
1405
  var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1428
1406
  var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
@@ -1431,15 +1409,15 @@ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
1431
1409
 
1432
1410
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1433
1411
  var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1434
- var import_jsx_runtime25 = require("react/jsx-runtime");
1412
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1435
1413
  function MusicBrainzRecordingLink({
1436
1414
  song,
1437
1415
  children,
1438
1416
  className: className2 = ""
1439
1417
  }) {
1440
1418
  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)(
1419
+ if (!url) return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children });
1420
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1443
1421
  "a",
1444
1422
  {
1445
1423
  href: url,
@@ -1456,9 +1434,9 @@ function MusicBrainzRecordingLink({
1456
1434
  // src/genre-tree/GenrePlaylistTracks.tsx
1457
1435
  var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
1458
1436
  var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
1459
- var import_jsx_runtime26 = require("react/jsx-runtime");
1437
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1460
1438
  var CATALOGUE_PAGE_SIZE = 100;
1461
- var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1439
+ var SongPageSchema = (0, import_transport3.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1462
1440
  overallTotal: import_zod.z.number().nullable(),
1463
1441
  page: import_zod.z.number().nullable(),
1464
1442
  totalPages: import_zod.z.number().nullable()
@@ -1490,12 +1468,12 @@ function GenrePlaylistTracks({
1490
1468
  const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree16.useTrackList)();
1491
1469
  const { session, sessionRestored } = (0, import_auth.useSession)();
1492
1470
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player5.usePlayer)();
1493
- const { showPopup } = (0, import_popup4.usePopup)();
1471
+ const { showPopup } = (0, import_popup3.usePopup)();
1494
1472
  const trackNoun = (0, import_track_noun.useTrackNoun)();
1495
1473
  const uuid = genrePlaylist?.uuid;
1496
1474
  const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
1497
- const { fetchBound } = (0, import_transport3.usePlaybackFetch)(getBackendBaseUrl);
1498
- const playback = (0, import_transport3.usePlaybackContext)();
1475
+ const { fetchBound } = (0, import_transport2.usePlaybackFetch)(getBackendBaseUrl);
1476
+ const playback = (0, import_transport2.usePlaybackContext)();
1499
1477
  const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
1500
1478
  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
1479
  queryFn: async ({ pageParam }) => {
@@ -1510,7 +1488,7 @@ function GenrePlaylistTracks({
1510
1488
  {},
1511
1489
  { genre: genreUuid, ...(0, import_genre_tree12.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1512
1490
  );
1513
- const parsed = (0, import_transport5.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1491
+ const parsed = (0, import_transport4.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1514
1492
  return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
1515
1493
  },
1516
1494
  initialPageParam: scope === "me" ? 1 : "",
@@ -1542,35 +1520,36 @@ function GenrePlaylistTracks({
1542
1520
  }) : 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
1521
  started.catch((error) => {
1544
1522
  console.error("Failed to play genre playlist:", error);
1545
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
1523
+ if (error instanceof import_genre_tree16.TracksFailureAlreadyReportedError) return;
1524
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport5.AppError ? error.code : import_transport5.ErrorCode.CLIENT_UNKNOWN }));
1546
1525
  });
1547
1526
  };
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: [
1527
+ 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
1528
  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: [
1529
+ 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
1530
  "Couldn't load ",
1552
1531
  trackNoun.plural,
1553
1532
  ".",
1554
1533
  " ",
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: [
1534
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1535
+ ] }) : 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
1536
  tracks.map((track, index) => {
1558
1537
  const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
1559
1538
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1560
1539
  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 })
1540
+ const text = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: TEXT_CELL, children: [
1541
+ /* @__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 }),
1542
+ track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1543
+ /* @__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 }),
1544
+ track.album && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1566
1545
  ] });
1567
1546
  const leading = renderSongLeading?.(track, index);
1568
1547
  const actions = renderSongActions?.(track);
1569
1548
  const isUnplayable = (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
1570
1549
  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: [
1550
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1572
1551
  leading,
1573
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1552
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1574
1553
  "button",
1575
1554
  {
1576
1555
  type: "button",
@@ -1578,27 +1557,27 @@ function GenrePlaylistTracks({
1578
1557
  "aria-disabled": "true",
1579
1558
  "aria-label": label,
1580
1559
  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 })
1560
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_fa2.FaPlay, { size: 10 })
1582
1561
  }
1583
1562
  ),
1584
1563
  text,
1585
1564
  actions
1586
1565
  ] }, track.uuid);
1587
1566
  }
1588
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1567
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1589
1568
  "li",
1590
1569
  {
1591
1570
  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
1571
  children: [
1593
1572
  leading,
1594
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1573
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1595
1574
  "button",
1596
1575
  {
1597
1576
  type: "button",
1598
1577
  "aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
1599
1578
  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
1579
  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 })
1580
+ 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
1581
  }
1603
1582
  ),
1604
1583
  text,
@@ -1608,7 +1587,7 @@ function GenrePlaylistTracks({
1608
1587
  track.uuid
1609
1588
  );
1610
1589
  }),
1611
- hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1590
+ hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1612
1591
  ] })
1613
1592
  ] }) });
1614
1593
  }
@@ -1616,7 +1595,7 @@ function GenrePlaylistTracks({
1616
1595
  // src/genre-tree/GenreDetailExtras.tsx
1617
1596
  var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
1618
1597
  var import_si2 = require("react-icons/si");
1619
- var import_jsx_runtime27 = require("react/jsx-runtime");
1598
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1620
1599
  function GenreDetailExtras({
1621
1600
  genreUuid,
1622
1601
  genrePlaylist,
@@ -1642,17 +1621,17 @@ function GenreDetailExtras({
1642
1621
  criteriaOverviewSchema
1643
1622
  );
1644
1623
  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" }) }) }),
1624
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1625
+ /* @__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
1626
  family
1648
1627
  ] });
1649
1628
  }
1650
- if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children: family });
1629
+ if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_jsx_runtime28.Fragment, { children: family });
1651
1630
  const { summary, summarySourceUrl, essentialSongs } = overview;
1652
1631
  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)(
1632
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1633
+ (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: [
1634
+ summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1656
1635
  "a",
1657
1636
  {
1658
1637
  href: summarySourceUrl,
@@ -1660,12 +1639,12 @@ function GenreDetailExtras({
1660
1639
  rel: "noopener noreferrer",
1661
1640
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1662
1641
  children: [
1663
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1642
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1664
1643
  "Wikipedia"
1665
1644
  ]
1666
1645
  }
1667
1646
  ),
1668
- wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1647
+ wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1669
1648
  "a",
1670
1649
  {
1671
1650
  href: wikidataUrl,
@@ -1673,17 +1652,17 @@ function GenreDetailExtras({
1673
1652
  rel: "noopener noreferrer",
1674
1653
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1675
1654
  children: [
1676
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1655
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1677
1656
  "Wikidata"
1678
1657
  ]
1679
1658
  }
1680
1659
  )
1681
1660
  ] }) }),
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) }),
1661
+ /* @__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
1662
  family,
1684
1663
  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)(
1664
+ (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" }) }),
1665
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
1687
1666
  GenrePlaylistTracks,
1688
1667
  {
1689
1668
  genreUuid,
@@ -1701,10 +1680,10 @@ function GenreDetailExtras({
1701
1680
  }
1702
1681
 
1703
1682
  // src/genre-tree/GenreInfoPanelBody.tsx
1704
- var import_jsx_runtime28 = require("react/jsx-runtime");
1683
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1705
1684
  var import_react14 = require("react");
1706
1685
  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 }) });
1686
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
1708
1687
  }
1709
1688
  function GenreInfoPanelBody({
1710
1689
  toolbar,
@@ -1721,9 +1700,9 @@ function GenreInfoPanelBody({
1721
1700
  }) {
1722
1701
  const summary = scope === "reference" ? import_genre_tree19.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
1723
1702
  const selection = summary ? (0, import_genre_tree19.defaultGenreMusicSelection)(summary) : void 0;
1724
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
1703
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1725
1704
  toolbar,
1726
- genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(GenreActionBand, { ...band }),
1705
+ genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(GenreActionBand, { ...band }),
1727
1706
  /* @__PURE__ */ (0, import_react14.createElement)(
1728
1707
  GenreDetailExtras,
1729
1708
  {
@@ -1743,7 +1722,7 @@ function GenreInfoPanelBody({
1743
1722
  }
1744
1723
 
1745
1724
  // src/genre-tree/GenreDetailPanel.tsx
1746
- var import_jsx_runtime29 = require("react/jsx-runtime");
1725
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1747
1726
  var RELATIVE_ITEM_COUNT = 1;
1748
1727
  function toNode(genre, parentId) {
1749
1728
  return {
@@ -1807,19 +1786,19 @@ function useGenreDetailContent({
1807
1786
  if (isError) return {
1808
1787
  name: null,
1809
1788
  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." })
1789
+ 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
1790
  };
1812
1791
  if (isPending || !detail || !view) return {
1813
1792
  name: null,
1814
1793
  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" })
1794
+ content: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
1795
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.Skeleton, { className: "h-6 w-2/3" }),
1796
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.Skeleton, { className: "h-4 w-full" }),
1797
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.Skeleton, { className: "h-4 w-3/4" })
1819
1798
  ] })
1820
1799
  };
1821
1800
  const selectGenre = (nodeId) => onSelectGenre(import_genre_tree20.GenreUuidSchema.parse(nodeId));
1822
- const family = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1801
+ const family = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1823
1802
  import_genre_tree_view4.GenreNodeFamily,
1824
1803
  {
1825
1804
  node: view.node,
@@ -1830,17 +1809,17 @@ function useGenreDetailContent({
1830
1809
  onSelectNode: selectGenre
1831
1810
  }
1832
1811
  );
1833
- const ancestors = /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1812
+ const ancestors = /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1834
1813
  return {
1835
1814
  name: detail.name,
1836
1815
  title: detail.name,
1837
1816
  headerAccent: { background: view.fill, color: view.textColor },
1838
1817
  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)(
1818
+ content: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1819
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1841
1820
  GenreInfoPanelBody,
1842
1821
  {
1843
- family: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1822
+ family: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1844
1823
  family,
1845
1824
  !expanded && ancestors
1846
1825
  ] }),
@@ -1884,7 +1863,7 @@ function GenreDetailPanel({
1884
1863
  expanded,
1885
1864
  band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
1886
1865
  });
1887
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1866
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1888
1867
  import_ui10.DetailPanel,
1889
1868
  {
1890
1869
  side: "left",
@@ -1897,7 +1876,7 @@ function GenreDetailPanel({
1897
1876
  edgeTab,
1898
1877
  aside: panel.aside,
1899
1878
  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,
1879
+ 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
1880
  children: panel.content
1902
1881
  }
1903
1882
  );
@@ -1906,7 +1885,7 @@ function GenreDetailPanel({
1906
1885
  // src/genre-tree/ChangeParentPopup.tsx
1907
1886
  var import_react16 = require("react");
1908
1887
  var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
1909
- var import_jsx_runtime30 = require("react/jsx-runtime");
1888
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1910
1889
  function ChangeParentPopup({
1911
1890
  genrePlaylist,
1912
1891
  genrePlaylists,
@@ -1941,12 +1920,12 @@ function ChangeParentPopup({
1941
1920
  window.addEventListener("keydown", handleKeyDown);
1942
1921
  return () => window.removeEventListener("keydown", handleKeyDown);
1943
1922
  }, [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: [
1923
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
1924
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
1946
1925
  "Current parent: ",
1947
1926
  genrePlaylist.parent?.name ?? "none"
1948
1927
  ] }),
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)(
1928
+ /* @__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
1929
  GenreSearch,
1951
1930
  {
1952
1931
  genrePlaylists: candidates,
@@ -1954,7 +1933,7 @@ function ChangeParentPopup({
1954
1933
  placeholder: "Search a new parent\u2026"
1955
1934
  }
1956
1935
  ) }),
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(" ") })
1936
+ 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
1937
  ] });
1959
1938
  }
1960
1939
 
@@ -1967,7 +1946,7 @@ var import_lucide_react10 = require("lucide-react");
1967
1946
  var import_ui11 = require("@behindthemusictree/ui");
1968
1947
  var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
1969
1948
  var import_player7 = require("@behindthemusictree/app-kit/player");
1970
- var import_jsx_runtime31 = require("react/jsx-runtime");
1949
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1971
1950
  function ArtistActionBand({ artist, share }) {
1972
1951
  const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree22.useTrackList)();
1973
1952
  const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
@@ -1983,11 +1962,11 @@ function ArtistActionBand({ artist, share }) {
1983
1962
  console.error("Failed to load artist tracks:", error);
1984
1963
  });
1985
1964
  };
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)(
1965
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_ui11.ActionBand, { children: [
1966
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1988
1967
  import_ui11.ActionButton,
1989
1968
  {
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" }),
1969
+ 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
1970
  label: playing ? "Pause" : "Play",
1992
1971
  caption: String(artist.playableTracksCount),
1993
1972
  disabled: artist.playableTracksCount === 0,
@@ -2006,7 +1985,7 @@ var import_ui13 = require("@behindthemusictree/ui");
2006
1985
  var import_lucide_react11 = require("lucide-react");
2007
1986
  var import_si3 = require("react-icons/si");
2008
1987
  var import_ui12 = require("@behindthemusictree/ui");
2009
- var import_jsx_runtime32 = require("react/jsx-runtime");
1988
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2010
1989
  var LINK_KINDS = {
2011
1990
  official: { label: "Official website", Icon: import_lucide_react11.Globe },
2012
1991
  wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
@@ -2043,7 +2022,7 @@ function artistLinks(artist) {
2043
2022
  return links;
2044
2023
  }
2045
2024
  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)) });
2025
+ 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
2026
  }
2048
2027
 
2049
2028
  // src/genre-tree/artist/formatYears.ts
@@ -2054,7 +2033,7 @@ function formatYears(beginYear, endYear, ended) {
2054
2033
  }
2055
2034
 
2056
2035
  // src/genre-tree/artist/ArtistHeader.tsx
2057
- var import_jsx_runtime33 = require("react/jsx-runtime");
2036
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2058
2037
  var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
2059
2038
  function ArtistHeader({ artist }) {
2060
2039
  const fields = [];
@@ -2064,35 +2043,35 @@ function ArtistHeader({ artist }) {
2064
2043
  const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
2065
2044
  if (years) fields.push({ label: "Active", value: years });
2066
2045
  const links = artistLinks(artist);
2067
- if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ArtistLinks, { links }) });
2046
+ if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistLinks, { links }) });
2068
2047
  if (fields.length === 0) return null;
2069
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui13.FieldList, { items: fields });
2048
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui13.FieldList, { items: fields });
2070
2049
  }
2071
2050
 
2072
2051
  // src/genre-tree/artist/ArtistPhoto.tsx
2073
- var import_jsx_runtime34 = require("react/jsx-runtime");
2052
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2074
2053
  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 });
2054
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
2076
2055
  }
2077
2056
  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: [
2057
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
2058
+ /* @__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" }),
2059
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
2081
2060
  "Photo: ",
2082
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2061
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2083
2062
  ",",
2084
2063
  " ",
2085
- image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2064
+ image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2086
2065
  ] })
2087
2066
  ] });
2088
2067
  }
2089
2068
 
2090
2069
  // src/genre-tree/artist/ArtistMembers.tsx
2091
2070
  var import_ui14 = require("@behindthemusictree/ui");
2092
- var import_jsx_runtime35 = require("react/jsx-runtime");
2071
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2093
2072
  function MembershipList({ title, memberships, renderArtistLink }) {
2094
2073
  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) => {
2074
+ 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
2075
  const details = [
2097
2076
  formatYears(membership.beginYear, membership.endYear, membership.ended),
2098
2077
  ...membership.roles
@@ -2100,9 +2079,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2100
2079
  const detailsText = details.filter(Boolean).join(" \xB7 ");
2101
2080
  return (
2102
2081
  // 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: [
2082
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
2083
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
2084
+ detailsText && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2106
2085
  " \u2014 ",
2107
2086
  detailsText
2108
2087
  ] })
@@ -2113,9 +2092,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2113
2092
  function ArtistMembers({ title, memberships, renderArtistLink }) {
2114
2093
  if (memberships.length === 0) return null;
2115
2094
  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)(
2095
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ui14.PanelSection, { title, children: [
2096
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2097
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2119
2098
  MembershipList,
2120
2099
  {
2121
2100
  title: "Past",
@@ -2128,7 +2107,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
2128
2107
 
2129
2108
  // src/genre-tree/artist/ArtistDiscography.tsx
2130
2109
  var import_ui15 = require("@behindthemusictree/ui");
2131
- var import_jsx_runtime36 = require("react/jsx-runtime");
2110
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2132
2111
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
2133
2112
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
2134
2113
  function discographyGroup({ primaryType, secondaryTypes }) {
@@ -2148,12 +2127,12 @@ function byYear(a, b) {
2148
2127
  function ArtistDiscography({ albums }) {
2149
2128
  if (albums.length === 0) return null;
2150
2129
  const sorted = [...albums].sort(byYear);
2151
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui15.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2130
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui15.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2152
2131
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
2153
2132
  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 })
2133
+ 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: [
2134
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2135
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2157
2136
  ] }, album.uuid)) }) }, group);
2158
2137
  }) });
2159
2138
  }
@@ -2165,7 +2144,7 @@ var import_lucide_react12 = require("lucide-react");
2165
2144
  var import_ui16 = require("@behindthemusictree/ui");
2166
2145
  var import_player11 = require("@behindthemusictree/app-kit/player");
2167
2146
  var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2168
- var import_transport7 = require("@behindthemusictree/app-kit/transport");
2147
+ var import_transport6 = require("@behindthemusictree/app-kit/transport");
2169
2148
 
2170
2149
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2171
2150
  var import_player10 = require("@behindthemusictree/app-kit/player");
@@ -2175,7 +2154,7 @@ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
2175
2154
  var import_fa3 = require("react-icons/fa");
2176
2155
  var import_player8 = require("@behindthemusictree/app-kit/player");
2177
2156
  var import_player9 = require("@behindthemusictree/app-kit/player");
2178
- var import_jsx_runtime37 = require("react/jsx-runtime");
2157
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2179
2158
  function TrackPositionPlayPause({
2180
2159
  position,
2181
2160
  uuid,
@@ -2189,14 +2168,14 @@ function TrackPositionPlayPause({
2189
2168
  const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PLAYING;
2190
2169
  const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PAUSED;
2191
2170
  const isCurrentTrackActive = isPlaying || isPaused;
2192
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2171
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2193
2172
  "div",
2194
2173
  {
2195
2174
  className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
2196
2175
  style: { minWidth: "4rem", maxWidth: "4rem" },
2197
2176
  onClick: handlePlayPauseClick,
2198
2177
  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)(
2178
+ /* @__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
2179
  "span",
2201
2180
  {
2202
2181
  className: "track-position-play-pause__bars",
@@ -2204,18 +2183,18 @@ function TrackPositionPlayPause({
2204
2183
  role: "img",
2205
2184
  "aria-label": isPlaying ? "Playing" : "Paused",
2206
2185
  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" })
2186
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" }),
2187
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" }),
2188
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" })
2210
2189
  ]
2211
2190
  }
2212
2191
  ) : position }),
2213
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2192
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2214
2193
  "div",
2215
2194
  {
2216
2195
  className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
2217
2196
  "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, {})
2197
+ 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
2198
  }
2220
2199
  )
2221
2200
  ]
@@ -2226,7 +2205,7 @@ function TrackPositionPlayPause({
2226
2205
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2227
2206
  var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
2228
2207
  var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
2229
- var import_jsx_runtime38 = require("react/jsx-runtime");
2208
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2230
2209
  function TrackItem({
2231
2210
  track,
2232
2211
  position,
@@ -2250,12 +2229,12 @@ function TrackItem({
2250
2229
  loadTrackForPlayer(track.uuid);
2251
2230
  }
2252
2231
  };
2253
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2232
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
2254
2233
  "div",
2255
2234
  {
2256
2235
  className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
2257
2236
  children: [
2258
- isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2237
+ isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2259
2238
  "div",
2260
2239
  {
2261
2240
  className: "flex items-center justify-center text-lg w-16",
@@ -2263,7 +2242,7 @@ function TrackItem({
2263
2242
  "aria-disabled": "true",
2264
2243
  children: position
2265
2244
  }
2266
- ) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2245
+ ) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2267
2246
  TrackPositionPlayPause,
2268
2247
  {
2269
2248
  position,
@@ -2272,12 +2251,12 @@ function TrackItem({
2272
2251
  isSelected: selectedTrack?.uuid === track.uuid
2273
2252
  }
2274
2253
  ),
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 }) }) : ""
2254
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2255
+ /* @__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 }) }),
2256
+ 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
2257
  ] }),
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)(
2258
+ /* @__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 }) : "" }),
2259
+ renderDuration && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2281
2260
  "div",
2282
2261
  {
2283
2262
  className: "duration flex text-base w-16 items-center justify-center",
@@ -2285,15 +2264,15 @@ function TrackItem({
2285
2264
  children: renderDuration(track)
2286
2265
  }
2287
2266
  ),
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) })
2267
+ 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
2268
  ]
2290
2269
  }
2291
2270
  );
2292
2271
  }
2293
2272
 
2294
2273
  // 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);
2274
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2275
+ var SongPageSchema2 = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
2297
2276
  var SORTS = [
2298
2277
  { ordering: "title", label: "Title" },
2299
2278
  { ordering: "-albumYear", label: "Year" }
@@ -2303,7 +2282,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2303
2282
  const [ordering, setOrdering] = (0, import_react17.useState)("title");
2304
2283
  const { playNewTrackListFromArtist } = (0, import_genre_tree26.useTrackList)();
2305
2284
  const { playerTrackObject } = (0, import_player11.usePlayer)();
2306
- const { data, isPending, isError } = (0, import_transport7.usePlaybackQuery)({
2285
+ const { data, isPending, isError } = (0, import_transport6.usePlaybackQuery)({
2307
2286
  getBackendBaseUrl,
2308
2287
  queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
2309
2288
  queryFn: (fetchBound) => fetchBound(import_genre_tree26.libraryEndpoints.reference.songs.list(), true, false, void 0, {
@@ -2325,21 +2304,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2325
2304
  event.stopPropagation();
2326
2305
  void play(song.uuid);
2327
2306
  };
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)(
2307
+ 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: [
2308
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
2309
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2331
2310
  "button",
2332
2311
  {
2333
2312
  type: "button",
2334
2313
  onClick: () => void play(),
2335
2314
  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
2315
  children: [
2337
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react12.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2316
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react12.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2338
2317
  "Play all"
2339
2318
  ]
2340
2319
  }
2341
2320
  ),
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)(
2321
+ /* @__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)(
2343
2322
  "button",
2344
2323
  {
2345
2324
  type: "button",
@@ -2354,7 +2333,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2354
2333
  sort.ordering
2355
2334
  )) })
2356
2335
  ] }),
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)(
2336
+ /* @__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
2337
  TrackItem,
2359
2338
  {
2360
2339
  track: song,
@@ -2363,12 +2342,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2363
2342
  renderSongLink
2364
2343
  }
2365
2344
  ) }, song.uuid)) }),
2366
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui16.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2345
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2367
2346
  ] }) });
2368
2347
  }
2369
2348
 
2370
2349
  // src/genre-tree/artist/useArtistPanel.tsx
2371
- var import_jsx_runtime40 = require("react/jsx-runtime");
2350
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2372
2351
  function useArtistPanel({
2373
2352
  uuid,
2374
2353
  scope,
@@ -2389,26 +2368,26 @@ function useArtistPanel({
2389
2368
  return {
2390
2369
  ...panel,
2391
2370
  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" })
2371
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2372
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2373
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
2374
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2375
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" }),
2376
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-16 mt-2.5" }),
2377
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-16" })
2399
2378
  ] })
2400
2379
  };
2401
2380
  case "not-found":
2402
2381
  return {
2403
2382
  ...panel,
2404
2383
  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." })
2384
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2406
2385
  };
2407
2386
  case "error":
2408
2387
  return {
2409
2388
  ...panel,
2410
2389
  title: "Artist",
2411
- content: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2390
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2412
2391
  };
2413
2392
  case "success": {
2414
2393
  const { artist } = result;
@@ -2417,19 +2396,19 @@ function useArtistPanel({
2417
2396
  return {
2418
2397
  ...panel,
2419
2398
  title: artist.name,
2420
- content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2421
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2399
+ content: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2400
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2422
2401
  ArtistActionBand,
2423
2402
  {
2424
2403
  artist,
2425
- share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2404
+ share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2426
2405
  }
2427
2406
  ),
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)(
2407
+ artist.image && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
2408
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistHeader, { artist }),
2409
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2410
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2411
+ artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2433
2412
  ArtistSongs,
2434
2413
  {
2435
2414
  artist: { uuid: artist.uuid, name: artist.name },
@@ -2439,7 +2418,7 @@ function useArtistPanel({
2439
2418
  }
2440
2419
  )
2441
2420
  ] }),
2442
- extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2421
+ extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2443
2422
  };
2444
2423
  }
2445
2424
  }
@@ -2455,7 +2434,7 @@ var import_lucide_react13 = require("lucide-react");
2455
2434
  var import_ui18 = require("@behindthemusictree/ui");
2456
2435
  var import_player12 = require("@behindthemusictree/app-kit/player");
2457
2436
  var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
2458
- var import_jsx_runtime41 = require("react/jsx-runtime");
2437
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2459
2438
  var numberFormat = new Intl.NumberFormat("en");
2460
2439
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2461
2440
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
@@ -2463,27 +2442,27 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2463
2442
  const [open, setOpen] = (0, import_react18.useState)(false);
2464
2443
  const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
2465
2444
  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." });
2445
+ if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-10", "aria-busy": "true" });
2446
+ else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2468
2447
  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: [
2448
+ 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: [
2449
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium", children: entry.action }),
2450
+ entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2472
2451
  " ",
2473
2452
  "\u2014 ",
2474
2453
  entry.oldValue ?? "none",
2475
2454
  " \u2192 ",
2476
2455
  entry.newValue ?? "none"
2477
2456
  ] }) : null,
2478
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2457
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2479
2458
  entry.actorPseudo ?? "Anonymous",
2480
2459
  " \xB7 ",
2481
2460
  dateFormat.format(new Date(entry.createdOn))
2482
2461
  ] })
2483
2462
  ] }, 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 })
2463
+ 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: [
2464
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui18.PANEL_LABEL_CLASS}`, children: "History" }),
2465
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "mt-1.5", children: body })
2487
2466
  ] }) });
2488
2467
  }
2489
2468
  function SongPanelContent(props) {
@@ -2502,13 +2481,13 @@ function SongPanelContent(props) {
2502
2481
  const genre = song.genre;
2503
2482
  fields.push({
2504
2483
  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 })
2484
+ 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: [
2485
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
2486
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2508
2487
  ] }, genrePlaylist.uuid)) }) : song.genre.name
2509
2488
  });
2510
2489
  } else {
2511
- fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2490
+ fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2512
2491
  }
2513
2492
  if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
2514
2493
  const viewCount = song.youtubeVideo?.viewCount;
@@ -2518,11 +2497,11 @@ function SongPanelContent(props) {
2518
2497
  const links = [];
2519
2498
  if (musicBrainzUrl)
2520
2499
  links.push(
2521
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2500
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2522
2501
  );
2523
2502
  if (song.youtubeVideo)
2524
2503
  links.push(
2525
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2504
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2526
2505
  "a",
2527
2506
  {
2528
2507
  href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
@@ -2537,31 +2516,31 @@ function SongPanelContent(props) {
2537
2516
  if (links.length > 0)
2538
2517
  fields.push({
2539
2518
  label: "Links",
2540
- value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex gap-3", children: links })
2519
+ value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "flex gap-3", children: links })
2541
2520
  });
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)(
2521
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
2522
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_ui18.ActionBand, { children: [
2523
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2545
2524
  import_ui18.ActionButton,
2546
2525
  {
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" }),
2526
+ icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react13.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react13.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2548
2527
  label: isPlaying ? "Pause" : "Play",
2549
2528
  disabled: !!unplayableReason,
2550
2529
  title: unplayableReason ?? void 0,
2551
2530
  onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
2552
2531
  }
2553
2532
  ),
2554
- shareUrl && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2533
+ shareUrl && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2555
2534
  ] }) }),
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 })
2535
+ 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 }) }),
2536
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.FieldList, { items: fields }),
2537
+ 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)) }) }),
2538
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2560
2539
  ] });
2561
2540
  }
2562
2541
 
2563
2542
  // src/genre-tree/song/useSongPanel.tsx
2564
- var import_jsx_runtime42 = require("react/jsx-runtime");
2543
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2565
2544
  function useSongPanel({
2566
2545
  uuid,
2567
2546
  scope,
@@ -2582,24 +2561,24 @@ function useSongPanel({
2582
2561
  return {
2583
2562
  ...panel,
2584
2563
  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" })
2564
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2565
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2566
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-4 w-32" }),
2567
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2568
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-4 w-24" })
2590
2569
  ] })
2591
2570
  };
2592
2571
  case "not-found":
2593
2572
  return {
2594
2573
  ...panel,
2595
2574
  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." })
2575
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2597
2576
  };
2598
2577
  case "error":
2599
2578
  return {
2600
2579
  ...panel,
2601
2580
  title: "Song",
2602
- content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2581
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2603
2582
  };
2604
2583
  case "success": {
2605
2584
  const { song } = result;
@@ -2607,7 +2586,7 @@ function useSongPanel({
2607
2586
  return {
2608
2587
  ...panel,
2609
2588
  title: song.title,
2610
- content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2589
+ content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2611
2590
  SongPanelContent,
2612
2591
  {
2613
2592
  song,
@@ -2660,12 +2639,12 @@ function useEntityPanel({
2660
2639
  // src/genre-tree/SizeMetricToggle.tsx
2661
2640
  var import_md = require("react-icons/md");
2662
2641
  var import_ui20 = require("@behindthemusictree/ui");
2663
- var import_jsx_runtime43 = require("react/jsx-runtime");
2642
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2664
2643
  function SizeMetricToggle({
2665
2644
  value,
2666
2645
  onChange
2667
2646
  }) {
2668
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2647
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2669
2648
  import_ui20.SegmentedControl,
2670
2649
  {
2671
2650
  variant: "floating",
@@ -2673,15 +2652,15 @@ function SizeMetricToggle({
2673
2652
  value,
2674
2653
  onChange,
2675
2654
  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" }
2655
+ { 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" },
2656
+ { 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
2657
  ]
2679
2658
  }
2680
2659
  );
2681
2660
  }
2682
2661
 
2683
2662
  // src/genre-tree/GenreTreeView.tsx
2684
- var import_jsx_runtime44 = require("react/jsx-runtime");
2663
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2685
2664
  var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
2686
2665
  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
2666
  var noop = () => {
@@ -2773,6 +2752,7 @@ function GenreTreeView({
2773
2752
  isFetching: isTreeFetching,
2774
2753
  genrePlaylistByNodeId,
2775
2754
  visibleGenrePlaylists,
2755
+ focusUuids,
2776
2756
  groupedGenrePlaylistsByRoot,
2777
2757
  selectedGenreUuid,
2778
2758
  selectGenre,
@@ -2832,14 +2812,14 @@ function GenreTreeView({
2832
2812
  const openSongRef = (0, import_react19.useRef)(openSong);
2833
2813
  openSongRef.current = openSong;
2834
2814
  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 }),
2815
+ (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
2816
  [renderSongLink]
2837
2817
  );
2838
2818
  const [expandedSongUuid, setExpandedSongUuid] = (0, import_react19.useState)(null);
2839
2819
  const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react19.useState)(null);
2840
2820
  const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
2841
2821
  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 }));
2822
+ 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
2823
  const getGenrePath = (genre) => {
2844
2824
  const path = [];
2845
2825
  let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
@@ -2881,7 +2861,7 @@ function GenreTreeView({
2881
2861
  selectGenrePlaylist(genrePlaylist2);
2882
2862
  }
2883
2863
  });
2884
- const builtInPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : entityPanel;
2864
+ const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
2885
2865
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react19.useState)(false);
2886
2866
  const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react19.useState)(false);
2887
2867
  const isCollapsed = collapsed ?? uncontrolledCollapsed;
@@ -2909,7 +2889,7 @@ function GenreTreeView({
2909
2889
  const obscuredInset = (0, import_ui21.useObscuredInset)(panelRef);
2910
2890
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
2911
2891
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
2912
- const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
2892
+ const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
2913
2893
  const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
2914
2894
  treeNodes,
2915
2895
  !overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
@@ -2920,7 +2900,7 @@ function GenreTreeView({
2920
2900
  setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
2921
2901
  }
2922
2902
  const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
2923
- const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
2903
+ const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
2924
2904
  const reopenPanel = () => {
2925
2905
  setUncontrolledCollapsed(false);
2926
2906
  onCollapsedChange?.(false);
@@ -2971,7 +2951,7 @@ function GenreTreeView({
2971
2951
  onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
2972
2952
  onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
2973
2953
  onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
2974
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2954
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2975
2955
  ChangeParentPopup,
2976
2956
  {
2977
2957
  genrePlaylist: { ...genrePlaylist, criteria },
@@ -3005,15 +2985,15 @@ function GenreTreeView({
3005
2985
  const isRotationControlled = controlledAllowWheelRotation !== void 0;
3006
2986
  const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
3007
2987
  let activePanel = null;
3008
- if (overlayPanel) {
2988
+ if (otherPanel) {
3009
2989
  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
2990
+ title: otherPanel.title,
2991
+ content: otherPanel.content,
2992
+ aside: otherPanel.extraContent,
2993
+ expanded: otherPanel.expanded ?? false,
2994
+ onExpandedChange: otherPanel.onExpandedChange,
2995
+ onClose: otherPanel.onClose,
2996
+ searchName: otherPanel.title
3017
2997
  };
3018
2998
  } else if (selectedGenreUuid) {
3019
2999
  activePanel = {
@@ -3023,38 +3003,28 @@ function GenreTreeView({
3023
3003
  expanded: isExpanded,
3024
3004
  onExpandedChange: changeExpanded,
3025
3005
  onClose: clearSelection,
3026
- titleInSearch: true,
3006
+ searchName: genreContent.name,
3027
3007
  headerAccent: genreContent.headerAccent
3028
3008
  };
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
3009
  }
3040
3010
  const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
3041
3011
  const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
3042
3012
  edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
3043
3013
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
3044
3014
  } : void 0;
3045
- const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3015
+ const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3046
3016
  import_ui21.IconButton,
3047
3017
  {
3048
3018
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
3049
3019
  style: activePanel.headerAccent && { color: "inherit" },
3050
3020
  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" })
3021
+ children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react15.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react15.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
3052
3022
  }
3053
3023
  ) : null;
3054
3024
  const renderArtistFilter = () => {
3055
3025
  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)(
3026
+ 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: [
3027
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3058
3028
  "button",
3059
3029
  {
3060
3030
  type: "button",
@@ -3066,7 +3036,7 @@ function GenreTreeView({
3066
3036
  children: filterArtist.name
3067
3037
  }
3068
3038
  ),
3069
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3039
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3070
3040
  "button",
3071
3041
  {
3072
3042
  type: "button",
@@ -3078,8 +3048,8 @@ function GenreTreeView({
3078
3048
  )
3079
3049
  ] });
3080
3050
  };
3081
- const actions = /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
3082
- !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3051
+ const actions = /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
3052
+ !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3083
3053
  GenreTreeSearch,
3084
3054
  {
3085
3055
  artistBaseUrl,
@@ -3102,9 +3072,9 @@ function GenreTreeView({
3102
3072
  if (songUuid) changeSong(null);
3103
3073
  selectGenre(uuid);
3104
3074
  },
3105
- selectedName: selectedGenreUuid ? genreContent.name : typeof builtInPanel?.title === "string" ? builtInPanel.title : null,
3075
+ selectedName: activePanel?.searchName ?? null,
3106
3076
  onClear: activePanel?.onClose,
3107
- showClear: activePanel?.titleInSearch ?? false,
3077
+ showClear: activePanel !== null,
3108
3078
  clearLabel: "Close panel",
3109
3079
  onPrevious: handlePrevious,
3110
3080
  onQueryChange: onSearchQueryChange,
@@ -3112,9 +3082,9 @@ function GenreTreeView({
3112
3082
  extraGroups: extraSearchGroups
3113
3083
  }
3114
3084
  ),
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)(
3085
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3086
+ /* @__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: [
3087
+ !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3118
3088
  import_ui21.SegmentedControl,
3119
3089
  {
3120
3090
  "aria-label": "Tree view mode",
@@ -3128,13 +3098,13 @@ function GenreTreeView({
3128
3098
  ]
3129
3099
  }
3130
3100
  ),
3131
- !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3101
+ !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3132
3102
  "div",
3133
3103
  {
3134
3104
  className: "flex items-center gap-1",
3135
3105
  role: "group",
3136
3106
  "aria-label": "Tree display options",
3137
- children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3107
+ children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3138
3108
  import_ui21.Button,
3139
3109
  {
3140
3110
  variant: allowWheelRotation ? "default" : "outline",
@@ -3145,7 +3115,7 @@ function GenreTreeView({
3145
3115
  )
3146
3116
  }
3147
3117
  ),
3148
- !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3118
+ !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3149
3119
  import_ui21.IconTextButton,
3150
3120
  {
3151
3121
  icon: import_lucide_react14.Plus,
@@ -3155,42 +3125,43 @@ function GenreTreeView({
3155
3125
  )
3156
3126
  ] })
3157
3127
  ] });
3158
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
3128
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3159
3129
  "div",
3160
3130
  {
3161
3131
  "data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
3162
3132
  className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
3163
3133
  children: [
3164
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3134
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3165
3135
  "div",
3166
3136
  {
3167
3137
  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
3138
  children: actions
3169
3139
  }
3170
3140
  ),
3171
- !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3141
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3172
3142
  "div",
3173
3143
  {
3174
3144
  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
3145
  children: renderArtistFilter()
3176
3146
  }
3177
3147
  ),
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: [
3148
+ /* @__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: [
3149
+ !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
3150
+ 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
3151
  "Couldn't load the genre tree.",
3182
3152
  " ",
3183
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3153
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3184
3154
  ] }) : viewMode === "wheel" ? (
3185
3155
  // The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
3186
3156
  // and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
3187
3157
  // Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
3188
3158
  // 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)(
3159
+ /* @__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
3160
  GenrePlaylistTreeWheel,
3191
3161
  {
3192
3162
  scope,
3193
3163
  genrePlaylists: visibleGenrePlaylists,
3164
+ focusUuids,
3194
3165
  handleGenreCreationAction,
3195
3166
  handleGenreRenameAction,
3196
3167
  additionalActions,
@@ -3203,9 +3174,9 @@ function GenreTreeView({
3203
3174
  allowWheelRotation,
3204
3175
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3205
3176
  busy,
3206
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
3177
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
3207
3178
  floatingControlsLeading,
3208
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3179
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3209
3180
  ] }) : void 0,
3210
3181
  defaultViewport,
3211
3182
  viewportRestore,
@@ -3214,11 +3185,12 @@ function GenreTreeView({
3214
3185
  ) }) })
3215
3186
  ) : viewMode === "pop-core" ? (
3216
3187
  // 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)(
3188
+ /* @__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
3189
  GenrePlaylistTreeWheelRadialPopCore,
3219
3190
  {
3220
3191
  scope,
3221
3192
  genrePlaylists: visibleGenrePlaylists,
3193
+ focusUuids,
3222
3194
  handleGenreCreationAction,
3223
3195
  handleGenreRenameAction,
3224
3196
  additionalActions,
@@ -3231,9 +3203,9 @@ function GenreTreeView({
3231
3203
  allowWheelRotation,
3232
3204
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3233
3205
  busy,
3234
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
3206
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
3235
3207
  floatingControlsLeading,
3236
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3208
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3237
3209
  ] }) : void 0,
3238
3210
  defaultViewport,
3239
3211
  viewportRestore,
@@ -3241,12 +3213,13 @@ function GenreTreeView({
3241
3213
  ...focus
3242
3214
  }
3243
3215
  ) }) })
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)(
3216
+ ) : 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
3217
  GenrePlaylistTreeWheelRadialPopCore,
3246
3218
  {
3247
3219
  outline: true,
3248
3220
  scope,
3249
3221
  genrePlaylists: visibleGenrePlaylists,
3222
+ focusUuids,
3250
3223
  handleGenreCreationAction,
3251
3224
  handleGenreRenameAction,
3252
3225
  additionalActions,
@@ -3257,13 +3230,13 @@ function GenreTreeView({
3257
3230
  selectedNodeId,
3258
3231
  readOnly
3259
3232
  }
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(
3233
+ ) }) : /* @__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
3234
  ([uuid, genrePlaylistTreePerRoot]) => {
3262
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3235
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3263
3236
  "div",
3264
3237
  {
3265
3238
  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)(
3239
+ children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3267
3240
  GenrePlaylistTreePerRoot,
3268
3241
  {
3269
3242
  scope,
@@ -3276,6 +3249,7 @@ function GenreTreeView({
3276
3249
  onNodeHover: handleNodeHover,
3277
3250
  ...hostPanel,
3278
3251
  playback,
3252
+ focusUuids,
3279
3253
  selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
3280
3254
  readOnly,
3281
3255
  wheelZoom: "modifier",
@@ -3289,13 +3263,13 @@ function GenreTreeView({
3289
3263
  );
3290
3264
  }
3291
3265
  ) }),
3292
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
3266
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3293
3267
  import_ui21.DetailPanel,
3294
3268
  {
3295
3269
  ref: panelRef,
3296
3270
  side: "left",
3297
3271
  title: activePanel?.title ?? "",
3298
- titleInSearch: activePanel?.titleInSearch,
3272
+ titleInSearch: !isLoading && !isError,
3299
3273
  headerAccent: activePanel?.headerAccent,
3300
3274
  headerActions: expandButton,
3301
3275
  onClose: activePanel?.onClose ?? noop,
@@ -3304,8 +3278,7 @@ function GenreTreeView({
3304
3278
  onExpandedChange: activePanel?.onExpandedChange,
3305
3279
  aside: activePanel?.aside,
3306
3280
  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: [
3281
+ children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
3309
3282
  activePanel.content,
3310
3283
  activePanel.aside
3311
3284
  ] }) : activePanel?.content
@@ -3326,7 +3299,7 @@ var import_lucide_react16 = require("lucide-react");
3326
3299
  var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
3327
3300
  var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
3328
3301
  var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
3329
- var import_jsx_runtime45 = require("react/jsx-runtime");
3302
+ var import_jsx_runtime46 = require("react/jsx-runtime");
3330
3303
  var useBrowserLayoutEffect = typeof window === "undefined" ? import_react20.useEffect : import_react20.useLayoutEffect;
3331
3304
  function TrackListSidebar({
3332
3305
  className: className2 = "",
@@ -3428,25 +3401,25 @@ function TrackListSidebar({
3428
3401
  }
3429
3402
  };
3430
3403
  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)(
3404
+ return trackList ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3432
3405
  "div",
3433
3406
  {
3434
3407
  "data-theme": "dark",
3435
3408
  className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
3436
3409
  style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
3437
3410
  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 }) })
3411
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3412
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3413
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "origin flex text-xl", children: [
3414
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3415
+ /* @__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
3416
  ] }),
3444
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3417
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3445
3418
  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
3419
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3447
3420
  ] })
3448
3421
  ] }),
3449
- (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
3422
+ (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3450
3423
  "button",
3451
3424
  {
3452
3425
  type: "button",
@@ -3455,11 +3428,11 @@ function TrackListSidebar({
3455
3428
  "aria-expanded": !isCollapsed,
3456
3429
  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
3430
  onClick: isCollapsed ? onExpand : onMinimize,
3458
- children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react16.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3431
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react16.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3459
3432
  }
3460
3433
  )
3461
3434
  ] }),
3462
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
3435
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3463
3436
  "ul",
3464
3437
  {
3465
3438
  ref: trackListRef,
@@ -3469,7 +3442,7 @@ function TrackListSidebar({
3469
3442
  onPointerDown: markUserScrollIntent,
3470
3443
  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
3444
  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)(
3445
+ 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
3446
  TrackItem,
3474
3447
  {
3475
3448
  track,
@@ -3480,7 +3453,7 @@ function TrackListSidebar({
3480
3453
  renderSongLink
3481
3454
  }
3482
3455
  ) }, track.uuid)),
3483
- hasMore && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3456
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3484
3457
  ]
3485
3458
  }
3486
3459
  )
@@ -3500,7 +3473,7 @@ var import_ui22 = require("@behindthemusictree/ui");
3500
3473
  var import_lucide_react17 = require("lucide-react");
3501
3474
  var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
3502
3475
  var import_player13 = require("@behindthemusictree/app-kit/player");
3503
- var import_jsx_runtime46 = require("react/jsx-runtime");
3476
+ var import_jsx_runtime47 = require("react/jsx-runtime");
3504
3477
  function PlayerControls() {
3505
3478
  const {
3506
3479
  duration,
@@ -3519,15 +3492,15 @@ function PlayerControls() {
3519
3492
  const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
3520
3493
  const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
3521
3494
  const isPlaying = playState === import_player13.PlayStates.PLAYING;
3522
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
3495
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
3523
3496
  "section",
3524
3497
  {
3525
3498
  "aria-label": "Player controls",
3526
3499
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3527
3500
  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)(
3501
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3502
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree33.formatTime)(safeCurrentTime) }),
3503
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3531
3504
  "input",
3532
3505
  {
3533
3506
  "aria-label": "Seek",
@@ -3541,11 +3514,11 @@ function PlayerControls() {
3541
3514
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3542
3515
  }
3543
3516
  ),
3544
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3517
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3545
3518
  ] }),
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)(
3519
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3520
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3521
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3549
3522
  "button",
3550
3523
  {
3551
3524
  type: "button",
@@ -3554,10 +3527,10 @@ function PlayerControls() {
3554
3527
  disabled: !isReady || !hasPreviousTrack,
3555
3528
  onClick: playPreviousTrack,
3556
3529
  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" })
3530
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3558
3531
  }
3559
3532
  ),
3560
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3533
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3561
3534
  "button",
3562
3535
  {
3563
3536
  type: "button",
@@ -3566,10 +3539,10 @@ function PlayerControls() {
3566
3539
  disabled: !isReady,
3567
3540
  onClick: handlePlayPauseAction,
3568
3541
  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" })
3542
+ children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3570
3543
  }
3571
3544
  ),
3572
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3545
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3573
3546
  "button",
3574
3547
  {
3575
3548
  type: "button",
@@ -3578,14 +3551,14 @@ function PlayerControls() {
3578
3551
  disabled: !isReady || !hasNextTrack,
3579
3552
  onClick: playNextTrack,
3580
3553
  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" })
3554
+ children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3582
3555
  }
3583
3556
  )
3584
3557
  ] }),
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)(
3558
+ /* @__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: [
3559
+ volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3560
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sr-only", children: "Volume" }),
3561
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3589
3562
  "input",
3590
3563
  {
3591
3564
  "aria-label": "Volume",
@@ -3607,7 +3580,7 @@ function PlayerControls() {
3607
3580
  }
3608
3581
 
3609
3582
  // src/genre-tree/PlayerPanel.tsx
3610
- var import_jsx_runtime47 = require("react/jsx-runtime");
3583
+ var import_jsx_runtime48 = require("react/jsx-runtime");
3611
3584
  var PANEL_ID = "player-panel";
3612
3585
  var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
3613
3586
  function PlayerPanel({
@@ -3647,7 +3620,7 @@ function PlayerPanel({
3647
3620
  onClick: () => setIsExtendRequested(true),
3648
3621
  expanded: false
3649
3622
  } : null;
3650
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
3623
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3651
3624
  "div",
3652
3625
  {
3653
3626
  id: PANEL_ID,
@@ -3660,7 +3633,7 @@ function PlayerPanel({
3660
3633
  ),
3661
3634
  style: isExtended ? { width: extendedWidth } : void 0,
3662
3635
  children: [
3663
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3636
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3664
3637
  import_ui22.PanelEdgeTab,
3665
3638
  {
3666
3639
  side: "right",
@@ -3670,7 +3643,7 @@ function PlayerPanel({
3670
3643
  className: "absolute right-full top-1/2 -translate-y-1/2"
3671
3644
  }
3672
3645
  ),
3673
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
3646
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3674
3647
  "div",
3675
3648
  {
3676
3649
  className: (0, import_tailwind_merge.twMerge)(
@@ -3678,20 +3651,20 @@ function PlayerPanel({
3678
3651
  isExtended ? "flex-row" : "flex-col"
3679
3652
  ),
3680
3653
  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: [
3654
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3655
+ /* @__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 }) }),
3656
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "relative w-full shrink-0", children: [
3657
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3658
+ playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3686
3659
  playerTrackObject.loadError,
3687
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3660
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3688
3661
  void retryPlayback();
3689
3662
  }, children: "Retry" })
3690
3663
  ] })
3691
3664
  ] }),
3692
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(PlayerControls, {})
3665
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PlayerControls, {})
3693
3666
  ] }),
3694
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3667
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3695
3668
  TrackListSidebar,
3696
3669
  {
3697
3670
  layout: "inline",
@@ -3718,11 +3691,11 @@ function PlayerPanel({
3718
3691
  var import_uuid = require("uuid");
3719
3692
  var import_ai = require("react-icons/ai");
3720
3693
  var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
3721
- var import_jsx_runtime48 = require("react/jsx-runtime");
3694
+ var import_jsx_runtime49 = require("react/jsx-runtime");
3722
3695
  function Rating({ rating, handleChange }) {
3723
3696
  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)(
3697
+ 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: [
3698
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3726
3699
  "input",
3727
3700
  {
3728
3701
  id: `star-${instanceId}-${i}`,
@@ -3734,16 +3707,16 @@ function Rating({ rating, handleChange }) {
3734
3707
  onChange: handleChange
3735
3708
  }
3736
3709
  ),
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)(
3710
+ /* @__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: [
3711
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3739
3712
  import_ai.AiOutlineStar,
3740
3713
  {
3741
3714
  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
3715
  }
3743
3716
  ),
3744
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "flex-grow w-8" }),
3717
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-grow w-8" }),
3745
3718
  " "
3746
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
3719
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3747
3720
  import_ai.AiFillStar,
3748
3721
  {
3749
3722
  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 +3728,7 @@ function Rating({ rating, handleChange }) {
3755
3728
  // src/genre-tree/EntityDetailPanel.tsx
3756
3729
  var import_react22 = require("react");
3757
3730
  var import_ui23 = require("@behindthemusictree/ui");
3758
- var import_jsx_runtime49 = require("react/jsx-runtime");
3731
+ var import_jsx_runtime50 = require("react/jsx-runtime");
3759
3732
  function EntityDetailPanel({
3760
3733
  scope,
3761
3734
  getBackendBaseUrl,
@@ -3778,8 +3751,8 @@ function EntityDetailPanel({
3778
3751
  getBackendBaseUrl,
3779
3752
  getArtistBackendBaseUrl,
3780
3753
  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 })),
3754
+ 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 })),
3755
+ renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
3783
3756
  song: {
3784
3757
  uuid: songUuid,
3785
3758
  expanded: songUuid !== null && expandedSongUuid === songUuid,
@@ -3795,7 +3768,7 @@ function EntityDetailPanel({
3795
3768
  ...navigation
3796
3769
  });
3797
3770
  if (!panel) return null;
3798
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3771
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3799
3772
  import_ui23.DetailPanel,
3800
3773
  {
3801
3774
  side: "left",
@@ -3807,7 +3780,7 @@ function EntityDetailPanel({
3807
3780
  onExpandedChange: panel.onExpandedChange,
3808
3781
  aside: panel.extraContent,
3809
3782
  edgeTab,
3810
- children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
3783
+ children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
3811
3784
  panel.content,
3812
3785
  panel.extraContent
3813
3786
  ] }) : panel.content