@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.
package/dist/index.cjs CHANGED
@@ -596,7 +596,7 @@ var import_lucide_react19 = require("lucide-react");
596
596
  var import_ui24 = require("@behindthemusictree/ui");
597
597
  var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
598
598
  var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
599
- var import_popup6 = require("@behindthemusictree/app-kit/popup");
599
+ var import_popup5 = require("@behindthemusictree/app-kit/popup");
600
600
 
601
601
  // src/genre-tree/playlist-tree/size-metric.ts
602
602
  var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
@@ -610,7 +610,7 @@ var genrePlaylistSize = (genrePlaylist, metric) => {
610
610
  };
611
611
 
612
612
  // src/genre-tree/playlist-tree/toTreeNodes.ts
613
- function toTreeNodes(genrePlaylists, sizeMetric) {
613
+ function toTreeNodes(genrePlaylists, sizeMetric, focusUuids) {
614
614
  return genrePlaylists.map((genrePlaylist) => ({
615
615
  id: genrePlaylist.uuid,
616
616
  parentId: genrePlaylist.parent?.uuid ?? null,
@@ -621,7 +621,8 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
621
621
  side: genrePlaylist.criteria?.side ?? void 0,
622
622
  wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
623
623
  umbrella: genrePlaylist.isUmbrella,
624
- scene: genrePlaylist.scene ?? void 0
624
+ scene: genrePlaylist.scene ?? void 0,
625
+ muted: focusUuids ? !focusUuids.has(genrePlaylist.uuid) : void 0
625
626
  }));
626
627
  }
627
628
 
@@ -630,18 +631,20 @@ var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
630
631
  var import_react4 = require("react");
631
632
  var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
632
633
  var import_player = require("@behindthemusictree/app-kit/player");
633
- var import_popup2 = require("@behindthemusictree/app-kit/popup");
634
- var import_transport3 = require("@behindthemusictree/app-kit/transport");
635
634
 
636
- // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
635
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
637
636
  var import_react3 = require("react");
638
637
  var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
638
+ var import_popup2 = require("@behindthemusictree/app-kit/popup");
639
+ var import_transport3 = require("@behindthemusictree/app-kit/transport");
640
+ var import_jsx_runtime12 = require("react/jsx-runtime");
639
641
  function useGenrePlaylistPlayback({
640
642
  scope,
641
643
  genrePlaylists,
642
644
  artist = null
643
645
  }) {
644
646
  const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
647
+ const { showPopup } = (0, import_popup2.usePopup)();
645
648
  const origin = trackList?.origin;
646
649
  const playing = (0, import_react3.useMemo)(() => {
647
650
  if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
@@ -653,14 +656,20 @@ function useGenrePlaylistPlayback({
653
656
  }, [origin, artist, genrePlaylists, scope]);
654
657
  const play = (0, import_react3.useCallback)(async (playlist, requested) => {
655
658
  if (artist && !playlist.criteria) return;
656
- const selection = scope === "reference" && playlist.criteria !== null ? requested ?? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
657
- return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
658
- }, [artist, scope, playNewTrackListFromGenrePlaylist]);
659
+ try {
660
+ const selection = scope === "reference" && playlist.criteria !== null ? requested ?? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
661
+ await (selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope));
662
+ } catch (error) {
663
+ console.error("Failed to load genre playlist tracks:", error);
664
+ if (error instanceof import_genre_tree2.TracksFailureAlreadyReportedError) return;
665
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport3.AppError ? error.code : import_transport3.ErrorCode.CLIENT_UNKNOWN }));
666
+ }
667
+ }, [artist, scope, playNewTrackListFromGenrePlaylist, showPopup]);
659
668
  return (0, import_react3.useMemo)(() => ({ playing, play }), [playing, play]);
660
669
  }
661
670
 
662
671
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
663
- var import_jsx_runtime12 = require("react/jsx-runtime");
672
+ var import_jsx_runtime13 = require("react/jsx-runtime");
664
673
  function GenrePlaylistTreePerRoot({
665
674
  scope,
666
675
  className: className2,
@@ -679,25 +688,16 @@ function GenrePlaylistTreePerRoot({
679
688
  viewportRestore,
680
689
  onViewportChange,
681
690
  playback,
691
+ focusUuids,
682
692
  ...hostPanel
683
693
  }) {
684
694
  const { isPlaying, setIsPlaying } = (0, import_player.usePlayer)();
685
695
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
686
696
  const { playing, play } = playback ?? defaultPlayback;
687
697
  const playingNodeId = playing?.uuid ?? null;
688
- const { showPopup } = (0, import_popup2.usePopup)();
689
698
  const nodes = (0, import_react4.useMemo)(
690
- () => genrePlaylistTreePerRoot.map((genrePlaylist) => ({
691
- id: genrePlaylist.uuid,
692
- parentId: genrePlaylist.parent?.uuid ?? null,
693
- name: genrePlaylist.name,
694
- itemCount: (0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist),
695
- actionable: Boolean(genrePlaylist.criteria),
696
- side: genrePlaylist.criteria?.side ?? void 0,
697
- wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
698
- umbrella: genrePlaylist.isUmbrella
699
- })),
700
- [genrePlaylistTreePerRoot]
699
+ () => toTreeNodes(genrePlaylistTreePerRoot, "songs", focusUuids),
700
+ [genrePlaylistTreePerRoot, focusUuids]
701
701
  );
702
702
  const handlePlayPause = (0, import_react4.useCallback)(
703
703
  (nodeId) => {
@@ -710,18 +710,14 @@ function GenrePlaylistTreePerRoot({
710
710
  if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
711
711
  return;
712
712
  }
713
- play(genrePlaylist).catch((error) => {
714
- console.error("Failed to load genre playlist tracks:", error);
715
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InternalErrorPopup, { errorCode: import_transport3.ErrorCode.CLIENT_UNKNOWN }));
716
- });
713
+ void play(genrePlaylist);
717
714
  },
718
715
  [
719
716
  genrePlaylistTreePerRoot,
720
717
  playingNodeId,
721
718
  isPlaying,
722
719
  setIsPlaying,
723
- play,
724
- showPopup
720
+ play
725
721
  ]
726
722
  );
727
723
  const handleAddChild = (0, import_react4.useCallback)(
@@ -744,7 +740,7 @@ function GenrePlaylistTreePerRoot({
744
740
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
745
741
  }
746
742
  }, []);
747
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
743
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
748
744
  import_genre_tree_view.GenreTree,
749
745
  {
750
746
  className: className2,
@@ -775,9 +771,7 @@ var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
775
771
  var import_react5 = require("react");
776
772
  var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
777
773
  var import_player2 = require("@behindthemusictree/app-kit/player");
778
- var import_popup3 = require("@behindthemusictree/app-kit/popup");
779
- var import_transport4 = require("@behindthemusictree/app-kit/transport");
780
- var import_jsx_runtime13 = require("react/jsx-runtime");
774
+ var import_jsx_runtime14 = require("react/jsx-runtime");
781
775
  function GenrePlaylistTreeWheel({
782
776
  scope,
783
777
  className: className2,
@@ -798,24 +792,16 @@ function GenrePlaylistTreeWheel({
798
792
  viewportRestore,
799
793
  onViewportChange,
800
794
  playback,
795
+ focusUuids,
801
796
  ...hostPanel
802
797
  }) {
803
798
  const { isPlaying, setIsPlaying } = (0, import_player2.usePlayer)();
804
799
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
805
800
  const { playing, play } = playback ?? defaultPlayback;
806
801
  const playingNodeId = playing?.uuid ?? null;
807
- const { showPopup } = (0, import_popup3.usePopup)();
808
802
  const nodes = (0, import_react5.useMemo)(
809
- () => genrePlaylists.map((genrePlaylist) => ({
810
- id: genrePlaylist.uuid,
811
- parentId: genrePlaylist.parent?.uuid ?? null,
812
- name: genrePlaylist.name,
813
- itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
814
- actionable: Boolean(genrePlaylist.criteria),
815
- side: genrePlaylist.criteria?.side ?? void 0,
816
- wikidataId: genrePlaylist.criteria?.wikidataId ?? null
817
- })),
818
- [genrePlaylists, sizeMetric]
803
+ () => toTreeNodes(genrePlaylists, sizeMetric, focusUuids),
804
+ [genrePlaylists, sizeMetric, focusUuids]
819
805
  );
820
806
  const handlePlayPause = (0, import_react5.useCallback)(
821
807
  (nodeId) => {
@@ -828,18 +814,14 @@ function GenrePlaylistTreeWheel({
828
814
  if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
829
815
  return;
830
816
  }
831
- play(genrePlaylist).catch((error) => {
832
- console.error("Failed to load genre playlist tracks:", error);
833
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InternalErrorPopup, { errorCode: import_transport4.ErrorCode.CLIENT_UNKNOWN }));
834
- });
817
+ void play(genrePlaylist);
835
818
  },
836
819
  [
837
820
  genrePlaylists,
838
821
  playingNodeId,
839
822
  isPlaying,
840
823
  setIsPlaying,
841
- play,
842
- showPopup
824
+ play
843
825
  ]
844
826
  );
845
827
  const handleAddChild = (0, import_react5.useCallback)(
@@ -862,7 +844,7 @@ function GenrePlaylistTreeWheel({
862
844
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
863
845
  }
864
846
  }, []);
865
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
847
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
866
848
  import_genre_tree_view2.GenreTreeWheel,
867
849
  {
868
850
  className: className2,
@@ -894,7 +876,7 @@ var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
894
876
  var import_react6 = require("react");
895
877
  var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
896
878
  var import_player3 = require("@behindthemusictree/app-kit/player");
897
- var import_jsx_runtime14 = require("react/jsx-runtime");
879
+ var import_jsx_runtime15 = require("react/jsx-runtime");
898
880
  function GenrePlaylistTreeWheelRadialPopCore(props) {
899
881
  const {
900
882
  scope,
@@ -927,7 +909,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
927
909
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
928
910
  const { playing, play } = playback ?? defaultPlayback;
929
911
  const playingNodeId = playing?.uuid ?? null;
930
- const nodes = (0, import_react6.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
912
+ const nodes = (0, import_react6.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric, props.focusUuids), [genrePlaylists, sizeMetric, props.focusUuids]);
931
913
  const handlePlayPause = (0, import_react6.useCallback)(
932
914
  (nodeId) => {
933
915
  const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
@@ -939,9 +921,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
939
921
  if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
940
922
  return;
941
923
  }
942
- play(genrePlaylist).catch((error) => {
943
- console.error("Failed to load genre playlist tracks:", error);
944
- });
924
+ void play(genrePlaylist);
945
925
  },
946
926
  [genrePlaylists, playingNodeId, isPlaying, setIsPlaying, play]
947
927
  );
@@ -981,7 +961,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
981
961
  onSelectedNodeChange,
982
962
  ...hostPanel
983
963
  };
984
- return outline ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
964
+ return outline ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
985
965
  import_genre_tree_view3.GenreTreeWheelRadialPopCore,
986
966
  {
987
967
  ...treeProps,
@@ -998,7 +978,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
998
978
 
999
979
  // src/genre-tree/GenreTreeWheelHandoff.tsx
1000
980
  var import_react7 = require("react");
1001
- var import_jsx_runtime15 = require("react/jsx-runtime");
981
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1002
982
  function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
1003
983
  const [revealed, setRevealed] = (0, import_react7.useState)(false);
1004
984
  const onRevealedChangeRef = (0, import_react7.useRef)(onRevealedChange);
@@ -1017,8 +997,8 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
1017
997
  cancelAnimationFrame(innerFrame);
1018
998
  };
1019
999
  }, []);
1020
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative w-full h-full", children: [
1021
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1000
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "relative w-full h-full", children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1022
1002
  "div",
1023
1003
  {
1024
1004
  className: "absolute inset-0 transition-opacity duration-150 ease-out",
@@ -1027,7 +1007,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
1027
1007
  children
1028
1008
  }
1029
1009
  ),
1030
- !revealed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
1010
+ !revealed && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
1031
1011
  ] });
