@behindthemusictree/app-kit-ui 32.0.3 → 33.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ import {
15
15
  TrackListSidebar,
16
16
  TrackPositionPlayPause,
17
17
  useGenrePlaylistPlayback
18
- } from "../chunk-QGGVPIXY.js";
18
+ } from "../chunk-2M4Q2IJU.js";
19
19
  import "../chunk-DKQQLLMY.js";
20
20
  export {
21
21
  GenreDetailPanel,
package/dist/index.cjs CHANGED
@@ -402,18 +402,21 @@ function InternalErrorPopup({ errorCode, contactEmail, ...rest }) {
402
402
 
403
403
  // src/popup/NetworkErrorPopup.tsx
404
404
  var import_lucide_react4 = require("lucide-react");
405
+ var import_transport = require("@behindthemusictree/app-kit/transport");
405
406
  var import_jsx_runtime8 = require("react/jsx-runtime");
406
- function NetworkErrorPopup(props) {
407
+ var OFFLINE_MESSAGE = "It seems that you are not connected to the internet. Please check your connection and try again.";
408
+ var UNREACHABLE_MESSAGE = "We couldn't reach the server. Please try again in a moment.";
409
+ function NetworkErrorPopup({ errorCode, ...rest }) {
407
410
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
408
411
  BasePopup,
409
412
  {
410
- ...props,
413
+ ...rest,
411
414
  title: "Network Error",
412
415
  isDismissable: false,
413
416
  icon: import_lucide_react4.AlertTriangle,
414
417
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex flex-col items-center space-y-6 py-4", children: [
415
418
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react4.AlertTriangle, { className: "h-16 w-16 text-[var(--color-neutral-600)]", strokeWidth: 1.5 }),
416
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-center text-[var(--color-neutral-700)]", children: "It seems that you are not connected to the internet. Please check your connection and try again." })
419
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-center text-[var(--color-neutral-700)]", children: errorCode === import_transport.ErrorCode.NETWORK_OFFLINE ? OFFLINE_MESSAGE : UNREACHABLE_MESSAGE })
417
420
  ] })
418
421
  }
419
422
  );
