@behindthemusictree/app-kit-ui 39.0.0 → 40.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -591,12 +591,12 @@ function AuthCallbackHandler({ renderStatus, ...options }) {
591
591
  // src/genre-tree/GenreTreeView.tsx
592
592
  var import_react19 = require("react");
593
593
  var import_react_query3 = require("@tanstack/react-query");
594
+ var import_lucide_react17 = require("lucide-react");
594
595
  var import_lucide_react18 = require("lucide-react");
595
- 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,27 +621,45 @@ 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
 
629
+ // src/genre-tree/playlist-tree/toGenreNodeStyles.ts
630
+ function toGenreNodeStyles(relations, genrePlaylists) {
631
+ const styles = /* @__PURE__ */ new Map();
632
+ if (!relations) return styles;
633
+ const genreUuidByPlaylistUuid = new Map(
634
+ genrePlaylists.flatMap((genrePlaylist) => genrePlaylist.criteria ? [[genrePlaylist.uuid, genrePlaylist.criteria.uuid]] : [])
635
+ );
636
+ const drawn = [relations, relations.parentNode, ...relations.childNodes, ...relations.ancestorNodes];
637
+ for (const entry of drawn) {
638
+ const genreUuid = entry && genreUuidByPlaylistUuid.get(entry.node.id);
639
+ if (entry && genreUuid) styles.set(genreUuid, { fill: entry.fill, textColor: entry.textColor });
640
+ }
641
+ return styles;
642
+ }
643
+
628
644
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
629
645
  var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
630
646
  var import_react4 = require("react");
631
647
  var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
632
648
  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
649
 
636
- // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
650
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
637
651
  var import_react3 = require("react");
638
652
  var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
653
+ var import_popup2 = require("@behindthemusictree/app-kit/popup");
654
+ var import_transport3 = require("@behindthemusictree/app-kit/transport");
655
+ var import_jsx_runtime12 = require("react/jsx-runtime");
639
656
  function useGenrePlaylistPlayback({
640
657
  scope,
641
658
  genrePlaylists,
642
659
  artist = null
643
660
  }) {
644
661
  const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
662
+ const { showPopup } = (0, import_popup2.usePopup)();
645
663
  const origin = trackList?.origin;
646
664
  const playing = (0, import_react3.useMemo)(() => {
647
665
  if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
@@ -653,14 +671,20 @@ function useGenrePlaylistPlayback({
653
671
  }, [origin, artist, genrePlaylists, scope]);
654
672
  const play = (0, import_react3.useCallback)(async (playlist, requested) => {
655
673
  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]);
674
+ try {
675
+ const selection = scope === "reference" && playlist.criteria !== null ? requested ?? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
676
+ await (selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope));
677
+ } catch (error) {
678
+ console.error("Failed to load genre playlist tracks:", error);
679
+ if (error instanceof import_genre_tree2.TracksFailureAlreadyReportedError) return;
680
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport3.AppError ? error.code : import_transport3.ErrorCode.CLIENT_UNKNOWN }));
681
+ }
682
+ }, [artist, scope, playNewTrackListFromGenrePlaylist, showPopup]);
659
683
  return (0, import_react3.useMemo)(() => ({ playing, play }), [playing, play]);
660
684
  }
661
685
 
662
686
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
663
- var import_jsx_runtime12 = require("react/jsx-runtime");
687
+ var import_jsx_runtime13 = require("react/jsx-runtime");
664
688
  function GenrePlaylistTreePerRoot({
665
689
  scope,
666
690
  className: className2,
@@ -679,25 +703,16 @@ function GenrePlaylistTreePerRoot({
679
703
  viewportRestore,
680
704
  onViewportChange,
681
705
  playback,
706
+ focusUuids,
682
707
  ...hostPanel
683
708
  }) {
684
709
  const { isPlaying, setIsPlaying } = (0, import_player.usePlayer)();
685
710
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
686
711
  const { playing, play } = playback ?? defaultPlayback;
687
712
  const playingNodeId = playing?.uuid ?? null;
688
- const { showPopup } = (0, import_popup2.usePopup)();
689
713
  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]
714
+ () => toTreeNodes(genrePlaylistTreePerRoot, "songs", focusUuids),
715
+ [genrePlaylistTreePerRoot, focusUuids]
701
716
  );
702
717
  const handlePlayPause = (0, import_react4.useCallback)(
703
718
  (nodeId) => {
@@ -710,18 +725,14 @@ function GenrePlaylistTreePerRoot({
710
725
  if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
711
726
  return;
712
727
  }
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
- });
728
+ void play(genrePlaylist);
717
729
  },
718
730
  [
719
731
  genrePlaylistTreePerRoot,
720
732
  playingNodeId,
721
733
  isPlaying,
722
734
  setIsPlaying,
723
- play,
724
- showPopup
735
+ play
725
736
  ]
726
737
  );
727
738
  const handleAddChild = (0, import_react4.useCallback)(
@@ -744,7 +755,7 @@ function GenrePlaylistTreePerRoot({
744
755
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
745
756
  }
746
757
  }, []);
747
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
758
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
748
759
  import_genre_tree_view.GenreTree,
749
760
  {
750
761
  className: className2,
@@ -775,9 +786,7 @@ var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
775
786
  var import_react5 = require("react");
776
787
  var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
777
788
  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");
789
+ var import_jsx_runtime14 = require("react/jsx-runtime");
781
790
  function GenrePlaylistTreeWheel({
782
791
  scope,
783
792
  className: className2,
@@ -798,24 +807,16 @@ function GenrePlaylistTreeWheel({
798
807
  viewportRestore,
799
808
  onViewportChange,
800
809
  playback,
810
+ focusUuids,
801
811
  ...hostPanel
802
812
  }) {
803
813
  const { isPlaying, setIsPlaying } = (0, import_player2.usePlayer)();
804
814
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
805
815
  const { playing, play } = playback ?? defaultPlayback;
806
816
  const playingNodeId = playing?.uuid ?? null;
807
- const { showPopup } = (0, import_popup3.usePopup)();
808
817
  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]
818
+ () => toTreeNodes(genrePlaylists, sizeMetric, focusUuids),
819
+ [genrePlaylists, sizeMetric, focusUuids]
819
820
  );
820
821
  const handlePlayPause = (0, import_react5.useCallback)(
821
822
  (nodeId) => {
@@ -828,18 +829,14 @@ function GenrePlaylistTreeWheel({
828
829
  if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
829
830
  return;
830
831
  }
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
- });
832
+ void play(genrePlaylist);
835
833
  },
836
834
  [
837
835
  genrePlaylists,
838
836
  playingNodeId,
839
837
  isPlaying,
840
838
  setIsPlaying,
841
- play,
842
- showPopup
839
+ play
843
840
  ]
844
841
  );
845
842
  const handleAddChild = (0, import_react5.useCallback)(
@@ -862,7 +859,7 @@ function GenrePlaylistTreeWheel({
862
859
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
863
860
  }
864
861
  }, []);
865
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
862
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
866
863
  import_genre_tree_view2.GenreTreeWheel,
867
864
  {
868
865
  className: className2,
@@ -894,7 +891,7 @@ var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
894
891
  var import_react6 = require("react");
895
892
  var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
896
893
  var import_player3 = require("@behindthemusictree/app-kit/player");
897
- var import_jsx_runtime14 = require("react/jsx-runtime");
894
+ var import_jsx_runtime15 = require("react/jsx-runtime");
898
895
  function GenrePlaylistTreeWheelRadialPopCore(props) {
899
896
  const {
900
897
  scope,
@@ -927,7 +924,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
927
924
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
928
925
  const { playing, play } = playback ?? defaultPlayback;
929
926
  const playingNodeId = playing?.uuid ?? null;
930
- const nodes = (0, import_react6.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
927
+ const nodes = (0, import_react6.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric, props.focusUuids), [genrePlaylists, sizeMetric, props.focusUuids]);
931
928
  const handlePlayPause = (0, import_react6.useCallback)(
932
929
  (nodeId) => {
933
930
  const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
@@ -939,9 +936,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
939
936
  if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
940
937
  return;
941
938
  }
942
- play(genrePlaylist).catch((error) => {
943
- console.error("Failed to load genre playlist tracks:", error);
944
- });
939
+ void play(genrePlaylist);
945
940
  },
946
941
  [genrePlaylists, playingNodeId, isPlaying, setIsPlaying, play]
947
942
  );
@@ -981,7 +976,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
981
976
  onSelectedNodeChange,
982
977
  ...hostPanel
983
978
  };
984
- return outline ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
979
+ return outline ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
985
980
  import_genre_tree_view3.GenreTreeWheelRadialPopCore,
986
981
  {
987
982
  ...treeProps,
@@ -998,7 +993,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
998
993
 
999
994
  // src/genre-tree/GenreTreeWheelHandoff.tsx
1000
995
  var import_react7 = require("react");
1001
- var import_jsx_runtime15 = require("react/jsx-runtime");
996
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1002
997
  function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
1003
998
  const [revealed, setRevealed] = (0, import_react7.useState)(false);
1004
999
  const onRevealedChangeRef = (0, import_react7.useRef)(onRevealedChange);
@@ -1017,8 +1012,8 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
1017
1012
  cancelAnimationFrame(innerFrame);
1018
1013
  };
1019
1014
  }, []);
1020
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "relative w-full h-full", children: [
1021
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1015
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "relative w-full h-full", children: [
1016
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1022
1017
  "div",
1023
1018
  {
1024
1019
  className: "absolute inset-0 transition-opacity duration-150 ease-out",
@@ -1027,7 +1022,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
1027
1022
  children
1028
1023
  }
1029
1024
  ),
1030
- !revealed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
1025
+ !revealed && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
1031
1026
  ] });
1032
1027
  }
1033
1028
 
@@ -1042,7 +1037,7 @@ var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
1042
1037
  // src/genre-tree/search/SearchBox.tsx
1043
1038
  var import_react8 = require("react");
1044
1039
  var import_ui6 = require("@behindthemusictree/ui");
1045
- var import_jsx_runtime16 = require("react/jsx-runtime");
1040
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1046
1041
  function SearchBox({
1047
1042
  label,
1048
1043
  placeholder,
@@ -1064,7 +1059,7 @@ function SearchBox({
1064
1059
  groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
1065
1060
  onPicked?.(groupKey, itemKey);
1066
1061
  };
1067
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1062
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1068
1063
  import_ui6.SearchCombobox,
1069
1064
  {
1070
1065
  ariaLabel: label,
@@ -1096,7 +1091,7 @@ var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS
1096
1091
 
1097
1092
  // src/genre-tree/search/useArtistSearchGroup.tsx
1098
1093
  var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
1099
- var import_jsx_runtime17 = require("react/jsx-runtime");
1094
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1100
1095
  function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1101
1096
  const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
1102
1097
  if (!enabled) return void 0;
@@ -1105,15 +1100,15 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1105
1100
  label: "Artists",
1106
1101
  items: search.artists.map((artist) => ({
1107
1102
  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: [
1103
+ content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
1104
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "block truncate font-medium", children: [
1110
1105
  artist.name,
1111
- artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
1106
+ artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
1112
1107
  " \xB7 ",
1113
1108
  artist.disambiguation
1114
1109
  ] })
1115
1110
  ] }),
1116
- artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
1111
+ artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
1117
1112
  "also known as ",
1118
1113
  artist.matchedAlias
1119
1114
  ] })