1032
1012
  }
1033
1013
 
@@ -1042,7 +1022,7 @@ var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
1042
1022
  // src/genre-tree/search/SearchBox.tsx
1043
1023
  var import_react8 = require("react");
1044
1024
  var import_ui6 = require("@behindthemusictree/ui");
1045
- var import_jsx_runtime16 = require("react/jsx-runtime");
1025
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1046
1026
  function SearchBox({
1047
1027
  label,
1048
1028
  placeholder,
@@ -1064,7 +1044,7 @@ function SearchBox({
1064
1044
  groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
1065
1045
  onPicked?.(groupKey, itemKey);
1066
1046
  };
1067
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1047
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1068
1048
  import_ui6.SearchCombobox,
1069
1049
  {
1070
1050
  ariaLabel: label,
@@ -1096,7 +1076,7 @@ var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS
1096
1076
 
1097
1077
  // src/genre-tree/search/useArtistSearchGroup.tsx
1098
1078
  var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
1099
- var import_jsx_runtime17 = require("react/jsx-runtime");
1079
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1100
1080
  function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1101
1081
  const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
1102
1082
  if (!enabled) return void 0;
@@ -1105,15 +1085,15 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1105
1085
  label: "Artists",
1106
1086
  items: search.artists.map((artist) => ({
1107
1087
  key: artist.uuid,
1108
- content: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
1109
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "block truncate font-medium", children: [
1088
+ content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1089
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "block truncate font-medium", children: [
1110
1090
  artist.name,
1111
- artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
1091
+ artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
1112
1092
  " \xB7 ",
1113
1093
  artist.disambiguation
1114
1094
  ] })
1115
1095
  ] }),
1116
- artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
1096
+ artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
1117
1097
  "also known as ",
1118
1098
  artist.matchedAlias
1119
1099
  ] })
@@ -1127,7 +1107,7 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1127
1107
 
1128
1108
  // src/genre-tree/search/useSongSearchGroup.tsx
1129
1109
  var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
1130
- var import_jsx_runtime18 = require("react/jsx-runtime");
1110
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1131
1111
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1132
1112
  const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
1133
1113
  if (!enabled) return void 0;
@@ -1136,9 +1116,9 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1136
1116
  label: "Songs",
1137
1117
  items: search.songs.map((song) => ({
1138
1118
  key: song.uuid,
1139
- content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
1140
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate font-medium", children: song.title }),
1141
- /* @__PURE__ */ (0, import_jsx_runtime18.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 ") })
1119
+ content: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
1120
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate font-medium", children: song.title }),
1121
+ /* @__PURE__ */ (0, import_jsx_runtime19.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 ") })
1142
1122
  ] }),
1143
1123
  onSelect: () => onSelect(song)
1144
1124
  })),
@@ -1149,7 +1129,7 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1149
1129
 
1150
1130
  // src/genre-tree/search/useGenreRemoteSearchGroup.tsx
1151
1131
  var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
1152
- var import_jsx_runtime19 = require("react/jsx-runtime");
1132
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1153
1133
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
1154
1134
  const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
1155
1135
  const genres = filter ? search.genres.filter(filter) : search.genres;
@@ -1158,12 +1138,12 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
1158
1138
  label,
1159
1139
  items: genres.map((genre) => ({
1160
1140
  key: genre.uuid,
1161
- content: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
1162
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "block truncate font-medium", children: [
1141
+ content: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1142
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "block truncate font-medium", children: [
1163
1143
  genre.name,
1164
- genre.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
1144
+ genre.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
1165
1145
  ] }),
1166
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
1146
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
1167
1147
  ] }),
1168
1148
  onSelect: () => onSelect(genre)
1169
1149
  })),
@@ -1173,7 +1153,7 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
1173
1153
  }
1174
1154
 
1175
1155
  // src/genre-tree/GenreSearch.tsx
1176
- var import_jsx_runtime20 = require("react/jsx-runtime");
1156
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1177
1157
  function GenreSearch({
1178
1158
  genrePlaylists,
1179
1159
  onSelect,
@@ -1202,9 +1182,9 @@ function GenreSearch({
1202
1182
  label: hasExtras ? "Genres" : void 0,
1203
1183
  items: results.map((genrePlaylist) => ({
1204
1184
  key: genrePlaylist.uuid,
1205
- content: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1185
+ content: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
1206
1186
  genrePlaylist.name,
1207
- genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
1187
+ genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
1208
1188
  ] }),
1209
1189
  onSelect: () => select(genrePlaylist)
1210
1190
  }))
@@ -1213,7 +1193,7 @@ function GenreSearch({
1213
1193
  (group) => active ? group : { ...group, items: [], status: "", loading: false }
1214
1194
  )
1215
1195
  ];
1216
- return /* @__PURE__ */ (0, import_jsx_runtime20.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_runtime20.jsx)(
1196
+ return /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)(
1217
1197
  SearchBox,
1218
1198
  {
1219
1199
  label,
@@ -1233,14 +1213,14 @@ function GenreSearch({
1233
1213
  clear();
1234
1214
  onQueryChange?.("");
1235
1215
  } : void 0,
1236
- leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui7.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
1216
+ leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui7.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
1237
1217
  onFocus
1238
1218
  }
1239
1219
  ) });
1240
1220
  }
1241
1221
 
1242
1222
  // src/genre-tree/GenreTreeSearch.tsx
1243
- var import_jsx_runtime21 = require("react/jsx-runtime");
1223
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1244
1224
  function GenreTreeSearch({
1245
1225
  artistBaseUrl,
1246
1226
  songBaseUrl,
@@ -1260,7 +1240,7 @@ function GenreTreeSearch({
1260
1240
  const deferredQuery = (0, import_react9.useDeferredValue)(query);
1261
1241
  const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
1262
1242
  const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
1263
- const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1243
+ const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1264
1244
  GenreSearch,
1265
1245
  {
1266
1246
  ...props,
@@ -1275,7 +1255,7 @@ function GenreTreeSearch({
1275
1255
  extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
1276
1256
  }
1277
1257
  );
1278
- return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
1258
+ return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
1279
1259
  }
1280
1260
 
1281
1261
  // src/genre-tree/GenreDetailPanel.tsx
@@ -1295,7 +1275,7 @@ var import_ui9 = require("@behindthemusictree/ui");
1295
1275
  // src/genre-tree/ShareActionButton.tsx
1296
1276
  var import_lucide_react10 = require("lucide-react");
1297
1277
  var import_ui8 = require("@behindthemusictree/ui");
1298
- var import_popup4 = require("@behindthemusictree/app-kit/popup");
1278
+ var import_popup3 = require("@behindthemusictree/app-kit/popup");
1299
1279
 
1300
1280
  // src/genre-tree/SharePopup.tsx
1301
1281
  var import_react10 = require("react");
@@ -1304,12 +1284,12 @@ var import_fa3 = require("react-icons/fa");
1304
1284
  var import_si = require("react-icons/si");
1305
1285
 
1306
1286
  // src/genre-tree/ShareDestinationButton.tsx
1307
- var import_jsx_runtime22 = require("react/jsx-runtime");
1287
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1308
1288
  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";
1309
1289
  function Contents({ icon, label }) {
1310
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
1311
- /* @__PURE__ */ (0, import_jsx_runtime22.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 }),
1312
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "max-w-full truncate", children: label })
1290
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
1291
+ /* @__PURE__ */ (0, import_jsx_runtime23.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 }),
1292
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "max-w-full truncate", children: label })
1313
1293
  ] });
1314
1294
  }
1315
1295
  function ShareDestinationButton({
@@ -1321,29 +1301,29 @@ function ShareDestinationButton({
1321
1301
  ariaLabel
1322
1302
  }) {
1323
1303
  if (href) {
1324
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Contents, { icon, label }) });
1304
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Contents, { icon, label }) });
1325
1305
  }
1326
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Contents, { icon, label }) });
1306
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Contents, { icon, label }) });
1327
1307
  }
1328
1308
 
1329
1309
  // src/genre-tree/SharePreviewCard.tsx
1330
1310
  var import_lucide_react8 = require("lucide-react");
1331
- var import_jsx_runtime23 = require("react/jsx-runtime");
1311
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1332
1312
  function SharePreviewCard({ target, url }) {
1333
1313
  const Icon = { genre: import_lucide_react8.Music2, artist: import_lucide_react8.UserRound, song: import_lucide_react8.Music }[target.kind];
1334
1314
  const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
1335
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
1336
- /* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
1337
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
1338
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
1339
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
1340
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
1315
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
1316
+ /* @__PURE__ */ (0, import_jsx_runtime24.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_runtime24.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
1317
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
1318
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
1319
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
1320
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
1341
1321
  ] })
1342
1322
  ] }) });
1343
1323
  }
1344
1324
 
1345
1325
  // src/genre-tree/SharePopup.tsx
1346
- var import_jsx_runtime24 = require("react/jsx-runtime");
1326
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1347
1327
  function resolveAbsoluteUrl(url) {
1348
1328
  try {
1349
1329
  const parsed = new URL(url, window.location.href);
@@ -1418,8 +1398,8 @@ function SharePopup({ target, url, onClose }) {
1418
1398
  setShareError("Could not open the share options.");
1419
1399
  }
1420
1400
  };
1421
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
1422
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1401
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
1402
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1423
1403
  BasePopup_default,
1424
1404
  {
1425
1405
  title: "Share",
@@ -1434,70 +1414,70 @@ function SharePopup({ target, url, onClose }) {
1434
1414
  titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
1435
1415
  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)]",
1436
1416
  contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
1437
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-4", children: [
1438
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
1439
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
1440
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1417
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-4", children: [
1418
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
1419
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
1420
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1441
1421
  ShareDestinationButton,
1442
1422
  {
1443
1423
  label: "Copy Link",
1444
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1424
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1445
1425
  onClick: () => void handleCopy(),
1446
1426
  ariaLabel: "Copy link",
1447
1427
  disabled: !absoluteUrl
1448
1428
  }
1449
1429
  ),
1450
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1430
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1451
1431
  ShareDestinationButton,
1452
1432
  {
1453
1433
  label: "WhatsApp",
1454
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1434
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1455
1435
  href: destinationUrls?.whatsapp,
1456
1436
  ariaLabel: `Share ${target.name} via WhatsApp`,
1457
1437
  disabled: !absoluteUrl
1458
1438
  }
1459
1439
  ),
1460
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1440
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1461
1441
  ShareDestinationButton,
1462
1442
  {
1463
1443
  label: "Gmail",
1464
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1444
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1465
1445
  href: destinationUrls?.gmail,
1466
1446
  ariaLabel: `Email ${target.name} with Gmail`,
1467
1447
  disabled: !absoluteUrl
1468
1448
  }
1469
1449
  ),
1470
- canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1450
+ canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1471
1451
  ShareDestinationButton,
