@behindthemusictree/app-kit-ui 42.0.2 → 43.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,7 +52,7 @@ var import_react19 = require("react");
52
52
  var import_react_query3 = require("@tanstack/react-query");
53
53
  var import_lucide_react13 = require("lucide-react");
54
54
  var import_lucide_react14 = require("lucide-react");
55
- var import_ui21 = require("@behindthemusictree/ui");
55
+ var import_ui22 = require("@behindthemusictree/ui");
56
56
  var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
57
57
  var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
58
58
  var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
@@ -1848,7 +1848,16 @@ function useGenreDetailContent({
1848
1848
  onSelectNode: selectGenre
1849
1849
  }
1850
1850
  );
1851
- const ancestors = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1851
+ const ancestors = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1852
+ import_genre_tree_view4.GenreNodeAncestors,
1853
+ {
1854
+ node: view.node,
1855
+ fill: view.fill,
1856
+ textColor: view.textColor,
1857
+ ancestorNodes: view.ancestorNodes,
1858
+ onSelectNode: selectGenre
1859
+ }
1860
+ );
1852
1861
  return {
1853
1862
  name: detail.name,
1854
1863
  title: detail.name,
@@ -1924,6 +1933,7 @@ function GenreDetailPanel({
1924
1933
  // src/genre-tree/ChangeParentPopup.tsx
1925
1934
  var import_react16 = require("react");
1926
1935
  var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
1936
+ var import_ui11 = require("@behindthemusictree/ui");
1927
1937
  var import_jsx_runtime32 = require("react/jsx-runtime");
1928
1938
  function ChangeParentPopup({
1929
1939
  genrePlaylist,
@@ -1938,20 +1948,22 @@ function ChangeParentPopup({
1938
1948
  () => (0, import_genre_tree21.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
1939
1949
  [genrePlaylists, genrePlaylist.uuid]
1940
1950
  );
1941
- const handleSelect = (newParent) => {
1951
+ const [confirmingRoot, setConfirmingRoot] = (0, import_react16.useState)(false);
1952
+ const handleMove = (to) => {
1942
1953
  const genre = genrePlaylist.criteria.uuid;
1943
1954
  const parentPlaylistUuid = genrePlaylist.parent?.uuid;
1944
1955
  const from = parentPlaylistUuid ? genrePlaylists.find(({ uuid }) => uuid === parentPlaylistUuid)?.criteria?.uuid ?? null : null;
1945
1956
  mutate(
1946
- { uuid: genre, data: { parent: newParent.criteria.uuid } },
1957
+ { uuid: genre, data: { parent: to } },
1947
1958
  {
1948
1959
  onSuccess: () => {
1949
1960
  onClose();
1950
- onParentChanged?.({ genre, from, to: newParent.criteria.uuid });
1961
+ onParentChanged?.({ genre, from, to });
1951
1962
  }
1952
1963
  }
1953
1964
  );
1954
1965
  };
1966
+ const handleSelect = (newParent) => handleMove(newParent.criteria.uuid);
1955
1967
  (0, import_react16.useEffect)(() => {
1956
1968
  const handleKeyDown = (event) => {
1957
1969
  if (event.key === "Escape") onClose();
@@ -1964,6 +1976,18 @@ function ChangeParentPopup({
1964
1976
  "Current parent: ",
1965
1977
  genrePlaylist.parent?.name ?? "none"
1966
1978
  ] }),
1979
+ genrePlaylist.parent && (confirmingRoot ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "mb-3 text-sm", children: [
1980
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("p", { className: "mb-2", children: [
1981
+ genrePlaylist.name,
1982
+ " and its descendants will no longer be under ",
1983
+ genrePlaylist.parent.name,
1984
+ "."
1985
+ ] }),
1986
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(PopupButtons, { alignment: "left", children: [
1987
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.Button, { disabled: isPending, onClick: () => handleMove(null), children: "Confirm" }),
1988
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.Button, { variant: "secondary", disabled: isPending, onClick: () => setConfirmingRoot(false), children: "Back" })
1989
+ ] })
1990
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.Button, { variant: "secondary", onClick: () => setConfirmingRoot(true), children: "Make root" }) })),
1967
1991
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1968
1992
  GenreSearch,
1969
1993
  {
@@ -1977,12 +2001,12 @@ function ChangeParentPopup({
1977
2001
  }
1978
2002
 
1979
2003
  // src/genre-tree/artist/useArtistPanel.tsx
1980
- var import_ui17 = require("@behindthemusictree/ui");
2004
+ var import_ui18 = require("@behindthemusictree/ui");
1981
2005
  var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
1982
2006
 
1983
2007
  // src/genre-tree/artist/ArtistActionBand.tsx
1984
2008
  var import_lucide_react10 = require("lucide-react");
1985
- var import_ui11 = require("@behindthemusictree/ui");
2009
+ var import_ui12 = require("@behindthemusictree/ui");
1986
2010
  var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
1987
2011
  var import_player7 = require("@behindthemusictree/app-kit/player");
1988
2012
  var import_jsx_runtime33 = require("react/jsx-runtime");
@@ -2001,9 +2025,9 @@ function ArtistActionBand({ artist, share }) {
2001
2025
  console.error("Failed to load artist tracks:", error);
2002
2026
  });
2003
2027
  };
2004
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ui11.ActionBand, { children: [
2028
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ui12.ActionBand, { children: [
2005
2029
  /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2006
- import_ui11.ActionButton,
2030
+ import_ui12.ActionButton,
2007
2031
  {
2008
2032
  icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2009
2033
  label: playing ? "Pause" : "Play",
@@ -2018,12 +2042,12 @@ function ArtistActionBand({ artist, share }) {
2018
2042
  }
2019
2043
 
2020
2044
  // src/genre-tree/artist/ArtistHeader.tsx
2021
- var import_ui13 = require("@behindthemusictree/ui");
2045
+ var import_ui14 = require("@behindthemusictree/ui");
2022
2046
 
2023
2047
  // src/genre-tree/artist/ArtistLinks.tsx
2024
2048
  var import_lucide_react11 = require("lucide-react");
2025
2049
  var import_si3 = require("react-icons/si");
2026
- var import_ui12 = require("@behindthemusictree/ui");
2050
+ var import_ui13 = require("@behindthemusictree/ui");
2027
2051
  var import_jsx_runtime34 = require("react/jsx-runtime");
2028
2052
  var LINK_KINDS = {
2029
2053
  official: { label: "Official website", Icon: import_lucide_react11.Globe },
@@ -2061,7 +2085,7 @@ function artistLinks(artist) {
2061
2085
  return links;
2062
2086
  }
2063
2087
  function ArtistLinks({ links }) {
2064
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2088
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui13.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2065
2089
  }
2066
2090
 
2067
2091
  // src/genre-tree/artist/formatYears.ts
@@ -2110,7 +2134,7 @@ function ArtistHeader({
2110
2134
  const links = artistLinks(artist);
2111
2135
  if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistLinks, { links }) });
2112
2136
  if (fields.length === 0) return null;
2113
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.FieldList, { items: fields });
2137
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui14.FieldList, { items: fields });
2114
2138
  }
2115
2139
 
2116
2140
  // src/genre-tree/artist/ArtistPhoto.tsx
@@ -2120,11 +2144,11 @@ function ArtistPhoto({ image, name }) {
2120
2144
  }
2121
2145
 
2122
2146
  // src/genre-tree/artist/ArtistMembers.tsx
2123
- var import_ui14 = require("@behindthemusictree/ui");
2147
+ var import_ui15 = require("@behindthemusictree/ui");
2124
2148
  var import_jsx_runtime37 = require("react/jsx-runtime");
2125
2149
  function MembershipList({ title, memberships, renderArtistLink }) {
2126
2150
  if (memberships.length === 0) return null;
2127
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: memberships.map((membership, index) => {
2151
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui15.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: memberships.map((membership, index) => {
2128
2152
  const details = [
2129
2153
  formatYears(membership.beginYear, membership.endYear, membership.ended),
2130
2154
  ...membership.roles
@@ -2145,7 +2169,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2145
2169
  function ArtistMembers({ title, memberships, renderArtistLink }) {
2146
2170
  if (memberships.length === 0) return null;
2147
2171
  const isCurrent = (membership) => !membership.ended && membership.endYear === null;
2148
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ui14.PanelSection, { title, children: [
2172
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ui15.PanelSection, { title, children: [
2149
2173
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2150
2174
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2151
2175
  MembershipList,
@@ -2159,7 +2183,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
2159
2183
  }
2160
2184
 
2161
2185
  // src/genre-tree/artist/ArtistDiscography.tsx
2162
- var import_ui15 = require("@behindthemusictree/ui");
2186
+ var import_ui16 = require("@behindthemusictree/ui");
2163
2187
  var import_jsx_runtime38 = require("react/jsx-runtime");
2164
2188
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
2165
2189
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
@@ -2180,10 +2204,10 @@ function byYear(a, b) {
2180
2204
  function ArtistDiscography({ albums }) {
2181
2205
  if (albums.length === 0) return null;
2182
2206
  const sorted = [...albums].sort(byYear);
2183
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2207
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui16.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2184
2208
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
2185
2209
  if (groupAlbums.length === 0) return null;
2186
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2210
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui16.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2187
2211
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2188
2212
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2189
2213
  ] }, album.uuid)) }) }, group);
@@ -2193,7 +2217,7 @@ function ArtistDiscography({ albums }) {
2193
2217
  // src/genre-tree/artist/ArtistSongs.tsx
2194
2218
  var import_react17 = require("react");
2195
2219
  var import_react_query2 = require("@tanstack/react-query");
2196
- var import_ui16 = require("@behindthemusictree/ui");
2220
+ var import_ui17 = require("@behindthemusictree/ui");
2197
2221
  var import_player11 = require("@behindthemusictree/app-kit/player");
2198
2222
  var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2199
2223
  var import_transport6 = require("@behindthemusictree/app-kit/transport");
@@ -2356,7 +2380,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2356
2380
  event.stopPropagation();
2357
2381
  void play(song.uuid);
2358
2382
  };
2359
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2383
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2360
2384
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
2361
2385
  "button",
2362
2386
  {
@@ -2380,7 +2404,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2380
2404
  renderSongLink
2381
2405
  }
2382
2406
  ) }, song.uuid)) }),
2383
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2407
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2384
2408
  ] }) });
2385
2409
  }
