@behindthemusictree/app-kit-ui 33.2.0 → 34.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -593,13 +593,14 @@ var import_lucide_react15 = require("lucide-react");
593
593
  var import_lucide_react16 = require("lucide-react");
594
594
  var import_ui19 = require("@behindthemusictree/ui");
595
595
  var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
596
- var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
596
+ var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
597
597
  var import_popup6 = require("@behindthemusictree/app-kit/popup");
598
598
 
599
599
  // src/genre-tree/playlist-tree/size-metric.ts
600
+ var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
600
601
  var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
601
602
  var genrePlaylistSize = (genrePlaylist, metric) => {
602
- if (metric === "songs") return genrePlaylist.tracksCount;
603
+ if (metric === "songs") return (0, import_genre_tree.genrePlaylistTrackCount)(genrePlaylist);
603
604
  if (genrePlaylist.viewsCount === void 0) {
604
605
  throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
605
606
  }
@@ -623,6 +624,7 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
623
624
  }
624
625
 
625
626
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
627
+ var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
626
628
  var import_react4 = require("react");
627
629
  var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
628
630
  var import_player = require("@behindthemusictree/app-kit/player");
@@ -631,29 +633,28 @@ var import_transport3 = require("@behindthemusictree/app-kit/transport");
631
633
 
632
634
  // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
633
635
  var import_react3 = require("react");
634
- var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
636
+ var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
635
637
  function useGenrePlaylistPlayback({
636
638
  scope,
637
639
  genrePlaylists,
638
640
  artist = null
639
641
  }) {
640
- const { trackList, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0, import_genre_tree.useTrackList)();
642
+ const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
641
643
  const origin = trackList?.origin;
642
644
  const playingNodeId = (0, import_react3.useMemo)(() => {
643
- if (!origin) return null;
644
- if (!artist) return origin.type === import_genre_tree.TrackListOriginType.GENRE_PLAYLIST ? origin.uuid : null;
645
- if (origin.type !== import_genre_tree.TrackListOriginType.ARTIST || origin.uuid !== artist.uuid) return null;
646
- const { genreUuid } = origin;
647
- return genreUuid ? genrePlaylists.find((gp) => gp.criteria?.uuid === genreUuid)?.uuid ?? null : null;
648
- }, [origin, artist, genrePlaylists]);
649
- const play = (0, import_react3.useCallback)(
650
- async (genrePlaylist) => {
651
- if (!artist) return playNewTrackListFromGenrePlaylist(genrePlaylist, scope);
652
- if (!genrePlaylist.criteria) return;
653
- return playNewTrackListFromArtist(artist, scope, { ordering: "title", genreUuid: genrePlaylist.criteria.uuid });
654
- },
655
- [artist, scope, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist]
656
- );
645
+ if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
646
+ const playlistOrigin = origin;
647
+ if (playlistOrigin.artistUuid !== artist?.uuid) return null;
648
+ const playlist = genrePlaylists.find((item) => item.uuid === origin.uuid) ?? playlistOrigin.criteriaPlaylist;
649
+ const expected = scope === "reference" && playlist && !("criteria" in playlist && playlist.criteria === null) ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
650
+ if (playlistOrigin.selection && expected && (0, import_genre_tree2.genreMusicSelectionKey)(playlistOrigin.selection).join() !== (0, import_genre_tree2.genreMusicSelectionKey)(expected).join()) return null;
651
+ return origin.uuid;
652
+ }, [origin, artist, genrePlaylists, scope]);
653
+ const play = (0, import_react3.useCallback)(async (playlist) => {
654
+ if (artist && !playlist.criteria) return;
655
+ const selection = scope === "reference" && playlist.criteria !== null ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
656
+ return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
657
+ }, [artist, scope, playNewTrackListFromGenrePlaylist]);
657
658
  return (0, import_react3.useMemo)(() => ({ playingNodeId, play }), [playingNodeId, play]);
658
659
  }
659
660
 