1472
1452
  {
1473
1453
  label: "More",
1474
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1454
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1475
1455
  onClick: () => void handleNativeShare(),
1476
1456
  ariaLabel: "More sharing options",
1477
1457
  disabled: !absoluteUrl
1478
1458
  }
1479
1459
  )
1480
1460
  ] }),
1481
- copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
1482
- shareError && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
1461
+ copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
1462
+ shareError && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
1483
1463
  ] })
1484
1464
  }
1485
1465
  ),
1486
- toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1466
+ toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1487
1467
  ] });
1488
1468
  }
1489
1469
 
1490
1470
  // src/genre-tree/ShareActionButton.tsx
1491
- var import_jsx_runtime25 = require("react/jsx-runtime");
1471
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1492
1472
  function ShareActionButton({ target, url }) {
1493
- const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
1473
+ const { showPopup, hidePopup } = (0, import_popup3.usePopup)();
1494
1474
  const openSharePopup = () => {
1495
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1475
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1496
1476
  };
1497
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1477
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1498
1478
  import_ui8.ActionButton,
1499
1479
  {
1500
- icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1480
+ icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1501
1481
  label: "Share",
1502
1482
  "aria-label": `Share ${target.name}`,
1503
1483
  title: `Share ${target.name}`,
@@ -1507,7 +1487,7 @@ function ShareActionButton({ target, url }) {
1507
1487
  }
1508
1488
 
1509
1489
  // src/genre-tree/GenreActionBand.tsx
1510
- var import_jsx_runtime26 = require("react/jsx-runtime");
1490
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1511
1491
  function GenreActionBand({
1512
1492
  node,
1513
1493
  playActions,
@@ -1528,24 +1508,24 @@ function GenreActionBand({
1528
1508
  items.push({
1529
1509
  key: "zoom-in",
1530
1510
  label: "Focus",
1531
- icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1511
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1532
1512
  onClick: onZoomIn
1533
1513
  });
1534
1514
  }
1535
1515
  if (onAddChild) {
1536
- adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
1516
+ adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react11.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
1537
1517
  }
1538
1518
  if (onRename) {
1539
- adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
1519
+ adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react11.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
1540
1520
  }
1541
1521
  if (onChangeParent) {
1542
- adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
1522
+ adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react11.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
1543
1523
  }
1544
1524
  if (onToggleSide) {
1545
1525
  adminItems.push({
1546
1526
  key: "toggle-side",
1547
1527
  label: side === "pop" ? "Make core" : "Make pop",
1548
- icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1528
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react11.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1549
1529
  onClick: onToggleSide,
1550
1530
  disabled: !!toggleSideDisabledReason,
1551
1531
  title: toggleSideDisabledReason
@@ -1560,9 +1540,9 @@ function GenreActionBand({
1560
1540
  disabled: action.enabled ? !action.enabled(node) : false
1561
1541
  });
1562
1542
  }
1563
- const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui9.ActionButton, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
1543
+ const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.ActionButton, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
1564
1544
  if (!playActions && items.length === 0 && adminItems.length === 0 && !(shareTarget && shareUrl)) return null;
1565
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui9.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1545
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1566
1546
  import_ui9.ActionBand,
1567
1547
  {
1568
1548
  className: "genre-action-band",
@@ -1570,7 +1550,7 @@ function GenreActionBand({
1570
1550
  children: [
1571
1551
  playActions,
1572
1552
  renderItems(items),
1573
- shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1553
+ shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1574
1554
  ]
1575
1555
  }
1576
1556
  ) });
@@ -1594,7 +1574,7 @@ var REGION_GLOBE_PATHS = {
1594
1574
  var REGION_GLOBES = ["africa", "americas", "asia"];
1595
1575
 
1596
1576
  // src/genre-tree/RegionGlobeIcon.tsx
1597
- var import_jsx_runtime27 = require("react/jsx-runtime");
1577
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1598
1578
  var ROTATION_INTERVAL_MS = 3e3;
1599
1579
  function RegionGlobeIcon({ className: className2 }) {
1600
1580
  const [index, setIndex] = (0, import_react11.useState)(0);
@@ -1612,29 +1592,29 @@ function RegionGlobeIcon({ className: className2 }) {
1612
1592
  clearInterval(timer);
1613
1593
  };
1614
1594
  }, []);
1615
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
1595
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
1616
1596
  }
1617
1597
 
1618
1598
  // src/genre-tree/treeMarkPath.ts
1619
1599
  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";
1620
1600
 
1621
1601
  // src/genre-tree/TreeMarkIcon.tsx
1622
- var import_jsx_runtime28 = require("react/jsx-runtime");
1602
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1623
1603
  function TreeMarkIcon({ className: className2 }) {
1624
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
1604
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
1625
1605
  }
1626
1606
 
1627
1607
  // src/genre-tree/GenrePlayActions.tsx
1628
- var import_jsx_runtime29 = require("react/jsx-runtime");
1608
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1629
1609
  function PlayIcon({ kind }) {
1630
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
1631
- kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1632
- kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1633
- kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
1634
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
1635
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
1610
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
1611
+ kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1612
+ kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1613
+ kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1614
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
1615
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
1636
1616
  ] }),
1637
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react12.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1617
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react12.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1638
1618
  ] });
1639
1619
  }
1640
1620
  function playOptions(playlist) {
@@ -1650,13 +1630,13 @@ function playOptions(playlist) {
1650
1630
  function GenrePlayActions({ genrePlaylist, playback }) {
1651
1631
  const { isPlaying, setIsPlaying } = (0, import_player4.usePlayer)();
1652
1632
  const options = playOptions(genrePlaylist);
1653
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_jsx_runtime29.Fragment, { children: options.map((option) => {
1633
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_jsx_runtime30.Fragment, { children: options.map((option) => {
1654
1634
  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());
1655
1635
  const playing = isCurrent && isPlaying;
1656
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1636
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1657
1637
  import_ui10.ActionButton,
1658
1638
  {
1659
- icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(PlayIcon, { kind: option.icon }),
1639
+ icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(PlayIcon, { kind: option.icon }),
1660
1640
  label: playing ? "Pause" : option.label,
1661
1641
  caption: String(option.count),
1662
1642
  disabled: option.count === 0,
@@ -1666,9 +1646,7 @@ function GenrePlayActions({ genrePlaylist, playback }) {
1666
1646
  setIsPlaying(!isPlaying);
1667
1647
  return;
1668
1648
  }
1669
- playback.play(genrePlaylist, option.selection).catch((error) => {
1670
- console.error("Failed to load genre playlist tracks:", error);
1671
- });
1649
+ void playback.play(genrePlaylist, option.selection);
1672
1650
  }
1673
1651
  },
1674
1652
  option.key
@@ -1682,7 +1660,7 @@ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
1682
1660
 
1683
1661
  // src/genre-tree/ClampedText.tsx
1684
1662
  var import_react12 = require("react");
1685
- var import_jsx_runtime30 = require("react/jsx-runtime");
1663
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1686
1664
  function ClampedText({ children, className: className2 = "" }) {
1687
1665
  const ref = (0, import_react12.useRef)(null);
1688
1666
  const [expanded, setExpanded] = (0, import_react12.useState)(false);
@@ -1692,9 +1670,9 @@ function ClampedText({ children, className: className2 = "" }) {
1692
1670
  if (!element || expanded) return;
1693
1671
  setOverflows(element.scrollHeight > element.clientHeight);
1694
1672
  }, [children, expanded]);
1695
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1696
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1697
- (overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1673
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
1674
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1675
+ (overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1698
1676
  "button",
1699
1677
  {
1700
1678
  type: "button",
@@ -1714,13 +1692,13 @@ var import_zod = require("zod");
1714
1692
  var import_fa4 = require("react-icons/fa");
1715
1693
  var import_ui11 = require("@behindthemusictree/ui");
1716
1694
  var import_auth2 = require("@behindthemusictree/app-kit/auth");
1695
+ var import_transport4 = require("@behindthemusictree/app-kit/transport");
1717
1696
  var import_transport5 = require("@behindthemusictree/app-kit/transport");
1718
1697
  var import_transport6 = require("@behindthemusictree/app-kit/transport");
1719
- var import_transport7 = require("@behindthemusictree/app-kit/transport");
1720
1698
  var import_player5 = require("@behindthemusictree/app-kit/player");
1721
1699
  var import_player6 = require("@behindthemusictree/app-kit/player");
1722
- var import_popup5 = require("@behindthemusictree/app-kit/popup");
1723
- var import_transport8 = require("@behindthemusictree/app-kit/transport");
1700
+ var import_popup4 = require("@behindthemusictree/app-kit/popup");
1701
+ var import_transport7 = require("@behindthemusictree/app-kit/transport");
1724
1702
  var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1725
1703
  var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1726
1704
  var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
@@ -1729,15 +1707,15 @@ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
1729
1707
 
1730
1708
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1731
1709
  var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1732
- var import_jsx_runtime31 = require("react/jsx-runtime");
1710
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1733
1711
  function MusicBrainzRecordingLink({
1734
1712
  song,
1735
1713
  children,
1736
1714
  className: className2 = ""
1737
1715
  }) {
1738
1716
  const url = (0, import_genre_tree11.musicBrainzRecordingUrl)(song);
1739
- if (!url) return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_jsx_runtime31.Fragment, { children });
1740
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1717
+ if (!url) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children });
1718
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1741
1719
  "a",
1742
1720
  {
1743
1721
  href: url,
@@ -1754,9 +1732,9 @@ function MusicBrainzRecordingLink({
1754
1732
  // src/genre-tree/GenrePlaylistTracks.tsx
1755
1733
  var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
1756
1734
  var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
1757
- var import_jsx_runtime32 = require("react/jsx-runtime");
1735
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1758
1736
  var CATALOGUE_PAGE_SIZE = 100;
1759
- var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1737
+ var SongPageSchema = (0, import_transport5.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1760
1738
  overallTotal: import_zod.z.number().nullable(),
1761
1739
  page: import_zod.z.number().nullable(),
1762
1740
  totalPages: import_zod.z.number().nullable()
@@ -1788,12 +1766,12 @@ function GenrePlaylistTracks({
1788
1766
  const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree16.useTrackList)();
1789
1767
  const { session, sessionRestored } = (0, import_auth2.useSession)();
1790
1768
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player5.usePlayer)();
1791
- const { showPopup } = (0, import_popup5.usePopup)();
1769
+ const { showPopup } = (0, import_popup4.usePopup)();
1792
1770
  const trackNoun = (0, import_track_noun2.useTrackNoun)();
1793
1771
  const uuid = genrePlaylist?.uuid;
1794
1772
  const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
1795
- const { fetchBound } = (0, import_transport5.usePlaybackFetch)(getBackendBaseUrl);
1796
- const playback = (0, import_transport5.usePlaybackContext)();
1773
+ const { fetchBound } = (0, import_transport4.usePlaybackFetch)(getBackendBaseUrl);
1774
+ const playback = (0, import_transport4.usePlaybackContext)();
1797
1775
  const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
1798
1776
  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] : []],
1799
1777
  queryFn: async ({ pageParam }) => {
@@ -1808,7 +1786,7 @@ function GenrePlaylistTracks({
1808
1786
  {},
1809
1787
  { genre: genreUuid, ...(0, import_genre_tree12.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1810
1788
  );
1811
- const parsed = (0, import_transport7.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1789
+ const parsed = (0, import_transport6.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1812
1790
  return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
1813
1791
  },
1814
1792
  initialPageParam: scope === "me" ? 1 : "",
@@ -1840,35 +1818,36 @@ function GenrePlaylistTracks({
1840
1818
  }) : 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"));
1841
1819
  started.catch((error) => {
1842
1820
  console.error("Failed to play genre playlist:", error);
1843
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
1821
+ if (error instanceof import_genre_tree16.TracksFailureAlreadyReportedError) return;
1822
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport7.AppError ? error.code : import_transport7.ErrorCode.CLIENT_UNKNOWN }));
1844
1823
  });
1845
1824
  };
1846
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { children: [
1825
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { children: [
1847
1826
  renderTracksToolbar?.(),
1848
- scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("p", { role: "alert", children: [
1827
+ scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { role: "alert", children: [
1849
1828
  "Couldn't load ",
1850
1829
  trackNoun.plural,
1851
1830
  ".",
1852
1831
  " ",
1853
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1854
- ] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1832
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1833
+ ] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1855
1834
  tracks.map((track, index) => {
1856
1835
  const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
1857
1836
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1858
1837
  const isPlaying = isCurrent && playState === import_player6.PlayStates.PLAYING;
1859
- const text = /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { className: TEXT_CELL, children: [
1860
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1861
- track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1862
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_genre_tree17.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1863
- track.album && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1838
+ const text = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: TEXT_CELL, children: [
1839
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1840
+ track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1841
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_genre_tree17.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1842
+ track.album && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1864
1843
  ] });
1865
1844
  const leading = renderSongLeading?.(track, index);
1866
1845
  const actions = renderSongActions?.(track);
1867
1846
  const isUnplayable = (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
1868
1847
  if (isUnplayable) {
1869
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1848
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1870
1849
  leading,
1871
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1850
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1872
1851
  "button",
1873
1852
  {
1874
1853
  type: "button",
@@ -1876,27 +1855,27 @@ function GenrePlaylistTracks({
1876
1855
  "aria-disabled": "true",
1877
1856
  "aria-label": label,
1878
1857
  className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
1879
- children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_fa4.FaPlay, { size: 10 })
1858
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa4.FaPlay, { size: 10 })
1880
1859
  }
1881
1860
  ),
1882
1861
  text,
1883
1862
  actions
1884
1863
  ] }, track.uuid);
1885
1864
  }
1886
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1865
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1887
1866
  "li",
1888
1867
  {
1889
1868
  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)]" : ""}`,
1890
1869
  children: [
1891
1870
  leading,
1892
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1871
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1893
1872
  "button",
1894
1873
  {
1895
1874
  type: "button",
1896
1875
  "aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
1897
1876
  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"}`,
1898
1877
  onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
1899
- children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_fa4.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_fa4.FaPlay, { size: 10 })
1878
+ children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa4.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa4.FaPlay, { size: 10 })
1900
1879
  }
1901
1880
  ),
1902
1881
  text,
@@ -1906,7 +1885,7 @@ function GenrePlaylistTracks({
1906
1885
  track.uuid
1907
1886
  );
1908
1887
  }),
1909
- hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1888
+ hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1910
1889
  ] })
1911
1890
  ] }) });
1912
1891
  }
@@ -1914,7 +1893,7 @@ function GenrePlaylistTracks({
1914
1893
  // src/genre-tree/GenreDetailExtras.tsx
1915
1894
  var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
1916
1895
  var import_si2 = require("react-icons/si");
1917
- var import_jsx_runtime33 = require("react/jsx-runtime");
1896
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1918
1897
  function GenreDetailExtras({
1919
1898
  genreUuid,
1920
1899
  genrePlaylist,
@@ -1940,17 +1919,17 @@ function GenreDetailExtras({
1940
1919
  criteriaOverviewSchema
1941
1920
  );
1942
1921
  if (isPending) {
1943
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
1944
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.Skeleton, { className: "h-4 w-3/4" }) }) }),
1922
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
1923
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Skeleton, { className: "h-4 w-3/4" }) }) }),
1945
1924
  family