@@ -483,7 +486,7 @@ function AuthPopup({
483
486
  var import_fa2 = require("react-icons/fa");
484
487
  var import_ui5 = require("@behindthemusictree/ui");
485
488
  var import_lucide_react6 = require("lucide-react");
486
- var import_transport = require("@behindthemusictree/app-kit/transport");
489
+ var import_transport2 = require("@behindthemusictree/app-kit/transport");
487
490
  var import_jsx_runtime10 = require("react/jsx-runtime");
488
491
  function SpotifyAuthErrorPopup({
489
492
  message,
@@ -492,7 +495,7 @@ function SpotifyAuthErrorPopup({
492
495
  contactEmail = null,
493
496
  ...rest
494
497
  }) {
495
- const requestAccessHref = (0, import_transport.getSpotifyAllowlistMailtoHref)(contactEmail);
498
+ const requestAccessHref = (0, import_transport2.getSpotifyAllowlistMailtoHref)(contactEmail);
496
499
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
497
500
  BasePopup,
498
501
  {
@@ -619,7 +622,7 @@ var import_react4 = require("react");
619
622
  var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
620
623
  var import_player = require("@behindthemusictree/app-kit/player");
621
624
  var import_popup2 = require("@behindthemusictree/app-kit/popup");
622
- var import_transport2 = require("@behindthemusictree/app-kit/transport");
625
+ var import_transport3 = require("@behindthemusictree/app-kit/transport");
623
626
 
624
627
  // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
625
628
  var import_react3 = require("react");
@@ -701,7 +704,7 @@ function GenrePlaylistTreePerRoot({
701
704
  }
702
705
  play(genrePlaylist).catch((error) => {
703
706
  console.error("Failed to load genre playlist tracks:", error);
704
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InternalErrorPopup, { errorCode: import_transport2.ErrorCode.CLIENT_UNKNOWN }));
707
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(InternalErrorPopup, { errorCode: import_transport3.ErrorCode.CLIENT_UNKNOWN }));
705
708
  });
706
709
  },
707
710
  [
@@ -764,7 +767,7 @@ var import_react5 = require("react");
764
767
  var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
765
768
  var import_player2 = require("@behindthemusictree/app-kit/player");
766
769
  var import_popup3 = require("@behindthemusictree/app-kit/popup");
767
- var import_transport3 = require("@behindthemusictree/app-kit/transport");
770
+ var import_transport4 = require("@behindthemusictree/app-kit/transport");
768
771
  var import_jsx_runtime13 = require("react/jsx-runtime");
769
772
  function GenrePlaylistTreeWheel({
770
773
  scope,
@@ -817,7 +820,7 @@ function GenrePlaylistTreeWheel({
817
820
  }
818
821
  play(genrePlaylist).catch((error) => {
819
822
  console.error("Failed to load genre playlist tracks:", error);
820
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InternalErrorPopup, { errorCode: import_transport3.ErrorCode.CLIENT_UNKNOWN }));
823
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InternalErrorPopup, { errorCode: import_transport4.ErrorCode.CLIENT_UNKNOWN }));
821
824
  });
822
825
  },
823
826
  [
@@ -1106,7 +1109,7 @@ function GenreSearch({
1106
1109
  onPick: handlePick,
1107
1110
  status,
1108
1111
  loading: loading ? "Searching\u2026" : void 0,
1109
- inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md`,
1112
+ inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md focus-visible:ring-0`,
1110
1113
  className: "genre-search-results z-40 rounded-xl shadow-md",
1111
1114
  listClassName: "max-h-[min(20rem,50dvh)]",
1112
1115
  optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
@@ -1505,13 +1508,13 @@ var import_zod = require("zod");
1505
1508
  var import_fa4 = require("react-icons/fa");
1506
1509
  var import_ui7 = require("@behindthemusictree/ui");
1507
1510
  var import_auth2 = require("@behindthemusictree/app-kit/auth");
1508
- var import_transport4 = require("@behindthemusictree/app-kit/transport");
1509
1511
  var import_transport5 = require("@behindthemusictree/app-kit/transport");
1510
1512
  var import_transport6 = require("@behindthemusictree/app-kit/transport");
1513
+ var import_transport7 = require("@behindthemusictree/app-kit/transport");
1511
1514
  var import_player4 = require("@behindthemusictree/app-kit/player");
1512
1515
  var import_player5 = require("@behindthemusictree/app-kit/player");
1513
1516
  var import_popup5 = require("@behindthemusictree/app-kit/popup");
1514
- var import_transport7 = require("@behindthemusictree/app-kit/transport");
1517
+ var import_transport8 = require("@behindthemusictree/app-kit/transport");
1515
1518
  var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
1516
1519
  var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
1517
1520
  var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
@@ -1546,7 +1549,7 @@ var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
1546
1549
  var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
1547
1550
  var import_jsx_runtime24 = require("react/jsx-runtime");
1548
1551
  var CATALOGUE_PAGE_SIZE = 100;
1549
- var SongPageSchema = (0, import_transport5.PaginatedResponseSchema)(import_genre_tree6.SongDetailedSchema).extend({
1552
+ var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree6.SongDetailedSchema).extend({
1550
1553
  overallTotal: import_zod.z.number().nullable(),
1551
1554
  page: import_zod.z.number().nullable(),
1552
1555
  totalPages: import_zod.z.number().nullable()
@@ -1579,9 +1582,10 @@ function GenrePlaylistTracks({
1579
1582
  const { showPopup } = (0, import_popup5.usePopup)();
1580
1583
  const trackNoun = (0, import_track_noun2.useTrackNoun)();
1581
1584
  const { uuid } = genrePlaylist;
1582
- const { fetch } = (0, import_transport4.useFetchWrapper)(getBackendBaseUrl);
1585
+ const { fetch } = (0, import_transport5.useFetchWrapper)(getBackendBaseUrl);
1586
+ const playback = (0, import_transport5.usePlaybackContext)();
1583
1587
  const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
1584
- queryKey: scope === "me" ? import_genre_tree4.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid),
1588
+ queryKey: [...scope === "me" ? import_genre_tree4.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), ...playback.key ? [playback.key] : []],
1585
1589
  queryFn: async ({ pageParam }) => {
1586
1590
  if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
1587
1591
  const response = await fetch(
@@ -1591,12 +1595,12 @@ function GenrePlaylistTracks({
1591
1595
  {},
1592
1596
  { genre: genreUuid, ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
1593
1597
  );
1594
- const parsed = (0, import_transport6.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1598
+ const parsed = (0, import_transport7.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
1595
1599
  return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
1596
1600
  },
1597
1601
  initialPageParam: scope === "me" ? 1 : "",
1598
1602
  getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
1599
- enabled: scope !== "me" || sessionRestored && !!session?.accessToken
1603
+ enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
1600
1604
  });
1601
1605
  const [sentinel, setSentinel] = (0, import_react11.useState)(null);
1602
1606
  (0, import_react11.useEffect)(() => {
@@ -1616,7 +1620,7 @@ function GenrePlaylistTracks({
1616
1620
  const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
1617
1621
  started.catch((error) => {
1618
1622
  console.error("Failed to play genre playlist:", error);
1619
- showPopup(/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(InternalErrorPopup, { errorCode: import_transport7.ErrorCode.CLIENT_UNKNOWN }));
1623
+ showPopup(/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
1620
1624
  });
1621
1625
  };
1622
1626
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui7.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { children: [
@@ -2050,7 +2054,7 @@ var import_lucide_react13 = require("lucide-react");
2050
2054
  var import_ui13 = require("@behindthemusictree/ui");
2051
2055
  var import_player10 = require("@behindthemusictree/app-kit/player");
2052
2056
  var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
2053
- var import_transport8 = require("@behindthemusictree/app-kit/transport");
2057
+ var import_transport9 = require("@behindthemusictree/app-kit/transport");
2054
2058
 
2055
2059
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2056
2060
  var import_player9 = require("@behindthemusictree/app-kit/player");
@@ -2178,7 +2182,7 @@ function TrackItem({
2178
2182
 
2179
2183
  // src/genre-tree/artist/ArtistSongs.tsx
2180
2184
  var import_jsx_runtime35 = require("react/jsx-runtime");
2181
- var SongPageSchema2 = (0, import_transport8.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema);
2185
+ var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema);
2182
2186
  var SORTS = [
2183
2187
  { ordering: "title", label: "Title" },
2184
2188
  { ordering: "-albumYear", label: "Year" }
@@ -2186,10 +2190,10 @@ var SORTS = [
2186
2190
  function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
2187
2191
  const [page, setPage] = (0, import_react14.useState)(1);
2188
2192
  const [ordering, setOrdering] = (0, import_react14.useState)("title");
2189
- const { fetch } = (0, import_transport8.useFetchWrapper)(getBackendBaseUrl);
2193
+ const { fetch } = (0, import_transport9.useFetchWrapper)(getBackendBaseUrl);
2190
2194
  const { playNewTrackListFromArtist } = (0, import_genre_tree14.useTrackList)();
2191
2195
  const { playerTrackObject } = (0, import_player10.usePlayer)();
2192
- const { data, isPending, isError } = (0, import_transport8.useQueryWithParse)({
2196
+ const { data, isPending, isError } = (0, import_transport9.useQueryWithParse)({
2193
2197
  queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
2194
2198
  queryFn: () => fetch(import_genre_tree14.libraryEndpoints.reference.songs.list(), true, false, void 0, {
2195
2199
  artist: artist.uuid,
@@ -2868,7 +2872,10 @@ function GenreTreeView({
2868
2872
  const ancestors = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
2869
2873
  const genrePlaylist = genrePlaylistByNodeId.get(node.id);
2870
2874
  const criteria = genrePlaylist?.criteria;
2871
- if (!genrePlaylist || !criteria) return { primary: family, aside: ancestors };
2875
+ if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2876
+ family,
2877
+ ancestors
2878
+ ] }) };
2872
2879
  const genreUuid = criteria.uuid;
2873
2880
  const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist.parent?.uuid);
2874
2881
  const canToggleSide = !readOnly && parentPlaylist !== void 0 && parentPlaylist.parent === null;
@@ -2889,7 +2896,10 @@ function GenreTreeView({
2889
2896
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2890
2897
  GenreInfoPanelBody,
2891
2898
  {
2892
- family,
2899
+ family: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
2900
+ family,
2901
+ ancestors
2902
+ ] }),
2893
2903
  genreUuid,
2894
2904
  genrePlaylist,
2895
2905
  wikidataId: criteria.wikidataId,
@@ -2936,8 +2946,7 @@ function GenreTreeView({
2936
2946
  }
2937
2947
  ),
2938
2948
  renderDangerZone?.(node)
2939
- ] }),
2940
- aside: ancestors
2949
+ ] })
2941
2950
  };
2942
2951
  };
2943
2952
  const isRotationControlled = controlledAllowWheelRotation !== void 0;
@@ -2954,11 +2963,10 @@ function GenreTreeView({
2954
2963
  compact: false
2955
2964
  };
2956
2965
  } else if (relations) {
2957
- const { primary, aside } = renderGenreBody(relations);
2966
+ const { primary } = renderGenreBody(relations);
2958
2967
  activePanel = {
2959
2968
  title: relations.node.name,
2960
2969
  content: primary,
2961
- aside,
2962
2970
  expanded: isExpanded,
2963
2971
  onExpandedChange: changeExpanded,
2964
2972
  onClose: clearSelection,
@@ -3335,12 +3343,14 @@ function GenreDetailPanel({
3335
3343
  expanded,
3336
3344
  onExpandedChange,
3337
3345
  edgeTab,
3338
- aside: ancestors,
3339
3346
  children: [
3340
3347
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
3341
3348
  GenreInfoPanelBody,
3342
3349
  {
3343
- family,
3350
+ family: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
3351
+ family,
3352
+ ancestors
3353
+ ] }),
3344
3354
  genreUuid: detail.uuid,
3345
3355
  genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
3346
3356
  wikidataId: detail.wikidataId,
@@ -3659,7 +3669,7 @@ function PlayerPanel({
3659
3669
  renderArtistLink,
3660
3670
  renderSongLink
3661
3671
  }) {
3662
- const { playerTrackObject } = (0, import_player13.usePlayer)();
3672
+ const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
3663
3673
  const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree24.useTrackListSidebarVisibility)();
3664
3674
  const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
3665
3675
  const loadedTrackTitle = playerTrackObject?.track.title;
@@ -3722,7 +3732,12 @@ function PlayerPanel({
3722
3732
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3723
3733
  /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "relative w-full shrink-0", children: [
3724
3734
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3725
- playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: playerTrackObject.loadError })
3735
+ playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3736
+ playerTrackObject.loadError,
3737
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
3738
+ void retryPlayback();
3739
+ }, children: "Retry" })
3740
+ ] })
3726
3741
  ] }),
3727
3742
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PlayerControls, {})
3728
3743
  ] }),