@behindthemusictree/app-kit-ui 33.3.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.
@@ -52,13 +52,14 @@ var import_lucide_react11 = require("lucide-react");
52
52
  var import_lucide_react12 = require("lucide-react");
53
53
  var import_ui16 = require("@behindthemusictree/ui");
54
54
  var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
55
- var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
55
+ var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
56
56
  var import_popup5 = require("@behindthemusictree/app-kit/popup");
57
57
 
58
58
  // src/genre-tree/playlist-tree/size-metric.ts
59
+ var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
59
60
  var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
60
61
  var genrePlaylistSize = (genrePlaylist, metric) => {
61
- if (metric === "songs") return genrePlaylist.tracksCount;
62
+ if (metric === "songs") return (0, import_genre_tree.genrePlaylistTrackCount)(genrePlaylist);
62
63
  if (genrePlaylist.viewsCount === void 0) {
63
64
  throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
64
65
  }
@@ -82,6 +83,7 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
82
83
  }
83
84
 
84
85
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
86
+ var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
85
87
  var import_react3 = require("react");
86
88
  var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
87
89
  var import_player = require("@behindthemusictree/app-kit/player");
@@ -306,29 +308,28 @@ var import_transport = require("@behindthemusictree/app-kit/transport");
306
308
 
307
309
  // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
308
310
  var import_react2 = require("react");
309
- var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
311
+ var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
310
312
  function useGenrePlaylistPlayback({
311
313
  scope,
312
314
  genrePlaylists,
313
315
  artist = null
314
316
  }) {
315
- const { trackList, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0, import_genre_tree.useTrackList)();
317
+ const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
316
318
  const origin = trackList?.origin;
317
319
  const playingNodeId = (0, import_react2.useMemo)(() => {
318
- if (!origin) return null;
319
- if (!artist) return origin.type === import_genre_tree.TrackListOriginType.GENRE_PLAYLIST ? origin.uuid : null;
320
- if (origin.type !== import_genre_tree.TrackListOriginType.ARTIST || origin.uuid !== artist.uuid) return null;
321
- const { genreUuid } = origin;
322
- return genreUuid ? genrePlaylists.find((gp) => gp.criteria?.uuid === genreUuid)?.uuid ?? null : null;
323
- }, [origin, artist, genrePlaylists]);
324
- const play = (0, import_react2.useCallback)(
325
- async (genrePlaylist) => {
326
- if (!artist) return playNewTrackListFromGenrePlaylist(genrePlaylist, scope);
327
- if (!genrePlaylist.criteria) return;
328
- return playNewTrackListFromArtist(artist, scope, { ordering: "title", genreUuid: genrePlaylist.criteria.uuid });
329
- },
330
- [artist, scope, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist]
331
- );
320
+ if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
321
+ const playlistOrigin = origin;
322
+ if (playlistOrigin.artistUuid !== artist?.uuid) return null;
323
+ const playlist = genrePlaylists.find((item) => item.uuid === origin.uuid) ?? playlistOrigin.criteriaPlaylist;
324
+ const expected = scope === "reference" && playlist && !("criteria" in playlist && playlist.criteria === null) ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
325
+ if (playlistOrigin.selection && expected && (0, import_genre_tree2.genreMusicSelectionKey)(playlistOrigin.selection).join() !== (0, import_genre_tree2.genreMusicSelectionKey)(expected).join()) return null;
326
+ return origin.uuid;
327
+ }, [origin, artist, genrePlaylists, scope]);
328
+ const play = (0, import_react2.useCallback)(async (playlist) => {
329
+ if (artist && !playlist.criteria) return;
330
+ const selection = scope === "reference" && playlist.criteria !== null ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
331
+ return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
332
+ }, [artist, scope, playNewTrackListFromGenrePlaylist]);
332
333
  return (0, import_react2.useMemo)(() => ({ playingNodeId, play }), [playingNodeId, play]);
333
334
  }
334
335
 