1946
1925
  ] });
1947
1926
  }
1948
- if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_jsx_runtime33.Fragment, { children: family });
1927
+ if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_jsx_runtime34.Fragment, { children: family });
1949
1928
  const { summary, summarySourceUrl, essentialSongs } = overview;
1950
1929
  const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
1951
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
1952
- (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
1953
- summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1930
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
1931
+ (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
1932
+ summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
1954
1933
  "a",
1955
1934
  {
1956
1935
  href: summarySourceUrl,
@@ -1958,12 +1937,12 @@ function GenreDetailExtras({
1958
1937
  rel: "noopener noreferrer",
1959
1938
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1960
1939
  children: [
1961
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1940
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1962
1941
  "Wikipedia"
1963
1942
  ]
1964
1943
  }
1965
1944
  ),
1966
- wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1945
+ wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
1967
1946
  "a",
1968
1947
  {
1969
1948
  href: wikidataUrl,
@@ -1971,17 +1950,17 @@ function GenreDetailExtras({
1971
1950
  rel: "noopener noreferrer",
1972
1951
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1973
1952
  children: [
1974
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1953
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1975
1954
  "Wikidata"
1976
1955
  ]
1977
1956
  }
1978
1957
  )
1979
1958
  ] }) }),
1980
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1959
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1981
1960
  family,
1982
1961
  renderGenreDetailExtras?.(overview),
1983
- (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
1984
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1962
+ (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
1963
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1985
1964
  GenrePlaylistTracks,
1986
1965
  {
1987
1966
  genreUuid,
@@ -1999,10 +1978,10 @@ function GenreDetailExtras({
1999
1978
  }
2000
1979
 
2001
1980
  // src/genre-tree/GenreInfoPanelBody.tsx
2002
- var import_jsx_runtime34 = require("react/jsx-runtime");
1981
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2003
1982
  var import_react14 = require("react");
2004
1983
  function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
2005
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
1984
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
2006
1985
  }
2007
1986
  function GenreInfoPanelBody({
2008
1987
  toolbar,
@@ -2019,9 +1998,9 @@ function GenreInfoPanelBody({
2019
1998
  }) {
2020
1999
  const summary = scope === "reference" ? import_genre_tree19.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
2021
2000
  const selection = summary ? (0, import_genre_tree19.defaultGenreMusicSelection)(summary) : void 0;
2022
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
2001
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
2023
2002
  toolbar,
2024
- genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenreActionBand, { ...band }),
2003
+ genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenreActionBand, { ...band }),
2025
2004
  /* @__PURE__ */ (0, import_react14.createElement)(
2026
2005
  GenreDetailExtras,
2027
2006
  {
@@ -2041,7 +2020,7 @@ function GenreInfoPanelBody({
2041
2020
  }
2042
2021
 
2043
2022
  // src/genre-tree/GenreDetailPanel.tsx
2044
- var import_jsx_runtime35 = require("react/jsx-runtime");
2023
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2045
2024
  var RELATIVE_ITEM_COUNT = 1;
2046
2025
  function toNode(genre, parentId) {
2047
2026
  return {
@@ -2105,19 +2084,19 @@ function useGenreDetailContent({
2105
2084
  if (isError) return {
2106
2085
  name: null,
2107
2086
  title: "Genre unavailable",
2108
- content: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
2087
+ content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
2109
2088
  };
2110
2089
  if (isPending || !detail || !view) return {
2111
2090
  name: null,
2112
2091
  title: "Loading genre\u2026",
2113
- content: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
2114
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.Skeleton, { className: "h-6 w-2/3" }),
2115
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.Skeleton, { className: "h-4 w-full" }),
2116
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.Skeleton, { className: "h-4 w-3/4" })
2092
+ content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
2093
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-6 w-2/3" }),
2094
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-4 w-full" }),
2095
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-4 w-3/4" })
2117
2096
  ] })
2118
2097
  };
2119
2098
  const selectGenre = (nodeId) => onSelectGenre(import_genre_tree20.GenreUuidSchema.parse(nodeId));
2120
- const family = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2099
+ const family = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2121
2100
  import_genre_tree_view4.GenreNodeFamily,
2122
2101
  {
2123
2102
  node: view.node,
@@ -2128,17 +2107,17 @@ function useGenreDetailContent({
2128
2107
  onSelectNode: selectGenre
2129
2108
  }
2130
2109
  );
2131
- const ancestors = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
2110
+ const ancestors = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
2132
2111
  return {
2133
2112
  name: detail.name,
2134
2113
  title: detail.name,
2135
2114
  headerAccent: { background: view.fill, color: view.textColor },
2136
2115
  aside: expanded ? ancestors : void 0,
2137
- content: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
2138
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2116
+ content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
2117
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2139
2118
  GenreInfoPanelBody,
2140
2119
  {
2141
- family: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
2120
+ family: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
2142
2121
  family,
2143
2122
  !expanded && ancestors
2144
2123
  ] }),
@@ -2182,7 +2161,7 @@ function GenreDetailPanel({
2182
2161
  expanded,
2183
2162
  band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
2184
2163
  });
2185
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2164
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2186
2165
  import_ui13.DetailPanel,
2187
2166
  {
2188
2167
  side: "left",
@@ -2195,7 +2174,7 @@ function GenreDetailPanel({
2195
2174
  edgeTab,
2196
2175
  aside: panel.aside,
2197
2176
  titleInSearch,
2198
- headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
2177
+ headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
2199
2178
  children: panel.content
2200
2179
  }
2201
2180
  );
@@ -2204,7 +2183,7 @@ function GenreDetailPanel({
2204
2183
  // src/genre-tree/ChangeParentPopup.tsx
2205
2184
  var import_react16 = require("react");
2206
2185
  var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
2207
- var import_jsx_runtime36 = require("react/jsx-runtime");
2186
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2208
2187
  function ChangeParentPopup({
2209
2188
  genrePlaylist,
2210
2189
  genrePlaylists,
@@ -2239,12 +2218,12 @@ function ChangeParentPopup({
2239
2218
  window.addEventListener("keydown", handleKeyDown);
2240
2219
  return () => window.removeEventListener("keydown", handleKeyDown);
2241
2220
  }, [onClose]);
2242
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
2243
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
2221
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
2222
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
2244
2223
  "Current parent: ",
2245
2224
  genrePlaylist.parent?.name ?? "none"
2246
2225
  ] }),
2247
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2226
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2248
2227
  GenreSearch,
2249
2228
  {
2250
2229
  genrePlaylists: candidates,
@@ -2252,7 +2231,7 @@ function ChangeParentPopup({
2252
2231
  placeholder: "Search a new parent\u2026"
2253
2232
  }
2254
2233
  ) }),
2255
- formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
2234
+ formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
2256
2235
  ] });
2257
2236
  }
2258
2237
 
@@ -2265,7 +2244,7 @@ var import_lucide_react14 = require("lucide-react");
2265
2244
  var import_ui14 = require("@behindthemusictree/ui");
2266
2245
  var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
2267
2246
  var import_player7 = require("@behindthemusictree/app-kit/player");
2268
- var import_jsx_runtime37 = require("react/jsx-runtime");
2247
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2269
2248
  function ArtistActionBand({ artist, share }) {
2270
2249
  const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree22.useTrackList)();
2271
2250
  const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
@@ -2281,11 +2260,11 @@ function ArtistActionBand({ artist, share }) {
2281
2260
  console.error("Failed to load artist tracks:", error);
2282
2261
  });
2283
2262
  };
