@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
|
@@ -32,10 +32,16 @@ __export(genre_tree_exports, {
|
|
|
32
32
|
GenreTreeWheelSkeleton: () => import_genre_tree_view6.GenreTreeWheelSkeleton,
|
|
33
33
|
PlayerPanel: () => PlayerPanel,
|
|
34
34
|
Rating: () => Rating,
|
|
35
|
+
SEARCH_FAILED_STATUS: () => SEARCH_FAILED_STATUS,
|
|
36
|
+
SearchBox: () => SearchBox,
|
|
35
37
|
TrackItem: () => TrackItem,
|
|
36
38
|
TrackListSidebar: () => TrackListSidebar,
|
|
37
39
|
TrackPositionPlayPause: () => TrackPositionPlayPause,
|
|
38
|
-
|
|
40
|
+
searchStatus: () => searchStatus,
|
|
41
|
+
useArtistSearchGroup: () => useArtistSearchGroup,
|
|
42
|
+
useGenrePlaylistPlayback: () => useGenrePlaylistPlayback,
|
|
43
|
+
useGenreRemoteSearchGroup: () => useGenreRemoteSearchGroup,
|
|
44
|
+
useSongSearchGroup: () => useSongSearchGroup
|
|
39
45
|
});
|
|
40
46
|
module.exports = __toCommonJS(genre_tree_exports);
|
|
41
47
|
|
|
@@ -44,10 +50,9 @@ var import_react16 = require("react");
|
|
|
44
50
|
var import_react_query3 = require("@tanstack/react-query");
|
|
45
51
|
var import_lucide_react11 = require("lucide-react");
|
|
46
52
|
var import_lucide_react12 = require("lucide-react");
|
|
47
|
-
var
|
|
53
|
+
var import_ui16 = require("@behindthemusictree/ui");
|
|
48
54
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
49
|
-
var
|
|
50
|
-
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
55
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
51
56
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
52
57
|
|
|
53
58
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -695,11 +700,143 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
695
700
|
}
|
|
696
701
|
|
|
697
702
|
// src/genre-tree/GenreSearch.tsx
|
|
698
|
-
var import_react7 = require("react");
|
|
699
703
|
var import_lucide_react3 = require("lucide-react");
|
|
704
|
+
var import_ui4 = require("@behindthemusictree/ui");
|
|
705
|
+
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
706
|
+
|
|
707
|
+
// src/genre-tree/search/SearchBox.tsx
|
|
708
|
+
var import_react7 = require("react");
|
|
700
709
|
var import_ui3 = require("@behindthemusictree/ui");
|
|
701
|
-
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
702
710
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
711
|
+
function SearchBox({
|
|
712
|
+
label,
|
|
713
|
+
placeholder,
|
|
714
|
+
value,
|
|
715
|
+
onValueChange,
|
|
716
|
+
groups,
|
|
717
|
+
onPicked,
|
|
718
|
+
onClear,
|
|
719
|
+
leading,
|
|
720
|
+
className: className2,
|
|
721
|
+
onFocus
|
|
722
|
+
}) {
|
|
723
|
+
const [open, setOpen] = (0, import_react7.useState)(false);
|
|
724
|
+
const loading = groups.some((group) => group.loading);
|
|
725
|
+
const status = groups.map((group) => group.status).filter(Boolean).join(". ");
|
|
726
|
+
const handlePick = (groupKey, itemKey) => {
|
|
727
|
+
setOpen(false);
|
|
728
|
+
groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
|
|
729
|
+
onPicked?.(groupKey, itemKey);
|
|
730
|
+
};
|
|
731
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
732
|
+
import_ui3.SearchCombobox,
|
|
733
|
+
{
|
|
734
|
+
ariaLabel: label,
|
|
735
|
+
listLabel: `${label} results`,
|
|
736
|
+
value,
|
|
737
|
+
placeholder: placeholder ?? `${label}\u2026`,
|
|
738
|
+
groups: groups.map((group) => ({
|
|
739
|
+
key: group.key,
|
|
740
|
+
label: group.label,
|
|
741
|
+
options: group.items.map((item) => ({ key: item.key, content: item.content }))
|
|
742
|
+
})),
|
|
743
|
+
open,
|
|
744
|
+
onOpenChange: setOpen,
|
|
745
|
+
onValueChange,
|
|
746
|
+
onPick: handlePick,
|
|
747
|
+
status,
|
|
748
|
+
loading,
|
|
749
|
+
onClear,
|
|
750
|
+
leading,
|
|
751
|
+
inputClassName: "focus-visible:ring-0"
|
|
752
|
+
}
|
|
753
|
+
) });
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// src/genre-tree/search/SearchGroup.ts
|
|
757
|
+
var SEARCH_FAILED_STATUS = "Search failed";
|
|
758
|
+
var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
|
|
759
|
+
|
|
760
|
+
// src/genre-tree/search/useArtistSearchGroup.tsx
|
|
761
|
+
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
762
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
763
|
+
function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
764
|
+
const search = (0, import_genre_tree2.useArtistSearch)(getBaseUrl, enabled ? text : "");
|
|
765
|
+
if (!enabled) return void 0;
|
|
766
|
+
return {
|
|
767
|
+
key: "artists",
|
|
768
|
+
label: "Artists",
|
|
769
|
+
items: search.artists.map((artist) => ({
|
|
770
|
+
key: artist.uuid,
|
|
771
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
772
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
773
|
+
artist.name,
|
|
774
|
+
artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
775
|
+
" \xB7 ",
|
|
776
|
+
artist.disambiguation
|
|
777
|
+
] })
|
|
778
|
+
] }),
|
|
779
|
+
artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
780
|
+
"also known as ",
|
|
781
|
+
artist.matchedAlias
|
|
782
|
+
] })
|
|
783
|
+
] }),
|
|
784
|
+
onSelect: () => onSelect(artist)
|
|
785
|
+
})),
|
|
786
|
+
status: searchStatus(search, search.artists.length, "artists"),
|
|
787
|
+
loading: search.pending
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/genre-tree/search/useSongSearchGroup.tsx
|
|
792
|
+
var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
|
|
793
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
794
|
+
function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
795
|
+
const search = (0, import_genre_tree3.useSongSearch)(getBaseUrl, enabled ? text : "");
|
|
796
|
+
if (!enabled) return void 0;
|
|
797
|
+
return {
|
|
798
|
+
key: "songs",
|
|
799
|
+
label: "Songs",
|
|
800
|
+
items: search.songs.map((song) => ({
|
|
801
|
+
key: song.uuid,
|
|
802
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: (0, import_genre_tree3.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
803
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
804
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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 ") })
|
|
805
|
+
] }),
|
|
806
|
+
onSelect: () => onSelect(song)
|
|
807
|
+
})),
|
|
808
|
+
status: searchStatus(search, search.songs.length, "songs"),
|
|
809
|
+
loading: search.pending
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/genre-tree/search/useGenreRemoteSearchGroup.tsx
|
|
814
|
+
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
815
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
816
|
+
function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
|
|
817
|
+
const search = (0, import_genre_tree4.useGenreRemoteSearch)(getBaseUrl, text, options);
|
|
818
|
+
const genres = filter ? search.genres.filter(filter) : search.genres;
|
|
819
|
+
return {
|
|
820
|
+
key,
|
|
821
|
+
label,
|
|
822
|
+
items: genres.map((genre) => ({
|
|
823
|
+
key: genre.uuid,
|
|
824
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
826
|
+
genre.name,
|
|
827
|
+
genre.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "ml-2 text-xs font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
828
|
+
] }),
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
|
|
830
|
+
] }),
|
|
831
|
+
onSelect: () => onSelect(genre)
|
|
832
|
+
})),
|
|
833
|
+
status: searchStatus(search, genres.length, "genres"),
|
|
834
|
+
loading: search.pending
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/genre-tree/GenreSearch.tsx
|
|
839
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
703
840
|
function GenreSearch({
|
|
704
841
|
genrePlaylists,
|
|
705
842
|
onSelect,
|
|
@@ -708,120 +845,58 @@ function GenreSearch({
|
|
|
708
845
|
onPrevious,
|
|
709
846
|
placeholder,
|
|
710
847
|
onQueryChange,
|
|
711
|
-
extraGroups = []
|
|
848
|
+
extraGroups = [],
|
|
849
|
+
onFocus
|
|
712
850
|
}) {
|
|
713
|
-
const { query, setQuery, results, canClear, clear, select } = (0,
|
|
851
|
+
const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree5.useGenreSearch)({
|
|
714
852
|
genrePlaylists,
|
|
715
853
|
onSelect,
|
|
716
854
|
selectedName,
|
|
717
855
|
onClear
|
|
718
856
|
});
|
|
719
|
-
const [open, setOpen] = (0, import_react7.useState)(false);
|
|
720
857
|
const active = query !== "" && query !== selectedName;
|
|
721
858
|
const hasExtras = extraGroups.length > 0;
|
|
722
859
|
const label = hasExtras ? "Search" : "Search a genre";
|
|
723
|
-
const loading = active && extraGroups.some((group) => group.loading);
|
|
724
|
-
const status = active ? extraGroups.map((group) => group.status).filter(Boolean).join(". ") : "";
|
|
725
860
|
const groups = [
|
|
726
861
|
{
|
|
727
862
|
key: "genres",
|
|
728
863
|
label: hasExtras ? "Genres" : void 0,
|
|
729
|
-
|
|
864
|
+
items: results.map((genrePlaylist) => ({
|
|
730
865
|
key: genrePlaylist.uuid,
|
|
731
|
-
content: /* @__PURE__ */ (0,
|
|
866
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
732
867
|
genrePlaylist.name,
|
|
733
|
-
genrePlaylist.isUmbrella && /* @__PURE__ */ (0,
|
|
734
|
-
] })
|
|
868
|
+
genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "ml-2 text-xs italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
869
|
+
] }),
|
|
870
|
+
onSelect: () => select(genrePlaylist)
|
|
735
871
|
}))
|
|
736
872
|
},
|
|
737
|
-
...extraGroups.map(
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
options: active ? group.items.map((item) => ({ key: item.key, content: item.content })) : []
|
|
741
|
-
}))
|
|
873
|
+
...extraGroups.map(
|
|
874
|
+
(group) => active ? group : { ...group, items: [], status: "", loading: false }
|
|
875
|
+
)
|
|
742
876
|
];
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
877
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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__ */ (0, import_jsx_runtime13.jsx)(
|
|
878
|
+
SearchBox,
|
|
879
|
+
{
|
|
880
|
+
label,
|
|
881
|
+
placeholder,
|
|
882
|
+
value: query,
|
|
883
|
+
onValueChange: (value) => {
|
|
884
|
+
setQuery(value);
|
|
885
|
+
onQueryChange?.(value);
|
|
886
|
+
},
|
|
887
|
+
groups,
|
|
888
|
+
onPicked: (groupKey) => {
|
|
889
|
+
if (groupKey !== "genres") setQuery("");
|
|
890
|
+
onQueryChange?.("");
|
|
891
|
+
},
|
|
892
|
+
onClear: canClear ? () => {
|
|
893
|
+
clear();
|
|
894
|
+
onQueryChange?.("");
|
|
895
|
+
} : void 0,
|
|
896
|
+
leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui4.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
|
|
897
|
+
onFocus
|
|
751
898
|
}
|
|
752
|
-
|
|
753
|
-
if (!item) return;
|
|
754
|
-
setQuery("");
|
|
755
|
-
onQueryChange?.("");
|
|
756
|
-
item.onSelect();
|
|
757
|
-
};
|
|
758
|
-
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("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: [
|
|
759
|
-
onPrevious && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
760
|
-
"button",
|
|
761
|
-
{
|
|
762
|
-
type: "button",
|
|
763
|
-
"aria-label": "Previous",
|
|
764
|
-
title: "Previous",
|
|
765
|
-
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)]",
|
|
766
|
-
onClick: onPrevious,
|
|
767
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
768
|
-
}
|
|
769
|
-
),
|
|
770
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
771
|
-
import_ui3.Combobox,
|
|
772
|
-
{
|
|
773
|
-
ariaLabel: label,
|
|
774
|
-
listLabel: `${label} results`,
|
|
775
|
-
value: query,
|
|
776
|
-
placeholder: placeholder ?? `${label}\u2026`,
|
|
777
|
-
groups,
|
|
778
|
-
open,
|
|
779
|
-
onOpenChange: setOpen,
|
|
780
|
-
onValueChange: (value) => {
|
|
781
|
-
setQuery(value);
|
|
782
|
-
onQueryChange?.(value);
|
|
783
|
-
},
|
|
784
|
-
onPick: handlePick,
|
|
785
|
-
status,
|
|
786
|
-
loading: loading ? "Searching\u2026" : void 0,
|
|
787
|
-
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`,
|
|
788
|
-
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
789
|
-
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
790
|
-
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
791
|
-
}
|
|
792
|
-
),
|
|
793
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "absolute right-3 top-1/2 flex items-center -translate-y-1/2", children: [
|
|
794
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { "data-loading": loading, className: "relative p-2 text-[var(--color-text-muted)] pointer-events-none", children: [
|
|
795
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
796
|
-
import_lucide_react3.Search,
|
|
797
|
-
{
|
|
798
|
-
"aria-hidden": "true",
|
|
799
|
-
className: `w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-0" : ""}`
|
|
800
|
-
}
|
|
801
|
-
),
|
|
802
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
803
|
-
import_lucide_react3.LoaderCircle,
|
|
804
|
-
{
|
|
805
|
-
"aria-hidden": "true",
|
|
806
|
-
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"}`
|
|
807
|
-
}
|
|
808
|
-
)
|
|
809
|
-
] }),
|
|
810
|
-
canClear && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
811
|
-
"button",
|
|
812
|
-
{
|
|
813
|
-
type: "button",
|
|
814
|
-
"aria-label": "Clear search",
|
|
815
|
-
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)]",
|
|
816
|
-
onClick: () => {
|
|
817
|
-
clear();
|
|
818
|
-
onQueryChange?.("");
|
|
819
|
-
},
|
|
820
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.X, { "aria-hidden": "true", className: "w-6 h-6" })
|
|
821
|
-
}
|
|
822
|
-
)
|
|
823
|
-
] })
|
|
824
|
-
] });
|
|
899
|
+
) });
|
|
825
900
|
}
|
|
826
901
|
|
|
827
902
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
@@ -843,7 +918,7 @@ var import_si = require("react-icons/si");
|
|
|
843
918
|
// src/popup/Toast.tsx
|
|
844
919
|
var import_react8 = require("react");
|
|
845
920
|
var import_react_dom2 = require("react-dom");
|
|
846
|
-
var
|
|
921
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
847
922
|
function Toast({ message, onClose, durationMs = 2200 }) {
|
|
848
923
|
const [mounted, setMounted] = (0, import_react8.useState)(false);
|
|
849
924
|
(0, import_react8.useEffect)(() => {
|
|
@@ -853,7 +928,7 @@ function Toast({ message, onClose, durationMs = 2200 }) {
|
|
|
853
928
|
}, [durationMs, onClose]);
|
|
854
929
|
if (!mounted) return null;
|
|
855
930
|
return (0, import_react_dom2.createPortal)(
|
|
856
|
-
/* @__PURE__ */ (0,
|
|
931
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "pointer-events-none fixed inset-x-0 bottom-8 z-[60] flex justify-center px-4", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
857
932
|
"div",
|
|
858
933
|
{
|
|
859
934
|
role: "status",
|
|
@@ -868,12 +943,12 @@ function Toast({ message, onClose, durationMs = 2200 }) {
|
|
|
868
943
|
}
|
|
869
944
|
|
|
870
945
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
871
|
-
var
|
|
946
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
872
947
|
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";
|
|
873
948
|
function Contents({ icon, label }) {
|
|
874
|
-
return /* @__PURE__ */ (0,
|
|
875
|
-
/* @__PURE__ */ (0,
|
|
876
|
-
/* @__PURE__ */ (0,
|
|
949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("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 }),
|
|
951
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "max-w-full truncate", children: label })
|
|
877
952
|
] });
|
|
878
953
|
}
|
|
879
954
|
function ShareDestinationButton({
|
|
@@ -885,29 +960,29 @@ function ShareDestinationButton({
|
|
|
885
960
|
ariaLabel
|
|
886
961
|
}) {
|
|
887
962
|
if (href) {
|
|
888
|
-
return /* @__PURE__ */ (0,
|
|
963
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Contents, { icon, label }) });
|
|
889
964
|
}
|
|
890
|
-
return /* @__PURE__ */ (0,
|
|
965
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Contents, { icon, label }) });
|
|
891
966
|
}
|
|
892
967
|
|
|
893
968
|
// src/genre-tree/SharePreviewCard.tsx
|
|
894
969
|
var import_lucide_react4 = require("lucide-react");
|
|
895
|
-
var
|
|
970
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
896
971
|
function SharePreviewCard({ target, url }) {
|
|
897
972
|
const Icon = { genre: import_lucide_react4.Music2, artist: import_lucide_react4.UserRound, song: import_lucide_react4.Music }[target.kind];
|
|
898
973
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
899
|
-
return /* @__PURE__ */ (0,
|
|
900
|
-
/* @__PURE__ */ (0,
|
|
901
|
-
/* @__PURE__ */ (0,
|
|
902
|
-
/* @__PURE__ */ (0,
|
|
903
|
-
/* @__PURE__ */ (0,
|
|
904
|
-
/* @__PURE__ */ (0,
|
|
974
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
975
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
976
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
977
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
978
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
979
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
905
980
|
] })
|
|
906
981
|
] }) });
|
|
907
982
|
}
|
|
908
983
|
|
|
909
984
|
// src/genre-tree/SharePopup.tsx
|
|
910
|
-
var
|
|
985
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
911
986
|
function resolveAbsoluteUrl(url) {
|
|
912
987
|
try {
|
|
913
988
|
const parsed = new URL(url, window.location.href);
|
|
@@ -982,8 +1057,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
982
1057
|
setShareError("Could not open the share options.");
|
|
983
1058
|
}
|
|
984
1059
|
};
|
|
985
|
-
return /* @__PURE__ */ (0,
|
|
986
|
-
/* @__PURE__ */ (0,
|
|
1060
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
1061
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
987
1062
|
BasePopup_default,
|
|
988
1063
|
{
|
|
989
1064
|
title: "Share",
|
|
@@ -998,67 +1073,67 @@ function SharePopup({ target, url, onClose }) {
|
|
|
998
1073
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
999
1074
|
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)]",
|
|
1000
1075
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
1001
|
-
children: /* @__PURE__ */ (0,
|
|
1002
|
-
/* @__PURE__ */ (0,
|
|
1003
|
-
/* @__PURE__ */ (0,
|
|
1004
|
-
/* @__PURE__ */ (0,
|
|
1076
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
1079
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1005
1080
|
ShareDestinationButton,
|
|
1006
1081
|
{
|
|
1007
1082
|
label: "Copy Link",
|
|
1008
|
-
icon: /* @__PURE__ */ (0,
|
|
1083
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1009
1084
|
onClick: () => void handleCopy(),
|
|
1010
1085
|
ariaLabel: "Copy link",
|
|
1011
1086
|
disabled: !absoluteUrl
|
|
1012
1087
|
}
|
|
1013
1088
|
),
|
|
1014
|
-
/* @__PURE__ */ (0,
|
|
1089
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1015
1090
|
ShareDestinationButton,
|
|
1016
1091
|
{
|
|
1017
1092
|
label: "WhatsApp",
|
|
1018
|
-
icon: /* @__PURE__ */ (0,
|
|
1093
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_fa.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
1019
1094
|
href: destinationUrls?.whatsapp,
|
|
1020
1095
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
1021
1096
|
disabled: !absoluteUrl
|
|
1022
1097
|
}
|
|
1023
1098
|
),
|
|
1024
|
-
/* @__PURE__ */ (0,
|
|
1099
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1025
1100
|
ShareDestinationButton,
|
|
1026
1101
|
{
|
|
1027
1102
|
label: "Gmail",
|
|
1028
|
-
icon: /* @__PURE__ */ (0,
|
|
1103
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
1029
1104
|
href: destinationUrls?.gmail,
|
|
1030
1105
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
1031
1106
|
disabled: !absoluteUrl
|
|
1032
1107
|
}
|
|
1033
1108
|
),
|
|
1034
|
-
canUseNativeShare && /* @__PURE__ */ (0,
|
|
1109
|
+
canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1035
1110
|
ShareDestinationButton,
|
|
1036
1111
|
{
|
|
1037
1112
|
label: "More",
|
|
1038
|
-
icon: /* @__PURE__ */ (0,
|
|
1113
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1039
1114
|
onClick: () => void handleNativeShare(),
|
|
1040
1115
|
ariaLabel: "More sharing options",
|
|
1041
1116
|
disabled: !absoluteUrl
|
|
1042
1117
|
}
|
|
1043
1118
|
)
|
|
1044
1119
|
] }),
|
|
1045
|
-
copyStatus === "failed" && /* @__PURE__ */ (0,
|
|
1046
|
-
shareError && /* @__PURE__ */ (0,
|
|
1120
|
+
copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
1121
|
+
shareError && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
1047
1122
|
] })
|
|
1048
1123
|
}
|
|
1049
1124
|
),
|
|
1050
|
-
toastId > 0 && /* @__PURE__ */ (0,
|
|
1125
|
+
toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
1051
1126
|
] });
|
|
1052
1127
|
}
|
|
1053
1128
|
|
|
1054
1129
|
// src/genre-tree/ShareActionButton.tsx
|
|
1055
|
-
var
|
|
1130
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1056
1131
|
function ShareActionButton({ target, url }) {
|
|
1057
1132
|
const { showPopup, hidePopup } = (0, import_popup3.usePopup)();
|
|
1058
1133
|
const openSharePopup = () => {
|
|
1059
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1134
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SharePopup, { target, url, onClose: hidePopup }));
|
|
1060
1135
|
};
|
|
1061
|
-
return /* @__PURE__ */ (0,
|
|
1136
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1062
1137
|
"button",
|
|
1063
1138
|
{
|
|
1064
1139
|
type: "button",
|
|
@@ -1067,7 +1142,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1067
1142
|
"aria-label": `Share ${target.name}`,
|
|
1068
1143
|
title: `Share ${target.name}`,
|
|
1069
1144
|
children: [
|
|
1070
|
-
/* @__PURE__ */ (0,
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("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__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react6.Share2, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1071
1146
|
"Share"
|
|
1072
1147
|
]
|
|
1073
1148
|
}
|
|
@@ -1075,7 +1150,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1075
1150
|
}
|
|
1076
1151
|
|
|
1077
1152
|
// src/genre-tree/GenreActionBand.tsx
|
|
1078
|
-
var
|
|
1153
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1079
1154
|
function GenreActionBand({
|
|
1080
1155
|
node,
|
|
1081
1156
|
onPlayPause,
|
|
@@ -1097,7 +1172,7 @@ function GenreActionBand({
|
|
|
1097
1172
|
items.push({
|
|
1098
1173
|
key: "play-pause",
|
|
1099
1174
|
label: isPlaying ? "Pause" : "Play",
|
|
1100
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
1175
|
+
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react7.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react7.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1101
1176
|
onClick: onPlayPause
|
|
1102
1177
|
});
|
|
1103
1178
|
}
|
|
@@ -1105,24 +1180,24 @@ function GenreActionBand({
|
|
|
1105
1180
|
items.push({
|
|
1106
1181
|
key: "zoom-in",
|
|
1107
1182
|
label: "Focus",
|
|
1108
|
-
icon: /* @__PURE__ */ (0,
|
|
1183
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react7.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1109
1184
|
onClick: onZoomIn
|
|
1110
1185
|
});
|
|
1111
1186
|
}
|
|
1112
1187
|
if (onAddChild) {
|
|
1113
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0,
|
|
1188
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react7.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
1114
1189
|
}
|
|
1115
1190
|
if (onRename) {
|
|
1116
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0,
|
|
1191
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react7.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
1117
1192
|
}
|
|
1118
1193
|
if (onChangeParent) {
|
|
1119
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0,
|
|
1194
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react7.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
1120
1195
|
}
|
|
1121
1196
|
if (onToggleSide) {
|
|
1122
1197
|
adminItems.push({
|
|
1123
1198
|
key: "toggle-side",
|
|
1124
1199
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
1125
|
-
icon: /* @__PURE__ */ (0,
|
|
1200
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react7.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1126
1201
|
onClick: onToggleSide,
|
|
1127
1202
|
disabled: !!toggleSideDisabledReason,
|
|
1128
1203
|
title: toggleSideDisabledReason
|
|
@@ -1138,7 +1213,7 @@ function GenreActionBand({
|
|
|
1138
1213
|
});
|
|
1139
1214
|
}
|
|
1140
1215
|
if (items.length === 0 && (!shareTarget || !shareUrl) && adminItems.length === 0) return null;
|
|
1141
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0,
|
|
1216
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1142
1217
|
"button",
|
|
1143
1218
|
{
|
|
1144
1219
|
type: "button",
|
|
@@ -1147,25 +1222,25 @@ function GenreActionBand({
|
|
|
1147
1222
|
onClick: item.onClick,
|
|
1148
1223
|
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",
|
|
1149
1224
|
children: [
|
|
1150
|
-
/* @__PURE__ */ (0,
|
|
1225
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("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 }),
|
|
1151
1226
|
item.label
|
|
1152
1227
|
]
|
|
1153
1228
|
},
|
|
1154
1229
|
item.key
|
|
1155
1230
|
));
|
|
1156
|
-
return /* @__PURE__ */ (0,
|
|
1157
|
-
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0,
|
|
1231
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
1232
|
+
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
1158
1233
|
renderItems(items),
|
|
1159
|
-
shareTarget && shareUrl && /* @__PURE__ */ (0,
|
|
1234
|
+
shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1160
1235
|
] }),
|
|
1161
|
-
adminItems.length > 0 && /* @__PURE__ */ (0,
|
|
1236
|
+
adminItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1162
1237
|
"section",
|
|
1163
1238
|
{
|
|
1164
1239
|
"aria-label": "Admin actions",
|
|
1165
1240
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
1166
1241
|
children: [
|
|
1167
|
-
/* @__PURE__ */ (0,
|
|
1168
|
-
/* @__PURE__ */ (0,
|
|
1242
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
1243
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
1169
1244
|
]
|
|
1170
1245
|
}
|
|
1171
1246
|
)
|
|
@@ -1173,12 +1248,12 @@ function GenreActionBand({
|
|
|
1173
1248
|
}
|
|
1174
1249
|
|
|
1175
1250
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1176
|
-
var
|
|
1177
|
-
var
|
|
1251
|
+
var import_ui6 = require("@behindthemusictree/ui");
|
|
1252
|
+
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1178
1253
|
|
|
1179
1254
|
// src/genre-tree/ClampedText.tsx
|
|
1180
1255
|
var import_react10 = require("react");
|
|
1181
|
-
var
|
|
1256
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1182
1257
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1183
1258
|
const ref = (0, import_react10.useRef)(null);
|
|
1184
1259
|
const [expanded, setExpanded] = (0, import_react10.useState)(false);
|
|
@@ -1188,9 +1263,9 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1188
1263
|
if (!element || expanded) return;
|
|
1189
1264
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1190
1265
|
}, [children, expanded]);
|
|
1191
|
-
return /* @__PURE__ */ (0,
|
|
1192
|
-
/* @__PURE__ */ (0,
|
|
1193
|
-
(overflows || expanded) && /* @__PURE__ */ (0,
|
|
1266
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1268
|
+
(overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1194
1269
|
"button",
|
|
1195
1270
|
{
|
|
1196
1271
|
type: "button",
|
|
@@ -1208,7 +1283,7 @@ var import_react11 = require("react");
|
|
|
1208
1283
|
var import_react_query = require("@tanstack/react-query");
|
|
1209
1284
|
var import_zod = require("zod");
|
|
1210
1285
|
var import_fa2 = require("react-icons/fa");
|
|
1211
|
-
var
|
|
1286
|
+
var import_ui5 = require("@behindthemusictree/ui");
|
|
1212
1287
|
var import_auth = require("@behindthemusictree/app-kit/auth");
|
|
1213
1288
|
var import_transport3 = require("@behindthemusictree/app-kit/transport");
|
|
1214
1289
|
var import_transport4 = require("@behindthemusictree/app-kit/transport");
|
|
@@ -1217,22 +1292,22 @@ var import_player4 = require("@behindthemusictree/app-kit/player");
|
|
|
1217
1292
|
var import_player5 = require("@behindthemusictree/app-kit/player");
|
|
1218
1293
|
var import_popup4 = require("@behindthemusictree/app-kit/popup");
|
|
1219
1294
|
var import_transport6 = require("@behindthemusictree/app-kit/transport");
|
|
1220
|
-
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1221
|
-
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1222
|
-
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1223
1295
|
var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1296
|
+
var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1297
|
+
var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1298
|
+
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1224
1299
|
|
|
1225
1300
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1226
|
-
var
|
|
1227
|
-
var
|
|
1301
|
+
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1302
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1228
1303
|
function MusicBrainzRecordingLink({
|
|
1229
1304
|
song,
|
|
1230
1305
|
children,
|
|
1231
1306
|
className: className2 = ""
|
|
1232
1307
|
}) {
|
|
1233
|
-
const url = (0,
|
|
1234
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1235
|
-
return /* @__PURE__ */ (0,
|
|
1308
|
+
const url = (0, import_genre_tree6.musicBrainzRecordingUrl)(song);
|
|
1309
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_jsx_runtime21.Fragment, { children });
|
|
1310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1236
1311
|
"a",
|
|
1237
1312
|
{
|
|
1238
1313
|
href: url,
|
|
@@ -1247,11 +1322,11 @@ function MusicBrainzRecordingLink({
|
|
|
1247
1322
|
}
|
|
1248
1323
|
|
|
1249
1324
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1250
|
-
var
|
|
1325
|
+
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1251
1326
|
var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
|
|
1252
|
-
var
|
|
1327
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1253
1328
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1254
|
-
var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(
|
|
1329
|
+
var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(import_genre_tree9.SongDetailedSchema).extend({
|
|
1255
1330
|
overallTotal: import_zod.z.number().nullable(),
|
|
1256
1331
|
page: import_zod.z.number().nullable(),
|
|
1257
1332
|
totalPages: import_zod.z.number().nullable()
|
|
@@ -1278,19 +1353,20 @@ function GenrePlaylistTracks({
|
|
|
1278
1353
|
renderTracksToolbar,
|
|
1279
1354
|
artist = null
|
|
1280
1355
|
}) {
|
|
1281
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0,
|
|
1356
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0, import_genre_tree10.useTrackList)();
|
|
1282
1357
|
const { session, sessionRestored } = (0, import_auth.useSession)();
|
|
1283
1358
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1284
1359
|
const { showPopup } = (0, import_popup4.usePopup)();
|
|
1285
1360
|
const trackNoun = (0, import_track_noun.useTrackNoun)();
|
|
1286
1361
|
const { uuid } = genrePlaylist;
|
|
1287
1362
|
const { fetch } = (0, import_transport3.useFetchWrapper)(getBackendBaseUrl);
|
|
1363
|
+
const playback = (0, import_transport3.usePlaybackContext)();
|
|
1288
1364
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1289
|
-
queryKey: scope === "me" ?
|
|
1365
|
+
queryKey: [...scope === "me" ? import_genre_tree7.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree8.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : import_genre_tree8.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), ...playback.key ? [playback.key] : []],
|
|
1290
1366
|
queryFn: async ({ pageParam }) => {
|
|
1291
1367
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1292
1368
|
const response = await fetch(
|
|
1293
|
-
|
|
1369
|
+
import_genre_tree8.libraryEndpoints.reference.songs.list(),
|
|
1294
1370
|
true,
|
|
1295
1371
|
false,
|
|
1296
1372
|
{},
|
|
@@ -1301,7 +1377,7 @@ function GenrePlaylistTracks({
|
|
|
1301
1377
|
},
|
|
1302
1378
|
initialPageParam: scope === "me" ? 1 : "",
|
|
1303
1379
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1304
|
-
enabled: scope !== "me" || sessionRestored && !!session?.accessToken
|
|
1380
|
+
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1305
1381
|
});
|
|
1306
1382
|
const [sentinel, setSentinel] = (0, import_react11.useState)(null);
|
|
1307
1383
|
(0, import_react11.useEffect)(() => {
|
|
@@ -1321,35 +1397,35 @@ function GenrePlaylistTracks({
|
|
|
1321
1397
|
const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1322
1398
|
started.catch((error) => {
|
|
1323
1399
|
console.error("Failed to play genre playlist:", error);
|
|
1324
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1400
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
|
|
1325
1401
|
});
|
|
1326
1402
|
};
|
|
1327
|
-
return /* @__PURE__ */ (0,
|
|
1403
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ui5.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { children: [
|
|
1328
1404
|
renderTracksToolbar?.(),
|
|
1329
|
-
isPending ? /* @__PURE__ */ (0,
|
|
1405
|
+
isPending ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ui5.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("p", { role: "alert", children: [
|
|
1330
1406
|
"Couldn't load ",
|
|
1331
1407
|
trackNoun.plural,
|
|
1332
1408
|
".",
|
|
1333
1409
|
" ",
|
|
1334
|
-
/* @__PURE__ */ (0,
|
|
1335
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1410
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1411
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("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: [
|
|
1336
1412
|
tracks.map((track, index) => {
|
|
1337
1413
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1338
1414
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1339
1415
|
const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
|
|
1340
|
-
const text = /* @__PURE__ */ (0,
|
|
1341
|
-
/* @__PURE__ */ (0,
|
|
1342
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1343
|
-
/* @__PURE__ */ (0,
|
|
1344
|
-
track.album && /* @__PURE__ */ (0,
|
|
1416
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1417
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree9.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1418
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1419
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_genre_tree11.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1420
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1345
1421
|
] });
|
|
1346
1422
|
const leading = renderSongLeading?.(track, index);
|
|
1347
1423
|
const actions = renderSongActions?.(track);
|
|
1348
|
-
const isUnplayable = (0,
|
|
1424
|
+
const isUnplayable = (0, import_genre_tree9.isSong)(track) && (0, import_genre_tree9.songUnplayableReason)(track) !== null;
|
|
1349
1425
|
if (isUnplayable) {
|
|
1350
|
-
return /* @__PURE__ */ (0,
|
|
1426
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1351
1427
|
leading,
|
|
1352
|
-
/* @__PURE__ */ (0,
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1353
1429
|
"button",
|
|
1354
1430
|
{
|
|
1355
1431
|
type: "button",
|
|
@@ -1357,27 +1433,27 @@ function GenrePlaylistTracks({
|
|
|
1357
1433
|
"aria-disabled": "true",
|
|
1358
1434
|
"aria-label": label,
|
|
1359
1435
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1360
|
-
children: /* @__PURE__ */ (0,
|
|
1436
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1361
1437
|
}
|
|
1362
1438
|
),
|
|
1363
1439
|
text,
|
|
1364
1440
|
actions
|
|
1365
1441
|
] }, track.uuid);
|
|
1366
1442
|
}
|
|
1367
|
-
return /* @__PURE__ */ (0,
|
|
1443
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1368
1444
|
"li",
|
|
1369
1445
|
{
|
|
1370
1446
|
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)]" : ""}`,
|
|
1371
1447
|
children: [
|
|
1372
1448
|
leading,
|
|
1373
|
-
/* @__PURE__ */ (0,
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1374
1450
|
"button",
|
|
1375
1451
|
{
|
|
1376
1452
|
type: "button",
|
|
1377
1453
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1378
1454
|
className: `flex h-6 w-6 shrink-0 items-center justify-center rounded-full cursor-pointer group-hover:opacity-100 focus-visible:opacity-100 ${isCurrent && playState !== import_player5.PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1379
1455
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1380
|
-
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1456
|
+
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ui5.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_fa2.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1381
1457
|
}
|
|
1382
1458
|
),
|
|
1383
1459
|
text,
|
|
@@ -1387,7 +1463,7 @@ function GenrePlaylistTracks({
|
|
|
1387
1463
|
track.uuid
|
|
1388
1464
|
);
|
|
1389
1465
|
}),
|
|
1390
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1466
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1391
1467
|
] })
|
|
1392
1468
|
] }) });
|
|
1393
1469
|
}
|
|
@@ -1395,7 +1471,7 @@ function GenrePlaylistTracks({
|
|
|
1395
1471
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1396
1472
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1397
1473
|
var import_si2 = require("react-icons/si");
|
|
1398
|
-
var
|
|
1474
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1399
1475
|
function GenreDetailExtras({
|
|
1400
1476
|
genreUuid,
|
|
1401
1477
|
genrePlaylist,
|
|
@@ -1412,24 +1488,24 @@ function GenreDetailExtras({
|
|
|
1412
1488
|
...trackSlots
|
|
1413
1489
|
}) {
|
|
1414
1490
|
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1415
|
-
const { data: overview, isPending } = (0,
|
|
1491
|
+
const { data: overview, isPending } = (0, import_genre_tree12.useFetchGenreOverview)(
|
|
1416
1492
|
genreUuid,
|
|
1417
1493
|
scope,
|
|
1418
1494
|
getBackendBaseUrl,
|
|
1419
1495
|
criteriaOverviewSchema
|
|
1420
1496
|
);
|
|
1421
1497
|
if (isPending) {
|
|
1422
|
-
return /* @__PURE__ */ (0,
|
|
1423
|
-
/* @__PURE__ */ (0,
|
|
1498
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
1499
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui6.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui6.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1424
1500
|
family
|
|
1425
1501
|
] });
|
|
1426
1502
|
}
|
|
1427
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1503
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: family });
|
|
1428
1504
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1429
1505
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1430
|
-
return /* @__PURE__ */ (0,
|
|
1431
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1432
|
-
summarySourceUrl && /* @__PURE__ */ (0,
|
|
1506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
1507
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui6.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1508
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1433
1509
|
"a",
|
|
1434
1510
|
{
|
|
1435
1511
|
href: summarySourceUrl,
|
|
@@ -1437,12 +1513,12 @@ function GenreDetailExtras({
|
|
|
1437
1513
|
rel: "noopener noreferrer",
|
|
1438
1514
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1439
1515
|
children: [
|
|
1440
|
-
/* @__PURE__ */ (0,
|
|
1516
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1441
1517
|
"Wikipedia"
|
|
1442
1518
|
]
|
|
1443
1519
|
}
|
|
1444
1520
|
),
|
|
1445
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1521
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1446
1522
|
"a",
|
|
1447
1523
|
{
|
|
1448
1524
|
href: wikidataUrl,
|
|
@@ -1450,17 +1526,17 @@ function GenreDetailExtras({
|
|
|
1450
1526
|
rel: "noopener noreferrer",
|
|
1451
1527
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1452
1528
|
children: [
|
|
1453
|
-
/* @__PURE__ */ (0,
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1454
1530
|
"Wikidata"
|
|
1455
1531
|
]
|
|
1456
1532
|
}
|
|
1457
1533
|
)
|
|
1458
1534
|
] }) }),
|
|
1459
|
-
/* @__PURE__ */ (0,
|
|
1535
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui6.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1460
1536
|
family,
|
|
1461
1537
|
renderGenreDetailExtras?.(overview),
|
|
1462
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1463
|
-
/* @__PURE__ */ (0,
|
|
1538
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui6.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1539
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1464
1540
|
GenrePlaylistTracks,
|
|
1465
1541
|
{
|
|
1466
1542
|
genreUuid,
|
|
@@ -1476,7 +1552,7 @@ function GenreDetailExtras({
|
|
|
1476
1552
|
}
|
|
1477
1553
|
|
|
1478
1554
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1479
|
-
var
|
|
1555
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1480
1556
|
var import_react12 = require("react");
|
|
1481
1557
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1482
1558
|
const { isPlaying, setIsPlaying } = (0, import_player6.usePlayer)();
|
|
@@ -1490,7 +1566,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1490
1566
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1491
1567
|
});
|
|
1492
1568
|
};
|
|
1493
|
-
return /* @__PURE__ */ (0,
|
|
1569
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1494
1570
|
GenreActionBand,
|
|
1495
1571
|
{
|
|
1496
1572
|
...bandProps,
|
|
@@ -1511,9 +1587,9 @@ function GenreInfoPanelBody({
|
|
|
1511
1587
|
band,
|
|
1512
1588
|
extras
|
|
1513
1589
|
}) {
|
|
1514
|
-
return /* @__PURE__ */ (0,
|
|
1590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
1515
1591
|
toolbar,
|
|
1516
|
-
/* @__PURE__ */ (0,
|
|
1592
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1517
1593
|
/* @__PURE__ */ (0, import_react12.createElement)(
|
|
1518
1594
|
GenreDetailExtras,
|
|
1519
1595
|
{
|
|
@@ -1532,8 +1608,8 @@ function GenreInfoPanelBody({
|
|
|
1532
1608
|
|
|
1533
1609
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1534
1610
|
var import_react13 = require("react");
|
|
1535
|
-
var
|
|
1536
|
-
var
|
|
1611
|
+
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1612
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1537
1613
|
function ChangeParentPopup({
|
|
1538
1614
|
genrePlaylist,
|
|
1539
1615
|
genrePlaylists,
|
|
@@ -1542,9 +1618,9 @@ function ChangeParentPopup({
|
|
|
1542
1618
|
onClose,
|
|
1543
1619
|
onParentChanged
|
|
1544
1620
|
}) {
|
|
1545
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1621
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree13.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1546
1622
|
const candidates = (0, import_react13.useMemo)(
|
|
1547
|
-
() => (0,
|
|
1623
|
+
() => (0, import_genre_tree13.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1548
1624
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1549
1625
|
);
|
|
1550
1626
|
const handleSelect = (newParent) => {
|
|
@@ -1568,12 +1644,12 @@ function ChangeParentPopup({
|
|
|
1568
1644
|
window.addEventListener("keydown", handleKeyDown);
|
|
1569
1645
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1570
1646
|
}, [onClose]);
|
|
1571
|
-
return /* @__PURE__ */ (0,
|
|
1572
|
-
/* @__PURE__ */ (0,
|
|
1647
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1648
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1573
1649
|
"Current parent: ",
|
|
1574
1650
|
genrePlaylist.parent?.name ?? "none"
|
|
1575
1651
|
] }),
|
|
1576
|
-
/* @__PURE__ */ (0,
|
|
1652
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1577
1653
|
GenreSearch,
|
|
1578
1654
|
{
|
|
1579
1655
|
genrePlaylists: candidates,
|
|
@@ -1581,22 +1657,22 @@ function ChangeParentPopup({
|
|
|
1581
1657
|
placeholder: "Search a new parent\u2026"
|
|
1582
1658
|
}
|
|
1583
1659
|
) }),
|
|
1584
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1660
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1585
1661
|
] });
|
|
1586
1662
|
}
|
|
1587
1663
|
|
|
1588
1664
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1589
|
-
var
|
|
1590
|
-
var
|
|
1665
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
1666
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1591
1667
|
|
|
1592
1668
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1593
|
-
var
|
|
1669
|
+
var import_ui8 = require("@behindthemusictree/ui");
|
|
1594
1670
|
|
|
1595
1671
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
1596
1672
|
var import_lucide_react8 = require("lucide-react");
|
|
1597
1673
|
var import_si3 = require("react-icons/si");
|
|
1598
|
-
var
|
|
1599
|
-
var
|
|
1674
|
+
var import_ui7 = require("@behindthemusictree/ui");
|
|
1675
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1600
1676
|
var LINK_KINDS = {
|
|
1601
1677
|
official: { label: "Official website", Icon: import_lucide_react8.Globe },
|
|
1602
1678
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -1633,7 +1709,7 @@ function artistLinks(artist) {
|
|
|
1633
1709
|
return links;
|
|
1634
1710
|
}
|
|
1635
1711
|
function ArtistLinks({ links }) {
|
|
1636
|
-
return /* @__PURE__ */ (0,
|
|
1712
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui7.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1637
1713
|
}
|
|
1638
1714
|
|
|
1639
1715
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1644,7 +1720,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1644
1720
|
}
|
|
1645
1721
|
|
|
1646
1722
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1647
|
-
var
|
|
1723
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1648
1724
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1649
1725
|
function ArtistHeader({ artist }) {
|
|
1650
1726
|
const fields = [];
|
|
@@ -1654,35 +1730,35 @@ function ArtistHeader({ artist }) {
|
|
|
1654
1730
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1655
1731
|
if (years) fields.push({ label: "Active", value: years });
|
|
1656
1732
|
const links = artistLinks(artist);
|
|
1657
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
1733
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ArtistLinks, { links }) });
|
|
1658
1734
|
if (fields.length === 0) return null;
|
|
1659
|
-
return /* @__PURE__ */ (0,
|
|
1735
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui8.FieldList, { items: fields });
|
|
1660
1736
|
}
|
|
1661
1737
|
|
|
1662
1738
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1663
|
-
var
|
|
1739
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1664
1740
|
function ExternalLink({ href, children }) {
|
|
1665
|
-
return /* @__PURE__ */ (0,
|
|
1741
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1666
1742
|
}
|
|
1667
1743
|
function ArtistPhoto({ image, name }) {
|
|
1668
|
-
return /* @__PURE__ */ (0,
|
|
1669
|
-
/* @__PURE__ */ (0,
|
|
1670
|
-
/* @__PURE__ */ (0,
|
|
1744
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1745
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1746
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1671
1747
|
"Photo: ",
|
|
1672
|
-
/* @__PURE__ */ (0,
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1673
1749
|
",",
|
|
1674
1750
|
" ",
|
|
1675
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
1751
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1676
1752
|
] })
|
|
1677
1753
|
] });
|
|
1678
1754
|
}
|
|
1679
1755
|
|
|
1680
1756
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1681
|
-
var
|
|
1682
|
-
var
|
|
1757
|
+
var import_ui9 = require("@behindthemusictree/ui");
|
|
1758
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1683
1759
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1684
1760
|
if (memberships.length === 0) return null;
|
|
1685
|
-
return /* @__PURE__ */ (0,
|
|
1761
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
1686
1762
|
const details = [
|
|
1687
1763
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1688
1764
|
...membership.roles
|
|
@@ -1690,9 +1766,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1690
1766
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1691
1767
|
return (
|
|
1692
1768
|
// A member can have several periods in the same group.
|
|
1693
|
-
/* @__PURE__ */ (0,
|
|
1694
|
-
/* @__PURE__ */ (0,
|
|
1695
|
-
detailsText && /* @__PURE__ */ (0,
|
|
1769
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1770
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1771
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1696
1772
|
" \u2014 ",
|
|
1697
1773
|
detailsText
|
|
1698
1774
|
] })
|
|
@@ -1703,9 +1779,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1703
1779
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1704
1780
|
if (memberships.length === 0) return null;
|
|
1705
1781
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1706
|
-
return /* @__PURE__ */ (0,
|
|
1707
|
-
/* @__PURE__ */ (0,
|
|
1708
|
-
/* @__PURE__ */ (0,
|
|
1782
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_ui9.PanelSection, { title, children: [
|
|
1783
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1784
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1709
1785
|
MembershipList,
|
|
1710
1786
|
{
|
|
1711
1787
|
title: "Past",
|
|
@@ -1717,8 +1793,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1717
1793
|
}
|
|
1718
1794
|
|
|
1719
1795
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1720
|
-
var
|
|
1721
|
-
var
|
|
1796
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
1797
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1722
1798
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1723
1799
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1724
1800
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1738,12 +1814,12 @@ function byYear(a, b) {
|
|
|
1738
1814
|
function ArtistDiscography({ albums }) {
|
|
1739
1815
|
if (albums.length === 0) return null;
|
|
1740
1816
|
const sorted = [...albums].sort(byYear);
|
|
1741
|
-
return /* @__PURE__ */ (0,
|
|
1817
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
1742
1818
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1743
1819
|
if (groupAlbums.length === 0) return null;
|
|
1744
|
-
return /* @__PURE__ */ (0,
|
|
1745
|
-
/* @__PURE__ */ (0,
|
|
1746
|
-
/* @__PURE__ */ (0,
|
|
1820
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1747
1823
|
] }, album.uuid)) }) }, group);
|
|
1748
1824
|
}) });
|
|
1749
1825
|
}
|
|
@@ -1752,20 +1828,20 @@ function ArtistDiscography({ albums }) {
|
|
|
1752
1828
|
var import_react14 = require("react");
|
|
1753
1829
|
var import_react_query2 = require("@tanstack/react-query");
|
|
1754
1830
|
var import_lucide_react9 = require("lucide-react");
|
|
1755
|
-
var
|
|
1831
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
1756
1832
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
1757
|
-
var
|
|
1833
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1758
1834
|
var import_transport7 = require("@behindthemusictree/app-kit/transport");
|
|
1759
1835
|
|
|
1760
1836
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1761
1837
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
1762
|
-
var
|
|
1838
|
+
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1763
1839
|
|
|
1764
1840
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
1765
1841
|
var import_fa3 = require("react-icons/fa");
|
|
1766
1842
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1767
1843
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
1768
|
-
var
|
|
1844
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1769
1845
|
function TrackPositionPlayPause({
|
|
1770
1846
|
position,
|
|
1771
1847
|
uuid,
|
|
@@ -1779,14 +1855,14 @@ function TrackPositionPlayPause({
|
|
|
1779
1855
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
1780
1856
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
1781
1857
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1782
|
-
return /* @__PURE__ */ (0,
|
|
1858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1783
1859
|
"div",
|
|
1784
1860
|
{
|
|
1785
1861
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1786
1862
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1787
1863
|
onClick: handlePlayPauseClick,
|
|
1788
1864
|
children: [
|
|
1789
|
-
/* @__PURE__ */ (0,
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1790
1866
|
"span",
|
|
1791
1867
|
{
|
|
1792
1868
|
className: "track-position-play-pause__bars",
|
|
@@ -1794,18 +1870,18 @@ function TrackPositionPlayPause({
|
|
|
1794
1870
|
role: "img",
|
|
1795
1871
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1796
1872
|
children: [
|
|
1797
|
-
/* @__PURE__ */ (0,
|
|
1798
|
-
/* @__PURE__ */ (0,
|
|
1799
|
-
/* @__PURE__ */ (0,
|
|
1873
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
1874
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
1875
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
1800
1876
|
]
|
|
1801
1877
|
}
|
|
1802
1878
|
) : position }),
|
|
1803
|
-
/* @__PURE__ */ (0,
|
|
1879
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1804
1880
|
"div",
|
|
1805
1881
|
{
|
|
1806
1882
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1807
1883
|
"aria-hidden": "true",
|
|
1808
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
1884
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_fa3.FaPlay, {})
|
|
1809
1885
|
}
|
|
1810
1886
|
)
|
|
1811
1887
|
]
|
|
@@ -1814,9 +1890,9 @@ function TrackPositionPlayPause({
|
|
|
1814
1890
|
}
|
|
1815
1891
|
|
|
1816
1892
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1817
|
-
var
|
|
1818
|
-
var
|
|
1819
|
-
var
|
|
1893
|
+
var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1894
|
+
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1895
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1820
1896
|
function TrackItem({
|
|
1821
1897
|
track,
|
|
1822
1898
|
position,
|
|
@@ -1826,11 +1902,11 @@ function TrackItem({
|
|
|
1826
1902
|
renderSongLink
|
|
1827
1903
|
}) {
|
|
1828
1904
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
1829
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
1905
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree14.useTrackList)();
|
|
1830
1906
|
const scope = trackList?.origin?.scope ?? null;
|
|
1831
1907
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
1832
1908
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
1833
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
1909
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
|
|
1834
1910
|
const handlePlayPauseClick = (event) => {
|
|
1835
1911
|
event.stopPropagation();
|
|
1836
1912
|
if (isCurrentTrack) {
|
|
@@ -1840,12 +1916,12 @@ function TrackItem({
|
|
|
1840
1916
|
loadTrackForPlayer(track.uuid);
|
|
1841
1917
|
}
|
|
1842
1918
|
};
|
|
1843
|
-
return /* @__PURE__ */ (0,
|
|
1919
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
1844
1920
|
"div",
|
|
1845
1921
|
{
|
|
1846
1922
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1847
1923
|
children: [
|
|
1848
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
1924
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1849
1925
|
"div",
|
|
1850
1926
|
{
|
|
1851
1927
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1853,7 +1929,7 @@ function TrackItem({
|
|
|
1853
1929
|
"aria-disabled": "true",
|
|
1854
1930
|
children: position
|
|
1855
1931
|
}
|
|
1856
|
-
) : /* @__PURE__ */ (0,
|
|
1932
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1857
1933
|
TrackPositionPlayPause,
|
|
1858
1934
|
{
|
|
1859
1935
|
position,
|
|
@@ -1862,12 +1938,12 @@ function TrackItem({
|
|
|
1862
1938
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1863
1939
|
}
|
|
1864
1940
|
),
|
|
1865
|
-
/* @__PURE__ */ (0,
|
|
1866
|
-
/* @__PURE__ */ (0,
|
|
1867
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
1941
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1942
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1943
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1868
1944
|
] }),
|
|
1869
|
-
/* @__PURE__ */ (0,
|
|
1870
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
1945
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1946
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1871
1947
|
"div",
|
|
1872
1948
|
{
|
|
1873
1949
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1875,15 +1951,15 @@ function TrackItem({
|
|
|
1875
1951
|
children: renderDuration(track)
|
|
1876
1952
|
}
|
|
1877
1953
|
),
|
|
1878
|
-
renderActions && /* @__PURE__ */ (0,
|
|
1954
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1879
1955
|
]
|
|
1880
1956
|
}
|
|
1881
1957
|
);
|
|
1882
1958
|
}
|
|
1883
1959
|
|
|
1884
1960
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1885
|
-
var
|
|
1886
|
-
var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(
|
|
1961
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1962
|
+
var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(import_genre_tree17.SongDetailedSchema);
|
|
1887
1963
|
var SORTS = [
|
|
1888
1964
|
{ ordering: "title", label: "Title" },
|
|
1889
1965
|
{ ordering: "-albumYear", label: "Year" }
|
|
@@ -1892,11 +1968,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1892
1968
|
const [page, setPage] = (0, import_react14.useState)(1);
|
|
1893
1969
|
const [ordering, setOrdering] = (0, import_react14.useState)("title");
|
|
1894
1970
|
const { fetch } = (0, import_transport7.useFetchWrapper)(getBackendBaseUrl);
|
|
1895
|
-
const { playNewTrackListFromArtist } = (0,
|
|
1971
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree17.useTrackList)();
|
|
1896
1972
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
1897
1973
|
const { data, isPending, isError } = (0, import_transport7.useQueryWithParse)({
|
|
1898
1974
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
1899
|
-
queryFn: () => fetch(
|
|
1975
|
+
queryFn: () => fetch(import_genre_tree17.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
1900
1976
|
artist: artist.uuid,
|
|
1901
1977
|
ordering,
|
|
1902
1978
|
page
|
|
@@ -1911,25 +1987,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1911
1987
|
});
|
|
1912
1988
|
const handleRowClickCapture = (event, song) => {
|
|
1913
1989
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
1914
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
1990
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree17.songUnplayableReason)(song) !== null) return;
|
|
1915
1991
|
event.stopPropagation();
|
|
1916
1992
|
void play(song.uuid);
|
|
1917
1993
|
};
|
|
1918
|
-
return /* @__PURE__ */ (0,
|
|
1919
|
-
/* @__PURE__ */ (0,
|
|
1920
|
-
/* @__PURE__ */ (0,
|
|
1994
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
|
|
1995
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
1996
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
1921
1997
|
"button",
|
|
1922
1998
|
{
|
|
1923
1999
|
type: "button",
|
|
1924
2000
|
onClick: () => void play(),
|
|
1925
2001
|
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",
|
|
1926
2002
|
children: [
|
|
1927
|
-
/* @__PURE__ */ (0,
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react9.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1928
2004
|
"Play all"
|
|
1929
2005
|
]
|
|
1930
2006
|
}
|
|
1931
2007
|
),
|
|
1932
|
-
/* @__PURE__ */ (0,
|
|
2008
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1933
2009
|
"button",
|
|
1934
2010
|
{
|
|
1935
2011
|
type: "button",
|
|
@@ -1944,7 +2020,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1944
2020
|
sort.ordering
|
|
1945
2021
|
)) })
|
|
1946
2022
|
] }),
|
|
1947
|
-
/* @__PURE__ */ (0,
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1948
2024
|
TrackItem,
|
|
1949
2025
|
{
|
|
1950
2026
|
track: song,
|
|
@@ -1953,12 +2029,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1953
2029
|
renderSongLink
|
|
1954
2030
|
}
|
|
1955
2031
|
) }, song.uuid)) }),
|
|
1956
|
-
/* @__PURE__ */ (0,
|
|
2032
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1957
2033
|
] }) });
|
|
1958
2034
|
}
|
|
1959
2035
|
|
|
1960
2036
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1961
|
-
var
|
|
2037
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1962
2038
|
function useArtistPanel({
|
|
1963
2039
|
uuid,
|
|
1964
2040
|
scope,
|
|
@@ -1970,7 +2046,7 @@ function useArtistPanel({
|
|
|
1970
2046
|
onExpandedChange,
|
|
1971
2047
|
onClose
|
|
1972
2048
|
}) {
|
|
1973
|
-
const result = (0,
|
|
2049
|
+
const result = (0, import_genre_tree18.useArtist)(uuid, getBackendBaseUrl);
|
|
1974
2050
|
const panel = { expanded, onExpandedChange, onClose };
|
|
1975
2051
|
switch (result.status) {
|
|
1976
2052
|
case "idle":
|
|
@@ -1979,26 +2055,26 @@ function useArtistPanel({
|
|
|
1979
2055
|
return {
|
|
1980
2056
|
...panel,
|
|
1981
2057
|
title: "Artist",
|
|
1982
|
-
content: /* @__PURE__ */ (0,
|
|
1983
|
-
/* @__PURE__ */ (0,
|
|
1984
|
-
/* @__PURE__ */ (0,
|
|
1985
|
-
/* @__PURE__ */ (0,
|
|
1986
|
-
/* @__PURE__ */ (0,
|
|
1987
|
-
/* @__PURE__ */ (0,
|
|
1988
|
-
/* @__PURE__ */ (0,
|
|
2058
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2059
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Skeleton, { className: "h-3 w-16" }),
|
|
2060
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Skeleton, { className: "h-4 w-32" }),
|
|
2061
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Skeleton, { className: "h-3 w-16" }),
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Skeleton, { className: "h-4 w-24" }),
|
|
2063
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2064
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Skeleton, { className: "h-16" })
|
|
1989
2065
|
] })
|
|
1990
2066
|
};
|
|
1991
2067
|
case "not-found":
|
|
1992
2068
|
return {
|
|
1993
2069
|
...panel,
|
|
1994
2070
|
title: "Artist not found",
|
|
1995
|
-
content: /* @__PURE__ */ (0,
|
|
2071
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
1996
2072
|
};
|
|
1997
2073
|
case "error":
|
|
1998
2074
|
return {
|
|
1999
2075
|
...panel,
|
|
2000
2076
|
title: "Artist",
|
|
2001
|
-
content: /* @__PURE__ */ (0,
|
|
2077
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2002
2078
|
};
|
|
2003
2079
|
case "success": {
|
|
2004
2080
|
const { artist } = result;
|
|
@@ -2007,13 +2083,13 @@ function useArtistPanel({
|
|
|
2007
2083
|
return {
|
|
2008
2084
|
...panel,
|
|
2009
2085
|
title: artist.name,
|
|
2010
|
-
content: /* @__PURE__ */ (0,
|
|
2011
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2012
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2013
|
-
/* @__PURE__ */ (0,
|
|
2014
|
-
/* @__PURE__ */ (0,
|
|
2015
|
-
/* @__PURE__ */ (0,
|
|
2016
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2086
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2087
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2088
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2089
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistHeader, { artist }),
|
|
2090
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2091
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2092
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2017
2093
|
ArtistSongs,
|
|
2018
2094
|
{
|
|
2019
2095
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2023,56 +2099,56 @@ function useArtistPanel({
|
|
|
2023
2099
|
}
|
|
2024
2100
|
)
|
|
2025
2101
|
] }),
|
|
2026
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2102
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2027
2103
|
};
|
|
2028
2104
|
}
|
|
2029
2105
|
}
|
|
2030
2106
|
}
|
|
2031
2107
|
|
|
2032
2108
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2033
|
-
var
|
|
2034
|
-
var
|
|
2109
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
2110
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2035
2111
|
|
|
2036
2112
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2037
2113
|
var import_react15 = require("react");
|
|
2038
2114
|
var import_lucide_react10 = require("lucide-react");
|
|
2039
|
-
var
|
|
2115
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
2040
2116
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2041
|
-
var
|
|
2042
|
-
var
|
|
2117
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2118
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2043
2119
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2044
2120
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2045
2121
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2046
2122
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2047
2123
|
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
2048
|
-
const history = (0,
|
|
2124
|
+
const history = (0, import_genre_tree19.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2049
2125
|
let body = null;
|
|
2050
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2051
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2126
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2127
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2052
2128
|
else if (history.status === "success")
|
|
2053
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2054
|
-
/* @__PURE__ */ (0,
|
|
2055
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2129
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2130
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2131
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2056
2132
|
" ",
|
|
2057
2133
|
"\u2014 ",
|
|
2058
2134
|
entry.oldValue ?? "none",
|
|
2059
2135
|
" \u2192 ",
|
|
2060
2136
|
entry.newValue ?? "none"
|
|
2061
2137
|
] }) : null,
|
|
2062
|
-
/* @__PURE__ */ (0,
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2063
2139
|
entry.actorPseudo ?? "Anonymous",
|
|
2064
2140
|
" \xB7 ",
|
|
2065
2141
|
dateFormat.format(new Date(entry.createdOn))
|
|
2066
2142
|
] })
|
|
2067
2143
|
] }, entry.uuid)) });
|
|
2068
|
-
return /* @__PURE__ */ (0,
|
|
2144
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2069
2145
|
"details",
|
|
2070
2146
|
{
|
|
2071
2147
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2072
2148
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2073
2149
|
children: [
|
|
2074
|
-
/* @__PURE__ */ (0,
|
|
2075
|
-
/* @__PURE__ */ (0,
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2151
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "mt-1.5", children: body })
|
|
2076
2152
|
]
|
|
2077
2153
|
}
|
|
2078
2154
|
);
|
|
@@ -2086,25 +2162,25 @@ function SongPanelContent({
|
|
|
2086
2162
|
genrePath,
|
|
2087
2163
|
onSelectGenre
|
|
2088
2164
|
}) {
|
|
2089
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2165
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree19.useTrackList)();
|
|
2090
2166
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2091
|
-
const unplayableReason = (0,
|
|
2167
|
+
const unplayableReason = (0, import_genre_tree19.songUnplayableReason)(song);
|
|
2092
2168
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2093
2169
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2094
2170
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2095
|
-
const musicBrainzUrl = (0,
|
|
2171
|
+
const musicBrainzUrl = (0, import_genre_tree19.musicBrainzRecordingUrl)(song);
|
|
2096
2172
|
const fields = [];
|
|
2097
2173
|
if (song.genre) {
|
|
2098
2174
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2099
2175
|
fields.push({
|
|
2100
2176
|
label: "Genre",
|
|
2101
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2102
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2103
|
-
/* @__PURE__ */ (0,
|
|
2177
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { children: [
|
|
2178
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2179
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2104
2180
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2105
2181
|
});
|
|
2106
2182
|
} else {
|
|
2107
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2183
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2108
2184
|
}
|
|
2109
2185
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2110
2186
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2114,11 +2190,11 @@ function SongPanelContent({
|
|
|
2114
2190
|
const links = [];
|
|
2115
2191
|
if (musicBrainzUrl)
|
|
2116
2192
|
links.push(
|
|
2117
|
-
/* @__PURE__ */ (0,
|
|
2193
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2118
2194
|
);
|
|
2119
2195
|
if (song.youtubeVideo)
|
|
2120
2196
|
links.push(
|
|
2121
|
-
/* @__PURE__ */ (0,
|
|
2197
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2122
2198
|
"a",
|
|
2123
2199
|
{
|
|
2124
2200
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2133,33 +2209,33 @@ function SongPanelContent({
|
|
|
2133
2209
|
if (links.length > 0)
|
|
2134
2210
|
fields.push({
|
|
2135
2211
|
label: "Links",
|
|
2136
|
-
value: /* @__PURE__ */ (0,
|
|
2212
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "flex gap-3", children: links })
|
|
2137
2213
|
});
|
|
2138
|
-
return /* @__PURE__ */ (0,
|
|
2139
|
-
/* @__PURE__ */ (0,
|
|
2140
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2214
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
2215
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2216
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2141
2217
|
"button",
|
|
2142
2218
|
{
|
|
2143
2219
|
type: "button",
|
|
2144
2220
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2145
2221
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2146
2222
|
children: [
|
|
2147
|
-
/* @__PURE__ */ (0,
|
|
2223
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("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__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2148
2224
|
isPlaying ? "Pause" : "Play"
|
|
2149
2225
|
]
|
|
2150
2226
|
}
|
|
2151
2227
|
),
|
|
2152
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2228
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2153
2229
|
] }) }),
|
|
2154
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2155
|
-
/* @__PURE__ */ (0,
|
|
2156
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2157
|
-
/* @__PURE__ */ (0,
|
|
2230
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree19.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2231
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.FieldList, { items: fields }),
|
|
2232
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2233
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2158
2234
|
] });
|
|
2159
2235
|
}
|
|
2160
2236
|
|
|
2161
2237
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2162
|
-
var
|
|
2238
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2163
2239
|
function useSongPanel({
|
|
2164
2240
|
uuid,
|
|
2165
2241
|
scope,
|
|
@@ -2172,7 +2248,7 @@ function useSongPanel({
|
|
|
2172
2248
|
onExpandedChange,
|
|
2173
2249
|
onClose
|
|
2174
2250
|
}) {
|
|
2175
|
-
const result = (0,
|
|
2251
|
+
const result = (0, import_genre_tree20.useSong)(uuid, getBackendBaseUrl);
|
|
2176
2252
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2177
2253
|
switch (result.status) {
|
|
2178
2254
|
case "idle":
|
|
@@ -2181,24 +2257,24 @@ function useSongPanel({
|
|
|
2181
2257
|
return {
|
|
2182
2258
|
...panel,
|
|
2183
2259
|
title: "Song",
|
|
2184
|
-
content: /* @__PURE__ */ (0,
|
|
2185
|
-
/* @__PURE__ */ (0,
|
|
2186
|
-
/* @__PURE__ */ (0,
|
|
2187
|
-
/* @__PURE__ */ (0,
|
|
2188
|
-
/* @__PURE__ */ (0,
|
|
2260
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2261
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-3 w-16" }),
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-4 w-32" }),
|
|
2263
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-3 w-16" }),
|
|
2264
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-4 w-24" })
|
|
2189
2265
|
] })
|
|
2190
2266
|
};
|
|
2191
2267
|
case "not-found":
|
|
2192
2268
|
return {
|
|
2193
2269
|
...panel,
|
|
2194
2270
|
title: "Song not found",
|
|
2195
|
-
content: /* @__PURE__ */ (0,
|
|
2271
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2196
2272
|
};
|
|
2197
2273
|
case "error":
|
|
2198
2274
|
return {
|
|
2199
2275
|
...panel,
|
|
2200
2276
|
title: "Song",
|
|
2201
|
-
content: /* @__PURE__ */ (0,
|
|
2277
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2202
2278
|
};
|
|
2203
2279
|
case "success": {
|
|
2204
2280
|
const { song } = result;
|
|
@@ -2206,7 +2282,7 @@ function useSongPanel({
|
|
|
2206
2282
|
return {
|
|
2207
2283
|
...panel,
|
|
2208
2284
|
title: song.title,
|
|
2209
|
-
content: /* @__PURE__ */ (0,
|
|
2285
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2210
2286
|
SongPanelContent,
|
|
2211
2287
|
{
|
|
2212
2288
|
song,
|
|
@@ -2225,29 +2301,29 @@ function useSongPanel({
|
|
|
2225
2301
|
|
|
2226
2302
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2227
2303
|
var import_md = require("react-icons/md");
|
|
2228
|
-
var
|
|
2229
|
-
var
|
|
2304
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
2305
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2230
2306
|
function SizeMetricToggle({
|
|
2231
2307
|
value,
|
|
2232
2308
|
onChange
|
|
2233
2309
|
}) {
|
|
2234
|
-
return /* @__PURE__ */ (0,
|
|
2235
|
-
|
|
2310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2311
|
+
import_ui15.SegmentedControl,
|
|
2236
2312
|
{
|
|
2237
2313
|
variant: "floating",
|
|
2238
2314
|
"aria-label": "Size nodes by",
|
|
2239
2315
|
value,
|
|
2240
2316
|
onChange,
|
|
2241
2317
|
options: [
|
|
2242
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2243
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2318
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2319
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2244
2320
|
]
|
|
2245
2321
|
}
|
|
2246
2322
|
);
|
|
2247
2323
|
}
|
|
2248
2324
|
|
|
2249
2325
|
// src/genre-tree/GenreTreeView.tsx
|
|
2250
|
-
var
|
|
2326
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2251
2327
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2252
2328
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2253
2329
|
var noop = () => {
|
|
@@ -2281,6 +2357,7 @@ function GenreTreeView({
|
|
|
2281
2357
|
artistExpanded: controlledArtistExpanded,
|
|
2282
2358
|
onArtistExpandedChange,
|
|
2283
2359
|
getShareUrl,
|
|
2360
|
+
extraSearchGroups = [],
|
|
2284
2361
|
artistSearch = true,
|
|
2285
2362
|
onArtistSelect,
|
|
2286
2363
|
artistSearchBaseUrl,
|
|
@@ -2346,7 +2423,7 @@ function GenreTreeView({
|
|
|
2346
2423
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2347
2424
|
toggleWheelRotation,
|
|
2348
2425
|
onNodeHover: handleNodeHover
|
|
2349
|
-
} = (0,
|
|
2426
|
+
} = (0, import_genre_tree21.useGenreTreeViewState)({
|
|
2350
2427
|
scope,
|
|
2351
2428
|
getBackendBaseUrl,
|
|
2352
2429
|
viewMode: controlledViewMode,
|
|
@@ -2365,12 +2442,12 @@ function GenreTreeView({
|
|
|
2365
2442
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2366
2443
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2367
2444
|
}
|
|
2368
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2369
|
-
const { mutate: updateGenre } = (0,
|
|
2445
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree21.genreUpdateMutationKey }) > 0;
|
|
2446
|
+
const { mutate: updateGenre } = (0, import_genre_tree21.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2370
2447
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2371
2448
|
const isArtistFiltered = artistUuid !== null;
|
|
2372
2449
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2373
|
-
const artistResult = (0,
|
|
2450
|
+
const artistResult = (0, import_genre_tree21.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2374
2451
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2375
2452
|
const filterArtist = (0, import_react16.useMemo)(
|
|
2376
2453
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -2390,7 +2467,7 @@ function GenreTreeView({
|
|
|
2390
2467
|
const openSongRef = (0, import_react16.useRef)(openSong);
|
|
2391
2468
|
openSongRef.current = openSong;
|
|
2392
2469
|
const resolvedRenderSongLink = (0, import_react16.useCallback)(
|
|
2393
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2470
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2394
2471
|
[renderSongLink]
|
|
2395
2472
|
);
|
|
2396
2473
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react16.useState)(null);
|
|
@@ -2402,7 +2479,7 @@ function GenreTreeView({
|
|
|
2402
2479
|
scope,
|
|
2403
2480
|
getBackendBaseUrl: artistBaseUrl,
|
|
2404
2481
|
getShareUrl: getShareUrlRef.current,
|
|
2405
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2482
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2406
2483
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2407
2484
|
onExpandedChange: (on) => {
|
|
2408
2485
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2411,7 +2488,7 @@ function GenreTreeView({
|
|
|
2411
2488
|
renderSongLink: resolvedRenderSongLink,
|
|
2412
2489
|
onClose: () => changeArtist(null)
|
|
2413
2490
|
});
|
|
2414
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2491
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2415
2492
|
const getGenrePath = (genre) => {
|
|
2416
2493
|
const path = [];
|
|
2417
2494
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2467,7 +2544,7 @@ function GenreTreeView({
|
|
|
2467
2544
|
[]
|
|
2468
2545
|
);
|
|
2469
2546
|
const panelRef = (0, import_react16.useRef)(null);
|
|
2470
|
-
const obscuredInset = (0,
|
|
2547
|
+
const obscuredInset = (0, import_ui16.useObscuredInset)(panelRef);
|
|
2471
2548
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2472
2549
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2473
2550
|
const treeNodes = (0, import_react16.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
@@ -2490,52 +2567,27 @@ function GenreTreeView({
|
|
|
2490
2567
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2491
2568
|
const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
|
|
2492
2569
|
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2493
|
-
const
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
onArtistSelect?.(artist);
|
|
2515
|
-
}
|
|
2516
|
-
})),
|
|
2517
|
-
status: artistSearchResult.isError ? "Search failed" : artistSearchResult.settled && artistSearchResult.artists.length === 0 ? "No artists found" : "",
|
|
2518
|
-
loading: artistSearchResult.pending
|
|
2519
|
-
} : void 0;
|
|
2520
|
-
const songSearchResult = (0, import_genre_tree18.useSongSearch)(songBaseUrl, songSearch ? searchText : "");
|
|
2521
|
-
const songGroup = songSearch ? {
|
|
2522
|
-
key: "songs",
|
|
2523
|
-
label: "Songs",
|
|
2524
|
-
items: songSearchResult.songs.map((song) => ({
|
|
2525
|
-
key: song.uuid,
|
|
2526
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: (0, import_genre_tree18.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
2527
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
2528
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("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 ") })
|
|
2529
|
-
] }),
|
|
2530
|
-
onSelect: () => {
|
|
2531
|
-
openSong(song.uuid);
|
|
2532
|
-
onSongSelect?.(song);
|
|
2533
|
-
}
|
|
2534
|
-
})),
|
|
2535
|
-
status: songSearchResult.isError ? "Search failed" : songSearchResult.settled && songSearchResult.songs.length === 0 ? "No songs found" : "",
|
|
2536
|
-
loading: songSearchResult.pending
|
|
2537
|
-
} : void 0;
|
|
2538
|
-
const extraGroups = [artistGroup, songGroup].filter((group) => group !== void 0);
|
|
2570
|
+
const artistGroup = useArtistSearchGroup(
|
|
2571
|
+
artistBaseUrl,
|
|
2572
|
+
searchText,
|
|
2573
|
+
(artist) => {
|
|
2574
|
+
openArtist(artist.uuid);
|
|
2575
|
+
onArtistSelect?.(artist);
|
|
2576
|
+
},
|
|
2577
|
+
Boolean(artistSearch)
|
|
2578
|
+
);
|
|
2579
|
+
const songGroup = useSongSearchGroup(
|
|
2580
|
+
songBaseUrl,
|
|
2581
|
+
searchText,
|
|
2582
|
+
(song) => {
|
|
2583
|
+
openSong(song.uuid);
|
|
2584
|
+
onSongSelect?.(song);
|
|
2585
|
+
},
|
|
2586
|
+
Boolean(songSearch)
|
|
2587
|
+
);
|
|
2588
|
+
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2589
|
+
(group) => group !== void 0
|
|
2590
|
+
);
|
|
2539
2591
|
const handleSelectedNodeChange = (node) => {
|
|
2540
2592
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2541
2593
|
selectNode(node);
|
|
@@ -2559,7 +2611,7 @@ function GenreTreeView({
|
|
|
2559
2611
|
};
|
|
2560
2612
|
const renderGenreBody = (relation) => {
|
|
2561
2613
|
const { node } = relation;
|
|
2562
|
-
const family = /* @__PURE__ */ (0,
|
|
2614
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2563
2615
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2564
2616
|
{
|
|
2565
2617
|
node,
|
|
@@ -2570,10 +2622,10 @@ function GenreTreeView({
|
|
|
2570
2622
|
onSelectNode: selectGenreFromPanel
|
|
2571
2623
|
}
|
|
2572
2624
|
);
|
|
2573
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2625
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2574
2626
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2575
2627
|
const criteria = genrePlaylist?.criteria;
|
|
2576
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0,
|
|
2628
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2577
2629
|
family,
|
|
2578
2630
|
ancestors
|
|
2579
2631
|
] }) };
|
|
@@ -2593,11 +2645,11 @@ function GenreTreeView({
|
|
|
2593
2645
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2594
2646
|
) : void 0;
|
|
2595
2647
|
return {
|
|
2596
|
-
primary: /* @__PURE__ */ (0,
|
|
2597
|
-
/* @__PURE__ */ (0,
|
|
2648
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2649
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2598
2650
|
GenreInfoPanelBody,
|
|
2599
2651
|
{
|
|
2600
|
-
family: /* @__PURE__ */ (0,
|
|
2652
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2601
2653
|
family,
|
|
2602
2654
|
ancestors
|
|
2603
2655
|
] }),
|
|
@@ -2615,7 +2667,7 @@ function GenreTreeView({
|
|
|
2615
2667
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2616
2668
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2617
2669
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2618
|
-
/* @__PURE__ */ (0,
|
|
2670
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2619
2671
|
ChangeParentPopup,
|
|
2620
2672
|
{
|
|
2621
2673
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2690,19 +2742,19 @@ function GenreTreeView({
|
|
|
2690
2742
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2691
2743
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2692
2744
|
} : void 0;
|
|
2693
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
2694
|
-
|
|
2745
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2746
|
+
import_ui16.IconButton,
|
|
2695
2747
|
{
|
|
2696
2748
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2697
2749
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2698
2750
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2699
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
2751
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react12.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react12.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2700
2752
|
}
|
|
2701
2753
|
) : null;
|
|
2702
2754
|
const renderArtistFilter = () => {
|
|
2703
2755
|
if (isLoading || !filterArtist) return null;
|
|
2704
|
-
return /* @__PURE__ */ (0,
|
|
2705
|
-
/* @__PURE__ */ (0,
|
|
2756
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2757
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2706
2758
|
"button",
|
|
2707
2759
|
{
|
|
2708
2760
|
type: "button",
|
|
@@ -2714,7 +2766,7 @@ function GenreTreeView({
|
|
|
2714
2766
|
children: filterArtist.name
|
|
2715
2767
|
}
|
|
2716
2768
|
),
|
|
2717
|
-
/* @__PURE__ */ (0,
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2718
2770
|
"button",
|
|
2719
2771
|
{
|
|
2720
2772
|
type: "button",
|
|
@@ -2726,8 +2778,8 @@ function GenreTreeView({
|
|
|
2726
2778
|
)
|
|
2727
2779
|
] });
|
|
2728
2780
|
};
|
|
2729
|
-
const actions = /* @__PURE__ */ (0,
|
|
2730
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
2781
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2782
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2731
2783
|
GenreSearch,
|
|
2732
2784
|
{
|
|
2733
2785
|
genrePlaylists: visibleGenrePlaylists,
|
|
@@ -2735,14 +2787,14 @@ function GenreTreeView({
|
|
|
2735
2787
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2736
2788
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2737
2789
|
onPrevious: handlePrevious,
|
|
2738
|
-
onQueryChange: artistSearch || songSearch ? setSearchText : void 0,
|
|
2790
|
+
onQueryChange: artistSearch || songSearch || extraSearchGroups.length > 0 ? setSearchText : void 0,
|
|
2739
2791
|
extraGroups
|
|
2740
2792
|
}
|
|
2741
2793
|
),
|
|
2742
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2743
|
-
/* @__PURE__ */ (0,
|
|
2744
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
2745
|
-
|
|
2794
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2795
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
2796
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2797
|
+
import_ui16.SegmentedControl,
|
|
2746
2798
|
{
|
|
2747
2799
|
"aria-label": "Tree view mode",
|
|
2748
2800
|
value: viewMode,
|
|
@@ -2755,14 +2807,14 @@ function GenreTreeView({
|
|
|
2755
2807
|
]
|
|
2756
2808
|
}
|
|
2757
2809
|
),
|
|
2758
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
2810
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2759
2811
|
"div",
|
|
2760
2812
|
{
|
|
2761
2813
|
className: "flex items-center gap-1",
|
|
2762
2814
|
role: "group",
|
|
2763
2815
|
"aria-label": "Tree display options",
|
|
2764
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
2765
|
-
|
|
2816
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2817
|
+
import_ui16.Button,
|
|
2766
2818
|
{
|
|
2767
2819
|
variant: allowWheelRotation ? "default" : "outline",
|
|
2768
2820
|
size: "sm",
|
|
@@ -2772,8 +2824,8 @@ function GenreTreeView({
|
|
|
2772
2824
|
)
|
|
2773
2825
|
}
|
|
2774
2826
|
),
|
|
2775
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
2776
|
-
|
|
2827
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2828
|
+
import_ui16.IconTextButton,
|
|
2777
2829
|
{
|
|
2778
2830
|
icon: import_lucide_react11.Plus,
|
|
2779
2831
|
text: "Add root",
|
|
@@ -2782,34 +2834,34 @@ function GenreTreeView({
|
|
|
2782
2834
|
)
|
|
2783
2835
|
] })
|
|
2784
2836
|
] });
|
|
2785
|
-
return /* @__PURE__ */ (0,
|
|
2837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2786
2838
|
"div",
|
|
2787
2839
|
{
|
|
2788
2840
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2789
2841
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2790
2842
|
children: [
|
|
2791
|
-
/* @__PURE__ */ (0,
|
|
2843
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2792
2844
|
"div",
|
|
2793
2845
|
{
|
|
2794
2846
|
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"}`,
|
|
2795
2847
|
children: actions
|
|
2796
2848
|
}
|
|
2797
2849
|
),
|
|
2798
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2850
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2799
2851
|
"div",
|
|
2800
2852
|
{
|
|
2801
2853
|
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"}`,
|
|
2802
2854
|
children: renderArtistFilter()
|
|
2803
2855
|
}
|
|
2804
2856
|
),
|
|
2805
|
-
/* @__PURE__ */ (0,
|
|
2806
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
2807
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
2857
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2858
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2859
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2808
2860
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2809
2861
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2810
2862
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2811
2863
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2812
|
-
/* @__PURE__ */ (0,
|
|
2864
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2813
2865
|
GenrePlaylistTreeWheel,
|
|
2814
2866
|
{
|
|
2815
2867
|
scope,
|
|
@@ -2826,9 +2878,9 @@ function GenreTreeView({
|
|
|
2826
2878
|
allowWheelRotation,
|
|
2827
2879
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2828
2880
|
busy,
|
|
2829
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2881
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2830
2882
|
floatingControlsLeading,
|
|
2831
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2883
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2832
2884
|
] }) : void 0,
|
|
2833
2885
|
defaultViewport,
|
|
2834
2886
|
viewportRestore,
|
|
@@ -2837,7 +2889,7 @@ function GenreTreeView({
|
|
|
2837
2889
|
) }) })
|
|
2838
2890
|
) : viewMode === "pop-core" ? (
|
|
2839
2891
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2840
|
-
/* @__PURE__ */ (0,
|
|
2892
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2841
2893
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2842
2894
|
{
|
|
2843
2895
|
scope,
|
|
@@ -2854,9 +2906,9 @@ function GenreTreeView({
|
|
|
2854
2906
|
allowWheelRotation,
|
|
2855
2907
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2856
2908
|
busy,
|
|
2857
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2909
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2858
2910
|
floatingControlsLeading,
|
|
2859
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2911
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2860
2912
|
] }) : void 0,
|
|
2861
2913
|
defaultViewport,
|
|
2862
2914
|
viewportRestore,
|
|
@@ -2864,7 +2916,7 @@ function GenreTreeView({
|
|
|
2864
2916
|
...focus
|
|
2865
2917
|
}
|
|
2866
2918
|
) }) })
|
|
2867
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
2919
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2868
2920
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2869
2921
|
{
|
|
2870
2922
|
outline: true,
|
|
@@ -2880,13 +2932,13 @@ function GenreTreeView({
|
|
|
2880
2932
|
selectedNodeId,
|
|
2881
2933
|
readOnly
|
|
2882
2934
|
}
|
|
2883
|
-
) }) : /* @__PURE__ */ (0,
|
|
2935
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
2884
2936
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2885
|
-
return /* @__PURE__ */ (0,
|
|
2937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2886
2938
|
"div",
|
|
2887
2939
|
{
|
|
2888
2940
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2889
|
-
children: /* @__PURE__ */ (0,
|
|
2941
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2890
2942
|
GenrePlaylistTreePerRoot,
|
|
2891
2943
|
{
|
|
2892
2944
|
scope,
|
|
@@ -2912,8 +2964,8 @@ function GenreTreeView({
|
|
|
2912
2964
|
);
|
|
2913
2965
|
}
|
|
2914
2966
|
) }),
|
|
2915
|
-
/* @__PURE__ */ (0,
|
|
2916
|
-
|
|
2967
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2968
|
+
import_ui16.DetailPanel,
|
|
2917
2969
|
{
|
|
2918
2970
|
ref: panelRef,
|
|
2919
2971
|
side: "left",
|
|
@@ -2929,7 +2981,7 @@ function GenreTreeView({
|
|
|
2929
2981
|
aside: activePanel?.aside,
|
|
2930
2982
|
edgeTab: detailTab,
|
|
2931
2983
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
2932
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
2984
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2933
2985
|
activePanel.content,
|
|
2934
2986
|
activePanel.aside
|
|
2935
2987
|
] }) : activePanel?.content
|
|
@@ -2944,10 +2996,10 @@ function GenreTreeView({
|
|
|
2944
2996
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
2945
2997
|
var import_react17 = require("react");
|
|
2946
2998
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
2947
|
-
var
|
|
2999
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2948
3000
|
var import_lucide_react13 = require("lucide-react");
|
|
2949
|
-
var
|
|
2950
|
-
var
|
|
3001
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3002
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2951
3003
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2952
3004
|
function toNode(genre, parentId) {
|
|
2953
3005
|
return {
|
|
@@ -2974,7 +3026,7 @@ function GenreDetailPanel({
|
|
|
2974
3026
|
edgeTab,
|
|
2975
3027
|
extras
|
|
2976
3028
|
}) {
|
|
2977
|
-
const { data: detail, isPending, isError } = (0,
|
|
3029
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree22.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
2978
3030
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
2979
3031
|
const view = (0, import_react17.useMemo)(() => {
|
|
2980
3032
|
if (!detail) return null;
|
|
@@ -3002,17 +3054,17 @@ function GenreDetailPanel({
|
|
|
3002
3054
|
};
|
|
3003
3055
|
}, [detail]);
|
|
3004
3056
|
if (isError) {
|
|
3005
|
-
return /* @__PURE__ */ (0,
|
|
3057
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3006
3058
|
}
|
|
3007
3059
|
if (isPending || !detail || !view) {
|
|
3008
|
-
return /* @__PURE__ */ (0,
|
|
3009
|
-
/* @__PURE__ */ (0,
|
|
3010
|
-
/* @__PURE__ */ (0,
|
|
3011
|
-
/* @__PURE__ */ (0,
|
|
3060
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3061
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui17.Skeleton, { className: "h-6 w-2/3" }),
|
|
3062
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui17.Skeleton, { className: "h-4 w-full" }),
|
|
3063
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui17.Skeleton, { className: "h-4 w-3/4" })
|
|
3012
3064
|
] });
|
|
3013
3065
|
}
|
|
3014
3066
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3015
|
-
const family = /* @__PURE__ */ (0,
|
|
3067
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3016
3068
|
import_genre_tree_view5.GenreNodeFamily,
|
|
3017
3069
|
{
|
|
3018
3070
|
node: view.node,
|
|
@@ -3023,20 +3075,20 @@ function GenreDetailPanel({
|
|
|
3023
3075
|
onSelectNode: selectGenre
|
|
3024
3076
|
}
|
|
3025
3077
|
);
|
|
3026
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
3027
|
-
return /* @__PURE__ */ (0,
|
|
3028
|
-
|
|
3078
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
3080
|
+
import_ui17.DetailPanel,
|
|
3029
3081
|
{
|
|
3030
3082
|
side: "left",
|
|
3031
3083
|
title: view.node.name,
|
|
3032
3084
|
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3033
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
3034
|
-
|
|
3085
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3086
|
+
import_ui17.IconButton,
|
|
3035
3087
|
{
|
|
3036
3088
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3037
3089
|
style: { color: "inherit" },
|
|
3038
3090
|
onClick: () => onExpandedChange(!expanded),
|
|
3039
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
3091
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3040
3092
|
}
|
|
3041
3093
|
) : void 0,
|
|
3042
3094
|
onClose,
|
|
@@ -3045,10 +3097,10 @@ function GenreDetailPanel({
|
|
|
3045
3097
|
onExpandedChange,
|
|
3046
3098
|
edgeTab,
|
|
3047
3099
|
children: [
|
|
3048
|
-
/* @__PURE__ */ (0,
|
|
3100
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3049
3101
|
GenreInfoPanelBody,
|
|
3050
3102
|
{
|
|
3051
|
-
family: /* @__PURE__ */ (0,
|
|
3103
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
3052
3104
|
family,
|
|
3053
3105
|
ancestors
|
|
3054
3106
|
] }),
|
|
@@ -3074,10 +3126,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3074
3126
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3075
3127
|
var import_react18 = require("react");
|
|
3076
3128
|
var import_lucide_react14 = require("lucide-react");
|
|
3077
|
-
var
|
|
3078
|
-
var
|
|
3129
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3130
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3079
3131
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
3080
|
-
var
|
|
3132
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3081
3133
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
|
|
3082
3134
|
function TrackListSidebar({
|
|
3083
3135
|
className: className2 = "",
|
|
@@ -3090,7 +3142,7 @@ function TrackListSidebar({
|
|
|
3090
3142
|
renderArtistLink,
|
|
3091
3143
|
renderSongLink
|
|
3092
3144
|
}) {
|
|
3093
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3145
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree23.useTrackList)();
|
|
3094
3146
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
3095
3147
|
const trackListRef = (0, import_react18.useRef)(null);
|
|
3096
3148
|
const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
|
|
@@ -3179,25 +3231,25 @@ function TrackListSidebar({
|
|
|
3179
3231
|
}
|
|
3180
3232
|
};
|
|
3181
3233
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3182
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3234
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3183
3235
|
"div",
|
|
3184
3236
|
{
|
|
3185
3237
|
"data-theme": "dark",
|
|
3186
3238
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3187
3239
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3188
3240
|
children: [
|
|
3189
|
-
/* @__PURE__ */ (0,
|
|
3190
|
-
/* @__PURE__ */ (0,
|
|
3191
|
-
/* @__PURE__ */ (0,
|
|
3192
|
-
/* @__PURE__ */ (0,
|
|
3193
|
-
/* @__PURE__ */ (0,
|
|
3241
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3242
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3243
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3244
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3245
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3194
3246
|
] }),
|
|
3195
|
-
/* @__PURE__ */ (0,
|
|
3196
|
-
trackList.origin.type ===
|
|
3247
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3248
|
+
trackList.origin.type === import_genre_tree24.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree24.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3197
3249
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3198
3250
|
] })
|
|
3199
3251
|
] }),
|
|
3200
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3252
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3201
3253
|
"button",
|
|
3202
3254
|
{
|
|
3203
3255
|
type: "button",
|
|
@@ -3206,11 +3258,11 @@ function TrackListSidebar({
|
|
|
3206
3258
|
"aria-expanded": !isCollapsed,
|
|
3207
3259
|
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"}`,
|
|
3208
3260
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3209
|
-
children: /* @__PURE__ */ (0,
|
|
3261
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react14.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3210
3262
|
}
|
|
3211
3263
|
)
|
|
3212
3264
|
] }),
|
|
3213
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3265
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3214
3266
|
"ul",
|
|
3215
3267
|
{
|
|
3216
3268
|
ref: trackListRef,
|
|
@@ -3220,7 +3272,7 @@ function TrackListSidebar({
|
|
|
3220
3272
|
onPointerDown: markUserScrollIntent,
|
|
3221
3273
|
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",
|
|
3222
3274
|
children: [
|
|
3223
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3275
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3224
3276
|
TrackItem,
|
|
3225
3277
|
{
|
|
3226
3278
|
track,
|
|
@@ -3231,7 +3283,7 @@ function TrackListSidebar({
|
|
|
3231
3283
|
renderSongLink
|
|
3232
3284
|
}
|
|
3233
3285
|
) }, track.uuid)),
|
|
3234
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3286
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3235
3287
|
]
|
|
3236
3288
|
}
|
|
3237
3289
|
)
|
|
@@ -3244,14 +3296,14 @@ function TrackListSidebar({
|
|
|
3244
3296
|
var import_react19 = require("react");
|
|
3245
3297
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3246
3298
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3247
|
-
var
|
|
3248
|
-
var
|
|
3299
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3300
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
3249
3301
|
|
|
3250
3302
|
// src/genre-tree/PlayerControls.tsx
|
|
3251
3303
|
var import_lucide_react15 = require("lucide-react");
|
|
3252
|
-
var
|
|
3304
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3253
3305
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3254
|
-
var
|
|
3306
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
3255
3307
|
function PlayerControls() {
|
|
3256
3308
|
const {
|
|
3257
3309
|
duration,
|
|
@@ -3263,22 +3315,22 @@ function PlayerControls() {
|
|
|
3263
3315
|
setVolume,
|
|
3264
3316
|
volume
|
|
3265
3317
|
} = (0, import_player12.usePlayer)();
|
|
3266
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3318
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree25.useTrackList)();
|
|
3267
3319
|
const currentTime = (0, import_player12.useCurrentTime)();
|
|
3268
3320
|
const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
|
|
3269
3321
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3270
3322
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3271
3323
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3272
3324
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3273
|
-
return /* @__PURE__ */ (0,
|
|
3325
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
3274
3326
|
"section",
|
|
3275
3327
|
{
|
|
3276
3328
|
"aria-label": "Player controls",
|
|
3277
3329
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3278
3330
|
children: [
|
|
3279
|
-
/* @__PURE__ */ (0,
|
|
3280
|
-
/* @__PURE__ */ (0,
|
|
3281
|
-
/* @__PURE__ */ (0,
|
|
3331
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3332
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree25.formatTime)(safeCurrentTime) }),
|
|
3333
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3282
3334
|
"input",
|
|
3283
3335
|
{
|
|
3284
3336
|
"aria-label": "Seek",
|
|
@@ -3292,11 +3344,11 @@ function PlayerControls() {
|
|
|
3292
3344
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3293
3345
|
}
|
|
3294
3346
|
),
|
|
3295
|
-
/* @__PURE__ */ (0,
|
|
3347
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree25.formatTime)(safeDuration) })
|
|
3296
3348
|
] }),
|
|
3297
|
-
/* @__PURE__ */ (0,
|
|
3298
|
-
/* @__PURE__ */ (0,
|
|
3299
|
-
/* @__PURE__ */ (0,
|
|
3349
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3350
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3351
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3300
3352
|
"button",
|
|
3301
3353
|
{
|
|
3302
3354
|
type: "button",
|
|
@@ -3305,10 +3357,10 @@ function PlayerControls() {
|
|
|
3305
3357
|
disabled: !isReady || !hasPreviousTrack,
|
|
3306
3358
|
onClick: playPreviousTrack,
|
|
3307
3359
|
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",
|
|
3308
|
-
children: /* @__PURE__ */ (0,
|
|
3360
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3309
3361
|
}
|
|
3310
3362
|
),
|
|
3311
|
-
/* @__PURE__ */ (0,
|
|
3363
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3312
3364
|
"button",
|
|
3313
3365
|
{
|
|
3314
3366
|
type: "button",
|
|
@@ -3317,10 +3369,10 @@ function PlayerControls() {
|
|
|
3317
3369
|
disabled: !isReady,
|
|
3318
3370
|
onClick: handlePlayPauseAction,
|
|
3319
3371
|
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",
|
|
3320
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3372
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3321
3373
|
}
|
|
3322
3374
|
),
|
|
3323
|
-
/* @__PURE__ */ (0,
|
|
3375
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3324
3376
|
"button",
|
|
3325
3377
|
{
|
|
3326
3378
|
type: "button",
|
|
@@ -3329,14 +3381,14 @@ function PlayerControls() {
|
|
|
3329
3381
|
disabled: !isReady || !hasNextTrack,
|
|
3330
3382
|
onClick: playNextTrack,
|
|
3331
3383
|
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",
|
|
3332
|
-
children: /* @__PURE__ */ (0,
|
|
3384
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3333
3385
|
}
|
|
3334
3386
|
)
|
|
3335
3387
|
] }),
|
|
3336
|
-
/* @__PURE__ */ (0,
|
|
3337
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3338
|
-
/* @__PURE__ */ (0,
|
|
3339
|
-
/* @__PURE__ */ (0,
|
|
3388
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3389
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3390
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3391
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3340
3392
|
"input",
|
|
3341
3393
|
{
|
|
3342
3394
|
"aria-label": "Volume",
|
|
@@ -3358,7 +3410,7 @@ function PlayerControls() {
|
|
|
3358
3410
|
}
|
|
3359
3411
|
|
|
3360
3412
|
// src/genre-tree/PlayerPanel.tsx
|
|
3361
|
-
var
|
|
3413
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
3362
3414
|
var PANEL_ID = "player-panel";
|
|
3363
3415
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3364
3416
|
function PlayerPanel({
|
|
@@ -3370,8 +3422,8 @@ function PlayerPanel({
|
|
|
3370
3422
|
renderArtistLink,
|
|
3371
3423
|
renderSongLink
|
|
3372
3424
|
}) {
|
|
3373
|
-
const { playerTrackObject } = (0, import_player13.usePlayer)();
|
|
3374
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3425
|
+
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3426
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree26.useTrackListSidebarVisibility)();
|
|
3375
3427
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3376
3428
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3377
3429
|
(0, import_react19.useEffect)(() => {
|
|
@@ -3398,7 +3450,7 @@ function PlayerPanel({
|
|
|
3398
3450
|
onClick: () => setIsExtendRequested(true),
|
|
3399
3451
|
expanded: false
|
|
3400
3452
|
} : null;
|
|
3401
|
-
return /* @__PURE__ */ (0,
|
|
3453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3402
3454
|
"div",
|
|
3403
3455
|
{
|
|
3404
3456
|
id: PANEL_ID,
|
|
@@ -3411,8 +3463,8 @@ function PlayerPanel({
|
|
|
3411
3463
|
),
|
|
3412
3464
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3413
3465
|
children: [
|
|
3414
|
-
/* @__PURE__ */ (0,
|
|
3415
|
-
|
|
3466
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3467
|
+
import_ui18.PanelEdgeTab,
|
|
3416
3468
|
{
|
|
3417
3469
|
side: "right",
|
|
3418
3470
|
edge: tabEdge,
|
|
@@ -3421,7 +3473,7 @@ function PlayerPanel({
|
|
|
3421
3473
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3422
3474
|
}
|
|
3423
3475
|
),
|
|
3424
|
-
/* @__PURE__ */ (0,
|
|
3476
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3425
3477
|
"div",
|
|
3426
3478
|
{
|
|
3427
3479
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3429,15 +3481,20 @@ function PlayerPanel({
|
|
|
3429
3481
|
isExtended ? "flex-row" : "flex-col"
|
|
3430
3482
|
),
|
|
3431
3483
|
children: [
|
|
3432
|
-
/* @__PURE__ */ (0,
|
|
3433
|
-
/* @__PURE__ */ (0,
|
|
3434
|
-
/* @__PURE__ */ (0,
|
|
3435
|
-
/* @__PURE__ */ (0,
|
|
3436
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3484
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3485
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3486
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3487
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3488
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3489
|
+
playerTrackObject.loadError,
|
|
3490
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3491
|
+
void retryPlayback();
|
|
3492
|
+
}, children: "Retry" })
|
|
3493
|
+
] })
|
|
3437
3494
|
] }),
|
|
3438
|
-
/* @__PURE__ */ (0,
|
|
3495
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(PlayerControls, {})
|
|
3439
3496
|
] }),
|
|
3440
|
-
/* @__PURE__ */ (0,
|
|
3497
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3441
3498
|
TrackListSidebar,
|
|
3442
3499
|
{
|
|
3443
3500
|
layout: "inline",
|
|
@@ -3463,33 +3520,33 @@ function PlayerPanel({
|
|
|
3463
3520
|
// src/genre-tree/Rating.tsx
|
|
3464
3521
|
var import_uuid = require("uuid");
|
|
3465
3522
|
var import_ai = require("react-icons/ai");
|
|
3466
|
-
var
|
|
3467
|
-
var
|
|
3523
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3524
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
3468
3525
|
function Rating({ rating, handleChange }) {
|
|
3469
3526
|
const instanceId = (0, import_uuid.v4)();
|
|
3470
|
-
return /* @__PURE__ */ (0,
|
|
3471
|
-
/* @__PURE__ */ (0,
|
|
3527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3528
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3472
3529
|
"input",
|
|
3473
3530
|
{
|
|
3474
3531
|
id: `star-${instanceId}-${i}`,
|
|
3475
3532
|
className: "w-6 h-6 opacity-0",
|
|
3476
3533
|
type: "radio",
|
|
3477
3534
|
name: "rating",
|
|
3478
|
-
value: i === 0 ?
|
|
3535
|
+
value: i === 0 ? import_genre_tree27.FORM_RATING_NULL_VALUE : i,
|
|
3479
3536
|
checked: rating === (i === 0 ? null : i),
|
|
3480
3537
|
onChange: handleChange
|
|
3481
3538
|
}
|
|
3482
3539
|
),
|
|
3483
|
-
/* @__PURE__ */ (0,
|
|
3484
|
-
/* @__PURE__ */ (0,
|
|
3540
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex", children: [
|
|
3541
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3485
3542
|
import_ai.AiOutlineStar,
|
|
3486
3543
|
{
|
|
3487
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3544
|
+
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree27.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3488
3545
|
}
|
|
3489
3546
|
),
|
|
3490
|
-
/* @__PURE__ */ (0,
|
|
3547
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-grow w-8" }),
|
|
3491
3548
|
" "
|
|
3492
|
-
] }) : /* @__PURE__ */ (0,
|
|
3549
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3493
3550
|
import_ai.AiFillStar,
|
|
3494
3551
|
{
|
|
3495
3552
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3511,9 +3568,15 @@ function Rating({ rating, handleChange }) {
|
|
|
3511
3568
|
GenreTreeWheelSkeleton,
|
|
3512
3569
|
PlayerPanel,
|
|
3513
3570
|
Rating,
|
|
3571
|
+
SEARCH_FAILED_STATUS,
|
|
3572
|
+
SearchBox,
|
|
3514
3573
|
TrackItem,
|
|
3515
3574
|
TrackListSidebar,
|
|
3516
3575
|
TrackPositionPlayPause,
|
|
3517
|
-
|
|
3576
|
+
searchStatus,
|
|
3577
|
+
useArtistSearchGroup,
|
|
3578
|
+
useGenrePlaylistPlayback,
|
|
3579
|
+
useGenreRemoteSearchGroup,
|
|
3580
|
+
useSongSearchGroup
|
|
3518
3581
|
});
|
|
3519
3582
|
//# sourceMappingURL=index.cjs.map
|