@behindthemusictree/app-kit-ui 37.0.0 → 38.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.
@@ -2,7 +2,7 @@ import {
2
2
  BasePopup_default,
3
3
  InternalErrorPopup,
4
4
  Toast
5
- } from "./chunk-DKQQLLMY.js";
5
+ } from "./chunk-2GH7CSCE.js";
6
6
 
7
7
  // src/genre-tree/GenreTreeView.tsx
8
8
  import { useCallback as useCallback6, useDeferredValue as useDeferredValue2, useEffect as useEffect5, useMemo as useMemo8, useRef as useRef3, useState as useState9 } from "react";
@@ -545,7 +545,7 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
545
545
  artist.disambiguation
546
546
  ] })
547
547
  ] }),
548
- artist.matchedAlias && /* @__PURE__ */ jsxs2("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
548
+ artist.matchedAlias && /* @__PURE__ */ jsxs2("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [
549
549
  "also known as ",
550
550
  artist.matchedAlias
551
551
  ] })
@@ -573,7 +573,7 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
573
573
  key: song.uuid,
574
574
  content: /* @__PURE__ */ jsxs3("span", { className: isSongSearchResultPlayable(song) ? void 0 : "opacity-60", children: [
575
575
  /* @__PURE__ */ jsx6("span", { className: "block truncate font-medium", children: song.title }),
576
- /* @__PURE__ */ jsx6("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 ") })
576
+ /* @__PURE__ */ jsx6("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
577
577
  ] }),
578
578
  onSelect: () => onSelect(song)
579
579
  })),
@@ -598,9 +598,9 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
598
598
  content: /* @__PURE__ */ jsxs4(Fragment2, { children: [
599
599
  /* @__PURE__ */ jsxs4("span", { className: "block truncate font-medium", children: [
600
600
  genre.name,
601
- genre.isUmbrella && /* @__PURE__ */ jsx7("span", { className: "ml-2 text-xs font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
601
+ genre.isUmbrella && /* @__PURE__ */ jsx7("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
602
602
  ] }),
603
- /* @__PURE__ */ jsx7("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
603
+ /* @__PURE__ */ jsx7("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
604
604
  ] }),
605
605
  onSelect: () => onSelect(genre)
606
606
  })),
@@ -641,7 +641,7 @@ function GenreSearch({
641
641
  key: genrePlaylist.uuid,
642
642
  content: /* @__PURE__ */ jsxs5(Fragment3, { children: [
643
643
  genrePlaylist.name,
644
- genrePlaylist.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-xs italic text-[var(--color-text-muted)]", children: "umbrella" })
644
+ genrePlaylist.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
645
645
  ] }),
646
646
  onSelect: () => select(genrePlaylist)
647
647
  }))
@@ -760,7 +760,7 @@ import { SiGmail } from "react-icons/si";
760
760
 
761
761
  // src/genre-tree/ShareDestinationButton.tsx
762
762
  import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
763
- var className = "group flex min-w-0 flex-1 flex-col items-center justify-center gap-1.5 rounded-xl px-2 py-2 text-xs text-[var(--color-text)] transition-colors hover:bg-[var(--color-surface-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
763
+ var className = "group flex min-w-0 flex-1 flex-col items-center justify-center gap-1.5 rounded-xl px-2 py-2 text-caption text-[var(--color-text)] transition-colors hover:bg-[var(--color-surface-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
764
764
  function Contents({ icon, label }) {
765
765
  return /* @__PURE__ */ jsxs6(Fragment4, { children: [
766
766
  /* @__PURE__ */ jsx10("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: icon }),
@@ -790,9 +790,9 @@ function SharePreviewCard({ target, url }) {
790
790
  return /* @__PURE__ */ jsx11("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
791
791
  /* @__PURE__ */ jsx11("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ jsx11(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
792
792
  /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
793
- /* @__PURE__ */ jsx11("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
793
+ /* @__PURE__ */ jsx11("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
794
794
  /* @__PURE__ */ jsx11("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
795
- /* @__PURE__ */ jsx11("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
795
+ /* @__PURE__ */ jsx11("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
796
796
  ] })
797
797
  ] }) });
798
798
  }
@@ -933,8 +933,8 @@ function SharePopup({ target, url, onClose }) {
933
933
  }
934
934
  )
935
935
  ] }),
936
- copyStatus === "failed" && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
937
- shareError && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
936
+ copyStatus === "failed" && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
937
+ shareError && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
938
938
  ] })