2284
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ui14.ActionBand, { children: [
2285
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2263
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_ui14.ActionBand, { children: [
2264
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2286
2265
  import_ui14.ActionButton,
2287
2266
  {
2288
- icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2267
+ icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2289
2268
  label: playing ? "Pause" : "Play",
2290
2269
  caption: String(artist.playableTracksCount),
2291
2270
  disabled: artist.playableTracksCount === 0,
@@ -2304,7 +2283,7 @@ var import_ui16 = require("@behindthemusictree/ui");
2304
2283
  var import_lucide_react15 = require("lucide-react");
2305
2284
  var import_si3 = require("react-icons/si");
2306
2285
  var import_ui15 = require("@behindthemusictree/ui");
2307
- var import_jsx_runtime38 = require("react/jsx-runtime");
2286
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2308
2287
  var LINK_KINDS = {
2309
2288
  official: { label: "Official website", Icon: import_lucide_react15.Globe },
2310
2289
  wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
@@ -2341,7 +2320,7 @@ function artistLinks(artist) {
2341
2320
  return links;
2342
2321
  }
2343
2322
  function ArtistLinks({ links }) {
2344
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2323
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui15.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2345
2324
  }
2346
2325
 
2347
2326
  // src/genre-tree/artist/formatYears.ts
@@ -2352,7 +2331,7 @@ function formatYears(beginYear, endYear, ended) {
2352
2331
  }
2353
2332
 
2354
2333
  // src/genre-tree/artist/ArtistHeader.tsx
2355
- var import_jsx_runtime39 = require("react/jsx-runtime");
2334
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2356
2335
  var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
2357
2336
  function ArtistHeader({ artist }) {
2358
2337
  const fields = [];
@@ -2362,35 +2341,35 @@ function ArtistHeader({ artist }) {
2362
2341
  const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
2363
2342
  if (years) fields.push({ label: "Active", value: years });
2364
2343
  const links = artistLinks(artist);
2365
- if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ArtistLinks, { links }) });
2344
+ if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistLinks, { links }) });
2366
2345
  if (fields.length === 0) return null;
2367
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui16.FieldList, { items: fields });
2346
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.FieldList, { items: fields });
2368
2347
  }
2369
2348
 
2370
2349
  // src/genre-tree/artist/ArtistPhoto.tsx
2371
- var import_jsx_runtime40 = require("react/jsx-runtime");
2350
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2372
2351
  function ExternalLink({ href, children }) {
2373
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
2352
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
2374
2353
  }
2375
2354
  function ArtistPhoto({ image, name }) {
2376
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
2377
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
2378
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
2355
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
2356
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
2357
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
2379
2358
  "Photo: ",
2380
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2359
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2381
2360
  ",",
2382
2361
  " ",
2383
- image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2362
+ image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2384
2363
  ] })
2385
2364
  ] });
2386
2365
  }
2387
2366
 
2388
2367
  // src/genre-tree/artist/ArtistMembers.tsx
2389
2368
  var import_ui17 = require("@behindthemusictree/ui");
2390
- var import_jsx_runtime41 = require("react/jsx-runtime");
2369
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2391
2370
  function MembershipList({ title, memberships, renderArtistLink }) {
2392
2371
  if (memberships.length === 0) return null;
2393
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { children: memberships.map((membership, index) => {
2372
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: memberships.map((membership, index) => {
2394
2373
  const details = [
2395
2374
  formatYears(membership.beginYear, membership.endYear, membership.ended),
2396
2375
  ...membership.roles
@@ -2398,9 +2377,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2398
2377
  const detailsText = details.filter(Boolean).join(" \xB7 ");
2399
2378
  return (
2400
2379
  // A member can have several periods in the same group.
2401
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
2402
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
2403
- detailsText && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2380
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
2381
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
2382
+ detailsText && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2404
2383
  " \u2014 ",
2405
2384
  detailsText
2406
2385
  ] })
@@ -2411,9 +2390,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2411
2390
  function ArtistMembers({ title, memberships, renderArtistLink }) {
2412
2391
  if (memberships.length === 0) return null;
2413
2392
  const isCurrent = (membership) => !membership.ended && membership.endYear === null;
2414
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_ui17.PanelSection, { title, children: [
2415
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2416
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2393
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_ui17.PanelSection, { title, children: [
2394
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2395
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2417
2396
  MembershipList,
2418
2397
  {
2419
2398
  title: "Past",
@@ -2426,7 +2405,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
2426
2405
 
2427
2406
  // src/genre-tree/artist/ArtistDiscography.tsx
2428
2407
  var import_ui18 = require("@behindthemusictree/ui");
2429
- var import_jsx_runtime42 = require("react/jsx-runtime");
2408
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2430
2409
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
2431
2410
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
2432
2411
  function discographyGroup({ primaryType, secondaryTypes }) {
@@ -2446,12 +2425,12 @@ function byYear(a, b) {
2446
2425
  function ArtistDiscography({ albums }) {
2447
2426
  if (albums.length === 0) return null;
2448
2427
  const sorted = [...albums].sort(byYear);
2449
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2428
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2450
2429
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
2451
2430
  if (groupAlbums.length === 0) return null;
2452
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2453
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2454
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2431
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2432
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2433
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2455
2434
  ] }, album.uuid)) }) }, group);
2456
2435
  }) });
2457
2436
  }
@@ -2463,7 +2442,7 @@ var import_lucide_react16 = require("lucide-react");
2463
2442
  var import_ui19 = require("@behindthemusictree/ui");
2464
2443
  var import_player11 = require("@behindthemusictree/app-kit/player");
2465
2444
  var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2466
- var import_transport9 = require("@behindthemusictree/app-kit/transport");
2445
+ var import_transport8 = require("@behindthemusictree/app-kit/transport");
2467
2446
 
2468
2447
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2469
2448
  var import_player10 = require("@behindthemusictree/app-kit/player");
@@ -2473,7 +2452,7 @@ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
2473
2452
  var import_fa5 = require("react-icons/fa");
2474
2453
  var import_player8 = require("@behindthemusictree/app-kit/player");
2475
2454
  var import_player9 = require("@behindthemusictree/app-kit/player");
2476
- var import_jsx_runtime43 = require("react/jsx-runtime");
2455
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2477
2456
  function TrackPositionPlayPause({
2478
2457
  position,
2479
2458
  uuid,
@@ -2487,14 +2466,14 @@ function TrackPositionPlayPause({
2487
2466
  const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PLAYING;
2488
2467
  const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PAUSED;
2489
2468
  const isCurrentTrackActive = isPlaying || isPaused;
2490
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2469
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2491
2470
  "div",
2492
2471
  {
2493
2472
  className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
2494
2473
  style: { minWidth: "4rem", maxWidth: "4rem" },
2495
2474
  onClick: handlePlayPauseClick,
2496
2475
  children: [
2497
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2476
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2498
2477
  "span",
2499
2478
  {
2500
2479
  className: "track-position-play-pause__bars",
@@ -2502,18 +2481,18 @@ function TrackPositionPlayPause({
2502
2481
  role: "img",
2503
2482
  "aria-label": isPlaying ? "Playing" : "Paused",
2504
2483
  children: [
2505
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "track-position-play-pause__bar" }),
2506
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "track-position-play-pause__bar" }),
2507
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "track-position-play-pause__bar" })
2484
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__bar" }),
2485
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__bar" }),
2486
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__bar" })
2508
2487
  ]
2509
2488
  }
2510
2489
  ) : position }),
2511
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2490
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2512
2491
  "div",
2513
2492
  {
2514
2493
  className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
2515
2494
  "aria-hidden": "true",
2516
- children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_fa5.FaPlay, {})
2495
+ children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_fa5.FaPlay, {})
2517
2496
  }
2518
2497
  )
2519
2498
  ]
@@ -2524,7 +2503,7 @@ function TrackPositionPlayPause({
2524
2503
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2525
2504
  var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
2526
2505
  var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
2527
- var import_jsx_runtime44 = require("react/jsx-runtime");
2506
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2528
2507
  function TrackItem({
2529
2508
  track,
2530
2509
  position,
@@ -2548,12 +2527,12 @@ function TrackItem({
2548
2527
  loadTrackForPlayer(track.uuid);
2549
2528
  }
2550
2529
  };
2551
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2530
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2552
2531
  "div",
2553
2532
  {
2554
2533
  className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
2555
2534
  children: [
2556
- isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2535
+ isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2557
2536
  "div",
2558
2537
  {
2559
2538
  className: "flex items-center justify-center text-lg w-16",
@@ -2561,7 +2540,7 @@ function TrackItem({
2561
2540
  "aria-disabled": "true",
2562
2541
  children: position
2563
2542
  }
2564
- ) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2543
+ ) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2565
2544
  TrackPositionPlayPause,
2566
2545
  {
2567
2546
  position,
@@ -2570,12 +2549,12 @@ function TrackItem({
2570
2549
  isSelected: selectedTrack?.uuid === track.uuid
2571
2550
  }
2572
2551
  ),
2573
- /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2574
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime44.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_runtime44.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2575
- track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree25.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
2552
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2553
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime45.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_runtime45.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2554
+ track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree25.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
2576
2555
  ] }),
2577
- /* @__PURE__ */ (0, import_jsx_runtime44.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_runtime44.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
2578
- renderDuration && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2556
+ /* @__PURE__ */ (0, import_jsx_runtime45.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_runtime45.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
2557
+ renderDuration && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2579
2558
  "div",
2580
2559
  {
2581
2560
  className: "duration flex text-base w-16 items-center justify-center",
@@ -2583,15 +2562,15 @@ function TrackItem({
2583
2562
  children: renderDuration(track)
2584
2563
  }
2585
2564
  ),
2586
- renderActions && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2565
+ renderActions && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2587
2566
  ]
2588
2567
  }
2589
2568
  );
2590
2569
  }
2591
2570
 
2592
2571
  // src/genre-tree/artist/ArtistSongs.tsx
2593
- var import_jsx_runtime45 = require("react/jsx-runtime");
2594
- var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
2572
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2573
+ var SongPageSchema2 = (0, import_transport8.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
2595
2574
  var SORTS = [
2596
2575
  { ordering: "title", label: "Title" },
2597
2576
  { ordering: "-albumYear", label: "Year" }
@@ -2601,7 +2580,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2601
2580
  const [ordering, setOrdering] = (0, import_react17.useState)("title");
2602
2581
  const { playNewTrackListFromArtist } = (0, import_genre_tree26.useTrackList)();
2603
2582
  const { playerTrackObject } = (0, import_player11.usePlayer)();
2604
- const { data, isPending, isError } = (0, import_transport9.usePlaybackQuery)({
2583
+ const { data, isPending, isError } = (0, import_transport8.usePlaybackQuery)({
2605
2584
  getBackendBaseUrl,
2606
2585
  queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
2607
2586
  queryFn: (fetchBound) => fetchBound(import_genre_tree26.libraryEndpoints.reference.songs.list(), true, false, void 0, {
@@ -2623,21 +2602,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2623
2602
  event.stopPropagation();
2624
2603
  void play(song.uuid);
2625
2604
  };
2626
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui19.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui19.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
2627
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
2628
- /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2605
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui19.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui19.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
2606
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
2607
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
2629
2608
  "button",
2630
2609
  {
2631
2610
  type: "button",
2632
2611
  onClick: () => void play(),
2633
2612
  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",
2634
2613
  children: [
2635
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react16.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2614
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react16.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2636
2615
  "Play all"
2637
2616
  ]
2638
2617
  }
2639
2618
  ),
2640
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2619
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2641
2620
  "button",
2642
2621
  {
2643
2622
  type: "button",
@@ -2652,7 +2631,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2652
2631
  sort.ordering
2653
2632
  )) })
2654
2633
  ] }),
2655
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2634
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2656
2635
  TrackItem,
2657
2636
  {
2658
2637
  track: song,
@@ -2661,12 +2640,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2661
2640
  renderSongLink
2662
2641
  }
2663
2642
  ) }, song.uuid)) }),