@@ -1127,7 +1122,7 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1127
1122
 
1128
1123
  // src/genre-tree/search/useSongSearchGroup.tsx
1129
1124
  var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
1130
- var import_jsx_runtime18 = require("react/jsx-runtime");
1125
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1131
1126
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1132
1127
  const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
1133
1128
  if (!enabled) return void 0;
@@ -1136,9 +1131,9 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1136
1131
  label: "Songs",
1137
1132
  items: search.songs.map((song) => ({
1138
1133
  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 ") })
1134
+ content: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate font-medium", children: song.title }),
1136
+ /* @__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
1137
  ] }),
1143
1138
  onSelect: () => onSelect(song)
1144
1139
  })),
@@ -1149,7 +1144,7 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1149
1144
 
1150
1145
  // src/genre-tree/search/useGenreRemoteSearchGroup.tsx
1151
1146
  var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
1152
- var import_jsx_runtime19 = require("react/jsx-runtime");
1147
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1153
1148
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
1154
1149
  const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
1155
1150
  const genres = filter ? search.genres.filter(filter) : search.genres;
@@ -1158,12 +1153,12 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
1158
1153
  label,
1159
1154
  items: genres.map((genre) => ({
1160
1155
  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: [
1156
+ content: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1157
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "block truncate font-medium", children: [
1163
1158
  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" })
1159
+ 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
1160
  ] }),
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}` })
1161
+ /* @__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
1162
  ] }),
1168
1163
  onSelect: () => onSelect(genre)
1169
1164
  })),
@@ -1173,7 +1168,7 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
1173
1168
  }
1174
1169
 
1175
1170
  // src/genre-tree/GenreSearch.tsx
1176
- var import_jsx_runtime20 = require("react/jsx-runtime");
1171
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1177
1172
  function GenreSearch({
1178
1173
  genrePlaylists,
1179
1174
  onSelect,
@@ -1202,9 +1197,9 @@ function GenreSearch({
1202
1197
  label: hasExtras ? "Genres" : void 0,
1203
1198
  items: results.map((genrePlaylist) => ({
1204
1199
  key: genrePlaylist.uuid,
1205
- content: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1200
+ content: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
1206
1201
  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" })
1202
+ genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
1208
1203
  ] }),
1209
1204
  onSelect: () => select(genrePlaylist)
1210
1205
  }))
@@ -1213,7 +1208,7 @@ function GenreSearch({
1213
1208
  (group) => active ? group : { ...group, items: [], status: "", loading: false }
1214
1209
  )
1215
1210
  ];
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)(
1211
+ 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
1212
  SearchBox,
1218
1213
  {
1219
1214
  label,
@@ -1233,14 +1228,14 @@ function GenreSearch({
1233
1228
  clear();
1234
1229
  onQueryChange?.("");
1235
1230
  } : 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" }) }),
1231
+ 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
1232
  onFocus
1238
1233
  }
1239
1234
  ) });
1240
1235
  }
1241
1236
 
1242
1237
  // src/genre-tree/GenreTreeSearch.tsx
1243
- var import_jsx_runtime21 = require("react/jsx-runtime");
1238
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1244
1239
  function GenreTreeSearch({
1245
1240
  artistBaseUrl,
1246
1241
  songBaseUrl,
@@ -1260,7 +1255,7 @@ function GenreTreeSearch({
1260
1255
  const deferredQuery = (0, import_react9.useDeferredValue)(query);
1261
1256
  const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
1262
1257
  const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
1263
- const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1258
+ const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1264
1259
  GenreSearch,
1265
1260
  {
1266
1261
  ...props,
@@ -1275,7 +1270,7 @@ function GenreTreeSearch({
1275
1270
  extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
1276
1271
  }
1277
1272
  );
1278
- return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
1273
+ return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
1279
1274
  }
1280
1275
 
1281
1276
  // src/genre-tree/GenreDetailPanel.tsx
@@ -1295,7 +1290,7 @@ var import_ui9 = require("@behindthemusictree/ui");
1295
1290
  // src/genre-tree/ShareActionButton.tsx
1296
1291
  var import_lucide_react10 = require("lucide-react");
1297
1292
  var import_ui8 = require("@behindthemusictree/ui");
1298
- var import_popup4 = require("@behindthemusictree/app-kit/popup");
1293
+ var import_popup3 = require("@behindthemusictree/app-kit/popup");
1299
1294
 
1300
1295
  // src/genre-tree/SharePopup.tsx
1301
1296
  var import_react10 = require("react");
@@ -1304,12 +1299,12 @@ var import_fa3 = require("react-icons/fa");
1304
1299
  var import_si = require("react-icons/si");
1305
1300
 
1306
1301
  // src/genre-tree/ShareDestinationButton.tsx
1307
- var import_jsx_runtime22 = require("react/jsx-runtime");
1302
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1308
1303
  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
1304
  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 })
1305
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
1306
+ /* @__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 }),
1307
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "max-w-full truncate", children: label })
1313
1308
  ] });
1314
1309
  }
1315
1310
  function ShareDestinationButton({
@@ -1321,29 +1316,29 @@ function ShareDestinationButton({
1321
1316
  ariaLabel
1322
1317
  }) {
1323
1318
  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 }) });
1319
+ 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
1320
  }
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 }) });
1321
+ 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
1322
  }
1328
1323
 
1329
1324
  // src/genre-tree/SharePreviewCard.tsx
1330
1325
  var import_lucide_react8 = require("lucide-react");
1331
- var import_jsx_runtime23 = require("react/jsx-runtime");
1326
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1332
1327
  function SharePreviewCard({ target, url }) {
1333
1328
  const Icon = { genre: import_lucide_react8.Music2, artist: import_lucide_react8.UserRound, song: import_lucide_react8.Music }[target.kind];
1334
1329
  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 })
1330
+ 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: [
1331
+ /* @__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" }) }),
1332
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
1333
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
1334
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
1335
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
1341
1336
  ] })
1342
1337
  ] }) });
1343
1338
  }
1344
1339
 
1345
1340
  // src/genre-tree/SharePopup.tsx
1346
- var import_jsx_runtime24 = require("react/jsx-runtime");
1341
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1347
1342
  function resolveAbsoluteUrl(url) {
1348
1343
  try {
1349
1344
  const parsed = new URL(url, window.location.href);
@@ -1418,8 +1413,8 @@ function SharePopup({ target, url, onClose }) {
1418
1413
  setShareError("Could not open the share options.");
1419
1414
  }
1420
1415
  };
1421
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
1422
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1416
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
1417
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1423
1418
  BasePopup_default,
1424
1419
  {
1425
1420
  title: "Share",
@@ -1434,70 +1429,70 @@ function SharePopup({ target, url, onClose }) {
1434
1429
  titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
1435
1430
  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
1431
  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)(
1432
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-col gap-4", children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
1434
+ /* @__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: [
1435
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1441
1436
  ShareDestinationButton,
1442
1437
  {
1443
1438
  label: "Copy Link",
1444
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1439
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
1445
1440
  onClick: () => void handleCopy(),
1446
1441
  ariaLabel: "Copy link",
1447
1442
  disabled: !absoluteUrl
1448
1443
  }
1449
1444
  ),
1450
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1445
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1451
1446
  ShareDestinationButton,
1452
1447
  {
1453
1448
  label: "WhatsApp",
1454
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1449
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
1455
1450
  href: destinationUrls?.whatsapp,
1456
1451
  ariaLabel: `Share ${target.name} via WhatsApp`,
1457
1452
  disabled: !absoluteUrl
1458
1453
  }
1459
1454
  ),
1460
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1455
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1461
1456
  ShareDestinationButton,
1462
1457
  {
1463
1458
  label: "Gmail",
1464
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1459
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
1465
1460
  href: destinationUrls?.gmail,
1466
1461
  ariaLabel: `Email ${target.name} with Gmail`,
1467
1462
  disabled: !absoluteUrl
1468
1463
  }
1469
1464
  ),
1470
- canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1465
+ canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1471
1466
  ShareDestinationButton,
1472
1467
  {
1473
1468
  label: "More",
1474
- icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1469
+ icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
1475
1470
  onClick: () => void handleNativeShare(),
1476
1471
  ariaLabel: "More sharing options",
1477
1472
  disabled: !absoluteUrl
1478
1473
  }
1479
1474
  )
1480
1475
  ] }),
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 })
1476
+ 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." }),
1477
+ shareError && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
1483
1478
  ] })
1484
1479
  }
1485
1480
  ),
1486
- toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1481
+ toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
1487
1482
  ] });
1488
1483
  }
1489
1484
 
1490
1485
  // src/genre-tree/ShareActionButton.tsx
1491
- var import_jsx_runtime25 = require("react/jsx-runtime");
1486
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1492
1487
  function ShareActionButton({ target, url }) {
1493
- const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
1488
+ const { showPopup, hidePopup } = (0, import_popup3.usePopup)();
1494
1489
  const openSharePopup = () => {
1495
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1490
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SharePopup, { target, url, onClose: hidePopup }));
1496
1491
  };
1497
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1492
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1498
1493
  import_ui8.ActionButton,
1499
1494
  {
1500
- icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1495
+ icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
1501
1496
  label: "Share",
1502
1497
  "aria-label": `Share ${target.name}`,
1503
1498
  title: `Share ${target.name}`,
@@ -1507,7 +1502,7 @@ function ShareActionButton({ target, url }) {
1507
1502
  }
1508
1503
 
1509
1504
  // src/genre-tree/GenreActionBand.tsx
1510
- var import_jsx_runtime26 = require("react/jsx-runtime");
1505
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1511
1506
  function GenreActionBand({
1512
1507
  node,
1513
1508
  playActions,
@@ -1528,24 +1523,24 @@ function GenreActionBand({
1528
1523
  items.push({
1529
1524
  key: "zoom-in",
1530
1525
  label: "Focus",
1531
- icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1526
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
1532
1527
  onClick: onZoomIn
1533
1528
  });
1534
1529
  }
1535
1530
  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 });
1531
+ 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
1532
  }
1538
1533
  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 });
1534
+ 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
1535
  }
1541
1536
  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 });
1537
+ 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
1538
  }
1544
1539
  if (onToggleSide) {
1545
1540
  adminItems.push({
1546
1541
  key: "toggle-side",
1547
1542
  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" }),
1543
+ icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react11.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1549
1544
  onClick: onToggleSide,
1550
1545
  disabled: !!toggleSideDisabledReason,
1551
1546
  title: toggleSideDisabledReason
@@ -1560,9 +1555,9 @@ function GenreActionBand({
1560
1555
  disabled: action.enabled ? !action.enabled(node) : false
1561
1556
  });
1562
1557
  }
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));
1558
+ 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
1559
  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)(
1560
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui9.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1566
1561
  import_ui9.ActionBand,
1567
1562
  {
1568
1563
  className: "genre-action-band",
@@ -1570,7 +1565,7 @@ function GenreActionBand({
1570
1565
  children: [
1571
1566
  playActions,
1572
1567
  renderItems(items),
1573
- shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1568
+ shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
1574
1569
  ]
1575
1570
  }
1576
1571
  ) });
@@ -1594,7 +1589,7 @@ var REGION_GLOBE_PATHS = {
1594
1589
  var REGION_GLOBES = ["africa", "americas", "asia"];
1595
1590
 
1596
1591
  // src/genre-tree/RegionGlobeIcon.tsx
1597
- var import_jsx_runtime27 = require("react/jsx-runtime");
1592
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1598
1593
  var ROTATION_INTERVAL_MS = 3e3;
1599
1594
  function RegionGlobeIcon({ className: className2 }) {
1600
1595
  const [index, setIndex] = (0, import_react11.useState)(0);
@@ -1612,29 +1607,29 @@ function RegionGlobeIcon({ className: className2 }) {
1612
1607
  clearInterval(timer);
1613
1608
  };
1614
1609
  }, []);
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]] }) });
1610
+ 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
1611
  }
1617
1612
 
1618
1613
  // src/genre-tree/treeMarkPath.ts
1619
1614
  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
1615
 
1621
1616
  // src/genre-tree/TreeMarkIcon.tsx
1622
- var import_jsx_runtime28 = require("react/jsx-runtime");
1617
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1623
1618
  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 }) });
1619
+ 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
1620
  }
1626
1621
 
1627
1622
  // src/genre-tree/GenrePlayActions.tsx
1628
- var import_jsx_runtime29 = require("react/jsx-runtime");
1623
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1629
1624
  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%)]" })
1625
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
1626
+ kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1627
+ kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1628
+ kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1629
+ /* @__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)]" }),
1630
+ /* @__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
1631
  ] }),
1637
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react12.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1632
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react12.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1638
1633
  ] });
1639
1634
  }
1640
1635
  function playOptions(playlist) {
@@ -1650,13 +1645,13 @@ function playOptions(playlist) {
1650
1645
  function GenrePlayActions({ genrePlaylist, playback }) {
1651
1646
  const { isPlaying, setIsPlaying } = (0, import_player4.usePlayer)();
1652
1647
  const options = playOptions(genrePlaylist);
1653
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_jsx_runtime29.Fragment, { children: options.map((option) => {
1648
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_jsx_runtime30.Fragment, { children: options.map((option) => {
1654
1649
  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
1650
  const playing = isCurrent && isPlaying;
1656
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
1651
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
1657
1652
  import_ui10.ActionButton,
1658
1653
  {
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 }),
1654
+ 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
1655
  label: playing ? "Pause" : option.label,
1661
1656
  caption: String(option.count),
1662
1657
  disabled: option.count === 0,
@@ -1666,9 +1661,7 @@ function GenrePlayActions({ genrePlaylist, playback }) {
1666
1661
  setIsPlaying(!isPlaying);
1667
1662
  return;
1668
1663
  }
1669
- playback.play(genrePlaylist, option.selection).catch((error) => {
1670
- console.error("Failed to load genre playlist tracks:", error);
1671
- });
1664
+ void playback.play(genrePlaylist, option.selection);
1672
1665
  }
1673
1666
  },
1674
1667
  option.key
@@ -1682,7 +1675,7 @@ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
1682
1675
 
1683
1676
  // src/genre-tree/ClampedText.tsx
1684
1677
  var import_react12 = require("react");
1685
- var import_jsx_runtime30 = require("react/jsx-runtime");
1678
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1686
1679
  function ClampedText({ children, className: className2 = "" }) {
1687
1680
  const ref = (0, import_react12.useRef)(null);
1688
1681
  const [expanded, setExpanded] = (0, import_react12.useState)(false);
@@ -1692,9 +1685,9 @@ function ClampedText({ children, className: className2 = "" }) {
1692
1685
  if (!element || expanded) return;
1693
1686
  setOverflows(element.scrollHeight > element.clientHeight);
1694
1687
  }, [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)(
1688
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
1689
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1690
+ (overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1698
1691
  "button",
1699
1692
  {
1700
1693
  type: "button",
@@ -1714,13 +1707,13 @@ var import_zod = require("zod");
1714
1707
  var import_fa4 = require("react-icons/fa");
1715
1708
  var import_ui11 = require("@behindthemusictree/ui");
1716
1709
  var import_auth2 = require("@behindthemusictree/app-kit/auth");
1710
+ var import_transport4 = require("@behindthemusictree/app-kit/transport");
1717
1711
  var import_transport5 = require("@behindthemusictree/app-kit/transport");
1718
1712
  var import_transport6 = require("@behindthemusictree/app-kit/transport");
1719
- var import_transport7 = require("@behindthemusictree/app-kit/transport");
1720
1713
  var import_player5 = require("@behindthemusictree/app-kit/player");
1721
1714
  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");
1715
+ var import_popup4 = require("@behindthemusictree/app-kit/popup");
1716
+ var import_transport7 = require("@behindthemusictree/app-kit/transport");
1724
1717
  var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1725
1718
  var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1726
1719
  var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
@@ -1729,15 +1722,15 @@ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
1729
1722
 
1730
1723
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1731
1724
  var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1732
- var import_jsx_runtime31 = require("react/jsx-runtime");
1725
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1733
1726
  function MusicBrainzRecordingLink({
1734
1727
  song,
1735
1728
  children,
1736
1729
  className: className2 = ""
1737
1730
  }) {
1738
1731
  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)(
1732
+ if (!url) return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children });
1733
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1741
1734
  "a",
1742
1735
  {
1743
1736
  href: url,
@@ -1754,9 +1747,9 @@ function MusicBrainzRecordingLink({
1754
1747
  // src/genre-tree/GenrePlaylistTracks.tsx
1755
1748
  var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
1756
1749
  var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
1757
- var import_jsx_runtime32 = require("react/jsx-runtime");
1750
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1758
1751
  var CATALOGUE_PAGE_SIZE = 100;
1759
- var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1752
+ var SongPageSchema = (0, import_transport5.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
1760
1753
  overallTotal: import_zod.z.number().nullable(),
1761
1754
  page: import_zod.z.number().nullable(),
1762
1755
  totalPages: import_zod.z.number().nullable()
@@ -1788,12 +1781,12 @@ function GenrePlaylistTracks({
1788
1781
  const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree16.useTrackList)();
1789
1782
  const { session, sessionRestored } = (0, import_auth2.useSession)();
1790
1783
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player5.usePlayer)();
1791
- const { showPopup } = (0, import_popup5.usePopup)();
1784
+ const { showPopup } = (0, import_popup4.usePopup)();
1792
1785
  const trackNoun = (0, import_track_noun2.useTrackNoun)();
1793
1786
  const uuid = genrePlaylist?.uuid;
1794
1787
  const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
1795
- const { fetchBound } = (0, import_transport5.usePlaybackFetch)(getBackendBaseUrl);
1796
- const playback = (0, import_transport5.usePlaybackContext)();
1788
+ const { fetchBound } = (0, import_transport4.usePlaybackFetch)(getBackendBaseUrl);
1789
+ const playback = (0, import_transport4.usePlaybackContext)();
1797
1790
  const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
1798
1791
  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
1792
  queryFn: async ({ pageParam }) => {
@@ -1808,7 +1801,7 @@ function GenrePlaylistTracks({
1808
1801
  {},
1809
1802
  { genre: genreUuid, ...(0, import_genre_tree12.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1810
1803
  );
1811
- const parsed = (0, import_transport7.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1804
+ const parsed = (0, import_transport6.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1812
1805
  return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
1813
1806
  },
1814
1807
  initialPageParam: scope === "me" ? 1 : "",
@@ -1840,35 +1833,36 @@ function GenrePlaylistTracks({
1840
1833
  }) : 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
1834
  started.catch((error) => {
1842
1835
  console.error("Failed to play genre playlist:", error);
1843
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
1836
+ if (error instanceof import_genre_tree16.TracksFailureAlreadyReportedError) return;
1837
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport7.AppError ? error.code : import_transport7.ErrorCode.CLIENT_UNKNOWN }));
1844
1838
  });
1845
1839
  };
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: [
1840
+ 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
1841
  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: [
1842
+ 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
1843
  "Couldn't load ",
1850
1844
  trackNoun.plural,
1851
1845
  ".",
1852
1846
  " ",
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: [
1847
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1848
+ ] }) : 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
1849
  tracks.map((track, index) => {
1856
1850
  const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
1857
1851
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1858
1852
  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 })
1853
+ const text = /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: TEXT_CELL, children: [
1854
+ /* @__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 }),
1855
+ track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1856
+ /* @__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 }),
1857
+ track.album && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1864
1858
  ] });
1865
1859
  const leading = renderSongLeading?.(track, index);
1866
1860
  const actions = renderSongActions?.(track);
1867
1861
  const isUnplayable = (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
1868
1862
  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: [
1863
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1870
1864
  leading,
1871
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1865
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1872
1866
  "button",
1873
1867
  {
1874
1868
  type: "button",
@@ -1876,27 +1870,27 @@ function GenrePlaylistTracks({
1876
1870
  "aria-disabled": "true",
1877
1871
  "aria-label": label,
1878
1872
  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 })
1873
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa4.FaPlay, { size: 10 })
1880
1874
  }
1881
1875
  ),
1882
1876
  text,
1883
1877
  actions
1884
1878
  ] }, track.uuid);
1885
1879
  }
1886
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1880
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1887
1881
  "li",
1888
1882
  {
1889
1883
  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
1884
  children: [
1891
1885
  leading,
1892
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1886
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1893
1887
  "button",
1894
1888
  {
1895
1889
  type: "button",
1896
1890
  "aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
1897
1891
  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
1892
  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 })
1893
+ 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
1894
  }
1901
1895
  ),
1902
1896
  text,
@@ -1906,7 +1900,7 @@ function GenrePlaylistTracks({
1906
1900
  track.uuid
1907
1901
  );
1908
1902
  }),
1909
- hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1903
+ hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1910
1904
  ] })
1911
1905
  ] }) });
1912
1906
  }
@@ -1914,7 +1908,7 @@ function GenrePlaylistTracks({
1914
1908
  // src/genre-tree/GenreDetailExtras.tsx
1915
1909
  var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
1916
1910
  var import_si2 = require("react-icons/si");
1917
- var import_jsx_runtime33 = require("react/jsx-runtime");
1911
+ var import_jsx_runtime34 = require("react/jsx-runtime");
1918
1912
  function GenreDetailExtras({
1919
1913
  genreUuid,
1920
1914
  genrePlaylist,
@@ -1940,17 +1934,17 @@ function GenreDetailExtras({
1940
1934
  criteriaOverviewSchema
1941
1935
  );
1942
1936
  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" }) }) }),
1937
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
1938
+ /* @__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
1939
  family
1946
1940
  ] });
1947
1941
  }
1948
- if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_jsx_runtime33.Fragment, { children: family });
1942
+ if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_jsx_runtime34.Fragment, { children: family });
1949
1943
  const { summary, summarySourceUrl, essentialSongs } = overview;
1950
1944
  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)(
1945
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
1946
+ (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: [
1947
+ summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
1954
1948
  "a",
1955
1949
  {
1956
1950
  href: summarySourceUrl,
@@ -1958,12 +1952,12 @@ function GenreDetailExtras({
1958
1952
  rel: "noopener noreferrer",
1959
1953
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1960
1954
  children: [
1961
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1955
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1962
1956
  "Wikipedia"
1963
1957
  ]
1964
1958
  }
1965
1959
  ),
1966
- wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1960
+ wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
1967
1961
  "a",
1968
1962
  {
1969
1963
  href: wikidataUrl,
@@ -1971,17 +1965,17 @@ function GenreDetailExtras({
1971
1965
  rel: "noopener noreferrer",
1972
1966
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1973
1967
  children: [
1974
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1968
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1975
1969
  "Wikidata"
1976
1970
  ]
1977
1971
  }
1978
1972
  )
1979
1973
  ] }) }),
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) }),
1974
+ /* @__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
1975
  family,
1982
1976
  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)(
1977
+ (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" }) }),
1978
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
1985
1979
  GenrePlaylistTracks,
1986
1980
  {
1987
1981
  genreUuid,
@@ -1999,10 +1993,10 @@ function GenreDetailExtras({
1999
1993
  }
2000
1994
 
2001
1995
  // src/genre-tree/GenreInfoPanelBody.tsx
2002
- var import_jsx_runtime34 = require("react/jsx-runtime");
1996
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2003
1997
  var import_react14 = require("react");
2004
1998
  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 }) });
1999
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
2006
2000
  }
2007
2001
  function GenreInfoPanelBody({
2008
2002
  toolbar,
@@ -2019,9 +2013,9 @@ function GenreInfoPanelBody({
2019
2013
  }) {
2020
2014
  const summary = scope === "reference" ? import_genre_tree19.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
2021
2015
  const selection = summary ? (0, import_genre_tree19.defaultGenreMusicSelection)(summary) : void 0;
2022
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
2016
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
2023
2017
  toolbar,
2024
- genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenreActionBand, { ...band }),
2018
+ genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GenreActionBand, { ...band }),
2025
2019
  /* @__PURE__ */ (0, import_react14.createElement)(
2026
2020
  GenreDetailExtras,
2027
2021
  {
@@ -2041,7 +2035,7 @@ function GenreInfoPanelBody({
2041
2035
  }
2042
2036
 
2043
2037
  // src/genre-tree/GenreDetailPanel.tsx
2044
- var import_jsx_runtime35 = require("react/jsx-runtime");
2038
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2045
2039
  var RELATIVE_ITEM_COUNT = 1;
2046
2040
  function toNode(genre, parentId) {
2047
2041
  return {
@@ -2064,7 +2058,7 @@ function useGenreDetailContent({
2064
2058
  trailing,
2065
2059
  genrePlaylist: hostPlaylist,
2066
2060
  expanded,
2067
- treeRelations
2061
+ treeStyles
2068
2062
  }) {
2069
2063
  const { data: detail, isPending, isError } = (0, import_genre_tree20.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
2070
2064
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
@@ -2072,19 +2066,12 @@ function useGenreDetailContent({
2072
2066
  const view = (0, import_react15.useMemo)(() => {
2073
2067
  if (!detail) return null;
2074
2068
  const rootColor = (0, import_genre_tree_view4.getGenreTreeColor)(detail.root.wikidataId);
2075
- const treeStyles = /* @__PURE__ */ new Map();
2076
- if (treeRelations) {
2077
- const drawn = [treeRelations, treeRelations.parentNode, ...treeRelations.childNodes, ...treeRelations.ancestorNodes];
2078
- for (const entry of drawn) {
2079
- if (entry) treeStyles.set(entry.node.id, { fill: entry.fill, textColor: entry.textColor });
2080
- }
2081
- }
2082
2069
  const relative = (genre, parentId) => ({
2083
2070
  node: toNode(genre, parentId),
2084
- ...treeStyles.get(genre.uuid) ?? { fill: (0, import_genre_tree_view4.tintSurface)(rootColor), textColor: import_genre_tree_view4.TEXT_COLOR }
2071
+ ...treeStyles?.get(genre.uuid) ?? { fill: (0, import_genre_tree_view4.tintSurface)(rootColor), textColor: import_genre_tree_view4.TEXT_COLOR }
2085
2072
  });
2086
2073
  const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
2087
- const own = treeStyles.get(detail.uuid) ?? { fill: rootColor, textColor: (0, import_genre_tree_view4.getTextColorOnFill)(rootColor) };
2074
+ const own = treeStyles?.get(detail.uuid) ?? { fill: rootColor, textColor: (0, import_genre_tree_view4.getTextColorOnFill)(rootColor) };
2088
2075
  return {
2089
2076
  node: {
2090
2077
  id: detail.uuid,
@@ -2101,23 +2088,23 @@ function useGenreDetailContent({
2101
2088
  ({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
2102
2089
  )
2103
2090
  };
2104
- }, [detail, scope, treeRelations]);
2091
+ }, [detail, scope, treeStyles]);
2105
2092
  if (isError) return {
2106
2093
  name: null,
2107
2094
  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." })
2095
+ 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
2096
  };
2110
2097
  if (isPending || !detail || !view) return {
2111
2098
  name: null,
2112
2099
  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" })
2100
+ content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
2101
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-6 w-2/3" }),
2102
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-4 w-full" }),
2103
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-4 w-3/4" })
2117
2104
  ] })
2118
2105
  };
2119
2106
  const selectGenre = (nodeId) => onSelectGenre(import_genre_tree20.GenreUuidSchema.parse(nodeId));
2120
- const family = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2107
+ const family = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2121
2108
  import_genre_tree_view4.GenreNodeFamily,
2122
2109
  {
2123
2110
  node: view.node,
@@ -2128,17 +2115,17 @@ function useGenreDetailContent({
2128
2115
  onSelectNode: selectGenre
2129
2116
  }
2130
2117
  );
2131
- const ancestors = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
2118
+ const ancestors = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
2132
2119
  return {
2133
2120
  name: detail.name,
2134
2121
  title: detail.name,
2135
2122
  headerAccent: { background: view.fill, color: view.textColor },
2136
2123
  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)(
2124
+ content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
2125
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2139
2126
  GenreInfoPanelBody,
2140
2127
  {
2141
- family: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
2128
+ family: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
2142
2129
  family,
2143
2130
  !expanded && ancestors
2144
2131
  ] }),
@@ -2182,7 +2169,7 @@ function GenreDetailPanel({
2182
2169
  expanded,
2183
2170
  band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
2184
2171
  });
2185
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2172
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2186
2173
  import_ui13.DetailPanel,
2187
2174
  {
2188
2175
  side: "left",
@@ -2195,7 +2182,7 @@ function GenreDetailPanel({
2195
2182
  edgeTab,
2196
2183
  aside: panel.aside,
2197
2184
  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,
2185
+ 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
2186
  children: panel.content
2200
2187
  }
2201
2188
  );
@@ -2204,7 +2191,7 @@ function GenreDetailPanel({
2204
2191
  // src/genre-tree/ChangeParentPopup.tsx
2205
2192
  var import_react16 = require("react");
2206
2193
  var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
2207
- var import_jsx_runtime36 = require("react/jsx-runtime");
2194
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2208
2195
  function ChangeParentPopup({
2209
2196
  genrePlaylist,
2210
2197
  genrePlaylists,
@@ -2239,12 +2226,12 @@ function ChangeParentPopup({
2239
2226
  window.addEventListener("keydown", handleKeyDown);
2240
2227
  return () => window.removeEventListener("keydown", handleKeyDown);
2241
2228
  }, [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: [
2229
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
2230
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
2244
2231
  "Current parent: ",
2245
2232
  genrePlaylist.parent?.name ?? "none"
2246
2233
  ] }),
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)(
2234
+ /* @__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
2235
  GenreSearch,
2249
2236
  {
2250
2237
  genrePlaylists: candidates,
@@ -2252,7 +2239,7 @@ function ChangeParentPopup({
2252
2239
  placeholder: "Search a new parent\u2026"
2253
2240
  }
2254
2241
  ) }),
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(" ") })
2242
+ 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
2243
  ] });
2257
2244
  }
2258
2245
 
@@ -2265,7 +2252,7 @@ var import_lucide_react14 = require("lucide-react");
2265
2252
  var import_ui14 = require("@behindthemusictree/ui");
2266
2253
  var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
2267
2254
  var import_player7 = require("@behindthemusictree/app-kit/player");
2268
- var import_jsx_runtime37 = require("react/jsx-runtime");
2255
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2269
2256
  function ArtistActionBand({ artist, share }) {
2270
2257
  const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree22.useTrackList)();
2271
2258
  const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
@@ -2281,11 +2268,11 @@ function ArtistActionBand({ artist, share }) {
2281
2268
  console.error("Failed to load artist tracks:", error);
2282
2269
  });
2283
2270
  };
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)(
2271
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_ui14.ActionBand, { children: [
2272
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2286
2273
  import_ui14.ActionButton,
2287
2274
  {
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" }),
2275
+ 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
2276
  label: playing ? "Pause" : "Play",
2290
2277
  caption: String(artist.playableTracksCount),
2291
2278
  disabled: artist.playableTracksCount === 0,
@@ -2304,7 +2291,7 @@ var import_ui16 = require("@behindthemusictree/ui");
2304
2291
  var import_lucide_react15 = require("lucide-react");
2305
2292
  var import_si3 = require("react-icons/si");
2306
2293
  var import_ui15 = require("@behindthemusictree/ui");
2307
- var import_jsx_runtime38 = require("react/jsx-runtime");
2294
+ var import_jsx_runtime39 = require("react/jsx-runtime");
2308
2295
  var LINK_KINDS = {
2309
2296
  official: { label: "Official website", Icon: import_lucide_react15.Globe },
2310
2297
  wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
@@ -2341,7 +2328,7 @@ function artistLinks(artist) {
2341
2328
  return links;
2342
2329
  }
2343
2330
  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)) });
2331
+ 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
2332
  }
2346
2333
 
2347
2334
  // src/genre-tree/artist/formatYears.ts
@@ -2352,7 +2339,7 @@ function formatYears(beginYear, endYear, ended) {
2352
2339
  }
2353
2340
 
2354
2341
  // src/genre-tree/artist/ArtistHeader.tsx
2355
- var import_jsx_runtime39 = require("react/jsx-runtime");
2342
+ var import_jsx_runtime40 = require("react/jsx-runtime");
2356
2343
  var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
2357
2344
  function ArtistHeader({ artist }) {
2358
2345
  const fields = [];
@@ -2362,35 +2349,35 @@ function ArtistHeader({ artist }) {
2362
2349
  const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
2363
2350
  if (years) fields.push({ label: "Active", value: years });
2364
2351
  const links = artistLinks(artist);
2365
- if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ArtistLinks, { links }) });
2352
+ if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistLinks, { links }) });
2366
2353
  if (fields.length === 0) return null;
2367
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui16.FieldList, { items: fields });
2354
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.FieldList, { items: fields });
2368
2355
  }
2369
2356
 
2370
2357
  // src/genre-tree/artist/ArtistPhoto.tsx
2371
- var import_jsx_runtime40 = require("react/jsx-runtime");
2358
+ var import_jsx_runtime41 = require("react/jsx-runtime");
2372
2359
  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 });
2360
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
2374
2361
  }
2375
2362
  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: [
2363
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
2364
+ /* @__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" }),
2365
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
2379
2366
  "Photo: ",
2380
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2367
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
2381
2368
  ",",
2382
2369
  " ",
2383
- image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2370
+ image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
2384
2371
  ] })
2385
2372
  ] });
2386
2373
  }
2387
2374
 
2388
2375
  // src/genre-tree/artist/ArtistMembers.tsx
2389
2376
  var import_ui17 = require("@behindthemusictree/ui");
2390
- var import_jsx_runtime41 = require("react/jsx-runtime");
2377
+ var import_jsx_runtime42 = require("react/jsx-runtime");
2391
2378
  function MembershipList({ title, memberships, renderArtistLink }) {
2392
2379
  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) => {
2380
+ 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
2381
  const details = [
2395
2382
  formatYears(membership.beginYear, membership.endYear, membership.ended),
2396
2383
  ...membership.roles
@@ -2398,9 +2385,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2398
2385
  const detailsText = details.filter(Boolean).join(" \xB7 ");
2399
2386
  return (
2400
2387
  // 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: [
2388
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
2389
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
2390
+ detailsText && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2404
2391
  " \u2014 ",
2405
2392
  detailsText
2406
2393
  ] })
@@ -2411,9 +2398,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2411
2398
  function ArtistMembers({ title, memberships, renderArtistLink }) {
2412
2399
  if (memberships.length === 0) return null;
2413
2400
  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)(
2401
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_ui17.PanelSection, { title, children: [
2402
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2403
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2417
2404
  MembershipList,
2418
2405
  {
2419
2406
  title: "Past",
@@ -2426,7 +2413,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
2426
2413
 
2427
2414
  // src/genre-tree/artist/ArtistDiscography.tsx
2428
2415
  var import_ui18 = require("@behindthemusictree/ui");
2429
- var import_jsx_runtime42 = require("react/jsx-runtime");
2416
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2430
2417
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
2431
2418
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
2432
2419
  function discographyGroup({ primaryType, secondaryTypes }) {
@@ -2446,12 +2433,12 @@ function byYear(a, b) {
2446
2433
  function ArtistDiscography({ albums }) {
2447
2434
  if (albums.length === 0) return null;
2448
2435
  const sorted = [...albums].sort(byYear);
2449
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2436
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2450
2437
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
2451
2438
  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 })
2439
+ 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: [
2440
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2441
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2455
2442
  ] }, album.uuid)) }) }, group);
2456
2443
  }) });
2457
2444
  }
@@ -2459,11 +2446,10 @@ function ArtistDiscography({ albums }) {
2459
2446
  // src/genre-tree/artist/ArtistSongs.tsx
2460
2447
  var import_react17 = require("react");
2461
2448
  var import_react_query2 = require("@tanstack/react-query");
2462
- var import_lucide_react16 = require("lucide-react");
2463
2449
  var import_ui19 = require("@behindthemusictree/ui");
2464
2450
  var import_player11 = require("@behindthemusictree/app-kit/player");
2465
2451
  var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2466
- var import_transport9 = require("@behindthemusictree/app-kit/transport");
2452
+ var import_transport8 = require("@behindthemusictree/app-kit/transport");
2467
2453
 
2468
2454
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2469
2455
  var import_player10 = require("@behindthemusictree/app-kit/player");
@@ -2473,7 +2459,7 @@ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
2473
2459
  var import_fa5 = require("react-icons/fa");
2474
2460
  var import_player8 = require("@behindthemusictree/app-kit/player");
2475
2461
  var import_player9 = require("@behindthemusictree/app-kit/player");
2476
- var import_jsx_runtime43 = require("react/jsx-runtime");
2462
+ var import_jsx_runtime44 = require("react/jsx-runtime");
2477
2463
  function TrackPositionPlayPause({
2478
2464
  position,
2479
2465
  uuid,
@@ -2487,14 +2473,14 @@ function TrackPositionPlayPause({
2487
2473
  const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PLAYING;
2488
2474
  const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PAUSED;
2489
2475
  const isCurrentTrackActive = isPlaying || isPaused;
2490
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
2476
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2491
2477
  "div",
2492
2478
  {
2493
2479
  className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
2494
2480
  style: { minWidth: "4rem", maxWidth: "4rem" },
2495
2481
  onClick: handlePlayPauseClick,
2496
2482
  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)(
2483
+ /* @__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
2484
  "span",
2499
2485
  {
2500
2486
  className: "track-position-play-pause__bars",
@@ -2502,18 +2488,18 @@ function TrackPositionPlayPause({
2502
2488
  role: "img",
2503
2489
  "aria-label": isPlaying ? "Playing" : "Paused",
2504
2490
  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" })
2491
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__bar" }),
2492
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__bar" }),
2493
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "track-position-play-pause__bar" })
2508
2494
  ]
2509
2495
  }
2510
2496
  ) : position }),
2511
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2497
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2512
2498
  "div",
2513
2499
  {
2514
2500
  className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
2515
2501
  "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, {})
2502
+ 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
2503
  }
2518
2504
  )
2519
2505
  ]
@@ -2524,7 +2510,7 @@ function TrackPositionPlayPause({
2524
2510
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2525
2511
  var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
2526
2512
  var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
2527
- var import_jsx_runtime44 = require("react/jsx-runtime");
2513
+ var import_jsx_runtime45 = require("react/jsx-runtime");
2528
2514
  function TrackItem({
2529
2515
  track,
2530
2516
  position,
@@ -2548,12 +2534,12 @@ function TrackItem({
2548
2534
  loadTrackForPlayer(track.uuid);
2549
2535
  }
2550
2536
  };
2551
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
2537
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
2552
2538
  "div",
2553
2539
  {
2554
2540
  className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
2555
2541
  children: [
2556
- isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2542
+ isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2557
2543
  "div",
2558
2544
  {
2559
2545
  className: "flex items-center justify-center text-lg w-16",
@@ -2561,7 +2547,7 @@ function TrackItem({
2561
2547
  "aria-disabled": "true",
2562
2548
  children: position
2563
2549
  }
2564
- ) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
2550
+ ) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2565
2551
  TrackPositionPlayPause,
2566
2552
  {
2567
2553
  position,
@@ -2570,12 +2556,12 @@ function TrackItem({
2570
2556
  isSelected: selectedTrack?.uuid === track.uuid
2571
2557
  }
2572
2558
  ),
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 }) }) : ""
2559
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2560
+ /* @__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 }) }),
2561
+ 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
2562
  ] }),
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)(
2563
+ /* @__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 }) : "" }),
2564
+ renderDuration && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2579
2565
  "div",
2580
2566
  {
2581
2567
  className: "duration flex text-base w-16 items-center justify-center",
@@ -2583,15 +2569,15 @@ function TrackItem({
2583
2569
  children: renderDuration(track)
2584
2570
  }
2585
2571
  ),
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) })
2572
+ 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
2573
  ]
2588
2574
  }
2589
2575
  );
2590
2576
  }
2591
2577
 
2592
2578
  // 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);
2579
+ var import_jsx_runtime46 = require("react/jsx-runtime");
2580
+ var SongPageSchema2 = (0, import_transport8.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
2595
2581
  var SORTS = [
2596
2582
  { ordering: "title", label: "Title" },
2597
2583
  { ordering: "-albumYear", label: "Year" }
@@ -2601,7 +2587,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2601
2587
  const [ordering, setOrdering] = (0, import_react17.useState)("title");
2602
2588
  const { playNewTrackListFromArtist } = (0, import_genre_tree26.useTrackList)();
2603
2589
  const { playerTrackObject } = (0, import_player11.usePlayer)();
2604
- const { data, isPending, isError } = (0, import_transport9.usePlaybackQuery)({
2590
+ const { data, isPending, isError } = (0, import_transport8.usePlaybackQuery)({
2605
2591
  getBackendBaseUrl,
2606
2592
  queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
2607
2593
  queryFn: (fetchBound) => fetchBound(import_genre_tree26.libraryEndpoints.reference.songs.list(), true, false, void 0, {
@@ -2623,36 +2609,22 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2623
2609
  event.stopPropagation();
2624
2610
  void play(song.uuid);
2625
2611
  };
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)(
2629
- "button",
2630
- {
2631
- type: "button",
2632
- onClick: () => void play(),
2633
- 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
- children: [
2635
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react16.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2636
- "Play all"
2637
- ]
2638
- }
2639
- ),
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)(
2641
- "button",
2642
- {
2643
- type: "button",
2644
- "aria-pressed": ordering === sort.ordering,
2645
- onClick: () => {
2646
- setOrdering(sort.ordering);
2647
- setPage(1);
2648
- },
2649
- className: "px-2.5 py-0.5 text-[var(--color-text)] rounded-full hover:bg-[var(--color-surface-muted)] aria-pressed:bg-gray-900 aria-pressed:text-white",
2650
- children: sort.label
2612
+ 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: [
2613
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__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)(
2614
+ "button",
2615
+ {
2616
+ type: "button",
2617
+ "aria-pressed": ordering === sort.ordering,
2618
+ onClick: () => {
2619
+ setOrdering(sort.ordering);
2620
+ setPage(1);
2651
2621
  },
2652
- sort.ordering
2653
- )) })
2654
- ] }),
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)(
2622
+ className: "px-2.5 py-0.5 text-[var(--color-text)] rounded-full hover:bg-[var(--color-surface-muted)] aria-pressed:bg-gray-900 aria-pressed:text-white",
2623
+ children: sort.label
2624
+ },
2625
+ sort.ordering
2626
+ )) }) }),
2627
+ /* @__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
2628
  TrackItem,
2657
2629
  {
2658
2630
  track: song,
@@ -2661,12 +2633,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2661
2633
  renderSongLink
2662
2634
  }
2663
2635
  ) }, song.uuid)) }),
2664
- /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui19.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2636
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui19.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2665
2637
  ] }) });
2666
2638
  }
2667
2639
 
2668
2640
  // src/genre-tree/artist/useArtistPanel.tsx
2669
- var import_jsx_runtime46 = require("react/jsx-runtime");
2641
+ var import_jsx_runtime47 = require("react/jsx-runtime");
2670
2642
  function useArtistPanel({
2671
2643
  uuid,
2672
2644
  scope,
@@ -2687,26 +2659,26 @@ function useArtistPanel({
2687
2659
  return {
2688
2660
  ...panel,
2689
2661
  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" })
2662
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2663
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2664
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-4 w-32" }),
2665
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2666
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-4 w-24" }),
2667
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-16 mt-2.5" }),
2668
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-16" })
2697
2669
  ] })
2698
2670
  };
2699
2671
  case "not-found":
2700
2672
  return {
2701
2673
  ...panel,
2702
2674
  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." })
2675
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2704
2676
  };
2705
2677
  case "error":
2706
2678
  return {
2707
2679
  ...panel,
2708
2680
  title: "Artist",
2709
- content: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2681
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
2710
2682
  };
2711
2683
  case "success": {
2712
2684
  const { artist } = result;
@@ -2715,19 +2687,19 @@ function useArtistPanel({
2715
2687
  return {
2716
2688
  ...panel,
2717
2689
  title: artist.name,
2718
- content: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
2719
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
2690
+ content: /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2691
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2720
2692
  ArtistActionBand,
2721
2693
  {
2722
2694
  artist,
2723
- share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2695
+ share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2724
2696
  }
2725
2697
  ),
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)(
2698
+ artist.image && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
2699
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistHeader, { artist }),
2700
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2701
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2702
+ artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2731
2703
  ArtistSongs,
2732
2704
  {
2733
2705
  artist: { uuid: artist.uuid, name: artist.name },
@@ -2737,7 +2709,7 @@ function useArtistPanel({
2737
2709
  }
2738
2710
  )
2739
2711
  ] }),
2740
- extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2712
+ extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
2741
2713
  };
2742
2714
  }
2743
2715
  }
@@ -2749,11 +2721,11 @@ var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
2749
2721
 
2750
2722
  // src/genre-tree/song/SongPanelContent.tsx
2751
2723
  var import_react18 = require("react");
2752
- var import_lucide_react17 = require("lucide-react");
2724
+ var import_lucide_react16 = require("lucide-react");
2753
2725
  var import_ui21 = require("@behindthemusictree/ui");
2754
2726
  var import_player12 = require("@behindthemusictree/app-kit/player");
2755
2727
  var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
2756
- var import_jsx_runtime47 = require("react/jsx-runtime");
2728
+ var import_jsx_runtime48 = require("react/jsx-runtime");
2757
2729
  var numberFormat = new Intl.NumberFormat("en");
2758
2730
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2759
2731
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
@@ -2761,27 +2733,27 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2761
2733
  const [open, setOpen] = (0, import_react18.useState)(false);
2762
2734
  const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
2763
2735
  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." });
2736
+ if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-10", "aria-busy": "true" });
2737
+ else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2766
2738
  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: [
2739
+ 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: [
2740
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "font-medium", children: entry.action }),
2741
+ entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
2770
2742
  " ",
2771
2743
  "\u2014 ",
2772
2744
  entry.oldValue ?? "none",
2773
2745
  " \u2192 ",
2774
2746
  entry.newValue ?? "none"
2775
2747
  ] }) : null,
2776
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2748
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2777
2749
  entry.actorPseudo ?? "Anonymous",
2778
2750
  " \xB7 ",
2779
2751
  dateFormat.format(new Date(entry.createdOn))
2780
2752
  ] })
2781
2753
  ] }, 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 })
2754
+ 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: [
2755
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui21.PANEL_LABEL_CLASS}`, children: "History" }),
2756
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "mt-1.5", children: body })
2785
2757
  ] }) });
2786
2758
  }
2787
2759
  function SongPanelContent(props) {
@@ -2800,13 +2772,13 @@ function SongPanelContent(props) {
2800
2772
  const genre = song.genre;
2801
2773
  fields.push({
2802
2774
  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 })
2775
+ 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: [
2776
+ index > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
2777
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2806
2778
  ] }, genrePlaylist.uuid)) }) : song.genre.name
2807
2779
  });
2808
2780
  } else {
2809
- fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2781
+ fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2810
2782
  }
2811
2783
  if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
2812
2784
  const viewCount = song.youtubeVideo?.viewCount;
@@ -2816,11 +2788,11 @@ function SongPanelContent(props) {
2816
2788
  const links = [];
2817
2789
  if (musicBrainzUrl)
2818
2790
  links.push(
2819
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2791
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2820
2792
  );
2821
2793
  if (song.youtubeVideo)
2822
2794
  links.push(
2823
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2795
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2824
2796
  "a",
2825
2797
  {
2826
2798
  href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
@@ -2835,31 +2807,31 @@ function SongPanelContent(props) {
2835
2807
  if (links.length > 0)
2836
2808
  fields.push({
2837
2809
  label: "Links",
2838
- value: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "flex gap-3", children: links })
2810
+ value: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "flex gap-3", children: links })
2839
2811
  });
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)(
2812
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
2813
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_ui21.ActionBand, { children: [
2814
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2843
2815
  import_ui21.ActionButton,
2844
2816
  {
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" }),
2817
+ icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2846
2818
  label: isPlaying ? "Pause" : "Play",
2847
2819
  disabled: !!unplayableReason,
2848
2820
  title: unplayableReason ?? void 0,
2849
2821
  onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
2850
2822
  }
2851
2823
  ),
2852
- shareUrl && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2824
+ shareUrl && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2853
2825
  ] }) }),
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 })
2826
+ 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 }) }),
2827
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.FieldList, { items: fields }),
2828
+ 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)) }) }),
2829
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2858
2830
  ] });
2859
2831
  }
2860
2832
 
2861
2833
  // src/genre-tree/song/useSongPanel.tsx
2862
- var import_jsx_runtime48 = require("react/jsx-runtime");
2834
+ var import_jsx_runtime49 = require("react/jsx-runtime");
2863
2835
  function useSongPanel({
2864
2836
  uuid,
2865
2837
  scope,
@@ -2880,24 +2852,24 @@ function useSongPanel({
2880
2852
  return {
2881
2853
  ...panel,
2882
2854
  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" })
2855
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2856
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2857
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-4 w-32" }),
2858
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2859
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-4 w-24" })
2888
2860
  ] })
2889
2861
  };
2890
2862
  case "not-found":
2891
2863
  return {
2892
2864
  ...panel,
2893
2865
  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." })
2866
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2895
2867
  };
2896
2868
  case "error":
2897
2869
  return {
2898
2870
  ...panel,
2899
2871
  title: "Song",
2900
- content: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2872
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
2901
2873
  };
2902
2874
  case "success": {
2903
2875
  const { song } = result;
@@ -2905,7 +2877,7 @@ function useSongPanel({
2905
2877
  return {
2906
2878
  ...panel,
2907
2879
  title: song.title,
2908
- content: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
2880
+ content: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
2909
2881
  SongPanelContent,
2910
2882
  {
2911
2883
  song,
@@ -2958,12 +2930,12 @@ function useEntityPanel({
2958
2930
  // src/genre-tree/SizeMetricToggle.tsx
2959
2931
  var import_md2 = require("react-icons/md");
2960
2932
  var import_ui23 = require("@behindthemusictree/ui");
2961
- var import_jsx_runtime49 = require("react/jsx-runtime");
2933
+ var import_jsx_runtime50 = require("react/jsx-runtime");
2962
2934
  function SizeMetricToggle({
2963
2935
  value,
2964
2936
  onChange
2965
2937
  }) {
2966
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
2938
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
2967
2939
  import_ui23.SegmentedControl,
2968
2940
  {
2969
2941
  variant: "floating",
@@ -2971,15 +2943,15 @@ function SizeMetricToggle({
2971
2943
  value,
2972
2944
  onChange,
2973
2945
  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" }
2946
+ { 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" },
2947
+ { 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
2948
  ]
2977
2949
  }
2978
2950
  );
2979
2951
  }
2980
2952
 
2981
2953
  // src/genre-tree/GenreTreeView.tsx
2982
- var import_jsx_runtime50 = require("react/jsx-runtime");
2954
+ var import_jsx_runtime51 = require("react/jsx-runtime");
2983
2955
  var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
2984
2956
  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
2957
  var noop = () => {
@@ -3071,6 +3043,7 @@ function GenreTreeView({
3071
3043
  isFetching: isTreeFetching,
3072
3044
  genrePlaylistByNodeId,
3073
3045
  visibleGenrePlaylists,
3046
+ focusUuids,
3074
3047
  groupedGenrePlaylistsByRoot,
3075
3048
  selectedGenreUuid,
3076
3049
  selectGenre,
@@ -3130,14 +3103,14 @@ function GenreTreeView({
3130
3103
  const openSongRef = (0, import_react19.useRef)(openSong);
3131
3104
  openSongRef.current = openSong;
3132
3105
  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 }),
3106
+ (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
3107
  [renderSongLink]
3135
3108
  );
3136
3109
  const [expandedSongUuid, setExpandedSongUuid] = (0, import_react19.useState)(null);
3137
3110
  const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react19.useState)(null);
3138
3111
  const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
3139
3112
  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 }));
3113
+ 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
3114
  const getGenrePath = (genre) => {
3142
3115
  const path = [];
3143
3116
  let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
@@ -3179,7 +3152,7 @@ function GenreTreeView({
3179
3152
  selectGenrePlaylist(genrePlaylist2);
3180
3153
  }
3181
3154
  });
3182
- const builtInPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : entityPanel;
3155
+ const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
3183
3156
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react19.useState)(false);
3184
3157
  const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react19.useState)(false);
3185
3158
  const isCollapsed = collapsed ?? uncontrolledCollapsed;
@@ -3207,18 +3180,19 @@ function GenreTreeView({
3207
3180
  const obscuredInset = (0, import_ui24.useObscuredInset)(panelRef);
3208
3181
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
3209
3182
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
3210
- const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
3183
+ const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
3211
3184
  const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
3212
3185
  treeNodes,
3213
3186
  !overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
3214
3187
  resolveStyle
3215
3188
  );
3189
+ const treeStyles = (0, import_react19.useMemo)(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
3216
3190
  const [selectionHistory, setSelectionHistory] = (0, import_react19.useState)({ current: selectedGenreUuid, previous: null });
3217
3191
  if (selectionHistory.current !== selectedGenreUuid) {
3218
3192
  setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
3219
3193
  }
3220
3194
  const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
3221
- const { showPopup, hidePopup } = (0, import_popup6.usePopup)();
3195
+ const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
3222
3196
  const reopenPanel = () => {
3223
3197
  setUncontrolledCollapsed(false);
3224
3198
  onCollapsedChange?.(false);
@@ -3258,7 +3232,7 @@ function GenreTreeView({
3258
3232
  selectGenre(uuid);
3259
3233
  },
3260
3234
  expanded: isExpanded,
3261
- treeRelations: relations,
3235
+ treeStyles,
3262
3236
  playback,
3263
3237
  genrePlaylist: selectedGenrePlaylist,
3264
3238
  band: {
@@ -3269,7 +3243,7 @@ function GenreTreeView({
3269
3243
  onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
3270
3244
  onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
3271
3245
  onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
3272
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3246
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3273
3247
  ChangeParentPopup,
3274
3248
  {
3275
3249
  genrePlaylist: { ...genrePlaylist, criteria },
@@ -3303,15 +3277,15 @@ function GenreTreeView({
3303
3277
  const isRotationControlled = controlledAllowWheelRotation !== void 0;
3304
3278
  const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
3305
3279
  let activePanel = null;
3306
- if (overlayPanel) {
3280
+ if (otherPanel) {
3307
3281
  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
3282
+ title: otherPanel.title,
3283
+ content: otherPanel.content,
3284
+ aside: otherPanel.extraContent,
3285
+ expanded: otherPanel.expanded ?? false,
3286
+ onExpandedChange: otherPanel.onExpandedChange,
3287
+ onClose: otherPanel.onClose,
3288
+ searchName: otherPanel.title
3315
3289
  };
3316
3290
  } else if (selectedGenreUuid) {
3317
3291
  activePanel = {
@@ -3321,38 +3295,28 @@ function GenreTreeView({
3321
3295
  expanded: isExpanded,
3322
3296
  onExpandedChange: changeExpanded,
3323
3297
  onClose: clearSelection,
3324
- titleInSearch: true,
3298
+ searchName: genreContent.name,
3325
3299
  headerAccent: genreContent.headerAccent
3326
3300
  };
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
3301
  }
3338
3302
  const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
3339
3303
  const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
3340
3304
  edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
3341
3305
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
3342
3306
  } : void 0;
3343
- const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3307
+ const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3344
3308
  import_ui24.IconButton,
3345
3309
  {
3346
3310
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
3347
3311
  style: activePanel.headerAccent && { color: "inherit" },
3348
3312
  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" })
3313
+ children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react18.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react18.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
3350
3314
  }
3351
3315
  ) : null;
3352
3316
  const renderArtistFilter = () => {
3353
3317
  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)(
3318
+ 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: [
3319
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3356
3320
  "button",
3357
3321
  {
3358
3322
  type: "button",
@@ -3364,7 +3328,7 @@ function GenreTreeView({
3364
3328
  children: filterArtist.name
3365
3329
  }
3366
3330
  ),
3367
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3331
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3368
3332
  "button",
3369
3333
  {
3370
3334
  type: "button",
@@ -3376,8 +3340,8 @@ function GenreTreeView({
3376
3340
  )
3377
3341
  ] });
3378
3342
  };
3379
- const actions = /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_jsx_runtime50.Fragment, { children: [
3380
- !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3343
+ const actions = /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
3344
+ !isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3381
3345
  GenreTreeSearch,
3382
3346
  {
3383
3347
  artistBaseUrl,
@@ -3400,9 +3364,9 @@ function GenreTreeView({
3400
3364
  if (songUuid) changeSong(null);
3401
3365
  selectGenre(uuid);
3402
3366
  },
3403
- selectedName: selectedGenreUuid ? genreContent.name : typeof builtInPanel?.title === "string" ? builtInPanel.title : null,
3367
+ selectedName: activePanel?.searchName ?? null,
3404
3368
  onClear: activePanel?.onClose,
3405
- showClear: activePanel?.titleInSearch ?? false,
3369
+ showClear: activePanel !== null,
3406
3370
  clearLabel: "Close panel",
3407
3371
  onPrevious: handlePrevious,
3408
3372
  onQueryChange: onSearchQueryChange,
@@ -3410,9 +3374,9 @@ function GenreTreeView({
3410
3374
  extraGroups: extraSearchGroups
3411
3375
  }
3412
3376
  ),
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)(
3377
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3378
+ /* @__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: [
3379
+ !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3416
3380
  import_ui24.SegmentedControl,
3417
3381
  {
3418
3382
  "aria-label": "Tree view mode",
@@ -3426,13 +3390,13 @@ function GenreTreeView({
3426
3390
  ]
3427
3391
  }
3428
3392
  ),
3429
- !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3393
+ !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3430
3394
  "div",
3431
3395
  {
3432
3396
  className: "flex items-center gap-1",
3433
3397
  role: "group",
3434
3398
  "aria-label": "Tree display options",
3435
- children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3399
+ children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3436
3400
  import_ui24.Button,
3437
3401
  {
3438
3402
  variant: allowWheelRotation ? "default" : "outline",
@@ -3443,52 +3407,53 @@ function GenreTreeView({
3443
3407
  )
3444
3408
  }
3445
3409
  ),
3446
- !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3410
+ !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3447
3411
  import_ui24.IconTextButton,
3448
3412
  {
3449
- icon: import_lucide_react18.Plus,
3413
+ icon: import_lucide_react17.Plus,
3450
3414
  text: "Add root",
3451
3415
  onClick: () => handleGenreCreationAction(null)
3452
3416
  }
3453
3417
  )
3454
3418
  ] })
3455
3419
  ] });
3456
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(
3420
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
3457
3421
  "div",
3458
3422
  {
3459
3423
  "data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
3460
3424
  className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
3461
3425
  children: [
3462
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3426
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3463
3427
  "div",
3464
3428
  {
3465
3429
  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
3430
  children: actions
3467
3431
  }
3468
3432
  ),
3469
- !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3433
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3470
3434
  "div",
3471
3435
  {
3472
3436
  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
3437
  children: renderArtistFilter()
3474
3438
  }
3475
3439
  ),
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: [
3440
+ /* @__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: [
3441
+ !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
3442
+ 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
3443
  "Couldn't load the genre tree.",
3480
3444
  " ",
3481
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3445
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
3482
3446
  ] }) : viewMode === "wheel" ? (
3483
3447
  // The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
3484
3448
  // and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
3485
3449
  // Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
3486
3450
  // 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)(
3451
+ /* @__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
3452
  GenrePlaylistTreeWheel,
3489
3453
  {
3490
3454
  scope,
3491
3455
  genrePlaylists: visibleGenrePlaylists,
3456
+ focusUuids,
3492
3457
  handleGenreCreationAction,
3493
3458
  handleGenreRenameAction,
3494
3459
  additionalActions,
@@ -3501,9 +3466,9 @@ function GenreTreeView({
3501
3466
  allowWheelRotation,
3502
3467
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3503
3468
  busy,
3504
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-2", children: [
3469
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center gap-2", children: [
3505
3470
  floatingControlsLeading,
3506
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3471
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3507
3472
  ] }) : void 0,
3508
3473
  defaultViewport,
3509
3474
  viewportRestore,
@@ -3512,11 +3477,12 @@ function GenreTreeView({
3512
3477
  ) }) })
3513
3478
  ) : viewMode === "pop-core" ? (
3514
3479
  // 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)(
3480
+ /* @__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
3481
  GenrePlaylistTreeWheelRadialPopCore,
3517
3482
  {
3518
3483
  scope,
3519
3484
  genrePlaylists: visibleGenrePlaylists,
3485
+ focusUuids,
3520
3486
  handleGenreCreationAction,
3521
3487
  handleGenreRenameAction,
3522
3488
  additionalActions,
@@ -3529,9 +3495,9 @@ function GenreTreeView({
3529
3495
  allowWheelRotation,
3530
3496
  sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
3531
3497
  busy,
3532
- floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex items-center gap-2", children: [
3498
+ floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "flex items-center gap-2", children: [
3533
3499
  floatingControlsLeading,
3534
- canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3500
+ canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3535
3501
  ] }) : void 0,
3536
3502
  defaultViewport,
3537
3503
  viewportRestore,
@@ -3539,12 +3505,13 @@ function GenreTreeView({
3539
3505
  ...focus
3540
3506
  }
3541
3507
  ) }) })
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)(
3508
+ ) : 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
3509
  GenrePlaylistTreeWheelRadialPopCore,
3544
3510
  {
3545
3511
  outline: true,
3546
3512
  scope,
3547
3513
  genrePlaylists: visibleGenrePlaylists,
3514
+ focusUuids,
3548
3515
  handleGenreCreationAction,
3549
3516
  handleGenreRenameAction,
3550
3517
  additionalActions,
@@ -3555,13 +3522,13 @@ function GenreTreeView({
3555
3522
  selectedNodeId,
3556
3523
  readOnly
3557
3524
  }
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(
3525
+ ) }) : /* @__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
3526
  ([uuid, genrePlaylistTreePerRoot]) => {
3560
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3527
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3561
3528
  "div",
3562
3529
  {
3563
3530
  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)(
3531
+ children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3565
3532
  GenrePlaylistTreePerRoot,
3566
3533
  {
3567
3534
  scope,
@@ -3574,6 +3541,7 @@ function GenreTreeView({
3574
3541
  onNodeHover: handleNodeHover,
3575
3542
  ...hostPanel,
3576
3543
  playback,
3544
+ focusUuids,
3577
3545
  selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
3578
3546
  readOnly,
3579
3547
  wheelZoom: "modifier",
@@ -3587,13 +3555,13 @@ function GenreTreeView({
3587
3555
  );
3588
3556
  }
3589
3557
  ) }),
3590
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
3558
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3591
3559
  import_ui24.DetailPanel,
3592
3560
  {
3593
3561
  ref: panelRef,
3594
3562
  side: "left",
3595
3563
  title: activePanel?.title ?? "",
3596
- titleInSearch: activePanel?.titleInSearch,
3564
+ titleInSearch: !isLoading && !isError,
3597
3565
  headerAccent: activePanel?.headerAccent,
3598
3566
  headerActions: expandButton,
3599
3567
  onClose: activePanel?.onClose ?? noop,
@@ -3602,8 +3570,7 @@ function GenreTreeView({
3602
3570
  onExpandedChange: activePanel?.onExpandedChange,
3603
3571
  aside: activePanel?.aside,
3604
3572
  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: [
3573
+ children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
3607
3574
  activePanel.content,
3608
3575
  activePanel.aside
3609
3576
  ] }) : activePanel?.content
@@ -3620,11 +3587,11 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
3620
3587
 
3621
3588
  // src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
3622
3589
  var import_react20 = require("react");
3623
- var import_lucide_react20 = require("lucide-react");
3590
+ var import_lucide_react19 = require("lucide-react");
3624
3591
  var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
3625
3592
  var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
3626
3593
  var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
3627
- var import_jsx_runtime51 = require("react/jsx-runtime");
3594
+ var import_jsx_runtime52 = require("react/jsx-runtime");
3628
3595
  var useBrowserLayoutEffect = typeof window === "undefined" ? import_react20.useEffect : import_react20.useLayoutEffect;
3629
3596
  function TrackListSidebar({
3630
3597
  className: className2 = "",
@@ -3726,25 +3693,25 @@ function TrackListSidebar({
3726
3693
  }
3727
3694
  };
3728
3695
  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)(
3696
+ return trackList ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3730
3697
  "div",
3731
3698
  {
3732
3699
  "data-theme": "dark",
3733
3700
  className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
3734
3701
  style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
3735
3702
  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 }) })
3703
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3704
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3705
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "origin flex text-xl", children: [
3706
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3707
+ /* @__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
3708
  ] }),
3742
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3709
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3743
3710
  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
3711
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3745
3712
  ] })
3746
3713
  ] }),
3747
- (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3714
+ (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3748
3715
  "button",
3749
3716
  {
3750
3717
  type: "button",
@@ -3753,11 +3720,11 @@ function TrackListSidebar({
3753
3720
  "aria-expanded": !isCollapsed,
3754
3721
  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
3722
  onClick: isCollapsed ? onExpand : onMinimize,
3756
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_lucide_react20.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3723
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react19.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3757
3724
  }
3758
3725
  )
3759
3726
  ] }),
3760
- !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(
3727
+ !isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3761
3728
  "ul",
3762
3729
  {
3763
3730
  ref: trackListRef,
@@ -3767,7 +3734,7 @@ function TrackListSidebar({
3767
3734
  onPointerDown: markUserScrollIntent,
3768
3735
  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
3736
  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)(
3737
+ 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
3738
  TrackItem,
3772
3739
  {
3773
3740
  track,
@@ -3778,7 +3745,7 @@ function TrackListSidebar({
3778
3745
  renderSongLink
3779
3746
  }
3780
3747
  ) }, track.uuid)),
3781
- hasMore && /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3748
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3782
3749
  ]
3783
3750
  }
3784
3751
  )
@@ -3795,10 +3762,10 @@ var import_genre_tree34 = require("@behindthemusictree/app-kit/genre-tree");
3795
3762
  var import_ui25 = require("@behindthemusictree/ui");
3796
3763
 
3797
3764
  // src/genre-tree/PlayerControls.tsx
3798
- var import_lucide_react21 = require("lucide-react");
3765
+ var import_lucide_react20 = require("lucide-react");
3799
3766
  var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
3800
3767
  var import_player13 = require("@behindthemusictree/app-kit/player");
3801
- var import_jsx_runtime52 = require("react/jsx-runtime");
3768
+ var import_jsx_runtime53 = require("react/jsx-runtime");
3802
3769
  function PlayerControls() {
3803
3770
  const {
3804
3771
  duration,
@@ -3817,15 +3784,15 @@ function PlayerControls() {
3817
3784
  const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
3818
3785
  const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
3819
3786
  const isPlaying = playState === import_player13.PlayStates.PLAYING;
3820
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
3787
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3821
3788
  "section",
3822
3789
  {
3823
3790
  "aria-label": "Player controls",
3824
3791
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3825
3792
  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)(
3793
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3794
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree33.formatTime)(safeCurrentTime) }),
3795
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3829
3796
  "input",
3830
3797
  {
3831
3798
  "aria-label": "Seek",
@@ -3839,11 +3806,11 @@ function PlayerControls() {
3839
3806
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3840
3807
  }
3841
3808
  ),
3842
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3809
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree33.formatTime)(safeDuration) })
3843
3810
  ] }),
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)(
3811
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3812
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3813
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3847
3814
  "button",
3848
3815
  {
3849
3816
  type: "button",
@@ -3852,10 +3819,10 @@ function PlayerControls() {
3852
3819
  disabled: !isReady || !hasPreviousTrack,
3853
3820
  onClick: playPreviousTrack,
3854
3821
  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" })
3822
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react20.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3856
3823
  }
3857
3824
  ),
3858
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3825
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3859
3826
  "button",
3860
3827
  {
3861
3828
  type: "button",
@@ -3864,10 +3831,10 @@ function PlayerControls() {
3864
3831
  disabled: !isReady,
3865
3832
  onClick: handlePlayPauseAction,
3866
3833
  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" })
3834
+ children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react20.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react20.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3868
3835
  }
3869
3836
  ),
3870
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3837
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3871
3838
  "button",
3872
3839
  {
3873
3840
  type: "button",
@@ -3876,14 +3843,14 @@ function PlayerControls() {
3876
3843
  disabled: !isReady || !hasNextTrack,
3877
3844
  onClick: playNextTrack,
3878
3845
  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" })
3846
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react20.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3880
3847
  }
3881
3848
  )
3882
3849
  ] }),
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)(
3850
+ /* @__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: [
3851
+ volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react20.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react20.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3852
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("span", { className: "sr-only", children: "Volume" }),
3853
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3887
3854
  "input",
3888
3855
  {
3889
3856
  "aria-label": "Volume",
@@ -3905,7 +3872,7 @@ function PlayerControls() {
3905
3872
  }
3906
3873
 
3907
3874
  // src/genre-tree/PlayerPanel.tsx
3908
- var import_jsx_runtime53 = require("react/jsx-runtime");
3875
+ var import_jsx_runtime54 = require("react/jsx-runtime");
3909
3876
  var PANEL_ID = "player-panel";
3910
3877
  var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
3911
3878
  function PlayerPanel({
@@ -3945,7 +3912,7 @@ function PlayerPanel({
3945
3912
  onClick: () => setIsExtendRequested(true),
3946
3913
  expanded: false
3947
3914
  } : null;
3948
- return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3915
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
3949
3916
  "div",
3950
3917
  {
3951
3918
  id: PANEL_ID,
@@ -3958,7 +3925,7 @@ function PlayerPanel({
3958
3925
  ),
3959
3926
  style: isExtended ? { width: extendedWidth } : void 0,
3960
3927
  children: [
3961
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3928
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
3962
3929
  import_ui25.PanelEdgeTab,
3963
3930
  {
3964
3931
  side: "right",
@@ -3968,7 +3935,7 @@ function PlayerPanel({
3968
3935
  className: "absolute right-full top-1/2 -translate-y-1/2"
3969
3936
  }
3970
3937
  ),
3971
- /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
3938
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
3972
3939
  "div",
3973
3940
  {
3974
3941
  className: (0, import_tailwind_merge.twMerge)(
@@ -3976,20 +3943,20 @@ function PlayerPanel({
3976
3943
  isExtended ? "flex-row" : "flex-col"
3977
3944
  ),
3978
3945
  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: [
3946
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3947
+ /* @__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 }) }),
3948
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "relative w-full shrink-0", children: [
3949
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3950
+ playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3984
3951
  playerTrackObject.loadError,
3985
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3952
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3986
3953
  void retryPlayback();
3987
3954
  }, children: "Retry" })
3988
3955
  ] })
3989
3956
  ] }),
3990
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(PlayerControls, {})
3957
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(PlayerControls, {})
3991
3958
  ] }),
3992
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
3959
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
3993
3960
  TrackListSidebar,
3994
3961
  {
3995
3962
  layout: "inline",
@@ -4016,11 +3983,11 @@ function PlayerPanel({
4016
3983
  var import_uuid = require("uuid");
4017
3984
  var import_ai = require("react-icons/ai");
4018
3985
  var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
4019
- var import_jsx_runtime54 = require("react/jsx-runtime");
3986
+ var import_jsx_runtime55 = require("react/jsx-runtime");
4020
3987
  function Rating({ rating, handleChange }) {
4021
3988
  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)(
3989
+ 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: [
3990
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4024
3991
  "input",
4025
3992
  {
4026
3993
  id: `star-${instanceId}-${i}`,
@@ -4032,16 +3999,16 @@ function Rating({ rating, handleChange }) {
4032
3999
  onChange: handleChange
4033
4000
  }
4034
4001
  ),
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)(
4002
+ /* @__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: [
4003
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4037
4004
  import_ai.AiOutlineStar,
4038
4005
  {
4039
4006
  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
4007
  }
4041
4008
  ),
4042
- /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "flex-grow w-8" }),
4009
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "flex-grow w-8" }),
4043
4010
  " "
4044
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
4011
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4045
4012
  import_ai.AiFillStar,
4046
4013
  {
4047
4014
  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 +4020,7 @@ function Rating({ rating, handleChange }) {
4053
4020
  // src/genre-tree/EntityDetailPanel.tsx
4054
4021
  var import_react22 = require("react");
4055
4022
  var import_ui26 = require("@behindthemusictree/ui");
4056
- var import_jsx_runtime55 = require("react/jsx-runtime");
4023
+ var import_jsx_runtime56 = require("react/jsx-runtime");
4057
4024
  function EntityDetailPanel({
4058
4025
  scope,
4059
4026
  getBackendBaseUrl,
@@ -4076,8 +4043,8 @@ function EntityDetailPanel({
4076
4043
  getBackendBaseUrl,
4077
4044
  getArtistBackendBaseUrl,
4078
4045
  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 })),
4046
+ 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 })),
4047
+ renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
4081
4048
  song: {
4082
4049
  uuid: songUuid,
4083
4050
  expanded: songUuid !== null && expandedSongUuid === songUuid,
@@ -4093,7 +4060,7 @@ function EntityDetailPanel({
4093
4060
  ...navigation
4094
4061
  });
4095
4062
  if (!panel) return null;
4096
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
4063
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
4097
4064
  import_ui26.DetailPanel,
4098
4065
  {
4099
4066
  side: "left",
@@ -4105,7 +4072,7 @@ function EntityDetailPanel({
4105
4072
  onExpandedChange: panel.onExpandedChange,
4106
4073
  aside: panel.extraContent,
4107
4074
  edgeTab,
4108
- children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
4075
+ children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { children: [
4109
4076
  panel.content,
4110
4077
  panel.extraContent
4111
4078
  ] }) : panel.content