@behindthemusictree/app-kit-ui 40.1.0 → 41.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.
- package/dist/{chunk-DPYUE6WD.js → chunk-GR4PCPCU.js} +260 -238
- package/dist/chunk-GR4PCPCU.js.map +1 -0
- package/dist/genre-tree/index.cjs +307 -287
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +7 -3
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +307 -287
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-DPYUE6WD.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -596,6 +596,7 @@ var import_lucide_react18 = require("lucide-react");
|
|
|
596
596
|
var import_ui24 = 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
|
+
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
599
600
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
600
601
|
|
|
601
602
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -1700,6 +1701,29 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1700
1701
|
] });
|
|
1701
1702
|
}
|
|
1702
1703
|
|
|
1704
|
+
// src/genre-tree/CommonsPhoto.tsx
|
|
1705
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1706
|
+
function ExternalLink({ href, children }) {
|
|
1707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1708
|
+
}
|
|
1709
|
+
function CommonsPhoto({
|
|
1710
|
+
image,
|
|
1711
|
+
alt,
|
|
1712
|
+
creditLabel = "Photo"
|
|
1713
|
+
}) {
|
|
1714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1716
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
1717
|
+
creditLabel,
|
|
1718
|
+
": ",
|
|
1719
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1720
|
+
",",
|
|
1721
|
+
" ",
|
|
1722
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1723
|
+
] })
|
|
1724
|
+
] });
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1703
1727
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1704
1728
|
var import_react13 = require("react");
|
|
1705
1729
|
var import_react_query = require("@tanstack/react-query");
|
|
@@ -1722,15 +1746,15 @@ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
1722
1746
|
|
|
1723
1747
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1724
1748
|
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1725
|
-
var
|
|
1749
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1726
1750
|
function MusicBrainzRecordingLink({
|
|
1727
1751
|
song,
|
|
1728
1752
|
children,
|
|
1729
1753
|
className: className2 = ""
|
|
1730
1754
|
}) {
|
|
1731
1755
|
const url = (0, import_genre_tree11.musicBrainzRecordingUrl)(song);
|
|
1732
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1733
|
-
return /* @__PURE__ */ (0,
|
|
1756
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_jsx_runtime33.Fragment, { children });
|
|
1757
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1734
1758
|
"a",
|
|
1735
1759
|
{
|
|
1736
1760
|
href: url,
|
|
@@ -1747,7 +1771,7 @@ function MusicBrainzRecordingLink({
|
|
|
1747
1771
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1748
1772
|
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1749
1773
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1750
|
-
var
|
|
1774
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1751
1775
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1752
1776
|
var SongPageSchema = (0, import_transport5.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
|
|
1753
1777
|
overallTotal: import_zod.z.number().nullable(),
|
|
@@ -1834,35 +1858,35 @@ function GenrePlaylistTracks({
|
|
|
1834
1858
|
started.catch((error) => {
|
|
1835
1859
|
console.error("Failed to play genre playlist:", error);
|
|
1836
1860
|
if (error instanceof import_genre_tree16.TracksFailureAlreadyReportedError) return;
|
|
1837
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1861
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport7.AppError ? error.code : import_transport7.ErrorCode.CLIENT_UNKNOWN }));
|
|
1838
1862
|
});
|
|
1839
1863
|
};
|
|
1840
|
-
return /* @__PURE__ */ (0,
|
|
1864
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { children: [
|
|
1841
1865
|
renderTracksToolbar?.(),
|
|
1842
|
-
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0,
|
|
1866
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { role: "alert", children: [
|
|
1843
1867
|
"Couldn't load ",
|
|
1844
1868
|
trackNoun.plural,
|
|
1845
1869
|
".",
|
|
1846
1870
|
" ",
|
|
1847
|
-
/* @__PURE__ */ (0,
|
|
1848
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1871
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1872
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
|
|
1849
1873
|
tracks.map((track, index) => {
|
|
1850
1874
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1851
1875
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1852
1876
|
const isPlaying = isCurrent && playState === import_player6.PlayStates.PLAYING;
|
|
1853
|
-
const text = /* @__PURE__ */ (0,
|
|
1854
|
-
/* @__PURE__ */ (0,
|
|
1855
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1856
|
-
/* @__PURE__ */ (0,
|
|
1857
|
-
track.album && /* @__PURE__ */ (0,
|
|
1877
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1878
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1879
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1880
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree17.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1881
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1858
1882
|
] });
|
|
1859
1883
|
const leading = renderSongLeading?.(track, index);
|
|
1860
1884
|
const actions = renderSongActions?.(track);
|
|
1861
1885
|
const isUnplayable = (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
|
|
1862
1886
|
if (isUnplayable) {
|
|
1863
|
-
return /* @__PURE__ */ (0,
|
|
1887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1864
1888
|
leading,
|
|
1865
|
-
/* @__PURE__ */ (0,
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
1866
1890
|
"button",
|
|
1867
1891
|
{
|
|
1868
1892
|
type: "button",
|
|
@@ -1870,27 +1894,27 @@ function GenrePlaylistTracks({
|
|
|
1870
1894
|
"aria-disabled": "true",
|
|
1871
1895
|
"aria-label": label,
|
|
1872
1896
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1873
|
-
children: /* @__PURE__ */ (0,
|
|
1897
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1874
1898
|
}
|
|
1875
1899
|
),
|
|
1876
1900
|
text,
|
|
1877
1901
|
actions
|
|
1878
1902
|
] }, track.uuid);
|
|
1879
1903
|
}
|
|
1880
|
-
return /* @__PURE__ */ (0,
|
|
1904
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
1881
1905
|
"li",
|
|
1882
1906
|
{
|
|
1883
1907
|
className: `group flex min-h-8 items-center gap-1 rounded px-1 hover:bg-[var(--color-neutral-100)] ${isCurrent ? "font-medium text-[var(--color-primary)]" : ""}`,
|
|
1884
1908
|
children: [
|
|
1885
1909
|
leading,
|
|
1886
|
-
/* @__PURE__ */ (0,
|
|
1910
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
1887
1911
|
"button",
|
|
1888
1912
|
{
|
|
1889
1913
|
type: "button",
|
|
1890
1914
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1891
1915
|
className: `flex h-6 w-6 shrink-0 items-center justify-center rounded-full cursor-pointer group-hover:opacity-100 focus-visible:opacity-100 ${isCurrent && playState !== import_player6.PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1892
1916
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1893
|
-
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1917
|
+
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_fa4.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1894
1918
|
}
|
|
1895
1919
|
),
|
|
1896
1920
|
text,
|
|
@@ -1900,7 +1924,7 @@ function GenrePlaylistTracks({
|
|
|
1900
1924
|
track.uuid
|
|
1901
1925
|
);
|
|
1902
1926
|
}),
|
|
1903
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1927
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1904
1928
|
] })
|
|
1905
1929
|
] }) });
|
|
1906
1930
|
}
|
|
@@ -1908,7 +1932,7 @@ function GenrePlaylistTracks({
|
|
|
1908
1932
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1909
1933
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
1910
1934
|
var import_si2 = require("react-icons/si");
|
|
1911
|
-
var
|
|
1935
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1912
1936
|
function GenreDetailExtras({
|
|
1913
1937
|
genreUuid,
|
|
1914
1938
|
genrePlaylist,
|
|
@@ -1934,17 +1958,18 @@ function GenreDetailExtras({
|
|
|
1934
1958
|
criteriaOverviewSchema
|
|
1935
1959
|
);
|
|
1936
1960
|
if (isPending) {
|
|
1937
|
-
return /* @__PURE__ */ (0,
|
|
1938
|
-
/* @__PURE__ */ (0,
|
|
1961
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
1962
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1939
1963
|
family
|
|
1940
1964
|
] });
|
|
1941
1965
|
}
|
|
1942
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1943
|
-
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1966
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_jsx_runtime35.Fragment, { children: family });
|
|
1967
|
+
const { summary, summarySourceUrl, essentialSongs, image } = overview;
|
|
1944
1968
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1945
|
-
return /* @__PURE__ */ (0,
|
|
1946
|
-
|
|
1947
|
-
|
|
1969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
1970
|
+
image && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(CommonsPhoto, { image, alt: overview.name, creditLabel: "Image" }),
|
|
1971
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
|
|
1972
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
1948
1973
|
"a",
|
|
1949
1974
|
{
|
|
1950
1975
|
href: summarySourceUrl,
|
|
@@ -1952,12 +1977,12 @@ function GenreDetailExtras({
|
|
|
1952
1977
|
rel: "noopener noreferrer",
|
|
1953
1978
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1954
1979
|
children: [
|
|
1955
|
-
/* @__PURE__ */ (0,
|
|
1980
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1956
1981
|
"Wikipedia"
|
|
1957
1982
|
]
|
|
1958
1983
|
}
|
|
1959
1984
|
),
|
|
1960
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1985
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
1961
1986
|
"a",
|
|
1962
1987
|
{
|
|
1963
1988
|
href: wikidataUrl,
|
|
@@ -1965,17 +1990,17 @@ function GenreDetailExtras({
|
|
|
1965
1990
|
rel: "noopener noreferrer",
|
|
1966
1991
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1967
1992
|
children: [
|
|
1968
|
-
/* @__PURE__ */ (0,
|
|
1993
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1969
1994
|
"Wikidata"
|
|
1970
1995
|
]
|
|
1971
1996
|
}
|
|
1972
1997
|
)
|
|
1973
1998
|
] }) }),
|
|
1974
|
-
/* @__PURE__ */ (0,
|
|
1999
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1975
2000
|
family,
|
|
1976
2001
|
renderGenreDetailExtras?.(overview),
|
|
1977
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1978
|
-
/* @__PURE__ */ (0,
|
|
2002
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1979
2004
|
GenrePlaylistTracks,
|
|
1980
2005
|
{
|
|
1981
2006
|
genreUuid,
|
|
@@ -1993,10 +2018,10 @@ function GenreDetailExtras({
|
|
|
1993
2018
|
}
|
|
1994
2019
|
|
|
1995
2020
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1996
|
-
var
|
|
2021
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1997
2022
|
var import_react14 = require("react");
|
|
1998
2023
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1999
|
-
return /* @__PURE__ */ (0,
|
|
2024
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
|
|
2000
2025
|
}
|
|
2001
2026
|
function GenreInfoPanelBody({
|
|
2002
2027
|
toolbar,
|
|
@@ -2013,9 +2038,9 @@ function GenreInfoPanelBody({
|
|
|
2013
2038
|
}) {
|
|
2014
2039
|
const summary = scope === "reference" ? import_genre_tree19.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
|
|
2015
2040
|
const selection = summary ? (0, import_genre_tree19.defaultGenreMusicSelection)(summary) : void 0;
|
|
2016
|
-
return /* @__PURE__ */ (0,
|
|
2041
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
2017
2042
|
toolbar,
|
|
2018
|
-
genrePlaylist ? /* @__PURE__ */ (0,
|
|
2043
|
+
genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(GenreActionBand, { ...band }),
|
|
2019
2044
|
/* @__PURE__ */ (0, import_react14.createElement)(
|
|
2020
2045
|
GenreDetailExtras,
|
|
2021
2046
|
{
|
|
@@ -2035,7 +2060,7 @@ function GenreInfoPanelBody({
|
|
|
2035
2060
|
}
|
|
2036
2061
|
|
|
2037
2062
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
2038
|
-
var
|
|
2063
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2039
2064
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2040
2065
|
function toNode(genre, parentId) {
|
|
2041
2066
|
return {
|
|
@@ -2060,7 +2085,8 @@ function useGenreDetailContent({
|
|
|
2060
2085
|
expanded,
|
|
2061
2086
|
treeStyles
|
|
2062
2087
|
}) {
|
|
2063
|
-
const
|
|
2088
|
+
const result = (0, import_genre_tree20.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
2089
|
+
const detail = result.status === "success" ? result.genre : void 0;
|
|
2064
2090
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
2065
2091
|
const playback = hostPlayback ?? defaultPlayback;
|
|
2066
2092
|
const view = (0, import_react15.useMemo)(() => {
|
|
@@ -2089,22 +2115,27 @@ function useGenreDetailContent({
|
|
|
2089
2115
|
)
|
|
2090
2116
|
};
|
|
2091
2117
|
}, [detail, scope, treeStyles]);
|
|
2092
|
-
if (
|
|
2118
|
+
if (result.status === "not-found") return {
|
|
2119
|
+
name: null,
|
|
2120
|
+
title: "Genre not found",
|
|
2121
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This genre does not exist." })
|
|
2122
|
+
};
|
|
2123
|
+
if (result.status === "error") return {
|
|
2093
2124
|
name: null,
|
|
2094
2125
|
title: "Genre unavailable",
|
|
2095
|
-
content: /* @__PURE__ */ (0,
|
|
2126
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
2096
2127
|
};
|
|
2097
|
-
if (
|
|
2128
|
+
if (!detail || !view) return {
|
|
2098
2129
|
name: null,
|
|
2099
2130
|
title: "Loading genre\u2026",
|
|
2100
|
-
content: /* @__PURE__ */ (0,
|
|
2101
|
-
/* @__PURE__ */ (0,
|
|
2102
|
-
/* @__PURE__ */ (0,
|
|
2103
|
-
/* @__PURE__ */ (0,
|
|
2131
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2132
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui13.Skeleton, { className: "h-6 w-2/3" }),
|
|
2133
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui13.Skeleton, { className: "h-4 w-full" }),
|
|
2134
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui13.Skeleton, { className: "h-4 w-3/4" })
|
|
2104
2135
|
] })
|
|
2105
2136
|
};
|
|
2106
2137
|
const selectGenre = (nodeId) => onSelectGenre(import_genre_tree20.GenreUuidSchema.parse(nodeId));
|
|
2107
|
-
const family = /* @__PURE__ */ (0,
|
|
2138
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2108
2139
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2109
2140
|
{
|
|
2110
2141
|
node: view.node,
|
|
@@ -2115,17 +2146,17 @@ function useGenreDetailContent({
|
|
|
2115
2146
|
onSelectNode: selectGenre
|
|
2116
2147
|
}
|
|
2117
2148
|
);
|
|
2118
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2149
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2119
2150
|
return {
|
|
2120
2151
|
name: detail.name,
|
|
2121
2152
|
title: detail.name,
|
|
2122
2153
|
headerAccent: { background: view.fill, color: view.textColor },
|
|
2123
2154
|
aside: expanded ? ancestors : void 0,
|
|
2124
|
-
content: /* @__PURE__ */ (0,
|
|
2125
|
-
/* @__PURE__ */ (0,
|
|
2155
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
2156
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2126
2157
|
GenreInfoPanelBody,
|
|
2127
2158
|
{
|
|
2128
|
-
family: /* @__PURE__ */ (0,
|
|
2159
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
2129
2160
|
family,
|
|
2130
2161
|
!expanded && ancestors
|
|
2131
2162
|
] }),
|
|
@@ -2169,7 +2200,7 @@ function GenreDetailPanel({
|
|
|
2169
2200
|
expanded,
|
|
2170
2201
|
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
2171
2202
|
});
|
|
2172
|
-
return /* @__PURE__ */ (0,
|
|
2203
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2173
2204
|
import_ui13.DetailPanel,
|
|
2174
2205
|
{
|
|
2175
2206
|
side: "left",
|
|
@@ -2182,7 +2213,7 @@ function GenreDetailPanel({
|
|
|
2182
2213
|
edgeTab,
|
|
2183
2214
|
aside: panel.aside,
|
|
2184
2215
|
titleInSearch,
|
|
2185
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
2216
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui13.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
2186
2217
|
children: panel.content
|
|
2187
2218
|
}
|
|
2188
2219
|
);
|
|
@@ -2191,7 +2222,7 @@ function GenreDetailPanel({
|
|
|
2191
2222
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
2192
2223
|
var import_react16 = require("react");
|
|
2193
2224
|
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2194
|
-
var
|
|
2225
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2195
2226
|
function ChangeParentPopup({
|
|
2196
2227
|
genrePlaylist,
|
|
2197
2228
|
genrePlaylists,
|
|
@@ -2226,12 +2257,12 @@ function ChangeParentPopup({
|
|
|
2226
2257
|
window.addEventListener("keydown", handleKeyDown);
|
|
2227
2258
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2228
2259
|
}, [onClose]);
|
|
2229
|
-
return /* @__PURE__ */ (0,
|
|
2230
|
-
/* @__PURE__ */ (0,
|
|
2260
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
2261
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
|
|
2231
2262
|
"Current parent: ",
|
|
2232
2263
|
genrePlaylist.parent?.name ?? "none"
|
|
2233
2264
|
] }),
|
|
2234
|
-
/* @__PURE__ */ (0,
|
|
2265
|
+
/* @__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)(
|
|
2235
2266
|
GenreSearch,
|
|
2236
2267
|
{
|
|
2237
2268
|
genrePlaylists: candidates,
|
|
@@ -2239,7 +2270,7 @@ function ChangeParentPopup({
|
|
|
2239
2270
|
placeholder: "Search a new parent\u2026"
|
|
2240
2271
|
}
|
|
2241
2272
|
) }),
|
|
2242
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
2273
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
2243
2274
|
] });
|
|
2244
2275
|
}
|
|
2245
2276
|
|
|
@@ -2252,7 +2283,7 @@ var import_lucide_react14 = require("lucide-react");
|
|
|
2252
2283
|
var import_ui14 = require("@behindthemusictree/ui");
|
|
2253
2284
|
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2254
2285
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
2255
|
-
var
|
|
2286
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2256
2287
|
function ArtistActionBand({ artist, share }) {
|
|
2257
2288
|
const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree22.useTrackList)();
|
|
2258
2289
|
const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
|
|
@@ -2268,11 +2299,11 @@ function ArtistActionBand({ artist, share }) {
|
|
|
2268
2299
|
console.error("Failed to load artist tracks:", error);
|
|
2269
2300
|
});
|
|
2270
2301
|
};
|
|
2271
|
-
return /* @__PURE__ */ (0,
|
|
2272
|
-
/* @__PURE__ */ (0,
|
|
2302
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_ui14.ActionBand, { children: [
|
|
2303
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2273
2304
|
import_ui14.ActionButton,
|
|
2274
2305
|
{
|
|
2275
|
-
icon: playing ? /* @__PURE__ */ (0,
|
|
2306
|
+
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" }),
|
|
2276
2307
|
label: playing ? "Pause" : "Play",
|
|
2277
2308
|
caption: String(artist.playableTracksCount),
|
|
2278
2309
|
disabled: artist.playableTracksCount === 0,
|
|
@@ -2291,7 +2322,7 @@ var import_ui16 = require("@behindthemusictree/ui");
|
|
|
2291
2322
|
var import_lucide_react15 = require("lucide-react");
|
|
2292
2323
|
var import_si3 = require("react-icons/si");
|
|
2293
2324
|
var import_ui15 = require("@behindthemusictree/ui");
|
|
2294
|
-
var
|
|
2325
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2295
2326
|
var LINK_KINDS = {
|
|
2296
2327
|
official: { label: "Official website", Icon: import_lucide_react15.Globe },
|
|
2297
2328
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -2328,7 +2359,7 @@ function artistLinks(artist) {
|
|
|
2328
2359
|
return links;
|
|
2329
2360
|
}
|
|
2330
2361
|
function ArtistLinks({ links }) {
|
|
2331
|
-
return /* @__PURE__ */ (0,
|
|
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)) });
|
|
2332
2363
|
}
|
|
2333
2364
|
|
|
2334
2365
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2339,7 +2370,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2339
2370
|
}
|
|
2340
2371
|
|
|
2341
2372
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2342
|
-
var
|
|
2373
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2343
2374
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2344
2375
|
function ArtistHeader({ artist }) {
|
|
2345
2376
|
const fields = [];
|
|
@@ -2349,35 +2380,23 @@ function ArtistHeader({ artist }) {
|
|
|
2349
2380
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2350
2381
|
if (years) fields.push({ label: "Active", value: years });
|
|
2351
2382
|
const links = artistLinks(artist);
|
|
2352
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2383
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistLinks, { links }) });
|
|
2353
2384
|
if (fields.length === 0) return null;
|
|
2354
|
-
return /* @__PURE__ */ (0,
|
|
2385
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.FieldList, { items: fields });
|
|
2355
2386
|
}
|
|
2356
2387
|
|
|
2357
2388
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2358
|
-
var
|
|
2359
|
-
function ExternalLink({ href, children }) {
|
|
2360
|
-
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
2361
|
-
}
|
|
2389
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2362
2390
|
function ArtistPhoto({ image, name }) {
|
|
2363
|
-
return /* @__PURE__ */ (0,
|
|
2364
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
2365
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
2366
|
-
"Photo: ",
|
|
2367
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
2368
|
-
",",
|
|
2369
|
-
" ",
|
|
2370
|
-
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
2371
|
-
] })
|
|
2372
|
-
] });
|
|
2391
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(CommonsPhoto, { image, alt: name });
|
|
2373
2392
|
}
|
|
2374
2393
|
|
|
2375
2394
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2376
2395
|
var import_ui17 = require("@behindthemusictree/ui");
|
|
2377
|
-
var
|
|
2396
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2378
2397
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2379
2398
|
if (memberships.length === 0) return null;
|
|
2380
|
-
return /* @__PURE__ */ (0,
|
|
2399
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2381
2400
|
const details = [
|
|
2382
2401
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2383
2402
|
...membership.roles
|
|
@@ -2385,9 +2404,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2385
2404
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
2386
2405
|
return (
|
|
2387
2406
|
// A member can have several periods in the same group.
|
|
2388
|
-
/* @__PURE__ */ (0,
|
|
2389
|
-
/* @__PURE__ */ (0,
|
|
2390
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2407
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2408
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2409
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2391
2410
|
" \u2014 ",
|
|
2392
2411
|
detailsText
|
|
2393
2412
|
] })
|
|
@@ -2398,9 +2417,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2398
2417
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2399
2418
|
if (memberships.length === 0) return null;
|
|
2400
2419
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2401
|
-
return /* @__PURE__ */ (0,
|
|
2402
|
-
/* @__PURE__ */ (0,
|
|
2403
|
-
/* @__PURE__ */ (0,
|
|
2420
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui17.PanelSection, { title, children: [
|
|
2421
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2404
2423
|
MembershipList,
|
|
2405
2424
|
{
|
|
2406
2425
|
title: "Past",
|
|
@@ -2413,7 +2432,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2413
2432
|
|
|
2414
2433
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2415
2434
|
var import_ui18 = require("@behindthemusictree/ui");
|
|
2416
|
-
var
|
|
2435
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2417
2436
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2418
2437
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2419
2438
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2433,12 +2452,12 @@ function byYear(a, b) {
|
|
|
2433
2452
|
function ArtistDiscography({ albums }) {
|
|
2434
2453
|
if (albums.length === 0) return null;
|
|
2435
2454
|
const sorted = [...albums].sort(byYear);
|
|
2436
|
-
return /* @__PURE__ */ (0,
|
|
2455
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui18.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2437
2456
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2438
2457
|
if (groupAlbums.length === 0) return null;
|
|
2439
|
-
return /* @__PURE__ */ (0,
|
|
2440
|
-
/* @__PURE__ */ (0,
|
|
2441
|
-
/* @__PURE__ */ (0,
|
|
2458
|
+
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: [
|
|
2459
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2460
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2442
2461
|
] }, album.uuid)) }) }, group);
|
|
2443
2462
|
}) });
|
|
2444
2463
|
}
|
|
@@ -2459,7 +2478,7 @@ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
2459
2478
|
var import_fa5 = require("react-icons/fa");
|
|
2460
2479
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2461
2480
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
2462
|
-
var
|
|
2481
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2463
2482
|
function TrackPositionPlayPause({
|
|
2464
2483
|
position,
|
|
2465
2484
|
uuid,
|
|
@@ -2473,14 +2492,14 @@ function TrackPositionPlayPause({
|
|
|
2473
2492
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PLAYING;
|
|
2474
2493
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PAUSED;
|
|
2475
2494
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2476
|
-
return /* @__PURE__ */ (0,
|
|
2495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
2477
2496
|
"div",
|
|
2478
2497
|
{
|
|
2479
2498
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2480
2499
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2481
2500
|
onClick: handlePlayPauseClick,
|
|
2482
2501
|
children: [
|
|
2483
|
-
/* @__PURE__ */ (0,
|
|
2502
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
2484
2503
|
"span",
|
|
2485
2504
|
{
|
|
2486
2505
|
className: "track-position-play-pause__bars",
|
|
@@ -2488,18 +2507,18 @@ function TrackPositionPlayPause({
|
|
|
2488
2507
|
role: "img",
|
|
2489
2508
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2490
2509
|
children: [
|
|
2491
|
-
/* @__PURE__ */ (0,
|
|
2492
|
-
/* @__PURE__ */ (0,
|
|
2493
|
-
/* @__PURE__ */ (0,
|
|
2510
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2512
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2494
2513
|
]
|
|
2495
2514
|
}
|
|
2496
2515
|
) : position }),
|
|
2497
|
-
/* @__PURE__ */ (0,
|
|
2516
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2498
2517
|
"div",
|
|
2499
2518
|
{
|
|
2500
2519
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2501
2520
|
"aria-hidden": "true",
|
|
2502
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2521
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_fa5.FaPlay, {})
|
|
2503
2522
|
}
|
|
2504
2523
|
)
|
|
2505
2524
|
]
|
|
@@ -2510,7 +2529,7 @@ function TrackPositionPlayPause({
|
|
|
2510
2529
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2511
2530
|
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2512
2531
|
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2513
|
-
var
|
|
2532
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2514
2533
|
function TrackItem({
|
|
2515
2534
|
track,
|
|
2516
2535
|
position,
|
|
@@ -2534,12 +2553,12 @@ function TrackItem({
|
|
|
2534
2553
|
loadTrackForPlayer(track.uuid);
|
|
2535
2554
|
}
|
|
2536
2555
|
};
|
|
2537
|
-
return /* @__PURE__ */ (0,
|
|
2556
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
2538
2557
|
"div",
|
|
2539
2558
|
{
|
|
2540
2559
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2541
2560
|
children: [
|
|
2542
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2561
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2543
2562
|
"div",
|
|
2544
2563
|
{
|
|
2545
2564
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2547,7 +2566,7 @@ function TrackItem({
|
|
|
2547
2566
|
"aria-disabled": "true",
|
|
2548
2567
|
children: position
|
|
2549
2568
|
}
|
|
2550
|
-
) : /* @__PURE__ */ (0,
|
|
2569
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2551
2570
|
TrackPositionPlayPause,
|
|
2552
2571
|
{
|
|
2553
2572
|
position,
|
|
@@ -2556,12 +2575,12 @@ function TrackItem({
|
|
|
2556
2575
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2557
2576
|
}
|
|
2558
2577
|
),
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2560
|
-
/* @__PURE__ */ (0,
|
|
2561
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2578
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2579
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree24.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2580
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree25.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2562
2581
|
] }),
|
|
2563
|
-
/* @__PURE__ */ (0,
|
|
2564
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2582
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
|
|
2583
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2565
2584
|
"div",
|
|
2566
2585
|
{
|
|
2567
2586
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2569,14 +2588,14 @@ function TrackItem({
|
|
|
2569
2588
|
children: renderDuration(track)
|
|
2570
2589
|
}
|
|
2571
2590
|
),
|
|
2572
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2591
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2573
2592
|
]
|
|
2574
2593
|
}
|
|
2575
2594
|
);
|
|
2576
2595
|
}
|
|
2577
2596
|
|
|
2578
2597
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2579
|
-
var
|
|
2598
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
2580
2599
|
var SongPageSchema2 = (0, import_transport8.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
|
|
2581
2600
|
var SORTS = [
|
|
2582
2601
|
{ ordering: "title", label: "Title" },
|
|
@@ -2609,8 +2628,8 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2609
2628
|
event.stopPropagation();
|
|
2610
2629
|
void play(song.uuid);
|
|
2611
2630
|
};
|
|
2612
|
-
return /* @__PURE__ */ (0,
|
|
2613
|
-
/* @__PURE__ */ (0,
|
|
2631
|
+
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: [
|
|
2632
|
+
/* @__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)(
|
|
2614
2633
|
"button",
|
|
2615
2634
|
{
|
|
2616
2635
|
type: "button",
|
|
@@ -2624,7 +2643,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2624
2643
|
},
|
|
2625
2644
|
sort.ordering
|
|
2626
2645
|
)) }) }),
|
|
2627
|
-
/* @__PURE__ */ (0,
|
|
2646
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2628
2647
|
TrackItem,
|
|
2629
2648
|
{
|
|
2630
2649
|
track: song,
|
|
@@ -2633,12 +2652,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2633
2652
|
renderSongLink
|
|
2634
2653
|
}
|
|
2635
2654
|
) }, song.uuid)) }),
|
|
2636
|
-
/* @__PURE__ */ (0,
|
|
2655
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui19.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2637
2656
|
] }) });
|
|
2638
2657
|
}
|
|
2639
2658
|
|
|
2640
2659
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2641
|
-
var
|
|
2660
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
2642
2661
|
function useArtistPanel({
|
|
2643
2662
|
uuid,
|
|
2644
2663
|
scope,
|
|
@@ -2659,26 +2678,26 @@ function useArtistPanel({
|
|
|
2659
2678
|
return {
|
|
2660
2679
|
...panel,
|
|
2661
2680
|
title: "Artist",
|
|
2662
|
-
content: /* @__PURE__ */ (0,
|
|
2663
|
-
/* @__PURE__ */ (0,
|
|
2664
|
-
/* @__PURE__ */ (0,
|
|
2665
|
-
/* @__PURE__ */ (0,
|
|
2666
|
-
/* @__PURE__ */ (0,
|
|
2667
|
-
/* @__PURE__ */ (0,
|
|
2668
|
-
/* @__PURE__ */ (0,
|
|
2681
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2682
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
|
|
2683
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-4 w-32" }),
|
|
2684
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-3 w-16" }),
|
|
2685
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-4 w-24" }),
|
|
2686
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2687
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui20.Skeleton, { className: "h-16" })
|
|
2669
2688
|
] })
|
|
2670
2689
|
};
|
|
2671
2690
|
case "not-found":
|
|
2672
2691
|
return {
|
|
2673
2692
|
...panel,
|
|
2674
2693
|
title: "Artist not found",
|
|
2675
|
-
content: /* @__PURE__ */ (0,
|
|
2694
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2676
2695
|
};
|
|
2677
2696
|
case "error":
|
|
2678
2697
|
return {
|
|
2679
2698
|
...panel,
|
|
2680
2699
|
title: "Artist",
|
|
2681
|
-
content: /* @__PURE__ */ (0,
|
|
2700
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
|
|
2682
2701
|
};
|
|
2683
2702
|
case "success": {
|
|
2684
2703
|
const { artist } = result;
|
|
@@ -2687,19 +2706,19 @@ function useArtistPanel({
|
|
|
2687
2706
|
return {
|
|
2688
2707
|
...panel,
|
|
2689
2708
|
title: artist.name,
|
|
2690
|
-
content: /* @__PURE__ */ (0,
|
|
2691
|
-
/* @__PURE__ */ (0,
|
|
2709
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
|
|
2710
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2692
2711
|
ArtistActionBand,
|
|
2693
2712
|
{
|
|
2694
2713
|
artist,
|
|
2695
|
-
share: shareTarget && shareUrl ? /* @__PURE__ */ (0,
|
|
2714
|
+
share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
|
|
2696
2715
|
}
|
|
2697
2716
|
),
|
|
2698
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2699
|
-
/* @__PURE__ */ (0,
|
|
2700
|
-
/* @__PURE__ */ (0,
|
|
2701
|
-
/* @__PURE__ */ (0,
|
|
2702
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2717
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2718
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistHeader, { artist }),
|
|
2719
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2720
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2721
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2703
2722
|
ArtistSongs,
|
|
2704
2723
|
{
|
|
2705
2724
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2709,7 +2728,7 @@ function useArtistPanel({
|
|
|
2709
2728
|
}
|
|
2710
2729
|
)
|
|
2711
2730
|
] }),
|
|
2712
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2731
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2713
2732
|
};
|
|
2714
2733
|
}
|
|
2715
2734
|
}
|
|
@@ -2725,7 +2744,7 @@ var import_lucide_react16 = require("lucide-react");
|
|
|
2725
2744
|
var import_ui21 = require("@behindthemusictree/ui");
|
|
2726
2745
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
2727
2746
|
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2728
|
-
var
|
|
2747
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
2729
2748
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2730
2749
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2731
2750
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
@@ -2733,27 +2752,27 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
2733
2752
|
const [open, setOpen] = (0, import_react18.useState)(false);
|
|
2734
2753
|
const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2735
2754
|
let body = null;
|
|
2736
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2737
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2755
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2756
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2738
2757
|
else if (history.status === "success")
|
|
2739
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2740
|
-
/* @__PURE__ */ (0,
|
|
2741
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2758
|
+
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: [
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2760
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2742
2761
|
" ",
|
|
2743
2762
|
"\u2014 ",
|
|
2744
2763
|
entry.oldValue ?? "none",
|
|
2745
2764
|
" \u2192 ",
|
|
2746
2765
|
entry.newValue ?? "none"
|
|
2747
2766
|
] }) : null,
|
|
2748
|
-
/* @__PURE__ */ (0,
|
|
2767
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
|
|
2749
2768
|
entry.actorPseudo ?? "Anonymous",
|
|
2750
2769
|
" \xB7 ",
|
|
2751
2770
|
dateFormat.format(new Date(entry.createdOn))
|
|
2752
2771
|
] })
|
|
2753
2772
|
] }, entry.uuid)) });
|
|
2754
|
-
return /* @__PURE__ */ (0,
|
|
2755
|
-
/* @__PURE__ */ (0,
|
|
2756
|
-
/* @__PURE__ */ (0,
|
|
2773
|
+
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: [
|
|
2774
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui21.PANEL_LABEL_CLASS}`, children: "History" }),
|
|
2775
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "mt-1.5", children: body })
|
|
2757
2776
|
] }) });
|
|
2758
2777
|
}
|
|
2759
2778
|
function SongPanelContent(props) {
|
|
@@ -2772,13 +2791,13 @@ function SongPanelContent(props) {
|
|
|
2772
2791
|
const genre = song.genre;
|
|
2773
2792
|
fields.push({
|
|
2774
2793
|
label: "Genre",
|
|
2775
|
-
value: onSelectGenre ? /* @__PURE__ */ (0,
|
|
2776
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2777
|
-
/* @__PURE__ */ (0,
|
|
2794
|
+
value: onSelectGenre ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { children: [
|
|
2795
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2796
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2778
2797
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2779
2798
|
});
|
|
2780
2799
|
} else {
|
|
2781
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2800
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2782
2801
|
}
|
|
2783
2802
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2784
2803
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2788,11 +2807,11 @@ function SongPanelContent(props) {
|
|
|
2788
2807
|
const links = [];
|
|
2789
2808
|
if (musicBrainzUrl)
|
|
2790
2809
|
links.push(
|
|
2791
|
-
/* @__PURE__ */ (0,
|
|
2810
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2792
2811
|
);
|
|
2793
2812
|
if (song.youtubeVideo)
|
|
2794
2813
|
links.push(
|
|
2795
|
-
/* @__PURE__ */ (0,
|
|
2814
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2796
2815
|
"a",
|
|
2797
2816
|
{
|
|
2798
2817
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2807,31 +2826,31 @@ function SongPanelContent(props) {
|
|
|
2807
2826
|
if (links.length > 0)
|
|
2808
2827
|
fields.push({
|
|
2809
2828
|
label: "Links",
|
|
2810
|
-
value: /* @__PURE__ */ (0,
|
|
2829
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex gap-3", children: links })
|
|
2811
2830
|
});
|
|
2812
|
-
return /* @__PURE__ */ (0,
|
|
2813
|
-
/* @__PURE__ */ (0,
|
|
2814
|
-
/* @__PURE__ */ (0,
|
|
2831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
|
|
2832
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_ui21.ActionBand, { children: [
|
|
2833
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2815
2834
|
import_ui21.ActionButton,
|
|
2816
2835
|
{
|
|
2817
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
2836
|
+
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" }),
|
|
2818
2837
|
label: isPlaying ? "Pause" : "Play",
|
|
2819
2838
|
disabled: !!unplayableReason,
|
|
2820
2839
|
title: unplayableReason ?? void 0,
|
|
2821
2840
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
|
|
2822
2841
|
}
|
|
2823
2842
|
),
|
|
2824
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2843
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2825
2844
|
] }) }),
|
|
2826
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2827
|
-
/* @__PURE__ */ (0,
|
|
2828
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2829
|
-
/* @__PURE__ */ (0,
|
|
2845
|
+
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 }) }),
|
|
2846
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui21.FieldList, { items: fields }),
|
|
2847
|
+
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)) }) }),
|
|
2848
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2830
2849
|
] });
|
|
2831
2850
|
}
|
|
2832
2851
|
|
|
2833
2852
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2834
|
-
var
|
|
2853
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
2835
2854
|
function useSongPanel({
|
|
2836
2855
|
uuid,
|
|
2837
2856
|
scope,
|
|
@@ -2852,24 +2871,24 @@ function useSongPanel({
|
|
|
2852
2871
|
return {
|
|
2853
2872
|
...panel,
|
|
2854
2873
|
title: "Song",
|
|
2855
|
-
content: /* @__PURE__ */ (0,
|
|
2856
|
-
/* @__PURE__ */ (0,
|
|
2857
|
-
/* @__PURE__ */ (0,
|
|
2858
|
-
/* @__PURE__ */ (0,
|
|
2859
|
-
/* @__PURE__ */ (0,
|
|
2874
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2875
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
|
|
2876
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-4 w-32" }),
|
|
2877
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
|
|
2878
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui22.Skeleton, { className: "h-4 w-24" })
|
|
2860
2879
|
] })
|
|
2861
2880
|
};
|
|
2862
2881
|
case "not-found":
|
|
2863
2882
|
return {
|
|
2864
2883
|
...panel,
|
|
2865
2884
|
title: "Song not found",
|
|
2866
|
-
content: /* @__PURE__ */ (0,
|
|
2885
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2867
2886
|
};
|
|
2868
2887
|
case "error":
|
|
2869
2888
|
return {
|
|
2870
2889
|
...panel,
|
|
2871
2890
|
title: "Song",
|
|
2872
|
-
content: /* @__PURE__ */ (0,
|
|
2891
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
|
|
2873
2892
|
};
|
|
2874
2893
|
case "success": {
|
|
2875
2894
|
const { song } = result;
|
|
@@ -2877,7 +2896,7 @@ function useSongPanel({
|
|
|
2877
2896
|
return {
|
|
2878
2897
|
...panel,
|
|
2879
2898
|
title: song.title,
|
|
2880
|
-
content: /* @__PURE__ */ (0,
|
|
2899
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
2881
2900
|
SongPanelContent,
|
|
2882
2901
|
{
|
|
2883
2902
|
song,
|
|
@@ -2930,12 +2949,12 @@ function useEntityPanel({
|
|
|
2930
2949
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2931
2950
|
var import_md2 = require("react-icons/md");
|
|
2932
2951
|
var import_ui23 = require("@behindthemusictree/ui");
|
|
2933
|
-
var
|
|
2952
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
2934
2953
|
function SizeMetricToggle({
|
|
2935
2954
|
value,
|
|
2936
2955
|
onChange
|
|
2937
2956
|
}) {
|
|
2938
|
-
return /* @__PURE__ */ (0,
|
|
2957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
2939
2958
|
import_ui23.SegmentedControl,
|
|
2940
2959
|
{
|
|
2941
2960
|
variant: "floating",
|
|
@@ -2943,15 +2962,15 @@ function SizeMetricToggle({
|
|
|
2943
2962
|
value,
|
|
2944
2963
|
onChange,
|
|
2945
2964
|
options: [
|
|
2946
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2947
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2965
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2966
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2948
2967
|
]
|
|
2949
2968
|
}
|
|
2950
2969
|
);
|
|
2951
2970
|
}
|
|
2952
2971
|
|
|
2953
2972
|
// src/genre-tree/GenreTreeView.tsx
|
|
2954
|
-
var
|
|
2973
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
2955
2974
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2956
2975
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2957
2976
|
var noop = () => {
|
|
@@ -3059,7 +3078,7 @@ function GenreTreeView({
|
|
|
3059
3078
|
allowWheelRotation: internalAllowWheelRotation,
|
|
3060
3079
|
toggleWheelRotation,
|
|
3061
3080
|
onNodeHover: handleNodeHover
|
|
3062
|
-
} = (0,
|
|
3081
|
+
} = (0, import_genre_tree31.useGenreTreeViewState)({
|
|
3063
3082
|
scope,
|
|
3064
3083
|
getBackendBaseUrl,
|
|
3065
3084
|
viewMode: controlledViewMode,
|
|
@@ -3078,12 +3097,12 @@ function GenreTreeView({
|
|
|
3078
3097
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
3079
3098
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
3080
3099
|
}
|
|
3081
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
3082
|
-
const { mutate: updateGenre } = (0,
|
|
3100
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree31.genreUpdateMutationKey }) > 0;
|
|
3101
|
+
const { mutate: updateGenre } = (0, import_genre_tree31.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
3083
3102
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
3084
3103
|
const isArtistFiltered = artistUuid !== null;
|
|
3085
3104
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
3086
|
-
const artistResult = (0,
|
|
3105
|
+
const artistResult = (0, import_genre_tree31.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
3087
3106
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
3088
3107
|
const filterArtist = (0, import_react19.useMemo)(
|
|
3089
3108
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -3103,14 +3122,14 @@ function GenreTreeView({
|
|
|
3103
3122
|
const openSongRef = (0, import_react19.useRef)(openSong);
|
|
3104
3123
|
openSongRef.current = openSong;
|
|
3105
3124
|
const resolvedRenderSongLink = (0, import_react19.useCallback)(
|
|
3106
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
3125
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
3107
3126
|
[renderSongLink]
|
|
3108
3127
|
);
|
|
3109
3128
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react19.useState)(null);
|
|
3110
3129
|
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react19.useState)(null);
|
|
3111
3130
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
3112
3131
|
const songBaseUrl = getBackendBaseUrl;
|
|
3113
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
3132
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
3114
3133
|
const getGenrePath = (genre) => {
|
|
3115
3134
|
const path = [];
|
|
3116
3135
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -3187,9 +3206,10 @@ function GenreTreeView({
|
|
|
3187
3206
|
resolveStyle
|
|
3188
3207
|
);
|
|
3189
3208
|
const treeStyles = (0, import_react19.useMemo)(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
|
|
3190
|
-
const
|
|
3191
|
-
|
|
3192
|
-
|
|
3209
|
+
const validSelectedGenreUuid = (0, import_genre_tree30.toGenreUuid)(selectedGenreUuid);
|
|
3210
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react19.useState)({ current: validSelectedGenreUuid, previous: null });
|
|
3211
|
+
if (selectionHistory.current !== validSelectedGenreUuid) {
|
|
3212
|
+
setSelectionHistory({ current: validSelectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
3193
3213
|
}
|
|
3194
3214
|
const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
|
|
3195
3215
|
const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
|
|
@@ -3221,7 +3241,7 @@ function GenreTreeView({
|
|
|
3221
3241
|
const canToggleSide = !readOnly && criteria !== void 0 && criteria !== null && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
3222
3242
|
const side = criteria?.side ?? "core";
|
|
3223
3243
|
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find((sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist?.uuid && sibling.criteria?.side === "pop") : void 0;
|
|
3224
|
-
const shareTarget =
|
|
3244
|
+
const shareTarget = validSelectedGenreUuid ? { kind: "genre", genreUuid: validSelectedGenreUuid, name: criteria?.name ?? "Genre", scope, artistUuid } : void 0;
|
|
3225
3245
|
const shareUrl = shareTarget ? getShareUrlRef.current?.(shareTarget) ?? void 0 : void 0;
|
|
3226
3246
|
const genreContent = useGenreDetailContent({
|
|
3227
3247
|
genreUuid: selectedGenreUuid,
|
|
@@ -3243,7 +3263,7 @@ function GenreTreeView({
|
|
|
3243
3263
|
onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
|
|
3244
3264
|
onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
|
|
3245
3265
|
onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
|
|
3246
|
-
/* @__PURE__ */ (0,
|
|
3266
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3247
3267
|
ChangeParentPopup,
|
|
3248
3268
|
{
|
|
3249
3269
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -3287,7 +3307,7 @@ function GenreTreeView({
|
|
|
3287
3307
|
onClose: otherPanel.onClose,
|
|
3288
3308
|
searchName: otherPanel.title
|
|
3289
3309
|
};
|
|
3290
|
-
} else if (selectedGenreUuid) {
|
|
3310
|
+
} else if (selectedGenreUuid !== null) {
|
|
3291
3311
|
activePanel = {
|
|
3292
3312
|
title: genreContent.title,
|
|
3293
3313
|
content: genreContent.content,
|
|
@@ -3304,19 +3324,19 @@ function GenreTreeView({
|
|
|
3304
3324
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
3305
3325
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
3306
3326
|
} : void 0;
|
|
3307
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
3327
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3308
3328
|
import_ui24.IconButton,
|
|
3309
3329
|
{
|
|
3310
3330
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
3311
3331
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
3312
3332
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
3313
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3333
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react18.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_lucide_react18.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3314
3334
|
}
|
|
3315
3335
|
) : null;
|
|
3316
3336
|
const renderArtistFilter = () => {
|
|
3317
3337
|
if (isLoading || !filterArtist) return null;
|
|
3318
|
-
return /* @__PURE__ */ (0,
|
|
3319
|
-
/* @__PURE__ */ (0,
|
|
3338
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
|
|
3339
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3320
3340
|
"button",
|
|
3321
3341
|
{
|
|
3322
3342
|
type: "button",
|
|
@@ -3328,7 +3348,7 @@ function GenreTreeView({
|
|
|
3328
3348
|
children: filterArtist.name
|
|
3329
3349
|
}
|
|
3330
3350
|
),
|
|
3331
|
-
/* @__PURE__ */ (0,
|
|
3351
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3332
3352
|
"button",
|
|
3333
3353
|
{
|
|
3334
3354
|
type: "button",
|
|
@@ -3340,8 +3360,8 @@ function GenreTreeView({
|
|
|
3340
3360
|
)
|
|
3341
3361
|
] });
|
|
3342
3362
|
};
|
|
3343
|
-
const actions = /* @__PURE__ */ (0,
|
|
3344
|
-
!isLoading && !isError && /* @__PURE__ */ (0,
|
|
3363
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_jsx_runtime52.Fragment, { children: [
|
|
3364
|
+
!isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3345
3365
|
GenreTreeSearch,
|
|
3346
3366
|
{
|
|
3347
3367
|
artistBaseUrl,
|
|
@@ -3374,9 +3394,9 @@ function GenreTreeView({
|
|
|
3374
3394
|
extraGroups: extraSearchGroups
|
|
3375
3395
|
}
|
|
3376
3396
|
),
|
|
3377
|
-
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0,
|
|
3378
|
-
/* @__PURE__ */ (0,
|
|
3379
|
-
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0,
|
|
3397
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3398
|
+
/* @__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: [
|
|
3399
|
+
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3380
3400
|
import_ui24.SegmentedControl,
|
|
3381
3401
|
{
|
|
3382
3402
|
"aria-label": "Tree view mode",
|
|
@@ -3390,13 +3410,13 @@ function GenreTreeView({
|
|
|
3390
3410
|
]
|
|
3391
3411
|
}
|
|
3392
3412
|
),
|
|
3393
|
-
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3413
|
+
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3394
3414
|
"div",
|
|
3395
3415
|
{
|
|
3396
3416
|
className: "flex items-center gap-1",
|
|
3397
3417
|
role: "group",
|
|
3398
3418
|
"aria-label": "Tree display options",
|
|
3399
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3419
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3400
3420
|
import_ui24.Button,
|
|
3401
3421
|
{
|
|
3402
3422
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -3407,7 +3427,7 @@ function GenreTreeView({
|
|
|
3407
3427
|
)
|
|
3408
3428
|
}
|
|
3409
3429
|
),
|
|
3410
|
-
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3430
|
+
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3411
3431
|
import_ui24.IconTextButton,
|
|
3412
3432
|
{
|
|
3413
3433
|
icon: import_lucide_react17.Plus,
|
|
@@ -3417,38 +3437,38 @@ function GenreTreeView({
|
|
|
3417
3437
|
)
|
|
3418
3438
|
] })
|
|
3419
3439
|
] });
|
|
3420
|
-
return /* @__PURE__ */ (0,
|
|
3440
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
|
|
3421
3441
|
"div",
|
|
3422
3442
|
{
|
|
3423
3443
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3424
3444
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3425
3445
|
children: [
|
|
3426
|
-
/* @__PURE__ */ (0,
|
|
3446
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3427
3447
|
"div",
|
|
3428
3448
|
{
|
|
3429
3449
|
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"}`,
|
|
3430
3450
|
children: actions
|
|
3431
3451
|
}
|
|
3432
3452
|
),
|
|
3433
|
-
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0,
|
|
3453
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3434
3454
|
"div",
|
|
3435
3455
|
{
|
|
3436
3456
|
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"}`,
|
|
3437
3457
|
children: renderArtistFilter()
|
|
3438
3458
|
}
|
|
3439
3459
|
),
|
|
3440
|
-
/* @__PURE__ */ (0,
|
|
3441
|
-
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
3442
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3460
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3461
|
+
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3462
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
|
|
3443
3463
|
"Couldn't load the genre tree.",
|
|
3444
3464
|
" ",
|
|
3445
|
-
/* @__PURE__ */ (0,
|
|
3465
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
|
|
3446
3466
|
] }) : viewMode === "wheel" ? (
|
|
3447
3467
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3448
3468
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3449
3469
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3450
3470
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3451
|
-
/* @__PURE__ */ (0,
|
|
3471
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3452
3472
|
GenrePlaylistTreeWheel,
|
|
3453
3473
|
{
|
|
3454
3474
|
scope,
|
|
@@ -3466,9 +3486,9 @@ function GenreTreeView({
|
|
|
3466
3486
|
allowWheelRotation,
|
|
3467
3487
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3468
3488
|
busy,
|
|
3469
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3489
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3470
3490
|
floatingControlsLeading,
|
|
3471
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3491
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3472
3492
|
] }) : void 0,
|
|
3473
3493
|
defaultViewport,
|
|
3474
3494
|
viewportRestore,
|
|
@@ -3477,7 +3497,7 @@ function GenreTreeView({
|
|
|
3477
3497
|
) }) })
|
|
3478
3498
|
) : viewMode === "pop-core" ? (
|
|
3479
3499
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3480
|
-
/* @__PURE__ */ (0,
|
|
3500
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3481
3501
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3482
3502
|
{
|
|
3483
3503
|
scope,
|
|
@@ -3495,9 +3515,9 @@ function GenreTreeView({
|
|
|
3495
3515
|
allowWheelRotation,
|
|
3496
3516
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3497
3517
|
busy,
|
|
3498
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3518
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3499
3519
|
floatingControlsLeading,
|
|
3500
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3520
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3501
3521
|
] }) : void 0,
|
|
3502
3522
|
defaultViewport,
|
|
3503
3523
|
viewportRestore,
|
|
@@ -3505,7 +3525,7 @@ function GenreTreeView({
|
|
|
3505
3525
|
...focus
|
|
3506
3526
|
}
|
|
3507
3527
|
) }) })
|
|
3508
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3528
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3509
3529
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3510
3530
|
{
|
|
3511
3531
|
outline: true,
|
|
@@ -3522,13 +3542,13 @@ function GenreTreeView({
|
|
|
3522
3542
|
selectedNodeId,
|
|
3523
3543
|
readOnly
|
|
3524
3544
|
}
|
|
3525
|
-
) }) : /* @__PURE__ */ (0,
|
|
3545
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
3526
3546
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3527
|
-
return /* @__PURE__ */ (0,
|
|
3547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3528
3548
|
"div",
|
|
3529
3549
|
{
|
|
3530
3550
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3531
|
-
children: /* @__PURE__ */ (0,
|
|
3551
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3532
3552
|
GenrePlaylistTreePerRoot,
|
|
3533
3553
|
{
|
|
3534
3554
|
scope,
|
|
@@ -3555,7 +3575,7 @@ function GenreTreeView({
|
|
|
3555
3575
|
);
|
|
3556
3576
|
}
|
|
3557
3577
|
) }),
|
|
3558
|
-
/* @__PURE__ */ (0,
|
|
3578
|
+
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3559
3579
|
import_ui24.DetailPanel,
|
|
3560
3580
|
{
|
|
3561
3581
|
ref: panelRef,
|
|
@@ -3570,7 +3590,7 @@ function GenreTreeView({
|
|
|
3570
3590
|
onExpandedChange: activePanel?.onExpandedChange,
|
|
3571
3591
|
aside: activePanel?.aside,
|
|
3572
3592
|
edgeTab: detailTab,
|
|
3573
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3593
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_jsx_runtime52.Fragment, { children: [
|
|
3574
3594
|
activePanel.content,
|
|
3575
3595
|
activePanel.aside
|
|
3576
3596
|
] }) : activePanel?.content
|
|
@@ -3588,10 +3608,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3588
3608
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3589
3609
|
var import_react20 = require("react");
|
|
3590
3610
|
var import_lucide_react19 = require("lucide-react");
|
|
3591
|
-
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3592
3611
|
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3612
|
+
var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3593
3613
|
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
3594
|
-
var
|
|
3614
|
+
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
3595
3615
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react20.useEffect : import_react20.useLayoutEffect;
|
|
3596
3616
|
function TrackListSidebar({
|
|
3597
3617
|
className: className2 = "",
|
|
@@ -3604,7 +3624,7 @@ function TrackListSidebar({
|
|
|
3604
3624
|
renderArtistLink,
|
|
3605
3625
|
renderSongLink
|
|
3606
3626
|
}) {
|
|
3607
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3627
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree32.useTrackList)();
|
|
3608
3628
|
const trackNoun = (0, import_track_noun4.useTrackNoun)();
|
|
3609
3629
|
const trackListRef = (0, import_react20.useRef)(null);
|
|
3610
3630
|
const previousOriginRef = (0, import_react20.useRef)(trackList?.origin ?? null);
|
|
@@ -3693,25 +3713,25 @@ function TrackListSidebar({
|
|
|
3693
3713
|
}
|
|
3694
3714
|
};
|
|
3695
3715
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3696
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3716
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
3697
3717
|
"div",
|
|
3698
3718
|
{
|
|
3699
3719
|
"data-theme": "dark",
|
|
3700
3720
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3701
3721
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3702
3722
|
children: [
|
|
3703
|
-
/* @__PURE__ */ (0,
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
3705
|
-
/* @__PURE__ */ (0,
|
|
3706
|
-
/* @__PURE__ */ (0,
|
|
3707
|
-
/* @__PURE__ */ (0,
|
|
3723
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3724
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3725
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3726
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3727
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3708
3728
|
] }),
|
|
3709
|
-
/* @__PURE__ */ (0,
|
|
3710
|
-
trackList.origin.type ===
|
|
3729
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
|
|
3730
|
+
trackList.origin.type === import_genre_tree33.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree33.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3711
3731
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3712
3732
|
] })
|
|
3713
3733
|
] }),
|
|
3714
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3734
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
3715
3735
|
"button",
|
|
3716
3736
|
{
|
|
3717
3737
|
type: "button",
|
|
@@ -3720,11 +3740,11 @@ function TrackListSidebar({
|
|
|
3720
3740
|
"aria-expanded": !isCollapsed,
|
|
3721
3741
|
className: `ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-md hover:bg-[var(--color-surface-muted)] ${isCollapsed ? "text-[var(--color-text-muted)]" : "text-white"}`,
|
|
3722
3742
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3723
|
-
children: /* @__PURE__ */ (0,
|
|
3743
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_lucide_react19.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3724
3744
|
}
|
|
3725
3745
|
)
|
|
3726
3746
|
] }),
|
|
3727
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3747
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
3728
3748
|
"ul",
|
|
3729
3749
|
{
|
|
3730
3750
|
ref: trackListRef,
|
|
@@ -3734,7 +3754,7 @@ function TrackListSidebar({
|
|
|
3734
3754
|
onPointerDown: markUserScrollIntent,
|
|
3735
3755
|
className: layout === "inline" ? "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0" : "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0",
|
|
3736
3756
|
children: [
|
|
3737
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3757
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
3738
3758
|
TrackItem,
|
|
3739
3759
|
{
|
|
3740
3760
|
track,
|
|
@@ -3745,7 +3765,7 @@ function TrackListSidebar({
|
|
|
3745
3765
|
renderSongLink
|
|
3746
3766
|
}
|
|
3747
3767
|
) }, track.uuid)),
|
|
3748
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3768
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3749
3769
|
]
|
|
3750
3770
|
}
|
|
3751
3771
|
)
|
|
@@ -3758,14 +3778,14 @@ function TrackListSidebar({
|
|
|
3758
3778
|
var import_react21 = require("react");
|
|
3759
3779
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3760
3780
|
var import_player14 = require("@behindthemusictree/app-kit/player");
|
|
3761
|
-
var
|
|
3781
|
+
var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3762
3782
|
var import_ui25 = require("@behindthemusictree/ui");
|
|
3763
3783
|
|
|
3764
3784
|
// src/genre-tree/PlayerControls.tsx
|
|
3765
3785
|
var import_lucide_react20 = require("lucide-react");
|
|
3766
|
-
var
|
|
3786
|
+
var import_genre_tree34 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3767
3787
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3768
|
-
var
|
|
3788
|
+
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
3769
3789
|
function PlayerControls() {
|
|
3770
3790
|
const {
|
|
3771
3791
|
duration,
|
|
@@ -3777,22 +3797,22 @@ function PlayerControls() {
|
|
|
3777
3797
|
setVolume,
|
|
3778
3798
|
volume
|
|
3779
3799
|
} = (0, import_player13.usePlayer)();
|
|
3780
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3800
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree34.useTrackList)();
|
|
3781
3801
|
const currentTime = (0, import_player13.useCurrentTime)();
|
|
3782
3802
|
const isBusy = isLoading || playState === import_player13.PlayStates.LOADING;
|
|
3783
3803
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3784
3804
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3785
3805
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3786
3806
|
const isPlaying = playState === import_player13.PlayStates.PLAYING;
|
|
3787
|
-
return /* @__PURE__ */ (0,
|
|
3807
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
|
|
3788
3808
|
"section",
|
|
3789
3809
|
{
|
|
3790
3810
|
"aria-label": "Player controls",
|
|
3791
3811
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3792
3812
|
children: [
|
|
3793
|
-
/* @__PURE__ */ (0,
|
|
3794
|
-
/* @__PURE__ */ (0,
|
|
3795
|
-
/* @__PURE__ */ (0,
|
|
3813
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3814
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree34.formatTime)(safeCurrentTime) }),
|
|
3815
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3796
3816
|
"input",
|
|
3797
3817
|
{
|
|
3798
3818
|
"aria-label": "Seek",
|
|
@@ -3806,11 +3826,11 @@ function PlayerControls() {
|
|
|
3806
3826
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3807
3827
|
}
|
|
3808
3828
|
),
|
|
3809
|
-
/* @__PURE__ */ (0,
|
|
3829
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree34.formatTime)(safeDuration) })
|
|
3810
3830
|
] }),
|
|
3811
|
-
/* @__PURE__ */ (0,
|
|
3812
|
-
/* @__PURE__ */ (0,
|
|
3813
|
-
/* @__PURE__ */ (0,
|
|
3831
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3832
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3833
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3814
3834
|
"button",
|
|
3815
3835
|
{
|
|
3816
3836
|
type: "button",
|
|
@@ -3819,10 +3839,10 @@ function PlayerControls() {
|
|
|
3819
3839
|
disabled: !isReady || !hasPreviousTrack,
|
|
3820
3840
|
onClick: playPreviousTrack,
|
|
3821
3841
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
3822
|
-
children: /* @__PURE__ */ (0,
|
|
3842
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react20.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3823
3843
|
}
|
|
3824
3844
|
),
|
|
3825
|
-
/* @__PURE__ */ (0,
|
|
3845
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3826
3846
|
"button",
|
|
3827
3847
|
{
|
|
3828
3848
|
type: "button",
|
|
@@ -3831,10 +3851,10 @@ function PlayerControls() {
|
|
|
3831
3851
|
disabled: !isReady,
|
|
3832
3852
|
onClick: handlePlayPauseAction,
|
|
3833
3853
|
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-white text-zinc-950 hover:bg-zinc-200 disabled:cursor-not-allowed disabled:opacity-40",
|
|
3834
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3854
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react20.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react20.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3835
3855
|
}
|
|
3836
3856
|
),
|
|
3837
|
-
/* @__PURE__ */ (0,
|
|
3857
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3838
3858
|
"button",
|
|
3839
3859
|
{
|
|
3840
3860
|
type: "button",
|
|
@@ -3843,14 +3863,14 @@ function PlayerControls() {
|
|
|
3843
3863
|
disabled: !isReady || !hasNextTrack,
|
|
3844
3864
|
onClick: playNextTrack,
|
|
3845
3865
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
3846
|
-
children: /* @__PURE__ */ (0,
|
|
3866
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react20.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3847
3867
|
}
|
|
3848
3868
|
)
|
|
3849
3869
|
] }),
|
|
3850
|
-
/* @__PURE__ */ (0,
|
|
3851
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3852
|
-
/* @__PURE__ */ (0,
|
|
3853
|
-
/* @__PURE__ */ (0,
|
|
3870
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3871
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react20.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_lucide_react20.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3872
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3873
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3854
3874
|
"input",
|
|
3855
3875
|
{
|
|
3856
3876
|
"aria-label": "Volume",
|
|
@@ -3872,7 +3892,7 @@ function PlayerControls() {
|
|
|
3872
3892
|
}
|
|
3873
3893
|
|
|
3874
3894
|
// src/genre-tree/PlayerPanel.tsx
|
|
3875
|
-
var
|
|
3895
|
+
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
3876
3896
|
var PANEL_ID = "player-panel";
|
|
3877
3897
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3878
3898
|
function PlayerPanel({
|
|
@@ -3885,7 +3905,7 @@ function PlayerPanel({
|
|
|
3885
3905
|
renderSongLink
|
|
3886
3906
|
}) {
|
|
3887
3907
|
const { playerTrackObject, retryPlayback } = (0, import_player14.usePlayer)();
|
|
3888
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3908
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree35.useTrackListSidebarVisibility)();
|
|
3889
3909
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react21.useState)("");
|
|
3890
3910
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3891
3911
|
(0, import_react21.useEffect)(() => {
|
|
@@ -3912,7 +3932,7 @@ function PlayerPanel({
|
|
|
3912
3932
|
onClick: () => setIsExtendRequested(true),
|
|
3913
3933
|
expanded: false
|
|
3914
3934
|
} : null;
|
|
3915
|
-
return /* @__PURE__ */ (0,
|
|
3935
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
3916
3936
|
"div",
|
|
3917
3937
|
{
|
|
3918
3938
|
id: PANEL_ID,
|
|
@@ -3925,7 +3945,7 @@ function PlayerPanel({
|
|
|
3925
3945
|
),
|
|
3926
3946
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3927
3947
|
children: [
|
|
3928
|
-
/* @__PURE__ */ (0,
|
|
3948
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
3929
3949
|
import_ui25.PanelEdgeTab,
|
|
3930
3950
|
{
|
|
3931
3951
|
side: "right",
|
|
@@ -3935,7 +3955,7 @@ function PlayerPanel({
|
|
|
3935
3955
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3936
3956
|
}
|
|
3937
3957
|
),
|
|
3938
|
-
/* @__PURE__ */ (0,
|
|
3958
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
|
|
3939
3959
|
"div",
|
|
3940
3960
|
{
|
|
3941
3961
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3943,20 +3963,20 @@ function PlayerPanel({
|
|
|
3943
3963
|
isExtended ? "flex-row" : "flex-col"
|
|
3944
3964
|
),
|
|
3945
3965
|
children: [
|
|
3946
|
-
/* @__PURE__ */ (0,
|
|
3947
|
-
/* @__PURE__ */ (0,
|
|
3948
|
-
/* @__PURE__ */ (0,
|
|
3949
|
-
/* @__PURE__ */ (0,
|
|
3950
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3966
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3967
|
+
/* @__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 }) }),
|
|
3968
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3969
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3970
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3951
3971
|
playerTrackObject.loadError,
|
|
3952
|
-
/* @__PURE__ */ (0,
|
|
3972
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3953
3973
|
void retryPlayback();
|
|
3954
3974
|
}, children: "Retry" })
|
|
3955
3975
|
] })
|
|
3956
3976
|
] }),
|
|
3957
|
-
/* @__PURE__ */ (0,
|
|
3977
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(PlayerControls, {})
|
|
3958
3978
|
] }),
|
|
3959
|
-
/* @__PURE__ */ (0,
|
|
3979
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
3960
3980
|
TrackListSidebar,
|
|
3961
3981
|
{
|
|
3962
3982
|
layout: "inline",
|
|
@@ -3982,33 +4002,33 @@ function PlayerPanel({
|
|
|
3982
4002
|
// src/genre-tree/Rating.tsx
|
|
3983
4003
|
var import_uuid = require("uuid");
|
|
3984
4004
|
var import_ai = require("react-icons/ai");
|
|
3985
|
-
var
|
|
3986
|
-
var
|
|
4005
|
+
var import_genre_tree36 = require("@behindthemusictree/app-kit/genre-tree");
|
|
4006
|
+
var import_jsx_runtime56 = require("react/jsx-runtime");
|
|
3987
4007
|
function Rating({ rating, handleChange }) {
|
|
3988
4008
|
const instanceId = (0, import_uuid.v4)();
|
|
3989
|
-
return /* @__PURE__ */ (0,
|
|
3990
|
-
/* @__PURE__ */ (0,
|
|
4009
|
+
return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
4010
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
3991
4011
|
"input",
|
|
3992
4012
|
{
|
|
3993
4013
|
id: `star-${instanceId}-${i}`,
|
|
3994
4014
|
className: "w-6 h-6 opacity-0",
|
|
3995
4015
|
type: "radio",
|
|
3996
4016
|
name: "rating",
|
|
3997
|
-
value: i === 0 ?
|
|
4017
|
+
value: i === 0 ? import_genre_tree36.FORM_RATING_NULL_VALUE : i,
|
|
3998
4018
|
checked: rating === (i === 0 ? null : i),
|
|
3999
4019
|
onChange: handleChange
|
|
4000
4020
|
}
|
|
4001
4021
|
),
|
|
4002
|
-
/* @__PURE__ */ (0,
|
|
4003
|
-
/* @__PURE__ */ (0,
|
|
4022
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className: "flex", children: [
|
|
4023
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
4004
4024
|
import_ai.AiOutlineStar,
|
|
4005
4025
|
{
|
|
4006
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
4026
|
+
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree36.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
4007
4027
|
}
|
|
4008
4028
|
),
|
|
4009
|
-
/* @__PURE__ */ (0,
|
|
4029
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "flex-grow w-8" }),
|
|
4010
4030
|
" "
|
|
4011
|
-
] }) : /* @__PURE__ */ (0,
|
|
4031
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
4012
4032
|
import_ai.AiFillStar,
|
|
4013
4033
|
{
|
|
4014
4034
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -4020,7 +4040,7 @@ function Rating({ rating, handleChange }) {
|
|
|
4020
4040
|
// src/genre-tree/EntityDetailPanel.tsx
|
|
4021
4041
|
var import_react22 = require("react");
|
|
4022
4042
|
var import_ui26 = require("@behindthemusictree/ui");
|
|
4023
|
-
var
|
|
4043
|
+
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
4024
4044
|
function EntityDetailPanel({
|
|
4025
4045
|
scope,
|
|
4026
4046
|
getBackendBaseUrl,
|
|
@@ -4043,8 +4063,8 @@ function EntityDetailPanel({
|
|
|
4043
4063
|
getBackendBaseUrl,
|
|
4044
4064
|
getArtistBackendBaseUrl,
|
|
4045
4065
|
getShareUrl,
|
|
4046
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
4047
|
-
renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0,
|
|
4066
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
4067
|
+
renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
|
|
4048
4068
|
song: {
|
|
4049
4069
|
uuid: songUuid,
|
|
4050
4070
|
expanded: songUuid !== null && expandedSongUuid === songUuid,
|
|
@@ -4060,7 +4080,7 @@ function EntityDetailPanel({
|
|
|
4060
4080
|
...navigation
|
|
4061
4081
|
});
|
|
4062
4082
|
if (!panel) return null;
|
|
4063
|
-
return /* @__PURE__ */ (0,
|
|
4083
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
4064
4084
|
import_ui26.DetailPanel,
|
|
4065
4085
|
{
|
|
4066
4086
|
side: "left",
|
|
@@ -4072,7 +4092,7 @@ function EntityDetailPanel({
|
|
|
4072
4092
|
onExpandedChange: panel.onExpandedChange,
|
|
4073
4093
|
aside: panel.extraContent,
|
|
4074
4094
|
edgeTab,
|
|
4075
|
-
children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0,
|
|
4095
|
+
children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
|
|
4076
4096
|
panel.content,
|
|
4077
4097
|
panel.extraContent
|
|
4078
4098
|
] }) : panel.content
|