2664
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui19.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2643
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui19.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2665
2644
  ] }) });
2666
2645
  }
2667
2646
 
2668
2647
  // src/genre-tree/artist/useArtistPanel.tsx
2669
- var import_jsx_runtime46 = require("react/jsx-runtime");
2648
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2670
2649
  function useArtistPanel({
2671
2650
  uuid,
2672
2651
  scope,
@@ -2687,26 +2666,26 @@ function useArtistPanel({
2687
2666
  return {
2688
2667
  ...panel,
2689
2668
  title: "Artist",
2690
- content: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2691
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2692
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-4 w-32" }),
2693
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2694
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-4 w-24" }),
2695
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-16 mt-2.5" }),
2696
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-16" })
2669
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2670
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2671
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-4 w-32" }),
2672
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2673
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-4 w-24" }),
2674
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-16 mt-2.5" }),
2675
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-16" })
2697
2676
  ] })
2698
2677
  };
2699
2678
  case "not-found":
2700
2679
  return {
2701
2680
  ...panel,
2702
2681
  title: "Artist not found",
2703
- content: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2682
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2704
2683
  };
2705
2684
  case "error":
2706
2685
  return {
2707
2686
  ...panel,
2708
2687
  title: "Artist",
2709
- content: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2688
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2710
2689
  };
2711
2690
  case "success": {
2712
2691
  const { artist } = result;
@@ -2715,19 +2694,19 @@ function useArtistPanel({
2715
2694
  return {
2716
2695
  ...panel,
2717
2696
  title: artist.name,
2718
- content: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
2719
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2697
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2698
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2720
2699
  ArtistActionBand,
2721
2700
  {
2722
2701
  artist,
2723
- share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2702
+ share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2724
2703
  }
2725
2704
  ),
2726
- artist.image && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
2727
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ArtistHeader, { artist }),
2728
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2729
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2730
- artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2705
+ artist.image && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
2706
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistHeader, { artist }),
2707
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2708
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2709
+ artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2731
2710
  ArtistSongs,
2732
2711
  {
2733
2712
  artist: { uuid: artist.uuid, name: artist.name },
@@ -2737,7 +2716,7 @@ function useArtistPanel({
2737
2716
  }
2738
2717
  )
2739
2718
  ] }),
2740
- extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2719
+ extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2741
2720
  };
2742
2721
  }
2743
2722
  }
@@ -2753,7 +2732,7 @@ var import_lucide_react17 = require("lucide-react");
2753
2732
  var import_ui21 = require("@behindthemusictree/ui");
2754
2733
  var import_player12 = require("@behindthemusictree/app-kit/player");
2755
2734
  var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
2756
- var import_jsx_runtime47 = require("react/jsx-runtime");
2735
+ var import_jsx_runtime48 = require("react/jsx-runtime");
2757
2736
  var numberFormat = new Intl.NumberFormat("en");
2758
2737
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2759
2738
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
@@ -2761,27 +2740,27 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2761
2740
  const [open, setOpen] = (0, import_react18.useState)(false);
2762
2741
  const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
2763
2742
  let body = null;
2764
- if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.Skeleton, { className: "h-10", "aria-busy": "true" });
2765
- else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2743
+ if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-10", "aria-busy": "true" });
2744
+ else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2766
2745
  else if (history.status === "success")
2767
- body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("li", { className: "py-1.5 text-body", children: [
2768
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "font-medium", children: entry.action }),
2769
- entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2746
+ body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("li", { className: "py-1.5 text-body", children: [
2747
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "font-medium", children: entry.action }),
2748
+ entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2770
2749
  " ",
2771
2750
  "\u2014 ",
2772
2751
  entry.oldValue ?? "none",
2773
2752
  " \u2192 ",
2774
2753
  entry.newValue ?? "none"
2775
2754
  ] }) : null,
2776
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2755
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2777
2756
  entry.actorPseudo ?? "Anonymous",
2778
2757
  " \xB7 ",
2779
2758
  dateFormat.format(new Date(entry.createdOn))
2780
2759
  ] })
2781
2760
  ] }, entry.uuid)) });
2782
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2783
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui21.PANEL_LABEL_CLASS}`, children: "History" }),
2784
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "mt-1.5", children: body })
2761
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2762
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui21.PANEL_LABEL_CLASS}`, children: "History" }),
2763
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "mt-1.5", children: body })
2785
2764
  ] }) });
2786
2765
  }
2787
2766
  function SongPanelContent(props) {
@@ -2800,13 +2779,13 @@ function SongPanelContent(props) {
2800
2779
  const genre = song.genre;
2801
2780
  fields.push({
2802
2781
  label: "Genre",
2803
- value: onSelectGenre ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { children: [
2804
- index > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
2805
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2782
+ value: onSelectGenre ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { children: [
2783
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
2784
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2806
2785
  ] }, genrePlaylist.uuid)) }) : song.genre.name
2807
2786
  });
2808
2787
  } else {
2809
- fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2788
+ fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2810
2789
  }
2811
2790
  if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
2812
2791
  const viewCount = song.youtubeVideo?.viewCount;
@@ -2816,11 +2795,11 @@ function SongPanelContent(props) {
2816
2795
  const links = [];
2817
2796
  if (musicBrainzUrl)
2818
2797
  links.push(
2819
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2798
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2820
2799
  );
2821
2800
  if (song.youtubeVideo)
2822
2801
  links.push(
2823
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2802
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2824
2803
  "a",
2825
2804
  {
2826
2805
  href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
@@ -2835,31 +2814,31 @@ function SongPanelContent(props) {
2835
2814
  if (links.length > 0)
2836
2815
  fields.push({
2837
2816
  label: "Links",
2838
- value: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "flex gap-3", children: links })
2817
+ value: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "flex gap-3", children: links })
2839
2818
  });
2840
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2841
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_ui21.ActionBand, { children: [
2842
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2819
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
2820
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_ui21.ActionBand, { children: [
2821
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2843
2822
  import_ui21.ActionButton,
2844
2823
  {
2845
- icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react17.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2824
+ icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react17.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react17.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2846
2825
  label: isPlaying ? "Pause" : "Play",
2847
2826
  disabled: !!unplayableReason,
2848
2827
  title: unplayableReason ?? void 0,
2849
2828
  onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
2850
2829
  }
2851
2830
  ),
2852
- shareUrl && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2831
+ shareUrl && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2853
2832
  ] }) }),
2854
- song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2855
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.FieldList, { items: fields }),
2856
- song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2857
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2833
+ song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2834
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.FieldList, { items: fields }),
2835
+ song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2836
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2858
2837
  ] });
2859
2838
  }
2860
2839
 
2861
2840
  // src/genre-tree/song/useSongPanel.tsx
