@behindthemusictree/app-kit-ui 33.1.0 → 33.3.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-QMELESFB.js} +518 -460
- package/dist/chunk-QMELESFB.js.map +1 -0
- package/dist/genre-tree/index.cjs +598 -535
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +74 -20
- package/dist/genre-tree/index.js +15 -3
- package/dist/index.cjs +596 -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,9 @@ function GenreTreeView({
|
|
|
2282
2357
|
artistExpanded: controlledArtistExpanded,
|
|
2283
2358
|
onArtistExpandedChange,
|
|
2284
2359
|
getShareUrl,
|
|
2360
|
+
extraSearchGroups = [],
|
|
2361
|
+
onSearchQueryChange,
|
|
2362
|
+
onSearchFocus,
|
|
2285
2363
|
artistSearch = true,
|
|
2286
2364
|
onArtistSelect,
|
|
2287
2365
|
artistSearchBaseUrl,
|
|
@@ -2347,7 +2425,7 @@ function GenreTreeView({
|
|
|
2347
2425
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2348
2426
|
toggleWheelRotation,
|
|
2349
2427
|
onNodeHover: handleNodeHover
|
|
2350
|
-
} = (0,
|
|
2428
|
+
} = (0, import_genre_tree21.useGenreTreeViewState)({
|
|
2351
2429
|
scope,
|
|
2352
2430
|
getBackendBaseUrl,
|
|
2353
2431
|
viewMode: controlledViewMode,
|
|
@@ -2366,12 +2444,12 @@ function GenreTreeView({
|
|
|
2366
2444
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2367
2445
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2368
2446
|
}
|
|
2369
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2370
|
-
const { mutate: updateGenre } = (0,
|
|
2447
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree21.genreUpdateMutationKey }) > 0;
|
|
2448
|
+
const { mutate: updateGenre } = (0, import_genre_tree21.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2371
2449
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2372
2450
|
const isArtistFiltered = artistUuid !== null;
|
|
2373
2451
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2374
|
-
const artistResult = (0,
|
|
2452
|
+
const artistResult = (0, import_genre_tree21.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2375
2453
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2376
2454
|
const filterArtist = (0, import_react16.useMemo)(
|
|
2377
2455
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -2391,7 +2469,7 @@ function GenreTreeView({
|
|
|
2391
2469
|
const openSongRef = (0, import_react16.useRef)(openSong);
|
|
2392
2470
|
openSongRef.current = openSong;
|
|
2393
2471
|
const resolvedRenderSongLink = (0, import_react16.useCallback)(
|
|
2394
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2472
|
+
(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
2473
|
[renderSongLink]
|
|
2396
2474
|
);
|
|
2397
2475
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react16.useState)(null);
|
|
@@ -2403,7 +2481,7 @@ function GenreTreeView({
|
|
|
2403
2481
|
scope,
|
|
2404
2482
|
getBackendBaseUrl: artistBaseUrl,
|
|
2405
2483
|
getShareUrl: getShareUrlRef.current,
|
|
2406
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2484
|
+
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
2485
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2408
2486
|
onExpandedChange: (on) => {
|
|
2409
2487
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2412,7 +2490,7 @@ function GenreTreeView({
|
|
|
2412
2490
|
renderSongLink: resolvedRenderSongLink,
|
|
2413
2491
|
onClose: () => changeArtist(null)
|
|
2414
2492
|
});
|
|
2415
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2493
|
+
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
2494
|
const getGenrePath = (genre) => {
|
|
2417
2495
|
const path = [];
|
|
2418
2496
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2468,7 +2546,7 @@ function GenreTreeView({
|
|
|
2468
2546
|
[]
|
|
2469
2547
|
);
|
|
2470
2548
|
const panelRef = (0, import_react16.useRef)(null);
|
|
2471
|
-
const obscuredInset = (0,
|
|
2549
|
+
const obscuredInset = (0, import_ui16.useObscuredInset)(panelRef);
|
|
2472
2550
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2473
2551
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2474
2552
|
const treeNodes = (0, import_react16.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
@@ -2491,52 +2569,27 @@ function GenreTreeView({
|
|
|
2491
2569
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2492
2570
|
const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
|
|
2493
2571
|
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);
|
|
2572
|
+
const artistGroup = useArtistSearchGroup(
|
|
2573
|
+
artistBaseUrl,
|
|
2574
|
+
searchText,
|
|
2575
|
+
(artist) => {
|
|
2576
|
+
openArtist(artist.uuid);
|
|
2577
|
+
onArtistSelect?.(artist);
|
|
2578
|
+
},
|
|
2579
|
+
Boolean(artistSearch)
|
|
2580
|
+
);
|
|
2581
|
+
const songGroup = useSongSearchGroup(
|
|
2582
|
+
songBaseUrl,
|
|
2583
|
+
searchText,
|
|
2584
|
+
(song) => {
|
|
2585
|
+
openSong(song.uuid);
|
|
2586
|
+
onSongSelect?.(song);
|
|
2587
|
+
},
|
|
2588
|
+
Boolean(songSearch)
|
|
2589
|
+
);
|
|
2590
|
+
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2591
|
+
(group) => group !== void 0
|
|
2592
|
+
);
|
|
2540
2593
|
const handleSelectedNodeChange = (node) => {
|
|
2541
2594
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2542
2595
|
selectNode(node);
|
|
@@ -2560,7 +2613,7 @@ function GenreTreeView({
|
|
|
2560
2613
|
};
|
|
2561
2614
|
const renderGenreBody = (relation) => {
|
|
2562
2615
|
const { node } = relation;
|
|
2563
|
-
const family = /* @__PURE__ */ (0,
|
|
2616
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2564
2617
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2565
2618
|
{
|
|
2566
2619
|
node,
|
|
@@ -2571,10 +2624,10 @@ function GenreTreeView({
|
|
|
2571
2624
|
onSelectNode: selectGenreFromPanel
|
|
2572
2625
|
}
|
|
2573
2626
|
);
|
|
2574
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2627
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2575
2628
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2576
2629
|
const criteria = genrePlaylist?.criteria;
|
|
2577
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0,
|
|
2630
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2578
2631
|
family,
|
|
2579
2632
|
ancestors
|
|
2580
2633
|
] }) };
|
|
@@ -2594,11 +2647,11 @@ function GenreTreeView({
|
|
|
2594
2647
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2595
2648
|
) : void 0;
|
|
2596
2649
|
return {
|
|
2597
|
-
primary: /* @__PURE__ */ (0,
|
|
2598
|
-
/* @__PURE__ */ (0,
|
|
2650
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2651
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2599
2652
|
GenreInfoPanelBody,
|
|
2600
2653
|
{
|
|
2601
|
-
family: /* @__PURE__ */ (0,
|
|
2654
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2602
2655
|
family,
|
|
2603
2656
|
ancestors
|
|
2604
2657
|
] }),
|
|
@@ -2616,7 +2669,7 @@ function GenreTreeView({
|
|
|
2616
2669
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2617
2670
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2618
2671
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2619
|
-
/* @__PURE__ */ (0,
|
|
2672
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2620
2673
|
ChangeParentPopup,
|
|
2621
2674
|
{
|
|
2622
2675
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2691,19 +2744,19 @@ function GenreTreeView({
|
|
|
2691
2744
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2692
2745
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2693
2746
|
} : void 0;
|
|
2694
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
2695
|
-
|
|
2747
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2748
|
+
import_ui16.IconButton,
|
|
2696
2749
|
{
|
|
2697
2750
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2698
2751
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2699
2752
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2700
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
2753
|
+
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
2754
|
}
|
|
2702
2755
|
) : null;
|
|
2703
2756
|
const renderArtistFilter = () => {
|
|
2704
2757
|
if (isLoading || !filterArtist) return null;
|
|
2705
|
-
return /* @__PURE__ */ (0,
|
|
2706
|
-
/* @__PURE__ */ (0,
|
|
2758
|
+
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: [
|
|
2759
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2707
2760
|
"button",
|
|
2708
2761
|
{
|
|
2709
2762
|
type: "button",
|
|
@@ -2715,7 +2768,7 @@ function GenreTreeView({
|
|
|
2715
2768
|
children: filterArtist.name
|
|
2716
2769
|
}
|
|
2717
2770
|
),
|
|
2718
|
-
/* @__PURE__ */ (0,
|
|
2771
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2719
2772
|
"button",
|
|
2720
2773
|
{
|
|
2721
2774
|
type: "button",
|
|
@@ -2727,8 +2780,8 @@ function GenreTreeView({
|
|
|
2727
2780
|
)
|
|
2728
2781
|
] });
|
|
2729
2782
|
};
|
|
2730
|
-
const actions = /* @__PURE__ */ (0,
|
|
2731
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
2783
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2784
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2732
2785
|
GenreSearch,
|
|
2733
2786
|
{
|
|
2734
2787
|
genrePlaylists: visibleGenrePlaylists,
|
|
@@ -2736,14 +2789,18 @@ function GenreTreeView({
|
|
|
2736
2789
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2737
2790
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2738
2791
|
onPrevious: handlePrevious,
|
|
2739
|
-
onQueryChange:
|
|
2792
|
+
onQueryChange: (text) => {
|
|
2793
|
+
setSearchText(text);
|
|
2794
|
+
onSearchQueryChange?.(text);
|
|
2795
|
+
},
|
|
2796
|
+
onFocus: onSearchFocus,
|
|
2740
2797
|
extraGroups
|
|
2741
2798
|
}
|
|
2742
2799
|
),
|
|
2743
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2744
|
-
/* @__PURE__ */ (0,
|
|
2745
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
2746
|
-
|
|
2800
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2801
|
+
/* @__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: [
|
|
2802
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2803
|
+
import_ui16.SegmentedControl,
|
|
2747
2804
|
{
|
|
2748
2805
|
"aria-label": "Tree view mode",
|
|
2749
2806
|
value: viewMode,
|
|
@@ -2756,14 +2813,14 @@ function GenreTreeView({
|
|
|
2756
2813
|
]
|
|
2757
2814
|
}
|
|
2758
2815
|
),
|
|
2759
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
2816
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2760
2817
|
"div",
|
|
2761
2818
|
{
|
|
2762
2819
|
className: "flex items-center gap-1",
|
|
2763
2820
|
role: "group",
|
|
2764
2821
|
"aria-label": "Tree display options",
|
|
2765
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
2766
|
-
|
|
2822
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2823
|
+
import_ui16.Button,
|
|
2767
2824
|
{
|
|
2768
2825
|
variant: allowWheelRotation ? "default" : "outline",
|
|
2769
2826
|
size: "sm",
|
|
@@ -2773,8 +2830,8 @@ function GenreTreeView({
|
|
|
2773
2830
|
)
|
|
2774
2831
|
}
|
|
2775
2832
|
),
|
|
2776
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
2777
|
-
|
|
2833
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2834
|
+
import_ui16.IconTextButton,
|
|
2778
2835
|
{
|
|
2779
2836
|
icon: import_lucide_react11.Plus,
|
|
2780
2837
|
text: "Add root",
|
|
@@ -2783,34 +2840,34 @@ function GenreTreeView({
|
|
|
2783
2840
|
)
|
|
2784
2841
|
] })
|
|
2785
2842
|
] });
|
|
2786
|
-
return /* @__PURE__ */ (0,
|
|
2843
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2787
2844
|
"div",
|
|
2788
2845
|
{
|
|
2789
2846
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2790
2847
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2791
2848
|
children: [
|
|
2792
|
-
/* @__PURE__ */ (0,
|
|
2849
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2793
2850
|
"div",
|
|
2794
2851
|
{
|
|
2795
2852
|
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
2853
|
children: actions
|
|
2797
2854
|
}
|
|
2798
2855
|
),
|
|
2799
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2856
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2800
2857
|
"div",
|
|
2801
2858
|
{
|
|
2802
2859
|
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
2860
|
children: renderArtistFilter()
|
|
2804
2861
|
}
|
|
2805
2862
|
),
|
|
2806
|
-
/* @__PURE__ */ (0,
|
|
2807
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
2808
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
2863
|
+
/* @__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: [
|
|
2864
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2865
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2809
2866
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2810
2867
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2811
2868
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2812
2869
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2813
|
-
/* @__PURE__ */ (0,
|
|
2870
|
+
/* @__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
2871
|
GenrePlaylistTreeWheel,
|
|
2815
2872
|
{
|
|
2816
2873
|
scope,
|
|
@@ -2827,9 +2884,9 @@ function GenreTreeView({
|
|
|
2827
2884
|
allowWheelRotation,
|
|
2828
2885
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2829
2886
|
busy,
|
|
2830
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2887
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2831
2888
|
floatingControlsLeading,
|
|
2832
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2889
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2833
2890
|
] }) : void 0,
|
|
2834
2891
|
defaultViewport,
|
|
2835
2892
|
viewportRestore,
|
|
@@ -2838,7 +2895,7 @@ function GenreTreeView({
|
|
|
2838
2895
|
) }) })
|
|
2839
2896
|
) : viewMode === "pop-core" ? (
|
|
2840
2897
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2841
|
-
/* @__PURE__ */ (0,
|
|
2898
|
+
/* @__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
2899
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2843
2900
|
{
|
|
2844
2901
|
scope,
|
|
@@ -2855,9 +2912,9 @@ function GenreTreeView({
|
|
|
2855
2912
|
allowWheelRotation,
|
|
2856
2913
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2857
2914
|
busy,
|
|
2858
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2915
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2859
2916
|
floatingControlsLeading,
|
|
2860
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2917
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2861
2918
|
] }) : void 0,
|
|
2862
2919
|
defaultViewport,
|
|
2863
2920
|
viewportRestore,
|
|
@@ -2865,7 +2922,7 @@ function GenreTreeView({
|
|
|
2865
2922
|
...focus
|
|
2866
2923
|
}
|
|
2867
2924
|
) }) })
|
|
2868
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
2925
|
+
) : 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
2926
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2870
2927
|
{
|
|
2871
2928
|
outline: true,
|
|
@@ -2881,13 +2938,13 @@ function GenreTreeView({
|
|
|
2881
2938
|
selectedNodeId,
|
|
2882
2939
|
readOnly
|
|
2883
2940
|
}
|
|
2884
|
-
) }) : /* @__PURE__ */ (0,
|
|
2941
|
+
) }) : /* @__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
2942
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2886
|
-
return /* @__PURE__ */ (0,
|
|
2943
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2887
2944
|
"div",
|
|
2888
2945
|
{
|
|
2889
2946
|
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,
|
|
2947
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2891
2948
|
GenrePlaylistTreePerRoot,
|
|
2892
2949
|
{
|
|
2893
2950
|
scope,
|
|
@@ -2913,8 +2970,8 @@ function GenreTreeView({
|
|
|
2913
2970
|
);
|
|
2914
2971
|
}
|
|
2915
2972
|
) }),
|
|
2916
|
-
/* @__PURE__ */ (0,
|
|
2917
|
-
|
|
2973
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2974
|
+
import_ui16.DetailPanel,
|
|
2918
2975
|
{
|
|
2919
2976
|
ref: panelRef,
|
|
2920
2977
|
side: "left",
|
|
@@ -2930,7 +2987,7 @@ function GenreTreeView({
|
|
|
2930
2987
|
aside: activePanel?.aside,
|
|
2931
2988
|
edgeTab: detailTab,
|
|
2932
2989
|
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,
|
|
2990
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2934
2991
|
activePanel.content,
|
|
2935
2992
|
activePanel.aside
|
|
2936
2993
|
] }) : activePanel?.content
|
|
@@ -2945,10 +3002,10 @@ function GenreTreeView({
|
|
|
2945
3002
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
2946
3003
|
var import_react17 = require("react");
|
|
2947
3004
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
2948
|
-
var
|
|
3005
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2949
3006
|
var import_lucide_react13 = require("lucide-react");
|
|
2950
|
-
var
|
|
2951
|
-
var
|
|
3007
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3008
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2952
3009
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2953
3010
|
function toNode(genre, parentId) {
|
|
2954
3011
|
return {
|
|
@@ -2975,7 +3032,7 @@ function GenreDetailPanel({
|
|
|
2975
3032
|
edgeTab,
|
|
2976
3033
|
extras
|
|
2977
3034
|
}) {
|
|
2978
|
-
const { data: detail, isPending, isError } = (0,
|
|
3035
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree22.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
2979
3036
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
2980
3037
|
const view = (0, import_react17.useMemo)(() => {
|
|
2981
3038
|
if (!detail) return null;
|
|
@@ -3003,17 +3060,17 @@ function GenreDetailPanel({
|
|
|
3003
3060
|
};
|
|
3004
3061
|
}, [detail]);
|
|
3005
3062
|
if (isError) {
|
|
3006
|
-
return /* @__PURE__ */ (0,
|
|
3063
|
+
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
3064
|
}
|
|
3008
3065
|
if (isPending || !detail || !view) {
|
|
3009
|
-
return /* @__PURE__ */ (0,
|
|
3010
|
-
/* @__PURE__ */ (0,
|
|
3011
|
-
/* @__PURE__ */ (0,
|
|
3012
|
-
/* @__PURE__ */ (0,
|
|
3066
|
+
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: [
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui17.Skeleton, { className: "h-6 w-2/3" }),
|
|
3068
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui17.Skeleton, { className: "h-4 w-full" }),
|
|
3069
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui17.Skeleton, { className: "h-4 w-3/4" })
|
|
3013
3070
|
] });
|
|
3014
3071
|
}
|
|
3015
3072
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3016
|
-
const family = /* @__PURE__ */ (0,
|
|
3073
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3017
3074
|
import_genre_tree_view5.GenreNodeFamily,
|
|
3018
3075
|
{
|
|
3019
3076
|
node: view.node,
|
|
@@ -3024,20 +3081,20 @@ function GenreDetailPanel({
|
|
|
3024
3081
|
onSelectNode: selectGenre
|
|
3025
3082
|
}
|
|
3026
3083
|
);
|
|
3027
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
3028
|
-
return /* @__PURE__ */ (0,
|
|
3029
|
-
|
|
3084
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3085
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
3086
|
+
import_ui17.DetailPanel,
|
|
3030
3087
|
{
|
|
3031
3088
|
side: "left",
|
|
3032
3089
|
title: view.node.name,
|
|
3033
3090
|
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3034
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
3035
|
-
|
|
3091
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3092
|
+
import_ui17.IconButton,
|
|
3036
3093
|
{
|
|
3037
3094
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3038
3095
|
style: { color: "inherit" },
|
|
3039
3096
|
onClick: () => onExpandedChange(!expanded),
|
|
3040
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
3097
|
+
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
3098
|
}
|
|
3042
3099
|
) : void 0,
|
|
3043
3100
|
onClose,
|
|
@@ -3046,10 +3103,10 @@ function GenreDetailPanel({
|
|
|
3046
3103
|
onExpandedChange,
|
|
3047
3104
|
edgeTab,
|
|
3048
3105
|
children: [
|
|
3049
|
-
/* @__PURE__ */ (0,
|
|
3106
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3050
3107
|
GenreInfoPanelBody,
|
|
3051
3108
|
{
|
|
3052
|
-
family: /* @__PURE__ */ (0,
|
|
3109
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
3053
3110
|
family,
|
|
3054
3111
|
ancestors
|
|
3055
3112
|
] }),
|
|
@@ -3075,10 +3132,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3075
3132
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3076
3133
|
var import_react18 = require("react");
|
|
3077
3134
|
var import_lucide_react14 = require("lucide-react");
|
|
3078
|
-
var
|
|
3079
|
-
var
|
|
3135
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3136
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3080
3137
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
3081
|
-
var
|
|
3138
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3082
3139
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
|
|
3083
3140
|
function TrackListSidebar({
|
|
3084
3141
|
className: className2 = "",
|
|
@@ -3091,7 +3148,7 @@ function TrackListSidebar({
|
|
|
3091
3148
|
renderArtistLink,
|
|
3092
3149
|
renderSongLink
|
|
3093
3150
|
}) {
|
|
3094
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3151
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree23.useTrackList)();
|
|
3095
3152
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
3096
3153
|
const trackListRef = (0, import_react18.useRef)(null);
|
|
3097
3154
|
const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
|
|
@@ -3180,25 +3237,25 @@ function TrackListSidebar({
|
|
|
3180
3237
|
}
|
|
3181
3238
|
};
|
|
3182
3239
|
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,
|
|
3240
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3184
3241
|
"div",
|
|
3185
3242
|
{
|
|
3186
3243
|
"data-theme": "dark",
|
|
3187
3244
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3188
3245
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3189
3246
|
children: [
|
|
3190
|
-
/* @__PURE__ */ (0,
|
|
3191
|
-
/* @__PURE__ */ (0,
|
|
3192
|
-
/* @__PURE__ */ (0,
|
|
3193
|
-
/* @__PURE__ */ (0,
|
|
3194
|
-
/* @__PURE__ */ (0,
|
|
3247
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3248
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3249
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3250
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3251
|
+
/* @__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
3252
|
] }),
|
|
3196
|
-
/* @__PURE__ */ (0,
|
|
3197
|
-
trackList.origin.type ===
|
|
3253
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3254
|
+
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
3255
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3199
3256
|
] })
|
|
3200
3257
|
] }),
|
|
3201
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3258
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3202
3259
|
"button",
|
|
3203
3260
|
{
|
|
3204
3261
|
type: "button",
|
|
@@ -3207,11 +3264,11 @@ function TrackListSidebar({
|
|
|
3207
3264
|
"aria-expanded": !isCollapsed,
|
|
3208
3265
|
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
3266
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3210
|
-
children: /* @__PURE__ */ (0,
|
|
3267
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react14.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3211
3268
|
}
|
|
3212
3269
|
)
|
|
3213
3270
|
] }),
|
|
3214
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3271
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3215
3272
|
"ul",
|
|
3216
3273
|
{
|
|
3217
3274
|
ref: trackListRef,
|
|
@@ -3221,7 +3278,7 @@ function TrackListSidebar({
|
|
|
3221
3278
|
onPointerDown: markUserScrollIntent,
|
|
3222
3279
|
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
3280
|
children: [
|
|
3224
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3281
|
+
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
3282
|
TrackItem,
|
|
3226
3283
|
{
|
|
3227
3284
|
track,
|
|
@@ -3232,7 +3289,7 @@ function TrackListSidebar({
|
|
|
3232
3289
|
renderSongLink
|
|
3233
3290
|
}
|
|
3234
3291
|
) }, track.uuid)),
|
|
3235
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3292
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3236
3293
|
]
|
|
3237
3294
|
}
|
|
3238
3295
|
)
|
|
@@ -3245,14 +3302,14 @@ function TrackListSidebar({
|
|
|
3245
3302
|
var import_react19 = require("react");
|
|
3246
3303
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3247
3304
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3248
|
-
var
|
|
3249
|
-
var
|
|
3305
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3306
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
3250
3307
|
|
|
3251
3308
|
// src/genre-tree/PlayerControls.tsx
|
|
3252
3309
|
var import_lucide_react15 = require("lucide-react");
|
|
3253
|
-
var
|
|
3310
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3254
3311
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3255
|
-
var
|
|
3312
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
3256
3313
|
function PlayerControls() {
|
|
3257
3314
|
const {
|
|
3258
3315
|
duration,
|
|
@@ -3264,22 +3321,22 @@ function PlayerControls() {
|
|
|
3264
3321
|
setVolume,
|
|
3265
3322
|
volume
|
|
3266
3323
|
} = (0, import_player12.usePlayer)();
|
|
3267
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3324
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree25.useTrackList)();
|
|
3268
3325
|
const currentTime = (0, import_player12.useCurrentTime)();
|
|
3269
3326
|
const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
|
|
3270
3327
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3271
3328
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3272
3329
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3273
3330
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3274
|
-
return /* @__PURE__ */ (0,
|
|
3331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
3275
3332
|
"section",
|
|
3276
3333
|
{
|
|
3277
3334
|
"aria-label": "Player controls",
|
|
3278
3335
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3279
3336
|
children: [
|
|
3280
|
-
/* @__PURE__ */ (0,
|
|
3281
|
-
/* @__PURE__ */ (0,
|
|
3282
|
-
/* @__PURE__ */ (0,
|
|
3337
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3338
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree25.formatTime)(safeCurrentTime) }),
|
|
3339
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3283
3340
|
"input",
|
|
3284
3341
|
{
|
|
3285
3342
|
"aria-label": "Seek",
|
|
@@ -3293,11 +3350,11 @@ function PlayerControls() {
|
|
|
3293
3350
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3294
3351
|
}
|
|
3295
3352
|
),
|
|
3296
|
-
/* @__PURE__ */ (0,
|
|
3353
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree25.formatTime)(safeDuration) })
|
|
3297
3354
|
] }),
|
|
3298
|
-
/* @__PURE__ */ (0,
|
|
3299
|
-
/* @__PURE__ */ (0,
|
|
3300
|
-
/* @__PURE__ */ (0,
|
|
3355
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3356
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3357
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3301
3358
|
"button",
|
|
3302
3359
|
{
|
|
3303
3360
|
type: "button",
|
|
@@ -3306,10 +3363,10 @@ function PlayerControls() {
|
|
|
3306
3363
|
disabled: !isReady || !hasPreviousTrack,
|
|
3307
3364
|
onClick: playPreviousTrack,
|
|
3308
3365
|
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,
|
|
3366
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3310
3367
|
}
|
|
3311
3368
|
),
|
|
3312
|
-
/* @__PURE__ */ (0,
|
|
3369
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3313
3370
|
"button",
|
|
3314
3371
|
{
|
|
3315
3372
|
type: "button",
|
|
@@ -3318,10 +3375,10 @@ function PlayerControls() {
|
|
|
3318
3375
|
disabled: !isReady,
|
|
3319
3376
|
onClick: handlePlayPauseAction,
|
|
3320
3377
|
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,
|
|
3378
|
+
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
3379
|
}
|
|
3323
3380
|
),
|
|
3324
|
-
/* @__PURE__ */ (0,
|
|
3381
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3325
3382
|
"button",
|
|
3326
3383
|
{
|
|
3327
3384
|
type: "button",
|
|
@@ -3330,14 +3387,14 @@ function PlayerControls() {
|
|
|
3330
3387
|
disabled: !isReady || !hasNextTrack,
|
|
3331
3388
|
onClick: playNextTrack,
|
|
3332
3389
|
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,
|
|
3390
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react15.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3334
3391
|
}
|
|
3335
3392
|
)
|
|
3336
3393
|
] }),
|
|
3337
|
-
/* @__PURE__ */ (0,
|
|
3338
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3339
|
-
/* @__PURE__ */ (0,
|
|
3340
|
-
/* @__PURE__ */ (0,
|
|
3394
|
+
/* @__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: [
|
|
3395
|
+
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" }),
|
|
3396
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3397
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3341
3398
|
"input",
|
|
3342
3399
|
{
|
|
3343
3400
|
"aria-label": "Volume",
|
|
@@ -3359,7 +3416,7 @@ function PlayerControls() {
|
|
|
3359
3416
|
}
|
|
3360
3417
|
|
|
3361
3418
|
// src/genre-tree/PlayerPanel.tsx
|
|
3362
|
-
var
|
|
3419
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
3363
3420
|
var PANEL_ID = "player-panel";
|
|
3364
3421
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3365
3422
|
function PlayerPanel({
|
|
@@ -3372,7 +3429,7 @@ function PlayerPanel({
|
|
|
3372
3429
|
renderSongLink
|
|
3373
3430
|
}) {
|
|
3374
3431
|
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3375
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3432
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree26.useTrackListSidebarVisibility)();
|
|
3376
3433
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3377
3434
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3378
3435
|
(0, import_react19.useEffect)(() => {
|
|
@@ -3399,7 +3456,7 @@ function PlayerPanel({
|
|
|
3399
3456
|
onClick: () => setIsExtendRequested(true),
|
|
3400
3457
|
expanded: false
|
|
3401
3458
|
} : null;
|
|
3402
|
-
return /* @__PURE__ */ (0,
|
|
3459
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3403
3460
|
"div",
|
|
3404
3461
|
{
|
|
3405
3462
|
id: PANEL_ID,
|
|
@@ -3412,8 +3469,8 @@ function PlayerPanel({
|
|
|
3412
3469
|
),
|
|
3413
3470
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3414
3471
|
children: [
|
|
3415
|
-
/* @__PURE__ */ (0,
|
|
3416
|
-
|
|
3472
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3473
|
+
import_ui18.PanelEdgeTab,
|
|
3417
3474
|
{
|
|
3418
3475
|
side: "right",
|
|
3419
3476
|
edge: tabEdge,
|
|
@@ -3422,7 +3479,7 @@ function PlayerPanel({
|
|
|
3422
3479
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3423
3480
|
}
|
|
3424
3481
|
),
|
|
3425
|
-
/* @__PURE__ */ (0,
|
|
3482
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3426
3483
|
"div",
|
|
3427
3484
|
{
|
|
3428
3485
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3430,20 +3487,20 @@ function PlayerPanel({
|
|
|
3430
3487
|
isExtended ? "flex-row" : "flex-col"
|
|
3431
3488
|
),
|
|
3432
3489
|
children: [
|
|
3433
|
-
/* @__PURE__ */ (0,
|
|
3434
|
-
/* @__PURE__ */ (0,
|
|
3435
|
-
/* @__PURE__ */ (0,
|
|
3436
|
-
/* @__PURE__ */ (0,
|
|
3437
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3490
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3491
|
+
/* @__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 }) }),
|
|
3492
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3493
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3494
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3438
3495
|
playerTrackObject.loadError,
|
|
3439
|
-
/* @__PURE__ */ (0,
|
|
3496
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3440
3497
|
void retryPlayback();
|
|
3441
3498
|
}, children: "Retry" })
|
|
3442
3499
|
] })
|
|
3443
3500
|
] }),
|
|
3444
|
-
/* @__PURE__ */ (0,
|
|
3501
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(PlayerControls, {})
|
|
3445
3502
|
] }),
|
|
3446
|
-
/* @__PURE__ */ (0,
|
|
3503
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3447
3504
|
TrackListSidebar,
|
|
3448
3505
|
{
|
|
3449
3506
|
layout: "inline",
|
|
@@ -3469,33 +3526,33 @@ function PlayerPanel({
|
|
|
3469
3526
|
// src/genre-tree/Rating.tsx
|
|
3470
3527
|
var import_uuid = require("uuid");
|
|
3471
3528
|
var import_ai = require("react-icons/ai");
|
|
3472
|
-
var
|
|
3473
|
-
var
|
|
3529
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3530
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
3474
3531
|
function Rating({ rating, handleChange }) {
|
|
3475
3532
|
const instanceId = (0, import_uuid.v4)();
|
|
3476
|
-
return /* @__PURE__ */ (0,
|
|
3477
|
-
/* @__PURE__ */ (0,
|
|
3533
|
+
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: [
|
|
3534
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3478
3535
|
"input",
|
|
3479
3536
|
{
|
|
3480
3537
|
id: `star-${instanceId}-${i}`,
|
|
3481
3538
|
className: "w-6 h-6 opacity-0",
|
|
3482
3539
|
type: "radio",
|
|
3483
3540
|
name: "rating",
|
|
3484
|
-
value: i === 0 ?
|
|
3541
|
+
value: i === 0 ? import_genre_tree27.FORM_RATING_NULL_VALUE : i,
|
|
3485
3542
|
checked: rating === (i === 0 ? null : i),
|
|
3486
3543
|
onChange: handleChange
|
|
3487
3544
|
}
|
|
3488
3545
|
),
|
|
3489
|
-
/* @__PURE__ */ (0,
|
|
3490
|
-
/* @__PURE__ */ (0,
|
|
3546
|
+
/* @__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: [
|
|
3547
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3491
3548
|
import_ai.AiOutlineStar,
|
|
3492
3549
|
{
|
|
3493
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3550
|
+
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
3551
|
}
|
|
3495
3552
|
),
|
|
3496
|
-
/* @__PURE__ */ (0,
|
|
3553
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex-grow w-8" }),
|
|
3497
3554
|
" "
|
|
3498
|
-
] }) : /* @__PURE__ */ (0,
|
|
3555
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3499
3556
|
import_ai.AiFillStar,
|
|
3500
3557
|
{
|
|
3501
3558
|
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 +3574,15 @@ function Rating({ rating, handleChange }) {
|
|
|
3517
3574
|
GenreTreeWheelSkeleton,
|
|
3518
3575
|
PlayerPanel,
|
|
3519
3576
|
Rating,
|
|
3577
|
+
SEARCH_FAILED_STATUS,
|
|
3578
|
+
SearchBox,
|
|
3520
3579
|
TrackItem,
|
|
3521
3580
|
TrackListSidebar,
|
|
3522
3581
|
TrackPositionPlayPause,
|
|
3523
|
-
|
|
3582
|
+
searchStatus,
|
|
3583
|
+
useArtistSearchGroup,
|
|
3584
|
+
useGenrePlaylistPlayback,
|
|
3585
|
+
useGenreRemoteSearchGroup,
|
|
3586
|
+
useSongSearchGroup
|
|
3524
3587
|
});
|
|
3525
3588
|
//# sourceMappingURL=index.cjs.map
|