@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.
@@ -199,7 +199,7 @@ interface GenreSearchGroupsProps {
199
199
  type ParentChange = {
200
200
  genre: GenreUuid;
201
201
  from: GenreUuid | null;
202
- to: GenreUuid;
202
+ to: GenreUuid | null;
203
203
  };
204
204
 
205
205
  /** Identifies the detail-panel entity whose current host route should be shared. */
@@ -245,7 +245,7 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
245
245
  handleGenreCreationAction: (parent: CriteriaMinimum | null) => void;
246
246
  handleGenreRenameAction: (genre: CriteriaMinimum) => void;
247
247
  getBackendBaseUrl: () => string;
248
- /** Called after the Parent popup moved a genre; `from` and `to` are genre (criteria) uuids, `from` null for a root genre. */
248
+ /** Called after the Parent popup moved a genre; `from` and `to` are genre (criteria) uuids, `from` null for a root genre, `to` null when the genre was made a root. */
249
249
  onParentChanged?: (change: ParentChange) => void;
250
250
  additionalActions?: (node: GenreTreeNode) => GenreTreeAction[];
251
251
  /** Controlled view mode. When provided, the internal Stacked/Wheel toggle is not rendered — the consumer owns that UI. */
@@ -466,8 +466,6 @@ interface PlayerPanelProps {
466
466
  className?: string;
467
467
  /** Hidden via CSS, never unmounted, so the video keeps playing while the host hides the panel. */
468
468
  isVisible?: boolean;
469
- /** @deprecated The player no longer closes; its header action minimizes to video-only mode. */
470
- onClose?: () => void;
471
469
  /** Controlled extended state; uncontrolled when omitted. */
472
470
  expanded?: boolean;
473
471
  onExpandedChange?: (expanded: boolean) => void;
@@ -23,7 +23,7 @@ import {
23
23
  useGenrePlaylistPlayback,
24
24
  useGenreRemoteSearchGroup,
25
25
  useSongSearchGroup
26
- } from "../chunk-PVYUC2PN.js";
26
+ } from "../chunk-UTUU463Q.js";
27
27
  import "../chunk-2GH7CSCE.js";
28
28
  export {
29
29
  EntityDetailPanel,
package/dist/index.cjs CHANGED
@@ -593,7 +593,7 @@ var import_react19 = require("react");
593
593
  var import_react_query3 = require("@tanstack/react-query");
594
594
  var import_lucide_react17 = require("lucide-react");
595
595
  var import_lucide_react18 = require("lucide-react");
596
- var import_ui24 = require("@behindthemusictree/ui");
596
+ var import_ui25 = require("@behindthemusictree/ui");
597
597
  var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
598
598
  var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
599
599
  var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
@@ -2146,7 +2146,16 @@ function useGenreDetailContent({
2146
2146
  onSelectNode: selectGenre
2147
2147
  }
2148
2148
  );
2149
- const ancestors = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
2149
+ const ancestors = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2150
+ import_genre_tree_view4.GenreNodeAncestors,
2151
+ {
2152
+ node: view.node,
2153
+ fill: view.fill,
2154
+ textColor: view.textColor,
2155
+ ancestorNodes: view.ancestorNodes,
2156
+ onSelectNode: selectGenre
2157
+ }
2158
+ );
2150
2159
  return {
2151
2160
  name: detail.name,
2152
2161
  title: detail.name,
@@ -2222,6 +2231,7 @@ function GenreDetailPanel({
2222
2231
  // src/genre-tree/ChangeParentPopup.tsx
2223
2232
  var import_react16 = require("react");
2224
2233
  var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
2234
+ var import_ui14 = require("@behindthemusictree/ui");
2225
2235
  var import_jsx_runtime38 = require("react/jsx-runtime");
2226
2236
  function ChangeParentPopup({
2227
2237
  genrePlaylist,
@@ -2236,20 +2246,22 @@ function ChangeParentPopup({
2236
2246
  () => (0, import_genre_tree21.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
2237
2247
  [genrePlaylists, genrePlaylist.uuid]
2238
2248
  );
2239
- const handleSelect = (newParent) => {
2249
+ const [confirmingRoot, setConfirmingRoot] = (0, import_react16.useState)(false);
2250
+ const handleMove = (to) => {
2240
2251
  const genre = genrePlaylist.criteria.uuid;
2241
2252
  const parentPlaylistUuid = genrePlaylist.parent?.uuid;
2242
2253
  const from = parentPlaylistUuid ? genrePlaylists.find(({ uuid }) => uuid === parentPlaylistUuid)?.criteria?.uuid ?? null : null;
2243
2254
  mutate(
2244
- { uuid: genre, data: { parent: newParent.criteria.uuid } },
2255
+ { uuid: genre, data: { parent: to } },
2245
2256
  {
2246
2257
  onSuccess: () => {
2247
2258
  onClose();
2248
- onParentChanged?.({ genre, from, to: newParent.criteria.uuid });
2259
+ onParentChanged?.({ genre, from, to });
2249
2260
  }
2250
2261
  }
2251
2262
  );
2252
2263
  };
2264
+ const handleSelect = (newParent) => handleMove(newParent.criteria.uuid);
2253
2265
  (0, import_react16.useEffect)(() => {
2254
2266
  const handleKeyDown = (event) => {
2255
2267
  if (event.key === "Escape") onClose();
@@ -2262,6 +2274,18 @@ function ChangeParentPopup({
2262
2274
  "Current parent: ",
2263
2275
  genrePlaylist.parent?.name ?? "none"
2264
2276
  ] }),
2277
+ genrePlaylist.parent && (confirmingRoot ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "mb-3 text-sm", children: [
2278
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "mb-2", children: [
2279
+ genrePlaylist.name,
2280
+ " and its descendants will no longer be under ",
2281
+ genrePlaylist.parent.name,
2282
+ "."
2283
+ ] }),
2284
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(PopupButtons, { alignment: "left", children: [
2285
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.Button, { disabled: isPending, onClick: () => handleMove(null), children: "Confirm" }),
2286
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.Button, { variant: "secondary", disabled: isPending, onClick: () => setConfirmingRoot(false), children: "Back" })
2287
+ ] })
2288
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.Button, { variant: "secondary", onClick: () => setConfirmingRoot(true), children: "Make root" }) })),
2265
2289
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2266
2290
  GenreSearch,
2267
2291
  {
@@ -2275,12 +2299,12 @@ function ChangeParentPopup({
2275
2299
  }
2276
2300
 
2277
2301
  // src/genre-tree/artist/useArtistPanel.tsx
2278
- var import_ui20 = require("@behindthemusictree/ui");
2302
+ var import_ui21 = require("@behindthemusictree/ui");
2279
2303
  var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
2280
2304
 
2281
2305
  // src/genre-tree/artist/ArtistActionBand.tsx
2282
2306
  var import_lucide_react14 = require("lucide-react");
2283
- var import_ui14 = require("@behindthemusictree/ui");
2307
+ var import_ui15 = require("@behindthemusictree/ui");
2284
2308
  var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
2285
2309
  var import_player7 = require("@behindthemusictree/app-kit/player");
2286
2310
  var import_jsx_runtime39 = require("react/jsx-runtime");
@@ -2299,9 +2323,9 @@ function ArtistActionBand({ artist, share }) {
2299
2323
  console.error("Failed to load artist tracks:", error);
2300
2324
  });
2301
2325
  };
2302
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_ui14.ActionBand, { children: [
2326
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui15.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_ui15.ActionBand, { children: [
2303
2327
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2304
- import_ui14.ActionButton,
2328
+ import_ui15.ActionButton,
2305
2329
  {
2306
2330
  icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2307
2331
  label: playing ? "Pause" : "Play",
@@ -2316,12 +2340,12 @@ function ArtistActionBand({ artist, share }) {
2316
2340
  }
2317
2341
 
2318
2342
  // src/genre-tree/artist/ArtistHeader.tsx
2319
- var import_ui16 = require("@behindthemusictree/ui");
2343
+ var import_ui17 = require("@behindthemusictree/ui");
2320
2344
 
2321
2345
  // src/genre-tree/artist/ArtistLinks.tsx
2322
2346
  var import_lucide_react15 = require("lucide-react");
2323
2347
  var import_si3 = require("react-icons/si");
2324
- var import_ui15 = require("@behindthemusictree/ui");
2348
+ var import_ui16 = require("@behindthemusictree/ui");
2325
2349
  var import_jsx_runtime40 = require("react/jsx-runtime");
2326
2350
  var LINK_KINDS = {
2327
2351
  official: { label: "Official website", Icon: import_lucide_react15.Globe },
@@ -2359,7 +2383,7 @@ function artistLinks(artist) {
2359
2383
  return links;
2360
2384
  }
2361
2385
  function ArtistLinks({ links }) {
2362
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2386
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
2363
2387
  }
2364
2388
 
2365
2389
  // src/genre-tree/artist/formatYears.ts
@@ -2408,7 +2432,7 @@ function ArtistHeader({
2408
2432
  const links = artistLinks(artist);
2409
2433
  if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistLinks, { links }) });
2410
2434
  if (fields.length === 0) return null;
2411
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.FieldList, { items: fields });
2435
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.FieldList, { items: fields });
2412
2436
  }
2413
2437
 
2414
2438
  // src/genre-tree/artist/ArtistPhoto.tsx
@@ -2418,11 +2442,11 @@ function ArtistPhoto({ image, name }) {
2418
2442
  }
2419
2443
 
2420
2444
  // src/genre-tree/artist/ArtistMembers.tsx
2421
- var import_ui17 = require("@behindthemusictree/ui");
2445
+ var import_ui18 = require("@behindthemusictree/ui");
2422
2446
  var import_jsx_runtime43 = require("react/jsx-runtime");
2423
2447
  function MembershipList({ title, memberships, renderArtistLink }) {
2424
2448
  if (memberships.length === 0) return null;
2425
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: memberships.map((membership, index) => {
2449
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: memberships.map((membership, index) => {
2426
2450
  const details = [
2427
2451
  formatYears(membership.beginYear, membership.endYear, membership.ended),
2428
2452
  ...membership.roles
@@ -2443,7 +2467,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
2443
2467
  function ArtistMembers({ title, memberships, renderArtistLink }) {
2444
2468
  if (memberships.length === 0) return null;
2445
2469
  const isCurrent = (membership) => !membership.ended && membership.endYear === null;
2446
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui17.PanelSection, { title, children: [
2470
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui18.PanelSection, { title, children: [
2447
2471
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
2448
2472
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2449
2473
  MembershipList,
@@ -2457,7 +2481,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
2457
2481
  }
2458
2482
 
2459
2483
  // src/genre-tree/artist/ArtistDiscography.tsx
2460
- var import_ui18 = require("@behindthemusictree/ui");
2484
+ var import_ui19 = require("@behindthemusictree/ui");
2461
2485
  var import_jsx_runtime44 = require("react/jsx-runtime");
2462
2486
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
2463
2487
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
@@ -2478,10 +2502,10 @@ function byYear(a, b) {
2478
2502
  function ArtistDiscography({ albums }) {
2479
2503
  if (albums.length === 0) return null;
2480
2504
  const sorted = [...albums].sort(byYear);
2481
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui18.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2505
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
2482
2506
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
2483
2507
  if (groupAlbums.length === 0) return null;
2484
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui18.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2508
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
2485
2509
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
2486
2510
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
2487
2511
  ] }, album.uuid)) }) }, group);
@@ -2491,7 +2515,7 @@ function ArtistDiscography({ albums }) {
2491
2515
  // src/genre-tree/artist/ArtistSongs.tsx
2492
2516
  var import_react17 = require("react");
2493
2517
  var import_react_query2 = require("@tanstack/react-query");
2494
- var import_ui19 = require("@behindthemusictree/ui");
2518
+ var import_ui20 = require("@behindthemusictree/ui");
2495
2519
  var import_player11 = require("@behindthemusictree/app-kit/player");
2496
2520
  var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
2497
2521
  var import_transport8 = require("@behindthemusictree/app-kit/transport");
@@ -2654,7 +2678,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2654
2678
  event.stopPropagation();
2655
2679
  void play(song.uuid);
2656
2680
  };
2657
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui19.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui19.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2681
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
2658
2682
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
2659
2683
  "button",
2660
2684
  {
@@ -2678,7 +2702,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2678
2702
  renderSongLink
2679
2703
  }
2680
2704
  ) }, song.uuid)) }),
2681
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui19.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2705
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui20.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2682
2706
  ] }) });