@@ -688,7 +689,7 @@ function GenrePlaylistTreePerRoot({
688
689
  id: genrePlaylist.uuid,
689
690
  parentId: genrePlaylist.parent?.uuid ?? null,
690
691
  name: genrePlaylist.name,
691
- itemCount: genrePlaylist.tracksCount,
692
+ itemCount: (0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist),
692
693
  actionable: Boolean(genrePlaylist.criteria),
693
694
  side: genrePlaylist.criteria?.side ?? void 0,
694
695
  wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
@@ -704,7 +705,7 @@ function GenrePlaylistTreePerRoot({
704
705
  setIsPlaying(!isPlaying);
705
706
  return;
706
707
  }
707
- if (genrePlaylist.tracksCount === 0) {
708
+ if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
708
709
  return;
709
710
  }
710
711
  play(genrePlaylist).catch((error) => {
@@ -768,6 +769,7 @@ function GenrePlaylistTreePerRoot({
768
769
  }
769
770
 
770
771
  // src/genre-tree/playlist-tree/TreeWheel.tsx
772
+ var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
771
773
  var import_react5 = require("react");
772
774
  var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
773
775
  var import_player2 = require("@behindthemusictree/app-kit/player");
@@ -820,7 +822,7 @@ function GenrePlaylistTreeWheel({
820
822
  setIsPlaying(!isPlaying);
821
823
  return;
822
824
  }
823
- if (genrePlaylist.tracksCount === 0) {
825
+ if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
824
826
  return;
825
827
  }
826
828
  play(genrePlaylist).catch((error) => {
@@ -885,6 +887,7 @@ function GenrePlaylistTreeWheel({
885
887
  }
886
888
 
887
889
  // src/genre-tree/playlist-tree/TreeWheelRadialPopCore.tsx
890
+ var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
888
891
  var import_react6 = require("react");
889
892
  var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
890
893
  var import_player3 = require("@behindthemusictree/app-kit/player");
@@ -929,7 +932,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
929
932
  setIsPlaying(!isPlaying);
930
933
  return;
931
934
  }
932
- if (genrePlaylist.tracksCount === 0) {
935
+ if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
933
936
  return;
934
937
  }
935
938
  play(genrePlaylist).catch((error) => {
@@ -1027,7 +1030,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
1027
1030
  // src/genre-tree/GenreSearch.tsx
1028
1031
  var import_lucide_react7 = require("lucide-react");
1029
1032
  var import_ui7 = require("@behindthemusictree/ui");
1030
- var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
1033
+ var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
1031
1034
 
1032
1035
  // src/genre-tree/search/SearchBox.tsx
1033
1036
  var import_react8 = require("react");
@@ -1083,10 +1086,10 @@ var SEARCH_FAILED_STATUS = "Search failed";
1083
1086
  var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
1084
1087
 
1085
1088
  // src/genre-tree/search/useArtistSearchGroup.tsx
1086
- var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
1089
+ var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
1087
1090
  var import_jsx_runtime17 = require("react/jsx-runtime");
1088
1091
  function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1089
- const search = (0, import_genre_tree2.useArtistSearch)(getBaseUrl, enabled ? text : "");
1092
+ const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
1090
1093
  if (!enabled) return void 0;
1091
1094
  return {
1092
1095
  key: "artists",
@@ -1114,17 +1117,17 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1114
1117
  }
1115
1118
 
1116
1119
  // src/genre-tree/search/useSongSearchGroup.tsx
1117
- var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
1120
+ var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
1118
1121
  var import_jsx_runtime18 = require("react/jsx-runtime");
1119
1122
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1120
- const search = (0, import_genre_tree3.useSongSearch)(getBaseUrl, enabled ? text : "");
1123
+ const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
1121
1124
  if (!enabled) return void 0;
1122
1125
  return {
1123
1126
  key: "songs",
1124
1127
  label: "Songs",
1125
1128
  items: search.songs.map((song) => ({
1126
1129
  key: song.uuid,
1127
- content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: (0, import_genre_tree3.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
1130
+ content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
1128
1131
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate font-medium", children: song.title }),
1129
1132
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
1130
1133
  ] }),
@@ -1136,10 +1139,10 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
1136
1139
  }
1137
1140
 
1138
1141
  // src/genre-tree/search/useGenreRemoteSearchGroup.tsx
1139
- var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
1142
+ var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
1140
1143
  var import_jsx_runtime19 = require("react/jsx-runtime");
1141
1144
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
1142
- const search = (0, import_genre_tree4.useGenreRemoteSearch)(getBaseUrl, text, options);
1145
+ const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
1143
1146
  const genres = filter ? search.genres.filter(filter) : search.genres;
1144
1147
  return {
1145
1148
  key,
@@ -1173,7 +1176,7 @@ function GenreSearch({
1173
1176
  extraGroups = [],
1174
1177
  onFocus
1175
1178
  }) {
1176
- const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree5.useGenreSearch)({
1179
+ const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree9.useGenreSearch)({
1177
1180
  genrePlaylists,
1178
1181
  onSelect,
1179
1182
  selectedName,
@@ -1225,6 +1228,8 @@ function GenreSearch({
1225
1228
  }
1226
1229
 
1227
1230
  // src/genre-tree/GenreInfoPanelBody.tsx
1231
+ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
1232
+ var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
1228
1233
  var import_player6 = require("@behindthemusictree/app-kit/player");
1229
1234
 
1230
1235
  // src/genre-tree/GenreActionBand.tsx
@@ -1547,7 +1552,7 @@ function GenreActionBand({
1547
1552
 
1548
1553
  // src/genre-tree/GenreDetailExtras.tsx
1549
1554
  var import_ui9 = require("@behindthemusictree/ui");
1550
- var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1555
+ var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
1551
1556
 
1552
1557
  // src/genre-tree/ClampedText.tsx
1553
1558
  var import_react10 = require("react");
@@ -1590,20 +1595,21 @@ var import_player4 = require("@behindthemusictree/app-kit/player");
1590
1595
  var import_player5 = require("@behindthemusictree/app-kit/player");
1591
1596
  var import_popup5 = require("@behindthemusictree/app-kit/popup");
1592
1597
  var import_transport8 = require("@behindthemusictree/app-kit/transport");
1593
- var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
1594
- var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
1595
- var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
1596
- var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
1598
+ var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1599
+ var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1600
+ var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1601
+ var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
1602
+ var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
1597
1603
 
1598
1604
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1599
- var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
1605
+ var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
1600
1606
  var import_jsx_runtime27 = require("react/jsx-runtime");
1601
1607
  function MusicBrainzRecordingLink({
1602
1608
  song,
1603
1609
  children,
1604
1610
  className: className2 = ""
1605
1611
  }) {
1606
- const url = (0, import_genre_tree6.musicBrainzRecordingUrl)(song);
1612
+ const url = (0, import_genre_tree10.musicBrainzRecordingUrl)(song);
1607
1613
  if (!url) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children });
1608
1614
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1609
1615
  "a",
@@ -1620,11 +1626,11 @@ function MusicBrainzRecordingLink({
1620
1626
  }
1621
1627
 
1622
1628
  // src/genre-tree/GenrePlaylistTracks.tsx
1623
- var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1629
+ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
1624
1630
  var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
1625
1631
  var import_jsx_runtime28 = require("react/jsx-runtime");
1626
1632
  var CATALOGUE_PAGE_SIZE = 100;
1627
- var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree9.SongDetailedSchema).extend({
1633
+ var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema).extend({
1628
1634
  overallTotal: import_zod.z.number().nullable(),
1629
1635
  page: import_zod.z.number().nullable(),
1630
1636
  totalPages: import_zod.z.number().nullable()
@@ -1642,6 +1648,7 @@ var nextCursor = (next) => {
1642
1648
  function GenrePlaylistTracks({
1643
1649
  genreUuid,
1644
1650
  genrePlaylist,
1651
+ selection,
1645
1652
  scope,
1646
1653
  getBackendBaseUrl,
1647
1654
  renderArtistLink,
@@ -1651,7 +1658,7 @@ function GenrePlaylistTracks({
1651
1658
  renderTracksToolbar,
1652
1659
  artist = null
1653
1660
  }) {
1654
- const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0, import_genre_tree10.useTrackList)();
1661
+ const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree15.useTrackList)();
1655
1662
  const { session, sessionRestored } = (0, import_auth2.useSession)();
1656
1663
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
1657
1664
  const { showPopup } = (0, import_popup5.usePopup)();
@@ -1660,15 +1667,15 @@ function GenrePlaylistTracks({
1660
1667
  const { fetch } = (0, import_transport5.useFetchWrapper)(getBackendBaseUrl);
1661
1668
  const playback = (0, import_transport5.usePlaybackContext)();
1662
1669
  const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
1663
- queryKey: [...scope === "me" ? import_genre_tree7.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree8.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : import_genre_tree8.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), ...playback.key ? [playback.key] : []],
1670
+ queryKey: [...scope === "me" ? import_genre_tree12.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
1664
1671
  queryFn: async ({ pageParam }) => {
1665
1672
  if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
1666
1673
  const response = await fetch(
1667
- import_genre_tree8.libraryEndpoints.reference.songs.list(),
1674
+ import_genre_tree13.libraryEndpoints.reference.songs.list(),
1668
1675
  true,
1669
1676
  false,
1670
1677
  {},
1671
- { genre: genreUuid, ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1678
+ { genre: genreUuid, ...(0, import_genre_tree11.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1672
1679
  );
1673
1680
  const parsed = (0, import_transport7.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1674
1681
  return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
@@ -1692,7 +1699,12 @@ function GenrePlaylistTracks({
1692
1699
  const tracks = pages.flatMap((page) => page.tracks);
1693
1700
  const total = pages[0]?.total ?? void 0;
1694
1701
  const play = (startSongUuid) => {
1695
- const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
1702
+ const started = artist ? playNewTrackListFromArtist(artist, scope, {
1703
+ startSongUuid,
1704
+ ordering: "title",
1705
+ genreUuid,
1706
+ ...selection && { selection }
1707
+ }) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genrePlaylist.name }, scope, { startSongUuid, ...selection && { selection } }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
1696
1708
  started.catch((error) => {
1697
1709
  console.error("Failed to play genre playlist:", error);
1698
1710
  showPopup(/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
@@ -1712,14 +1724,14 @@ function GenrePlaylistTracks({
1712
1724
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1713
1725
  const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
1714
1726
  const text = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: TEXT_CELL, children: [
1715
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree9.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1727
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree14.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1716
1728
  track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1717
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_genre_tree11.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1729
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1718
1730
  track.album && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1719
1731
  ] });
1720
1732
  const leading = renderSongLeading?.(track, index);
1721
1733
  const actions = renderSongActions?.(track);
1722
- const isUnplayable = (0, import_genre_tree9.isSong)(track) && (0, import_genre_tree9.songUnplayableReason)(track) !== null;
1734
+ const isUnplayable = (0, import_genre_tree14.isSong)(track) && (0, import_genre_tree14.songUnplayableReason)(track) !== null;
1723
1735
  if (isUnplayable) {
1724
1736
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1725
1737
  leading,
@@ -1773,6 +1785,7 @@ var import_jsx_runtime29 = require("react/jsx-runtime");
1773
1785
  function GenreDetailExtras({
1774
1786
  genreUuid,
1775
1787
  genrePlaylist,
1788
+ selection,
1776
1789
  wikidataId,
1777
1790
  scope,
1778
1791
  getBackendBaseUrl,
@@ -1786,7 +1799,7 @@ function GenreDetailExtras({
1786
1799
  ...trackSlots
1787
1800
  }) {
1788
1801
  const trackNoun = (0, import_track_noun3.useTrackNoun)();
1789
- const { data: overview, isPending } = (0, import_genre_tree12.useFetchGenreOverview)(
1802
+ const { data: overview, isPending } = (0, import_genre_tree17.useFetchGenreOverview)(
1790
1803
  genreUuid,
1791
1804
  scope,
1792
1805
  getBackendBaseUrl,
@@ -1839,6 +1852,7 @@ function GenreDetailExtras({
1839
1852
  {
1840
1853
  genreUuid,
1841
1854
  genrePlaylist,
1855
+ selection,
1842
1856
  scope,
1843
1857
  getBackendBaseUrl,
1844
1858
  renderArtistLink,
@@ -1868,7 +1882,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
1868
1882
  GenreActionBand,
1869
1883
  {
1870
1884
  ...bandProps,
1871
- onPlayPause: genrePlaylist.tracksCount > 0 ? handlePlayPause : void 0,
1885
+ onPlayPause: ("genreOnlyTracksCount" in genrePlaylist ? (0, import_genre_tree18.genreMusicCount)(genrePlaylist) : genrePlaylist.tracksCount) > 0 ? handlePlayPause : void 0,
1872
1886
  isPlaying: isCurrentTrackList && isPlaying
1873
1887
  }
1874
1888
  );
@@ -1878,6 +1892,7 @@ function GenreInfoPanelBody({
1878
1892
  family,
1879
1893
  genreUuid,
1880
1894
  genrePlaylist,
1895
+ catalogue,
1881
1896
  wikidataId,
1882
1897
  scope,
1883
1898
  getBackendBaseUrl,
@@ -1885,9 +1900,30 @@ function GenreInfoPanelBody({
1885
1900
  band,
1886
1901
  extras
1887
1902
  }) {
1903
+ const noun = (0, import_track_noun4.useTrackNoun)();
1904
+ const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist) : void 0;
1905
+ const selection = summary ? (0, import_genre_tree18.defaultGenreMusicSelection)(summary) : void 0;
1906
+ const describeCounts = (counts) => {
1907
+ const scopes = counts.genreAndSubgenresTracksCount;
1908
+ return "canonical" in scopes ? `${counts.genreOnlyTracksCount} this genre only \xB7 ${scopes.canonical} with canonical subgenres \xB7 ${scopes.regional} with regional subgenres \xB7 ${scopes.all} with all subgenres` : `${counts.genreOnlyTracksCount} this genre only \xB7 ${scopes.all} with subgenres`;
1909
+ };
1888
1910
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
1889
1911
  toolbar,
1890
1912
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
1913
+ summary && catalogue && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
1914
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { children: [
1915
+ "Catalogue ",
1916
+ noun.plural,
1917
+ ": ",
1918
+ describeCounts(catalogue)
1919
+ ] }),
1920
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { children: [
1921
+ "Playlist ",
1922
+ noun.plural,
1923
+ ": ",
1924
+ describeCounts(summary)
1925
+ ] })
1926
+ ] }),
1891
1927
  /* @__PURE__ */ (0, import_react12.createElement)(
1892
1928
  GenreDetailExtras,
1893
1929
  {
@@ -1896,6 +1932,7 @@ function GenreInfoPanelBody({
1896
1932
  family,
1897
1933
  genreUuid,
1898
1934
  genrePlaylist,
1935
+ selection,
1899
1936
  wikidataId,
1900
1937
  scope,
1901
1938
  getBackendBaseUrl
@@ -1906,7 +1943,7 @@ function GenreInfoPanelBody({
1906
1943
 
1907
1944
  // src/genre-tree/ChangeParentPopup.tsx
1908
1945
  var import_react13 = require("react");
1909
- var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1946
+ var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
1910
1947
  var import_jsx_runtime31 = require("react/jsx-runtime");
1911
1948
  function ChangeParentPopup({
1912
1949
  genrePlaylist,
@@ -1916,9 +1953,9 @@ function ChangeParentPopup({
1916
1953
  onClose,
1917
1954
  onParentChanged
1918
1955
  }) {
1919
- const { mutate, isPending, formErrors } = (0, import_genre_tree13.useUpdateGenre)(scope, getBackendBaseUrl);
1956
+ const { mutate, isPending, formErrors } = (0, import_genre_tree19.useUpdateGenre)(scope, getBackendBaseUrl);
1920
1957
  const candidates = (0, import_react13.useMemo)(
1921
- () => (0, import_genre_tree13.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
1958
+ () => (0, import_genre_tree19.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
1922
1959
  [genrePlaylists, genrePlaylist.uuid]
1923
1960
  );
1924
1961
  const handleSelect = (newParent) => {
@@ -1961,7 +1998,7 @@ function ChangeParentPopup({
1961
1998
 
1962
1999
  // src/genre-tree/artist/useArtistPanel.tsx
1963
2000
  var import_ui15 = require("@behindthemusictree/ui");
1964
- var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
2001
+ var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
1965
2002
 
1966
2003
  // src/genre-tree/artist/ArtistHeader.tsx
1967
2004
  var import_ui11 = require("@behindthemusictree/ui");
@@ -2128,12 +2165,12 @@ var import_react_query2 = require("@tanstack/react-query");
2128
2165
  var import_lucide_react13 = require("lucide-react");
2129
2166
  var import_ui14 = require("@behindthemusictree/ui");
2130
2167
  var import_player10 = require("@behindthemusictree/app-kit/player");
2131
- var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
2168
+ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
2132
2169
  var import_transport9 = require("@behindthemusictree/app-kit/transport");
2133
2170
 
2134
2171
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2135
2172
  var import_player9 = require("@behindthemusictree/app-kit/player");
2136
- var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
2173
+ var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
2137
2174
 
2138
2175
  // src/genre-tree/TrackPositionPlayPause.tsx
2139
2176
  var import_fa5 = require("react-icons/fa");
@@ -2188,8 +2225,8 @@ function TrackPositionPlayPause({
2188
2225
  }
2189
2226
 
2190
2227
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2191
- var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
2192
- var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
2228
+ var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
2229
+ var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
2193
2230
  var import_jsx_runtime38 = require("react/jsx-runtime");
2194
2231
  function TrackItem({
2195
2232
  track,
@@ -2200,11 +2237,11 @@ function TrackItem({
2200
2237
  renderSongLink
2201
2238
  }) {
2202
2239
  const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
2203
- const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree14.useTrackList)();
2240
+ const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree20.useTrackList)();
2204
2241
  const scope = trackList?.origin?.scope ?? null;
2205
2242
  const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
2206
2243
  const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
2207
- const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
2244
+ const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree21.isSong)(track) && (0, import_genre_tree21.songUnplayableReason)(track) !== null;
2208
2245
  const handlePlayPauseClick = (event) => {
2209
2246
  event.stopPropagation();
2210
2247
  if (isCurrentTrack) {
@@ -2237,8 +2274,8 @@ function TrackItem({
2237
2274
  }
2238
2275
  ),
2239
2276
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2240
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2241
- track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
2277
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree21.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2278
+ track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree22.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
2242
2279
  ] }),
2243
2280
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
2244
2281
  renderDuration && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
@@ -2257,7 +2294,7 @@ function TrackItem({
2257
2294
 
2258
2295
  // src/genre-tree/artist/ArtistSongs.tsx
2259
2296
  var import_jsx_runtime39 = require("react/jsx-runtime");
2260
- var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree17.SongDetailedSchema);
2297
+ var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree23.SongDetailedSchema);
2261
2298
  var SORTS = [
2262
2299
  { ordering: "title", label: "Title" },
2263
2300
  { ordering: "-albumYear", label: "Year" }
@@ -2266,11 +2303,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2266
2303
  const [page, setPage] = (0, import_react14.useState)(1);
2267
2304
  const [ordering, setOrdering] = (0, import_react14.useState)("title");
2268
2305
  const { fetch } = (0, import_transport9.useFetchWrapper)(getBackendBaseUrl);
2269
- const { playNewTrackListFromArtist } = (0, import_genre_tree17.useTrackList)();
2306
+ const { playNewTrackListFromArtist } = (0, import_genre_tree23.useTrackList)();
2270
2307
  const { playerTrackObject } = (0, import_player10.usePlayer)();
2271
2308
  const { data, isPending, isError } = (0, import_transport9.useQueryWithParse)({
2272
2309
  queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
2273
- queryFn: () => fetch(import_genre_tree17.libraryEndpoints.reference.songs.list(), true, false, void 0, {
2310
+ queryFn: () => fetch(import_genre_tree23.libraryEndpoints.reference.songs.list(), true, false, void 0, {
2274
2311
  artist: artist.uuid,
2275
2312
  ordering,
2276
2313
  page
@@ -2285,7 +2322,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2285
2322
  });
2286
2323
  const handleRowClickCapture = (event, song) => {
2287
2324
  if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
2288
- if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree17.songUnplayableReason)(song) !== null) return;
2325
+ if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree23.songUnplayableReason)(song) !== null) return;
2289
2326
  event.stopPropagation();
2290
2327
  void play(song.uuid);
2291
2328
  };
@@ -2344,7 +2381,7 @@ function useArtistPanel({
2344
2381
  onExpandedChange,
2345
2382
  onClose
2346
2383
  }) {
2347
- const result = (0, import_genre_tree18.useArtist)(uuid, getBackendBaseUrl);
2384
+ const result = (0, import_genre_tree24.useArtist)(uuid, getBackendBaseUrl);
2348
2385
  const panel = { expanded, onExpandedChange, onClose };
2349
2386
  switch (result.status) {
2350
2387
  case "idle":
@@ -2405,21 +2442,21 @@ function useArtistPanel({
2405
2442
 
2406
2443
  // src/genre-tree/song/useSongPanel.tsx
2407
2444
  var import_ui17 = require("@behindthemusictree/ui");
2408
- var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
2445
+ var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2409
2446
 
2410
2447
  // src/genre-tree/song/SongPanelContent.tsx
2411
2448
  var import_react15 = require("react");
2412
2449
  var import_lucide_react14 = require("lucide-react");
2413
2450
  var import_ui16 = require("@behindthemusictree/ui");
2414
2451
  var import_player11 = require("@behindthemusictree/app-kit/player");
2415
- var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
2452
+ var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
2416
2453
  var import_jsx_runtime41 = require("react/jsx-runtime");
2417
2454
  var numberFormat = new Intl.NumberFormat("en");
2418
2455
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2419
2456
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
2420
2457
  function SongHistory({ uuid, getBackendBaseUrl }) {
2421
2458
  const [open, setOpen] = (0, import_react15.useState)(false);
2422
- const history = (0, import_genre_tree19.useSongHistory)(uuid, getBackendBaseUrl, open);
2459
+ const history = (0, import_genre_tree25.useSongHistory)(uuid, getBackendBaseUrl, open);
2423
2460
  let body = null;
2424
2461
  if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Skeleton, { className: "h-10", "aria-busy": "true" });
2425
2462
  else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
@@ -2460,13 +2497,13 @@ function SongPanelContent({
2460
2497
  genrePath,
2461
2498
  onSelectGenre
2462
2499
  }) {
2463
- const { playNewTrackListFromTrackUuid } = (0, import_genre_tree19.useTrackList)();
2500
+ const { playNewTrackListFromTrackUuid } = (0, import_genre_tree25.useTrackList)();
2464
2501
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
2465
- const unplayableReason = (0, import_genre_tree19.songUnplayableReason)(song);
2502
+ const unplayableReason = (0, import_genre_tree25.songUnplayableReason)(song);
2466
2503
  const isCurrent = playerTrackObject?.track.id === song.uuid;
2467
2504
  const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
2468
2505
  const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
2469
- const musicBrainzUrl = (0, import_genre_tree19.musicBrainzRecordingUrl)(song);
2506
+ const musicBrainzUrl = (0, import_genre_tree25.musicBrainzRecordingUrl)(song);
2470
2507
  const fields = [];
2471
2508
  if (song.genre) {
2472
2509
  const path = genrePath.length > 0 ? genrePath : null;
@@ -2525,7 +2562,7 @@ function SongPanelContent({
2525
2562
  ),
2526
2563
  shareUrl && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2527
2564
  ] }) }),
2528
- song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_genre_tree19.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2565
+ song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_genre_tree25.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2529
2566
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.FieldList, { items: fields }),
2530
2567
  song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2531
2568
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
@@ -2546,7 +2583,7 @@ function useSongPanel({
2546
2583
  onExpandedChange,
2547
2584
  onClose
2548
2585
  }) {
2549
- const result = (0, import_genre_tree20.useSong)(uuid, getBackendBaseUrl);
2586
+ const result = (0, import_genre_tree26.useSong)(uuid, getBackendBaseUrl);
2550
2587
  const panel = { expanded, onExpandedChange, onClose };
2551
2588
  switch (result.status) {
2552
2589
  case "idle":
@@ -2656,6 +2693,8 @@ function GenreTreeView({
2656
2693
  onArtistExpandedChange,
2657
2694
  getShareUrl,
2658
2695
  extraSearchGroups = [],
2696
+ onSearchQueryChange,
2697
+ onSearchFocus,
2659
2698
  artistSearch = true,
2660
2699
  onArtistSelect,
2661
2700
  artistSearchBaseUrl,
@@ -2721,7 +2760,7 @@ function GenreTreeView({
2721
2760
  allowWheelRotation: internalAllowWheelRotation,
2722
2761
  toggleWheelRotation,
2723
2762
  onNodeHover: handleNodeHover
2724
- } = (0, import_genre_tree21.useGenreTreeViewState)({
2763
+ } = (0, import_genre_tree27.useGenreTreeViewState)({
2725
2764
  scope,
2726
2765
  getBackendBaseUrl,
2727
2766
  viewMode: controlledViewMode,
@@ -2740,12 +2779,12 @@ function GenreTreeView({
2740
2779
  restoreRootUuid = selectedGenrePlaylist.root.uuid;
2741
2780
  setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
2742
2781
  }
2743
- const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree21.genreUpdateMutationKey }) > 0;
2744
- const { mutate: updateGenre } = (0, import_genre_tree21.useUpdateGenre)(scope, getBackendBaseUrl);
2782
+ const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree27.genreUpdateMutationKey }) > 0;
2783
+ const { mutate: updateGenre } = (0, import_genre_tree27.useUpdateGenre)(scope, getBackendBaseUrl);
2745
2784
  const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
2746
2785
  const isArtistFiltered = artistUuid !== null;
2747
2786
  const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
2748
- const artistResult = (0, import_genre_tree21.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
2787
+ const artistResult = (0, import_genre_tree27.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
2749
2788
  const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
2750
2789
  const filterArtist = (0, import_react16.useMemo)(
2751
2790
  () => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
@@ -2953,6 +2992,7 @@ function GenreTreeView({
2953
2992
  ] }),
2954
2993
  genreUuid,
2955
2994
  genrePlaylist,
2995
+ catalogue: genrePlaylist.catalogue,
2956
2996
  wikidataId: criteria.wikidataId,
2957
2997
  scope,
2958
2998
  getBackendBaseUrl,
@@ -3085,7 +3125,11 @@ function GenreTreeView({
3085
3125
  selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
3086
3126
  onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
3087
3127
  onPrevious: handlePrevious,
3088
- onQueryChange: artistSearch || songSearch || extraSearchGroups.length > 0 ? setSearchText : void 0,
3128
+ onQueryChange: (text) => {
3129
+ setSearchText(text);
3130
+ onSearchQueryChange?.(text);
3131
+ },
3132
+ onFocus: onSearchFocus,
3089
3133
  extraGroups
3090
3134
  }
3091
3135
  ),
@@ -3296,7 +3340,7 @@ var import_react17 = require("react");
3296
3340
  var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
3297
3341
  var import_ui20 = require("@behindthemusictree/ui");
3298
3342
  var import_lucide_react17 = require("lucide-react");
3299
- var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
3343
+ var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
3300
3344
  var import_jsx_runtime45 = require("react/jsx-runtime");
3301
3345
  var RELATIVE_ITEM_COUNT = 1;
3302
3346
  function toNode(genre, parentId) {
@@ -3324,7 +3368,7 @@ function GenreDetailPanel({
3324
3368
  edgeTab,
3325
3369
  extras
3326
3370
  }) {
3327
- const { data: detail, isPending, isError } = (0, import_genre_tree22.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
3371
+ const { data: detail, isPending, isError } = (0, import_genre_tree28.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
3328
3372
  const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
3329
3373
  const view = (0, import_react17.useMemo)(() => {
3330
3374
  if (!detail) return null;
@@ -3340,7 +3384,7 @@ function GenreDetailPanel({
3340
3384
  id: detail.uuid,
3341
3385
  parentId: detail.parent?.uuid ?? null,
3342
3386
  name: detail.name,
3343
- itemCount: detail.tracksCount,
3387
+ itemCount: scope === "reference" ? (0, import_genre_tree28.genreMusicCount)(import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
3344
3388
  wikidataId: detail.wikidataId
3345
3389
  },
3346
3390
  fill: rootColor,
@@ -3350,7 +3394,7 @@ function GenreDetailPanel({
3350
3394
  ({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
3351
3395
  )
3352
3396
  };
3353
- }, [detail]);
3397
+ }, [detail, scope]);
3354
3398
  if (isError) {
3355
3399
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
3356
3400
  }
@@ -3403,7 +3447,8 @@ function GenreDetailPanel({
3403
3447
  ancestors
3404
3448
  ] }),
3405
3449
  genreUuid: detail.uuid,
3406
- genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
3450
+ genrePlaylist: scope === "reference" ? import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
3451
+ catalogue: detail.catalogue,
3407
3452
  wikidataId: detail.wikidataId,
3408
3453
  scope,
3409
3454
  getBackendBaseUrl,
@@ -3424,9 +3469,9 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
3424
3469
  // src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
3425
3470
  var import_react18 = require("react");
3426
3471
  var import_lucide_react18 = require("lucide-react");
3427
- var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
3428
- var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
3429
- var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
3472
+ var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
3473
+ var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
3474
+ var import_track_noun5 = require("@behindthemusictree/app-kit/track-noun");
3430
3475
  var import_jsx_runtime46 = require("react/jsx-runtime");
3431
3476
  var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
3432
3477
  function TrackListSidebar({
@@ -3440,8 +3485,8 @@ function TrackListSidebar({
3440
3485
  renderArtistLink,
3441
3486
  renderSongLink
3442
3487
  }) {
3443
- const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree23.useTrackList)();
3444
- const trackNoun = (0, import_track_noun4.useTrackNoun)();
3488
+ const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree29.useTrackList)();
3489
+ const trackNoun = (0, import_track_noun5.useTrackNoun)();
3445
3490
  const trackListRef = (0, import_react18.useRef)(null);
3446
3491
  const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
3447
3492
  const handledAutoAdvanceRequestIdRef = (0, import_react18.useRef)(null);
@@ -3543,7 +3588,7 @@ function TrackListSidebar({
3543
3588
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3544
3589
  ] }),
3545
3590
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
3546
- trackList.origin.type === import_genre_tree24.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree24.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
3591
+ trackList.origin.type === import_genre_tree30.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree30.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
3547
3592
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3548
3593
  ] })
3549
3594
  ] }),
@@ -3594,12 +3639,12 @@ function TrackListSidebar({
3594
3639
  var import_react19 = require("react");
3595
3640
  var import_tailwind_merge = require("tailwind-merge");
3596
3641
  var import_player13 = require("@behindthemusictree/app-kit/player");
3597
- var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
3642
+ var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
3598
3643
  var import_ui21 = require("@behindthemusictree/ui");
3599
3644
 
3600
3645
  // src/genre-tree/PlayerControls.tsx
3601
3646
  var import_lucide_react19 = require("lucide-react");
3602
- var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
3647
+ var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
3603
3648
  var import_player12 = require("@behindthemusictree/app-kit/player");
3604
3649
  var import_jsx_runtime47 = require("react/jsx-runtime");
3605
3650
  function PlayerControls() {
@@ -3613,7 +3658,7 @@ function PlayerControls() {
3613
3658
  setVolume,
3614
3659
  volume
3615
3660
  } = (0, import_player12.usePlayer)();
3616
- const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree25.useTrackList)();
3661
+ const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree31.useTrackList)();
3617
3662
  const currentTime = (0, import_player12.useCurrentTime)();
3618
3663
  const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
3619
3664
  const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
@@ -3627,7 +3672,7 @@ function PlayerControls() {
3627
3672
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3628
3673
  children: [
3629
3674
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3630
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree25.formatTime)(safeCurrentTime) }),
3675
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree31.formatTime)(safeCurrentTime) }),
3631
3676
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
3632
3677
  "input",
3633
3678
  {
@@ -3642,7 +3687,7 @@ function PlayerControls() {
3642
3687
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3643
3688
  }
3644
3689
  ),
3645
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree25.formatTime)(safeDuration) })
3690
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree31.formatTime)(safeDuration) })
3646
3691
  ] }),
3647
3692
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3648
3693
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
@@ -3721,7 +3766,7 @@ function PlayerPanel({
3721
3766
  renderSongLink
3722
3767
  }) {
3723
3768
  const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
3724
- const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree26.useTrackListSidebarVisibility)();
3769
+ const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree32.useTrackListSidebarVisibility)();
3725
3770
  const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
3726
3771
  const loadedTrackTitle = playerTrackObject?.track.title;
3727
3772
  (0, import_react19.useEffect)(() => {
@@ -3818,7 +3863,7 @@ function PlayerPanel({
3818
3863
  // src/genre-tree/Rating.tsx
3819
3864
  var import_uuid = require("uuid");
3820
3865
  var import_ai = require("react-icons/ai");
3821
- var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
3866
+ var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
3822
3867
  var import_jsx_runtime49 = require("react/jsx-runtime");
3823
3868
  function Rating({ rating, handleChange }) {
3824
3869
  const instanceId = (0, import_uuid.v4)();
@@ -3830,7 +3875,7 @@ function Rating({ rating, handleChange }) {
3830
3875
  className: "w-6 h-6 opacity-0",
3831
3876
  type: "radio",
3832
3877
  name: "rating",
3833
- value: i === 0 ? import_genre_tree27.FORM_RATING_NULL_VALUE : i,
3878
+ value: i === 0 ? import_genre_tree33.FORM_RATING_NULL_VALUE : i,
3834
3879
  checked: rating === (i === 0 ? null : i),
3835
3880
  onChange: handleChange
3836
3881
  }
@@ -3839,7 +3884,7 @@ function Rating({ rating, handleChange }) {
3839
3884
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3840
3885
  import_ai.AiOutlineStar,
3841
3886
  {
3842
- className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree27.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
3887
+ className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree33.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
3843
3888
  }
3844
3889
  ),
3845
3890
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-grow w-8" }),