2386
2410
 
@@ -2408,12 +2432,12 @@ function useArtistPanel({
2408
2432
  ...panel,
2409
2433
  title: "Artist",
2410
2434
  content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2411
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2412
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
2413
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
2414
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" }),
2415
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-16 mt-2.5" }),
2416
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-16" })
2435
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-3 w-16" }),
2436
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-4 w-32" }),
2437
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-3 w-16" }),
2438
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-4 w-24" }),
2439
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-16 mt-2.5" }),
2440
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.Skeleton, { className: "h-16" })
2417
2441
  ] })
2418
2442
  };
2419
2443
  case "not-found":
@@ -2464,13 +2488,13 @@ function useArtistPanel({
2464
2488
  }
2465
2489
 
2466
2490
  // src/genre-tree/song/useSongPanel.tsx
2467
- var import_ui19 = require("@behindthemusictree/ui");
2491
+ var import_ui20 = require("@behindthemusictree/ui");
2468
2492
  var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
2469
2493
 
2470
2494
  // src/genre-tree/song/SongPanelContent.tsx
2471
2495
  var import_react18 = require("react");
2472
2496
  var import_lucide_react12 = require("lucide-react");
2473
- var import_ui18 = require("@behindthemusictree/ui");
2497
+ var import_ui19 = require("@behindthemusictree/ui");
2474
2498
  var import_player12 = require("@behindthemusictree/app-kit/player");
2475
2499
  var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
2476
2500
  var import_jsx_runtime43 = require("react/jsx-runtime");
@@ -2481,7 +2505,7 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2481
2505
  const [open, setOpen] = (0, import_react18.useState)(false);
2482
2506
  const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
2483
2507
  let body = null;
2484
- if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.Skeleton, { className: "h-10", "aria-busy": "true" });
2508
+ if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.Skeleton, { className: "h-10", "aria-busy": "true" });
2485
2509
  else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2486