2683
2707
  }
2684
2708
 
@@ -2706,12 +2730,12 @@ function useArtistPanel({
2706
2730
  ...panel,
2707
2731
  title: "Artist",
2708
2732
  content: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2709
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2710
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-4 w-32" }),
2711
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
2712
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-4 w-24" }),
2713
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-16 mt-2.5" }),
2714
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-16" })
2733
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-3 w-16" }),
2734
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-4 w-32" }),
2735
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-3 w-16" }),
2736
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-4 w-24" }),
2737
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-16 mt-2.5" }),
2738
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui21.Skeleton, { className: "h-16" })
2715
2739
  ] })
2716
2740
  };
2717
2741
  case "not-found":
@@ -2762,13 +2786,13 @@ function useArtistPanel({
2762
2786
  }
2763
2787
 
2764
2788
  // src/genre-tree/song/useSongPanel.tsx
2765
- var import_ui22 = require("@behindthemusictree/ui");
2789
+ var import_ui23 = require("@behindthemusictree/ui");
2766
2790
  var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
2767
2791
 
2768
2792
  // src/genre-tree/song/SongPanelContent.tsx
2769
2793
  var import_react18 = require("react");
2770
2794
  var import_lucide_react16 = require("lucide-react");
2771
- var import_ui21 = require("@behindthemusictree/ui");
2795
+ var import_ui22 = require("@behindthemusictree/ui");
2772
2796
  var import_player12 = require("@behindthemusictree/app-kit/player");
2773
2797
  var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
2774
2798
  var import_jsx_runtime49 = require("react/jsx-runtime");
@@ -2779,7 +2803,7 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2779
2803
  const [open, setOpen] = (0, import_react18.useState)(false);
2780
2804
  const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
2781
2805
  let body = null;
2782
- if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.Skeleton, { className: "h-10", "aria-busy": "true" });
2806
+ if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.Skeleton, { className: "h-10", "aria-busy": "true" });
2783
2807
  else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