@@ -363,7 +364,7 @@ function GenrePlaylistTreePerRoot({
363
364
  id: genrePlaylist.uuid,
364
365
  parentId: genrePlaylist.parent?.uuid ?? null,
365
366
  name: genrePlaylist.name,
366
- itemCount: genrePlaylist.tracksCount,
367
+ itemCount: (0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist),
367
368
  actionable: Boolean(genrePlaylist.criteria),
368
369
  side: genrePlaylist.criteria?.side ?? void 0,
369
370
  wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
@@ -379,7 +380,7 @@ function GenrePlaylistTreePerRoot({
379
380
  setIsPlaying(!isPlaying);
380
381
  return;
381
382
  }
382
- if (genrePlaylist.tracksCount === 0) {
383
+ if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
383
384
  return;
384
385
  }
385
386
  play(genrePlaylist).catch((error) => {
@@ -443,6 +444,7 @@ function GenrePlaylistTreePerRoot({
443
444
  }
444
445
 
445
446
  // src/genre-tree/playlist-tree/TreeWheel.tsx
447
+ var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
446
448
  var import_react4 = require("react");
447
449
  var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
448
450
  var import_player2 = require("@behindthemusictree/app-kit/player");
@@ -495,7 +497,7 @@ function GenrePlaylistTreeWheel({
495
497
  setIsPlaying(!isPlaying);
496
498
  return;
497
499
  }
498
- if (genrePlaylist.tracksCount === 0) {
500
+ if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
499
501
  return;
500
502
  }
501
503
  play(genrePlaylist).catch((error) => {
@@ -560,6 +562,7 @@ function GenrePlaylistTreeWheel({
560
562
  }
561
563
 
562
564
  // src/genre-tree/playlist-tree/TreeWheelRadialPopCore.tsx
565
+ var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
563
566
  var import_react5 = require("react");
564
567
  var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
565
568
  var import_player3 = require("@behindthemusictree/app-kit/player");
@@ -604,7 +607,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
604
607
  setIsPlaying(!isPlaying);
605
608
  return;
606
609
  }
607
- if (genrePlaylist.tracksCount === 0) {
610
+ if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
608
611
  return;
609
612
  }
610
613
  play(genrePlaylist).catch((error) => {
@@ -702,7 +705,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
702
705
  // src/genre-tree/GenreSearch.tsx
703
706
  var import_lucide_react3 = require("lucide-react");
704
707
  var import_ui4 = require("@behindthemusictree/ui");
705
- var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
708
+ var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
706
709
 
707
710
  // src/genre-tree/search/SearchBox.tsx
708
711
  var import_react7 = require("react");
@@ -758,10 +761,10 @@ var SEARCH_FAILED_STATUS = "Search failed";
758
761
  var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
759
762
 
760
763
  // src/genre-tree/search/useArtistSearchGroup.tsx
761
- var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
764
+ var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
762
765
  var import_jsx_runtime10 = require("react/jsx-runtime");
763
766
  function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
764
- const search = (0, import_genre_tree2.useArtistSearch)(getBaseUrl, enabled ? text : "");
767
+ const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
765
768
  if (!enabled) return void 0;
766
769
  return {
767
770
  key: "artists",
@@ -789,17 +792,17 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
789
792
  }
790
793
 
791
794
  // src/genre-tree/search/useSongSearchGroup.tsx
792
- var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
795
+ var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
793
796
  var import_jsx_runtime11 = require("react/jsx-runtime");
794
797
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
795
- const search = (0, import_genre_tree3.useSongSearch)(getBaseUrl, enabled ? text : "");
798
+ const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
796
799
  if (!enabled) return void 0;
797
800
  return {
798
801
  key: "songs",
799
802
  label: "Songs",
800
803
  items: search.songs.map((song) => ({
801
804
  key: song.uuid,
802
- content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: (0, import_genre_tree3.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
805
+ content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
803
806
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "block truncate font-medium", children: song.title }),
804
807
  /* @__PURE__ */ (0, import_jsx_runtime11.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 ") })
805
808
  ] }),
@@ -811,10 +814,10 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
811
814
  }
812
815
 
813
816
  // src/genre-tree/search/useGenreRemoteSearchGroup.tsx
814
- var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
817
+ var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
815
818
  var import_jsx_runtime12 = require("react/jsx-runtime");
816
819
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
817
- const search = (0, import_genre_tree4.useGenreRemoteSearch)(getBaseUrl, text, options);
820
+ const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
818
821
  const genres = filter ? search.genres.filter(filter) : search.genres;
819
822
  return {
820
823
  key,
@@ -848,7 +851,7 @@ function GenreSearch({
848
851
  extraGroups = [],
849
852
  onFocus
850
853
  }) {
851
- const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree5.useGenreSearch)({
854
+ const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree9.useGenreSearch)({
852
855
  genrePlaylists,
853
856
  onSelect,
854
857
  selectedName,
@@ -900,6 +903,8 @@ function GenreSearch({
900
903
  }
901
904
 
902
905
  // src/genre-tree/GenreInfoPanelBody.tsx
906
+ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
907
+ var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
903
908
  var import_player6 = require("@behindthemusictree/app-kit/player");
904
909
 
905
910
  // src/genre-tree/GenreActionBand.tsx
@@ -1249,7 +1254,7 @@ function GenreActionBand({
1249
1254
 
1250
1255
  // src/genre-tree/GenreDetailExtras.tsx
1251
1256
  var import_ui6 = require("@behindthemusictree/ui");
1252
- var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1257
+ var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
1253
1258
 
1254
1259
  // src/genre-tree/ClampedText.tsx
1255
1260
  var import_react10 = require("react");
@@ -1292,20 +1297,21 @@ var import_player4 = require("@behindthemusictree/app-kit/player");
1292
1297
  var import_player5 = require("@behindthemusictree/app-kit/player");
1293
1298
  var import_popup4 = require("@behindthemusictree/app-kit/popup");
1294
1299
  var import_transport6 = require("@behindthemusictree/app-kit/transport");
1295
- var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
1296
- var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
1297
- var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
1298
- var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
1300
+ var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1301
+ var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
1302
+ var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1303
+ var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
1304
+ var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
1299
1305
 
1300
1306
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1301
- var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
1307
+ var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
1302
1308
  var import_jsx_runtime21 = require("react/jsx-runtime");
1303
1309
  function MusicBrainzRecordingLink({
1304
1310
  song,
1305
1311
  children,
1306
1312
  className: className2 = ""
1307
1313
  }) {
1308
- const url = (0, import_genre_tree6.musicBrainzRecordingUrl)(song);
1314
+ const url = (0, import_genre_tree10.musicBrainzRecordingUrl)(song);
1309
1315
  if (!url) return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
1310
1316
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1311
1317
  "a",
@@ -1322,11 +1328,11 @@ function MusicBrainzRecordingLink({
1322
1328
  }
1323
1329
 
1324
1330
  // src/genre-tree/GenrePlaylistTracks.tsx
1325
- var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
1331
+ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
1326
1332
  var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
1327
1333
  var import_jsx_runtime22 = require("react/jsx-runtime");
1328
1334
  var CATALOGUE_PAGE_SIZE = 100;
1329
- var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(import_genre_tree9.SongDetailedSchema).extend({
1335
+ var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema).extend({
1330
1336
  overallTotal: import_zod.z.number().nullable(),
1331
1337
  page: import_zod.z.number().nullable(),
1332
1338
  totalPages: import_zod.z.number().nullable()
@@ -1344,6 +1350,7 @@ var nextCursor = (next) => {
1344
1350
  function GenrePlaylistTracks({
1345
1351
  genreUuid,
1346
1352
  genrePlaylist,
1353
+ selection,
1347
1354
  scope,
1348
1355
  getBackendBaseUrl,
1349
1356
  renderArtistLink,
@@ -1353,7 +1360,7 @@ function GenrePlaylistTracks({
1353
1360
  renderTracksToolbar,
1354
1361
  artist = null
1355
1362
  }) {
1356
- const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0, import_genre_tree10.useTrackList)();
1363
+ const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree15.useTrackList)();
1357
1364
  const { session, sessionRestored } = (0, import_auth.useSession)();
1358
1365
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
1359
1366
  const { showPopup } = (0, import_popup4.usePopup)();
@@ -1362,15 +1369,15 @@ function GenrePlaylistTracks({
1362
1369
  const { fetch } = (0, import_transport3.useFetchWrapper)(getBackendBaseUrl);
1363
1370
  const playback = (0, import_transport3.usePlaybackContext)();
1364
1371
  const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
1365
- 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] : []],
1372
+ 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] : []],
1366
1373
  queryFn: async ({ pageParam }) => {
1367
1374
  if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
1368
1375
  const response = await fetch(
1369
- import_genre_tree8.libraryEndpoints.reference.songs.list(),
1376
+ import_genre_tree13.libraryEndpoints.reference.songs.list(),
1370
1377
  true,
1371
1378
  false,
1372
1379
  {},
1373
- { genre: genreUuid, ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1380
+ { genre: genreUuid, ...(0, import_genre_tree11.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1374
1381
  );
1375
1382
  const parsed = (0, import_transport5.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1376
1383
  return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
@@ -1394,7 +1401,12 @@ function GenrePlaylistTracks({
1394
1401
  const tracks = pages.flatMap((page) => page.tracks);
1395
1402
  const total = pages[0]?.total ?? void 0;
1396
1403
  const play = (startSongUuid) => {
1397
- const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
1404
+ const started = artist ? playNewTrackListFromArtist(artist, scope, {
1405
+ startSongUuid,
1406
+ ordering: "title",
1407
+ genreUuid,
1408
+ ...selection && { selection }
1409
+ }) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genrePlaylist.name }, scope, { startSongUuid, ...selection && { selection } }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
1398
1410
  started.catch((error) => {
1399
1411
  console.error("Failed to play genre playlist:", error);
1400
1412
  showPopup(/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
@@ -1414,14 +1426,14 @@ function GenrePlaylistTracks({
1414
1426
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1415
1427
  const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
1416
1428
  const text = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: TEXT_CELL, children: [
1417
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree9.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1429
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree14.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1418
1430
  track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1419
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_genre_tree11.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1431
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1420
1432
  track.album && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
1421
1433
  ] });
1422
1434
  const leading = renderSongLeading?.(track, index);
1423
1435
  const actions = renderSongActions?.(track);
1424
- const isUnplayable = (0, import_genre_tree9.isSong)(track) && (0, import_genre_tree9.songUnplayableReason)(track) !== null;
1436
+ const isUnplayable = (0, import_genre_tree14.isSong)(track) && (0, import_genre_tree14.songUnplayableReason)(track) !== null;
1425
1437
  if (isUnplayable) {
1426
1438
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1427
1439
  leading,
@@ -1475,6 +1487,7 @@ var import_jsx_runtime23 = require("react/jsx-runtime");
1475
1487
  function GenreDetailExtras({
1476
1488
  genreUuid,
1477
1489
  genrePlaylist,
1490
+ selection,
1478
1491
  wikidataId,
1479
1492
  scope,
1480
1493
  getBackendBaseUrl,
@@ -1488,7 +1501,7 @@ function GenreDetailExtras({
1488
1501
  ...trackSlots
1489
1502
  }) {
1490
1503
  const trackNoun = (0, import_track_noun2.useTrackNoun)();
1491
- const { data: overview, isPending } = (0, import_genre_tree12.useFetchGenreOverview)(
1504
+ const { data: overview, isPending } = (0, import_genre_tree17.useFetchGenreOverview)(
1492
1505
  genreUuid,
1493
1506
  scope,
1494
1507
  getBackendBaseUrl,
@@ -1541,6 +1554,7 @@ function GenreDetailExtras({
1541
1554
  {
1542
1555
  genreUuid,
1543
1556
  genrePlaylist,
1557
+ selection,
1544
1558
  scope,
1545
1559
  getBackendBaseUrl,
1546
1560
  renderArtistLink,
@@ -1570,7 +1584,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
1570
1584
  GenreActionBand,
1571
1585
  {
1572
1586
  ...bandProps,
1573
- onPlayPause: genrePlaylist.tracksCount > 0 ? handlePlayPause : void 0,
1587
+ onPlayPause: ("genreOnlyTracksCount" in genrePlaylist ? (0, import_genre_tree18.genreMusicCount)(genrePlaylist) : genrePlaylist.tracksCount) > 0 ? handlePlayPause : void 0,
1574
1588
  isPlaying: isCurrentTrackList && isPlaying
1575
1589
  }
1576
1590
  );
@@ -1580,6 +1594,7 @@ function GenreInfoPanelBody({
1580
1594
  family,
1581
1595
  genreUuid,
1582
1596
  genrePlaylist,
1597
+ catalogue,
1583
1598
  wikidataId,
1584
1599
  scope,
1585
1600
  getBackendBaseUrl,
@@ -1587,9 +1602,30 @@ function GenreInfoPanelBody({
1587
1602
  band,
1588
1603
  extras
1589
1604
  }) {
1605
+ const noun = (0, import_track_noun3.useTrackNoun)();
1606
+ const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist) : void 0;
1607
+ const selection = summary ? (0, import_genre_tree18.defaultGenreMusicSelection)(summary) : void 0;
1608
+ const describeCounts = (counts) => {
1609
+ const scopes = counts.genreAndSubgenresTracksCount;
1610
+ 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`;
1611
+ };
1590
1612
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
1591
1613
  toolbar,
1592
1614
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
1615
+ summary && catalogue && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
1616
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("p", { children: [
1617
+ "Catalogue ",
1618
+ noun.plural,
1619
+ ": ",
1620
+ describeCounts(catalogue)
1621
+ ] }),
1622
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("p", { children: [
1623
+ "Playlist ",
1624
+ noun.plural,
1625
+ ": ",
1626
+ describeCounts(summary)
1627
+ ] })
1628
+ ] }),
1593
1629
  /* @__PURE__ */ (0, import_react12.createElement)(
1594
1630
  GenreDetailExtras,
1595
1631
  {
@@ -1598,6 +1634,7 @@ function GenreInfoPanelBody({
1598
1634
  family,
1599
1635
  genreUuid,
1600
1636
  genrePlaylist,
1637
+ selection,
1601
1638
  wikidataId,
1602
1639
  scope,
1603
1640
  getBackendBaseUrl
@@ -1608,7 +1645,7 @@ function GenreInfoPanelBody({
1608
1645
 
1609
1646
  // src/genre-tree/ChangeParentPopup.tsx
1610
1647
  var import_react13 = require("react");
1611
- var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
1648
+ var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
1612
1649
  var import_jsx_runtime25 = require("react/jsx-runtime");
1613
1650
  function ChangeParentPopup({
1614
1651
  genrePlaylist,
@@ -1618,9 +1655,9 @@ function ChangeParentPopup({
1618
1655
  onClose,
1619
1656
  onParentChanged
1620
1657
  }) {
1621
- const { mutate, isPending, formErrors } = (0, import_genre_tree13.useUpdateGenre)(scope, getBackendBaseUrl);
1658
+ const { mutate, isPending, formErrors } = (0, import_genre_tree19.useUpdateGenre)(scope, getBackendBaseUrl);
1622
1659
  const candidates = (0, import_react13.useMemo)(
1623
- () => (0, import_genre_tree13.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
1660
+ () => (0, import_genre_tree19.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
1624
1661
  [genrePlaylists, genrePlaylist.uuid]
1625
1662
  );
1626
1663
  const handleSelect = (newParent) => {
@@ -1663,7 +1700,7 @@ function ChangeParentPopup({
1663
1700
 
1664
1701
  // src/genre-tree/artist/useArtistPanel.tsx
1665
1702
  var import_ui12 = require("@behindthemusictree/ui");
1666
- var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
1703
+ var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
1667
1704
 
1668
1705
  // src/genre-tree/artist/ArtistHeader.tsx
1669
1706
  var import_ui8 = require("@behindthemusictree/ui");
@@ -1830,12 +1867,12 @@ var import_react_query2 = require("@tanstack/react-query");
1830
1867
  var import_lucide_react9 = require("lucide-react");
1831
1868
  var import_ui11 = require("@behindthemusictree/ui");
1832
1869
  var import_player10 = require("@behindthemusictree/app-kit/player");
1833
- var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
1870
+ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
1834
1871
  var import_transport7 = require("@behindthemusictree/app-kit/transport");
1835
1872
 
1836
1873
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
1837
1874
  var import_player9 = require("@behindthemusictree/app-kit/player");
1838
- var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
1875
+ var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
1839
1876
 
1840
1877
  // src/genre-tree/TrackPositionPlayPause.tsx
1841
1878
  var import_fa3 = require("react-icons/fa");
@@ -1890,8 +1927,8 @@ function TrackPositionPlayPause({
1890
1927
  }
1891
1928
 
1892
1929
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
1893
- var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
1894
- var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
1930
+ var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
1931
+ var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
1895
1932
  var import_jsx_runtime32 = require("react/jsx-runtime");
1896
1933
  function TrackItem({
1897
1934
  track,
@@ -1902,11 +1939,11 @@ function TrackItem({
1902
1939
  renderSongLink
1903
1940
  }) {
1904
1941
  const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
1905
- const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree14.useTrackList)();
1942
+ const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree20.useTrackList)();
1906
1943
  const scope = trackList?.origin?.scope ?? null;
1907
1944
  const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
1908
1945
  const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
1909
- const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
1946
+ const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree21.isSong)(track) && (0, import_genre_tree21.songUnplayableReason)(track) !== null;
1910
1947
  const handlePlayPauseClick = (event) => {
1911
1948
  event.stopPropagation();
1912
1949
  if (isCurrentTrack) {
@@ -1939,8 +1976,8 @@ function TrackItem({
1939
1976
  }
1940
1977
  ),
1941
1978
  /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
1942
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime32.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_runtime32.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
1943
- track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
1979
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime32.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_runtime32.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
1980
+ track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_genre_tree22.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
1944
1981
  ] }),
1945
1982
  /* @__PURE__ */ (0, import_jsx_runtime32.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_runtime32.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
1946
1983
  renderDuration && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
@@ -1959,7 +1996,7 @@ function TrackItem({
1959
1996
 
1960
1997
  // src/genre-tree/artist/ArtistSongs.tsx
1961
1998
  var import_jsx_runtime33 = require("react/jsx-runtime");
1962
- var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(import_genre_tree17.SongDetailedSchema);
1999
+ var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(import_genre_tree23.SongDetailedSchema);
1963
2000
  var SORTS = [
1964
2001
  { ordering: "title", label: "Title" },
1965
2002
  { ordering: "-albumYear", label: "Year" }
@@ -1968,11 +2005,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
1968
2005
  const [page, setPage] = (0, import_react14.useState)(1);
1969
2006
  const [ordering, setOrdering] = (0, import_react14.useState)("title");
1970
2007
  const { fetch } = (0, import_transport7.useFetchWrapper)(getBackendBaseUrl);
1971
- const { playNewTrackListFromArtist } = (0, import_genre_tree17.useTrackList)();
2008
+ const { playNewTrackListFromArtist } = (0, import_genre_tree23.useTrackList)();
1972
2009
  const { playerTrackObject } = (0, import_player10.usePlayer)();
1973
2010
  const { data, isPending, isError } = (0, import_transport7.useQueryWithParse)({
1974
2011
  queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
1975
- queryFn: () => fetch(import_genre_tree17.libraryEndpoints.reference.songs.list(), true, false, void 0, {
2012
+ queryFn: () => fetch(import_genre_tree23.libraryEndpoints.reference.songs.list(), true, false, void 0, {
1976
2013
  artist: artist.uuid,
1977
2014
  ordering,
1978
2015
  page
@@ -1987,7 +2024,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
1987
2024
  });
1988
2025
  const handleRowClickCapture = (event, song) => {
1989
2026
  if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
1990
- if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree17.songUnplayableReason)(song) !== null) return;
2027
+ if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree23.songUnplayableReason)(song) !== null) return;
1991
2028
  event.stopPropagation();
1992
2029
  void play(song.uuid);
1993
2030
  };
@@ -2046,7 +2083,7 @@ function useArtistPanel({
2046
2083
  onExpandedChange,
2047
2084
  onClose
2048
2085
  }) {
2049
- const result = (0, import_genre_tree18.useArtist)(uuid, getBackendBaseUrl);
2086
+ const result = (0, import_genre_tree24.useArtist)(uuid, getBackendBaseUrl);
2050
2087
  const panel = { expanded, onExpandedChange, onClose };
2051
2088
  switch (result.status) {
2052
2089
  case "idle":
@@ -2107,21 +2144,21 @@ function useArtistPanel({
2107
2144
 
2108
2145
  // src/genre-tree/song/useSongPanel.tsx
2109
2146
  var import_ui14 = require("@behindthemusictree/ui");
2110
- var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
2147
+ var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2111
2148
 
2112
2149
  // src/genre-tree/song/SongPanelContent.tsx
2113
2150
  var import_react15 = require("react");
2114
2151
  var import_lucide_react10 = require("lucide-react");
2115
2152
  var import_ui13 = require("@behindthemusictree/ui");
2116
2153
  var import_player11 = require("@behindthemusictree/app-kit/player");
2117
- var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
2154
+ var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
2118
2155
  var import_jsx_runtime35 = require("react/jsx-runtime");
2119
2156
  var numberFormat = new Intl.NumberFormat("en");
2120
2157
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2121
2158
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
2122
2159
  function SongHistory({ uuid, getBackendBaseUrl }) {
2123
2160
  const [open, setOpen] = (0, import_react15.useState)(false);
2124
- const history = (0, import_genre_tree19.useSongHistory)(uuid, getBackendBaseUrl, open);
2161
+ const history = (0, import_genre_tree25.useSongHistory)(uuid, getBackendBaseUrl, open);
2125
2162
  let body = null;
2126
2163
  if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.Skeleton, { className: "h-10", "aria-busy": "true" });
2127
2164
  else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
@@ -2162,13 +2199,13 @@ function SongPanelContent({
2162
2199
  genrePath,
2163
2200
  onSelectGenre
2164
2201
  }) {
2165
- const { playNewTrackListFromTrackUuid } = (0, import_genre_tree19.useTrackList)();
2202
+ const { playNewTrackListFromTrackUuid } = (0, import_genre_tree25.useTrackList)();
2166
2203
  const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
2167
- const unplayableReason = (0, import_genre_tree19.songUnplayableReason)(song);
2204
+ const unplayableReason = (0, import_genre_tree25.songUnplayableReason)(song);
2168
2205
  const isCurrent = playerTrackObject?.track.id === song.uuid;
2169
2206
  const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
2170
2207
  const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
2171
- const musicBrainzUrl = (0, import_genre_tree19.musicBrainzRecordingUrl)(song);
2208
+ const musicBrainzUrl = (0, import_genre_tree25.musicBrainzRecordingUrl)(song);
2172
2209
  const fields = [];
2173
2210
  if (song.genre) {
2174
2211
  const path = genrePath.length > 0 ? genrePath : null;
@@ -2227,7 +2264,7 @@ function SongPanelContent({
2227
2264
  ),
2228
2265
  shareUrl && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2229
2266
  ] }) }),
2230
- song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree19.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2267
+ song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree25.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2231
2268
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.FieldList, { items: fields }),
2232
2269
  song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2233
2270
  /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
@@ -2248,7 +2285,7 @@ function useSongPanel({
2248
2285
  onExpandedChange,
2249
2286
  onClose
2250
2287
  }) {
2251
- const result = (0, import_genre_tree20.useSong)(uuid, getBackendBaseUrl);
2288
+ const result = (0, import_genre_tree26.useSong)(uuid, getBackendBaseUrl);
2252
2289
  const panel = { expanded, onExpandedChange, onClose };
2253
2290
  switch (result.status) {
2254
2291
  case "idle":
@@ -2425,7 +2462,7 @@ function GenreTreeView({
2425
2462
  allowWheelRotation: internalAllowWheelRotation,
2426
2463
  toggleWheelRotation,
2427
2464
  onNodeHover: handleNodeHover
2428
- } = (0, import_genre_tree21.useGenreTreeViewState)({
2465
+ } = (0, import_genre_tree27.useGenreTreeViewState)({
2429
2466
  scope,
2430
2467
  getBackendBaseUrl,
2431
2468
  viewMode: controlledViewMode,
@@ -2444,12 +2481,12 @@ function GenreTreeView({
2444
2481
  restoreRootUuid = selectedGenrePlaylist.root.uuid;
2445
2482
  setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
2446
2483
  }
2447
- const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree21.genreUpdateMutationKey }) > 0;
2448
- const { mutate: updateGenre } = (0, import_genre_tree21.useUpdateGenre)(scope, getBackendBaseUrl);
2484
+ const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree27.genreUpdateMutationKey }) > 0;
2485
+ const { mutate: updateGenre } = (0, import_genre_tree27.useUpdateGenre)(scope, getBackendBaseUrl);
2449
2486
  const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
2450
2487
  const isArtistFiltered = artistUuid !== null;
2451
2488
  const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
2452
- const artistResult = (0, import_genre_tree21.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
2489
+ const artistResult = (0, import_genre_tree27.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
2453
2490
  const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
2454
2491
  const filterArtist = (0, import_react16.useMemo)(
2455
2492
  () => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
@@ -2657,6 +2694,7 @@ function GenreTreeView({
2657
2694
  ] }),
2658
2695
  genreUuid,
2659
2696
  genrePlaylist,
2697
+ catalogue: genrePlaylist.catalogue,
2660
2698
  wikidataId: criteria.wikidataId,
2661
2699
  scope,
2662
2700
  getBackendBaseUrl,
@@ -3004,7 +3042,7 @@ var import_react17 = require("react");
3004
3042
  var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
3005
3043
  var import_ui17 = require("@behindthemusictree/ui");
3006
3044
  var import_lucide_react13 = require("lucide-react");
3007
- var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
3045
+ var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
3008
3046
  var import_jsx_runtime39 = require("react/jsx-runtime");
3009
3047
  var RELATIVE_ITEM_COUNT = 1;
3010
3048
  function toNode(genre, parentId) {
@@ -3032,7 +3070,7 @@ function GenreDetailPanel({
3032
3070
  edgeTab,
3033
3071
  extras
3034
3072
  }) {
3035
- const { data: detail, isPending, isError } = (0, import_genre_tree22.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
3073
+ const { data: detail, isPending, isError } = (0, import_genre_tree28.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
3036
3074
  const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
3037
3075
  const view = (0, import_react17.useMemo)(() => {
3038
3076
  if (!detail) return null;
@@ -3048,7 +3086,7 @@ function GenreDetailPanel({
3048
3086
  id: detail.uuid,
3049
3087
  parentId: detail.parent?.uuid ?? null,
3050
3088
  name: detail.name,
3051
- itemCount: detail.tracksCount,
3089
+ itemCount: scope === "reference" ? (0, import_genre_tree28.genreMusicCount)(import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
3052
3090
  wikidataId: detail.wikidataId
3053
3091
  },
3054
3092
  fill: rootColor,
@@ -3058,7 +3096,7 @@ function GenreDetailPanel({
3058
3096
  ({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
3059
3097
  )
3060
3098
  };
3061
- }, [detail]);
3099
+ }, [detail, scope]);
3062
3100
  if (isError) {
3063
3101
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
3064
3102
  }
@@ -3111,7 +3149,8 @@ function GenreDetailPanel({
3111
3149
  ancestors
3112
3150
  ] }),
3113
3151
  genreUuid: detail.uuid,
3114
- genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
3152
+ genrePlaylist: scope === "reference" ? import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
3153
+ catalogue: detail.catalogue,
3115
3154
  wikidataId: detail.wikidataId,
3116
3155
  scope,
3117
3156
  getBackendBaseUrl,
@@ -3132,9 +3171,9 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
3132
3171
  // src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
3133
3172
  var import_react18 = require("react");
3134
3173
  var import_lucide_react14 = require("lucide-react");
3135
- var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
3136
- var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
3137
- var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
3174
+ var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
3175
+ var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
3176
+ var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
3138
3177
  var import_jsx_runtime40 = require("react/jsx-runtime");
3139
3178
  var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
3140
3179
  function TrackListSidebar({
@@ -3148,8 +3187,8 @@ function TrackListSidebar({
3148
3187
  renderArtistLink,
3149
3188
  renderSongLink
3150
3189
  }) {
3151
- const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree23.useTrackList)();
3152
- const trackNoun = (0, import_track_noun3.useTrackNoun)();
3190
+ const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree29.useTrackList)();
3191
+ const trackNoun = (0, import_track_noun4.useTrackNoun)();
3153
3192
  const trackListRef = (0, import_react18.useRef)(null);
3154
3193
  const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
3155
3194
  const handledAutoAdvanceRequestIdRef = (0, import_react18.useRef)(null);
@@ -3251,7 +3290,7 @@ function TrackListSidebar({
3251
3290
  /* @__PURE__ */ (0, import_jsx_runtime40.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_runtime40.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3252
3291
  ] }),
3253
3292
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
3254
- 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 `,
3293
+ 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 `,
3255
3294
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3256
3295
  ] })
3257
3296
  ] }),
@@ -3302,12 +3341,12 @@ function TrackListSidebar({
3302
3341
  var import_react19 = require("react");
3303
3342
  var import_tailwind_merge = require("tailwind-merge");
3304
3343
  var import_player13 = require("@behindthemusictree/app-kit/player");
3305
- var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
3344
+ var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
3306
3345
  var import_ui18 = require("@behindthemusictree/ui");
3307
3346
 
3308
3347
  // src/genre-tree/PlayerControls.tsx
3309
3348
  var import_lucide_react15 = require("lucide-react");
3310
- var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
3349
+ var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
3311
3350
  var import_player12 = require("@behindthemusictree/app-kit/player");
3312
3351
  var import_jsx_runtime41 = require("react/jsx-runtime");
3313
3352
  function PlayerControls() {
@@ -3321,7 +3360,7 @@ function PlayerControls() {
3321
3360
  setVolume,
3322
3361
  volume
3323
3362
  } = (0, import_player12.usePlayer)();
3324
- const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree25.useTrackList)();
3363
+ const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree31.useTrackList)();
3325
3364
  const currentTime = (0, import_player12.useCurrentTime)();
3326
3365
  const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
3327
3366
  const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
@@ -3335,7 +3374,7 @@ function PlayerControls() {
3335
3374
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3336
3375
  children: [
3337
3376
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3338
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree25.formatTime)(safeCurrentTime) }),
3377
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree31.formatTime)(safeCurrentTime) }),
3339
3378
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
3340
3379
  "input",
3341
3380
  {
@@ -3350,7 +3389,7 @@ function PlayerControls() {
3350
3389
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3351
3390
  }
3352
3391
  ),
3353
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree25.formatTime)(safeDuration) })
3392
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree31.formatTime)(safeDuration) })
3354
3393
  ] }),
3355
3394
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3356
3395
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
@@ -3429,7 +3468,7 @@ function PlayerPanel({
3429
3468
  renderSongLink
3430
3469
  }) {
3431
3470
  const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
3432
- const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree26.useTrackListSidebarVisibility)();
3471
+ const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree32.useTrackListSidebarVisibility)();
3433
3472
  const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
3434
3473
  const loadedTrackTitle = playerTrackObject?.track.title;
3435
3474
  (0, import_react19.useEffect)(() => {
@@ -3526,7 +3565,7 @@ function PlayerPanel({
3526
3565
  // src/genre-tree/Rating.tsx
3527
3566
  var import_uuid = require("uuid");
3528
3567
  var import_ai = require("react-icons/ai");
3529
- var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
3568
+ var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
3530
3569
  var import_jsx_runtime43 = require("react/jsx-runtime");
3531
3570
  function Rating({ rating, handleChange }) {
3532
3571
  const instanceId = (0, import_uuid.v4)();
@@ -3538,7 +3577,7 @@ function Rating({ rating, handleChange }) {
3538
3577
  className: "w-6 h-6 opacity-0",
3539
3578
  type: "radio",
3540
3579
  name: "rating",
3541
- value: i === 0 ? import_genre_tree27.FORM_RATING_NULL_VALUE : i,
3580
+ value: i === 0 ? import_genre_tree33.FORM_RATING_NULL_VALUE : i,
3542
3581
  checked: rating === (i === 0 ? null : i),
3543
3582
  onChange: handleChange
3544
3583
  }
@@ -3547,7 +3586,7 @@ function Rating({ rating, handleChange }) {
3547
3586
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
3548
3587
  import_ai.AiOutlineStar,
3549
3588
  {
3550
- 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)]"}`
3589
+ 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)]"}`
3551
3590
  }
3552
3591
  ),
3553
3592
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-grow w-8" }),