939
939
  }
940
940
  ),
@@ -954,7 +954,7 @@ function ShareActionButton({ target, url }) {
954
954
  {
955
955
  type: "button",
956
956
  onClick: openSharePopup,
957
- className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
957
+ className: "group flex flex-col items-center gap-1 w-16 text-caption text-center text-[var(--color-text)] focus-visible:outline-none",
958
958
  "aria-label": `Share ${target.name}`,
959
959
  title: `Share ${target.name}`,
960
960
  children: [
@@ -1036,7 +1036,7 @@ function GenreActionBand({
1036
1036
  disabled: item.disabled,
1037
1037
  title: item.title,
1038
1038
  onClick: item.onClick,
1039
- className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none disabled:opacity-50",
1039
+ className: "group flex flex-col items-center gap-1 w-16 text-caption text-center text-[var(--color-text)] focus-visible:outline-none disabled:opacity-50",
1040
1040
  children: [
1041
1041
  /* @__PURE__ */ jsx14("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)] group-disabled:group-hover:bg-[var(--color-surface-muted)]", children: item.icon }),
1042
1042
  item.label
@@ -1055,7 +1055,7 @@ function GenreActionBand({
1055
1055
  "aria-label": "Admin actions",
1056
1056
  className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
1057
1057
  children: [
1058
- /* @__PURE__ */ jsx14("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
1058
+ /* @__PURE__ */ jsx14("h3", { className: "mb-2 text-caption font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
1059
1059
  /* @__PURE__ */ jsx14("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
1060
1060
  ]
1061
1061
  }
@@ -1087,7 +1087,7 @@ function ClampedText({ children, className: className2 = "" }) {
1087
1087
  type: "button",
1088
1088
  "aria-expanded": expanded,
1089
1089
  onClick: () => setExpanded((value) => !value),
1090
- className: "mt-1 text-xs text-[var(--color-text-muted)] underline hover:text-[var(--color-text)]",
1090
+ className: "mt-1 text-caption text-[var(--color-text-muted)] underline hover:text-[var(--color-text)]",
1091
1091
  children: expanded ? "Show less" : "Show more"
1092
1092
  }
1093
1093
  )
@@ -1238,7 +1238,7 @@ function GenrePlaylistTracks({
1238
1238
  ".",
1239
1239
  " ",
1240
1240
  /* @__PURE__ */ jsx17("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1241
- ] }) : tracks.length === 0 ? /* @__PURE__ */ jsx17("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1241
+ ] }) : tracks.length === 0 ? /* @__PURE__ */ jsx17("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1242
1242
  tracks.map((track, index) => {
1243
1243
  const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
1244
1244
  const isCurrent = playerTrackObject?.track.id === track.uuid;
@@ -1336,7 +1336,7 @@ function GenreDetailExtras({
1336
1336
  const { summary, summarySourceUrl, essentialSongs } = overview;
1337
1337
  const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
1338
1338
  return /* @__PURE__ */ jsxs13(Fragment8, { children: [
1339
- (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx18(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
1339
+ (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx18(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
1340
1340
  summarySourceUrl && /* @__PURE__ */ jsxs13(
1341
1341
  "a",
1342
1342
  {
@@ -1364,10 +1364,10 @@ function GenreDetailExtras({
1364
1364
  }
1365
1365
  )
1366
1366
  ] }) }),
1367
- /* @__PURE__ */ jsx18(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx18(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1367
+ /* @__PURE__ */ jsx18(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx18(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1368
1368
  family,
1369
1369
  renderGenreDetailExtras?.(overview),
1370
- (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx18(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx18("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx18("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx18("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
1370
+ (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx18(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx18("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx18("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx18("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
1371
1371
  /* @__PURE__ */ jsx18(
1372
1372
  GenrePlaylistTracks,
1373
1373
  {
@@ -1433,7 +1433,7 @@ function GenreInfoPanelBody({
1433
1433
  return /* @__PURE__ */ jsxs14(Fragment9, { children: [
1434
1434
  toolbar,
1435
1435
  genrePlaylist ? /* @__PURE__ */ jsx19(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ jsx19(GenreActionBand, { ...band }),
1436
- summary && catalogue && /* @__PURE__ */ jsxs14("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
1436
+ summary && catalogue && /* @__PURE__ */ jsxs14("div", { className: "px-3.5 py-3 text-body text-[var(--color-text)]", children: [
1437
1437
  /* @__PURE__ */ jsxs14("p", { children: [
1438
1438
  "Catalogue ",
1439
1439
  noun.plural,
@@ -1523,7 +1523,7 @@ function useGenreDetailContent({
1523
1523
  if (isError) return {
1524
1524
  name: null,
1525
1525
  title: "Genre unavailable",
1526
- content: /* @__PURE__ */ jsx20("p", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." })
1526
+ content: /* @__PURE__ */ jsx20("p", { role: "alert", className: "p-4 text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
1527
1527
  };
1528
1528
  if (isPending || !detail || !view) return {
1529
1529
  name: null,
@@ -1662,7 +1662,7 @@ function ChangeParentPopup({
1662
1662
  return () => window.removeEventListener("keydown", handleKeyDown);
1663
1663
  }, [onClose]);
1664
1664
  return /* @__PURE__ */ jsxs16(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
1665
- /* @__PURE__ */ jsxs16("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
1665
+ /* @__PURE__ */ jsxs16("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
1666
1666
  "Current parent: ",
1667
1667
  genrePlaylist.parent?.name ?? "none"
1668
1668
  ] }),
@@ -1674,7 +1674,7 @@ function ChangeParentPopup({
1674
1674
  placeholder: "Search a new parent\u2026"
1675
1675
  }
1676
1676
  ) }),
1677
- formErrors.length > 0 && /* @__PURE__ */ jsx21("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
1677
+ formErrors.length > 0 && /* @__PURE__ */ jsx21("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
1678
1678
  ] });
1679
1679
  }
1680
1680
 
@@ -1769,7 +1769,7 @@ function ExternalLink({ href, children }) {
1769
1769
  function ArtistPhoto({ image, name }) {
1770
1770
  return /* @__PURE__ */ jsxs17("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
1771
1771
  /* @__PURE__ */ jsx24("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
1772
- /* @__PURE__ */ jsxs17("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
1772
+ /* @__PURE__ */ jsxs17("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
1773
1773
  "Photo: ",
1774
1774
  /* @__PURE__ */ jsx24(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
1775
1775
  ",",
@@ -1973,7 +1973,7 @@ function TrackItem({
1973
1973
  /* @__PURE__ */ jsx28("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx28("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx28(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
1974
1974
  track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx28("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx28(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
1975
1975
  ] }),
1976
- /* @__PURE__ */ jsx28("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx28("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
1976
+ /* @__PURE__ */ jsx28("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx28("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
1977
1977
  renderDuration && /* @__PURE__ */ jsx28(
1978
1978
  "div",
1979
1979
  {
@@ -2029,14 +2029,14 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2029
2029
  {
2030
2030
  type: "button",
2031
2031
  onClick: () => void play(),
2032
- className: "flex w-fit items-center gap-1.5 px-3 py-1 text-xs font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
2032
+ className: "flex w-fit items-center gap-1.5 px-3 py-1 text-caption font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
2033
2033
  children: [
2034
2034
  /* @__PURE__ */ jsx29(Play2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2035
2035
  "Play all"
2036
2036
  ]
2037
2037
  }
2038
2038
  ),
2039
- /* @__PURE__ */ jsx29("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ jsx29(
2039
+ /* @__PURE__ */ jsx29("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ jsx29(
2040
2040
  "button",
2041
2041
  {
2042
2042
  type: "button",
@@ -2165,7 +2165,7 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2165
2165
  if (history.status === "loading") body = /* @__PURE__ */ jsx31(Skeleton6, { className: "h-10", "aria-busy": "true" });
2166
2166
  else if (history.status === "error") body = /* @__PURE__ */ jsx31("p", { className: "text-red-600", children: "Could not load the history." });
2167
2167
  else if (history.status === "success")
2168
- body = history.entries.length === 0 ? /* @__PURE__ */ jsx31("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx31("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs24("li", { className: "py-1.5 text-sm", children: [
2168
+ body = history.entries.length === 0 ? /* @__PURE__ */ jsx31("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx31("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs24("li", { className: "py-1.5 text-body", children: [
2169
2169
  /* @__PURE__ */ jsx31("span", { className: "font-medium", children: entry.action }),
2170
2170
  entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs24("span", { className: "text-[var(--color-text-muted)]", children: [
2171
2171
  " ",
@@ -2174,7 +2174,7 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2174
2174
  " \u2192 ",
2175
2175
  entry.newValue ?? "none"
2176
2176
  ] }) : null,
2177
- /* @__PURE__ */ jsxs24("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
2177
+ /* @__PURE__ */ jsxs24("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
2178
2178
  entry.actorPseudo ?? "Anonymous",
2179
2179
  " \xB7 ",
2180
2180
  dateFormat.format(new Date(entry.createdOn))
@@ -2192,15 +2192,8 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2192
2192
  }
2193
2193
  );
2194
2194
  }
2195
- function SongPanelContent({
2196
- song,
2197
- scope,
2198
- getBackendBaseUrl,
2199
- shareUrl,
2200
- renderArtistLink,
2201
- genrePath,
2202
- onSelectGenre
2203
- }) {
2195
+ function SongPanelContent(props) {
2196
+ const { song, scope, getBackendBaseUrl, shareUrl, renderArtistLink } = props;
2204
2197
  const { playNewTrackListFromTrackUuid } = useTrackList5();
2205
2198
  const { playerTrackObject, playState, handlePlayPauseAction } = usePlayer9();
2206
2199
  const unplayableReason = songUnplayableReason4(song);
@@ -2210,12 +2203,14 @@ function SongPanelContent({
2210
2203
  const musicBrainzUrl = musicBrainzRecordingUrl2(song);
2211
2204
  const fields = [];
2212
2205
  if (song.genre) {
2213
- const path = genrePath.length > 0 ? genrePath : null;
2206
+ const path = props.genrePath && props.genrePath.length > 0 ? props.genrePath : null;
2207
+ const { onSelectGenre } = props;
2208
+ const genre = song.genre;
2214
2209
  fields.push({
2215
2210
  label: "Genre",
2216
- value: path ? /* @__PURE__ */ jsx31("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs24("span", { children: [
2211
+ value: onSelectGenre ? /* @__PURE__ */ jsx31("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ jsx31("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs24("span", { children: [
2217
2212
  index > 0 && /* @__PURE__ */ jsx31("span", { "aria-hidden": "true", children: "\u203A " }),
2218
- /* @__PURE__ */ jsx31("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2213
+ /* @__PURE__ */ jsx31("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2219
2214
  ] }, genrePlaylist.uuid)) }) : song.genre.name
2220
2215
  });
2221
2216
  } else {
@@ -2252,12 +2247,12 @@ function SongPanelContent({
2252
2247
  });
2253
2248
  return /* @__PURE__ */ jsxs24(Fragment13, { children: [
2254
2249
  /* @__PURE__ */ jsx31("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxs24("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
2255
- unplayableReason ? /* @__PURE__ */ jsx31("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs24(
2250
+ unplayableReason ? /* @__PURE__ */ jsx31("p", { className: "flex items-center px-2.5 text-body text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs24(
2256
2251
  "button",
2257
2252
  {
2258
2253
  type: "button",
2259
2254
  onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
2260
- className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
2255
+ className: "group flex flex-col items-center gap-1 w-16 text-caption text-center text-[var(--color-text)] focus-visible:outline-none",
2261
2256
  children: [
2262
2257
  /* @__PURE__ */ jsx31("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ jsx31(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx31(Play3, { "aria-hidden": "true", className: "w-5 h-5" }) }),
2263
2258
  isPlaying ? "Pause" : "Play"
@@ -2281,11 +2276,10 @@ function useSongPanel({
2281
2276
  getBackendBaseUrl,
2282
2277
  getShareUrl,
2283
2278
  renderArtistLink,
2284
- getGenrePath,
2285
- onSelectGenre,
2286
2279
  expanded,
2287
2280
  onExpandedChange,
2288
- onClose
2281
+ onClose,
2282
+ ...navigation
2289
2283
  }) {
2290
2284
  const result = useSong(uuid, getBackendBaseUrl);
2291
2285
  const panel = { expanded, onExpandedChange, onClose };
@@ -2329,8 +2323,10 @@ function useSongPanel({
2329
2323
  getBackendBaseUrl,
2330
2324
  shareUrl: shareUrl ?? void 0,
2331
2325
  renderArtistLink,
2332
- genrePath: song.genre ? getGenrePath(song.genre) : [],
2333
- onSelectGenre
2326
+ ...navigation.getGenrePath ? {
2327
+ genrePath: song.genre ? navigation.getGenrePath(song.genre) : [],
2328
+ onSelectGenrePlaylist: navigation.onSelectGenrePlaylist
2329
+ } : { onSelectGenre: navigation.onSelectGenre }
2334
2330
  }
2335
2331
  )
2336
2332
  };
@@ -2338,6 +2334,37 @@ function useSongPanel({
2338
2334
  }
2339
2335
  }
2340
2336
 
2337
+ // src/genre-tree/useEntityPanel.tsx
2338
+ function useEntityPanel({
2339
+ scope,
2340
+ getBackendBaseUrl,
2341
+ getArtistBackendBaseUrl,
2342
+ getShareUrl,
2343
+ renderArtistLink,
2344
+ renderSongLink,
2345
+ song,
2346
+ artist,
2347
+ ...navigation
2348
+ }) {
2349
+ const songPanel = useSongPanel({
2350
+ ...song,
2351
+ scope,
2352
+ getBackendBaseUrl,
2353
+ getShareUrl,
2354
+ renderArtistLink,
2355
+ ...navigation
2356
+ });
2357
+ const artistPanel = useArtistPanel({
2358
+ ...artist,
2359
+ scope,
2360
+ getBackendBaseUrl: getArtistBackendBaseUrl ?? getBackendBaseUrl,
2361
+ getShareUrl,
2362
+ renderArtistLink,
2363
+ renderSongLink
2364
+ });
2365
+ return songPanel ?? artistPanel;
2366
+ }
2367
+
2341
2368
  // src/genre-tree/SizeMetricToggle.tsx
2342
2369
  import { MdMusicNote, MdVisibility } from "react-icons/md";
2343
2370
  import { SegmentedControl } from "@behindthemusictree/ui";
@@ -2520,20 +2547,6 @@ function GenreTreeView({
2520
2547
  const [expandedArtistUuid, setExpandedArtistUuid] = useState9(null);
2521
2548
  const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
2522
2549
  const songBaseUrl = getBackendBaseUrl;
2523
- const artistPanel = useArtistPanel({
2524
- uuid: artistUuid,
2525
- scope,
2526
- getBackendBaseUrl: artistBaseUrl,
2527
- getShareUrl: getShareUrlRef.current,
2528
- renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx34("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
2529
- expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
2530
- onExpandedChange: (on) => {
2531
- if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
2532
- onArtistExpandedChange?.(on);
2533
- },
2534
- renderSongLink: resolvedRenderSongLink,
2535
- onClose: () => changeArtist(null)
2536
- });
2537
2550
  const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx34("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
2538
2551
  const getGenrePath = (genre) => {
2539
2552
  const path = [];
@@ -2545,27 +2558,38 @@ function GenreTreeView({
2545
2558
  }
2546
2559
  return path;
2547
2560
  };
2548
- const songPanel = useSongPanel({
2549
- uuid: songUuid,
2561
+ const entityPanel = useEntityPanel({
2550
2562
  scope,
2551
2563
  getBackendBaseUrl: songBaseUrl,
2564
+ getArtistBackendBaseUrl: artistBaseUrl,
2552
2565
  getShareUrl: getShareUrlRef.current,
2553
2566
  renderArtistLink: resolvedRenderArtistLink,
2567
+ renderSongLink: resolvedRenderSongLink,
2568
+ song: {
2569
+ uuid: songUuid,
2570
+ expanded: controlledSongExpanded ?? (songUuid !== null && expandedSongUuid === songUuid),
2571
+ onExpandedChange: (on) => {
2572
+ if (controlledSongExpanded === void 0) setExpandedSongUuid(on ? songUuid : null);
2573
+ onSongExpandedChange?.(on);
2574
+ },
2575
+ onClose: () => changeSong(null)
2576
+ },
2577
+ artist: {
2578
+ uuid: artistUuid,
2579
+ expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
2580
+ onExpandedChange: (on) => {
2581
+ if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
2582
+ onArtistExpandedChange?.(on);
2583
+ },
2584
+ onClose: () => changeArtist(null)
2585
+ },
2554
2586
  getGenrePath,
2555
- onSelectGenre: (genrePlaylist2) => {
2587
+ onSelectGenrePlaylist: (genrePlaylist2) => {
2556
2588
  changeSong(null);
2557
2589
  selectGenrePlaylist(genrePlaylist2);
2558
- },
2559
- expanded: controlledSongExpanded ?? (songUuid !== null && expandedSongUuid === songUuid),
2560
- onExpandedChange: (on) => {
2561
- if (controlledSongExpanded === void 0) setExpandedSongUuid(on ? songUuid : null);
2562
- onSongExpandedChange?.(on);
2563
- },
2564
- onClose: () => changeSong(null)
2590
+ }
2565
2591
  });
2566
- const builtInSongPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : songPanel;
2567
- const builtInArtistPanel = overlayPanel || selectedGenreUuid !== null || builtInSongPanel ? void 0 : artistPanel;
2568
- const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
2592
+ const builtInPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : entityPanel;
2569
2593
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState9(false);
2570
2594
  const [uncontrolledExpanded, setUncontrolledExpanded] = useState9(false);
2571
2595
  const isCollapsed = collapsed ?? uncontrolledCollapsed;
@@ -2736,7 +2760,7 @@ function GenreTreeView({
2736
2760
  ) : null;
2737
2761
  const renderArtistFilter = () => {
2738
2762
  if (isLoading || !filterArtist) return null;
2739
- return /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
2763
+ return /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
2740
2764
  /* @__PURE__ */ jsx34(
2741
2765
  "button",
2742
2766
  {
@@ -2847,14 +2871,14 @@ function GenreTreeView({
2847
2871
  /* @__PURE__ */ jsx34(
2848
2872
  "div",
2849
2873
  {
2850
- className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
2874
+ className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] md:left-[var(--layout-panel-inset)] md:top-[var(--layout-panel-inset)] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
2851
2875
  children: actions
2852
2876
  }
2853
2877
  ),
2854
2878
  !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx34(
2855
2879
  "div",
2856
2880
  {
2857
- className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))]" : "top-5"}`,
2881
+ className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] md:top-[var(--layout-panel-inset)]" : "top-5"}`,
2858
2882
  children: renderArtistFilter()
2859
2883
  }
2860
2884
  ),
@@ -3129,7 +3153,7 @@ function TrackListSidebar({
3129
3153
  /* @__PURE__ */ jsx35("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3130
3154
  /* @__PURE__ */ jsx35("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__ */ jsx35("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3131
3155
  ] }),
3132
- /* @__PURE__ */ jsxs27("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
3156
+ /* @__PURE__ */ jsxs27("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3133
3157
  trackList.origin.type === TrackListOriginType2.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType2.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
3134
3158
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3135
3159
  ] })
@@ -3445,6 +3469,69 @@ function Rating({ rating, handleChange }) {
3445
3469
  ] }, i)) });
3446
3470
  }
3447
3471
 
3472
+ // src/genre-tree/EntityDetailPanel.tsx
3473
+ import { useState as useState11 } from "react";
3474
+ import { DetailPanel as DetailPanel3 } from "@behindthemusictree/ui";
3475
+ import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs31 } from "react/jsx-runtime";
3476
+ function EntityDetailPanel({
3477
+ scope,
3478
+ getBackendBaseUrl,
3479
+ getArtistBackendBaseUrl,
3480
+ getShareUrl,
3481
+ songUuid,
3482
+ onSongUuidChange,
3483
+ artistUuid,
3484
+ onArtistUuidChange,
3485
+ renderArtistLink,
3486
+ renderSongLink,
3487
+ collapsed,
3488
+ edgeTab,
3489
+ ...navigation
3490
+ }) {
3491
+ const [expandedSongUuid, setExpandedSongUuid] = useState11(null);
3492
+ const [expandedArtistUuid, setExpandedArtistUuid] = useState11(null);
3493
+ const panel = useEntityPanel({
3494
+ scope,
3495
+ getBackendBaseUrl,
3496
+ getArtistBackendBaseUrl,
3497
+ getShareUrl,
3498
+ renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx39("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
3499
+ renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ jsx39("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
3500
+ song: {
3501
+ uuid: songUuid,
3502
+ expanded: songUuid !== null && expandedSongUuid === songUuid,
3503
+ onExpandedChange: (on) => setExpandedSongUuid(on ? songUuid : null),
3504
+ onClose: () => onSongUuidChange(null)
3505
+ },
3506
+ artist: {
3507
+ uuid: artistUuid,
3508
+ expanded: artistUuid !== null && expandedArtistUuid === artistUuid,
3509
+ onExpandedChange: (on) => setExpandedArtistUuid(on ? artistUuid : null),
3510
+ onClose: () => onArtistUuidChange(null)
3511
+ },
3512
+ ...navigation
3513
+ });
3514
+ if (!panel) return null;
3515
+ return /* @__PURE__ */ jsx39(
3516
+ DetailPanel3,
3517
+ {
3518
+ side: "left",
3519
+ title: panel.title,
3520
+ titleInSearch: true,
3521
+ onClose: panel.onClose,
3522
+ collapsed,
3523
+ expanded: panel.expanded,
3524
+ onExpandedChange: panel.onExpandedChange,
3525
+ aside: panel.extraContent,
3526
+ edgeTab,
3527
+ children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ jsxs31(Fragment15, { children: [
3528
+ panel.content,
3529
+ panel.extraContent
3530
+ ] }) : panel.content
3531
+ }
3532
+ );
3533
+ }
3534
+
3448
3535
  export {
3449
3536
  useGenrePlaylistPlayback,
3450
3537
  GenrePlaylistTreePerRoot,
@@ -3460,13 +3547,15 @@ export {
3460
3547
  GenreDetailPanel,
3461
3548
  TrackPositionPlayPause,
3462
3549
  TrackItem,
3550
+ useEntityPanel,
3463
3551
  GenreTreeView,
3464
3552
  TrackListSidebar,
3465
3553
  PlayerPanel,
3466
3554
  Rating,
3555
+ EntityDetailPanel,
3467
3556
  GenreTreeSkeleton,
3468
3557
  GenreTreeWheelSkeleton2 as GenreTreeWheelSkeleton,
3469
3558
  GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
3470
3559
  GenreTreeOutlineSkeleton
3471
3560
  };
3472
- //# sourceMappingURL=chunk-FTYMRCJS.js.map
3561
+ //# sourceMappingURL=chunk-F7H5XPWU.js.map