2784
2808
  else if (history.status === "success")
2785
2809
  body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("li", { className: "py-1.5 text-body", children: [
@@ -2797,8 +2821,8 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2797
2821
  dateFormat.format(new Date(entry.createdOn))
2798
2822
  ] })
2799
2823
  ] }, entry.uuid)) });
2800
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2801
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui21.PANEL_LABEL_CLASS}`, children: "History" }),
2824
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2825
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui22.PANEL_LABEL_CLASS}`, children: "History" }),
2802
2826
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "mt-1.5", children: body })
2803
2827
  ] }) });
2804
2828
  }
@@ -2856,9 +2880,9 @@ function SongPanelContent(props) {
2856
2880
  value: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex gap-3", children: links })
2857
2881
  });
2858
2882
  return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
2859
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_ui21.ActionBand, { children: [
2883
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_ui22.ActionBand, { children: [
2860
2884
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
2861
- import_ui21.ActionButton,
2885
+ import_ui22.ActionButton,
2862
2886
  {
2863
2887
  icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react16.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react16.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
2864
2888
  label: isPlaying ? "Pause" : "Play",
@@ -2870,8 +2894,8 @@ function SongPanelContent(props) {
2870
2894
  shareUrl && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
2871
2895
  ] }) }),
2872
2896
  song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
2873
- /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.FieldList, { items: fields }),
2874
- song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2897
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.FieldList, { items: fields }),
2898
+ song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui22.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2875
2899
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2876
2900
  ] });
