@behindthemusictree/app-kit-ui 33.1.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-2M4Q2IJU.js → chunk-FHGQGTCN.js} +512 -460
- package/dist/chunk-FHGQGTCN.js.map +1 -0
- package/dist/genre-tree/index.cjs +592 -535
- 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 +590 -533
- 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-2M4Q2IJU.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 focus-visible:ring-0`,
|
|
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,7 +1353,7 @@ 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)();
|
|
@@ -1287,11 +1362,11 @@ function GenrePlaylistTracks({
|
|
|
1287
1362
|
const { fetch } = (0, import_transport3.useFetchWrapper)(getBackendBaseUrl);
|
|
1288
1363
|
const playback = (0, import_transport3.usePlaybackContext)();
|
|
1289
1364
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1290
|
-
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] : []],
|
|
1291
1366
|
queryFn: async ({ pageParam }) => {
|
|
1292
1367
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1293
1368
|
const response = await fetch(
|
|
1294
|
-
|
|
1369
|
+
import_genre_tree8.libraryEndpoints.reference.songs.list(),
|
|
1295
1370
|
true,
|
|
1296
1371
|
false,
|
|
1297
1372
|
{},
|
|
@@ -1322,35 +1397,35 @@ function GenrePlaylistTracks({
|
|
|
1322
1397
|
const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1323
1398
|
started.catch((error) => {
|
|
1324
1399
|
console.error("Failed to play genre playlist:", error);
|
|
1325
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1400
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
|
|
1326
1401
|
});
|
|
1327
1402
|
};
|
|
1328
|
-
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: [
|
|
1329
1404
|
renderTracksToolbar?.(),
|
|
1330
|
-
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: [
|
|
1331
1406
|
"Couldn't load ",
|
|
1332
1407
|
trackNoun.plural,
|
|
1333
1408
|
".",
|
|
1334
1409
|
" ",
|
|
1335
|
-
/* @__PURE__ */ (0,
|
|
1336
|
-
] }) : 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: [
|
|
1337
1412
|
tracks.map((track, index) => {
|
|
1338
1413
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1339
1414
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1340
1415
|
const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
|
|
1341
|
-
const text = /* @__PURE__ */ (0,
|
|
1342
|
-
/* @__PURE__ */ (0,
|
|
1343
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1344
|
-
/* @__PURE__ */ (0,
|
|
1345
|
-
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 })
|
|
1346
1421
|
] });
|
|
1347
1422
|
const leading = renderSongLeading?.(track, index);
|
|
1348
1423
|
const actions = renderSongActions?.(track);
|
|
1349
|
-
const isUnplayable = (0,
|
|
1424
|
+
const isUnplayable = (0, import_genre_tree9.isSong)(track) && (0, import_genre_tree9.songUnplayableReason)(track) !== null;
|
|
1350
1425
|
if (isUnplayable) {
|
|
1351
|
-
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: [
|
|
1352
1427
|
leading,
|
|
1353
|
-
/* @__PURE__ */ (0,
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1354
1429
|
"button",
|
|
1355
1430
|
{
|
|
1356
1431
|
type: "button",
|
|
@@ -1358,27 +1433,27 @@ function GenrePlaylistTracks({
|
|
|
1358
1433
|
"aria-disabled": "true",
|
|
1359
1434
|
"aria-label": label,
|
|
1360
1435
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1361
|
-
children: /* @__PURE__ */ (0,
|
|
1436
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1362
1437
|
}
|
|
1363
1438
|
),
|
|
1364
1439
|
text,
|
|
1365
1440
|
actions
|
|
1366
1441
|
] }, track.uuid);
|
|
1367
1442
|
}
|
|
1368
|
-
return /* @__PURE__ */ (0,
|
|
1443
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1369
1444
|
"li",
|
|
1370
1445
|
{
|
|
1371
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)]" : ""}`,
|
|
1372
1447
|
children: [
|
|
1373
1448
|
leading,
|
|
1374
|
-
/* @__PURE__ */ (0,
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1375
1450
|
"button",
|
|
1376
1451
|
{
|
|
1377
1452
|
type: "button",
|
|
1378
1453
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1379
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"}`,
|
|
1380
1455
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1381
|
-
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 })
|
|
1382
1457
|
}
|
|
1383
1458
|
),
|
|
1384
1459
|
text,
|
|
@@ -1388,7 +1463,7 @@ function GenrePlaylistTracks({
|
|
|
1388
1463
|
track.uuid
|
|
1389
1464
|
);
|
|
1390
1465
|
}),
|
|
1391
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1466
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1392
1467
|
] })
|
|
1393
1468
|
] }) });
|
|
1394
1469
|
}
|
|
@@ -1396,7 +1471,7 @@ function GenrePlaylistTracks({
|
|
|
1396
1471
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1397
1472
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1398
1473
|
var import_si2 = require("react-icons/si");
|
|
1399
|
-
var
|
|
1474
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1400
1475
|
function GenreDetailExtras({
|
|
1401
1476
|
genreUuid,
|
|
1402
1477
|
genrePlaylist,
|
|
@@ -1413,24 +1488,24 @@ function GenreDetailExtras({
|
|
|
1413
1488
|
...trackSlots
|
|
1414
1489
|
}) {
|
|
1415
1490
|
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1416
|
-
const { data: overview, isPending } = (0,
|
|
1491
|
+
const { data: overview, isPending } = (0, import_genre_tree12.useFetchGenreOverview)(
|
|
1417
1492
|
genreUuid,
|
|
1418
1493
|
scope,
|
|
1419
1494
|
getBackendBaseUrl,
|
|
1420
1495
|
criteriaOverviewSchema
|
|
1421
1496
|
);
|
|
1422
1497
|
if (isPending) {
|
|
1423
|
-
return /* @__PURE__ */ (0,
|
|
1424
|
-
/* @__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" }) }) }),
|
|
1425
1500
|
family
|
|
1426
1501
|
] });
|
|
1427
1502
|
}
|
|
1428
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1503
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: family });
|
|
1429
1504
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1430
1505
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1431
|
-
return /* @__PURE__ */ (0,
|
|
1432
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1433
|
-
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)(
|
|
1434
1509
|
"a",
|
|
1435
1510
|
{
|
|
1436
1511
|
href: summarySourceUrl,
|
|
@@ -1438,12 +1513,12 @@ function GenreDetailExtras({
|
|
|
1438
1513
|
rel: "noopener noreferrer",
|
|
1439
1514
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1440
1515
|
children: [
|
|
1441
|
-
/* @__PURE__ */ (0,
|
|
1516
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1442
1517
|
"Wikipedia"
|
|
1443
1518
|
]
|
|
1444
1519
|
}
|
|
1445
1520
|
),
|
|
1446
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1521
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1447
1522
|
"a",
|
|
1448
1523
|
{
|
|
1449
1524
|
href: wikidataUrl,
|
|
@@ -1451,17 +1526,17 @@ function GenreDetailExtras({
|
|
|
1451
1526
|
rel: "noopener noreferrer",
|
|
1452
1527
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1453
1528
|
children: [
|
|
1454
|
-
/* @__PURE__ */ (0,
|
|
1529
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1455
1530
|
"Wikidata"
|
|
1456
1531
|
]
|
|
1457
1532
|
}
|
|
1458
1533
|
)
|
|
1459
1534
|
] }) }),
|
|
1460
|
-
/* @__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) }),
|
|
1461
1536
|
family,
|
|
1462
1537
|
renderGenreDetailExtras?.(overview),
|
|
1463
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1464
|
-
/* @__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)(
|
|
1465
1540
|
GenrePlaylistTracks,
|
|
1466
1541
|
{
|
|
1467
1542
|
genreUuid,
|
|
@@ -1477,7 +1552,7 @@ function GenreDetailExtras({
|
|
|
1477
1552
|
}
|
|
1478
1553
|
|
|
1479
1554
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1480
|
-
var
|
|
1555
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1481
1556
|
var import_react12 = require("react");
|
|
1482
1557
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1483
1558
|
const { isPlaying, setIsPlaying } = (0, import_player6.usePlayer)();
|
|
@@ -1491,7 +1566,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1491
1566
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1492
1567
|
});
|
|
1493
1568
|
};
|
|
1494
|
-
return /* @__PURE__ */ (0,
|
|
1569
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1495
1570
|
GenreActionBand,
|
|
1496
1571
|
{
|
|
1497
1572
|
...bandProps,
|
|
@@ -1512,9 +1587,9 @@ function GenreInfoPanelBody({
|
|
|
1512
1587
|
band,
|
|
1513
1588
|
extras
|
|
1514
1589
|
}) {
|
|
1515
|
-
return /* @__PURE__ */ (0,
|
|
1590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
1516
1591
|
toolbar,
|
|
1517
|
-
/* @__PURE__ */ (0,
|
|
1592
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1518
1593
|
/* @__PURE__ */ (0, import_react12.createElement)(
|
|
1519
1594
|
GenreDetailExtras,
|
|
1520
1595
|
{
|
|
@@ -1533,8 +1608,8 @@ function GenreInfoPanelBody({
|
|
|
1533
1608
|
|
|
1534
1609
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1535
1610
|
var import_react13 = require("react");
|
|
1536
|
-
var
|
|
1537
|
-
var
|
|
1611
|
+
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1612
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1538
1613
|
function ChangeParentPopup({
|
|
1539
1614
|
genrePlaylist,
|
|
1540
1615
|
genrePlaylists,
|
|
@@ -1543,9 +1618,9 @@ function ChangeParentPopup({
|
|
|
1543
1618
|
onClose,
|
|
1544
1619
|
onParentChanged
|
|
1545
1620
|
}) {
|
|
1546
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1621
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree13.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1547
1622
|
const candidates = (0, import_react13.useMemo)(
|
|
1548
|
-
() => (0,
|
|
1623
|
+
() => (0, import_genre_tree13.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1549
1624
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1550
1625
|
);
|
|
1551
1626
|
const handleSelect = (newParent) => {
|
|
@@ -1569,12 +1644,12 @@ function ChangeParentPopup({
|
|
|
1569
1644
|
window.addEventListener("keydown", handleKeyDown);
|
|
1570
1645
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1571
1646
|
}, [onClose]);
|
|
1572
|
-
return /* @__PURE__ */ (0,
|
|
1573
|
-
/* @__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: [
|
|
1574
1649
|
"Current parent: ",
|
|
1575
1650
|
genrePlaylist.parent?.name ?? "none"
|
|
1576
1651
|
] }),
|
|
1577
|
-
/* @__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)(
|
|
1578
1653
|
GenreSearch,
|
|
1579
1654
|
{
|
|
1580
1655
|
genrePlaylists: candidates,
|
|
@@ -1582,22 +1657,22 @@ function ChangeParentPopup({
|
|
|
1582
1657
|
placeholder: "Search a new parent\u2026"
|
|
1583
1658
|
}
|
|
1584
1659
|
) }),
|
|
1585
|
-
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(" ") })
|
|
1586
1661
|
] });
|
|
1587
1662
|
}
|
|
1588
1663
|
|
|
1589
1664
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1590
|
-
var
|
|
1591
|
-
var
|
|
1665
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
1666
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1592
1667
|
|
|
1593
1668
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1594
|
-
var
|
|
1669
|
+
var import_ui8 = require("@behindthemusictree/ui");
|
|
1595
1670
|
|
|
1596
1671
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
1597
1672
|
var import_lucide_react8 = require("lucide-react");
|
|
1598
1673
|
var import_si3 = require("react-icons/si");
|
|
1599
|
-
var
|
|
1600
|
-
var
|
|
1674
|
+
var import_ui7 = require("@behindthemusictree/ui");
|
|
1675
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1601
1676
|
var LINK_KINDS = {
|
|
1602
1677
|
official: { label: "Official website", Icon: import_lucide_react8.Globe },
|
|
1603
1678
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -1634,7 +1709,7 @@ function artistLinks(artist) {
|
|
|
1634
1709
|
return links;
|
|
1635
1710
|
}
|
|
1636
1711
|
function ArtistLinks({ links }) {
|
|
1637
|
-
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)) });
|
|
1638
1713
|
}
|
|
1639
1714
|
|
|
1640
1715
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1645,7 +1720,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1645
1720
|
}
|
|
1646
1721
|
|
|
1647
1722
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1648
|
-
var
|
|
1723
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1649
1724
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1650
1725
|
function ArtistHeader({ artist }) {
|
|
1651
1726
|
const fields = [];
|
|
@@ -1655,35 +1730,35 @@ function ArtistHeader({ artist }) {
|
|
|
1655
1730
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1656
1731
|
if (years) fields.push({ label: "Active", value: years });
|
|
1657
1732
|
const links = artistLinks(artist);
|
|
1658
|
-
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 }) });
|
|
1659
1734
|
if (fields.length === 0) return null;
|
|
1660
|
-
return /* @__PURE__ */ (0,
|
|
1735
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui8.FieldList, { items: fields });
|
|
1661
1736
|
}
|
|
1662
1737
|
|
|
1663
1738
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1664
|
-
var
|
|
1739
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1665
1740
|
function ExternalLink({ href, children }) {
|
|
1666
|
-
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 });
|
|
1667
1742
|
}
|
|
1668
1743
|
function ArtistPhoto({ image, name }) {
|
|
1669
|
-
return /* @__PURE__ */ (0,
|
|
1670
|
-
/* @__PURE__ */ (0,
|
|
1671
|
-
/* @__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: [
|
|
1672
1747
|
"Photo: ",
|
|
1673
|
-
/* @__PURE__ */ (0,
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1674
1749
|
",",
|
|
1675
1750
|
" ",
|
|
1676
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
1751
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1677
1752
|
] })
|
|
1678
1753
|
] });
|
|
1679
1754
|
}
|
|
1680
1755
|
|
|
1681
1756
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1682
|
-
var
|
|
1683
|
-
var
|
|
1757
|
+
var import_ui9 = require("@behindthemusictree/ui");
|
|
1758
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1684
1759
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1685
1760
|
if (memberships.length === 0) return null;
|
|
1686
|
-
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) => {
|
|
1687
1762
|
const details = [
|
|
1688
1763
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1689
1764
|
...membership.roles
|
|
@@ -1691,9 +1766,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1691
1766
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1692
1767
|
return (
|
|
1693
1768
|
// A member can have several periods in the same group.
|
|
1694
|
-
/* @__PURE__ */ (0,
|
|
1695
|
-
/* @__PURE__ */ (0,
|
|
1696
|
-
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: [
|
|
1697
1772
|
" \u2014 ",
|
|
1698
1773
|
detailsText
|
|
1699
1774
|
] })
|
|
@@ -1704,9 +1779,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1704
1779
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1705
1780
|
if (memberships.length === 0) return null;
|
|
1706
1781
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1707
|
-
return /* @__PURE__ */ (0,
|
|
1708
|
-
/* @__PURE__ */ (0,
|
|
1709
|
-
/* @__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)(
|
|
1710
1785
|
MembershipList,
|
|
1711
1786
|
{
|
|
1712
1787
|
title: "Past",
|
|
@@ -1718,8 +1793,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1718
1793
|
}
|
|
1719
1794
|
|
|
1720
1795
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1721
|
-
var
|
|
1722
|
-
var
|
|
1796
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
1797
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1723
1798
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1724
1799
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1725
1800
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1739,12 +1814,12 @@ function byYear(a, b) {
|
|
|
1739
1814
|
function ArtistDiscography({ albums }) {
|
|
1740
1815
|
if (albums.length === 0) return null;
|
|
1741
1816
|
const sorted = [...albums].sort(byYear);
|
|
1742
|
-
return /* @__PURE__ */ (0,
|
|
1817
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
1743
1818
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1744
1819
|
if (groupAlbums.length === 0) return null;
|
|
1745
|
-
return /* @__PURE__ */ (0,
|
|
1746
|
-
/* @__PURE__ */ (0,
|
|
1747
|
-
/* @__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 })
|
|
1748
1823
|
] }, album.uuid)) }) }, group);
|
|
1749
1824
|
}) });
|
|
1750
1825
|
}
|
|
@@ -1753,20 +1828,20 @@ function ArtistDiscography({ albums }) {
|
|
|
1753
1828
|
var import_react14 = require("react");
|
|
1754
1829
|
var import_react_query2 = require("@tanstack/react-query");
|
|
1755
1830
|
var import_lucide_react9 = require("lucide-react");
|
|
1756
|
-
var
|
|
1831
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
1757
1832
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
1758
|
-
var
|
|
1833
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1759
1834
|
var import_transport7 = require("@behindthemusictree/app-kit/transport");
|
|
1760
1835
|
|
|
1761
1836
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1762
1837
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
1763
|
-
var
|
|
1838
|
+
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1764
1839
|
|
|
1765
1840
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
1766
1841
|
var import_fa3 = require("react-icons/fa");
|
|
1767
1842
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1768
1843
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
1769
|
-
var
|
|
1844
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1770
1845
|
function TrackPositionPlayPause({
|
|
1771
1846
|
position,
|
|
1772
1847
|
uuid,
|
|
@@ -1780,14 +1855,14 @@ function TrackPositionPlayPause({
|
|
|
1780
1855
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
1781
1856
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
1782
1857
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1783
|
-
return /* @__PURE__ */ (0,
|
|
1858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
1784
1859
|
"div",
|
|
1785
1860
|
{
|
|
1786
1861
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1787
1862
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1788
1863
|
onClick: handlePlayPauseClick,
|
|
1789
1864
|
children: [
|
|
1790
|
-
/* @__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)(
|
|
1791
1866
|
"span",
|
|
1792
1867
|
{
|
|
1793
1868
|
className: "track-position-play-pause__bars",
|
|
@@ -1795,18 +1870,18 @@ function TrackPositionPlayPause({
|
|
|
1795
1870
|
role: "img",
|
|
1796
1871
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1797
1872
|
children: [
|
|
1798
|
-
/* @__PURE__ */ (0,
|
|
1799
|
-
/* @__PURE__ */ (0,
|
|
1800
|
-
/* @__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" })
|
|
1801
1876
|
]
|
|
1802
1877
|
}
|
|
1803
1878
|
) : position }),
|
|
1804
|
-
/* @__PURE__ */ (0,
|
|
1879
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1805
1880
|
"div",
|
|
1806
1881
|
{
|
|
1807
1882
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1808
1883
|
"aria-hidden": "true",
|
|
1809
|
-
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, {})
|
|
1810
1885
|
}
|
|
1811
1886
|
)
|
|
1812
1887
|
]
|
|
@@ -1815,9 +1890,9 @@ function TrackPositionPlayPause({
|
|
|
1815
1890
|
}
|
|
1816
1891
|
|
|
1817
1892
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1818
|
-
var
|
|
1819
|
-
var
|
|
1820
|
-
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");
|
|
1821
1896
|
function TrackItem({
|
|
1822
1897
|
track,
|
|
1823
1898
|
position,
|
|
@@ -1827,11 +1902,11 @@ function TrackItem({
|
|
|
1827
1902
|
renderSongLink
|
|
1828
1903
|
}) {
|
|
1829
1904
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
1830
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
1905
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree14.useTrackList)();
|
|
1831
1906
|
const scope = trackList?.origin?.scope ?? null;
|
|
1832
1907
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
1833
1908
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
1834
|
-
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;
|
|
1835
1910
|
const handlePlayPauseClick = (event) => {
|
|
1836
1911
|
event.stopPropagation();
|
|
1837
1912
|
if (isCurrentTrack) {
|
|
@@ -1841,12 +1916,12 @@ function TrackItem({
|
|
|
1841
1916
|
loadTrackForPlayer(track.uuid);
|
|
1842
1917
|
}
|
|
1843
1918
|
};
|
|
1844
|
-
return /* @__PURE__ */ (0,
|
|
1919
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
1845
1920
|
"div",
|
|
1846
1921
|
{
|
|
1847
1922
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1848
1923
|
children: [
|
|
1849
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
1924
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1850
1925
|
"div",
|
|
1851
1926
|
{
|
|
1852
1927
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1854,7 +1929,7 @@ function TrackItem({
|
|
|
1854
1929
|
"aria-disabled": "true",
|
|
1855
1930
|
children: position
|
|
1856
1931
|
}
|
|
1857
|
-
) : /* @__PURE__ */ (0,
|
|
1932
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1858
1933
|
TrackPositionPlayPause,
|
|
1859
1934
|
{
|
|
1860
1935
|
position,
|
|
@@ -1863,12 +1938,12 @@ function TrackItem({
|
|
|
1863
1938
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1864
1939
|
}
|
|
1865
1940
|
),
|
|
1866
|
-
/* @__PURE__ */ (0,
|
|
1867
|
-
/* @__PURE__ */ (0,
|
|
1868
|
-
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 }) }) : ""
|
|
1869
1944
|
] }),
|
|
1870
|
-
/* @__PURE__ */ (0,
|
|
1871
|
-
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)(
|
|
1872
1947
|
"div",
|
|
1873
1948
|
{
|
|
1874
1949
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1876,15 +1951,15 @@ function TrackItem({
|
|
|
1876
1951
|
children: renderDuration(track)
|
|
1877
1952
|
}
|
|
1878
1953
|
),
|
|
1879
|
-
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) })
|
|
1880
1955
|
]
|
|
1881
1956
|
}
|
|
1882
1957
|
);
|
|
1883
1958
|
}
|
|
1884
1959
|
|
|
1885
1960
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1886
|
-
var
|
|
1887
|
-
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);
|
|
1888
1963
|
var SORTS = [
|
|
1889
1964
|
{ ordering: "title", label: "Title" },
|
|
1890
1965
|
{ ordering: "-albumYear", label: "Year" }
|
|
@@ -1893,11 +1968,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1893
1968
|
const [page, setPage] = (0, import_react14.useState)(1);
|
|
1894
1969
|
const [ordering, setOrdering] = (0, import_react14.useState)("title");
|
|
1895
1970
|
const { fetch } = (0, import_transport7.useFetchWrapper)(getBackendBaseUrl);
|
|
1896
|
-
const { playNewTrackListFromArtist } = (0,
|
|
1971
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree17.useTrackList)();
|
|
1897
1972
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
1898
1973
|
const { data, isPending, isError } = (0, import_transport7.useQueryWithParse)({
|
|
1899
1974
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
1900
|
-
queryFn: () => fetch(
|
|
1975
|
+
queryFn: () => fetch(import_genre_tree17.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
1901
1976
|
artist: artist.uuid,
|
|
1902
1977
|
ordering,
|
|
1903
1978
|
page
|
|
@@ -1912,25 +1987,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1912
1987
|
});
|
|
1913
1988
|
const handleRowClickCapture = (event, song) => {
|
|
1914
1989
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
1915
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
1990
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree17.songUnplayableReason)(song) !== null) return;
|
|
1916
1991
|
event.stopPropagation();
|
|
1917
1992
|
void play(song.uuid);
|
|
1918
1993
|
};
|
|
1919
|
-
return /* @__PURE__ */ (0,
|
|
1920
|
-
/* @__PURE__ */ (0,
|
|
1921
|
-
/* @__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)(
|
|
1922
1997
|
"button",
|
|
1923
1998
|
{
|
|
1924
1999
|
type: "button",
|
|
1925
2000
|
onClick: () => void play(),
|
|
1926
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",
|
|
1927
2002
|
children: [
|
|
1928
|
-
/* @__PURE__ */ (0,
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react9.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1929
2004
|
"Play all"
|
|
1930
2005
|
]
|
|
1931
2006
|
}
|
|
1932
2007
|
),
|
|
1933
|
-
/* @__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)(
|
|
1934
2009
|
"button",
|
|
1935
2010
|
{
|
|
1936
2011
|
type: "button",
|
|
@@ -1945,7 +2020,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1945
2020
|
sort.ordering
|
|
1946
2021
|
)) })
|
|
1947
2022
|
] }),
|
|
1948
|
-
/* @__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)(
|
|
1949
2024
|
TrackItem,
|
|
1950
2025
|
{
|
|
1951
2026
|
track: song,
|
|
@@ -1954,12 +2029,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1954
2029
|
renderSongLink
|
|
1955
2030
|
}
|
|
1956
2031
|
) }, song.uuid)) }),
|
|
1957
|
-
/* @__PURE__ */ (0,
|
|
2032
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1958
2033
|
] }) });
|
|
1959
2034
|
}
|
|
1960
2035
|
|
|
1961
2036
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1962
|
-
var
|
|
2037
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1963
2038
|
function useArtistPanel({
|
|
1964
2039
|
uuid,
|
|
1965
2040
|
scope,
|
|
@@ -1971,7 +2046,7 @@ function useArtistPanel({
|
|
|
1971
2046
|
onExpandedChange,
|
|
1972
2047
|
onClose
|
|
1973
2048
|
}) {
|
|
1974
|
-
const result = (0,
|
|
2049
|
+
const result = (0, import_genre_tree18.useArtist)(uuid, getBackendBaseUrl);
|
|
1975
2050
|
const panel = { expanded, onExpandedChange, onClose };
|
|
1976
2051
|
switch (result.status) {
|
|
1977
2052
|
case "idle":
|
|
@@ -1980,26 +2055,26 @@ function useArtistPanel({
|
|
|
1980
2055
|
return {
|
|
1981
2056
|
...panel,
|
|
1982
2057
|
title: "Artist",
|
|
1983
|
-
content: /* @__PURE__ */ (0,
|
|
1984
|
-
/* @__PURE__ */ (0,
|
|
1985
|
-
/* @__PURE__ */ (0,
|
|
1986
|
-
/* @__PURE__ */ (0,
|
|
1987
|
-
/* @__PURE__ */ (0,
|
|
1988
|
-
/* @__PURE__ */ (0,
|
|
1989
|
-
/* @__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" })
|
|
1990
2065
|
] })
|
|
1991
2066
|
};
|
|
1992
2067
|
case "not-found":
|
|
1993
2068
|
return {
|
|
1994
2069
|
...panel,
|
|
1995
2070
|
title: "Artist not found",
|
|
1996
|
-
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." })
|
|
1997
2072
|
};
|
|
1998
2073
|
case "error":
|
|
1999
2074
|
return {
|
|
2000
2075
|
...panel,
|
|
2001
2076
|
title: "Artist",
|
|
2002
|
-
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." })
|
|
2003
2078
|
};
|
|
2004
2079
|
case "success": {
|
|
2005
2080
|
const { artist } = result;
|
|
@@ -2008,13 +2083,13 @@ function useArtistPanel({
|
|
|
2008
2083
|
return {
|
|
2009
2084
|
...panel,
|
|
2010
2085
|
title: artist.name,
|
|
2011
|
-
content: /* @__PURE__ */ (0,
|
|
2012
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2013
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2014
|
-
/* @__PURE__ */ (0,
|
|
2015
|
-
/* @__PURE__ */ (0,
|
|
2016
|
-
/* @__PURE__ */ (0,
|
|
2017
|
-
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)(
|
|
2018
2093
|
ArtistSongs,
|
|
2019
2094
|
{
|
|
2020
2095
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2024,56 +2099,56 @@ function useArtistPanel({
|
|
|
2024
2099
|
}
|
|
2025
2100
|
)
|
|
2026
2101
|
] }),
|
|
2027
|
-
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
|
|
2028
2103
|
};
|
|
2029
2104
|
}
|
|
2030
2105
|
}
|
|
2031
2106
|
}
|
|
2032
2107
|
|
|
2033
2108
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2034
|
-
var
|
|
2035
|
-
var
|
|
2109
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
2110
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2036
2111
|
|
|
2037
2112
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2038
2113
|
var import_react15 = require("react");
|
|
2039
2114
|
var import_lucide_react10 = require("lucide-react");
|
|
2040
|
-
var
|
|
2115
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
2041
2116
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2042
|
-
var
|
|
2043
|
-
var
|
|
2117
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2118
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2044
2119
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2045
2120
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2046
2121
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2047
2122
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2048
2123
|
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
2049
|
-
const history = (0,
|
|
2124
|
+
const history = (0, import_genre_tree19.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2050
2125
|
let body = null;
|
|
2051
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2052
|
-
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." });
|
|
2053
2128
|
else if (history.status === "success")
|
|
2054
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2055
|
-
/* @__PURE__ */ (0,
|
|
2056
|
-
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: [
|
|
2057
2132
|
" ",
|
|
2058
2133
|
"\u2014 ",
|
|
2059
2134
|
entry.oldValue ?? "none",
|
|
2060
2135
|
" \u2192 ",
|
|
2061
2136
|
entry.newValue ?? "none"
|
|
2062
2137
|
] }) : null,
|
|
2063
|
-
/* @__PURE__ */ (0,
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2064
2139
|
entry.actorPseudo ?? "Anonymous",
|
|
2065
2140
|
" \xB7 ",
|
|
2066
2141
|
dateFormat.format(new Date(entry.createdOn))
|
|
2067
2142
|
] })
|
|
2068
2143
|
] }, entry.uuid)) });
|
|
2069
|
-
return /* @__PURE__ */ (0,
|
|
2144
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2070
2145
|
"details",
|
|
2071
2146
|
{
|
|
2072
2147
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2073
2148
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2074
2149
|
children: [
|
|
2075
|
-
/* @__PURE__ */ (0,
|
|
2076
|
-
/* @__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 })
|
|
2077
2152
|
]
|
|
2078
2153
|
}
|
|
2079
2154
|
);
|
|
@@ -2087,25 +2162,25 @@ function SongPanelContent({
|
|
|
2087
2162
|
genrePath,
|
|
2088
2163
|
onSelectGenre
|
|
2089
2164
|
}) {
|
|
2090
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2165
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree19.useTrackList)();
|
|
2091
2166
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2092
|
-
const unplayableReason = (0,
|
|
2167
|
+
const unplayableReason = (0, import_genre_tree19.songUnplayableReason)(song);
|
|
2093
2168
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2094
2169
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2095
2170
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2096
|
-
const musicBrainzUrl = (0,
|
|
2171
|
+
const musicBrainzUrl = (0, import_genre_tree19.musicBrainzRecordingUrl)(song);
|
|
2097
2172
|
const fields = [];
|
|
2098
2173
|
if (song.genre) {
|
|
2099
2174
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2100
2175
|
fields.push({
|
|
2101
2176
|
label: "Genre",
|
|
2102
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2103
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2104
|
-
/* @__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 })
|
|
2105
2180
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2106
2181
|
});
|
|
2107
2182
|
} else {
|
|
2108
|
-
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" }) });
|
|
2109
2184
|
}
|
|
2110
2185
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2111
2186
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2115,11 +2190,11 @@ function SongPanelContent({
|
|
|
2115
2190
|
const links = [];
|
|
2116
2191
|
if (musicBrainzUrl)
|
|
2117
2192
|
links.push(
|
|
2118
|
-
/* @__PURE__ */ (0,
|
|
2193
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2119
2194
|
);
|
|
2120
2195
|
if (song.youtubeVideo)
|
|
2121
2196
|
links.push(
|
|
2122
|
-
/* @__PURE__ */ (0,
|
|
2197
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2123
2198
|
"a",
|
|
2124
2199
|
{
|
|
2125
2200
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2134,33 +2209,33 @@ function SongPanelContent({
|
|
|
2134
2209
|
if (links.length > 0)
|
|
2135
2210
|
fields.push({
|
|
2136
2211
|
label: "Links",
|
|
2137
|
-
value: /* @__PURE__ */ (0,
|
|
2212
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "flex gap-3", children: links })
|
|
2138
2213
|
});
|
|
2139
|
-
return /* @__PURE__ */ (0,
|
|
2140
|
-
/* @__PURE__ */ (0,
|
|
2141
|
-
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)(
|
|
2142
2217
|
"button",
|
|
2143
2218
|
{
|
|
2144
2219
|
type: "button",
|
|
2145
2220
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2146
2221
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2147
2222
|
children: [
|
|
2148
|
-
/* @__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" }) }),
|
|
2149
2224
|
isPlaying ? "Pause" : "Play"
|
|
2150
2225
|
]
|
|
2151
2226
|
}
|
|
2152
2227
|
),
|
|
2153
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2228
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2154
2229
|
] }) }),
|
|
2155
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2156
|
-
/* @__PURE__ */ (0,
|
|
2157
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2158
|
-
/* @__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 })
|
|
2159
2234
|
] });
|
|
2160
2235
|
}
|
|
2161
2236
|
|
|
2162
2237
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2163
|
-
var
|
|
2238
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2164
2239
|
function useSongPanel({
|
|
2165
2240
|
uuid,
|
|
2166
2241
|
scope,
|
|
@@ -2173,7 +2248,7 @@ function useSongPanel({
|
|
|
2173
2248
|
onExpandedChange,
|
|
2174
2249
|
onClose
|
|
2175
2250
|
}) {
|
|
2176
|
-
const result = (0,
|
|
2251
|
+
const result = (0, import_genre_tree20.useSong)(uuid, getBackendBaseUrl);
|
|
2177
2252
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2178
2253
|
switch (result.status) {
|
|
2179
2254
|
case "idle":
|
|
@@ -2182,24 +2257,24 @@ function useSongPanel({
|
|
|
2182
2257
|
return {
|
|
2183
2258
|
...panel,
|
|
2184
2259
|
title: "Song",
|
|
2185
|
-
content: /* @__PURE__ */ (0,
|
|
2186
|
-
/* @__PURE__ */ (0,
|
|
2187
|
-
/* @__PURE__ */ (0,
|
|
2188
|
-
/* @__PURE__ */ (0,
|
|
2189
|
-
/* @__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" })
|
|
2190
2265
|
] })
|
|
2191
2266
|
};
|
|
2192
2267
|
case "not-found":
|
|
2193
2268
|
return {
|
|
2194
2269
|
...panel,
|
|
2195
2270
|
title: "Song not found",
|
|
2196
|
-
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." })
|
|
2197
2272
|
};
|
|
2198
2273
|
case "error":
|
|
2199
2274
|
return {
|
|
2200
2275
|
...panel,
|
|
2201
2276
|
title: "Song",
|
|
2202
|
-
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." })
|
|
2203
2278
|
};
|
|
2204
2279
|
case "success": {
|
|
2205
2280
|
const { song } = result;
|
|
@@ -2207,7 +2282,7 @@ function useSongPanel({
|
|
|
2207
2282
|
return {
|
|
2208
2283
|
...panel,
|
|
2209
2284
|
title: song.title,
|
|
2210
|
-
content: /* @__PURE__ */ (0,
|
|
2285
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2211
2286
|
SongPanelContent,
|
|
2212
2287
|
{
|
|
2213
2288
|
song,
|
|
@@ -2226,29 +2301,29 @@ function useSongPanel({
|
|
|
2226
2301
|
|
|
2227
2302
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2228
2303
|
var import_md = require("react-icons/md");
|
|
2229
|
-
var
|
|
2230
|
-
var
|
|
2304
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
2305
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2231
2306
|
function SizeMetricToggle({
|
|
2232
2307
|
value,
|
|
2233
2308
|
onChange
|
|
2234
2309
|
}) {
|
|
2235
|
-
return /* @__PURE__ */ (0,
|
|
2236
|
-
|
|
2310
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2311
|
+
import_ui15.SegmentedControl,
|
|
2237
2312
|
{
|
|
2238
2313
|
variant: "floating",
|
|
2239
2314
|
"aria-label": "Size nodes by",
|
|
2240
2315
|
value,
|
|
2241
2316
|
onChange,
|
|
2242
2317
|
options: [
|
|
2243
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2244
|
-
{ 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" }
|
|
2245
2320
|
]
|
|
2246
2321
|
}
|
|
2247
2322
|
);
|
|
2248
2323
|
}
|
|
2249
2324
|
|
|
2250
2325
|
// src/genre-tree/GenreTreeView.tsx
|
|
2251
|
-
var
|
|
2326
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2252
2327
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2253
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)]";
|
|
2254
2329
|
var noop = () => {
|
|
@@ -2282,6 +2357,7 @@ function GenreTreeView({
|
|
|
2282
2357
|
artistExpanded: controlledArtistExpanded,
|
|
2283
2358
|
onArtistExpandedChange,
|
|
2284
2359
|
getShareUrl,
|
|
2360
|
+
extraSearchGroups = [],
|
|
2285
2361
|
artistSearch = true,
|
|
2286
2362
|
onArtistSelect,
|
|
2287
2363
|
artistSearchBaseUrl,
|
|
@@ -2347,7 +2423,7 @@ function GenreTreeView({
|
|
|
2347
2423
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2348
2424
|
toggleWheelRotation,
|
|
2349
2425
|
onNodeHover: handleNodeHover
|
|
2350
|
-
} = (0,
|
|
2426
|
+
} = (0, import_genre_tree21.useGenreTreeViewState)({
|
|
2351
2427
|
scope,
|
|
2352
2428
|
getBackendBaseUrl,
|
|
2353
2429
|
viewMode: controlledViewMode,
|
|
@@ -2366,12 +2442,12 @@ function GenreTreeView({
|
|
|
2366
2442
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2367
2443
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2368
2444
|
}
|
|
2369
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2370
|
-
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);
|
|
2371
2447
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2372
2448
|
const isArtistFiltered = artistUuid !== null;
|
|
2373
2449
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2374
|
-
const artistResult = (0,
|
|
2450
|
+
const artistResult = (0, import_genre_tree21.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2375
2451
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2376
2452
|
const filterArtist = (0, import_react16.useMemo)(
|
|
2377
2453
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -2391,7 +2467,7 @@ function GenreTreeView({
|
|
|
2391
2467
|
const openSongRef = (0, import_react16.useRef)(openSong);
|
|
2392
2468
|
openSongRef.current = openSong;
|
|
2393
2469
|
const resolvedRenderSongLink = (0, import_react16.useCallback)(
|
|
2394
|
-
(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 }),
|
|
2395
2471
|
[renderSongLink]
|
|
2396
2472
|
);
|
|
2397
2473
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react16.useState)(null);
|
|
@@ -2403,7 +2479,7 @@ function GenreTreeView({
|
|
|
2403
2479
|
scope,
|
|
2404
2480
|
getBackendBaseUrl: artistBaseUrl,
|
|
2405
2481
|
getShareUrl: getShareUrlRef.current,
|
|
2406
|
-
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 })),
|
|
2407
2483
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2408
2484
|
onExpandedChange: (on) => {
|
|
2409
2485
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2412,7 +2488,7 @@ function GenreTreeView({
|
|
|
2412
2488
|
renderSongLink: resolvedRenderSongLink,
|
|
2413
2489
|
onClose: () => changeArtist(null)
|
|
2414
2490
|
});
|
|
2415
|
-
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 }));
|
|
2416
2492
|
const getGenrePath = (genre) => {
|
|
2417
2493
|
const path = [];
|
|
2418
2494
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2468,7 +2544,7 @@ function GenreTreeView({
|
|
|
2468
2544
|
[]
|
|
2469
2545
|
);
|
|
2470
2546
|
const panelRef = (0, import_react16.useRef)(null);
|
|
2471
|
-
const obscuredInset = (0,
|
|
2547
|
+
const obscuredInset = (0, import_ui16.useObscuredInset)(panelRef);
|
|
2472
2548
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2473
2549
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2474
2550
|
const treeNodes = (0, import_react16.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
@@ -2491,52 +2567,27 @@ function GenreTreeView({
|
|
|
2491
2567
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2492
2568
|
const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
|
|
2493
2569
|
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
onArtistSelect?.(artist);
|
|
2516
|
-
}
|
|
2517
|
-
})),
|
|
2518
|
-
status: artistSearchResult.isError ? "Search failed" : artistSearchResult.settled && artistSearchResult.artists.length === 0 ? "No artists found" : "",
|
|
2519
|
-
loading: artistSearchResult.pending
|
|
2520
|
-
} : void 0;
|
|
2521
|
-
const songSearchResult = (0, import_genre_tree18.useSongSearch)(songBaseUrl, songSearch ? searchText : "");
|
|
2522
|
-
const songGroup = songSearch ? {
|
|
2523
|
-
key: "songs",
|
|
2524
|
-
label: "Songs",
|
|
2525
|
-
items: songSearchResult.songs.map((song) => ({
|
|
2526
|
-
key: song.uuid,
|
|
2527
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: (0, import_genre_tree18.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
2528
|
-
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
2529
|
-
/* @__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 ") })
|
|
2530
|
-
] }),
|
|
2531
|
-
onSelect: () => {
|
|
2532
|
-
openSong(song.uuid);
|
|
2533
|
-
onSongSelect?.(song);
|
|
2534
|
-
}
|
|
2535
|
-
})),
|
|
2536
|
-
status: songSearchResult.isError ? "Search failed" : songSearchResult.settled && songSearchResult.songs.length === 0 ? "No songs found" : "",
|
|
2537
|
-
loading: songSearchResult.pending
|
|
2538
|
-
} : void 0;
|
|
2539
|
-
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
|
+
);
|
|
2540
2591
|
const handleSelectedNodeChange = (node) => {
|
|
2541
2592
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2542
2593
|
selectNode(node);
|
|
@@ -2560,7 +2611,7 @@ function GenreTreeView({
|
|
|
2560
2611
|
};
|
|
2561
2612
|
const renderGenreBody = (relation) => {
|
|
2562
2613
|
const { node } = relation;
|
|
2563
|
-
const family = /* @__PURE__ */ (0,
|
|
2614
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2564
2615
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2565
2616
|
{
|
|
2566
2617
|
node,
|
|
@@ -2571,10 +2622,10 @@ function GenreTreeView({
|
|
|
2571
2622
|
onSelectNode: selectGenreFromPanel
|
|
2572
2623
|
}
|
|
2573
2624
|
);
|
|
2574
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2625
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2575
2626
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2576
2627
|
const criteria = genrePlaylist?.criteria;
|
|
2577
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0,
|
|
2628
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2578
2629
|
family,
|
|
2579
2630
|
ancestors
|
|
2580
2631
|
] }) };
|
|
@@ -2594,11 +2645,11 @@ function GenreTreeView({
|
|
|
2594
2645
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2595
2646
|
) : void 0;
|
|
2596
2647
|
return {
|
|
2597
|
-
primary: /* @__PURE__ */ (0,
|
|
2598
|
-
/* @__PURE__ */ (0,
|
|
2648
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2649
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2599
2650
|
GenreInfoPanelBody,
|
|
2600
2651
|
{
|
|
2601
|
-
family: /* @__PURE__ */ (0,
|
|
2652
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2602
2653
|
family,
|
|
2603
2654
|
ancestors
|
|
2604
2655
|
] }),
|
|
@@ -2616,7 +2667,7 @@ function GenreTreeView({
|
|
|
2616
2667
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2617
2668
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2618
2669
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2619
|
-
/* @__PURE__ */ (0,
|
|
2670
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2620
2671
|
ChangeParentPopup,
|
|
2621
2672
|
{
|
|
2622
2673
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2691,19 +2742,19 @@ function GenreTreeView({
|
|
|
2691
2742
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2692
2743
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2693
2744
|
} : void 0;
|
|
2694
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
2695
|
-
|
|
2745
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2746
|
+
import_ui16.IconButton,
|
|
2696
2747
|
{
|
|
2697
2748
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2698
2749
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2699
2750
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2700
|
-
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" })
|
|
2701
2752
|
}
|
|
2702
2753
|
) : null;
|
|
2703
2754
|
const renderArtistFilter = () => {
|
|
2704
2755
|
if (isLoading || !filterArtist) return null;
|
|
2705
|
-
return /* @__PURE__ */ (0,
|
|
2706
|
-
/* @__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)(
|
|
2707
2758
|
"button",
|
|
2708
2759
|
{
|
|
2709
2760
|
type: "button",
|
|
@@ -2715,7 +2766,7 @@ function GenreTreeView({
|
|
|
2715
2766
|
children: filterArtist.name
|
|
2716
2767
|
}
|
|
2717
2768
|
),
|
|
2718
|
-
/* @__PURE__ */ (0,
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2719
2770
|
"button",
|
|
2720
2771
|
{
|
|
2721
2772
|
type: "button",
|
|
@@ -2727,8 +2778,8 @@ function GenreTreeView({
|
|
|
2727
2778
|
)
|
|
2728
2779
|
] });
|
|
2729
2780
|
};
|
|
2730
|
-
const actions = /* @__PURE__ */ (0,
|
|
2731
|
-
!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)(
|
|
2732
2783
|
GenreSearch,
|
|
2733
2784
|
{
|
|
2734
2785
|
genrePlaylists: visibleGenrePlaylists,
|
|
@@ -2736,14 +2787,14 @@ function GenreTreeView({
|
|
|
2736
2787
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2737
2788
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2738
2789
|
onPrevious: handlePrevious,
|
|
2739
|
-
onQueryChange: artistSearch || songSearch ? setSearchText : void 0,
|
|
2790
|
+
onQueryChange: artistSearch || songSearch || extraSearchGroups.length > 0 ? setSearchText : void 0,
|
|
2740
2791
|
extraGroups
|
|
2741
2792
|
}
|
|
2742
2793
|
),
|
|
2743
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2744
|
-
/* @__PURE__ */ (0,
|
|
2745
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
2746
|
-
|
|
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,
|
|
2747
2798
|
{
|
|
2748
2799
|
"aria-label": "Tree view mode",
|
|
2749
2800
|
value: viewMode,
|
|
@@ -2756,14 +2807,14 @@ function GenreTreeView({
|
|
|
2756
2807
|
]
|
|
2757
2808
|
}
|
|
2758
2809
|
),
|
|
2759
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
2810
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2760
2811
|
"div",
|
|
2761
2812
|
{
|
|
2762
2813
|
className: "flex items-center gap-1",
|
|
2763
2814
|
role: "group",
|
|
2764
2815
|
"aria-label": "Tree display options",
|
|
2765
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
2766
|
-
|
|
2816
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2817
|
+
import_ui16.Button,
|
|
2767
2818
|
{
|
|
2768
2819
|
variant: allowWheelRotation ? "default" : "outline",
|
|
2769
2820
|
size: "sm",
|
|
@@ -2773,8 +2824,8 @@ function GenreTreeView({
|
|
|
2773
2824
|
)
|
|
2774
2825
|
}
|
|
2775
2826
|
),
|
|
2776
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
2777
|
-
|
|
2827
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2828
|
+
import_ui16.IconTextButton,
|
|
2778
2829
|
{
|
|
2779
2830
|
icon: import_lucide_react11.Plus,
|
|
2780
2831
|
text: "Add root",
|
|
@@ -2783,34 +2834,34 @@ function GenreTreeView({
|
|
|
2783
2834
|
)
|
|
2784
2835
|
] })
|
|
2785
2836
|
] });
|
|
2786
|
-
return /* @__PURE__ */ (0,
|
|
2837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2787
2838
|
"div",
|
|
2788
2839
|
{
|
|
2789
2840
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2790
2841
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2791
2842
|
children: [
|
|
2792
|
-
/* @__PURE__ */ (0,
|
|
2843
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2793
2844
|
"div",
|
|
2794
2845
|
{
|
|
2795
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"}`,
|
|
2796
2847
|
children: actions
|
|
2797
2848
|
}
|
|
2798
2849
|
),
|
|
2799
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2850
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2800
2851
|
"div",
|
|
2801
2852
|
{
|
|
2802
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"}`,
|
|
2803
2854
|
children: renderArtistFilter()
|
|
2804
2855
|
}
|
|
2805
2856
|
),
|
|
2806
|
-
/* @__PURE__ */ (0,
|
|
2807
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
2808
|
-
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" ? (
|
|
2809
2860
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2810
2861
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2811
2862
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2812
2863
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2813
|
-
/* @__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)(
|
|
2814
2865
|
GenrePlaylistTreeWheel,
|
|
2815
2866
|
{
|
|
2816
2867
|
scope,
|
|
@@ -2827,9 +2878,9 @@ function GenreTreeView({
|
|
|
2827
2878
|
allowWheelRotation,
|
|
2828
2879
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2829
2880
|
busy,
|
|
2830
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2881
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2831
2882
|
floatingControlsLeading,
|
|
2832
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2883
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2833
2884
|
] }) : void 0,
|
|
2834
2885
|
defaultViewport,
|
|
2835
2886
|
viewportRestore,
|
|
@@ -2838,7 +2889,7 @@ function GenreTreeView({
|
|
|
2838
2889
|
) }) })
|
|
2839
2890
|
) : viewMode === "pop-core" ? (
|
|
2840
2891
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2841
|
-
/* @__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)(
|
|
2842
2893
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2843
2894
|
{
|
|
2844
2895
|
scope,
|
|
@@ -2855,9 +2906,9 @@ function GenreTreeView({
|
|
|
2855
2906
|
allowWheelRotation,
|
|
2856
2907
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2857
2908
|
busy,
|
|
2858
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2909
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2859
2910
|
floatingControlsLeading,
|
|
2860
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2911
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2861
2912
|
] }) : void 0,
|
|
2862
2913
|
defaultViewport,
|
|
2863
2914
|
viewportRestore,
|
|
@@ -2865,7 +2916,7 @@ function GenreTreeView({
|
|
|
2865
2916
|
...focus
|
|
2866
2917
|
}
|
|
2867
2918
|
) }) })
|
|
2868
|
-
) : 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)(
|
|
2869
2920
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2870
2921
|
{
|
|
2871
2922
|
outline: true,
|
|
@@ -2881,13 +2932,13 @@ function GenreTreeView({
|
|
|
2881
2932
|
selectedNodeId,
|
|
2882
2933
|
readOnly
|
|
2883
2934
|
}
|
|
2884
|
-
) }) : /* @__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(
|
|
2885
2936
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2886
|
-
return /* @__PURE__ */ (0,
|
|
2937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2887
2938
|
"div",
|
|
2888
2939
|
{
|
|
2889
2940
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2890
|
-
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)(
|
|
2891
2942
|
GenrePlaylistTreePerRoot,
|
|
2892
2943
|
{
|
|
2893
2944
|
scope,
|
|
@@ -2913,8 +2964,8 @@ function GenreTreeView({
|
|
|
2913
2964
|
);
|
|
2914
2965
|
}
|
|
2915
2966
|
) }),
|
|
2916
|
-
/* @__PURE__ */ (0,
|
|
2917
|
-
|
|
2967
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2968
|
+
import_ui16.DetailPanel,
|
|
2918
2969
|
{
|
|
2919
2970
|
ref: panelRef,
|
|
2920
2971
|
side: "left",
|
|
@@ -2930,7 +2981,7 @@ function GenreTreeView({
|
|
|
2930
2981
|
aside: activePanel?.aside,
|
|
2931
2982
|
edgeTab: detailTab,
|
|
2932
2983
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
2933
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
2984
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2934
2985
|
activePanel.content,
|
|
2935
2986
|
activePanel.aside
|
|
2936
2987
|
] }) : activePanel?.content
|
|
@@ -2945,10 +2996,10 @@ function GenreTreeView({
|
|
|
2945
2996
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
2946
2997
|
var import_react17 = require("react");
|
|
2947
2998
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
2948
|
-
var
|
|
2999
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2949
3000
|
var import_lucide_react13 = require("lucide-react");
|
|
2950
|
-
var
|
|
2951
|
-
var
|
|
3001
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3002
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2952
3003
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2953
3004
|
function toNode(genre, parentId) {
|
|
2954
3005
|
return {
|
|
@@ -2975,7 +3026,7 @@ function GenreDetailPanel({
|
|
|
2975
3026
|
edgeTab,
|
|
2976
3027
|
extras
|
|
2977
3028
|
}) {
|
|
2978
|
-
const { data: detail, isPending, isError } = (0,
|
|
3029
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree22.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
2979
3030
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
2980
3031
|
const view = (0, import_react17.useMemo)(() => {
|
|
2981
3032
|
if (!detail) return null;
|
|
@@ -3003,17 +3054,17 @@ function GenreDetailPanel({
|
|
|
3003
3054
|
};
|
|
3004
3055
|
}, [detail]);
|
|
3005
3056
|
if (isError) {
|
|
3006
|
-
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." });
|
|
3007
3058
|
}
|
|
3008
3059
|
if (isPending || !detail || !view) {
|
|
3009
|
-
return /* @__PURE__ */ (0,
|
|
3010
|
-
/* @__PURE__ */ (0,
|
|
3011
|
-
/* @__PURE__ */ (0,
|
|
3012
|
-
/* @__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" })
|
|
3013
3064
|
] });
|
|
3014
3065
|
}
|
|
3015
3066
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3016
|
-
const family = /* @__PURE__ */ (0,
|
|
3067
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3017
3068
|
import_genre_tree_view5.GenreNodeFamily,
|
|
3018
3069
|
{
|
|
3019
3070
|
node: view.node,
|
|
@@ -3024,20 +3075,20 @@ function GenreDetailPanel({
|
|
|
3024
3075
|
onSelectNode: selectGenre
|
|
3025
3076
|
}
|
|
3026
3077
|
);
|
|
3027
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
3028
|
-
return /* @__PURE__ */ (0,
|
|
3029
|
-
|
|
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,
|
|
3030
3081
|
{
|
|
3031
3082
|
side: "left",
|
|
3032
3083
|
title: view.node.name,
|
|
3033
3084
|
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3034
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
3035
|
-
|
|
3085
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3086
|
+
import_ui17.IconButton,
|
|
3036
3087
|
{
|
|
3037
3088
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3038
3089
|
style: { color: "inherit" },
|
|
3039
3090
|
onClick: () => onExpandedChange(!expanded),
|
|
3040
|
-
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" })
|
|
3041
3092
|
}
|
|
3042
3093
|
) : void 0,
|
|
3043
3094
|
onClose,
|
|
@@ -3046,10 +3097,10 @@ function GenreDetailPanel({
|
|
|
3046
3097
|
onExpandedChange,
|
|
3047
3098
|
edgeTab,
|
|
3048
3099
|
children: [
|
|
3049
|
-
/* @__PURE__ */ (0,
|
|
3100
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3050
3101
|
GenreInfoPanelBody,
|
|
3051
3102
|
{
|
|
3052
|
-
family: /* @__PURE__ */ (0,
|
|
3103
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
3053
3104
|
family,
|
|
3054
3105
|
ancestors
|
|
3055
3106
|
] }),
|
|
@@ -3075,10 +3126,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3075
3126
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3076
3127
|
var import_react18 = require("react");
|
|
3077
3128
|
var import_lucide_react14 = require("lucide-react");
|
|
3078
|
-
var
|
|
3079
|
-
var
|
|
3129
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3130
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3080
3131
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
3081
|
-
var
|
|
3132
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3082
3133
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
|
|
3083
3134
|
function TrackListSidebar({
|
|
3084
3135
|
className: className2 = "",
|
|
@@ -3091,7 +3142,7 @@ function TrackListSidebar({
|
|
|
3091
3142
|
renderArtistLink,
|
|
3092
3143
|
renderSongLink
|
|
3093
3144
|
}) {
|
|
3094
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3145
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree23.useTrackList)();
|
|
3095
3146
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
3096
3147
|
const trackListRef = (0, import_react18.useRef)(null);
|
|
3097
3148
|
const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
|
|
@@ -3180,25 +3231,25 @@ function TrackListSidebar({
|
|
|
3180
3231
|
}
|
|
3181
3232
|
};
|
|
3182
3233
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3183
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3234
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3184
3235
|
"div",
|
|
3185
3236
|
{
|
|
3186
3237
|
"data-theme": "dark",
|
|
3187
3238
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3188
3239
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3189
3240
|
children: [
|
|
3190
|
-
/* @__PURE__ */ (0,
|
|
3191
|
-
/* @__PURE__ */ (0,
|
|
3192
|
-
/* @__PURE__ */ (0,
|
|
3193
|
-
/* @__PURE__ */ (0,
|
|
3194
|
-
/* @__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 }) })
|
|
3195
3246
|
] }),
|
|
3196
|
-
/* @__PURE__ */ (0,
|
|
3197
|
-
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 `,
|
|
3198
3249
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3199
3250
|
] })
|
|
3200
3251
|
] }),
|
|
3201
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3252
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3202
3253
|
"button",
|
|
3203
3254
|
{
|
|
3204
3255
|
type: "button",
|
|
@@ -3207,11 +3258,11 @@ function TrackListSidebar({
|
|
|
3207
3258
|
"aria-expanded": !isCollapsed,
|
|
3208
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"}`,
|
|
3209
3260
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3210
|
-
children: /* @__PURE__ */ (0,
|
|
3261
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react14.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3211
3262
|
}
|
|
3212
3263
|
)
|
|
3213
3264
|
] }),
|
|
3214
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3265
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3215
3266
|
"ul",
|
|
3216
3267
|
{
|
|
3217
3268
|
ref: trackListRef,
|
|
@@ -3221,7 +3272,7 @@ function TrackListSidebar({
|
|
|
3221
3272
|
onPointerDown: markUserScrollIntent,
|
|
3222
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",
|
|
3223
3274
|
children: [
|
|
3224
|
-
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)(
|
|
3225
3276
|
TrackItem,
|
|
3226
3277
|
{
|
|
3227
3278
|
track,
|
|
@@ -3232,7 +3283,7 @@ function TrackListSidebar({
|
|
|
3232
3283
|
renderSongLink
|
|
3233
3284
|
}
|
|
3234
3285
|
) }, track.uuid)),
|
|
3235
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3286
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3236
3287
|
]
|
|
3237
3288
|
}
|
|
3238
3289
|
)
|
|
@@ -3245,14 +3296,14 @@ function TrackListSidebar({
|
|
|
3245
3296
|
var import_react19 = require("react");
|
|
3246
3297
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3247
3298
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3248
|
-
var
|
|
3249
|
-
var
|
|
3299
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3300
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
3250
3301
|
|
|
3251
3302
|
// src/genre-tree/PlayerControls.tsx
|
|
3252
3303
|
var import_lucide_react15 = require("lucide-react");
|
|
3253
|
-
var
|
|
3304
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3254
3305
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3255
|
-
var
|
|
3306
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
3256
3307
|
function PlayerControls() {
|
|
3257
3308
|
const {
|
|
3258
3309
|
duration,
|
|
@@ -3264,22 +3315,22 @@ function PlayerControls() {
|
|
|
3264
3315
|
setVolume,
|
|
3265
3316
|
volume
|
|
3266
3317
|
} = (0, import_player12.usePlayer)();
|
|
3267
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3318
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree25.useTrackList)();
|
|
3268
3319
|
const currentTime = (0, import_player12.useCurrentTime)();
|
|
3269
3320
|
const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
|
|
3270
3321
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3271
3322
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3272
3323
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3273
3324
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3274
|
-
return /* @__PURE__ */ (0,
|
|
3325
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
3275
3326
|
"section",
|
|
3276
3327
|
{
|
|
3277
3328
|
"aria-label": "Player controls",
|
|
3278
3329
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3279
3330
|
children: [
|
|
3280
|
-
/* @__PURE__ */ (0,
|
|
3281
|
-
/* @__PURE__ */ (0,
|
|
3282
|
-
/* @__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)(
|
|
3283
3334
|
"input",
|
|
3284
3335
|
{
|
|
3285
3336
|
"aria-label": "Seek",
|
|
@@ -3293,11 +3344,11 @@ function PlayerControls() {
|
|
|
3293
3344
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3294
3345
|
}
|
|
3295
3346
|
),
|
|
3296
|
-
/* @__PURE__ */ (0,
|
|
3347
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree25.formatTime)(safeDuration) })
|
|
3297
3348
|
] }),
|
|
3298
|
-
/* @__PURE__ */ (0,
|
|
3299
|
-
/* @__PURE__ */ (0,
|
|
3300
|
-
/* @__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)(
|
|
3301
3352
|
"button",
|
|
3302
3353
|
{
|
|
3303
3354
|
type: "button",
|
|
@@ -3306,10 +3357,10 @@ function PlayerControls() {
|
|
|
3306
3357
|
disabled: !isReady || !hasPreviousTrack,
|
|
3307
3358
|
onClick: playPreviousTrack,
|
|
3308
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",
|
|
3309
|
-
children: /* @__PURE__ */ (0,
|
|
3360
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3310
3361
|
}
|
|
3311
3362
|
),
|
|
3312
|
-
/* @__PURE__ */ (0,
|
|
3363
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3313
3364
|
"button",
|
|
3314
3365
|
{
|
|
3315
3366
|
type: "button",
|
|
@@ -3318,10 +3369,10 @@ function PlayerControls() {
|
|
|
3318
3369
|
disabled: !isReady,
|
|
3319
3370
|
onClick: handlePlayPauseAction,
|
|
3320
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",
|
|
3321
|
-
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" })
|
|
3322
3373
|
}
|
|
3323
3374
|
),
|
|
3324
|
-
/* @__PURE__ */ (0,
|
|
3375
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3325
3376
|
"button",
|
|
3326
3377
|
{
|
|
3327
3378
|
type: "button",
|
|
@@ -3330,14 +3381,14 @@ function PlayerControls() {
|
|
|
3330
3381
|
disabled: !isReady || !hasNextTrack,
|
|
3331
3382
|
onClick: playNextTrack,
|
|
3332
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",
|
|
3333
|
-
children: /* @__PURE__ */ (0,
|
|
3384
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3334
3385
|
}
|
|
3335
3386
|
)
|
|
3336
3387
|
] }),
|
|
3337
|
-
/* @__PURE__ */ (0,
|
|
3338
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3339
|
-
/* @__PURE__ */ (0,
|
|
3340
|
-
/* @__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)(
|
|
3341
3392
|
"input",
|
|
3342
3393
|
{
|
|
3343
3394
|
"aria-label": "Volume",
|
|
@@ -3359,7 +3410,7 @@ function PlayerControls() {
|
|
|
3359
3410
|
}
|
|
3360
3411
|
|
|
3361
3412
|
// src/genre-tree/PlayerPanel.tsx
|
|
3362
|
-
var
|
|
3413
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
3363
3414
|
var PANEL_ID = "player-panel";
|
|
3364
3415
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3365
3416
|
function PlayerPanel({
|
|
@@ -3372,7 +3423,7 @@ function PlayerPanel({
|
|
|
3372
3423
|
renderSongLink
|
|
3373
3424
|
}) {
|
|
3374
3425
|
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3375
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3426
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree26.useTrackListSidebarVisibility)();
|
|
3376
3427
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3377
3428
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3378
3429
|
(0, import_react19.useEffect)(() => {
|
|
@@ -3399,7 +3450,7 @@ function PlayerPanel({
|
|
|
3399
3450
|
onClick: () => setIsExtendRequested(true),
|
|
3400
3451
|
expanded: false
|
|
3401
3452
|
} : null;
|
|
3402
|
-
return /* @__PURE__ */ (0,
|
|
3453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3403
3454
|
"div",
|
|
3404
3455
|
{
|
|
3405
3456
|
id: PANEL_ID,
|
|
@@ -3412,8 +3463,8 @@ function PlayerPanel({
|
|
|
3412
3463
|
),
|
|
3413
3464
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3414
3465
|
children: [
|
|
3415
|
-
/* @__PURE__ */ (0,
|
|
3416
|
-
|
|
3466
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3467
|
+
import_ui18.PanelEdgeTab,
|
|
3417
3468
|
{
|
|
3418
3469
|
side: "right",
|
|
3419
3470
|
edge: tabEdge,
|
|
@@ -3422,7 +3473,7 @@ function PlayerPanel({
|
|
|
3422
3473
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3423
3474
|
}
|
|
3424
3475
|
),
|
|
3425
|
-
/* @__PURE__ */ (0,
|
|
3476
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3426
3477
|
"div",
|
|
3427
3478
|
{
|
|
3428
3479
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3430,20 +3481,20 @@ function PlayerPanel({
|
|
|
3430
3481
|
isExtended ? "flex-row" : "flex-col"
|
|
3431
3482
|
),
|
|
3432
3483
|
children: [
|
|
3433
|
-
/* @__PURE__ */ (0,
|
|
3434
|
-
/* @__PURE__ */ (0,
|
|
3435
|
-
/* @__PURE__ */ (0,
|
|
3436
|
-
/* @__PURE__ */ (0,
|
|
3437
|
-
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: [
|
|
3438
3489
|
playerTrackObject.loadError,
|
|
3439
|
-
/* @__PURE__ */ (0,
|
|
3490
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3440
3491
|
void retryPlayback();
|
|
3441
3492
|
}, children: "Retry" })
|
|
3442
3493
|
] })
|
|
3443
3494
|
] }),
|
|
3444
|
-
/* @__PURE__ */ (0,
|
|
3495
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(PlayerControls, {})
|
|
3445
3496
|
] }),
|
|
3446
|
-
/* @__PURE__ */ (0,
|
|
3497
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3447
3498
|
TrackListSidebar,
|
|
3448
3499
|
{
|
|
3449
3500
|
layout: "inline",
|
|
@@ -3469,33 +3520,33 @@ function PlayerPanel({
|
|
|
3469
3520
|
// src/genre-tree/Rating.tsx
|
|
3470
3521
|
var import_uuid = require("uuid");
|
|
3471
3522
|
var import_ai = require("react-icons/ai");
|
|
3472
|
-
var
|
|
3473
|
-
var
|
|
3523
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3524
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
3474
3525
|
function Rating({ rating, handleChange }) {
|
|
3475
3526
|
const instanceId = (0, import_uuid.v4)();
|
|
3476
|
-
return /* @__PURE__ */ (0,
|
|
3477
|
-
/* @__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)(
|
|
3478
3529
|
"input",
|
|
3479
3530
|
{
|
|
3480
3531
|
id: `star-${instanceId}-${i}`,
|
|
3481
3532
|
className: "w-6 h-6 opacity-0",
|
|
3482
3533
|
type: "radio",
|
|
3483
3534
|
name: "rating",
|
|
3484
|
-
value: i === 0 ?
|
|
3535
|
+
value: i === 0 ? import_genre_tree27.FORM_RATING_NULL_VALUE : i,
|
|
3485
3536
|
checked: rating === (i === 0 ? null : i),
|
|
3486
3537
|
onChange: handleChange
|
|
3487
3538
|
}
|
|
3488
3539
|
),
|
|
3489
|
-
/* @__PURE__ */ (0,
|
|
3490
|
-
/* @__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)(
|
|
3491
3542
|
import_ai.AiOutlineStar,
|
|
3492
3543
|
{
|
|
3493
|
-
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)]"}`
|
|
3494
3545
|
}
|
|
3495
3546
|
),
|
|
3496
|
-
/* @__PURE__ */ (0,
|
|
3547
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-grow w-8" }),
|
|
3497
3548
|
" "
|
|
3498
|
-
] }) : /* @__PURE__ */ (0,
|
|
3549
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3499
3550
|
import_ai.AiFillStar,
|
|
3500
3551
|
{
|
|
3501
3552
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3517,9 +3568,15 @@ function Rating({ rating, handleChange }) {
|
|
|
3517
3568
|
GenreTreeWheelSkeleton,
|
|
3518
3569
|
PlayerPanel,
|
|
3519
3570
|
Rating,
|
|
3571
|
+
SEARCH_FAILED_STATUS,
|
|
3572
|
+
SearchBox,
|
|
3520
3573
|
TrackItem,
|
|
3521
3574
|
TrackListSidebar,
|
|
3522
3575
|
TrackPositionPlayPause,
|
|
3523
|
-
|
|
3576
|
+
searchStatus,
|
|
3577
|
+
useArtistSearchGroup,
|
|
3578
|
+
useGenrePlaylistPlayback,
|
|
3579
|
+
useGenreRemoteSearchGroup,
|
|
3580
|
+
useSongSearchGroup
|
|
3524
3581
|
});
|
|
3525
3582
|
//# sourceMappingURL=index.cjs.map
|