2510
  else if (history.status === "success")
2487
2511
  body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: "py-1.5 text-body", children: [
@@ -2499,8 +2523,8 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2499
2523
  dateFormat.format(new Date(entry.createdOn))
2500
2524
  ] })
2501
2525
  ] }, entry.uuid)) });
2502
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2503
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui18.PANEL_LABEL_CLASS}`, children: "History" }),
2526
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2527
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui19.PANEL_LABEL_CLASS}`, children: "History" }),
2504
2528
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "mt-1.5", children: body })
2505
2529
  ] }) });
2506
2530
  }
@@ -2558,9 +2582,9 @@ function SongPanelContent(props) {
2558
2582
  value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex gap-3", children: links })
2559
2583
  });
2560
2584
  return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
2561
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui18.ActionBand, { children: [
2585
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui19.ActionBand, { children: [
2562
2586
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2563
- import_ui18.ActionButton,
2587
+ import_ui19.ActionButton,
2564
2588
  {
2565
2589
  icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2566
2590
  label: isPlaying ? "Pause" : "Play",
@@ -2572,8 +2596,8 @@ function SongPanelContent(props) {
2572
2596
  shareUrl && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2573
2597
  ] }) }),
2574
2598
  song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2575
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.FieldList, { items: fields }),
2576
- song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2599
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.FieldList, { items: fields }),
2600
+ song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2577
2601
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2578
2602
  ] });