2877
2901
  }
@@ -2899,10 +2923,10 @@ function useSongPanel({
2899
2923
  ...panel,
2900
2924
  title: "Song",
2901
2925
  content: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2902
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2903
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-4 w-32" }),
2904
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
2905
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-4 w-24" })
2926
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui23.Skeleton, { className: "h-3 w-16" }),
2927
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui23.Skeleton, { className: "h-4 w-32" }),
2928
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui23.Skeleton, { className: "h-3 w-16" }),
2929
+ /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui23.Skeleton, { className: "h-4 w-24" })
2906
2930
  ] })
2907
2931
  };
2908
2932
  case "not-found":
@@ -2977,14 +3001,14 @@ function useEntityPanel({
2977
3001
 
2978
3002
  // src/genre-tree/SizeMetricToggle.tsx
2979
3003
  var import_md2 = require("react-icons/md");
2980
- var import_ui23 = require("@behindthemusictree/ui");
3004
+ var import_ui24 = require("@behindthemusictree/ui");
2981
3005
  var import_jsx_runtime51 = require("react/jsx-runtime");
2982
3006
  function SizeMetricToggle({
2983
3007
  value,
2984
3008
  onChange
2985
3009
  }) {
2986
3010
  return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
2987
- import_ui23.SegmentedControl,
3011
+ import_ui24.SegmentedControl,
2988
3012
  {
2989
3013
  variant: "floating",
2990
3014
  "aria-label": "Size nodes by",
@@ -3227,7 +3251,7 @@ function GenreTreeView({
3227
3251
  []
3228
3252
  );
3229
3253
  const panelRef = (0, import_react19.useRef)(null);
3230
- const obscuredInset = (0, import_ui24.useObscuredInset)(panelRef);
3254
+ const obscuredInset = (0, import_ui25.useObscuredInset)(panelRef);
3231
3255
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
3232
3256
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
3233
3257
  const treeNodes = (0, import_react19.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
@@ -3356,7 +3380,7 @@ function GenreTreeView({
3356
3380
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
3357
3381
  } : void 0;
3358
3382
  const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3359
- import_ui24.IconButton,
3383
+ import_ui25.IconButton,
3360
3384
  {
3361
3385
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
3362
3386
  style: activePanel.headerAccent && { color: "inherit" },
@@ -3428,7 +3452,7 @@ function GenreTreeView({
3428
3452
  !isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
3429
3453
  /* @__PURE__ */ (0, import_jsx_runtime52.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: [
3430
3454
  !isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3431
- import_ui24.SegmentedControl,
3455
+ import_ui25.SegmentedControl,
3432
3456
  {
3433
3457
  "aria-label": "Tree view mode",
3434
3458
  value: viewMode,
@@ -3448,7 +3472,7 @@ function GenreTreeView({
3448
3472
  role: "group",
3449
3473
  "aria-label": "Tree display options",
3450
3474
  children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3451
- import_ui24.Button,
3475
+ import_ui25.Button,
3452
3476
  {
3453
3477
  variant: allowWheelRotation ? "default" : "outline",
3454
3478
  size: "sm",
@@ -3459,7 +3483,7 @@ function GenreTreeView({
3459
3483
  }
3460
3484
  ),
3461
3485
  !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3462
- import_ui24.IconTextButton,
3486
+ import_ui25.IconTextButton,
3463
3487
  {
3464
3488
  icon: import_lucide_react17.Plus,
3465
3489
  text: "Add root",
@@ -3607,7 +3631,7 @@ function GenreTreeView({
3607
3631
  }
3608
3632
  ) }),
3609
3633
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
3610
- import_ui24.DetailPanel,
3634
+ import_ui25.DetailPanel,
3611
3635
  {
3612
3636
  ref: panelRef,
3613
3637
  side: "left",
@@ -3807,10 +3831,11 @@ function TrackListSidebar({
3807
3831
 
3808
3832
  // src/genre-tree/PlayerPanel.tsx
3809
3833
  var import_react21 = require("react");
3834
+ var import_lucide_react21 = require("lucide-react");
3810
3835
  var import_tailwind_merge = require("tailwind-merge");
3811
3836
  var import_player14 = require("@behindthemusictree/app-kit/player");
3812
3837
  var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
3813
- var import_ui25 = require("@behindthemusictree/ui");
3838
+ var import_ui26 = require("@behindthemusictree/ui");
3814
3839
 
3815
3840
  // src/genre-tree/PlayerControls.tsx
3816
3841
  var import_lucide_react20 = require("lucide-react");
@@ -3935,7 +3960,7 @@ function PlayerPanel({
3935
3960
  renderArtistLink,
3936
3961
  renderSongLink
3937
3962
  }) {
3938
- const { playerTrackObject, retryPlayback } = (0, import_player14.usePlayer)();
3963
+ const { playerTrackObject, retryPlayback, stop } = (0, import_player14.usePlayer)();
3939
3964
  const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree35.useTrackListSidebarVisibility)();
3940
3965
  const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react21.useState)("");
3941
3966
  const loadedTrackTitle = playerTrackObject?.track.title;
@@ -3977,7 +4002,7 @@ function PlayerPanel({
3977
4002
  style: isExtended ? { width: extendedWidth } : void 0,
3978
4003
  children: [
3979
4004
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
3980
- import_ui25.PanelEdgeTab,
4005
+ import_ui26.PanelEdgeTab,
3981
4006
  {
3982
4007
  side: "right",
3983
4008
  edge: tabEdge,
@@ -3995,7 +4020,10 @@ function PlayerPanel({
3995
4020
  ),
3996
4021
  children: [
3997
4022
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3998
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
4023
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: [
4024
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }),
4025
+ playerTrackObject && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_ui26.IconButton, { "aria-label": "Close player", onClick: stop, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_lucide_react21.X, { "aria-hidden": "true", className: "h-4 w-4" }) })
4026
+ ] }),
3999
4027
  /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "relative w-full shrink-0", children: [
4000
4028
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
4001
4029
  playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
@@ -4070,7 +4098,7 @@ function Rating({ rating, handleChange }) {
4070
4098
 
4071
4099
  // src/genre-tree/EntityDetailPanel.tsx
4072
4100
  var import_react22 = require("react");
4073
- var import_ui26 = require("@behindthemusictree/ui");
4101
+ var import_ui27 = require("@behindthemusictree/ui");
4074
4102
  var import_jsx_runtime57 = require("react/jsx-runtime");
4075
4103
  function EntityDetailPanel({
4076
4104
  scope,
@@ -4114,7 +4142,7 @@ function EntityDetailPanel({
4114
4142
  });
4115
4143
  if (!panel) return null;
4116
4144
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
4117
- import_ui26.DetailPanel,
4145
+ import_ui27.DetailPanel,
4118
4146
  {
4119
4147
  side: "left",
4120
4148
  title: panel.title,