@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
|
@@ -55,6 +55,7 @@ var import_lucide_react14 = require("lucide-react");
|
|
|
55
55
|
var import_ui21 = require("@behindthemusictree/ui");
|
|
56
56
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
57
57
|
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
58
|
+
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
58
59
|
var import_popup4 = require("@behindthemusictree/app-kit/popup");
|
|
59
60
|
|
|
60
61
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -1402,6 +1403,29 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1402
1403
|
] });
|
|
1403
1404
|
}
|
|
1404
1405
|
|
|
1406
|
+
// src/genre-tree/CommonsPhoto.tsx
|
|
1407
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1408
|
+
function ExternalLink({ href, children }) {
|
|
1409
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1410
|
+
}
|
|
1411
|
+
function CommonsPhoto({
|
|
1412
|
+
image,
|
|
1413
|
+
alt,
|
|
1414
|
+
creditLabel = "Photo"
|
|
1415
|
+
}) {
|
|
1416
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
|
|
1417
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
1419
|
+
creditLabel,
|
|
1420
|
+
": ",
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1422
|
+
",",
|
|
1423
|
+
" ",
|
|
1424
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1425
|
+
] })
|
|
1426
|
+
] });
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1405
1429
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1406
1430
|
var import_react13 = require("react");
|
|
1407
1431
|
var import_react_query = require("@tanstack/react-query");
|
|
@@ -1424,15 +1448,15 @@ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
1424
1448
|
|
|
1425
1449
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1426
1450
|
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1427
|
-
var
|
|
1451
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1428
1452
|
function MusicBrainzRecordingLink({
|
|
1429
1453
|
song,
|
|
1430
1454
|
children,
|
|
1431
1455
|
className: className2 = ""
|
|
1432
1456
|
}) {
|
|
1433
1457
|
const url = (0, import_genre_tree11.musicBrainzRecordingUrl)(song);
|
|
1434
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1435
|
-
return /* @__PURE__ */ (0,
|
|
1458
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children });
|
|
1459
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1436
1460
|
"a",
|
|
1437
1461
|
{
|
|
1438
1462
|
href: url,
|
|
@@ -1449,7 +1473,7 @@ function MusicBrainzRecordingLink({
|
|
|
1449
1473
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1450
1474
|
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1451
1475
|
var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
|
|
1452
|
-
var
|
|
1476
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1453
1477
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1454
1478
|
var SongPageSchema = (0, import_transport3.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
|
|
1455
1479
|
overallTotal: import_zod.z.number().nullable(),
|
|
@@ -1536,35 +1560,35 @@ function GenrePlaylistTracks({
|
|
|
1536
1560
|
started.catch((error) => {
|
|
1537
1561
|
console.error("Failed to play genre playlist:", error);
|
|
1538
1562
|
if (error instanceof import_genre_tree16.TracksFailureAlreadyReportedError) return;
|
|
1539
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1563
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport5.AppError ? error.code : import_transport5.ErrorCode.CLIENT_UNKNOWN }));
|
|
1540
1564
|
});
|
|
1541
1565
|
};
|
|
1542
|
-
return /* @__PURE__ */ (0,
|
|
1566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { children: [
|
|
1543
1567
|
renderTracksToolbar?.(),
|
|
1544
|
-
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0,
|
|
1568
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { role: "alert", children: [
|
|
1545
1569
|
"Couldn't load ",
|
|
1546
1570
|
trackNoun.plural,
|
|
1547
1571
|
".",
|
|
1548
1572
|
" ",
|
|
1549
|
-
/* @__PURE__ */ (0,
|
|
1550
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1573
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1574
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
|
|
1551
1575
|
tracks.map((track, index) => {
|
|
1552
1576
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1553
1577
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1554
1578
|
const isPlaying = isCurrent && playState === import_player6.PlayStates.PLAYING;
|
|
1555
|
-
const text = /* @__PURE__ */ (0,
|
|
1556
|
-
/* @__PURE__ */ (0,
|
|
1557
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1558
|
-
/* @__PURE__ */ (0,
|
|
1559
|
-
track.album && /* @__PURE__ */ (0,
|
|
1579
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1581
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1582
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_genre_tree17.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1583
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1560
1584
|
] });
|
|
1561
1585
|
const leading = renderSongLeading?.(track, index);
|
|
1562
1586
|
const actions = renderSongActions?.(track);
|
|
1563
1587
|
const isUnplayable = (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
|
|
1564
1588
|
if (isUnplayable) {
|
|
1565
|
-
return /* @__PURE__ */ (0,
|
|
1589
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1566
1590
|
leading,
|
|
1567
|
-
/* @__PURE__ */ (0,
|
|
1591
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1568
1592
|
"button",
|
|
1569
1593
|
{
|
|
1570
1594
|
type: "button",
|
|
@@ -1572,27 +1596,27 @@ function GenrePlaylistTracks({
|
|
|
1572
1596
|
"aria-disabled": "true",
|
|
1573
1597
|
"aria-label": label,
|
|
1574
1598
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1575
|
-
children: /* @__PURE__ */ (0,
|
|
1599
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1576
1600
|
}
|
|
1577
1601
|
),
|
|
1578
1602
|
text,
|
|
1579
1603
|
actions
|
|
1580
1604
|
] }, track.uuid);
|
|
1581
1605
|
}
|
|
1582
|
-
return /* @__PURE__ */ (0,
|
|
1606
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
1583
1607
|
"li",
|
|
1584
1608
|
{
|
|
1585
1609
|
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)]" : ""}`,
|
|
1586
1610
|
children: [
|
|
1587
1611
|
leading,
|
|
1588
|
-
/* @__PURE__ */ (0,
|
|
1612
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1589
1613
|
"button",
|
|
1590
1614
|
{
|
|
1591
1615
|
type: "button",
|
|
1592
1616
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1593
1617
|
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"}`,
|
|
1594
1618
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1595
|
-
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1619
|
+
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa2.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1596
1620
|
}
|
|
1597
1621
|
),
|
|
1598
1622
|
text,
|
|
@@ -1602,7 +1626,7 @@ function GenrePlaylistTracks({
|
|
|
1602
1626
|
track.uuid
|
|
1603
1627
|
);
|
|
1604
1628
|
}),
|
|
1605
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1629
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1606
1630
|
] })
|
|
1607
1631
|
] }) });
|
|
1608
1632
|
}
|
|
@@ -1610,7 +1634,7 @@ function GenrePlaylistTracks({
|
|
|
1610
1634
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1611
1635
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1612
1636
|
var import_si2 = require("react-icons/si");
|
|
1613
|
-
var
|
|
1637
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1614
1638
|
function GenreDetailExtras({
|
|
1615
1639
|
genreUuid,
|
|
1616
1640
|
genrePlaylist,
|
|
@@ -1636,17 +1660,18 @@ function GenreDetailExtras({
|
|
|
1636
1660
|
criteriaOverviewSchema
|
|
1637
1661
|
);
|
|
1638
1662
|
if (isPending) {
|
|
1639
|
-
return /* @__PURE__ */ (0,
|
|
1640
|
-
/* @__PURE__ */ (0,
|
|
1663
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1664
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1641
1665
|
family
|
|
1642
1666
|
] });
|
|
1643
1667
|
}
|
|
1644
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1645
|
-
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1668
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_jsx_runtime29.Fragment, { children: family });
|
|
1669
|
+
const { summary, summarySourceUrl, essentialSongs, image } = overview;
|
|
1646
1670
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1647
|
-
return /* @__PURE__ */ (0,
|
|
1648
|
-
|
|
1649
|
-
|
|
1671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1672
|
+
image && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommonsPhoto, { image, alt: overview.name, creditLabel: "Image" }),
|
|
1673
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
|
|
1674
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1650
1675
|
"a",
|
|
1651
1676
|
{
|
|
1652
1677
|
href: summarySourceUrl,
|
|
@@ -1654,12 +1679,12 @@ function GenreDetailExtras({
|
|
|
1654
1679
|
rel: "noopener noreferrer",
|
|
1655
1680
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1656
1681
|
children: [
|
|
1657
|
-
/* @__PURE__ */ (0,
|
|
1682
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1658
1683
|
"Wikipedia"
|
|
1659
1684
|
]
|
|
1660
1685
|
}
|
|
1661
1686
|
),
|
|
1662
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1687
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1663
1688
|
"a",
|
|
1664
1689
|
{
|
|
1665
1690
|
href: wikidataUrl,
|
|
@@ -1667,17 +1692,17 @@ function GenreDetailExtras({
|
|
|
1667
1692
|
rel: "noopener noreferrer",
|
|
1668
1693
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1669
1694
|
children: [
|
|
1670
|
-
/* @__PURE__ */ (0,
|
|
1695
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1671
1696
|
"Wikidata"
|
|
1672
1697
|
]
|
|
1673
1698
|
}
|
|
1674
1699
|
)
|
|
1675
1700
|
] }) }),
|
|
1676
|
-
/* @__PURE__ */ (0,
|
|
1701
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1677
1702
|
family,
|
|
1678
1703
|
renderGenreDetailExtras?.(overview),
|
|
1679
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1680
|
-
/* @__PURE__ */ (0,
|
|
1704
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1681
1706
|
GenrePlaylistTracks,
|
|
1682
1707
|
{
|
|
1683
1708
|
genreUuid,
|
|
@@ -1695,10 +1720,10 @@ function GenreDetailExtras({
|
|
|
1695
1720
|
}
|
|
1696
1721
|
|
|
1697
1722
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1698
|
-
var
|
|
1723
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1699
1724
|
var import_react14 = require("react");
|
|
1700
1725
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1701
|
-
return /* @__PURE__ */ (0,
|
|
1726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
|
|
1702
1727
|
}
|
|
1703
1728
|
function GenreInfoPanelBody({
|
|
1704
1729
|
toolbar,
|
|
@@ -1715,9 +1740,9 @@ function GenreInfoPanelBody({
|
|
|
1715
1740
|
}) {
|
|
1716
1741
|
const summary = scope === "reference" ? import_genre_tree19.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
|
|
1717
1742
|
const selection = summary ? (0, import_genre_tree19.defaultGenreMusicSelection)(summary) : void 0;
|
|
1718
|
-
return /* @__PURE__ */ (0,
|
|
1743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1719
1744
|
toolbar,
|
|
1720
|
-
genrePlaylist ? /* @__PURE__ */ (0,
|
|
1745
|
+
genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreActionBand, { ...band }),
|
|
1721
1746
|
/* @__PURE__ */ (0, import_react14.createElement)(
|
|
1722
1747
|
GenreDetailExtras,
|
|
1723
1748
|
{
|
|
@@ -1737,7 +1762,7 @@ function GenreInfoPanelBody({
|
|
|
1737
1762
|
}
|
|
1738
1763
|
|
|
1739
1764
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
1740
|
-
var
|
|
1765
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1741
1766
|
var RELATIVE_ITEM_COUNT = 1;
|
|
1742
1767
|
function toNode(genre, parentId) {
|
|
1743
1768
|
return {
|
|
@@ -1762,7 +1787,8 @@ function useGenreDetailContent({
|
|
|
1762
1787
|
expanded,
|
|
1763
1788
|
treeStyles
|
|
1764
1789
|
}) {
|
|
1765
|
-
const
|
|
1790
|
+
const result = (0, import_genre_tree20.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
1791
|
+
const detail = result.status === "success" ? result.genre : void 0;
|
|
1766
1792
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
1767
1793
|
const playback = hostPlayback ?? defaultPlayback;
|
|
1768
1794
|
const view = (0, import_react15.useMemo)(() => {
|
|
@@ -1791,22 +1817,27 @@ function useGenreDetailContent({
|
|
|
1791
1817
|
)
|
|
1792
1818
|
};
|
|
1793
1819
|
}, [detail, scope, treeStyles]);
|
|
1794
|
-
if (
|
|
1820
|
+
if (result.status === "not-found") return {
|
|
1821
|
+
name: null,
|
|
1822
|
+
title: "Genre not found",
|
|
1823
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This genre does not exist." })
|
|
1824
|
+
};
|
|
1825
|
+
if (result.status === "error") return {
|
|
1795
1826
|
name: null,
|
|
1796
1827
|
title: "Genre unavailable",
|
|
1797
|
-
content: /* @__PURE__ */ (0,
|
|
1828
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
1798
1829
|
};
|
|
1799
|
-
if (
|
|
1830
|
+
if (!detail || !view) return {
|
|
1800
1831
|
name: null,
|
|
1801
1832
|
title: "Loading genre\u2026",
|
|
1802
|
-
content: /* @__PURE__ */ (0,
|
|
1803
|
-
/* @__PURE__ */ (0,
|
|
1804
|
-
/* @__PURE__ */ (0,
|
|
1805
|
-
/* @__PURE__ */ (0,
|
|
1833
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
1834
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.Skeleton, { className: "h-6 w-2/3" }),
|
|
1835
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.Skeleton, { className: "h-4 w-full" }),
|
|
1836
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.Skeleton, { className: "h-4 w-3/4" })
|
|
1806
1837
|
] })
|
|
1807
1838
|
};
|
|
1808
1839
|
const selectGenre = (nodeId) => onSelectGenre(import_genre_tree20.GenreUuidSchema.parse(nodeId));
|
|
1809
|
-
const family = /* @__PURE__ */ (0,
|
|
1840
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1810
1841
|
import_genre_tree_view4.GenreNodeFamily,
|
|
1811
1842
|
{
|
|
1812
1843
|
node: view.node,
|
|
@@ -1817,17 +1848,17 @@ function useGenreDetailContent({
|
|
|
1817
1848
|
onSelectNode: selectGenre
|
|
1818
1849
|
}
|
|
1819
1850
|
);
|
|
1820
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
1851
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
1821
1852
|
return {
|
|
1822
1853
|
name: detail.name,
|
|
1823
1854
|
title: detail.name,
|
|
1824
1855
|
headerAccent: { background: view.fill, color: view.textColor },
|
|
1825
1856
|
aside: expanded ? ancestors : void 0,
|
|
1826
|
-
content: /* @__PURE__ */ (0,
|
|
1827
|
-
/* @__PURE__ */ (0,
|
|
1857
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1858
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1828
1859
|
GenreInfoPanelBody,
|
|
1829
1860
|
{
|
|
1830
|
-
family: /* @__PURE__ */ (0,
|
|
1861
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1831
1862
|
family,
|
|
1832
1863
|
!expanded && ancestors
|
|
1833
1864
|
] }),
|
|
@@ -1871,7 +1902,7 @@ function GenreDetailPanel({
|
|
|
1871
1902
|
expanded,
|
|
1872
1903
|
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
1873
1904
|
});
|
|
1874
|
-
return /* @__PURE__ */ (0,
|
|
1905
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1875
1906
|
import_ui10.DetailPanel,
|
|
1876
1907
|
{
|
|
1877
1908
|
side: "left",
|
|
@@ -1884,7 +1915,7 @@ function GenreDetailPanel({
|
|
|
1884
1915
|
edgeTab,
|
|
1885
1916
|
aside: panel.aside,
|
|
1886
1917
|
titleInSearch,
|
|
1887
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
1918
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react9.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react9.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
1888
1919
|
children: panel.content
|
|
1889
1920
|
}
|
|
1890
1921
|
);
|
|
@@ -1893,7 +1924,7 @@ function GenreDetailPanel({
|
|
|
1893
1924
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1894
1925
|
var import_react16 = require("react");
|
|
1895
1926
|
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1896
|
-
var
|
|
1927
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1897
1928
|
function ChangeParentPopup({
|
|
1898
1929
|
genrePlaylist,
|
|
1899
1930
|
genrePlaylists,
|
|
@@ -1928,12 +1959,12 @@ function ChangeParentPopup({
|
|
|
1928
1959
|
window.addEventListener("keydown", handleKeyDown);
|
|
1929
1960
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1930
1961
|
}, [onClose]);
|
|
1931
|
-
return /* @__PURE__ */ (0,
|
|
1932
|
-
/* @__PURE__ */ (0,
|
|
1962
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
|
|
1933
1964
|
"Current parent: ",
|
|
1934
1965
|
genrePlaylist.parent?.name ?? "none"
|
|
1935
1966
|
] }),
|
|
1936
|
-
/* @__PURE__ */ (0,
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1937
1968
|
GenreSearch,
|
|
1938
1969
|
{
|
|
1939
1970
|
genrePlaylists: candidates,
|
|
@@ -1941,7 +1972,7 @@ function ChangeParentPopup({
|
|
|
1941
1972
|
placeholder: "Search a new parent\u2026"
|
|
1942
1973
|
}
|
|
1943
1974
|
) }),
|
|
1944
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1975
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1945
1976
|
] });
|
|
1946
1977
|
}
|
|
1947
1978
|
|
|
@@ -1954,7 +1985,7 @@ var import_lucide_react10 = require("lucide-react");
|
|
|
1954
1985
|
var import_ui11 = require("@behindthemusictree/ui");
|
|
1955
1986
|
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1956
1987
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1957
|
-
var
|
|
1988
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1958
1989
|
function ArtistActionBand({ artist, share }) {
|
|
1959
1990
|
const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree22.useTrackList)();
|
|
1960
1991
|
const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
|
|
@@ -1970,11 +2001,11 @@ function ArtistActionBand({ artist, share }) {
|
|
|
1970
2001
|
console.error("Failed to load artist tracks:", error);
|
|
1971
2002
|
});
|
|
1972
2003
|
};
|
|
1973
|
-
return /* @__PURE__ */ (0,
|
|
1974
|
-
/* @__PURE__ */ (0,
|
|
2004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ui11.ActionBand, { children: [
|
|
2005
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1975
2006
|
import_ui11.ActionButton,
|
|
1976
2007
|
{
|
|
1977
|
-
icon: playing ? /* @__PURE__ */ (0,
|
|
2008
|
+
icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1978
2009
|
label: playing ? "Pause" : "Play",
|
|
1979
2010
|
caption: String(artist.playableTracksCount),
|
|
1980
2011
|
disabled: artist.playableTracksCount === 0,
|
|
@@ -1993,7 +2024,7 @@ var import_ui13 = require("@behindthemusictree/ui");
|
|
|
1993
2024
|
var import_lucide_react11 = require("lucide-react");
|
|
1994
2025
|
var import_si3 = require("react-icons/si");
|
|
1995
2026
|
var import_ui12 = require("@behindthemusictree/ui");
|
|
1996
|
-
var
|
|
2027
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1997
2028
|
var LINK_KINDS = {
|
|
1998
2029
|
official: { label: "Official website", Icon: import_lucide_react11.Globe },
|
|
1999
2030
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -2030,7 +2061,7 @@ function artistLinks(artist) {
|
|
|
2030
2061
|
return links;
|
|
2031
2062
|
}
|
|
2032
2063
|
function ArtistLinks({ links }) {
|
|
2033
|
-
return /* @__PURE__ */ (0,
|
|
2064
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
2034
2065
|
}
|
|
2035
2066
|
|
|
2036
2067
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2041,7 +2072,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2041
2072
|
}
|
|
2042
2073
|
|
|
2043
2074
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2044
|
-
var
|
|
2075
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2045
2076
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2046
2077
|
function ArtistHeader({ artist }) {
|
|
2047
2078
|
const fields = [];
|
|
@@ -2051,35 +2082,23 @@ function ArtistHeader({ artist }) {
|
|
|
2051
2082
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2052
2083
|
if (years) fields.push({ label: "Active", value: years });
|
|
2053
2084
|
const links = artistLinks(artist);
|
|
2054
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2085
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistLinks, { links }) });
|
|
2055
2086
|
if (fields.length === 0) return null;
|
|
2056
|
-
return /* @__PURE__ */ (0,
|
|
2087
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.FieldList, { items: fields });
|
|
2057
2088
|
}
|
|
2058
2089
|
|
|
2059
2090
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2060
|
-
var
|
|
2061
|
-
function ExternalLink({ href, children }) {
|
|
2062
|
-
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
2063
|
-
}
|
|
2091
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2064
2092
|
function ArtistPhoto({ image, name }) {
|
|
2065
|
-
return /* @__PURE__ */ (0,
|
|
2066
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
2067
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
2068
|
-
"Photo: ",
|
|
2069
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
2070
|
-
",",
|
|
2071
|
-
" ",
|
|
2072
|
-
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
2073
|
-
] })
|
|
2074
|
-
] });
|
|
2093
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommonsPhoto, { image, alt: name });
|
|
2075
2094
|
}
|
|
2076
2095
|
|
|
2077
2096
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2078
2097
|
var import_ui14 = require("@behindthemusictree/ui");
|
|
2079
|
-
var
|
|
2098
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2080
2099
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2081
2100
|
if (memberships.length === 0) return null;
|
|
2082
|
-
return /* @__PURE__ */ (0,
|
|
2101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2083
2102
|
const details = [
|
|
2084
2103
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2085
2104
|
...membership.roles
|
|
@@ -2087,9 +2106,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2087
2106
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
2088
2107
|
return (
|
|
2089
2108
|
// A member can have several periods in the same group.
|
|
2090
|
-
/* @__PURE__ */ (0,
|
|
2091
|
-
/* @__PURE__ */ (0,
|
|
2092
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2111
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2093
2112
|
" \u2014 ",
|
|
2094
2113
|
detailsText
|
|
2095
2114
|
] })
|
|
@@ -2100,9 +2119,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2100
2119
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2101
2120
|
if (memberships.length === 0) return null;
|
|
2102
2121
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2103
|
-
return /* @__PURE__ */ (0,
|
|
2104
|
-
/* @__PURE__ */ (0,
|
|
2105
|
-
/* @__PURE__ */ (0,
|
|
2122
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ui14.PanelSection, { title, children: [
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2124
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2106
2125
|
MembershipList,
|
|
2107
2126
|
{
|
|
2108
2127
|
title: "Past",
|
|
@@ -2115,7 +2134,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2115
2134
|
|
|
2116
2135
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2117
2136
|
var import_ui15 = require("@behindthemusictree/ui");
|
|
2118
|
-
var
|
|
2137
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2119
2138
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2120
2139
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2121
2140
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2135,12 +2154,12 @@ function byYear(a, b) {
|
|
|
2135
2154
|
function ArtistDiscography({ albums }) {
|
|
2136
2155
|
if (albums.length === 0) return null;
|
|
2137
2156
|
const sorted = [...albums].sort(byYear);
|
|
2138
|
-
return /* @__PURE__ */ (0,
|
|
2157
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2139
2158
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2140
2159
|
if (groupAlbums.length === 0) return null;
|
|
2141
|
-
return /* @__PURE__ */ (0,
|
|
2142
|
-
/* @__PURE__ */ (0,
|
|
2143
|
-
/* @__PURE__ */ (0,
|
|
2160
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2161
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2162
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2144
2163
|
] }, album.uuid)) }) }, group);
|
|
2145
2164
|
}) });
|
|
2146
2165
|
}
|
|
@@ -2161,7 +2180,7 @@ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
2161
2180
|
var import_fa3 = require("react-icons/fa");
|
|
2162
2181
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2163
2182
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
2164
|
-
var
|
|
2183
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2165
2184
|
function TrackPositionPlayPause({
|
|
2166
2185
|
position,
|
|
2167
2186
|
uuid,
|
|
@@ -2175,14 +2194,14 @@ function TrackPositionPlayPause({
|
|
|
2175
2194
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PLAYING;
|
|
2176
2195
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PAUSED;
|
|
2177
2196
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2178
|
-
return /* @__PURE__ */ (0,
|
|
2197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2179
2198
|
"div",
|
|
2180
2199
|
{
|
|
2181
2200
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2182
2201
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2183
2202
|
onClick: handlePlayPauseClick,
|
|
2184
2203
|
children: [
|
|
2185
|
-
/* @__PURE__ */ (0,
|
|
2204
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2186
2205
|
"span",
|
|
2187
2206
|
{
|
|
2188
2207
|
className: "track-position-play-pause__bars",
|
|
@@ -2190,18 +2209,18 @@ function TrackPositionPlayPause({
|
|
|
2190
2209
|
role: "img",
|
|
2191
2210
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2192
2211
|
children: [
|
|
2193
|
-
/* @__PURE__ */ (0,
|
|
2194
|
-
/* @__PURE__ */ (0,
|
|
2195
|
-
/* @__PURE__ */ (0,
|
|
2212
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2213
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2214
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2196
2215
|
]
|
|
2197
2216
|
}
|
|
2198
2217
|
) : position }),
|
|
2199
|
-
/* @__PURE__ */ (0,
|
|
2218
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2200
2219
|
"div",
|
|
2201
2220
|
{
|
|
2202
2221
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2203
2222
|
"aria-hidden": "true",
|
|
2204
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2223
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_fa3.FaPlay, {})
|
|
2205
2224
|
}
|
|
2206
2225
|
)
|
|
2207
2226
|
]
|
|
@@ -2212,7 +2231,7 @@ function TrackPositionPlayPause({
|
|
|
2212
2231
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2213
2232
|
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2214
2233
|
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2215
|
-
var
|
|
2234
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2216
2235
|
function TrackItem({
|
|
2217
2236
|
track,
|
|
2218
2237
|
position,
|
|
@@ -2236,12 +2255,12 @@ function TrackItem({
|
|
|
2236
2255
|
loadTrackForPlayer(track.uuid);
|
|
2237
2256
|
}
|
|
2238
2257
|
};
|
|
2239
|
-
return /* @__PURE__ */ (0,
|
|
2258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
2240
2259
|
"div",
|
|
2241
2260
|
{
|
|
2242
2261
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2243
2262
|
children: [
|
|
2244
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2263
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2245
2264
|
"div",
|
|
2246
2265
|
{
|
|
2247
2266
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2249,7 +2268,7 @@ function TrackItem({
|
|
|
2249
2268
|
"aria-disabled": "true",
|
|
2250
2269
|
children: position
|
|
2251
2270
|
}
|
|
2252
|
-
) : /* @__PURE__ */ (0,
|
|
2271
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2253
2272
|
TrackPositionPlayPause,
|
|
2254
2273
|
{
|
|
2255
2274
|
position,
|
|
@@ -2258,12 +2277,12 @@ function TrackItem({
|
|
|
2258
2277
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2259
2278
|
}
|
|
2260
2279
|
),
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
2262
|
-
/* @__PURE__ */ (0,
|
|
2263
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2280
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2281
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime40.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_runtime40.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2282
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree25.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2264
2283
|
] }),
|
|
2265
|
-
/* @__PURE__ */ (0,
|
|
2266
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2284
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.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_runtime40.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
|
|
2285
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2267
2286
|
"div",
|
|
2268
2287
|
{
|
|
2269
2288
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2271,14 +2290,14 @@ function TrackItem({
|
|
|
2271
2290
|
children: renderDuration(track)
|
|
2272
2291
|
}
|
|
2273
2292
|
),
|
|
2274
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2293
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2275
2294
|
]
|
|
2276
2295
|
}
|
|
2277
2296
|
);
|
|
2278
2297
|
}
|
|
2279
2298
|
|
|
2280
2299
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2281
|
-
var
|
|
2300
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2282
2301
|
var SongPageSchema2 = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
|
|
2283
2302
|
var SORTS = [
|
|
2284
2303
|
{ ordering: "title", label: "Title" },
|
|
@@ -2311,8 +2330,8 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2311
2330
|
event.stopPropagation();
|
|
2312
2331
|
void play(song.uuid);
|
|
2313
2332
|
};
|
|
2314
|
-
return /* @__PURE__ */ (0,
|
|
2315
|
-
/* @__PURE__ */ (0,
|
|
2333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2334
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2316
2335
|
"button",
|
|
2317
2336
|
{
|
|
2318
2337
|
type: "button",
|
|
@@ -2326,7 +2345,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2326
2345
|
},
|
|
2327
2346
|
sort.ordering
|
|
2328
2347
|
)) }) }),
|
|
2329
|
-
/* @__PURE__ */ (0,
|
|
2348
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2330
2349
|
TrackItem,
|
|
2331
2350
|
{
|
|
2332
2351
|
track: song,
|
|
@@ -2335,12 +2354,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2335
2354
|
renderSongLink
|
|
2336
2355
|
}
|
|
2337
2356
|
) }, song.uuid)) }),
|
|
2338
|
-
/* @__PURE__ */ (0,
|
|
2357
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2339
2358
|
] }) });
|
|
2340
2359
|
}
|
|
2341
2360
|
|
|
2342
2361
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2343
|
-
var
|
|
2362
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2344
2363
|
function useArtistPanel({
|
|
2345
2364
|
uuid,
|
|
2346
2365
|
scope,
|
|
@@ -2361,26 +2380,26 @@ function useArtistPanel({
|
|
|
2361
2380
|
return {
|
|
2362
2381
|
...panel,
|
|
2363
2382
|
title: "Artist",
|
|
2364
|
-
content: /* @__PURE__ */ (0,
|
|
2365
|
-
/* @__PURE__ */ (0,
|
|
2366
|
-
/* @__PURE__ */ (0,
|
|
2367
|
-
/* @__PURE__ */ (0,
|
|
2368
|
-
/* @__PURE__ */ (0,
|
|
2369
|
-
/* @__PURE__ */ (0,
|
|
2370
|
-
/* @__PURE__ */ (0,
|
|
2383
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2384
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2385
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
|
|
2386
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" }),
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2389
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-16" })
|
|
2371
2390
|
] })
|
|
2372
2391
|
};
|
|
2373
2392
|
case "not-found":
|
|
2374
2393
|
return {
|
|
2375
2394
|
...panel,
|
|
2376
2395
|
title: "Artist not found",
|
|
2377
|
-
content: /* @__PURE__ */ (0,
|
|
2396
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2378
2397
|
};
|
|
2379
2398
|
case "error":
|
|
2380
2399
|
return {
|
|
2381
2400
|
...panel,
|
|
2382
2401
|
title: "Artist",
|
|
2383
|
-
content: /* @__PURE__ */ (0,
|
|
2402
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
|
|
2384
2403
|
};
|
|
2385
2404
|
case "success": {
|
|
2386
2405
|
const { artist } = result;
|
|
@@ -2389,19 +2408,19 @@ function useArtistPanel({
|
|
|
2389
2408
|
return {
|
|
2390
2409
|
...panel,
|
|
2391
2410
|
title: artist.name,
|
|
2392
|
-
content: /* @__PURE__ */ (0,
|
|
2393
|
-
/* @__PURE__ */ (0,
|
|
2411
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2394
2413
|
ArtistActionBand,
|
|
2395
2414
|
{
|
|
2396
2415
|
artist,
|
|
2397
|
-
share: shareTarget && shareUrl ? /* @__PURE__ */ (0,
|
|
2416
|
+
share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
|
|
2398
2417
|
}
|
|
2399
2418
|
),
|
|
2400
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2401
|
-
/* @__PURE__ */ (0,
|
|
2402
|
-
/* @__PURE__ */ (0,
|
|
2403
|
-
/* @__PURE__ */ (0,
|
|
2404
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2419
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2420
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistHeader, { artist }),
|
|
2421
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2423
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2405
2424
|
ArtistSongs,
|
|
2406
2425
|
{
|
|
2407
2426
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2411,7 +2430,7 @@ function useArtistPanel({
|
|
|
2411
2430
|
}
|
|
2412
2431
|
)
|
|
2413
2432
|
] }),
|
|
2414
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2433
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2415
2434
|
};
|
|
2416
2435
|
}
|
|
2417
2436
|
}
|
|
@@ -2427,7 +2446,7 @@ var import_lucide_react12 = require("lucide-react");
|
|
|
2427
2446
|
var import_ui18 = require("@behindthemusictree/ui");
|
|
2428
2447
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
2429
2448
|
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2430
|
-
var
|
|
2449
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2431
2450
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2432
2451
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2433
2452
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
@@ -2435,27 +2454,27 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
2435
2454
|
const [open, setOpen] = (0, import_react18.useState)(false);
|
|
2436
2455
|
const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2437
2456
|
let body = null;
|
|
2438
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2439
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2457
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2458
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2440
2459
|
else if (history.status === "success")
|
|
2441
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2442
|
-
/* @__PURE__ */ (0,
|
|
2443
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2460
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: "py-1.5 text-body", children: [
|
|
2461
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2462
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2444
2463
|
" ",
|
|
2445
2464
|
"\u2014 ",
|
|
2446
2465
|
entry.oldValue ?? "none",
|
|
2447
2466
|
" \u2192 ",
|
|
2448
2467
|
entry.newValue ?? "none"
|
|
2449
2468
|
] }) : null,
|
|
2450
|
-
/* @__PURE__ */ (0,
|
|
2469
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
|
|
2451
2470
|
entry.actorPseudo ?? "Anonymous",
|
|
2452
2471
|
" \xB7 ",
|
|
2453
2472
|
dateFormat.format(new Date(entry.createdOn))
|
|
2454
2473
|
] })
|
|
2455
2474
|
] }, entry.uuid)) });
|
|
2456
|
-
return /* @__PURE__ */ (0,
|
|
2457
|
-
/* @__PURE__ */ (0,
|
|
2458
|
-
/* @__PURE__ */ (0,
|
|
2475
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
|
|
2476
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui18.PANEL_LABEL_CLASS}`, children: "History" }),
|
|
2477
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "mt-1.5", children: body })
|
|
2459
2478
|
] }) });
|
|
2460
2479
|
}
|
|
2461
2480
|
function SongPanelContent(props) {
|
|
@@ -2474,13 +2493,13 @@ function SongPanelContent(props) {
|
|
|
2474
2493
|
const genre = song.genre;
|
|
2475
2494
|
fields.push({
|
|
2476
2495
|
label: "Genre",
|
|
2477
|
-
value: onSelectGenre ? /* @__PURE__ */ (0,
|
|
2478
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2479
|
-
/* @__PURE__ */ (0,
|
|
2496
|
+
value: onSelectGenre ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
|
|
2497
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2498
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2480
2499
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2481
2500
|
});
|
|
2482
2501
|
} else {
|
|
2483
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2502
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2484
2503
|
}
|
|
2485
2504
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2486
2505
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2490,11 +2509,11 @@ function SongPanelContent(props) {
|
|
|
2490
2509
|
const links = [];
|
|
2491
2510
|
if (musicBrainzUrl)
|
|
2492
2511
|
links.push(
|
|
2493
|
-
/* @__PURE__ */ (0,
|
|
2512
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2494
2513
|
);
|
|
2495
2514
|
if (song.youtubeVideo)
|
|
2496
2515
|
links.push(
|
|
2497
|
-
/* @__PURE__ */ (0,
|
|
2516
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2498
2517
|
"a",
|
|
2499
2518
|
{
|
|
2500
2519
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2509,31 +2528,31 @@ function SongPanelContent(props) {
|
|
|
2509
2528
|
if (links.length > 0)
|
|
2510
2529
|
fields.push({
|
|
2511
2530
|
label: "Links",
|
|
2512
|
-
value: /* @__PURE__ */ (0,
|
|
2531
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex gap-3", children: links })
|
|
2513
2532
|
});
|
|
2514
|
-
return /* @__PURE__ */ (0,
|
|
2515
|
-
/* @__PURE__ */ (0,
|
|
2516
|
-
/* @__PURE__ */ (0,
|
|
2533
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
|
|
2534
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui18.ActionBand, { children: [
|
|
2535
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2517
2536
|
import_ui18.ActionButton,
|
|
2518
2537
|
{
|
|
2519
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
2538
|
+
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
2520
2539
|
label: isPlaying ? "Pause" : "Play",
|
|
2521
2540
|
disabled: !!unplayableReason,
|
|
2522
2541
|
title: unplayableReason ?? void 0,
|
|
2523
2542
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
|
|
2524
2543
|
}
|
|
2525
2544
|
),
|
|
2526
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2545
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2527
2546
|
] }) }),
|
|
2528
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
2530
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2531
|
-
/* @__PURE__ */ (0,
|
|
2547
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2548
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.FieldList, { items: fields }),
|
|
2549
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2550
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2532
2551
|
] });
|
|
2533
2552
|
}
|
|
2534
2553
|
|
|
2535
2554
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2536
|
-
var
|
|
2555
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2537
2556
|
function useSongPanel({
|
|
2538
2557
|
uuid,
|
|
2539
2558
|
scope,
|
|
@@ -2554,24 +2573,24 @@ function useSongPanel({
|
|
|
2554
2573
|
return {
|
|
2555
2574
|
...panel,
|
|
2556
2575
|
title: "Song",
|
|
2557
|
-
content: /* @__PURE__ */ (0,
|
|
2558
|
-
/* @__PURE__ */ (0,
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2560
|
-
/* @__PURE__ */ (0,
|
|
2561
|
-
/* @__PURE__ */ (0,
|
|
2576
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
|
|
2578
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-4 w-32" }),
|
|
2579
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
|
|
2580
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-4 w-24" })
|
|
2562
2581
|
] })
|
|
2563
2582
|
};
|
|
2564
2583
|
case "not-found":
|
|
2565
2584
|
return {
|
|
2566
2585
|
...panel,
|
|
2567
2586
|
title: "Song not found",
|
|
2568
|
-
content: /* @__PURE__ */ (0,
|
|
2587
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2569
2588
|
};
|
|
2570
2589
|
case "error":
|
|
2571
2590
|
return {
|
|
2572
2591
|
...panel,
|
|
2573
2592
|
title: "Song",
|
|
2574
|
-
content: /* @__PURE__ */ (0,
|
|
2593
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
|
|
2575
2594
|
};
|
|
2576
2595
|
case "success": {
|
|
2577
2596
|
const { song } = result;
|
|
@@ -2579,7 +2598,7 @@ function useSongPanel({
|
|
|
2579
2598
|
return {
|
|
2580
2599
|
...panel,
|
|
2581
2600
|
title: song.title,
|
|
2582
|
-
content: /* @__PURE__ */ (0,
|
|
2601
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2583
2602
|
SongPanelContent,
|
|
2584
2603
|
{
|
|
2585
2604
|
song,
|
|
@@ -2632,12 +2651,12 @@ function useEntityPanel({
|
|
|
2632
2651
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2633
2652
|
var import_md = require("react-icons/md");
|
|
2634
2653
|
var import_ui20 = require("@behindthemusictree/ui");
|
|
2635
|
-
var
|
|
2654
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2636
2655
|
function SizeMetricToggle({
|
|
2637
2656
|
value,
|
|
2638
2657
|
onChange
|
|
2639
2658
|
}) {
|
|
2640
|
-
return /* @__PURE__ */ (0,
|
|
2659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2641
2660
|
import_ui20.SegmentedControl,
|
|
2642
2661
|
{
|
|
2643
2662
|
variant: "floating",
|
|
@@ -2645,15 +2664,15 @@ function SizeMetricToggle({
|
|
|
2645
2664
|
value,
|
|
2646
2665
|
onChange,
|
|
2647
2666
|
options: [
|
|
2648
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2649
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2667
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2668
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2650
2669
|
]
|
|
2651
2670
|
}
|
|
2652
2671
|
);
|
|
2653
2672
|
}
|
|
2654
2673
|
|
|
2655
2674
|
// src/genre-tree/GenreTreeView.tsx
|
|
2656
|
-
var
|
|
2675
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2657
2676
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2658
2677
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2659
2678
|
var noop = () => {
|
|
@@ -2761,7 +2780,7 @@ function GenreTreeView({
|
|
|
2761
2780
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2762
2781
|
toggleWheelRotation,
|
|
2763
2782
|
onNodeHover: handleNodeHover
|
|
2764
|
-
} = (0,
|
|
2783
|
+
} = (0, import_genre_tree31.useGenreTreeViewState)({
|
|
2765
2784
|
scope,
|
|
2766
2785
|
getBackendBaseUrl,
|
|
2767
2786
|
viewMode: controlledViewMode,
|
|
@@ -2780,12 +2799,12 @@ function GenreTreeView({
|
|
|
2780
2799
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2781
2800
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2782
2801
|
}
|
|
2783
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2784
|
-
const { mutate: updateGenre } = (0,
|
|
2802
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree31.genreUpdateMutationKey }) > 0;
|
|
2803
|
+
const { mutate: updateGenre } = (0, import_genre_tree31.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2785
2804
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2786
2805
|
const isArtistFiltered = artistUuid !== null;
|
|
2787
2806
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2788
|
-
const artistResult = (0,
|
|
2807
|
+
const artistResult = (0, import_genre_tree31.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2789
2808
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2790
2809
|
const filterArtist = (0, import_react19.useMemo)(
|
|
2791
2810
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -2805,14 +2824,14 @@ function GenreTreeView({
|
|
|
2805
2824
|
const openSongRef = (0, import_react19.useRef)(openSong);
|
|
2806
2825
|
openSongRef.current = openSong;
|
|
2807
2826
|
const resolvedRenderSongLink = (0, import_react19.useCallback)(
|
|
2808
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2827
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2809
2828
|
[renderSongLink]
|
|
2810
2829
|
);
|
|
2811
2830
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react19.useState)(null);
|
|
2812
2831
|
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react19.useState)(null);
|
|
2813
2832
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2814
2833
|
const songBaseUrl = getBackendBaseUrl;
|
|
2815
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2834
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2816
2835
|
const getGenrePath = (genre) => {
|
|
2817
2836
|
const path = [];
|
|
2818
2837
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2889,9 +2908,10 @@ function GenreTreeView({
|
|
|
2889
2908
|
resolveStyle
|
|
2890
2909
|
);
|
|
2891
2910
|
const treeStyles = (0, import_react19.useMemo)(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
|
|
2892
|
-
const
|
|
2893
|
-
|
|
2894
|
-
|
|
2911
|
+
const validSelectedGenreUuid = (0, import_genre_tree30.toGenreUuid)(selectedGenreUuid);
|
|
2912
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react19.useState)({ current: validSelectedGenreUuid, previous: null });
|
|
2913
|
+
if (selectionHistory.current !== validSelectedGenreUuid) {
|
|
2914
|
+
setSelectionHistory({ current: validSelectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
2895
2915
|
}
|
|
2896
2916
|
const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
|
|
2897
2917
|
const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
|
|
@@ -2923,7 +2943,7 @@ function GenreTreeView({
|
|
|
2923
2943
|
const canToggleSide = !readOnly && criteria !== void 0 && criteria !== null && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
2924
2944
|
const side = criteria?.side ?? "core";
|
|
2925
2945
|
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find((sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist?.uuid && sibling.criteria?.side === "pop") : void 0;
|
|
2926
|
-
const shareTarget =
|
|
2946
|
+
const shareTarget = validSelectedGenreUuid ? { kind: "genre", genreUuid: validSelectedGenreUuid, name: criteria?.name ?? "Genre", scope, artistUuid } : void 0;
|
|
2927
2947
|
const shareUrl = shareTarget ? getShareUrlRef.current?.(shareTarget) ?? void 0 : void 0;
|
|
2928
2948
|
const genreContent = useGenreDetailContent({
|
|
2929
2949
|
genreUuid: selectedGenreUuid,
|
|
@@ -2945,7 +2965,7 @@ function GenreTreeView({
|
|
|
2945
2965
|
onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2946
2966
|
onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2947
2967
|
onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
|
|
2948
|
-
/* @__PURE__ */ (0,
|
|
2968
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2949
2969
|
ChangeParentPopup,
|
|
2950
2970
|
{
|
|
2951
2971
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2989,7 +3009,7 @@ function GenreTreeView({
|
|
|
2989
3009
|
onClose: otherPanel.onClose,
|
|
2990
3010
|
searchName: otherPanel.title
|
|
2991
3011
|
};
|
|
2992
|
-
} else if (selectedGenreUuid) {
|
|
3012
|
+
} else if (selectedGenreUuid !== null) {
|
|
2993
3013
|
activePanel = {
|
|
2994
3014
|
title: genreContent.title,
|
|
2995
3015
|
content: genreContent.content,
|
|
@@ -3006,19 +3026,19 @@ function GenreTreeView({
|
|
|
3006
3026
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
3007
3027
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
3008
3028
|
} : void 0;
|
|
3009
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
3029
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3010
3030
|
import_ui21.IconButton,
|
|
3011
3031
|
{
|
|
3012
3032
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
3013
3033
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
3014
3034
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
3015
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3035
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react14.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react14.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3016
3036
|
}
|
|
3017
3037
|
) : null;
|
|
3018
3038
|
const renderArtistFilter = () => {
|
|
3019
3039
|
if (isLoading || !filterArtist) return null;
|
|
3020
|
-
return /* @__PURE__ */ (0,
|
|
3021
|
-
/* @__PURE__ */ (0,
|
|
3040
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
|
|
3041
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3022
3042
|
"button",
|
|
3023
3043
|
{
|
|
3024
3044
|
type: "button",
|
|
@@ -3030,7 +3050,7 @@ function GenreTreeView({
|
|
|
3030
3050
|
children: filterArtist.name
|
|
3031
3051
|
}
|
|
3032
3052
|
),
|
|
3033
|
-
/* @__PURE__ */ (0,
|
|
3053
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3034
3054
|
"button",
|
|
3035
3055
|
{
|
|
3036
3056
|
type: "button",
|
|
@@ -3042,8 +3062,8 @@ function GenreTreeView({
|
|
|
3042
3062
|
)
|
|
3043
3063
|
] });
|
|
3044
3064
|
};
|
|
3045
|
-
const actions = /* @__PURE__ */ (0,
|
|
3046
|
-
!isLoading && !isError && /* @__PURE__ */ (0,
|
|
3065
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3066
|
+
!isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3047
3067
|
GenreTreeSearch,
|
|
3048
3068
|
{
|
|
3049
3069
|
artistBaseUrl,
|
|
@@ -3076,9 +3096,9 @@ function GenreTreeView({
|
|
|
3076
3096
|
extraGroups: extraSearchGroups
|
|
3077
3097
|
}
|
|
3078
3098
|
),
|
|
3079
|
-
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0,
|
|
3080
|
-
/* @__PURE__ */ (0,
|
|
3081
|
-
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0,
|
|
3099
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3100
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
3101
|
+
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3082
3102
|
import_ui21.SegmentedControl,
|
|
3083
3103
|
{
|
|
3084
3104
|
"aria-label": "Tree view mode",
|
|
@@ -3092,13 +3112,13 @@ function GenreTreeView({
|
|
|
3092
3112
|
]
|
|
3093
3113
|
}
|
|
3094
3114
|
),
|
|
3095
|
-
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3115
|
+
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3096
3116
|
"div",
|
|
3097
3117
|
{
|
|
3098
3118
|
className: "flex items-center gap-1",
|
|
3099
3119
|
role: "group",
|
|
3100
3120
|
"aria-label": "Tree display options",
|
|
3101
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3121
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3102
3122
|
import_ui21.Button,
|
|
3103
3123
|
{
|
|
3104
3124
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -3109,7 +3129,7 @@ function GenreTreeView({
|
|
|
3109
3129
|
)
|
|
3110
3130
|
}
|
|
3111
3131
|
),
|
|
3112
|
-
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3132
|
+
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3113
3133
|
import_ui21.IconTextButton,
|
|
3114
3134
|
{
|
|
3115
3135
|
icon: import_lucide_react13.Plus,
|
|
@@ -3119,38 +3139,38 @@ function GenreTreeView({
|
|
|
3119
3139
|
)
|
|
3120
3140
|
] })
|
|
3121
3141
|
] });
|
|
3122
|
-
return /* @__PURE__ */ (0,
|
|
3142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3123
3143
|
"div",
|
|
3124
3144
|
{
|
|
3125
3145
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3126
3146
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3127
3147
|
children: [
|
|
3128
|
-
/* @__PURE__ */ (0,
|
|
3148
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3129
3149
|
"div",
|
|
3130
3150
|
{
|
|
3131
3151
|
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"}`,
|
|
3132
3152
|
children: actions
|
|
3133
3153
|
}
|
|
3134
3154
|
),
|
|
3135
|
-
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0,
|
|
3155
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3136
3156
|
"div",
|
|
3137
3157
|
{
|
|
3138
3158
|
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"}`,
|
|
3139
3159
|
children: renderArtistFilter()
|
|
3140
3160
|
}
|
|
3141
3161
|
),
|
|
3142
|
-
/* @__PURE__ */ (0,
|
|
3143
|
-
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
3144
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3162
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3163
|
+
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3164
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
|
|
3145
3165
|
"Couldn't load the genre tree.",
|
|
3146
3166
|
" ",
|
|
3147
|
-
/* @__PURE__ */ (0,
|
|
3167
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
|
|
3148
3168
|
] }) : viewMode === "wheel" ? (
|
|
3149
3169
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3150
3170
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3151
3171
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3152
3172
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3153
|
-
/* @__PURE__ */ (0,
|
|
3173
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3154
3174
|
GenrePlaylistTreeWheel,
|
|
3155
3175
|
{
|
|
3156
3176
|
scope,
|
|
@@ -3168,9 +3188,9 @@ function GenreTreeView({
|
|
|
3168
3188
|
allowWheelRotation,
|
|
3169
3189
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3170
3190
|
busy,
|
|
3171
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3191
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3172
3192
|
floatingControlsLeading,
|
|
3173
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3193
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3174
3194
|
] }) : void 0,
|
|
3175
3195
|
defaultViewport,
|
|
3176
3196
|
viewportRestore,
|
|
@@ -3179,7 +3199,7 @@ function GenreTreeView({
|
|
|
3179
3199
|
) }) })
|
|
3180
3200
|
) : viewMode === "pop-core" ? (
|
|
3181
3201
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3182
|
-
/* @__PURE__ */ (0,
|
|
3202
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3183
3203
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3184
3204
|
{
|
|
3185
3205
|
scope,
|
|
@@ -3197,9 +3217,9 @@ function GenreTreeView({
|
|
|
3197
3217
|
allowWheelRotation,
|
|
3198
3218
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3199
3219
|
busy,
|
|
3200
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3220
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3201
3221
|
floatingControlsLeading,
|
|
3202
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3222
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3203
3223
|
] }) : void 0,
|
|
3204
3224
|
defaultViewport,
|
|
3205
3225
|
viewportRestore,
|
|
@@ -3207,7 +3227,7 @@ function GenreTreeView({
|
|
|
3207
3227
|
...focus
|
|
3208
3228
|
}
|
|
3209
3229
|
) }) })
|
|
3210
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3230
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3211
3231
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3212
3232
|
{
|
|
3213
3233
|
outline: true,
|
|
@@ -3224,13 +3244,13 @@ function GenreTreeView({
|
|
|
3224
3244
|
selectedNodeId,
|
|
3225
3245
|
readOnly
|
|
3226
3246
|
}
|
|
3227
|
-
) }) : /* @__PURE__ */ (0,
|
|
3247
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime46.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(
|
|
3228
3248
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3229
|
-
return /* @__PURE__ */ (0,
|
|
3249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3230
3250
|
"div",
|
|
3231
3251
|
{
|
|
3232
3252
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3233
|
-
children: /* @__PURE__ */ (0,
|
|
3253
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3234
3254
|
GenrePlaylistTreePerRoot,
|
|
3235
3255
|
{
|
|
3236
3256
|
scope,
|
|
@@ -3257,7 +3277,7 @@ function GenreTreeView({
|
|
|
3257
3277
|
);
|
|
3258
3278
|
}
|
|
3259
3279
|
) }),
|
|
3260
|
-
/* @__PURE__ */ (0,
|
|
3280
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3261
3281
|
import_ui21.DetailPanel,
|
|
3262
3282
|
{
|
|
3263
3283
|
ref: panelRef,
|
|
@@ -3272,7 +3292,7 @@ function GenreTreeView({
|
|
|
3272
3292
|
onExpandedChange: activePanel?.onExpandedChange,
|
|
3273
3293
|
aside: activePanel?.aside,
|
|
3274
3294
|
edgeTab: detailTab,
|
|
3275
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3295
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3276
3296
|
activePanel.content,
|
|
3277
3297
|
activePanel.aside
|
|
3278
3298
|
] }) : activePanel?.content
|
|
@@ -3290,10 +3310,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3290
3310
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3291
3311
|
var import_react20 = require("react");
|
|
3292
3312
|
var import_lucide_react15 = require("lucide-react");
|
|
3293
|
-
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3294
3313
|
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3314
|
+
var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3295
3315
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
3296
|
-
var
|
|
3316
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
3297
3317
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react20.useEffect : import_react20.useLayoutEffect;
|
|
3298
3318
|
function TrackListSidebar({
|
|
3299
3319
|
className: className2 = "",
|
|
@@ -3306,7 +3326,7 @@ function TrackListSidebar({
|
|
|
3306
3326
|
renderArtistLink,
|
|
3307
3327
|
renderSongLink
|
|
3308
3328
|
}) {
|
|
3309
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3329
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree32.useTrackList)();
|
|
3310
3330
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
3311
3331
|
const trackListRef = (0, import_react20.useRef)(null);
|
|
3312
3332
|
const previousOriginRef = (0, import_react20.useRef)(trackList?.origin ?? null);
|
|
@@ -3395,25 +3415,25 @@ function TrackListSidebar({
|
|
|
3395
3415
|
}
|
|
3396
3416
|
};
|
|
3397
3417
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3398
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3418
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3399
3419
|
"div",
|
|
3400
3420
|
{
|
|
3401
3421
|
"data-theme": "dark",
|
|
3402
3422
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3403
3423
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3404
3424
|
children: [
|
|
3405
|
-
/* @__PURE__ */ (0,
|
|
3406
|
-
/* @__PURE__ */ (0,
|
|
3407
|
-
/* @__PURE__ */ (0,
|
|
3408
|
-
/* @__PURE__ */ (0,
|
|
3409
|
-
/* @__PURE__ */ (0,
|
|
3425
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3426
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3427
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3428
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3429
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.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_runtime47.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3410
3430
|
] }),
|
|
3411
|
-
/* @__PURE__ */ (0,
|
|
3412
|
-
trackList.origin.type ===
|
|
3431
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
|
|
3432
|
+
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 `,
|
|
3413
3433
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3414
3434
|
] })
|
|
3415
3435
|
] }),
|
|
3416
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3436
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3417
3437
|
"button",
|
|
3418
3438
|
{
|
|
3419
3439
|
type: "button",
|
|
@@ -3422,11 +3442,11 @@ function TrackListSidebar({
|
|
|
3422
3442
|
"aria-expanded": !isCollapsed,
|
|
3423
3443
|
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"}`,
|
|
3424
3444
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3425
|
-
children: /* @__PURE__ */ (0,
|
|
3445
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react15.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3426
3446
|
}
|
|
3427
3447
|
)
|
|
3428
3448
|
] }),
|
|
3429
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3449
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3430
3450
|
"ul",
|
|
3431
3451
|
{
|
|
3432
3452
|
ref: trackListRef,
|
|
@@ -3436,7 +3456,7 @@ function TrackListSidebar({
|
|
|
3436
3456
|
onPointerDown: markUserScrollIntent,
|
|
3437
3457
|
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",
|
|
3438
3458
|
children: [
|
|
3439
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3459
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3440
3460
|
TrackItem,
|
|
3441
3461
|
{
|
|
3442
3462
|
track,
|
|
@@ -3447,7 +3467,7 @@ function TrackListSidebar({
|
|
|
3447
3467
|
renderSongLink
|
|
3448
3468
|
}
|
|
3449
3469
|
) }, track.uuid)),
|
|
3450
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3470
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3451
3471
|
]
|
|
3452
3472
|
}
|
|
3453
3473
|
)
|
|
@@ -3460,14 +3480,14 @@ function TrackListSidebar({
|
|
|
3460
3480
|
var import_react21 = require("react");
|
|
3461
3481
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3462
3482
|
var import_player14 = require("@behindthemusictree/app-kit/player");
|
|
3463
|
-
var
|
|
3483
|
+
var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3464
3484
|
var import_ui22 = require("@behindthemusictree/ui");
|
|
3465
3485
|
|
|
3466
3486
|
// src/genre-tree/PlayerControls.tsx
|
|
3467
3487
|
var import_lucide_react16 = require("lucide-react");
|
|
3468
|
-
var
|
|
3488
|
+
var import_genre_tree34 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3469
3489
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3470
|
-
var
|
|
3490
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
3471
3491
|
function PlayerControls() {
|
|
3472
3492
|
const {
|
|
3473
3493
|
duration,
|
|
@@ -3479,22 +3499,22 @@ function PlayerControls() {
|
|
|
3479
3499
|
setVolume,
|
|
3480
3500
|
volume
|
|
3481
3501
|
} = (0, import_player13.usePlayer)();
|
|
3482
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3502
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree34.useTrackList)();
|
|
3483
3503
|
const currentTime = (0, import_player13.useCurrentTime)();
|
|
3484
3504
|
const isBusy = isLoading || playState === import_player13.PlayStates.LOADING;
|
|
3485
3505
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3486
3506
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3487
3507
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3488
3508
|
const isPlaying = playState === import_player13.PlayStates.PLAYING;
|
|
3489
|
-
return /* @__PURE__ */ (0,
|
|
3509
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3490
3510
|
"section",
|
|
3491
3511
|
{
|
|
3492
3512
|
"aria-label": "Player controls",
|
|
3493
3513
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3494
3514
|
children: [
|
|
3495
|
-
/* @__PURE__ */ (0,
|
|
3496
|
-
/* @__PURE__ */ (0,
|
|
3497
|
-
/* @__PURE__ */ (0,
|
|
3515
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3516
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree34.formatTime)(safeCurrentTime) }),
|
|
3517
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3498
3518
|
"input",
|
|
3499
3519
|
{
|
|
3500
3520
|
"aria-label": "Seek",
|
|
@@ -3508,11 +3528,11 @@ function PlayerControls() {
|
|
|
3508
3528
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3509
3529
|
}
|
|
3510
3530
|
),
|
|
3511
|
-
/* @__PURE__ */ (0,
|
|
3531
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree34.formatTime)(safeDuration) })
|
|
3512
3532
|
] }),
|
|
3513
|
-
/* @__PURE__ */ (0,
|
|
3514
|
-
/* @__PURE__ */ (0,
|
|
3515
|
-
/* @__PURE__ */ (0,
|
|
3533
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3534
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3535
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3516
3536
|
"button",
|
|
3517
3537
|
{
|
|
3518
3538
|
type: "button",
|
|
@@ -3521,10 +3541,10 @@ function PlayerControls() {
|
|
|
3521
3541
|
disabled: !isReady || !hasPreviousTrack,
|
|
3522
3542
|
onClick: playPreviousTrack,
|
|
3523
3543
|
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",
|
|
3524
|
-
children: /* @__PURE__ */ (0,
|
|
3544
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3525
3545
|
}
|
|
3526
3546
|
),
|
|
3527
|
-
/* @__PURE__ */ (0,
|
|
3547
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3528
3548
|
"button",
|
|
3529
3549
|
{
|
|
3530
3550
|
type: "button",
|
|
@@ -3533,10 +3553,10 @@ function PlayerControls() {
|
|
|
3533
3553
|
disabled: !isReady,
|
|
3534
3554
|
onClick: handlePlayPauseAction,
|
|
3535
3555
|
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",
|
|
3536
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3556
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3537
3557
|
}
|
|
3538
3558
|
),
|
|
3539
|
-
/* @__PURE__ */ (0,
|
|
3559
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3540
3560
|
"button",
|
|
3541
3561
|
{
|
|
3542
3562
|
type: "button",
|
|
@@ -3545,14 +3565,14 @@ function PlayerControls() {
|
|
|
3545
3565
|
disabled: !isReady || !hasNextTrack,
|
|
3546
3566
|
onClick: playNextTrack,
|
|
3547
3567
|
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",
|
|
3548
|
-
children: /* @__PURE__ */ (0,
|
|
3568
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3549
3569
|
}
|
|
3550
3570
|
)
|
|
3551
3571
|
] }),
|
|
3552
|
-
/* @__PURE__ */ (0,
|
|
3553
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3554
|
-
/* @__PURE__ */ (0,
|
|
3555
|
-
/* @__PURE__ */ (0,
|
|
3572
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.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: [
|
|
3573
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3574
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3575
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3556
3576
|
"input",
|
|
3557
3577
|
{
|
|
3558
3578
|
"aria-label": "Volume",
|
|
@@ -3574,7 +3594,7 @@ function PlayerControls() {
|
|
|
3574
3594
|
}
|
|
3575
3595
|
|
|
3576
3596
|
// src/genre-tree/PlayerPanel.tsx
|
|
3577
|
-
var
|
|
3597
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
3578
3598
|
var PANEL_ID = "player-panel";
|
|
3579
3599
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3580
3600
|
function PlayerPanel({
|
|
@@ -3587,7 +3607,7 @@ function PlayerPanel({
|
|
|
3587
3607
|
renderSongLink
|
|
3588
3608
|
}) {
|
|
3589
3609
|
const { playerTrackObject, retryPlayback } = (0, import_player14.usePlayer)();
|
|
3590
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3610
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree35.useTrackListSidebarVisibility)();
|
|
3591
3611
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react21.useState)("");
|
|
3592
3612
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3593
3613
|
(0, import_react21.useEffect)(() => {
|
|
@@ -3614,7 +3634,7 @@ function PlayerPanel({
|
|
|
3614
3634
|
onClick: () => setIsExtendRequested(true),
|
|
3615
3635
|
expanded: false
|
|
3616
3636
|
} : null;
|
|
3617
|
-
return /* @__PURE__ */ (0,
|
|
3637
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
3618
3638
|
"div",
|
|
3619
3639
|
{
|
|
3620
3640
|
id: PANEL_ID,
|
|
@@ -3627,7 +3647,7 @@ function PlayerPanel({
|
|
|
3627
3647
|
),
|
|
3628
3648
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3629
3649
|
children: [
|
|
3630
|
-
/* @__PURE__ */ (0,
|
|
3650
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3631
3651
|
import_ui22.PanelEdgeTab,
|
|
3632
3652
|
{
|
|
3633
3653
|
side: "right",
|
|
@@ -3637,7 +3657,7 @@ function PlayerPanel({
|
|
|
3637
3657
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3638
3658
|
}
|
|
3639
3659
|
),
|
|
3640
|
-
/* @__PURE__ */ (0,
|
|
3660
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
3641
3661
|
"div",
|
|
3642
3662
|
{
|
|
3643
3663
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3645,20 +3665,20 @@ function PlayerPanel({
|
|
|
3645
3665
|
isExtended ? "flex-row" : "flex-col"
|
|
3646
3666
|
),
|
|
3647
3667
|
children: [
|
|
3648
|
-
/* @__PURE__ */ (0,
|
|
3649
|
-
/* @__PURE__ */ (0,
|
|
3650
|
-
/* @__PURE__ */ (0,
|
|
3651
|
-
/* @__PURE__ */ (0,
|
|
3652
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3668
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3669
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3670
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3671
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3672
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3653
3673
|
playerTrackObject.loadError,
|
|
3654
|
-
/* @__PURE__ */ (0,
|
|
3674
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3655
3675
|
void retryPlayback();
|
|
3656
3676
|
}, children: "Retry" })
|
|
3657
3677
|
] })
|
|
3658
3678
|
] }),
|
|
3659
|
-
/* @__PURE__ */ (0,
|
|
3679
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(PlayerControls, {})
|
|
3660
3680
|
] }),
|
|
3661
|
-
/* @__PURE__ */ (0,
|
|
3681
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3662
3682
|
TrackListSidebar,
|
|
3663
3683
|
{
|
|
3664
3684
|
layout: "inline",
|
|
@@ -3684,33 +3704,33 @@ function PlayerPanel({
|
|
|
3684
3704
|
// src/genre-tree/Rating.tsx
|
|
3685
3705
|
var import_uuid = require("uuid");
|
|
3686
3706
|
var import_ai = require("react-icons/ai");
|
|
3687
|
-
var
|
|
3688
|
-
var
|
|
3707
|
+
var import_genre_tree36 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3708
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
3689
3709
|
function Rating({ rating, handleChange }) {
|
|
3690
3710
|
const instanceId = (0, import_uuid.v4)();
|
|
3691
|
-
return /* @__PURE__ */ (0,
|
|
3692
|
-
/* @__PURE__ */ (0,
|
|
3711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3712
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3693
3713
|
"input",
|
|
3694
3714
|
{
|
|
3695
3715
|
id: `star-${instanceId}-${i}`,
|
|
3696
3716
|
className: "w-6 h-6 opacity-0",
|
|
3697
3717
|
type: "radio",
|
|
3698
3718
|
name: "rating",
|
|
3699
|
-
value: i === 0 ?
|
|
3719
|
+
value: i === 0 ? import_genre_tree36.FORM_RATING_NULL_VALUE : i,
|
|
3700
3720
|
checked: rating === (i === 0 ? null : i),
|
|
3701
3721
|
onChange: handleChange
|
|
3702
3722
|
}
|
|
3703
3723
|
),
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
3705
|
-
/* @__PURE__ */ (0,
|
|
3724
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex", children: [
|
|
3725
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3706
3726
|
import_ai.AiOutlineStar,
|
|
3707
3727
|
{
|
|
3708
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3728
|
+
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)]"}`
|
|
3709
3729
|
}
|
|
3710
3730
|
),
|
|
3711
|
-
/* @__PURE__ */ (0,
|
|
3731
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "flex-grow w-8" }),
|
|
3712
3732
|
" "
|
|
3713
|
-
] }) : /* @__PURE__ */ (0,
|
|
3733
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3714
3734
|
import_ai.AiFillStar,
|
|
3715
3735
|
{
|
|
3716
3736
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3722,7 +3742,7 @@ function Rating({ rating, handleChange }) {
|
|
|
3722
3742
|
// src/genre-tree/EntityDetailPanel.tsx
|
|
3723
3743
|
var import_react22 = require("react");
|
|
3724
3744
|
var import_ui23 = require("@behindthemusictree/ui");
|
|
3725
|
-
var
|
|
3745
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
3726
3746
|
function EntityDetailPanel({
|
|
3727
3747
|
scope,
|
|
3728
3748
|
getBackendBaseUrl,
|
|
@@ -3745,8 +3765,8 @@ function EntityDetailPanel({
|
|
|
3745
3765
|
getBackendBaseUrl,
|
|
3746
3766
|
getArtistBackendBaseUrl,
|
|
3747
3767
|
getShareUrl,
|
|
3748
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
3749
|
-
renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0,
|
|
3768
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
3769
|
+
renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
|
|
3750
3770
|
song: {
|
|
3751
3771
|
uuid: songUuid,
|
|
3752
3772
|
expanded: songUuid !== null && expandedSongUuid === songUuid,
|
|
@@ -3762,7 +3782,7 @@ function EntityDetailPanel({
|
|
|
3762
3782
|
...navigation
|
|
3763
3783
|
});
|
|
3764
3784
|
if (!panel) return null;
|
|
3765
|
-
return /* @__PURE__ */ (0,
|
|
3785
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
3766
3786
|
import_ui23.DetailPanel,
|
|
3767
3787
|
{
|
|
3768
3788
|
side: "left",
|
|
@@ -3774,7 +3794,7 @@ function EntityDetailPanel({
|
|
|
3774
3794
|
onExpandedChange: panel.onExpandedChange,
|
|
3775
3795
|
aside: panel.extraContent,
|
|
3776
3796
|
edgeTab,
|
|
3777
|
-
children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0,
|
|
3797
|
+
children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
|
|
3778
3798
|
panel.content,
|
|
3779
3799
|
panel.extraContent
|
|
3780
3800
|
] }) : panel.content
|