2862
- var import_jsx_runtime48 = require("react/jsx-runtime");
2841
+ var import_jsx_runtime49 = require("react/jsx-runtime");
2863
2842
  function useSongPanel({
2864
2843
  uuid,
2865
2844
  scope,
@@ -2880,24 +2859,24 @@ function useSongPanel({
2880
2859
  return {
2881
2860
  ...panel,
2882
2861
  title: "Song",
2883
- content: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2884
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2885
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-4 w-32" }),
2886
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2887
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-4 w-24" })
2862
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2863
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2864
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-4 w-32" }),
2865
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2866
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-4 w-24" })
2888
2867
  ] })
2889
2868
  };
2890
2869
  case "not-found":
2891
2870
  return {
2892
2871
  ...panel,
2893
2872
  title: "Song not found",
2894
- content: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2873
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2895
2874
  };
2896
2875
  case "error":
2897
2876
  return {
2898
2877
  ...panel,
2899
2878
  title: "Song",
2900
- content: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2879
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2901
2880
  };
2902
2881
  case "success": {
2903
2882
  const { song } = result;
@@ -2905,7 +2884,7 @@ function useSongPanel({
2905
2884
  return {
2906
2885
  ...panel,
2907
2886
  title: song.title,
2908
- content: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2887
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
2909
2888
  SongPanelContent,
2910
2889
  {
2911
2890
  song,
@@ -2958,12 +2937,12 @@ function useEntityPanel({
2958
2937
  // src/genre-tree/SizeMetricToggle.tsx
2959
2938
  var import_md2 = require("react-icons/md");
2960
2939
  var import_ui23 = require("@behindthemusictree/ui");
2961
- var import_jsx_runtime49 = require("react/jsx-runtime");
2940
+ var import_jsx_runtime50 = require("react/jsx-runtime");
2962
2941
  function SizeMetricToggle({
2963
2942
  value,
2964
2943
  onChange
2965
2944
  }) {
2966
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
2945
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
2967
2946
  import_ui23.SegmentedControl,
2968
2947
  {
2969
2948
  variant: "floating",
@@ -2971,15 +2950,15 @@ function SizeMetricToggle({
2971
2950
  value,
2972
2951
  onChange,
2973
2952
  options: [
2974
- { value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
2975
- { value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
2953
+ { value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
2954
+ { value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
2976
2955
  ]
2977
2956
  }
2978
2957
  );
2979
2958
  }
2980
2959
 
2981
2960
  // src/genre-tree/GenreTreeView.tsx
2982
- var import_jsx_runtime50 = require("react/jsx-runtime");
2961
+ var import_jsx_runtime51 = require("react/jsx-runtime");
2983
2962
  var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
2984
2963
  var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
2985
2964
  var noop = () => {
@@ -3071,6 +3050,7 @@ function GenreTreeView({
3071
3050
  isFetching: isTreeFetching,
3072
3051
  genrePlaylistByNodeId,
3073
3052
  visibleGenrePlaylists,
3053
+ focusUuids,
3074
3054
  groupedGenrePlaylistsByRoot,
3075
3055
  selectedGenreUuid,
3076
3056
  selectGenre,
@@ -3130,14 +3110,14 @@ function GenreTreeView({
3130
3110
  const openSongRef = (0, import_react19.useRef)(openSong);
3131
3111
  openSongRef.current = openSong;
3132
3112
  const resolvedRenderSongLink = (0, import_react19.useCallback)(
3133
- (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
3113
+ (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
3134
3114
  [renderSongLink]
3135
3115
  );
3136
3116
  const [expandedSongUuid, setExpandedSongUuid] = (0, import_react19.useState)(null);
3137
3117
  const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react19.useState)(null);
3138
3118
  const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
3139
3119
  const songBaseUrl = getBackendBaseUrl;
3140
- const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
3120
+ const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
3141
3121
  const getGenrePath = (genre) => {
3142
3122
  const path = [];
3143
3123
  let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
@@ -3179,7 +3159,7 @@ function GenreTreeView({
3179
3159
  selectGenrePlaylist(genrePlaylist2);
3180
3160
  }
3181
3161
  });
3182
- const builtInPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : entityPanel;
3162
+ const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
3183
3163
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react19.useState)(false);
3184
3164
  const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react19.useState)(false);
3185
3165
  const isCollapsed = collapsed ?? uncontrolledCollapsed;
@@ -3207,7 +3187,7 @@ function GenreTreeView({
3207
3187
  const obscuredInset = (0, import_ui24.useObscuredInset)(panelRef);
3208
3188
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
3209
3189
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
3210
- const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
3190
+ const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
3211
3191
  const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
3212
3192
  treeNodes,
3213
3193
  !overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
@@ -3218,7 +3198,7 @@ function GenreTreeView({
3218
3198
  setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
3219
3199
  }
3220
3200
  const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
3221
- const { showPopup, hidePopup } = (0, import_popup6.usePopup)();
3201
+ const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
3222
3202
  const reopenPanel = () => {
3223
3203
  setUncontrolledCollapsed(false);
3224
3204
  onCollapsedChange?.(false);
@@ -3269,7 +3249,7 @@ function GenreTreeView({
3269
3249
  onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
3270
3250
  onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
3271
3251
  onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
3272
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3252
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3273
3253
  ChangeParentPopup,
3274
3254
  {
3275
3255
  genrePlaylist: { ...genrePlaylist, criteria },
@@ -3303,15 +3283,15 @@ function GenreTreeView({
3303
3283
  const isRotationControlled = controlledAllowWheelRotation !== void 0;
3304
3284
  const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
3305
3285
  let activePanel = null;
3306
- if (overlayPanel) {
3286
+ if (otherPanel) {
3307
3287
  activePanel = {
3308
- title: overlayPanel.title,
3309
- content: overlayPanel.content,
3310
- aside: overlayPanel.extraContent,
3311
- expanded: overlayPanel.expanded ?? false,
3312
- onExpandedChange: overlayPanel.onExpandedChange,
3313
- onClose: overlayPanel.onClose,
3314
- titleInSearch: false
3288
+ title: otherPanel.title,
3289
+ content: otherPanel.content,
3290
+ aside: otherPanel.extraContent,
3291
+ expanded: otherPanel.expanded ?? false,
3292
+ onExpandedChange: otherPanel.onExpandedChange,
3293
+ onClose: otherPanel.onClose,
3294
+ searchName: otherPanel.title
3315
3295
  };
3316
3296
  } else if (selectedGenreUuid) {
3317
3297
  activePanel = {
@@ -3321,38 +3301,28 @@ function GenreTreeView({
3321
3301
  expanded: isExpanded,
3322
3302
  onExpandedChange: changeExpanded,
3323
3303
  onClose: clearSelection,
3324
- titleInSearch: true,
3304
+ searchName: genreContent.name,
3325
3305
  headerAccent: genreContent.headerAccent
3326
3306
  };
3327
- } else if (builtInPanel) {
3328
- activePanel = {
3329
- title: builtInPanel.title,
3330
- content: builtInPanel.content,
3331
- aside: builtInPanel.extraContent,
3332
- expanded: builtInPanel.expanded,
3333
- onExpandedChange: builtInPanel.onExpandedChange,
3334
- onClose: builtInPanel.onClose,
3335
- titleInSearch: true
3336
- };
3337
3307
  }
3338
3308
  const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
3339
3309
  const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
3340
3310
  edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
3341
3311
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
3342
3312
  } : void 0;
3343
- const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3313
+ const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3344
3314
  import_ui24.IconButton,
3345
3315
  {
3346
3316
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
3347
3317
  style: activePanel.headerAccent && { color: "inherit" },
3348
3318
  onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
3349
- children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_lucide_react19.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_lucide_react19.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
3319
+ children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react19.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react19.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
3350
3320
  }
3351
3321
  ) : null;
3352
3322
  const renderArtistFilter = () => {
3353
3323
  if (isLoading || !filterArtist) return null;
3354
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
3355
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3324
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
3325
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3356
3326
  "button",
3357
3327
  {
3358
3328
  type: "button",
@@ -3364,7 +3334,7 @@ function GenreTreeView({
3364
3334
  children: filterArtist.name
3365
3335
  }
3366
3336
  ),
3367
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3337
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3368
3338
  "button",
3369
3339
  {
3370
3340
  type: "button",
@@ -3376,8 +3346,8 @@ function GenreTreeView({
3376
3346
  )
3377
3347
  ] });
3378
3348
  };
3379
- const actions = /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
3380
- !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3349
+ const actions = /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
3350
+ !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3381
3351
  GenreTreeSearch,
3382
3352
  {
3383
3353
  artistBaseUrl,
@@ -3400,9 +3370,9 @@ function GenreTreeView({
3400
3370
  if (songUuid) changeSong(null);
3401
3371
  selectGenre(uuid);
3402
3372
  },
3403
- selectedName: selectedGenreUuid ? genreContent.name : typeof builtInPanel?.title === "string" ? builtInPanel.title : null,
3373
+ selectedName: activePanel?.searchName ?? null,
3404
3374
  onClear: activePanel?.onClose,
3405
- showClear: activePanel?.titleInSearch ?? false,
3375
+ showClear: activePanel !== null,
3406
3376
  clearLabel: "Close panel",
3407
3377
  onPrevious: handlePrevious,
3408
3378
  onQueryChange: onSearchQueryChange,
@@ -3410,9 +3380,9 @@ function GenreTreeView({
3410
3380
  extraGroups: extraSearchGroups
3411
3381
  }
3412
3382
  ),
3413
- !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3414
- /* @__PURE__ */ (0, import_jsx_runtime50.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: [
3415
- !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3383
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3384
+ /* @__PURE__ */ (0, import_jsx_runtime51.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: [
3385
+ !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3416
3386
  import_ui24.SegmentedControl,
3417
3387
  {
3418
3388
  "aria-label": "Tree view mode",
@@ -3426,13 +3396,13 @@ function GenreTreeView({
3426
3396
  ]
3427
3397
  }
3428
3398
  ),
3429
- !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3399
+ !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3430
3400
  "div",
3431
3401
  {
3432
3402
  className: "flex items-center gap-1",
3433
3403
  role: "group",
3434
3404
  "aria-label": "Tree display options",
3435
- children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3405
+ children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3436
3406
  import_ui24.Button,
3437
3407
  {
3438
3408
  variant: allowWheelRotation ? "default" : "outline",
@@ -3443,7 +3413,7 @@ function GenreTreeView({
3443
3413
  )
3444
3414
  }
3445
3415
  ),
3446
- !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3416
+ !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3447
3417
  import_ui24.IconTextButton,
3448
3418
  {
3449
3419
  icon: import_lucide_react18.Plus,
@@ -3453,42 +3423,43 @@ function GenreTreeView({
3453
3423
  )
3454
3424
  ] })
3455
3425
  ] });
3456
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3426
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
3457
3427
  "div",
3458
3428
  {
3459
3429
  "data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
3460
3430
  className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
3461
3431
  children: [
3462
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3432
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3463
3433
  "div",
3464
3434
  {
3465
3435
  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"}`,
3466
3436
  children: actions
3467
3437
  }
3468
3438
  ),
3469
- !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3439
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3470
3440
  "div",
3471
3441
  {
3472
3442
  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"}`,
3473
3443
  children: renderArtistFilter()
3474
3444
  }
3475
3445
  ),
3476
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
3477
- !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
3478
- isLoading ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
3446
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
3447
+ !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
3448
+ isLoading ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
3479
3449
  "Couldn't load the genre tree.",
3480
3450
  " ",
3481
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3451
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3482
3452
  ] }) : viewMode === "wheel" ? (
3483
3453
  // The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
3484
3454
  // and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
3485
3455
  // Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
3486
3456
  // The panel's own Zoom in button is hidden: the search bar carries it.
3487
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3457
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3488
3458
  GenrePlaylistTreeWheel,
3489
3459
  {
3490
3460
  scope,
3491
3461
  genrePlaylists: visibleGenrePlaylists,
3462
+ focusUuids,
3492
3463
  handleGenreCreationAction,
3493
3464
  handleGenreRenameAction,
3494
3465
  additionalActions,
@@ -3501,9 +3472,9 @@ function GenreTreeView({
3501
3472
  allowWheelRotation,
3502
3473
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3503
3474
  busy,
3504
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-2", children: [
3475
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center gap-2", children: [
3505
3476
  floatingControlsLeading,
3506
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3477
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3507
3478
  ] }) : void 0,
3508
3479
  defaultViewport,
3509
3480
  viewportRestore,
@@ -3512,11 +3483,12 @@ function GenreTreeView({
3512
3483
  ) }) })
3513
3484
  ) : viewMode === "pop-core" ? (
3514
3485
  // Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
3515
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3486
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3516
3487
  GenrePlaylistTreeWheelRadialPopCore,
3517
3488
  {
3518
3489
  scope,
3519
3490
  genrePlaylists: visibleGenrePlaylists,
3491
+ focusUuids,
3520
3492
  handleGenreCreationAction,
3521
3493
  handleGenreRenameAction,
3522
3494
  additionalActions,
@@ -3529,9 +3501,9 @@ function GenreTreeView({
3529
3501
  allowWheelRotation,
3530
3502
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3531
3503
  busy,
3532
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-2", children: [
3504
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center gap-2", children: [
3533
3505
  floatingControlsLeading,
3534
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3506
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3535
3507
  ] }) : void 0,
3536
3508
  defaultViewport,
3537
3509
  viewportRestore,
@@ -3539,12 +3511,13 @@ function GenreTreeView({
3539
3511
  ...focus
3540
3512
  }
3541
3513
  ) }) })
3542
- ) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3514
+ ) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3543
3515
  GenrePlaylistTreeWheelRadialPopCore,
3544
3516
  {
3545
3517
  outline: true,
3546
3518
  scope,
3547
3519
  genrePlaylists: visibleGenrePlaylists,
3520
+ focusUuids,
3548
3521
  handleGenreCreationAction,
3549
3522
  handleGenreRenameAction,
3550
3523
  additionalActions,
@@ -3555,13 +3528,13 @@ function GenreTreeView({
3555
3528
  selectedNodeId,
3556
3529
  readOnly
3557
3530
  }
3558
- ) }) : /* @__PURE__ */ (0, import_jsx_runtime50.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(
3531
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime51.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(
3559
3532
  ([uuid, genrePlaylistTreePerRoot]) => {
3560
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3533
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3561
3534
  "div",
3562
3535
  {
3563
3536
  className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
3564
- children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3537
+ children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3565
3538
  GenrePlaylistTreePerRoot,
3566
3539
  {
3567
3540
  scope,
@@ -3574,6 +3547,7 @@ function GenreTreeView({
3574
3547
  onNodeHover: handleNodeHover,
3575
3548
  ...hostPanel,
3576
3549
  playback,
3550
+ focusUuids,
3577
3551
  selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
3578
3552
  readOnly,
3579
3553
  wheelZoom: "modifier",
@@ -3587,13 +3561,13 @@ function GenreTreeView({
3587
3561
  );
3588
3562
  }
3589
3563
  ) }),
3590
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3564
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3591
3565
  import_ui24.DetailPanel,
3592
3566
  {
3593
3567
  ref: panelRef,
3594
3568
  side: "left",
3595
3569
  title: activePanel?.title ?? "",
3596
- titleInSearch: activePanel?.titleInSearch,
3570
+ titleInSearch: !isLoading && !isError,
3597
3571
  headerAccent: activePanel?.headerAccent,
3598
3572
  headerActions: expandButton,
3599
3573
  onClose: activePanel?.onClose ?? noop,
@@ -3602,8 +3576,7 @@ function GenreTreeView({
3602
3576
  onExpandedChange: activePanel?.onExpandedChange,
3603
3577
  aside: activePanel?.aside,
3604
3578
  edgeTab: detailTab,
3605
- headerClassName: activePanel?.titleInSearch ? void 0 : "pt-search-clear",
3606
- children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
3579
+ children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
3607
3580
  activePanel.content,
3608
3581
  activePanel.aside
3609
3582
  ] }) : activePanel?.content
@@ -3624,7 +3597,7 @@ var import_lucide_react20 = require("lucide-react");
3624
3597
  var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
3625
3598
  var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
3626
3599
  var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
3627
- var import_jsx_runtime51 = require("react/jsx-runtime");
3600
+ var import_jsx_runtime52 = require("react/jsx-runtime");
3628
3601
  var useBrowserLayoutEffect = typeof window === "undefined" ? import_react20.useEffect : import_react20.useLayoutEffect;
3629
3602
  function TrackListSidebar({
3630
3603
  className: className2 = "",
@@ -3726,25 +3699,25 @@ function TrackListSidebar({
3726
3699
  }
3727
3700
  };
3728
3701
  const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
3729
- return trackList ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
3702
+ return trackList ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3730
3703
  "div",
3731
3704
  {
3732
3705
  "data-theme": "dark",
3733
3706
  className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
3734
3707
  style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
3735
3708
  children: [
3736
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3737
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3738
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "origin flex text-xl", children: [
3739
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3740
- /* @__PURE__ */ (0, import_jsx_runtime51.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_runtime51.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3709
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3710
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3711
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "origin flex text-xl", children: [
3712
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3713
+ /* @__PURE__ */ (0, import_jsx_runtime52.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_runtime52.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3741
3714
  ] }),
3742
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3715
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3743
3716
  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 `,
3744
3717
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3745
3718
  ] })
3746
3719
  ] }),
3747
- (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3720
+ (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3748
3721
  "button",
3749
3722
  {
3750
3723
  type: "button",
@@ -3753,11 +3726,11 @@ function TrackListSidebar({
3753
3726
  "aria-expanded": !isCollapsed,
3754
3727
  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"}`,
3755
3728
  onClick: isCollapsed ? onExpand : onMinimize,
3756
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react20.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3729
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react20.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3757
3730
  }
3758
3731
  )
3759
3732
  ] }),
3760
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
3733
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3761
3734
  "ul",
3762
3735
  {
3763
3736
  ref: trackListRef,
@@ -3767,7 +3740,7 @@ function TrackListSidebar({
3767
3740
  onPointerDown: markUserScrollIntent,
3768
3741
  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",
3769
3742
  children: [
3770
- trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3743
+ trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3771
3744
  TrackItem,
3772
3745
  {
3773
3746
  track,
@@ -3778,7 +3751,7 @@ function TrackListSidebar({
3778
3751
  renderSongLink
3779
3752
  }
3780
3753
  ) }, track.uuid)),
3781
- hasMore && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3754
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3782
3755
  ]
3783
3756
  }
3784
3757
  )
@@ -3798,7 +3771,7 @@ var import_ui25 = require("@behindthemusictree/ui");
3798
3771
  var import_lucide_react21 = require("lucide-react");
3799
3772
  var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
3800
3773
  var import_player13 = require("@behindthemusictree/app-kit/player");
3801
- var import_jsx_runtime52 = require("react/jsx-runtime");
3774
+ var import_jsx_runtime53 = require("react/jsx-runtime");
3802
3775
  function PlayerControls() {
3803
3776
  const {
3804
3777
  duration,
@@ -3817,15 +3790,15 @@ function PlayerControls() {
3817
3790
  const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
3818
3791
  const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
3819
3792
  const isPlaying = playState === import_player13.PlayStates.PLAYING;
3820
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3793
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3821
3794
  "section",
3822
3795
  {
3823
3796
  "aria-label": "Player controls",
3824
3797
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3825
3798
  children: [
3826
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3827
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree33.formatTime)(safeCurrentTime) }),
3828
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3799
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3800
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree33.formatTime)(safeCurrentTime) }),
3801
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3829
3802
  "input",
3830
3803
  {
3831
3804
  "aria-label": "Seek",
@@ -3839,11 +3812,11 @@ function PlayerControls() {
3839
3812
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3840
3813
  }
3841
3814
  ),
3842
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3815
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3843
3816
  ] }),
3844
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3845
- /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3846
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3817
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3818
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3819
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3847
3820
  "button",
3848
3821
  {
3849
3822
  type: "button",
@@ -3852,10 +3825,10 @@ function PlayerControls() {
3852
3825
  disabled: !isReady || !hasPreviousTrack,
3853
3826
  onClick: playPreviousTrack,
3854
3827
  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",
3855
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react21.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3828
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react21.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3856
3829
  }
3857
3830
  ),
3858
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3831
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3859
3832
  "button",
3860
3833
  {
3861
3834
  type: "button",
@@ -3864,10 +3837,10 @@ function PlayerControls() {
3864
3837
  disabled: !isReady,
3865
3838
  onClick: handlePlayPauseAction,
3866
3839
  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",
3867
- children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react21.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react21.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3840
+ children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react21.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react21.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3868
3841
  }
3869
3842
  ),
3870
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3843
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3871
3844
  "button",
3872
3845
  {
3873
3846
  type: "button",
@@ -3876,14 +3849,14 @@ function PlayerControls() {
3876
3849
  disabled: !isReady || !hasNextTrack,
3877
3850
  onClick: playNextTrack,
3878
3851
  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",
3879
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react21.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3852
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react21.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3880
3853
  }
3881
3854
  )
3882
3855
  ] }),
3883
- /* @__PURE__ */ (0, import_jsx_runtime52.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: [
3884
- volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react21.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react21.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3885
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { className: "sr-only", children: "Volume" }),
3886
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3856
+ /* @__PURE__ */ (0, import_jsx_runtime53.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: [
3857
+ volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react21.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react21.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3858
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "sr-only", children: "Volume" }),
3859
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3887
3860
  "input",
3888
3861
  {
3889
3862
  "aria-label": "Volume",
@@ -3905,7 +3878,7 @@ function PlayerControls() {
3905
3878
  }
3906
3879
 
3907
3880
  // src/genre-tree/PlayerPanel.tsx
3908
- var import_jsx_runtime53 = require("react/jsx-runtime");
3881
+ var import_jsx_runtime54 = require("react/jsx-runtime");
3909
3882
  var PANEL_ID = "player-panel";
3910
3883
  var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
3911
3884
  function PlayerPanel({
@@ -3945,7 +3918,7 @@ function PlayerPanel({
3945
3918
  onClick: () => setIsExtendRequested(true),
3946
3919
  expanded: false
3947
3920
  } : null;
3948
- return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3921
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
3949
3922
  "div",
3950
3923
  {
3951
3924
  id: PANEL_ID,
@@ -3958,7 +3931,7 @@ function PlayerPanel({
3958
3931
  ),
3959
3932
  style: isExtended ? { width: extendedWidth } : void 0,
3960
3933
  children: [
3961
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3934
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
3962
3935
  import_ui25.PanelEdgeTab,
3963
3936
  {
3964
3937
  side: "right",
@@ -3968,7 +3941,7 @@ function PlayerPanel({
3968
3941
  className: "absolute right-full top-1/2 -translate-y-1/2"
3969
3942
  }
3970
3943
  ),
3971
- /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3944
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
3972
3945
  "div",
3973
3946
  {
3974
3947
  className: (0, import_tailwind_merge.twMerge)(
@@ -3976,20 +3949,20 @@ function PlayerPanel({
3976
3949
  isExtended ? "flex-row" : "flex-col"
3977
3950
  ),
3978
3951
  children: [
3979
- /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3980
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3981
- /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "relative w-full shrink-0", children: [
3982
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3983
- playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3952
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3953
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3954
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "relative w-full shrink-0", children: [
3955
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3956
+ playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3984
3957
  playerTrackObject.loadError,
3985
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3958
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3986
3959
  void retryPlayback();
3987
3960
  }, children: "Retry" })
3988
3961
  ] })
3989
3962
  ] }),
3990
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(PlayerControls, {})
3963
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PlayerControls, {})
3991
3964
  ] }),
3992
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3965
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
3993
3966
  TrackListSidebar,
3994
3967
  {
3995
3968
  layout: "inline",
@@ -4016,11 +3989,11 @@ function PlayerPanel({
4016
3989
  var import_uuid = require("uuid");
4017
3990
  var import_ai = require("react-icons/ai");
4018
3991
  var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
4019
- var import_jsx_runtime54 = require("react/jsx-runtime");
3992
+ var import_jsx_runtime55 = require("react/jsx-runtime");
4020
3993
  function Rating({ rating, handleChange }) {
4021
3994
  const instanceId = (0, import_uuid.v4)();
4022
- return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
4023
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
3995
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
3996
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4024
3997
  "input",
4025
3998
  {
4026
3999
  id: `star-${instanceId}-${i}`,
@@ -4032,16 +4005,16 @@ function Rating({ rating, handleChange }) {
4032
4005
  onChange: handleChange
4033
4006
  }
4034
4007
  ),
4035
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex", children: [
4036
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
4008
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex", children: [
4009
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4037
4010
  import_ai.AiOutlineStar,
4038
4011
  {
4039
4012
  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)]"}`
4040
4013
  }
4041
4014
  ),
4042
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "flex-grow w-8" }),
4015
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "flex-grow w-8" }),
4043
4016
  " "
4044
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
4017
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4045
4018
  import_ai.AiFillStar,
4046
4019
  {
4047
4020
  className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
@@ -4053,7 +4026,7 @@ function Rating({ rating, handleChange }) {
4053
4026
  // src/genre-tree/EntityDetailPanel.tsx
4054
4027
  var import_react22 = require("react");
4055
4028
  var import_ui26 = require("@behindthemusictree/ui");
4056
- var import_jsx_runtime55 = require("react/jsx-runtime");
4029
+ var import_jsx_runtime56 = require("react/jsx-runtime");
4057
4030
  function EntityDetailPanel({
4058
4031
  scope,
4059
4032
  getBackendBaseUrl,
@@ -4076,8 +4049,8 @@ function EntityDetailPanel({
4076
4049
  getBackendBaseUrl,
4077
4050
  getArtistBackendBaseUrl,
4078
4051
  getShareUrl,
4079
- renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
4080
- renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
4052
+ renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
4053
+ renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
4081
4054
  song: {
4082
4055
  uuid: songUuid,
4083
4056
  expanded: songUuid !== null && expandedSongUuid === songUuid,
@@ -4093,7 +4066,7 @@ function EntityDetailPanel({
4093
4066
  ...navigation
4094
4067
  });
4095
4068
  if (!panel) return null;
4096
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4069
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
4097
4070
  import_ui26.DetailPanel,
4098
4071
  {
4099
4072
  side: "left",
@@ -4105,7 +4078,7 @@ function EntityDetailPanel({
4105
4078
  onExpandedChange: panel.onExpandedChange,
4106
4079
  aside: panel.extraContent,
4107
4080
  edgeTab,
4108
- children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
4081
+ children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
4109
4082
  panel.content,
4110
4083
  panel.extraContent
4111
4084
  ] }) : panel.content