2579
2603
  }
@@ -2601,10 +2625,10 @@ function useSongPanel({
2601
2625
  ...panel,
2602
2626
  title: "Song",
2603
2627
  content: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2604
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2605
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-4 w-32" }),
2606
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
2607
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-4 w-24" })
2628
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2629
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui20.Skeleton, { className: "h-4 w-32" }),
2630
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2631
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui20.Skeleton, { className: "h-4 w-24" })
2608
2632
  ] })
2609
2633
  };
2610
2634
  case "not-found":
@@ -2679,14 +2703,14 @@ function useEntityPanel({
2679
2703
 
2680
2704
  // src/genre-tree/SizeMetricToggle.tsx
2681
2705
  var import_md = require("react-icons/md");
2682
- var import_ui20 = require("@behindthemusictree/ui");
2706
+ var import_ui21 = require("@behindthemusictree/ui");
2683
2707
  var import_jsx_runtime45 = require("react/jsx-runtime");
2684
2708
  function SizeMetricToggle({
2685
2709
  value,
2686
2710
  onChange
2687
2711
  }) {
2688
2712
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
2689
- import_ui20.SegmentedControl,
2713
+ import_ui21.SegmentedControl,
2690
2714
  {
2691
2715
  variant: "floating",
2692
2716
  "aria-label": "Size nodes by",
@@ -2929,7 +2953,7 @@ function GenreTreeView({
2929
2953
  []
2930
2954
  );
2931
2955
  const panelRef = (0, import_react19.useRef)(null);
2932
- const obscuredInset = (0, import_ui21.useObscuredInset)(panelRef);
2956
+ const obscuredInset = (0, import_ui22.useObscuredInset)(panelRef);
2933
2957
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
2934
2958
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
2935
2959
  const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
@@ -3058,7 +3082,7 @@ function GenreTreeView({
3058
3082
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
3059
3083
  } : void 0;
3060
3084
  const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3061
- import_ui21.IconButton,
3085
+ import_ui22.IconButton,
3062
3086
  {
3063
3087
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
3064
3088
  style: activePanel.headerAccent && { color: "inherit" },
@@ -3130,7 +3154,7 @@ function GenreTreeView({
3130
3154
  !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3131
3155
  /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
3132
3156
  !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3133
- import_ui21.SegmentedControl,
3157
+ import_ui22.SegmentedControl,
3134
3158
  {
3135
3159
  "aria-label": "Tree view mode",
3136
3160
  value: viewMode,
@@ -3150,7 +3174,7 @@ function GenreTreeView({
3150
3174
  role: "group",
3151
3175
  "aria-label": "Tree display options",
3152
3176
  children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3153
- import_ui21.Button,
3177
+ import_ui22.Button,
3154
3178
  {
3155
3179
  variant: allowWheelRotation ? "default" : "outline",
3156
3180
  size: "sm",
@@ -3161,7 +3185,7 @@ function GenreTreeView({
3161
3185
  }
3162
3186
  ),
3163
3187
  !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3164
- import_ui21.IconTextButton,
3188
+ import_ui22.IconTextButton,
3165
3189
  {
3166
3190
  icon: import_lucide_react13.Plus,
3167
3191
  text: "Add root",
@@ -3309,7 +3333,7 @@ function GenreTreeView({
3309
3333
  }
3310
3334
  ) }),
3311
3335
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3312
- import_ui21.DetailPanel,
3336
+ import_ui22.DetailPanel,
3313
3337
  {
3314
3338
  ref: panelRef,
3315
3339
  side: "left",
@@ -3509,10 +3533,11 @@ function TrackListSidebar({
3509
3533
 
3510
3534
  // src/genre-tree/PlayerPanel.tsx
3511
3535
  var import_react21 = require("react");
3536
+ var import_lucide_react17 = require("lucide-react");
3512
3537
  var import_tailwind_merge = require("tailwind-merge");
3513
3538
  var import_player14 = require("@behindthemusictree/app-kit/player");
3514
3539
  var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
3515
- var import_ui22 = require("@behindthemusictree/ui");
3540
+ var import_ui23 = require("@behindthemusictree/ui");
3516
3541
 
3517
3542
  // src/genre-tree/PlayerControls.tsx
3518
3543
  var import_lucide_react16 = require("lucide-react");
@@ -3637,7 +3662,7 @@ function PlayerPanel({
3637
3662
  renderArtistLink,
3638
3663
  renderSongLink
3639
3664
  }) {
3640
- const { playerTrackObject, retryPlayback } = (0, import_player14.usePlayer)();
3665
+ const { playerTrackObject, retryPlayback, stop } = (0, import_player14.usePlayer)();
3641
3666
  const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree35.useTrackListSidebarVisibility)();
3642
3667
  const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react21.useState)("");
3643
3668
  const loadedTrackTitle = playerTrackObject?.track.title;
@@ -3679,7 +3704,7 @@ function PlayerPanel({
3679
3704
  style: isExtended ? { width: extendedWidth } : void 0,
3680
3705
  children: [
3681
3706
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3682
- import_ui22.PanelEdgeTab,
3707
+ import_ui23.PanelEdgeTab,
3683
3708
  {
3684
3709
  side: "right",
3685
3710
  edge: tabEdge,
@@ -3697,7 +3722,10 @@ function PlayerPanel({
3697
3722
  ),
3698
3723
  children: [
3699
3724
  /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3700
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3725
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: [
3726
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }),
3727
+ playerTrackObject && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui23.IconButton, { "aria-label": "Close player", onClick: stop, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react17.X, { "aria-hidden": "true", className: "h-4 w-4" }) })
3728
+ ] }),
3701
3729
  /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "relative w-full shrink-0", children: [
3702
3730
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3703
3731
  playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
@@ -3772,7 +3800,7 @@ function Rating({ rating, handleChange }) {
3772
3800
 
3773
3801
  // src/genre-tree/EntityDetailPanel.tsx
3774
3802
  var import_react22 = require("react");
3775
- var import_ui23 = require("@behindthemusictree/ui");
3803
+ var import_ui24 = require("@behindthemusictree/ui");
3776
3804
  var import_jsx_runtime51 = require("react/jsx-runtime");
3777
3805
  function EntityDetailPanel({
3778
3806
  scope,
@@ -3816,7 +3844,7 @@ function EntityDetailPanel({
3816
3844
  });
3817
3845
  if (!panel) return null;
3818
3846
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
3819
- import_ui23.DetailPanel,
3847
+ import_ui24.DetailPanel,
3820
3848
  {
3821
3849
  side: "left",
3822
3850
  title: panel.title,