@behindthemusictree/app-kit-ui 33.0.0 → 33.2.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-GHFKICEB.js → chunk-FHGQGTCN.js} +521 -463
- package/dist/chunk-FHGQGTCN.js.map +1 -0
- package/dist/genre-tree/index.cjs +599 -536
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +70 -20
- package/dist/genre-tree/index.js +15 -3
- package/dist/index.cjs +597 -534
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -3
- package/package.json +5 -5
- package/dist/chunk-GHFKICEB.js.map +0 -1
|
@@ -17,11 +17,6 @@ import {
|
|
|
17
17
|
GenreTreeViewSkeleton,
|
|
18
18
|
useGenreNodeRelations
|
|
19
19
|
} from "@behindthemusictree/genre-tree-view";
|
|
20
|
-
import {
|
|
21
|
-
useArtistSearch,
|
|
22
|
-
useSongSearch,
|
|
23
|
-
isSongSearchResultPlayable
|
|
24
|
-
} from "@behindthemusictree/app-kit/genre-tree";
|
|
25
20
|
import { useGenreTreeViewState, useArtist as useArtist2, useUpdateGenre as useUpdateGenre2, genreUpdateMutationKey } from "@behindthemusictree/app-kit/genre-tree";
|
|
26
21
|
import { usePopup as usePopup5 } from "@behindthemusictree/app-kit/popup";
|
|
27
22
|
|
|
@@ -465,11 +460,148 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
465
460
|
}
|
|
466
461
|
|
|
467
462
|
// src/genre-tree/GenreSearch.tsx
|
|
468
|
-
import {
|
|
469
|
-
import {
|
|
470
|
-
import { Combobox } from "@behindthemusictree/ui";
|
|
463
|
+
import { ArrowLeft } from "lucide-react";
|
|
464
|
+
import { SEARCH_LEADING_BUTTON_CLASS } from "@behindthemusictree/ui";
|
|
471
465
|
import { useGenreSearch } from "@behindthemusictree/app-kit/genre-tree";
|
|
472
|
-
|
|
466
|
+
|
|
467
|
+
// src/genre-tree/search/SearchBox.tsx
|
|
468
|
+
import { useState as useState2 } from "react";
|
|
469
|
+
import { SearchCombobox } from "@behindthemusictree/ui";
|
|
470
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
471
|
+
function SearchBox({
|
|
472
|
+
label,
|
|
473
|
+
placeholder,
|
|
474
|
+
value,
|
|
475
|
+
onValueChange,
|
|
476
|
+
groups,
|
|
477
|
+
onPicked,
|
|
478
|
+
onClear,
|
|
479
|
+
leading,
|
|
480
|
+
className: className2,
|
|
481
|
+
onFocus
|
|
482
|
+
}) {
|
|
483
|
+
const [open, setOpen] = useState2(false);
|
|
484
|
+
const loading = groups.some((group) => group.loading);
|
|
485
|
+
const status = groups.map((group) => group.status).filter(Boolean).join(". ");
|
|
486
|
+
const handlePick = (groupKey, itemKey) => {
|
|
487
|
+
setOpen(false);
|
|
488
|
+
groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
|
|
489
|
+
onPicked?.(groupKey, itemKey);
|
|
490
|
+
};
|
|
491
|
+
return /* @__PURE__ */ jsx5("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ jsx5(
|
|
492
|
+
SearchCombobox,
|
|
493
|
+
{
|
|
494
|
+
ariaLabel: label,
|
|
495
|
+
listLabel: `${label} results`,
|
|
496
|
+
value,
|
|
497
|
+
placeholder: placeholder ?? `${label}\u2026`,
|
|
498
|
+
groups: groups.map((group) => ({
|
|
499
|
+
key: group.key,
|
|
500
|
+
label: group.label,
|
|
501
|
+
options: group.items.map((item) => ({ key: item.key, content: item.content }))
|
|
502
|
+
})),
|
|
503
|
+
open,
|
|
504
|
+
onOpenChange: setOpen,
|
|
505
|
+
onValueChange,
|
|
506
|
+
onPick: handlePick,
|
|
507
|
+
status,
|
|
508
|
+
loading,
|
|
509
|
+
onClear,
|
|
510
|
+
leading,
|
|
511
|
+
inputClassName: "focus-visible:ring-0"
|
|
512
|
+
}
|
|
513
|
+
) });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/genre-tree/search/SearchGroup.ts
|
|
517
|
+
var SEARCH_FAILED_STATUS = "Search failed";
|
|
518
|
+
var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
|
|
519
|
+
|
|
520
|
+
// src/genre-tree/search/useArtistSearchGroup.tsx
|
|
521
|
+
import { useArtistSearch } from "@behindthemusictree/app-kit/genre-tree";
|
|
522
|
+
import { Fragment, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
523
|
+
function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
524
|
+
const search = useArtistSearch(getBaseUrl, enabled ? text : "");
|
|
525
|
+
if (!enabled) return void 0;
|
|
526
|
+
return {
|
|
527
|
+
key: "artists",
|
|
528
|
+
label: "Artists",
|
|
529
|
+
items: search.artists.map((artist) => ({
|
|
530
|
+
key: artist.uuid,
|
|
531
|
+
content: /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
532
|
+
/* @__PURE__ */ jsxs2("span", { className: "block truncate font-medium", children: [
|
|
533
|
+
artist.name,
|
|
534
|
+
artist.disambiguation && /* @__PURE__ */ jsxs2("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
535
|
+
" \xB7 ",
|
|
536
|
+
artist.disambiguation
|
|
537
|
+
] })
|
|
538
|
+
] }),
|
|
539
|
+
artist.matchedAlias && /* @__PURE__ */ jsxs2("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
540
|
+
"also known as ",
|
|
541
|
+
artist.matchedAlias
|
|
542
|
+
] })
|
|
543
|
+
] }),
|
|
544
|
+
onSelect: () => onSelect(artist)
|
|
545
|
+
})),
|
|
546
|
+
status: searchStatus(search, search.artists.length, "artists"),
|
|
547
|
+
loading: search.pending
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// src/genre-tree/search/useSongSearchGroup.tsx
|
|
552
|
+
import {
|
|
553
|
+
isSongSearchResultPlayable,
|
|
554
|
+
useSongSearch
|
|
555
|
+
} from "@behindthemusictree/app-kit/genre-tree";
|
|
556
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
557
|
+
function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
558
|
+
const search = useSongSearch(getBaseUrl, enabled ? text : "");
|
|
559
|
+
if (!enabled) return void 0;
|
|
560
|
+
return {
|
|
561
|
+
key: "songs",
|
|
562
|
+
label: "Songs",
|
|
563
|
+
items: search.songs.map((song) => ({
|
|
564
|
+
key: song.uuid,
|
|
565
|
+
content: /* @__PURE__ */ jsxs3("span", { className: isSongSearchResultPlayable(song) ? void 0 : "opacity-60", children: [
|
|
566
|
+
/* @__PURE__ */ jsx6("span", { className: "block truncate font-medium", children: song.title }),
|
|
567
|
+
/* @__PURE__ */ jsx6("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
568
|
+
] }),
|
|
569
|
+
onSelect: () => onSelect(song)
|
|
570
|
+
})),
|
|
571
|
+
status: searchStatus(search, search.songs.length, "songs"),
|
|
572
|
+
loading: search.pending
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/genre-tree/search/useGenreRemoteSearchGroup.tsx
|
|
577
|
+
import {
|
|
578
|
+
useGenreRemoteSearch
|
|
579
|
+
} from "@behindthemusictree/app-kit/genre-tree";
|
|
580
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
581
|
+
function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
|
|
582
|
+
const search = useGenreRemoteSearch(getBaseUrl, text, options);
|
|
583
|
+
const genres = filter ? search.genres.filter(filter) : search.genres;
|
|
584
|
+
return {
|
|
585
|
+
key,
|
|
586
|
+
label,
|
|
587
|
+
items: genres.map((genre) => ({
|
|
588
|
+
key: genre.uuid,
|
|
589
|
+
content: /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
590
|
+
/* @__PURE__ */ jsxs4("span", { className: "block truncate font-medium", children: [
|
|
591
|
+
genre.name,
|
|
592
|
+
genre.isUmbrella && /* @__PURE__ */ jsx7("span", { className: "ml-2 text-xs font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
593
|
+
] }),
|
|
594
|
+
/* @__PURE__ */ jsx7("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
|
|
595
|
+
] }),
|
|
596
|
+
onSelect: () => onSelect(genre)
|
|
597
|
+
})),
|
|
598
|
+
status: searchStatus(search, genres.length, "genres"),
|
|
599
|
+
loading: search.pending
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/genre-tree/GenreSearch.tsx
|
|
604
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
473
605
|
function GenreSearch({
|
|
474
606
|
genrePlaylists,
|
|
475
607
|
onSelect,
|
|
@@ -478,7 +610,8 @@ function GenreSearch({
|
|
|
478
610
|
onPrevious,
|
|
479
611
|
placeholder,
|
|
480
612
|
onQueryChange,
|
|
481
|
-
extraGroups = []
|
|
613
|
+
extraGroups = [],
|
|
614
|
+
onFocus
|
|
482
615
|
}) {
|
|
483
616
|
const { query, setQuery, results, canClear, clear, select } = useGenreSearch({
|
|
484
617
|
genrePlaylists,
|
|
@@ -486,112 +619,49 @@ function GenreSearch({
|
|
|
486
619
|
selectedName,
|
|
487
620
|
onClear
|
|
488
621
|
});
|
|
489
|
-
const [open, setOpen] = useState2(false);
|
|
490
622
|
const active = query !== "" && query !== selectedName;
|
|
491
623
|
const hasExtras = extraGroups.length > 0;
|
|
492
624
|
const label = hasExtras ? "Search" : "Search a genre";
|
|
493
|
-
const loading = active && extraGroups.some((group) => group.loading);
|
|
494
|
-
const status = active ? extraGroups.map((group) => group.status).filter(Boolean).join(". ") : "";
|
|
495
625
|
const groups = [
|
|
496
626
|
{
|
|
497
627
|
key: "genres",
|
|
498
628
|
label: hasExtras ? "Genres" : void 0,
|
|
499
|
-
|
|
629
|
+
items: results.map((genrePlaylist) => ({
|
|
500
630
|
key: genrePlaylist.uuid,
|
|
501
|
-
content: /* @__PURE__ */
|
|
631
|
+
content: /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
502
632
|
genrePlaylist.name,
|
|
503
|
-
genrePlaylist.isUmbrella && /* @__PURE__ */
|
|
504
|
-
] })
|
|
633
|
+
genrePlaylist.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-xs italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
634
|
+
] }),
|
|
635
|
+
onSelect: () => select(genrePlaylist)
|
|
505
636
|
}))
|
|
506
637
|
},
|
|
507
|
-
...extraGroups.map(
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
options: active ? group.items.map((item) => ({ key: item.key, content: item.content })) : []
|
|
511
|
-
}))
|
|
638
|
+
...extraGroups.map(
|
|
639
|
+
(group) => active ? group : { ...group, items: [], status: "", loading: false }
|
|
640
|
+
)
|
|
512
641
|
];
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
642
|
+
return /* @__PURE__ */ jsx8("div", { className: "genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: /* @__PURE__ */ jsx8(
|
|
643
|
+
SearchBox,
|
|
644
|
+
{
|
|
645
|
+
label,
|
|
646
|
+
placeholder,
|
|
647
|
+
value: query,
|
|
648
|
+
onValueChange: (value) => {
|
|
649
|
+
setQuery(value);
|
|
650
|
+
onQueryChange?.(value);
|
|
651
|
+
},
|
|
652
|
+
groups,
|
|
653
|
+
onPicked: (groupKey) => {
|
|
654
|
+
if (groupKey !== "genres") setQuery("");
|
|
655
|
+
onQueryChange?.("");
|
|
656
|
+
},
|
|
657
|
+
onClear: canClear ? () => {
|
|
658
|
+
clear();
|
|
659
|
+
onQueryChange?.("");
|
|
660
|
+
} : void 0,
|
|
661
|
+
leading: onPrevious && /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Previous", title: "Previous", className: SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ jsx8(ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
|
|
662
|
+
onFocus
|
|
521
663
|
}
|
|
522
|
-
|
|
523
|
-
if (!item) return;
|
|
524
|
-
setQuery("");
|
|
525
|
-
onQueryChange?.("");
|
|
526
|
-
item.onSelect();
|
|
527
|
-
};
|
|
528
|
-
return /* @__PURE__ */ jsxs2("div", { className: "genre-search relative w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: [
|
|
529
|
-
onPrevious && /* @__PURE__ */ jsx5(
|
|
530
|
-
"button",
|
|
531
|
-
{
|
|
532
|
-
type: "button",
|
|
533
|
-
"aria-label": "Previous",
|
|
534
|
-
title: "Previous",
|
|
535
|
-
className: "genre-search-previous absolute left-2 top-1/2 z-10 flex items-center justify-center p-1.5 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] [@media(pointer:coarse)]:min-w-[var(--layout-touch-target)] text-[var(--color-text-muted)] rounded-full -translate-y-1/2 hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)]",
|
|
536
|
-
onClick: onPrevious,
|
|
537
|
-
children: /* @__PURE__ */ jsx5(ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
538
|
-
}
|
|
539
|
-
),
|
|
540
|
-
/* @__PURE__ */ jsx5(
|
|
541
|
-
Combobox,
|
|
542
|
-
{
|
|
543
|
-
ariaLabel: label,
|
|
544
|
-
listLabel: `${label} results`,
|
|
545
|
-
value: query,
|
|
546
|
-
placeholder: placeholder ?? `${label}\u2026`,
|
|
547
|
-
groups,
|
|
548
|
-
open,
|
|
549
|
-
onOpenChange: setOpen,
|
|
550
|
-
onValueChange: (value) => {
|
|
551
|
-
setQuery(value);
|
|
552
|
-
onQueryChange?.(value);
|
|
553
|
-
},
|
|
554
|
-
onPick: handlePick,
|
|
555
|
-
status,
|
|
556
|
-
loading: loading ? "Searching\u2026" : void 0,
|
|
557
|
-
inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md`,
|
|
558
|
-
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
559
|
-
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
560
|
-
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
561
|
-
}
|
|
562
|
-
),
|
|
563
|
-
/* @__PURE__ */ jsxs2("div", { className: "absolute right-3 top-1/2 flex items-center -translate-y-1/2", children: [
|
|
564
|
-
/* @__PURE__ */ jsxs2("span", { "data-loading": loading, className: "relative p-2 text-[var(--color-text-muted)] pointer-events-none", children: [
|
|
565
|
-
/* @__PURE__ */ jsx5(
|
|
566
|
-
Search,
|
|
567
|
-
{
|
|
568
|
-
"aria-hidden": "true",
|
|
569
|
-
className: `w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-0" : ""}`
|
|
570
|
-
}
|
|
571
|
-
),
|
|
572
|
-
/* @__PURE__ */ jsx5(
|
|
573
|
-
LoaderCircle,
|
|
574
|
-
{
|
|
575
|
-
"aria-hidden": "true",
|
|
576
|
-
className: `absolute inset-2 w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-100 delay-300 animate-spin motion-reduce:animate-none" : "opacity-0"}`
|
|
577
|
-
}
|
|
578
|
-
)
|
|
579
|
-
] }),
|
|
580
|
-
canClear && /* @__PURE__ */ jsx5(
|
|
581
|
-
"button",
|
|
582
|
-
{
|
|
583
|
-
type: "button",
|
|
584
|
-
"aria-label": "Clear search",
|
|
585
|
-
className: "flex items-center justify-center p-2 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] [@media(pointer:coarse)]:min-w-[var(--layout-touch-target)] text-[var(--color-text-muted)] rounded-full hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)]",
|
|
586
|
-
onClick: () => {
|
|
587
|
-
clear();
|
|
588
|
-
onQueryChange?.("");
|
|
589
|
-
},
|
|
590
|
-
children: /* @__PURE__ */ jsx5(X, { "aria-hidden": "true", className: "w-6 h-6" })
|
|
591
|
-
}
|
|
592
|
-
)
|
|
593
|
-
] })
|
|
594
|
-
] });
|
|
664
|
+
) });
|
|
595
665
|
}
|
|
596
666
|
|
|
597
667
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
@@ -611,12 +681,12 @@ import { FaWhatsapp } from "react-icons/fa";
|
|
|
611
681
|
import { SiGmail } from "react-icons/si";
|
|
612
682
|
|
|
613
683
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
614
|
-
import { Fragment as
|
|
684
|
+
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
615
685
|
var className = "group flex min-w-0 flex-1 flex-col items-center justify-center gap-1.5 rounded-xl px-2 py-2 text-xs text-[var(--color-text)] transition-colors hover:bg-[var(--color-surface-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
|
|
616
686
|
function Contents({ icon, label }) {
|
|
617
|
-
return /* @__PURE__ */
|
|
618
|
-
/* @__PURE__ */
|
|
619
|
-
/* @__PURE__ */
|
|
687
|
+
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
688
|
+
/* @__PURE__ */ jsx9("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: icon }),
|
|
689
|
+
/* @__PURE__ */ jsx9("span", { className: "max-w-full truncate", children: label })
|
|
620
690
|
] });
|
|
621
691
|
}
|
|
622
692
|
function ShareDestinationButton({
|
|
@@ -628,29 +698,29 @@ function ShareDestinationButton({
|
|
|
628
698
|
ariaLabel
|
|
629
699
|
}) {
|
|
630
700
|
if (href) {
|
|
631
|
-
return /* @__PURE__ */
|
|
701
|
+
return /* @__PURE__ */ jsx9("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx9(Contents, { icon, label }) });
|
|
632
702
|
}
|
|
633
|
-
return /* @__PURE__ */
|
|
703
|
+
return /* @__PURE__ */ jsx9("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx9(Contents, { icon, label }) });
|
|
634
704
|
}
|
|
635
705
|
|
|
636
706
|
// src/genre-tree/SharePreviewCard.tsx
|
|
637
707
|
import { Music, Music2, UserRound } from "lucide-react";
|
|
638
|
-
import { jsx as
|
|
708
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
639
709
|
function SharePreviewCard({ target, url }) {
|
|
640
710
|
const Icon = { genre: Music2, artist: UserRound, song: Music }[target.kind];
|
|
641
711
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
642
|
-
return /* @__PURE__ */
|
|
643
|
-
/* @__PURE__ */
|
|
644
|
-
/* @__PURE__ */
|
|
645
|
-
/* @__PURE__ */
|
|
646
|
-
/* @__PURE__ */
|
|
647
|
-
/* @__PURE__ */
|
|
712
|
+
return /* @__PURE__ */ jsx10("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
713
|
+
/* @__PURE__ */ jsx10("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ jsx10(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
714
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
715
|
+
/* @__PURE__ */ jsx10("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
716
|
+
/* @__PURE__ */ jsx10("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
717
|
+
/* @__PURE__ */ jsx10("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
648
718
|
] })
|
|
649
719
|
] }) });
|
|
650
720
|
}
|
|
651
721
|
|
|
652
722
|
// src/genre-tree/SharePopup.tsx
|
|
653
|
-
import { Fragment as
|
|
723
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
654
724
|
function resolveAbsoluteUrl(url) {
|
|
655
725
|
try {
|
|
656
726
|
const parsed = new URL(url, window.location.href);
|
|
@@ -725,8 +795,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
725
795
|
setShareError("Could not open the share options.");
|
|
726
796
|
}
|
|
727
797
|
};
|
|
728
|
-
return /* @__PURE__ */
|
|
729
|
-
/* @__PURE__ */
|
|
798
|
+
return /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
799
|
+
/* @__PURE__ */ jsx11(
|
|
730
800
|
BasePopup_default,
|
|
731
801
|
{
|
|
732
802
|
title: "Share",
|
|
@@ -741,67 +811,67 @@ function SharePopup({ target, url, onClose }) {
|
|
|
741
811
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
742
812
|
closeButtonClassName: "!h-10 !w-10 !rounded-none !border-0 !bg-transparent !p-0 !text-[var(--color-text)] hover:!bg-transparent hover:!text-[var(--color-text)]",
|
|
743
813
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
744
|
-
children: /* @__PURE__ */
|
|
745
|
-
/* @__PURE__ */
|
|
746
|
-
/* @__PURE__ */
|
|
747
|
-
/* @__PURE__ */
|
|
814
|
+
children: /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-4", children: [
|
|
815
|
+
/* @__PURE__ */ jsx11(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
816
|
+
/* @__PURE__ */ jsxs8("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
817
|
+
/* @__PURE__ */ jsx11(
|
|
748
818
|
ShareDestinationButton,
|
|
749
819
|
{
|
|
750
820
|
label: "Copy Link",
|
|
751
|
-
icon: /* @__PURE__ */
|
|
821
|
+
icon: /* @__PURE__ */ jsx11(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
752
822
|
onClick: () => void handleCopy(),
|
|
753
823
|
ariaLabel: "Copy link",
|
|
754
824
|
disabled: !absoluteUrl
|
|
755
825
|
}
|
|
756
826
|
),
|
|
757
|
-
/* @__PURE__ */
|
|
827
|
+
/* @__PURE__ */ jsx11(
|
|
758
828
|
ShareDestinationButton,
|
|
759
829
|
{
|
|
760
830
|
label: "WhatsApp",
|
|
761
|
-
icon: /* @__PURE__ */
|
|
831
|
+
icon: /* @__PURE__ */ jsx11(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
762
832
|
href: destinationUrls?.whatsapp,
|
|
763
833
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
764
834
|
disabled: !absoluteUrl
|
|
765
835
|
}
|
|
766
836
|
),
|
|
767
|
-
/* @__PURE__ */
|
|
837
|
+
/* @__PURE__ */ jsx11(
|
|
768
838
|
ShareDestinationButton,
|
|
769
839
|
{
|
|
770
840
|
label: "Gmail",
|
|
771
|
-
icon: /* @__PURE__ */
|
|
841
|
+
icon: /* @__PURE__ */ jsx11(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
772
842
|
href: destinationUrls?.gmail,
|
|
773
843
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
774
844
|
disabled: !absoluteUrl
|
|
775
845
|
}
|
|
776
846
|
),
|
|
777
|
-
canUseNativeShare && /* @__PURE__ */
|
|
847
|
+
canUseNativeShare && /* @__PURE__ */ jsx11(
|
|
778
848
|
ShareDestinationButton,
|
|
779
849
|
{
|
|
780
850
|
label: "More",
|
|
781
|
-
icon: /* @__PURE__ */
|
|
851
|
+
icon: /* @__PURE__ */ jsx11(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
782
852
|
onClick: () => void handleNativeShare(),
|
|
783
853
|
ariaLabel: "More sharing options",
|
|
784
854
|
disabled: !absoluteUrl
|
|
785
855
|
}
|
|
786
856
|
)
|
|
787
857
|
] }),
|
|
788
|
-
copyStatus === "failed" && /* @__PURE__ */
|
|
789
|
-
shareError && /* @__PURE__ */
|
|
858
|
+
copyStatus === "failed" && /* @__PURE__ */ jsx11("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
859
|
+
shareError && /* @__PURE__ */ jsx11("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
790
860
|
] })
|
|
791
861
|
}
|
|
792
862
|
),
|
|
793
|
-
toastId > 0 && /* @__PURE__ */
|
|
863
|
+
toastId > 0 && /* @__PURE__ */ jsx11(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
794
864
|
] });
|
|
795
865
|
}
|
|
796
866
|
|
|
797
867
|
// src/genre-tree/ShareActionButton.tsx
|
|
798
|
-
import { jsx as
|
|
868
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
799
869
|
function ShareActionButton({ target, url }) {
|
|
800
870
|
const { showPopup, hidePopup } = usePopup3();
|
|
801
871
|
const openSharePopup = () => {
|
|
802
|
-
showPopup(/* @__PURE__ */
|
|
872
|
+
showPopup(/* @__PURE__ */ jsx12(SharePopup, { target, url, onClose: hidePopup }));
|
|
803
873
|
};
|
|
804
|
-
return /* @__PURE__ */
|
|
874
|
+
return /* @__PURE__ */ jsxs9(
|
|
805
875
|
"button",
|
|
806
876
|
{
|
|
807
877
|
type: "button",
|
|
@@ -810,7 +880,7 @@ function ShareActionButton({ target, url }) {
|
|
|
810
880
|
"aria-label": `Share ${target.name}`,
|
|
811
881
|
title: `Share ${target.name}`,
|
|
812
882
|
children: [
|
|
813
|
-
/* @__PURE__ */
|
|
883
|
+
/* @__PURE__ */ jsx12("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: /* @__PURE__ */ jsx12(Share22, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
814
884
|
"Share"
|
|
815
885
|
]
|
|
816
886
|
}
|
|
@@ -818,7 +888,7 @@ function ShareActionButton({ target, url }) {
|
|
|
818
888
|
}
|
|
819
889
|
|
|
820
890
|
// src/genre-tree/GenreActionBand.tsx
|
|
821
|
-
import { jsx as
|
|
891
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
822
892
|
function GenreActionBand({
|
|
823
893
|
node,
|
|
824
894
|
onPlayPause,
|
|
@@ -840,7 +910,7 @@ function GenreActionBand({
|
|
|
840
910
|
items.push({
|
|
841
911
|
key: "play-pause",
|
|
842
912
|
label: isPlaying ? "Pause" : "Play",
|
|
843
|
-
icon: isPlaying ? /* @__PURE__ */
|
|
913
|
+
icon: isPlaying ? /* @__PURE__ */ jsx13(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx13(Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
844
914
|
onClick: onPlayPause
|
|
845
915
|
});
|
|
846
916
|
}
|
|
@@ -848,24 +918,24 @@ function GenreActionBand({
|
|
|
848
918
|
items.push({
|
|
849
919
|
key: "zoom-in",
|
|
850
920
|
label: "Focus",
|
|
851
|
-
icon: /* @__PURE__ */
|
|
921
|
+
icon: /* @__PURE__ */ jsx13(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
852
922
|
onClick: onZoomIn
|
|
853
923
|
});
|
|
854
924
|
}
|
|
855
925
|
if (onAddChild) {
|
|
856
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */
|
|
926
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx13(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
857
927
|
}
|
|
858
928
|
if (onRename) {
|
|
859
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */
|
|
929
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx13(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
860
930
|
}
|
|
861
931
|
if (onChangeParent) {
|
|
862
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */
|
|
932
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx13(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
863
933
|
}
|
|
864
934
|
if (onToggleSide) {
|
|
865
935
|
adminItems.push({
|
|
866
936
|
key: "toggle-side",
|
|
867
937
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
868
|
-
icon: /* @__PURE__ */
|
|
938
|
+
icon: /* @__PURE__ */ jsx13(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
869
939
|
onClick: onToggleSide,
|
|
870
940
|
disabled: !!toggleSideDisabledReason,
|
|
871
941
|
title: toggleSideDisabledReason
|
|
@@ -881,7 +951,7 @@ function GenreActionBand({
|
|
|
881
951
|
});
|
|
882
952
|
}
|
|
883
953
|
if (items.length === 0 && (!shareTarget || !shareUrl) && adminItems.length === 0) return null;
|
|
884
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */
|
|
954
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ jsxs10(
|
|
885
955
|
"button",
|
|
886
956
|
{
|
|
887
957
|
type: "button",
|
|
@@ -890,25 +960,25 @@ function GenreActionBand({
|
|
|
890
960
|
onClick: item.onClick,
|
|
891
961
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none disabled:opacity-50",
|
|
892
962
|
children: [
|
|
893
|
-
/* @__PURE__ */
|
|
963
|
+
/* @__PURE__ */ jsx13("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)] group-disabled:group-hover:bg-[var(--color-surface-muted)]", children: item.icon }),
|
|
894
964
|
item.label
|
|
895
965
|
]
|
|
896
966
|
},
|
|
897
967
|
item.key
|
|
898
968
|
));
|
|
899
|
-
return /* @__PURE__ */
|
|
900
|
-
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */
|
|
969
|
+
return /* @__PURE__ */ jsxs10("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
970
|
+
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
901
971
|
renderItems(items),
|
|
902
|
-
shareTarget && shareUrl && /* @__PURE__ */
|
|
972
|
+
shareTarget && shareUrl && /* @__PURE__ */ jsx13(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
903
973
|
] }),
|
|
904
|
-
adminItems.length > 0 && /* @__PURE__ */
|
|
974
|
+
adminItems.length > 0 && /* @__PURE__ */ jsxs10(
|
|
905
975
|
"section",
|
|
906
976
|
{
|
|
907
977
|
"aria-label": "Admin actions",
|
|
908
978
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
909
979
|
children: [
|
|
910
|
-
/* @__PURE__ */
|
|
911
|
-
/* @__PURE__ */
|
|
980
|
+
/* @__PURE__ */ jsx13("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
981
|
+
/* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
912
982
|
]
|
|
913
983
|
}
|
|
914
984
|
)
|
|
@@ -921,7 +991,7 @@ import { useFetchGenreOverview } from "@behindthemusictree/app-kit/genre-tree";
|
|
|
921
991
|
|
|
922
992
|
// src/genre-tree/ClampedText.tsx
|
|
923
993
|
import { useLayoutEffect, useRef as useRef2, useState as useState4 } from "react";
|
|
924
|
-
import { Fragment as
|
|
994
|
+
import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
925
995
|
function ClampedText({ children, className: className2 = "" }) {
|
|
926
996
|
const ref = useRef2(null);
|
|
927
997
|
const [expanded, setExpanded] = useState4(false);
|
|
@@ -931,9 +1001,9 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
931
1001
|
if (!element || expanded) return;
|
|
932
1002
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
933
1003
|
}, [children, expanded]);
|
|
934
|
-
return /* @__PURE__ */
|
|
935
|
-
/* @__PURE__ */
|
|
936
|
-
(overflows || expanded) && /* @__PURE__ */
|
|
1004
|
+
return /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1005
|
+
/* @__PURE__ */ jsx14("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1006
|
+
(overflows || expanded) && /* @__PURE__ */ jsx14(
|
|
937
1007
|
"button",
|
|
938
1008
|
{
|
|
939
1009
|
type: "button",
|
|
@@ -953,7 +1023,7 @@ import { z } from "zod";
|
|
|
953
1023
|
import { FaPlay, FaPause } from "react-icons/fa";
|
|
954
1024
|
import { PanelSection, RingLoader, Skeleton } from "@behindthemusictree/ui";
|
|
955
1025
|
import { useSession } from "@behindthemusictree/app-kit/auth";
|
|
956
|
-
import { useFetchWrapper } from "@behindthemusictree/app-kit/transport";
|
|
1026
|
+
import { usePlaybackContext, useFetchWrapper } from "@behindthemusictree/app-kit/transport";
|
|
957
1027
|
import { PaginatedResponseSchema } from "@behindthemusictree/app-kit/transport";
|
|
958
1028
|
import { parseWithLog } from "@behindthemusictree/app-kit/transport";
|
|
959
1029
|
import { usePlayer as usePlayer4 } from "@behindthemusictree/app-kit/player";
|
|
@@ -967,15 +1037,15 @@ import { useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre
|
|
|
967
1037
|
|
|
968
1038
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
969
1039
|
import { musicBrainzRecordingUrl } from "@behindthemusictree/app-kit/genre-tree";
|
|
970
|
-
import { Fragment as
|
|
1040
|
+
import { Fragment as Fragment7, jsx as jsx15 } from "react/jsx-runtime";
|
|
971
1041
|
function MusicBrainzRecordingLink({
|
|
972
1042
|
song,
|
|
973
1043
|
children,
|
|
974
1044
|
className: className2 = ""
|
|
975
1045
|
}) {
|
|
976
1046
|
const url = musicBrainzRecordingUrl(song);
|
|
977
|
-
if (!url) return /* @__PURE__ */
|
|
978
|
-
return /* @__PURE__ */
|
|
1047
|
+
if (!url) return /* @__PURE__ */ jsx15(Fragment7, { children });
|
|
1048
|
+
return /* @__PURE__ */ jsx15(
|
|
979
1049
|
"a",
|
|
980
1050
|
{
|
|
981
1051
|
href: url,
|
|
@@ -992,7 +1062,7 @@ function MusicBrainzRecordingLink({
|
|
|
992
1062
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
993
1063
|
import { ArtistNames } from "@behindthemusictree/app-kit/genre-tree";
|
|
994
1064
|
import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
|
|
995
|
-
import { jsx as
|
|
1065
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
996
1066
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
997
1067
|
var SongPageSchema = PaginatedResponseSchema(SongDetailedSchema).extend({
|
|
998
1068
|
overallTotal: z.number().nullable(),
|
|
@@ -1028,8 +1098,9 @@ function GenrePlaylistTracks({
|
|
|
1028
1098
|
const trackNoun = useTrackNoun();
|
|
1029
1099
|
const { uuid } = genrePlaylist;
|
|
1030
1100
|
const { fetch } = useFetchWrapper(getBackendBaseUrl);
|
|
1101
|
+
const playback = usePlaybackContext();
|
|
1031
1102
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({
|
|
1032
|
-
queryKey: scope === "me" ? genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid),
|
|
1103
|
+
queryKey: [...scope === "me" ? genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), ...playback.key ? [playback.key] : []],
|
|
1033
1104
|
queryFn: async ({ pageParam }) => {
|
|
1034
1105
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1035
1106
|
const response = await fetch(
|
|
@@ -1044,7 +1115,7 @@ function GenrePlaylistTracks({
|
|
|
1044
1115
|
},
|
|
1045
1116
|
initialPageParam: scope === "me" ? 1 : "",
|
|
1046
1117
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1047
|
-
enabled: scope !== "me" || sessionRestored && !!session?.accessToken
|
|
1118
|
+
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1048
1119
|
});
|
|
1049
1120
|
const [sentinel, setSentinel] = useState5(null);
|
|
1050
1121
|
useEffect3(() => {
|
|
@@ -1064,35 +1135,35 @@ function GenrePlaylistTracks({
|
|
|
1064
1135
|
const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1065
1136
|
started.catch((error) => {
|
|
1066
1137
|
console.error("Failed to play genre playlist:", error);
|
|
1067
|
-
showPopup(/* @__PURE__ */
|
|
1138
|
+
showPopup(/* @__PURE__ */ jsx16(InternalErrorPopup, { errorCode: ErrorCode3.CLIENT_UNKNOWN }));
|
|
1068
1139
|
});
|
|
1069
1140
|
};
|
|
1070
|
-
return /* @__PURE__ */
|
|
1141
|
+
return /* @__PURE__ */ jsx16(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
|
|
1071
1142
|
renderTracksToolbar?.(),
|
|
1072
|
-
isPending ? /* @__PURE__ */
|
|
1143
|
+
isPending ? /* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
|
|
1073
1144
|
"Couldn't load ",
|
|
1074
1145
|
trackNoun.plural,
|
|
1075
1146
|
".",
|
|
1076
1147
|
" ",
|
|
1077
|
-
/* @__PURE__ */
|
|
1078
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */
|
|
1148
|
+
/* @__PURE__ */ jsx16("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1149
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ jsx16("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${genrePlaylist.name} ${trackNoun.plural}`, children: [
|
|
1079
1150
|
tracks.map((track, index) => {
|
|
1080
1151
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1081
1152
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1082
1153
|
const isPlaying = isCurrent && playState === PlayStates.PLAYING;
|
|
1083
|
-
const text = /* @__PURE__ */
|
|
1084
|
-
/* @__PURE__ */
|
|
1085
|
-
track.artists?.length ? /* @__PURE__ */
|
|
1086
|
-
/* @__PURE__ */
|
|
1087
|
-
track.album && /* @__PURE__ */
|
|
1154
|
+
const text = /* @__PURE__ */ jsxs12("span", { className: TEXT_CELL, children: [
|
|
1155
|
+
/* @__PURE__ */ jsx16("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : isSong(track) ? /* @__PURE__ */ jsx16(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1156
|
+
track.artists?.length ? /* @__PURE__ */ jsx16("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1157
|
+
/* @__PURE__ */ jsx16("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ jsx16(ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1158
|
+
track.album && /* @__PURE__ */ jsx16("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1088
1159
|
] });
|
|
1089
1160
|
const leading = renderSongLeading?.(track, index);
|
|
1090
1161
|
const actions = renderSongActions?.(track);
|
|
1091
1162
|
const isUnplayable = isSong(track) && songUnplayableReason(track) !== null;
|
|
1092
1163
|
if (isUnplayable) {
|
|
1093
|
-
return /* @__PURE__ */
|
|
1164
|
+
return /* @__PURE__ */ jsxs12("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1094
1165
|
leading,
|
|
1095
|
-
/* @__PURE__ */
|
|
1166
|
+
/* @__PURE__ */ jsx16(
|
|
1096
1167
|
"button",
|
|
1097
1168
|
{
|
|
1098
1169
|
type: "button",
|
|
@@ -1100,27 +1171,27 @@ function GenrePlaylistTracks({
|
|
|
1100
1171
|
"aria-disabled": "true",
|
|
1101
1172
|
"aria-label": label,
|
|
1102
1173
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1103
|
-
children: /* @__PURE__ */
|
|
1174
|
+
children: /* @__PURE__ */ jsx16(FaPlay, { size: 10 })
|
|
1104
1175
|
}
|
|
1105
1176
|
),
|
|
1106
1177
|
text,
|
|
1107
1178
|
actions
|
|
1108
1179
|
] }, track.uuid);
|
|
1109
1180
|
}
|
|
1110
|
-
return /* @__PURE__ */
|
|
1181
|
+
return /* @__PURE__ */ jsxs12(
|
|
1111
1182
|
"li",
|
|
1112
1183
|
{
|
|
1113
1184
|
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)]" : ""}`,
|
|
1114
1185
|
children: [
|
|
1115
1186
|
leading,
|
|
1116
|
-
/* @__PURE__ */
|
|
1187
|
+
/* @__PURE__ */ jsx16(
|
|
1117
1188
|
"button",
|
|
1118
1189
|
{
|
|
1119
1190
|
type: "button",
|
|
1120
1191
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1121
1192
|
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 !== PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1122
1193
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1123
|
-
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */
|
|
1194
|
+
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */ jsx16(RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ jsx16(FaPause, { size: 10 }) : /* @__PURE__ */ jsx16(FaPlay, { size: 10 })
|
|
1124
1195
|
}
|
|
1125
1196
|
),
|
|
1126
1197
|
text,
|
|
@@ -1130,7 +1201,7 @@ function GenrePlaylistTracks({
|
|
|
1130
1201
|
track.uuid
|
|
1131
1202
|
);
|
|
1132
1203
|
}),
|
|
1133
|
-
hasNextPage && /* @__PURE__ */
|
|
1204
|
+
hasNextPage && /* @__PURE__ */ jsx16("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1134
1205
|
] })
|
|
1135
1206
|
] }) });
|
|
1136
1207
|
}
|
|
@@ -1138,7 +1209,7 @@ function GenrePlaylistTracks({
|
|
|
1138
1209
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1139
1210
|
import { useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
|
|
1140
1211
|
import { SiWikidata, SiWikipedia } from "react-icons/si";
|
|
1141
|
-
import { Fragment as
|
|
1212
|
+
import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1142
1213
|
function GenreDetailExtras({
|
|
1143
1214
|
genreUuid,
|
|
1144
1215
|
genrePlaylist,
|
|
@@ -1162,17 +1233,17 @@ function GenreDetailExtras({
|
|
|
1162
1233
|
criteriaOverviewSchema
|
|
1163
1234
|
);
|
|
1164
1235
|
if (isPending) {
|
|
1165
|
-
return /* @__PURE__ */
|
|
1166
|
-
/* @__PURE__ */
|
|
1236
|
+
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1237
|
+
/* @__PURE__ */ jsx17(PanelSection2, { title: "Summary", children: /* @__PURE__ */ jsx17("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ jsx17(Skeleton2, { className: "h-4 w-3/4" }) }) }),
|
|
1167
1238
|
family
|
|
1168
1239
|
] });
|
|
1169
1240
|
}
|
|
1170
|
-
if (!overview) return /* @__PURE__ */
|
|
1241
|
+
if (!overview) return /* @__PURE__ */ jsx17(Fragment8, { children: family });
|
|
1171
1242
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1172
1243
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1173
|
-
return /* @__PURE__ */
|
|
1174
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */
|
|
1175
|
-
summarySourceUrl && /* @__PURE__ */
|
|
1244
|
+
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1245
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx17(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1246
|
+
summarySourceUrl && /* @__PURE__ */ jsxs13(
|
|
1176
1247
|
"a",
|
|
1177
1248
|
{
|
|
1178
1249
|
href: summarySourceUrl,
|
|
@@ -1180,12 +1251,12 @@ function GenreDetailExtras({
|
|
|
1180
1251
|
rel: "noopener noreferrer",
|
|
1181
1252
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1182
1253
|
children: [
|
|
1183
|
-
/* @__PURE__ */
|
|
1254
|
+
/* @__PURE__ */ jsx17(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1184
1255
|
"Wikipedia"
|
|
1185
1256
|
]
|
|
1186
1257
|
}
|
|
1187
1258
|
),
|
|
1188
|
-
wikidataUrl && /* @__PURE__ */
|
|
1259
|
+
wikidataUrl && /* @__PURE__ */ jsxs13(
|
|
1189
1260
|
"a",
|
|
1190
1261
|
{
|
|
1191
1262
|
href: wikidataUrl,
|
|
@@ -1193,17 +1264,17 @@ function GenreDetailExtras({
|
|
|
1193
1264
|
rel: "noopener noreferrer",
|
|
1194
1265
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1195
1266
|
children: [
|
|
1196
|
-
/* @__PURE__ */
|
|
1267
|
+
/* @__PURE__ */ jsx17(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1197
1268
|
"Wikidata"
|
|
1198
1269
|
]
|
|
1199
1270
|
}
|
|
1200
1271
|
)
|
|
1201
1272
|
] }) }),
|
|
1202
|
-
/* @__PURE__ */
|
|
1273
|
+
/* @__PURE__ */ jsx17(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx17(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1203
1274
|
family,
|
|
1204
1275
|
renderGenreDetailExtras?.(overview),
|
|
1205
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */
|
|
1206
|
-
/* @__PURE__ */
|
|
1276
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx17(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx17("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx17("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx17("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1277
|
+
/* @__PURE__ */ jsx17(
|
|
1207
1278
|
GenrePlaylistTracks,
|
|
1208
1279
|
{
|
|
1209
1280
|
genreUuid,
|
|
@@ -1219,7 +1290,7 @@ function GenreDetailExtras({
|
|
|
1219
1290
|
}
|
|
1220
1291
|
|
|
1221
1292
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1222
|
-
import { Fragment as
|
|
1293
|
+
import { Fragment as Fragment9, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1223
1294
|
import { createElement } from "react";
|
|
1224
1295
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1225
1296
|
const { isPlaying, setIsPlaying } = usePlayer5();
|
|
@@ -1233,7 +1304,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1233
1304
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1234
1305
|
});
|
|
1235
1306
|
};
|
|
1236
|
-
return /* @__PURE__ */
|
|
1307
|
+
return /* @__PURE__ */ jsx18(
|
|
1237
1308
|
GenreActionBand,
|
|
1238
1309
|
{
|
|
1239
1310
|
...bandProps,
|
|
@@ -1254,9 +1325,9 @@ function GenreInfoPanelBody({
|
|
|
1254
1325
|
band,
|
|
1255
1326
|
extras
|
|
1256
1327
|
}) {
|
|
1257
|
-
return /* @__PURE__ */
|
|
1328
|
+
return /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1258
1329
|
toolbar,
|
|
1259
|
-
/* @__PURE__ */
|
|
1330
|
+
/* @__PURE__ */ jsx18(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1260
1331
|
/* @__PURE__ */ createElement(
|
|
1261
1332
|
GenreDetailExtras,
|
|
1262
1333
|
{
|
|
@@ -1279,7 +1350,7 @@ import {
|
|
|
1279
1350
|
getReparentCandidates,
|
|
1280
1351
|
useUpdateGenre
|
|
1281
1352
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1282
|
-
import { jsx as
|
|
1353
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1283
1354
|
function ChangeParentPopup({
|
|
1284
1355
|
genrePlaylist,
|
|
1285
1356
|
genrePlaylists,
|
|
@@ -1314,12 +1385,12 @@ function ChangeParentPopup({
|
|
|
1314
1385
|
window.addEventListener("keydown", handleKeyDown);
|
|
1315
1386
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1316
1387
|
}, [onClose]);
|
|
1317
|
-
return /* @__PURE__ */
|
|
1318
|
-
/* @__PURE__ */
|
|
1388
|
+
return /* @__PURE__ */ jsxs15(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1389
|
+
/* @__PURE__ */ jsxs15("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1319
1390
|
"Current parent: ",
|
|
1320
1391
|
genrePlaylist.parent?.name ?? "none"
|
|
1321
1392
|
] }),
|
|
1322
|
-
/* @__PURE__ */
|
|
1393
|
+
/* @__PURE__ */ jsx19("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx19(
|
|
1323
1394
|
GenreSearch,
|
|
1324
1395
|
{
|
|
1325
1396
|
genrePlaylists: candidates,
|
|
@@ -1327,7 +1398,7 @@ function ChangeParentPopup({
|
|
|
1327
1398
|
placeholder: "Search a new parent\u2026"
|
|
1328
1399
|
}
|
|
1329
1400
|
) }),
|
|
1330
|
-
formErrors.length > 0 && /* @__PURE__ */
|
|
1401
|
+
formErrors.length > 0 && /* @__PURE__ */ jsx19("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1331
1402
|
] });
|
|
1332
1403
|
}
|
|
1333
1404
|
|
|
@@ -1351,7 +1422,7 @@ import {
|
|
|
1351
1422
|
SiYoutube
|
|
1352
1423
|
} from "react-icons/si";
|
|
1353
1424
|
import { Chip } from "@behindthemusictree/ui";
|
|
1354
|
-
import { jsx as
|
|
1425
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1355
1426
|
var LINK_KINDS = {
|
|
1356
1427
|
official: { label: "Official website", Icon: Globe },
|
|
1357
1428
|
wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
|
|
@@ -1388,7 +1459,7 @@ function artistLinks(artist) {
|
|
|
1388
1459
|
return links;
|
|
1389
1460
|
}
|
|
1390
1461
|
function ArtistLinks({ links }) {
|
|
1391
|
-
return /* @__PURE__ */
|
|
1462
|
+
return /* @__PURE__ */ jsx20("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx20("li", { children: /* @__PURE__ */ jsx20(Chip, { href: url, icon: /* @__PURE__ */ jsx20(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1392
1463
|
}
|
|
1393
1464
|
|
|
1394
1465
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1399,7 +1470,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1399
1470
|
}
|
|
1400
1471
|
|
|
1401
1472
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1402
|
-
import { jsx as
|
|
1473
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1403
1474
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1404
1475
|
function ArtistHeader({ artist }) {
|
|
1405
1476
|
const fields = [];
|
|
@@ -1409,35 +1480,35 @@ function ArtistHeader({ artist }) {
|
|
|
1409
1480
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1410
1481
|
if (years) fields.push({ label: "Active", value: years });
|
|
1411
1482
|
const links = artistLinks(artist);
|
|
1412
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */
|
|
1483
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx21(ArtistLinks, { links }) });
|
|
1413
1484
|
if (fields.length === 0) return null;
|
|
1414
|
-
return /* @__PURE__ */
|
|
1485
|
+
return /* @__PURE__ */ jsx21(FieldList, { items: fields });
|
|
1415
1486
|
}
|
|
1416
1487
|
|
|
1417
1488
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1418
|
-
import { jsx as
|
|
1489
|
+
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1419
1490
|
function ExternalLink({ href, children }) {
|
|
1420
|
-
return /* @__PURE__ */
|
|
1491
|
+
return /* @__PURE__ */ jsx22("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1421
1492
|
}
|
|
1422
1493
|
function ArtistPhoto({ image, name }) {
|
|
1423
|
-
return /* @__PURE__ */
|
|
1424
|
-
/* @__PURE__ */
|
|
1425
|
-
/* @__PURE__ */
|
|
1494
|
+
return /* @__PURE__ */ jsxs16("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1495
|
+
/* @__PURE__ */ jsx22("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1496
|
+
/* @__PURE__ */ jsxs16("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1426
1497
|
"Photo: ",
|
|
1427
|
-
/* @__PURE__ */
|
|
1498
|
+
/* @__PURE__ */ jsx22(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1428
1499
|
",",
|
|
1429
1500
|
" ",
|
|
1430
|
-
image.licenseUrl ? /* @__PURE__ */
|
|
1501
|
+
image.licenseUrl ? /* @__PURE__ */ jsx22(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1431
1502
|
] })
|
|
1432
1503
|
] });
|
|
1433
1504
|
}
|
|
1434
1505
|
|
|
1435
1506
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1436
1507
|
import { PanelGroup, PanelSection as PanelSection3 } from "@behindthemusictree/ui";
|
|
1437
|
-
import { jsx as
|
|
1508
|
+
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1438
1509
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1439
1510
|
if (memberships.length === 0) return null;
|
|
1440
|
-
return /* @__PURE__ */
|
|
1511
|
+
return /* @__PURE__ */ jsx23(PanelGroup, { title, children: /* @__PURE__ */ jsx23("ul", { children: memberships.map((membership, index) => {
|
|
1441
1512
|
const details = [
|
|
1442
1513
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1443
1514
|
...membership.roles
|
|
@@ -1445,9 +1516,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1445
1516
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1446
1517
|
return (
|
|
1447
1518
|
// A member can have several periods in the same group.
|
|
1448
|
-
/* @__PURE__ */
|
|
1449
|
-
/* @__PURE__ */
|
|
1450
|
-
detailsText && /* @__PURE__ */
|
|
1519
|
+
/* @__PURE__ */ jsxs17("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1520
|
+
/* @__PURE__ */ jsx23("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1521
|
+
detailsText && /* @__PURE__ */ jsxs17("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1451
1522
|
" \u2014 ",
|
|
1452
1523
|
detailsText
|
|
1453
1524
|
] })
|
|
@@ -1458,9 +1529,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1458
1529
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1459
1530
|
if (memberships.length === 0) return null;
|
|
1460
1531
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1461
|
-
return /* @__PURE__ */
|
|
1462
|
-
/* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
1532
|
+
return /* @__PURE__ */ jsxs17(PanelSection3, { title, children: [
|
|
1533
|
+
/* @__PURE__ */ jsx23(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1534
|
+
/* @__PURE__ */ jsx23(
|
|
1464
1535
|
MembershipList,
|
|
1465
1536
|
{
|
|
1466
1537
|
title: "Past",
|
|
@@ -1473,7 +1544,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1473
1544
|
|
|
1474
1545
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1475
1546
|
import { PanelGroup as PanelGroup2, PanelSection as PanelSection4 } from "@behindthemusictree/ui";
|
|
1476
|
-
import { jsx as
|
|
1547
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1477
1548
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1478
1549
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1479
1550
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1493,12 +1564,12 @@ function byYear(a, b) {
|
|
|
1493
1564
|
function ArtistDiscography({ albums }) {
|
|
1494
1565
|
if (albums.length === 0) return null;
|
|
1495
1566
|
const sorted = [...albums].sort(byYear);
|
|
1496
|
-
return /* @__PURE__ */
|
|
1567
|
+
return /* @__PURE__ */ jsx24(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
|
|
1497
1568
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1498
1569
|
if (groupAlbums.length === 0) return null;
|
|
1499
|
-
return /* @__PURE__ */
|
|
1500
|
-
/* @__PURE__ */
|
|
1501
|
-
/* @__PURE__ */
|
|
1570
|
+
return /* @__PURE__ */ jsx24(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx24("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs18("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1571
|
+
/* @__PURE__ */ jsx24("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1572
|
+
/* @__PURE__ */ jsx24("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1502
1573
|
] }, album.uuid)) }) }, group);
|
|
1503
1574
|
}) });
|
|
1504
1575
|
}
|
|
@@ -1525,7 +1596,7 @@ import { useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre
|
|
|
1525
1596
|
import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
|
|
1526
1597
|
import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
|
|
1527
1598
|
import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
|
|
1528
|
-
import { jsx as
|
|
1599
|
+
import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1529
1600
|
function TrackPositionPlayPause({
|
|
1530
1601
|
position,
|
|
1531
1602
|
uuid,
|
|
@@ -1539,14 +1610,14 @@ function TrackPositionPlayPause({
|
|
|
1539
1610
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PLAYING;
|
|
1540
1611
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PAUSED;
|
|
1541
1612
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1542
|
-
return /* @__PURE__ */
|
|
1613
|
+
return /* @__PURE__ */ jsxs19(
|
|
1543
1614
|
"div",
|
|
1544
1615
|
{
|
|
1545
1616
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1546
1617
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1547
1618
|
onClick: handlePlayPauseClick,
|
|
1548
1619
|
children: [
|
|
1549
|
-
/* @__PURE__ */
|
|
1620
|
+
/* @__PURE__ */ jsx25("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs19(
|
|
1550
1621
|
"span",
|
|
1551
1622
|
{
|
|
1552
1623
|
className: "track-position-play-pause__bars",
|
|
@@ -1554,18 +1625,18 @@ function TrackPositionPlayPause({
|
|
|
1554
1625
|
role: "img",
|
|
1555
1626
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1556
1627
|
children: [
|
|
1557
|
-
/* @__PURE__ */
|
|
1558
|
-
/* @__PURE__ */
|
|
1559
|
-
/* @__PURE__ */
|
|
1628
|
+
/* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__bar" }),
|
|
1629
|
+
/* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__bar" }),
|
|
1630
|
+
/* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__bar" })
|
|
1560
1631
|
]
|
|
1561
1632
|
}
|
|
1562
1633
|
) : position }),
|
|
1563
|
-
/* @__PURE__ */
|
|
1634
|
+
/* @__PURE__ */ jsx25(
|
|
1564
1635
|
"div",
|
|
1565
1636
|
{
|
|
1566
1637
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1567
1638
|
"aria-hidden": "true",
|
|
1568
|
-
children: isAwaitingPlayback ? /* @__PURE__ */
|
|
1639
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx25(FaPause2, {}) : /* @__PURE__ */ jsx25(FaPlay2, {})
|
|
1569
1640
|
}
|
|
1570
1641
|
)
|
|
1571
1642
|
]
|
|
@@ -1576,7 +1647,7 @@ function TrackPositionPlayPause({
|
|
|
1576
1647
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1577
1648
|
import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1578
1649
|
import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1579
|
-
import { jsx as
|
|
1650
|
+
import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1580
1651
|
function TrackItem({
|
|
1581
1652
|
track,
|
|
1582
1653
|
position,
|
|
@@ -1600,12 +1671,12 @@ function TrackItem({
|
|
|
1600
1671
|
loadTrackForPlayer(track.uuid);
|
|
1601
1672
|
}
|
|
1602
1673
|
};
|
|
1603
|
-
return /* @__PURE__ */
|
|
1674
|
+
return /* @__PURE__ */ jsxs20(
|
|
1604
1675
|
"div",
|
|
1605
1676
|
{
|
|
1606
1677
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1607
1678
|
children: [
|
|
1608
|
-
isUnplayable ? /* @__PURE__ */
|
|
1679
|
+
isUnplayable ? /* @__PURE__ */ jsx26(
|
|
1609
1680
|
"div",
|
|
1610
1681
|
{
|
|
1611
1682
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1613,7 +1684,7 @@ function TrackItem({
|
|
|
1613
1684
|
"aria-disabled": "true",
|
|
1614
1685
|
children: position
|
|
1615
1686
|
}
|
|
1616
|
-
) : /* @__PURE__ */
|
|
1687
|
+
) : /* @__PURE__ */ jsx26(
|
|
1617
1688
|
TrackPositionPlayPause,
|
|
1618
1689
|
{
|
|
1619
1690
|
position,
|
|
@@ -1622,12 +1693,12 @@ function TrackItem({
|
|
|
1622
1693
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1623
1694
|
}
|
|
1624
1695
|
),
|
|
1625
|
-
/* @__PURE__ */
|
|
1626
|
-
/* @__PURE__ */
|
|
1627
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */
|
|
1696
|
+
/* @__PURE__ */ jsxs20("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1697
|
+
/* @__PURE__ */ jsx26("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx26("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx26(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1698
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx26("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx26(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1628
1699
|
] }),
|
|
1629
|
-
/* @__PURE__ */
|
|
1630
|
-
renderDuration && /* @__PURE__ */
|
|
1700
|
+
/* @__PURE__ */ jsx26("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx26("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1701
|
+
renderDuration && /* @__PURE__ */ jsx26(
|
|
1631
1702
|
"div",
|
|
1632
1703
|
{
|
|
1633
1704
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1635,14 +1706,14 @@ function TrackItem({
|
|
|
1635
1706
|
children: renderDuration(track)
|
|
1636
1707
|
}
|
|
1637
1708
|
),
|
|
1638
|
-
renderActions && /* @__PURE__ */
|
|
1709
|
+
renderActions && /* @__PURE__ */ jsx26("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1639
1710
|
]
|
|
1640
1711
|
}
|
|
1641
1712
|
);
|
|
1642
1713
|
}
|
|
1643
1714
|
|
|
1644
1715
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1645
|
-
import { Fragment as
|
|
1716
|
+
import { Fragment as Fragment10, jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1646
1717
|
var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
|
|
1647
1718
|
var SORTS = [
|
|
1648
1719
|
{ ordering: "title", label: "Title" },
|
|
@@ -1675,21 +1746,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1675
1746
|
event.stopPropagation();
|
|
1676
1747
|
void play(song.uuid);
|
|
1677
1748
|
};
|
|
1678
|
-
return /* @__PURE__ */
|
|
1679
|
-
/* @__PURE__ */
|
|
1680
|
-
/* @__PURE__ */
|
|
1749
|
+
return /* @__PURE__ */ jsx27(PanelSection5, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx27(Skeleton3, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx27("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx27("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs21(Fragment10, { children: [
|
|
1750
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
1751
|
+
/* @__PURE__ */ jsxs21(
|
|
1681
1752
|
"button",
|
|
1682
1753
|
{
|
|
1683
1754
|
type: "button",
|
|
1684
1755
|
onClick: () => void play(),
|
|
1685
1756
|
className: "flex w-fit items-center gap-1.5 px-3 py-1 text-xs font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
|
|
1686
1757
|
children: [
|
|
1687
|
-
/* @__PURE__ */
|
|
1758
|
+
/* @__PURE__ */ jsx27(Play2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1688
1759
|
"Play all"
|
|
1689
1760
|
]
|
|
1690
1761
|
}
|
|
1691
1762
|
),
|
|
1692
|
-
/* @__PURE__ */
|
|
1763
|
+
/* @__PURE__ */ jsx27("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ jsx27(
|
|
1693
1764
|
"button",
|
|
1694
1765
|
{
|
|
1695
1766
|
type: "button",
|
|
@@ -1704,7 +1775,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1704
1775
|
sort.ordering
|
|
1705
1776
|
)) })
|
|
1706
1777
|
] }),
|
|
1707
|
-
/* @__PURE__ */
|
|
1778
|
+
/* @__PURE__ */ jsx27("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx27("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx27(
|
|
1708
1779
|
TrackItem,
|
|
1709
1780
|
{
|
|
1710
1781
|
track: song,
|
|
@@ -1713,12 +1784,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1713
1784
|
renderSongLink
|
|
1714
1785
|
}
|
|
1715
1786
|
) }, song.uuid)) }),
|
|
1716
|
-
/* @__PURE__ */
|
|
1787
|
+
/* @__PURE__ */ jsx27(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1717
1788
|
] }) });
|
|
1718
1789
|
}
|
|
1719
1790
|
|
|
1720
1791
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1721
|
-
import { Fragment as
|
|
1792
|
+
import { Fragment as Fragment11, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1722
1793
|
function useArtistPanel({
|
|
1723
1794
|
uuid,
|
|
1724
1795
|
scope,
|
|
@@ -1739,26 +1810,26 @@ function useArtistPanel({
|
|
|
1739
1810
|
return {
|
|
1740
1811
|
...panel,
|
|
1741
1812
|
title: "Artist",
|
|
1742
|
-
content: /* @__PURE__ */
|
|
1743
|
-
/* @__PURE__ */
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
1746
|
-
/* @__PURE__ */
|
|
1747
|
-
/* @__PURE__ */
|
|
1748
|
-
/* @__PURE__ */
|
|
1813
|
+
content: /* @__PURE__ */ jsxs22("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
1814
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-3 w-16" }),
|
|
1815
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-4 w-32" }),
|
|
1816
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-3 w-16" }),
|
|
1817
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-4 w-24" }),
|
|
1818
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-16 mt-2.5" }),
|
|
1819
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-16" })
|
|
1749
1820
|
] })
|
|
1750
1821
|
};
|
|
1751
1822
|
case "not-found":
|
|
1752
1823
|
return {
|
|
1753
1824
|
...panel,
|
|
1754
1825
|
title: "Artist not found",
|
|
1755
|
-
content: /* @__PURE__ */
|
|
1826
|
+
content: /* @__PURE__ */ jsx28("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
1756
1827
|
};
|
|
1757
1828
|
case "error":
|
|
1758
1829
|
return {
|
|
1759
1830
|
...panel,
|
|
1760
1831
|
title: "Artist",
|
|
1761
|
-
content: /* @__PURE__ */
|
|
1832
|
+
content: /* @__PURE__ */ jsx28("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
1762
1833
|
};
|
|
1763
1834
|
case "success": {
|
|
1764
1835
|
const { artist } = result;
|
|
@@ -1767,13 +1838,13 @@ function useArtistPanel({
|
|
|
1767
1838
|
return {
|
|
1768
1839
|
...panel,
|
|
1769
1840
|
title: artist.name,
|
|
1770
|
-
content: /* @__PURE__ */
|
|
1771
|
-
shareUrl && /* @__PURE__ */
|
|
1772
|
-
artist.image && /* @__PURE__ */
|
|
1773
|
-
/* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
artist.tracksCount > 0 && /* @__PURE__ */
|
|
1841
|
+
content: /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
1842
|
+
shareUrl && /* @__PURE__ */ jsx28("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsx28("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ jsx28(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
1843
|
+
artist.image && /* @__PURE__ */ jsx28(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
1844
|
+
/* @__PURE__ */ jsx28(ArtistHeader, { artist }),
|
|
1845
|
+
/* @__PURE__ */ jsx28(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
1846
|
+
/* @__PURE__ */ jsx28(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
1847
|
+
artist.tracksCount > 0 && /* @__PURE__ */ jsx28(
|
|
1777
1848
|
ArtistSongs,
|
|
1778
1849
|
{
|
|
1779
1850
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -1783,7 +1854,7 @@ function useArtistPanel({
|
|
|
1783
1854
|
}
|
|
1784
1855
|
)
|
|
1785
1856
|
] }),
|
|
1786
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */
|
|
1857
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx28(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
1787
1858
|
};
|
|
1788
1859
|
}
|
|
1789
1860
|
}
|
|
@@ -1807,7 +1878,7 @@ import {
|
|
|
1807
1878
|
useSongHistory,
|
|
1808
1879
|
useTrackList as useTrackList5
|
|
1809
1880
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1810
|
-
import { Fragment as
|
|
1881
|
+
import { Fragment as Fragment12, jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1811
1882
|
var numberFormat = new Intl.NumberFormat("en");
|
|
1812
1883
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
1813
1884
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
@@ -1815,32 +1886,32 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
1815
1886
|
const [open, setOpen] = useState7(false);
|
|
1816
1887
|
const history = useSongHistory(uuid, getBackendBaseUrl, open);
|
|
1817
1888
|
let body = null;
|
|
1818
|
-
if (history.status === "loading") body = /* @__PURE__ */
|
|
1819
|
-
else if (history.status === "error") body = /* @__PURE__ */
|
|
1889
|
+
if (history.status === "loading") body = /* @__PURE__ */ jsx29(Skeleton5, { className: "h-10", "aria-busy": "true" });
|
|
1890
|
+
else if (history.status === "error") body = /* @__PURE__ */ jsx29("p", { className: "text-red-600", children: "Could not load the history." });
|
|
1820
1891
|
else if (history.status === "success")
|
|
1821
|
-
body = history.entries.length === 0 ? /* @__PURE__ */
|
|
1822
|
-
/* @__PURE__ */
|
|
1823
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */
|
|
1892
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ jsx29("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx29("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs23("li", { className: "py-1.5 text-sm", children: [
|
|
1893
|
+
/* @__PURE__ */ jsx29("span", { className: "font-medium", children: entry.action }),
|
|
1894
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs23("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1824
1895
|
" ",
|
|
1825
1896
|
"\u2014 ",
|
|
1826
1897
|
entry.oldValue ?? "none",
|
|
1827
1898
|
" \u2192 ",
|
|
1828
1899
|
entry.newValue ?? "none"
|
|
1829
1900
|
] }) : null,
|
|
1830
|
-
/* @__PURE__ */
|
|
1901
|
+
/* @__PURE__ */ jsxs23("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
1831
1902
|
entry.actorPseudo ?? "Anonymous",
|
|
1832
1903
|
" \xB7 ",
|
|
1833
1904
|
dateFormat.format(new Date(entry.createdOn))
|
|
1834
1905
|
] })
|
|
1835
1906
|
] }, entry.uuid)) });
|
|
1836
|
-
return /* @__PURE__ */
|
|
1907
|
+
return /* @__PURE__ */ jsxs23(
|
|
1837
1908
|
"details",
|
|
1838
1909
|
{
|
|
1839
1910
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
1840
1911
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
1841
1912
|
children: [
|
|
1842
|
-
/* @__PURE__ */
|
|
1843
|
-
/* @__PURE__ */
|
|
1913
|
+
/* @__PURE__ */ jsx29("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
1914
|
+
/* @__PURE__ */ jsx29("div", { className: "mt-1.5", children: body })
|
|
1844
1915
|
]
|
|
1845
1916
|
}
|
|
1846
1917
|
);
|
|
@@ -1866,13 +1937,13 @@ function SongPanelContent({
|
|
|
1866
1937
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
1867
1938
|
fields.push({
|
|
1868
1939
|
label: "Genre",
|
|
1869
|
-
value: path ? /* @__PURE__ */
|
|
1870
|
-
index > 0 && /* @__PURE__ */
|
|
1871
|
-
/* @__PURE__ */
|
|
1940
|
+
value: path ? /* @__PURE__ */ jsx29("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs23("span", { children: [
|
|
1941
|
+
index > 0 && /* @__PURE__ */ jsx29("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
1942
|
+
/* @__PURE__ */ jsx29("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
1872
1943
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
1873
1944
|
});
|
|
1874
1945
|
} else {
|
|
1875
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */
|
|
1946
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ jsx29("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
1876
1947
|
}
|
|
1877
1948
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
1878
1949
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -1882,11 +1953,11 @@ function SongPanelContent({
|
|
|
1882
1953
|
const links = [];
|
|
1883
1954
|
if (musicBrainzUrl)
|
|
1884
1955
|
links.push(
|
|
1885
|
-
/* @__PURE__ */
|
|
1956
|
+
/* @__PURE__ */ jsx29("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
1886
1957
|
);
|
|
1887
1958
|
if (song.youtubeVideo)
|
|
1888
1959
|
links.push(
|
|
1889
|
-
/* @__PURE__ */
|
|
1960
|
+
/* @__PURE__ */ jsx29(
|
|
1890
1961
|
"a",
|
|
1891
1962
|
{
|
|
1892
1963
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -1901,33 +1972,33 @@ function SongPanelContent({
|
|
|
1901
1972
|
if (links.length > 0)
|
|
1902
1973
|
fields.push({
|
|
1903
1974
|
label: "Links",
|
|
1904
|
-
value: /* @__PURE__ */
|
|
1975
|
+
value: /* @__PURE__ */ jsx29("span", { className: "flex gap-3", children: links })
|
|
1905
1976
|
});
|
|
1906
|
-
return /* @__PURE__ */
|
|
1907
|
-
/* @__PURE__ */
|
|
1908
|
-
unplayableReason ? /* @__PURE__ */
|
|
1977
|
+
return /* @__PURE__ */ jsxs23(Fragment12, { children: [
|
|
1978
|
+
/* @__PURE__ */ jsx29("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
1979
|
+
unplayableReason ? /* @__PURE__ */ jsx29("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs23(
|
|
1909
1980
|
"button",
|
|
1910
1981
|
{
|
|
1911
1982
|
type: "button",
|
|
1912
1983
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
1913
1984
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
1914
1985
|
children: [
|
|
1915
|
-
/* @__PURE__ */
|
|
1986
|
+
/* @__PURE__ */ jsx29("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ jsx29(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx29(Play3, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1916
1987
|
isPlaying ? "Pause" : "Play"
|
|
1917
1988
|
]
|
|
1918
1989
|
}
|
|
1919
1990
|
),
|
|
1920
|
-
shareUrl && /* @__PURE__ */
|
|
1991
|
+
shareUrl && /* @__PURE__ */ jsx29(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1921
1992
|
] }) }),
|
|
1922
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */
|
|
1923
|
-
/* @__PURE__ */
|
|
1924
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
1993
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx29("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx29(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
|
|
1994
|
+
/* @__PURE__ */ jsx29(FieldList2, { items: fields }),
|
|
1995
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx29(PanelSection6, { title: "Essential in", children: /* @__PURE__ */ jsx29("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx29("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
1996
|
+
/* @__PURE__ */ jsx29(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
1926
1997
|
] });
|
|
1927
1998
|
}
|
|
1928
1999
|
|
|
1929
2000
|
// src/genre-tree/song/useSongPanel.tsx
|
|
1930
|
-
import { jsx as
|
|
2001
|
+
import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1931
2002
|
function useSongPanel({
|
|
1932
2003
|
uuid,
|
|
1933
2004
|
scope,
|
|
@@ -1949,24 +2020,24 @@ function useSongPanel({
|
|
|
1949
2020
|
return {
|
|
1950
2021
|
...panel,
|
|
1951
2022
|
title: "Song",
|
|
1952
|
-
content: /* @__PURE__ */
|
|
1953
|
-
/* @__PURE__ */
|
|
1954
|
-
/* @__PURE__ */
|
|
1955
|
-
/* @__PURE__ */
|
|
1956
|
-
/* @__PURE__ */
|
|
2023
|
+
content: /* @__PURE__ */ jsxs24("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2024
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-3 w-16" }),
|
|
2025
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-4 w-32" }),
|
|
2026
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-3 w-16" }),
|
|
2027
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-4 w-24" })
|
|
1957
2028
|
] })
|
|
1958
2029
|
};
|
|
1959
2030
|
case "not-found":
|
|
1960
2031
|
return {
|
|
1961
2032
|
...panel,
|
|
1962
2033
|
title: "Song not found",
|
|
1963
|
-
content: /* @__PURE__ */
|
|
2034
|
+
content: /* @__PURE__ */ jsx30("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
1964
2035
|
};
|
|
1965
2036
|
case "error":
|
|
1966
2037
|
return {
|
|
1967
2038
|
...panel,
|
|
1968
2039
|
title: "Song",
|
|
1969
|
-
content: /* @__PURE__ */
|
|
2040
|
+
content: /* @__PURE__ */ jsx30("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
1970
2041
|
};
|
|
1971
2042
|
case "success": {
|
|
1972
2043
|
const { song } = result;
|
|
@@ -1974,7 +2045,7 @@ function useSongPanel({
|
|
|
1974
2045
|
return {
|
|
1975
2046
|
...panel,
|
|
1976
2047
|
title: song.title,
|
|
1977
|
-
content: /* @__PURE__ */
|
|
2048
|
+
content: /* @__PURE__ */ jsx30(
|
|
1978
2049
|
SongPanelContent,
|
|
1979
2050
|
{
|
|
1980
2051
|
song,
|
|
@@ -1994,12 +2065,12 @@ function useSongPanel({
|
|
|
1994
2065
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
1995
2066
|
import { MdMusicNote, MdVisibility } from "react-icons/md";
|
|
1996
2067
|
import { SegmentedControl } from "@behindthemusictree/ui";
|
|
1997
|
-
import { jsx as
|
|
2068
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1998
2069
|
function SizeMetricToggle({
|
|
1999
2070
|
value,
|
|
2000
2071
|
onChange
|
|
2001
2072
|
}) {
|
|
2002
|
-
return /* @__PURE__ */
|
|
2073
|
+
return /* @__PURE__ */ jsx31(
|
|
2003
2074
|
SegmentedControl,
|
|
2004
2075
|
{
|
|
2005
2076
|
variant: "floating",
|
|
@@ -2007,15 +2078,15 @@ function SizeMetricToggle({
|
|
|
2007
2078
|
value,
|
|
2008
2079
|
onChange,
|
|
2009
2080
|
options: [
|
|
2010
|
-
{ value: "songs", label: /* @__PURE__ */
|
|
2011
|
-
{ value: "views", label: /* @__PURE__ */
|
|
2081
|
+
{ value: "songs", label: /* @__PURE__ */ jsx31(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2082
|
+
{ value: "views", label: /* @__PURE__ */ jsx31(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2012
2083
|
]
|
|
2013
2084
|
}
|
|
2014
2085
|
);
|
|
2015
2086
|
}
|
|
2016
2087
|
|
|
2017
2088
|
// src/genre-tree/GenreTreeView.tsx
|
|
2018
|
-
import { Fragment as
|
|
2089
|
+
import { Fragment as Fragment13, jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2019
2090
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2020
2091
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2021
2092
|
var noop = () => {
|
|
@@ -2049,6 +2120,7 @@ function GenreTreeView({
|
|
|
2049
2120
|
artistExpanded: controlledArtistExpanded,
|
|
2050
2121
|
onArtistExpandedChange,
|
|
2051
2122
|
getShareUrl,
|
|
2123
|
+
extraSearchGroups = [],
|
|
2052
2124
|
artistSearch = true,
|
|
2053
2125
|
onArtistSelect,
|
|
2054
2126
|
artistSearchBaseUrl,
|
|
@@ -2158,7 +2230,7 @@ function GenreTreeView({
|
|
|
2158
2230
|
const openSongRef = useRef3(openSong);
|
|
2159
2231
|
openSongRef.current = openSong;
|
|
2160
2232
|
const resolvedRenderSongLink = useCallback6(
|
|
2161
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */
|
|
2233
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx32("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2162
2234
|
[renderSongLink]
|
|
2163
2235
|
);
|
|
2164
2236
|
const [expandedSongUuid, setExpandedSongUuid] = useState8(null);
|
|
@@ -2170,7 +2242,7 @@ function GenreTreeView({
|
|
|
2170
2242
|
scope,
|
|
2171
2243
|
getBackendBaseUrl: artistBaseUrl,
|
|
2172
2244
|
getShareUrl: getShareUrlRef.current,
|
|
2173
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2245
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx32("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2174
2246
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2175
2247
|
onExpandedChange: (on) => {
|
|
2176
2248
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2179,7 +2251,7 @@ function GenreTreeView({
|
|
|
2179
2251
|
renderSongLink: resolvedRenderSongLink,
|
|
2180
2252
|
onClose: () => changeArtist(null)
|
|
2181
2253
|
});
|
|
2182
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2254
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx32("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2183
2255
|
const getGenrePath = (genre) => {
|
|
2184
2256
|
const path = [];
|
|
2185
2257
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2258,52 +2330,27 @@ function GenreTreeView({
|
|
|
2258
2330
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2259
2331
|
const { showPopup, hidePopup } = usePopup5();
|
|
2260
2332
|
const [searchText, setSearchText] = useState8("");
|
|
2261
|
-
const
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
onArtistSelect?.(artist);
|
|
2283
|
-
}
|
|
2284
|
-
})),
|
|
2285
|
-
status: artistSearchResult.isError ? "Search failed" : artistSearchResult.settled && artistSearchResult.artists.length === 0 ? "No artists found" : "",
|
|
2286
|
-
loading: artistSearchResult.pending
|
|
2287
|
-
} : void 0;
|
|
2288
|
-
const songSearchResult = useSongSearch(songBaseUrl, songSearch ? searchText : "");
|
|
2289
|
-
const songGroup = songSearch ? {
|
|
2290
|
-
key: "songs",
|
|
2291
|
-
label: "Songs",
|
|
2292
|
-
items: songSearchResult.songs.map((song) => ({
|
|
2293
|
-
key: song.uuid,
|
|
2294
|
-
content: /* @__PURE__ */ jsxs22("span", { className: isSongSearchResultPlayable(song) ? void 0 : "opacity-60", children: [
|
|
2295
|
-
/* @__PURE__ */ jsx29("span", { className: "block truncate font-medium", children: song.title }),
|
|
2296
|
-
/* @__PURE__ */ jsx29("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
2297
|
-
] }),
|
|
2298
|
-
onSelect: () => {
|
|
2299
|
-
openSong(song.uuid);
|
|
2300
|
-
onSongSelect?.(song);
|
|
2301
|
-
}
|
|
2302
|
-
})),
|
|
2303
|
-
status: songSearchResult.isError ? "Search failed" : songSearchResult.settled && songSearchResult.songs.length === 0 ? "No songs found" : "",
|
|
2304
|
-
loading: songSearchResult.pending
|
|
2305
|
-
} : void 0;
|
|
2306
|
-
const extraGroups = [artistGroup, songGroup].filter((group) => group !== void 0);
|
|
2333
|
+
const artistGroup = useArtistSearchGroup(
|
|
2334
|
+
artistBaseUrl,
|
|
2335
|
+
searchText,
|
|
2336
|
+
(artist) => {
|
|
2337
|
+
openArtist(artist.uuid);
|
|
2338
|
+
onArtistSelect?.(artist);
|
|
2339
|
+
},
|
|
2340
|
+
Boolean(artistSearch)
|
|
2341
|
+
);
|
|
2342
|
+
const songGroup = useSongSearchGroup(
|
|
2343
|
+
songBaseUrl,
|
|
2344
|
+
searchText,
|
|
2345
|
+
(song) => {
|
|
2346
|
+
openSong(song.uuid);
|
|
2347
|
+
onSongSelect?.(song);
|
|
2348
|
+
},
|
|
2349
|
+
Boolean(songSearch)
|
|
2350
|
+
);
|
|
2351
|
+
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2352
|
+
(group) => group !== void 0
|
|
2353
|
+
);
|
|
2307
2354
|
const handleSelectedNodeChange = (node) => {
|
|
2308
2355
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2309
2356
|
selectNode(node);
|
|
@@ -2327,7 +2374,7 @@ function GenreTreeView({
|
|
|
2327
2374
|
};
|
|
2328
2375
|
const renderGenreBody = (relation) => {
|
|
2329
2376
|
const { node } = relation;
|
|
2330
|
-
const family = /* @__PURE__ */
|
|
2377
|
+
const family = /* @__PURE__ */ jsx32(
|
|
2331
2378
|
GenreNodeFamily,
|
|
2332
2379
|
{
|
|
2333
2380
|
node,
|
|
@@ -2338,10 +2385,10 @@ function GenreTreeView({
|
|
|
2338
2385
|
onSelectNode: selectGenreFromPanel
|
|
2339
2386
|
}
|
|
2340
2387
|
);
|
|
2341
|
-
const ancestors = /* @__PURE__ */
|
|
2388
|
+
const ancestors = /* @__PURE__ */ jsx32(GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2342
2389
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2343
2390
|
const criteria = genrePlaylist?.criteria;
|
|
2344
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */
|
|
2391
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2345
2392
|
family,
|
|
2346
2393
|
ancestors
|
|
2347
2394
|
] }) };
|
|
@@ -2361,11 +2408,11 @@ function GenreTreeView({
|
|
|
2361
2408
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2362
2409
|
) : void 0;
|
|
2363
2410
|
return {
|
|
2364
|
-
primary: /* @__PURE__ */
|
|
2365
|
-
/* @__PURE__ */
|
|
2411
|
+
primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2412
|
+
/* @__PURE__ */ jsx32(
|
|
2366
2413
|
GenreInfoPanelBody,
|
|
2367
2414
|
{
|
|
2368
|
-
family: /* @__PURE__ */
|
|
2415
|
+
family: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2369
2416
|
family,
|
|
2370
2417
|
ancestors
|
|
2371
2418
|
] }),
|
|
@@ -2383,7 +2430,7 @@ function GenreTreeView({
|
|
|
2383
2430
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2384
2431
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2385
2432
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2386
|
-
/* @__PURE__ */
|
|
2433
|
+
/* @__PURE__ */ jsx32(
|
|
2387
2434
|
ChangeParentPopup,
|
|
2388
2435
|
{
|
|
2389
2436
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2458,19 +2505,19 @@ function GenreTreeView({
|
|
|
2458
2505
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2459
2506
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2460
2507
|
} : void 0;
|
|
2461
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */
|
|
2508
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx32(
|
|
2462
2509
|
IconButton,
|
|
2463
2510
|
{
|
|
2464
2511
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2465
2512
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2466
2513
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2467
|
-
children: activePanel.expanded ? /* @__PURE__ */
|
|
2514
|
+
children: activePanel.expanded ? /* @__PURE__ */ jsx32(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx32(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2468
2515
|
}
|
|
2469
2516
|
) : null;
|
|
2470
2517
|
const renderArtistFilter = () => {
|
|
2471
2518
|
if (isLoading || !filterArtist) return null;
|
|
2472
|
-
return /* @__PURE__ */
|
|
2473
|
-
/* @__PURE__ */
|
|
2519
|
+
return /* @__PURE__ */ jsxs25("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2520
|
+
/* @__PURE__ */ jsx32(
|
|
2474
2521
|
"button",
|
|
2475
2522
|
{
|
|
2476
2523
|
type: "button",
|
|
@@ -2482,7 +2529,7 @@ function GenreTreeView({
|
|
|
2482
2529
|
children: filterArtist.name
|
|
2483
2530
|
}
|
|
2484
2531
|
),
|
|
2485
|
-
/* @__PURE__ */
|
|
2532
|
+
/* @__PURE__ */ jsx32(
|
|
2486
2533
|
"button",
|
|
2487
2534
|
{
|
|
2488
2535
|
type: "button",
|
|
@@ -2494,8 +2541,8 @@ function GenreTreeView({
|
|
|
2494
2541
|
)
|
|
2495
2542
|
] });
|
|
2496
2543
|
};
|
|
2497
|
-
const actions = /* @__PURE__ */
|
|
2498
|
-
!isLoading && /* @__PURE__ */
|
|
2544
|
+
const actions = /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2545
|
+
!isLoading && /* @__PURE__ */ jsx32(
|
|
2499
2546
|
GenreSearch,
|
|
2500
2547
|
{
|
|
2501
2548
|
genrePlaylists: visibleGenrePlaylists,
|
|
@@ -2503,13 +2550,13 @@ function GenreTreeView({
|
|
|
2503
2550
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2504
2551
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2505
2552
|
onPrevious: handlePrevious,
|
|
2506
|
-
onQueryChange: artistSearch || songSearch ? setSearchText : void 0,
|
|
2553
|
+
onQueryChange: artistSearch || songSearch || extraSearchGroups.length > 0 ? setSearchText : void 0,
|
|
2507
2554
|
extraGroups
|
|
2508
2555
|
}
|
|
2509
2556
|
),
|
|
2510
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2511
|
-
/* @__PURE__ */
|
|
2512
|
-
!isLoading && !isControlled && /* @__PURE__ */
|
|
2557
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx32("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2558
|
+
/* @__PURE__ */ jsxs25("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: [
|
|
2559
|
+
!isLoading && !isControlled && /* @__PURE__ */ jsx32(
|
|
2513
2560
|
SegmentedControl2,
|
|
2514
2561
|
{
|
|
2515
2562
|
"aria-label": "Tree view mode",
|
|
@@ -2523,13 +2570,13 @@ function GenreTreeView({
|
|
|
2523
2570
|
]
|
|
2524
2571
|
}
|
|
2525
2572
|
),
|
|
2526
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */
|
|
2573
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ jsx32(
|
|
2527
2574
|
"div",
|
|
2528
2575
|
{
|
|
2529
2576
|
className: "flex items-center gap-1",
|
|
2530
2577
|
role: "group",
|
|
2531
2578
|
"aria-label": "Tree display options",
|
|
2532
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */
|
|
2579
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx32(
|
|
2533
2580
|
Button,
|
|
2534
2581
|
{
|
|
2535
2582
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2540,7 +2587,7 @@ function GenreTreeView({
|
|
|
2540
2587
|
)
|
|
2541
2588
|
}
|
|
2542
2589
|
),
|
|
2543
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */
|
|
2590
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ jsx32(
|
|
2544
2591
|
IconTextButton,
|
|
2545
2592
|
{
|
|
2546
2593
|
icon: Plus2,
|
|
@@ -2550,34 +2597,34 @@ function GenreTreeView({
|
|
|
2550
2597
|
)
|
|
2551
2598
|
] })
|
|
2552
2599
|
] });
|
|
2553
|
-
return /* @__PURE__ */
|
|
2600
|
+
return /* @__PURE__ */ jsxs25(
|
|
2554
2601
|
"div",
|
|
2555
2602
|
{
|
|
2556
2603
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2557
2604
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2558
2605
|
children: [
|
|
2559
|
-
/* @__PURE__ */
|
|
2606
|
+
/* @__PURE__ */ jsx32(
|
|
2560
2607
|
"div",
|
|
2561
2608
|
{
|
|
2562
2609
|
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
|
|
2563
2610
|
children: actions
|
|
2564
2611
|
}
|
|
2565
2612
|
),
|
|
2566
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2613
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx32(
|
|
2567
2614
|
"div",
|
|
2568
2615
|
{
|
|
2569
2616
|
className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))]" : "top-5"}`,
|
|
2570
2617
|
children: renderArtistFilter()
|
|
2571
2618
|
}
|
|
2572
2619
|
),
|
|
2573
|
-
/* @__PURE__ */
|
|
2574
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */
|
|
2575
|
-
isLoading ? /* @__PURE__ */
|
|
2620
|
+
/* @__PURE__ */ jsx32("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs25("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2621
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx32("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2622
|
+
isLoading ? /* @__PURE__ */ jsx32(GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2576
2623
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2577
2624
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2578
2625
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2579
2626
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2580
|
-
/* @__PURE__ */
|
|
2627
|
+
/* @__PURE__ */ jsx32("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx32(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx32(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx32(
|
|
2581
2628
|
GenrePlaylistTreeWheel,
|
|
2582
2629
|
{
|
|
2583
2630
|
scope,
|
|
@@ -2594,9 +2641,9 @@ function GenreTreeView({
|
|
|
2594
2641
|
allowWheelRotation,
|
|
2595
2642
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2596
2643
|
busy,
|
|
2597
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2644
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2598
2645
|
floatingControlsLeading,
|
|
2599
|
-
canSizeByViews && /* @__PURE__ */
|
|
2646
|
+
canSizeByViews && /* @__PURE__ */ jsx32(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2600
2647
|
] }) : void 0,
|
|
2601
2648
|
defaultViewport,
|
|
2602
2649
|
viewportRestore,
|
|
@@ -2605,7 +2652,7 @@ function GenreTreeView({
|
|
|
2605
2652
|
) }) })
|
|
2606
2653
|
) : viewMode === "pop-core" ? (
|
|
2607
2654
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2608
|
-
/* @__PURE__ */
|
|
2655
|
+
/* @__PURE__ */ jsx32("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx32(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx32(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx32(
|
|
2609
2656
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2610
2657
|
{
|
|
2611
2658
|
scope,
|
|
@@ -2622,9 +2669,9 @@ function GenreTreeView({
|
|
|
2622
2669
|
allowWheelRotation,
|
|
2623
2670
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2624
2671
|
busy,
|
|
2625
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2672
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2626
2673
|
floatingControlsLeading,
|
|
2627
|
-
canSizeByViews && /* @__PURE__ */
|
|
2674
|
+
canSizeByViews && /* @__PURE__ */ jsx32(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2628
2675
|
] }) : void 0,
|
|
2629
2676
|
defaultViewport,
|
|
2630
2677
|
viewportRestore,
|
|
@@ -2632,7 +2679,7 @@ function GenreTreeView({
|
|
|
2632
2679
|
...focus
|
|
2633
2680
|
}
|
|
2634
2681
|
) }) })
|
|
2635
|
-
) : viewMode === "outline" ? /* @__PURE__ */
|
|
2682
|
+
) : viewMode === "outline" ? /* @__PURE__ */ jsx32("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx32(
|
|
2636
2683
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2637
2684
|
{
|
|
2638
2685
|
outline: true,
|
|
@@ -2648,13 +2695,13 @@ function GenreTreeView({
|
|
|
2648
2695
|
selectedNodeId,
|
|
2649
2696
|
readOnly
|
|
2650
2697
|
}
|
|
2651
|
-
) }) : /* @__PURE__ */
|
|
2698
|
+
) }) : /* @__PURE__ */ jsx32("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(
|
|
2652
2699
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2653
|
-
return /* @__PURE__ */
|
|
2700
|
+
return /* @__PURE__ */ jsx32(
|
|
2654
2701
|
"div",
|
|
2655
2702
|
{
|
|
2656
2703
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2657
|
-
children: /* @__PURE__ */
|
|
2704
|
+
children: /* @__PURE__ */ jsx32("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx32(
|
|
2658
2705
|
GenrePlaylistTreePerRoot,
|
|
2659
2706
|
{
|
|
2660
2707
|
scope,
|
|
@@ -2680,7 +2727,7 @@ function GenreTreeView({
|
|
|
2680
2727
|
);
|
|
2681
2728
|
}
|
|
2682
2729
|
) }),
|
|
2683
|
-
/* @__PURE__ */
|
|
2730
|
+
/* @__PURE__ */ jsx32(
|
|
2684
2731
|
DetailPanel,
|
|
2685
2732
|
{
|
|
2686
2733
|
ref: panelRef,
|
|
@@ -2697,7 +2744,7 @@ function GenreTreeView({
|
|
|
2697
2744
|
aside: activePanel?.aside,
|
|
2698
2745
|
edgeTab: detailTab,
|
|
2699
2746
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
2700
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */
|
|
2747
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2701
2748
|
activePanel.content,
|
|
2702
2749
|
activePanel.aside
|
|
2703
2750
|
] }) : activePanel?.content
|
|
@@ -2724,7 +2771,7 @@ import { Maximize2 as Maximize22, Minimize2 as Minimize22 } from "lucide-react";
|
|
|
2724
2771
|
import {
|
|
2725
2772
|
useFetchGenreDetail
|
|
2726
2773
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
2727
|
-
import { Fragment as
|
|
2774
|
+
import { Fragment as Fragment14, jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2728
2775
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2729
2776
|
function toNode(genre, parentId) {
|
|
2730
2777
|
return {
|
|
@@ -2779,17 +2826,17 @@ function GenreDetailPanel({
|
|
|
2779
2826
|
};
|
|
2780
2827
|
}, [detail]);
|
|
2781
2828
|
if (isError) {
|
|
2782
|
-
return /* @__PURE__ */
|
|
2829
|
+
return /* @__PURE__ */ jsx33("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
2783
2830
|
}
|
|
2784
2831
|
if (isPending || !detail || !view) {
|
|
2785
|
-
return /* @__PURE__ */
|
|
2786
|
-
/* @__PURE__ */
|
|
2787
|
-
/* @__PURE__ */
|
|
2788
|
-
/* @__PURE__ */
|
|
2832
|
+
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2833
|
+
/* @__PURE__ */ jsx33(Skeleton7, { className: "h-6 w-2/3" }),
|
|
2834
|
+
/* @__PURE__ */ jsx33(Skeleton7, { className: "h-4 w-full" }),
|
|
2835
|
+
/* @__PURE__ */ jsx33(Skeleton7, { className: "h-4 w-3/4" })
|
|
2789
2836
|
] });
|
|
2790
2837
|
}
|
|
2791
2838
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
2792
|
-
const family = /* @__PURE__ */
|
|
2839
|
+
const family = /* @__PURE__ */ jsx33(
|
|
2793
2840
|
GenreNodeFamily2,
|
|
2794
2841
|
{
|
|
2795
2842
|
node: view.node,
|
|
@@ -2800,20 +2847,20 @@ function GenreDetailPanel({
|
|
|
2800
2847
|
onSelectNode: selectGenre
|
|
2801
2848
|
}
|
|
2802
2849
|
);
|
|
2803
|
-
const ancestors = /* @__PURE__ */
|
|
2804
|
-
return /* @__PURE__ */
|
|
2850
|
+
const ancestors = /* @__PURE__ */ jsx33(GenreNodeAncestors2, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2851
|
+
return /* @__PURE__ */ jsxs26(
|
|
2805
2852
|
DetailPanel2,
|
|
2806
2853
|
{
|
|
2807
2854
|
side: "left",
|
|
2808
2855
|
title: view.node.name,
|
|
2809
2856
|
headerAccent: { background: view.fill, color: ACCENT_TEXT_COLOR },
|
|
2810
|
-
headerActions: onExpandedChange ? /* @__PURE__ */
|
|
2857
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ jsx33(
|
|
2811
2858
|
IconButton2,
|
|
2812
2859
|
{
|
|
2813
2860
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
2814
2861
|
style: { color: "inherit" },
|
|
2815
2862
|
onClick: () => onExpandedChange(!expanded),
|
|
2816
|
-
children: expanded ? /* @__PURE__ */
|
|
2863
|
+
children: expanded ? /* @__PURE__ */ jsx33(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx33(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2817
2864
|
}
|
|
2818
2865
|
) : void 0,
|
|
2819
2866
|
onClose,
|
|
@@ -2822,10 +2869,10 @@ function GenreDetailPanel({
|
|
|
2822
2869
|
onExpandedChange,
|
|
2823
2870
|
edgeTab,
|
|
2824
2871
|
children: [
|
|
2825
|
-
/* @__PURE__ */
|
|
2872
|
+
/* @__PURE__ */ jsx33(
|
|
2826
2873
|
GenreInfoPanelBody,
|
|
2827
2874
|
{
|
|
2828
|
-
family: /* @__PURE__ */
|
|
2875
|
+
family: /* @__PURE__ */ jsxs26(Fragment14, { children: [
|
|
2829
2876
|
family,
|
|
2830
2877
|
ancestors
|
|
2831
2878
|
] }),
|
|
@@ -2859,7 +2906,7 @@ import { ListMusic } from "lucide-react";
|
|
|
2859
2906
|
import { useTrackList as useTrackList6 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2860
2907
|
import { TrackListOriginType as TrackListOriginType2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2861
2908
|
import { useTrackNoun as useTrackNoun3 } from "@behindthemusictree/app-kit/track-noun";
|
|
2862
|
-
import { jsx as
|
|
2909
|
+
import { jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2863
2910
|
var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect2;
|
|
2864
2911
|
function TrackListSidebar({
|
|
2865
2912
|
className: className2 = "",
|
|
@@ -2961,25 +3008,25 @@ function TrackListSidebar({
|
|
|
2961
3008
|
}
|
|
2962
3009
|
};
|
|
2963
3010
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
2964
|
-
return trackList ? /* @__PURE__ */
|
|
3011
|
+
return trackList ? /* @__PURE__ */ jsxs27(
|
|
2965
3012
|
"div",
|
|
2966
3013
|
{
|
|
2967
3014
|
"data-theme": "dark",
|
|
2968
3015
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
2969
3016
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
2970
3017
|
children: [
|
|
2971
|
-
/* @__PURE__ */
|
|
2972
|
-
/* @__PURE__ */
|
|
2973
|
-
/* @__PURE__ */
|
|
2974
|
-
/* @__PURE__ */
|
|
2975
|
-
/* @__PURE__ */
|
|
3018
|
+
/* @__PURE__ */ jsxs27("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3019
|
+
/* @__PURE__ */ jsxs27("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3020
|
+
/* @__PURE__ */ jsxs27("div", { className: "origin flex text-xl", children: [
|
|
3021
|
+
/* @__PURE__ */ jsx34("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3022
|
+
/* @__PURE__ */ jsx34("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__ */ jsx34("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
2976
3023
|
] }),
|
|
2977
|
-
/* @__PURE__ */
|
|
3024
|
+
/* @__PURE__ */ jsxs27("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
2978
3025
|
trackList.origin.type === TrackListOriginType2.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType2.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
2979
3026
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
2980
3027
|
] })
|
|
2981
3028
|
] }),
|
|
2982
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */
|
|
3029
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx34(
|
|
2983
3030
|
"button",
|
|
2984
3031
|
{
|
|
2985
3032
|
type: "button",
|
|
@@ -2988,11 +3035,11 @@ function TrackListSidebar({
|
|
|
2988
3035
|
"aria-expanded": !isCollapsed,
|
|
2989
3036
|
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"}`,
|
|
2990
3037
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
2991
|
-
children: /* @__PURE__ */
|
|
3038
|
+
children: /* @__PURE__ */ jsx34(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
2992
3039
|
}
|
|
2993
3040
|
)
|
|
2994
3041
|
] }),
|
|
2995
|
-
!isCollapsed && /* @__PURE__ */
|
|
3042
|
+
!isCollapsed && /* @__PURE__ */ jsxs27(
|
|
2996
3043
|
"ul",
|
|
2997
3044
|
{
|
|
2998
3045
|
ref: trackListRef,
|
|
@@ -3002,7 +3049,7 @@ function TrackListSidebar({
|
|
|
3002
3049
|
onPointerDown: markUserScrollIntent,
|
|
3003
3050
|
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",
|
|
3004
3051
|
children: [
|
|
3005
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */
|
|
3052
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ jsx34("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx34(
|
|
3006
3053
|
TrackItem,
|
|
3007
3054
|
{
|
|
3008
3055
|
track,
|
|
@@ -3013,7 +3060,7 @@ function TrackListSidebar({
|
|
|
3013
3060
|
renderSongLink
|
|
3014
3061
|
}
|
|
3015
3062
|
) }, track.uuid)),
|
|
3016
|
-
hasMore && /* @__PURE__ */
|
|
3063
|
+
hasMore && /* @__PURE__ */ jsx34("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3017
3064
|
]
|
|
3018
3065
|
}
|
|
3019
3066
|
)
|
|
@@ -3038,7 +3085,7 @@ import { PanelEdgeTab } from "@behindthemusictree/ui";
|
|
|
3038
3085
|
import { Pause as Pause3, Play as Play4, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
|
|
3039
3086
|
import { formatTime, useTrackList as useTrackList7 } from "@behindthemusictree/app-kit/genre-tree";
|
|
3040
3087
|
import { PlayStates as PlayStates4, useCurrentTime, usePlayer as usePlayer10 } from "@behindthemusictree/app-kit/player";
|
|
3041
|
-
import { jsx as
|
|
3088
|
+
import { jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3042
3089
|
function PlayerControls() {
|
|
3043
3090
|
const {
|
|
3044
3091
|
duration,
|
|
@@ -3057,15 +3104,15 @@ function PlayerControls() {
|
|
|
3057
3104
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3058
3105
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3059
3106
|
const isPlaying = playState === PlayStates4.PLAYING;
|
|
3060
|
-
return /* @__PURE__ */
|
|
3107
|
+
return /* @__PURE__ */ jsxs28(
|
|
3061
3108
|
"section",
|
|
3062
3109
|
{
|
|
3063
3110
|
"aria-label": "Player controls",
|
|
3064
3111
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3065
3112
|
children: [
|
|
3066
|
-
/* @__PURE__ */
|
|
3067
|
-
/* @__PURE__ */
|
|
3068
|
-
/* @__PURE__ */
|
|
3113
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3114
|
+
/* @__PURE__ */ jsx35("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
|
|
3115
|
+
/* @__PURE__ */ jsx35(
|
|
3069
3116
|
"input",
|
|
3070
3117
|
{
|
|
3071
3118
|
"aria-label": "Seek",
|
|
@@ -3079,11 +3126,11 @@ function PlayerControls() {
|
|
|
3079
3126
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3080
3127
|
}
|
|
3081
3128
|
),
|
|
3082
|
-
/* @__PURE__ */
|
|
3129
|
+
/* @__PURE__ */ jsx35("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
|
|
3083
3130
|
] }),
|
|
3084
|
-
/* @__PURE__ */
|
|
3085
|
-
/* @__PURE__ */
|
|
3086
|
-
/* @__PURE__ */
|
|
3131
|
+
/* @__PURE__ */ jsxs28("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3132
|
+
/* @__PURE__ */ jsxs28("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3133
|
+
/* @__PURE__ */ jsx35(
|
|
3087
3134
|
"button",
|
|
3088
3135
|
{
|
|
3089
3136
|
type: "button",
|
|
@@ -3092,10 +3139,10 @@ function PlayerControls() {
|
|
|
3092
3139
|
disabled: !isReady || !hasPreviousTrack,
|
|
3093
3140
|
onClick: playPreviousTrack,
|
|
3094
3141
|
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",
|
|
3095
|
-
children: /* @__PURE__ */
|
|
3142
|
+
children: /* @__PURE__ */ jsx35(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3096
3143
|
}
|
|
3097
3144
|
),
|
|
3098
|
-
/* @__PURE__ */
|
|
3145
|
+
/* @__PURE__ */ jsx35(
|
|
3099
3146
|
"button",
|
|
3100
3147
|
{
|
|
3101
3148
|
type: "button",
|
|
@@ -3104,10 +3151,10 @@ function PlayerControls() {
|
|
|
3104
3151
|
disabled: !isReady,
|
|
3105
3152
|
onClick: handlePlayPauseAction,
|
|
3106
3153
|
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",
|
|
3107
|
-
children: isPlaying ? /* @__PURE__ */
|
|
3154
|
+
children: isPlaying ? /* @__PURE__ */ jsx35(Pause3, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx35(Play4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3108
3155
|
}
|
|
3109
3156
|
),
|
|
3110
|
-
/* @__PURE__ */
|
|
3157
|
+
/* @__PURE__ */ jsx35(
|
|
3111
3158
|
"button",
|
|
3112
3159
|
{
|
|
3113
3160
|
type: "button",
|
|
@@ -3116,14 +3163,14 @@ function PlayerControls() {
|
|
|
3116
3163
|
disabled: !isReady || !hasNextTrack,
|
|
3117
3164
|
onClick: playNextTrack,
|
|
3118
3165
|
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",
|
|
3119
|
-
children: /* @__PURE__ */
|
|
3166
|
+
children: /* @__PURE__ */ jsx35(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3120
3167
|
}
|
|
3121
3168
|
)
|
|
3122
3169
|
] }),
|
|
3123
|
-
/* @__PURE__ */
|
|
3124
|
-
volume === 0 ? /* @__PURE__ */
|
|
3125
|
-
/* @__PURE__ */
|
|
3126
|
-
/* @__PURE__ */
|
|
3170
|
+
/* @__PURE__ */ jsxs28("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: [
|
|
3171
|
+
volume === 0 ? /* @__PURE__ */ jsx35(VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ jsx35(Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3172
|
+
/* @__PURE__ */ jsx35("span", { className: "sr-only", children: "Volume" }),
|
|
3173
|
+
/* @__PURE__ */ jsx35(
|
|
3127
3174
|
"input",
|
|
3128
3175
|
{
|
|
3129
3176
|
"aria-label": "Volume",
|
|
@@ -3145,7 +3192,7 @@ function PlayerControls() {
|
|
|
3145
3192
|
}
|
|
3146
3193
|
|
|
3147
3194
|
// src/genre-tree/PlayerPanel.tsx
|
|
3148
|
-
import { jsx as
|
|
3195
|
+
import { jsx as jsx36, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3149
3196
|
var PANEL_ID = "player-panel";
|
|
3150
3197
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3151
3198
|
function PlayerPanel({
|
|
@@ -3157,7 +3204,7 @@ function PlayerPanel({
|
|
|
3157
3204
|
renderArtistLink,
|
|
3158
3205
|
renderSongLink
|
|
3159
3206
|
}) {
|
|
3160
|
-
const { playerTrackObject } = usePlayer11();
|
|
3207
|
+
const { playerTrackObject, retryPlayback } = usePlayer11();
|
|
3161
3208
|
const { toggleTrackListSidebar, isTrackListSidebarVisible } = useTrackListSidebarVisibility();
|
|
3162
3209
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = useState9("");
|
|
3163
3210
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
@@ -3185,7 +3232,7 @@ function PlayerPanel({
|
|
|
3185
3232
|
onClick: () => setIsExtendRequested(true),
|
|
3186
3233
|
expanded: false
|
|
3187
3234
|
} : null;
|
|
3188
|
-
return /* @__PURE__ */
|
|
3235
|
+
return /* @__PURE__ */ jsxs29(
|
|
3189
3236
|
"div",
|
|
3190
3237
|
{
|
|
3191
3238
|
id: PANEL_ID,
|
|
@@ -3198,7 +3245,7 @@ function PlayerPanel({
|
|
|
3198
3245
|
),
|
|
3199
3246
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3200
3247
|
children: [
|
|
3201
|
-
/* @__PURE__ */
|
|
3248
|
+
/* @__PURE__ */ jsx36(
|
|
3202
3249
|
PanelEdgeTab,
|
|
3203
3250
|
{
|
|
3204
3251
|
side: "right",
|
|
@@ -3208,7 +3255,7 @@ function PlayerPanel({
|
|
|
3208
3255
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3209
3256
|
}
|
|
3210
3257
|
),
|
|
3211
|
-
/* @__PURE__ */
|
|
3258
|
+
/* @__PURE__ */ jsxs29(
|
|
3212
3259
|
"div",
|
|
3213
3260
|
{
|
|
3214
3261
|
className: twMerge(
|
|
@@ -3216,15 +3263,20 @@ function PlayerPanel({
|
|
|
3216
3263
|
isExtended ? "flex-row" : "flex-col"
|
|
3217
3264
|
),
|
|
3218
3265
|
children: [
|
|
3219
|
-
/* @__PURE__ */
|
|
3220
|
-
/* @__PURE__ */
|
|
3221
|
-
/* @__PURE__ */
|
|
3222
|
-
/* @__PURE__ */
|
|
3223
|
-
playerTrackObject?.loadError && /* @__PURE__ */
|
|
3266
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3267
|
+
/* @__PURE__ */ jsx36("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ jsx36("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3268
|
+
/* @__PURE__ */ jsxs29("div", { className: "relative w-full shrink-0", children: [
|
|
3269
|
+
/* @__PURE__ */ jsx36(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3270
|
+
playerTrackObject?.loadError && /* @__PURE__ */ jsxs29("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3271
|
+
playerTrackObject.loadError,
|
|
3272
|
+
/* @__PURE__ */ jsx36("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3273
|
+
void retryPlayback();
|
|
3274
|
+
}, children: "Retry" })
|
|
3275
|
+
] })
|
|
3224
3276
|
] }),
|
|
3225
|
-
/* @__PURE__ */
|
|
3277
|
+
/* @__PURE__ */ jsx36(PlayerControls, {})
|
|
3226
3278
|
] }),
|
|
3227
|
-
/* @__PURE__ */
|
|
3279
|
+
/* @__PURE__ */ jsx36(
|
|
3228
3280
|
TrackListSidebar,
|
|
3229
3281
|
{
|
|
3230
3282
|
layout: "inline",
|
|
@@ -3251,11 +3303,11 @@ function PlayerPanel({
|
|
|
3251
3303
|
import { v4 as uuidv4 } from "uuid";
|
|
3252
3304
|
import { AiFillStar, AiOutlineStar } from "react-icons/ai";
|
|
3253
3305
|
import { FORM_RATING_NULL_VALUE } from "@behindthemusictree/app-kit/genre-tree";
|
|
3254
|
-
import { jsx as
|
|
3306
|
+
import { jsx as jsx37, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3255
3307
|
function Rating({ rating, handleChange }) {
|
|
3256
3308
|
const instanceId = uuidv4();
|
|
3257
|
-
return /* @__PURE__ */
|
|
3258
|
-
/* @__PURE__ */
|
|
3309
|
+
return /* @__PURE__ */ jsx37("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs30("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3310
|
+
/* @__PURE__ */ jsx37(
|
|
3259
3311
|
"input",
|
|
3260
3312
|
{
|
|
3261
3313
|
id: `star-${instanceId}-${i}`,
|
|
@@ -3267,16 +3319,16 @@ function Rating({ rating, handleChange }) {
|
|
|
3267
3319
|
onChange: handleChange
|
|
3268
3320
|
}
|
|
3269
3321
|
),
|
|
3270
|
-
/* @__PURE__ */
|
|
3271
|
-
/* @__PURE__ */
|
|
3322
|
+
/* @__PURE__ */ jsx37("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs30("div", { className: "flex", children: [
|
|
3323
|
+
/* @__PURE__ */ jsx37(
|
|
3272
3324
|
AiOutlineStar,
|
|
3273
3325
|
{
|
|
3274
3326
|
className: `w-7 h-7 absolute top-0 left-0 ${rating === FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3275
3327
|
}
|
|
3276
3328
|
),
|
|
3277
|
-
/* @__PURE__ */
|
|
3329
|
+
/* @__PURE__ */ jsx37("span", { className: "flex-grow w-8" }),
|
|
3278
3330
|
" "
|
|
3279
|
-
] }) : /* @__PURE__ */
|
|
3331
|
+
] }) : /* @__PURE__ */ jsx37(
|
|
3280
3332
|
AiFillStar,
|
|
3281
3333
|
{
|
|
3282
3334
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3288,6 +3340,12 @@ function Rating({ rating, handleChange }) {
|
|
|
3288
3340
|
export {
|
|
3289
3341
|
useGenrePlaylistPlayback,
|
|
3290
3342
|
GenrePlaylistTreePerRoot,
|
|
3343
|
+
SearchBox,
|
|
3344
|
+
SEARCH_FAILED_STATUS,
|
|
3345
|
+
searchStatus,
|
|
3346
|
+
useArtistSearchGroup,
|
|
3347
|
+
useSongSearchGroup,
|
|
3348
|
+
useGenreRemoteSearchGroup,
|
|
3291
3349
|
GenreSearch,
|
|
3292
3350
|
GenreNodeActionBand,
|
|
3293
3351
|
GenreInfoPanelBody,
|
|
@@ -3303,4 +3361,4 @@ export {
|
|
|
3303
3361
|
GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
|
|
3304
3362
|
GenreTreeOutlineSkeleton
|
|
3305
3363
|
};
|
|
3306
|
-
//# sourceMappingURL=chunk-
|
|
3364
|
+
//# sourceMappingURL=chunk-FHGQGTCN.js.map
|