@behindthemusictree/app-kit-ui 33.0.0 → 33.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GHFKICEB.js → chunk-FHGQGTCN.js} +521 -463
- package/dist/chunk-FHGQGTCN.js.map +1 -0
- package/dist/genre-tree/index.cjs +599 -536
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +70 -20
- package/dist/genre-tree/index.js +15 -3
- package/dist/index.cjs +597 -534
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -3
- package/package.json +5 -5
- package/dist/chunk-GHFKICEB.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -40,13 +40,19 @@ __export(src_exports, {
|
|
|
40
40
|
PopupButtons: () => PopupButtons,
|
|
41
41
|
PopupTitle: () => PopupTitle,
|
|
42
42
|
Rating: () => Rating,
|
|
43
|
+
SEARCH_FAILED_STATUS: () => SEARCH_FAILED_STATUS,
|
|
44
|
+
SearchBox: () => SearchBox,
|
|
43
45
|
SpotifyAuthErrorPopup: () => SpotifyAuthErrorPopup,
|
|
44
46
|
Toast: () => Toast,
|
|
45
47
|
TrackItem: () => TrackItem,
|
|
46
48
|
TrackListSidebar: () => TrackListSidebar,
|
|
47
49
|
TrackPositionPlayPause: () => TrackPositionPlayPause,
|
|
48
50
|
TrackUploadPopup: () => TrackUploadPopup,
|
|
49
|
-
|
|
51
|
+
searchStatus: () => searchStatus,
|
|
52
|
+
useArtistSearchGroup: () => useArtistSearchGroup,
|
|
53
|
+
useGenrePlaylistPlayback: () => useGenrePlaylistPlayback,
|
|
54
|
+
useGenreRemoteSearchGroup: () => useGenreRemoteSearchGroup,
|
|
55
|
+
useSongSearchGroup: () => useSongSearchGroup
|
|
50
56
|
});
|
|
51
57
|
module.exports = __toCommonJS(src_exports);
|
|
52
58
|
|
|
@@ -585,10 +591,9 @@ var import_react16 = require("react");
|
|
|
585
591
|
var import_react_query3 = require("@tanstack/react-query");
|
|
586
592
|
var import_lucide_react15 = require("lucide-react");
|
|
587
593
|
var import_lucide_react16 = require("lucide-react");
|
|
588
|
-
var
|
|
594
|
+
var import_ui19 = require("@behindthemusictree/ui");
|
|
589
595
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
590
|
-
var
|
|
591
|
-
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
596
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
592
597
|
var import_popup6 = require("@behindthemusictree/app-kit/popup");
|
|
593
598
|
|
|
594
599
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -1020,11 +1025,143 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
1020
1025
|
}
|
|
1021
1026
|
|
|
1022
1027
|
// src/genre-tree/GenreSearch.tsx
|
|
1023
|
-
var import_react8 = require("react");
|
|
1024
1028
|
var import_lucide_react7 = require("lucide-react");
|
|
1029
|
+
var import_ui7 = require("@behindthemusictree/ui");
|
|
1030
|
+
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1031
|
+
|
|
1032
|
+
// src/genre-tree/search/SearchBox.tsx
|
|
1033
|
+
var import_react8 = require("react");
|
|
1025
1034
|
var import_ui6 = require("@behindthemusictree/ui");
|
|
1026
|
-
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1027
1035
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1036
|
+
function SearchBox({
|
|
1037
|
+
label,
|
|
1038
|
+
placeholder,
|
|
1039
|
+
value,
|
|
1040
|
+
onValueChange,
|
|
1041
|
+
groups,
|
|
1042
|
+
onPicked,
|
|
1043
|
+
onClear,
|
|
1044
|
+
leading,
|
|
1045
|
+
className: className2,
|
|
1046
|
+
onFocus
|
|
1047
|
+
}) {
|
|
1048
|
+
const [open, setOpen] = (0, import_react8.useState)(false);
|
|
1049
|
+
const loading = groups.some((group) => group.loading);
|
|
1050
|
+
const status = groups.map((group) => group.status).filter(Boolean).join(". ");
|
|
1051
|
+
const handlePick = (groupKey, itemKey) => {
|
|
1052
|
+
setOpen(false);
|
|
1053
|
+
groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
|
|
1054
|
+
onPicked?.(groupKey, itemKey);
|
|
1055
|
+
};
|
|
1056
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1057
|
+
import_ui6.SearchCombobox,
|
|
1058
|
+
{
|
|
1059
|
+
ariaLabel: label,
|
|
1060
|
+
listLabel: `${label} results`,
|
|
1061
|
+
value,
|
|
1062
|
+
placeholder: placeholder ?? `${label}\u2026`,
|
|
1063
|
+
groups: groups.map((group) => ({
|
|
1064
|
+
key: group.key,
|
|
1065
|
+
label: group.label,
|
|
1066
|
+
options: group.items.map((item) => ({ key: item.key, content: item.content }))
|
|
1067
|
+
})),
|
|
1068
|
+
open,
|
|
1069
|
+
onOpenChange: setOpen,
|
|
1070
|
+
onValueChange,
|
|
1071
|
+
onPick: handlePick,
|
|
1072
|
+
status,
|
|
1073
|
+
loading,
|
|
1074
|
+
onClear,
|
|
1075
|
+
leading,
|
|
1076
|
+
inputClassName: "focus-visible:ring-0"
|
|
1077
|
+
}
|
|
1078
|
+
) });
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/genre-tree/search/SearchGroup.ts
|
|
1082
|
+
var SEARCH_FAILED_STATUS = "Search failed";
|
|
1083
|
+
var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
|
|
1084
|
+
|
|
1085
|
+
// src/genre-tree/search/useArtistSearchGroup.tsx
|
|
1086
|
+
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1087
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1088
|
+
function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
1089
|
+
const search = (0, import_genre_tree2.useArtistSearch)(getBaseUrl, enabled ? text : "");
|
|
1090
|
+
if (!enabled) return void 0;
|
|
1091
|
+
return {
|
|
1092
|
+
key: "artists",
|
|
1093
|
+
label: "Artists",
|
|
1094
|
+
items: search.artists.map((artist) => ({
|
|
1095
|
+
key: artist.uuid,
|
|
1096
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
1097
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
1098
|
+
artist.name,
|
|
1099
|
+
artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
1100
|
+
" \xB7 ",
|
|
1101
|
+
artist.disambiguation
|
|
1102
|
+
] })
|
|
1103
|
+
] }),
|
|
1104
|
+
artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
1105
|
+
"also known as ",
|
|
1106
|
+
artist.matchedAlias
|
|
1107
|
+
] })
|
|
1108
|
+
] }),
|
|
1109
|
+
onSelect: () => onSelect(artist)
|
|
1110
|
+
})),
|
|
1111
|
+
status: searchStatus(search, search.artists.length, "artists"),
|
|
1112
|
+
loading: search.pending
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// src/genre-tree/search/useSongSearchGroup.tsx
|
|
1117
|
+
var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1118
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1119
|
+
function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
1120
|
+
const search = (0, import_genre_tree3.useSongSearch)(getBaseUrl, enabled ? text : "");
|
|
1121
|
+
if (!enabled) return void 0;
|
|
1122
|
+
return {
|
|
1123
|
+
key: "songs",
|
|
1124
|
+
label: "Songs",
|
|
1125
|
+
items: search.songs.map((song) => ({
|
|
1126
|
+
key: song.uuid,
|
|
1127
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: (0, import_genre_tree3.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
1128
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
1129
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.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 ") })
|
|
1130
|
+
] }),
|
|
1131
|
+
onSelect: () => onSelect(song)
|
|
1132
|
+
})),
|
|
1133
|
+
status: searchStatus(search, search.songs.length, "songs"),
|
|
1134
|
+
loading: search.pending
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// src/genre-tree/search/useGenreRemoteSearchGroup.tsx
|
|
1139
|
+
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1140
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1141
|
+
function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
|
|
1142
|
+
const search = (0, import_genre_tree4.useGenreRemoteSearch)(getBaseUrl, text, options);
|
|
1143
|
+
const genres = filter ? search.genres.filter(filter) : search.genres;
|
|
1144
|
+
return {
|
|
1145
|
+
key,
|
|
1146
|
+
label,
|
|
1147
|
+
items: genres.map((genre) => ({
|
|
1148
|
+
key: genre.uuid,
|
|
1149
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
1150
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
1151
|
+
genre.name,
|
|
1152
|
+
genre.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ml-2 text-xs font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
1153
|
+
] }),
|
|
1154
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
|
|
1155
|
+
] }),
|
|
1156
|
+
onSelect: () => onSelect(genre)
|
|
1157
|
+
})),
|
|
1158
|
+
status: searchStatus(search, genres.length, "genres"),
|
|
1159
|
+
loading: search.pending
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// src/genre-tree/GenreSearch.tsx
|
|
1164
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1028
1165
|
function GenreSearch({
|
|
1029
1166
|
genrePlaylists,
|
|
1030
1167
|
onSelect,
|
|
@@ -1033,120 +1170,58 @@ function GenreSearch({
|
|
|
1033
1170
|
onPrevious,
|
|
1034
1171
|
placeholder,
|
|
1035
1172
|
onQueryChange,
|
|
1036
|
-
extraGroups = []
|
|
1173
|
+
extraGroups = [],
|
|
1174
|
+
onFocus
|
|
1037
1175
|
}) {
|
|
1038
|
-
const { query, setQuery, results, canClear, clear, select } = (0,
|
|
1176
|
+
const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree5.useGenreSearch)({
|
|
1039
1177
|
genrePlaylists,
|
|
1040
1178
|
onSelect,
|
|
1041
1179
|
selectedName,
|
|
1042
1180
|
onClear
|
|
1043
1181
|
});
|
|
1044
|
-
const [open, setOpen] = (0, import_react8.useState)(false);
|
|
1045
1182
|
const active = query !== "" && query !== selectedName;
|
|
1046
1183
|
const hasExtras = extraGroups.length > 0;
|
|
1047
1184
|
const label = hasExtras ? "Search" : "Search a genre";
|
|
1048
|
-
const loading = active && extraGroups.some((group) => group.loading);
|
|
1049
|
-
const status = active ? extraGroups.map((group) => group.status).filter(Boolean).join(". ") : "";
|
|
1050
1185
|
const groups = [
|
|
1051
1186
|
{
|
|
1052
1187
|
key: "genres",
|
|
1053
1188
|
label: hasExtras ? "Genres" : void 0,
|
|
1054
|
-
|
|
1189
|
+
items: results.map((genrePlaylist) => ({
|
|
1055
1190
|
key: genrePlaylist.uuid,
|
|
1056
|
-
content: /* @__PURE__ */ (0,
|
|
1191
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
1057
1192
|
genrePlaylist.name,
|
|
1058
|
-
genrePlaylist.isUmbrella && /* @__PURE__ */ (0,
|
|
1059
|
-
] })
|
|
1193
|
+
genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ml-2 text-xs italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
1194
|
+
] }),
|
|
1195
|
+
onSelect: () => select(genrePlaylist)
|
|
1060
1196
|
}))
|
|
1061
1197
|
},
|
|
1062
|
-
...extraGroups.map(
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
options: active ? group.items.map((item) => ({ key: item.key, content: item.content })) : []
|
|
1066
|
-
}))
|
|
1198
|
+
...extraGroups.map(
|
|
1199
|
+
(group) => active ? group : { ...group, items: [], status: "", loading: false }
|
|
1200
|
+
)
|
|
1067
1201
|
];
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.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_runtime20.jsx)(
|
|
1203
|
+
SearchBox,
|
|
1204
|
+
{
|
|
1205
|
+
label,
|
|
1206
|
+
placeholder,
|
|
1207
|
+
value: query,
|
|
1208
|
+
onValueChange: (value) => {
|
|
1209
|
+
setQuery(value);
|
|
1210
|
+
onQueryChange?.(value);
|
|
1211
|
+
},
|
|
1212
|
+
groups,
|
|
1213
|
+
onPicked: (groupKey) => {
|
|
1214
|
+
if (groupKey !== "genres") setQuery("");
|
|
1215
|
+
onQueryChange?.("");
|
|
1216
|
+
},
|
|
1217
|
+
onClear: canClear ? () => {
|
|
1218
|
+
clear();
|
|
1219
|
+
onQueryChange?.("");
|
|
1220
|
+
} : void 0,
|
|
1221
|
+
leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui7.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
|
|
1222
|
+
onFocus
|
|
1076
1223
|
}
|
|
1077
|
-
|
|
1078
|
-
if (!item) return;
|
|
1079
|
-
setQuery("");
|
|
1080
|
-
onQueryChange?.("");
|
|
1081
|
-
item.onSelect();
|
|
1082
|
-
};
|
|
1083
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.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: [
|
|
1084
|
-
onPrevious && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1085
|
-
"button",
|
|
1086
|
-
{
|
|
1087
|
-
type: "button",
|
|
1088
|
-
"aria-label": "Previous",
|
|
1089
|
-
title: "Previous",
|
|
1090
|
-
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)]",
|
|
1091
|
-
onClick: onPrevious,
|
|
1092
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
1093
|
-
}
|
|
1094
|
-
),
|
|
1095
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1096
|
-
import_ui6.Combobox,
|
|
1097
|
-
{
|
|
1098
|
-
ariaLabel: label,
|
|
1099
|
-
listLabel: `${label} results`,
|
|
1100
|
-
value: query,
|
|
1101
|
-
placeholder: placeholder ?? `${label}\u2026`,
|
|
1102
|
-
groups,
|
|
1103
|
-
open,
|
|
1104
|
-
onOpenChange: setOpen,
|
|
1105
|
-
onValueChange: (value) => {
|
|
1106
|
-
setQuery(value);
|
|
1107
|
-
onQueryChange?.(value);
|
|
1108
|
-
},
|
|
1109
|
-
onPick: handlePick,
|
|
1110
|
-
status,
|
|
1111
|
-
loading: loading ? "Searching\u2026" : void 0,
|
|
1112
|
-
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`,
|
|
1113
|
-
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
1114
|
-
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
1115
|
-
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
1116
|
-
}
|
|
1117
|
-
),
|
|
1118
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "absolute right-3 top-1/2 flex items-center -translate-y-1/2", children: [
|
|
1119
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { "data-loading": loading, className: "relative p-2 text-[var(--color-text-muted)] pointer-events-none", children: [
|
|
1120
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1121
|
-
import_lucide_react7.Search,
|
|
1122
|
-
{
|
|
1123
|
-
"aria-hidden": "true",
|
|
1124
|
-
className: `w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-0" : ""}`
|
|
1125
|
-
}
|
|
1126
|
-
),
|
|
1127
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1128
|
-
import_lucide_react7.LoaderCircle,
|
|
1129
|
-
{
|
|
1130
|
-
"aria-hidden": "true",
|
|
1131
|
-
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"}`
|
|
1132
|
-
}
|
|
1133
|
-
)
|
|
1134
|
-
] }),
|
|
1135
|
-
canClear && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1136
|
-
"button",
|
|
1137
|
-
{
|
|
1138
|
-
type: "button",
|
|
1139
|
-
"aria-label": "Clear search",
|
|
1140
|
-
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)]",
|
|
1141
|
-
onClick: () => {
|
|
1142
|
-
clear();
|
|
1143
|
-
onQueryChange?.("");
|
|
1144
|
-
},
|
|
1145
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.X, { "aria-hidden": "true", className: "w-6 h-6" })
|
|
1146
|
-
}
|
|
1147
|
-
)
|
|
1148
|
-
] })
|
|
1149
|
-
] });
|
|
1224
|
+
) });
|
|
1150
1225
|
}
|
|
1151
1226
|
|
|
1152
1227
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
@@ -1166,12 +1241,12 @@ var import_fa3 = require("react-icons/fa");
|
|
|
1166
1241
|
var import_si = require("react-icons/si");
|
|
1167
1242
|
|
|
1168
1243
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
1169
|
-
var
|
|
1244
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1170
1245
|
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";
|
|
1171
1246
|
function Contents({ icon, label }) {
|
|
1172
|
-
return /* @__PURE__ */ (0,
|
|
1173
|
-
/* @__PURE__ */ (0,
|
|
1174
|
-
/* @__PURE__ */ (0,
|
|
1247
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
1248
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.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 }),
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "max-w-full truncate", children: label })
|
|
1175
1250
|
] });
|
|
1176
1251
|
}
|
|
1177
1252
|
function ShareDestinationButton({
|
|
@@ -1183,29 +1258,29 @@ function ShareDestinationButton({
|
|
|
1183
1258
|
ariaLabel
|
|
1184
1259
|
}) {
|
|
1185
1260
|
if (href) {
|
|
1186
|
-
return /* @__PURE__ */ (0,
|
|
1261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Contents, { icon, label }) });
|
|
1187
1262
|
}
|
|
1188
|
-
return /* @__PURE__ */ (0,
|
|
1263
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Contents, { icon, label }) });
|
|
1189
1264
|
}
|
|
1190
1265
|
|
|
1191
1266
|
// src/genre-tree/SharePreviewCard.tsx
|
|
1192
1267
|
var import_lucide_react8 = require("lucide-react");
|
|
1193
|
-
var
|
|
1268
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1194
1269
|
function SharePreviewCard({ target, url }) {
|
|
1195
1270
|
const Icon = { genre: import_lucide_react8.Music2, artist: import_lucide_react8.UserRound, song: import_lucide_react8.Music }[target.kind];
|
|
1196
1271
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
1197
|
-
return /* @__PURE__ */ (0,
|
|
1198
|
-
/* @__PURE__ */ (0,
|
|
1199
|
-
/* @__PURE__ */ (0,
|
|
1200
|
-
/* @__PURE__ */ (0,
|
|
1201
|
-
/* @__PURE__ */ (0,
|
|
1202
|
-
/* @__PURE__ */ (0,
|
|
1272
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
1273
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.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_runtime22.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
1274
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
1277
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
1203
1278
|
] })
|
|
1204
1279
|
] }) });
|
|
1205
1280
|
}
|
|
1206
1281
|
|
|
1207
1282
|
// src/genre-tree/SharePopup.tsx
|
|
1208
|
-
var
|
|
1283
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1209
1284
|
function resolveAbsoluteUrl(url) {
|
|
1210
1285
|
try {
|
|
1211
1286
|
const parsed = new URL(url, window.location.href);
|
|
@@ -1280,8 +1355,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1280
1355
|
setShareError("Could not open the share options.");
|
|
1281
1356
|
}
|
|
1282
1357
|
};
|
|
1283
|
-
return /* @__PURE__ */ (0,
|
|
1284
|
-
/* @__PURE__ */ (0,
|
|
1358
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
1359
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1285
1360
|
BasePopup_default,
|
|
1286
1361
|
{
|
|
1287
1362
|
title: "Share",
|
|
@@ -1296,67 +1371,67 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1296
1371
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
1297
1372
|
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)]",
|
|
1298
1373
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
1299
|
-
children: /* @__PURE__ */ (0,
|
|
1300
|
-
/* @__PURE__ */ (0,
|
|
1301
|
-
/* @__PURE__ */ (0,
|
|
1302
|
-
/* @__PURE__ */ (0,
|
|
1374
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
1377
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1303
1378
|
ShareDestinationButton,
|
|
1304
1379
|
{
|
|
1305
1380
|
label: "Copy Link",
|
|
1306
|
-
icon: /* @__PURE__ */ (0,
|
|
1381
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1307
1382
|
onClick: () => void handleCopy(),
|
|
1308
1383
|
ariaLabel: "Copy link",
|
|
1309
1384
|
disabled: !absoluteUrl
|
|
1310
1385
|
}
|
|
1311
1386
|
),
|
|
1312
|
-
/* @__PURE__ */ (0,
|
|
1387
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1313
1388
|
ShareDestinationButton,
|
|
1314
1389
|
{
|
|
1315
1390
|
label: "WhatsApp",
|
|
1316
|
-
icon: /* @__PURE__ */ (0,
|
|
1391
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
1317
1392
|
href: destinationUrls?.whatsapp,
|
|
1318
1393
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
1319
1394
|
disabled: !absoluteUrl
|
|
1320
1395
|
}
|
|
1321
1396
|
),
|
|
1322
|
-
/* @__PURE__ */ (0,
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1323
1398
|
ShareDestinationButton,
|
|
1324
1399
|
{
|
|
1325
1400
|
label: "Gmail",
|
|
1326
|
-
icon: /* @__PURE__ */ (0,
|
|
1401
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
1327
1402
|
href: destinationUrls?.gmail,
|
|
1328
1403
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
1329
1404
|
disabled: !absoluteUrl
|
|
1330
1405
|
}
|
|
1331
1406
|
),
|
|
1332
|
-
canUseNativeShare && /* @__PURE__ */ (0,
|
|
1407
|
+
canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1333
1408
|
ShareDestinationButton,
|
|
1334
1409
|
{
|
|
1335
1410
|
label: "More",
|
|
1336
|
-
icon: /* @__PURE__ */ (0,
|
|
1411
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1337
1412
|
onClick: () => void handleNativeShare(),
|
|
1338
1413
|
ariaLabel: "More sharing options",
|
|
1339
1414
|
disabled: !absoluteUrl
|
|
1340
1415
|
}
|
|
1341
1416
|
)
|
|
1342
1417
|
] }),
|
|
1343
|
-
copyStatus === "failed" && /* @__PURE__ */ (0,
|
|
1344
|
-
shareError && /* @__PURE__ */ (0,
|
|
1418
|
+
copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
1419
|
+
shareError && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
1345
1420
|
] })
|
|
1346
1421
|
}
|
|
1347
1422
|
),
|
|
1348
|
-
toastId > 0 && /* @__PURE__ */ (0,
|
|
1423
|
+
toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
1349
1424
|
] });
|
|
1350
1425
|
}
|
|
1351
1426
|
|
|
1352
1427
|
// src/genre-tree/ShareActionButton.tsx
|
|
1353
|
-
var
|
|
1428
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1354
1429
|
function ShareActionButton({ target, url }) {
|
|
1355
1430
|
const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
|
|
1356
1431
|
const openSharePopup = () => {
|
|
1357
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1432
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SharePopup, { target, url, onClose: hidePopup }));
|
|
1358
1433
|
};
|
|
1359
|
-
return /* @__PURE__ */ (0,
|
|
1434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
1360
1435
|
"button",
|
|
1361
1436
|
{
|
|
1362
1437
|
type: "button",
|
|
@@ -1365,7 +1440,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1365
1440
|
"aria-label": `Share ${target.name}`,
|
|
1366
1441
|
title: `Share ${target.name}`,
|
|
1367
1442
|
children: [
|
|
1368
|
-
/* @__PURE__ */ (0,
|
|
1443
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.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_runtime24.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1369
1444
|
"Share"
|
|
1370
1445
|
]
|
|
1371
1446
|
}
|
|
@@ -1373,7 +1448,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1373
1448
|
}
|
|
1374
1449
|
|
|
1375
1450
|
// src/genre-tree/GenreActionBand.tsx
|
|
1376
|
-
var
|
|
1451
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1377
1452
|
function GenreActionBand({
|
|
1378
1453
|
node,
|
|
1379
1454
|
onPlayPause,
|
|
@@ -1395,7 +1470,7 @@ function GenreActionBand({
|
|
|
1395
1470
|
items.push({
|
|
1396
1471
|
key: "play-pause",
|
|
1397
1472
|
label: isPlaying ? "Pause" : "Play",
|
|
1398
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
1473
|
+
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1399
1474
|
onClick: onPlayPause
|
|
1400
1475
|
});
|
|
1401
1476
|
}
|
|
@@ -1403,24 +1478,24 @@ function GenreActionBand({
|
|
|
1403
1478
|
items.push({
|
|
1404
1479
|
key: "zoom-in",
|
|
1405
1480
|
label: "Focus",
|
|
1406
|
-
icon: /* @__PURE__ */ (0,
|
|
1481
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1407
1482
|
onClick: onZoomIn
|
|
1408
1483
|
});
|
|
1409
1484
|
}
|
|
1410
1485
|
if (onAddChild) {
|
|
1411
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0,
|
|
1486
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
1412
1487
|
}
|
|
1413
1488
|
if (onRename) {
|
|
1414
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0,
|
|
1489
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
1415
1490
|
}
|
|
1416
1491
|
if (onChangeParent) {
|
|
1417
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0,
|
|
1492
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
1418
1493
|
}
|
|
1419
1494
|
if (onToggleSide) {
|
|
1420
1495
|
adminItems.push({
|
|
1421
1496
|
key: "toggle-side",
|
|
1422
1497
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
1423
|
-
icon: /* @__PURE__ */ (0,
|
|
1498
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1424
1499
|
onClick: onToggleSide,
|
|
1425
1500
|
disabled: !!toggleSideDisabledReason,
|
|
1426
1501
|
title: toggleSideDisabledReason
|
|
@@ -1436,7 +1511,7 @@ function GenreActionBand({
|
|
|
1436
1511
|
});
|
|
1437
1512
|
}
|
|
1438
1513
|
if (items.length === 0 && (!shareTarget || !shareUrl) && adminItems.length === 0) return null;
|
|
1439
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0,
|
|
1514
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1440
1515
|
"button",
|
|
1441
1516
|
{
|
|
1442
1517
|
type: "button",
|
|
@@ -1445,25 +1520,25 @@ function GenreActionBand({
|
|
|
1445
1520
|
onClick: item.onClick,
|
|
1446
1521
|
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",
|
|
1447
1522
|
children: [
|
|
1448
|
-
/* @__PURE__ */ (0,
|
|
1523
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.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 }),
|
|
1449
1524
|
item.label
|
|
1450
1525
|
]
|
|
1451
1526
|
},
|
|
1452
1527
|
item.key
|
|
1453
1528
|
));
|
|
1454
|
-
return /* @__PURE__ */ (0,
|
|
1455
|
-
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0,
|
|
1529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
1530
|
+
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
1456
1531
|
renderItems(items),
|
|
1457
|
-
shareTarget && shareUrl && /* @__PURE__ */ (0,
|
|
1532
|
+
shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1458
1533
|
] }),
|
|
1459
|
-
adminItems.length > 0 && /* @__PURE__ */ (0,
|
|
1534
|
+
adminItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1460
1535
|
"section",
|
|
1461
1536
|
{
|
|
1462
1537
|
"aria-label": "Admin actions",
|
|
1463
1538
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
1464
1539
|
children: [
|
|
1465
|
-
/* @__PURE__ */ (0,
|
|
1466
|
-
/* @__PURE__ */ (0,
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
1467
1542
|
]
|
|
1468
1543
|
}
|
|
1469
1544
|
)
|
|
@@ -1471,12 +1546,12 @@ function GenreActionBand({
|
|
|
1471
1546
|
}
|
|
1472
1547
|
|
|
1473
1548
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1474
|
-
var
|
|
1475
|
-
var
|
|
1549
|
+
var import_ui9 = require("@behindthemusictree/ui");
|
|
1550
|
+
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1476
1551
|
|
|
1477
1552
|
// src/genre-tree/ClampedText.tsx
|
|
1478
1553
|
var import_react10 = require("react");
|
|
1479
|
-
var
|
|
1554
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1480
1555
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1481
1556
|
const ref = (0, import_react10.useRef)(null);
|
|
1482
1557
|
const [expanded, setExpanded] = (0, import_react10.useState)(false);
|
|
@@ -1486,9 +1561,9 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1486
1561
|
if (!element || expanded) return;
|
|
1487
1562
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1488
1563
|
}, [children, expanded]);
|
|
1489
|
-
return /* @__PURE__ */ (0,
|
|
1490
|
-
/* @__PURE__ */ (0,
|
|
1491
|
-
(overflows || expanded) && /* @__PURE__ */ (0,
|
|
1564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
1565
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1566
|
+
(overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1492
1567
|
"button",
|
|
1493
1568
|
{
|
|
1494
1569
|
type: "button",
|
|
@@ -1506,7 +1581,7 @@ var import_react11 = require("react");
|
|
|
1506
1581
|
var import_react_query = require("@tanstack/react-query");
|
|
1507
1582
|
var import_zod = require("zod");
|
|
1508
1583
|
var import_fa4 = require("react-icons/fa");
|
|
1509
|
-
var
|
|
1584
|
+
var import_ui8 = require("@behindthemusictree/ui");
|
|
1510
1585
|
var import_auth2 = require("@behindthemusictree/app-kit/auth");
|
|
1511
1586
|
var import_transport5 = require("@behindthemusictree/app-kit/transport");
|
|
1512
1587
|
var import_transport6 = require("@behindthemusictree/app-kit/transport");
|
|
@@ -1515,22 +1590,22 @@ var import_player4 = require("@behindthemusictree/app-kit/player");
|
|
|
1515
1590
|
var import_player5 = require("@behindthemusictree/app-kit/player");
|
|
1516
1591
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
1517
1592
|
var import_transport8 = require("@behindthemusictree/app-kit/transport");
|
|
1518
|
-
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1519
|
-
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1520
|
-
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1521
1593
|
var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1594
|
+
var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1595
|
+
var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1596
|
+
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1522
1597
|
|
|
1523
1598
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1524
|
-
var
|
|
1525
|
-
var
|
|
1599
|
+
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1600
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1526
1601
|
function MusicBrainzRecordingLink({
|
|
1527
1602
|
song,
|
|
1528
1603
|
children,
|
|
1529
1604
|
className: className2 = ""
|
|
1530
1605
|
}) {
|
|
1531
|
-
const url = (0,
|
|
1532
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1533
|
-
return /* @__PURE__ */ (0,
|
|
1606
|
+
const url = (0, import_genre_tree6.musicBrainzRecordingUrl)(song);
|
|
1607
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children });
|
|
1608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1534
1609
|
"a",
|
|
1535
1610
|
{
|
|
1536
1611
|
href: url,
|
|
@@ -1545,11 +1620,11 @@ function MusicBrainzRecordingLink({
|
|
|
1545
1620
|
}
|
|
1546
1621
|
|
|
1547
1622
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1548
|
-
var
|
|
1623
|
+
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1549
1624
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1550
|
-
var
|
|
1625
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1551
1626
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1552
|
-
var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(
|
|
1627
|
+
var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree9.SongDetailedSchema).extend({
|
|
1553
1628
|
overallTotal: import_zod.z.number().nullable(),
|
|
1554
1629
|
page: import_zod.z.number().nullable(),
|
|
1555
1630
|
totalPages: import_zod.z.number().nullable()
|
|
@@ -1576,19 +1651,20 @@ function GenrePlaylistTracks({
|
|
|
1576
1651
|
renderTracksToolbar,
|
|
1577
1652
|
artist = null
|
|
1578
1653
|
}) {
|
|
1579
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0,
|
|
1654
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0, import_genre_tree10.useTrackList)();
|
|
1580
1655
|
const { session, sessionRestored } = (0, import_auth2.useSession)();
|
|
1581
1656
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1582
1657
|
const { showPopup } = (0, import_popup5.usePopup)();
|
|
1583
1658
|
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1584
1659
|
const { uuid } = genrePlaylist;
|
|
1585
1660
|
const { fetch } = (0, import_transport5.useFetchWrapper)(getBackendBaseUrl);
|
|
1661
|
+
const playback = (0, import_transport5.usePlaybackContext)();
|
|
1586
1662
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1587
|
-
queryKey: scope === "me" ?
|
|
1663
|
+
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] : []],
|
|
1588
1664
|
queryFn: async ({ pageParam }) => {
|
|
1589
1665
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1590
1666
|
const response = await fetch(
|
|
1591
|
-
|
|
1667
|
+
import_genre_tree8.libraryEndpoints.reference.songs.list(),
|
|
1592
1668
|
true,
|
|
1593
1669
|
false,
|
|
1594
1670
|
{},
|
|
@@ -1599,7 +1675,7 @@ function GenrePlaylistTracks({
|
|
|
1599
1675
|
},
|
|
1600
1676
|
initialPageParam: scope === "me" ? 1 : "",
|
|
1601
1677
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1602
|
-
enabled: scope !== "me" || sessionRestored && !!session?.accessToken
|
|
1678
|
+
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1603
1679
|
});
|
|
1604
1680
|
const [sentinel, setSentinel] = (0, import_react11.useState)(null);
|
|
1605
1681
|
(0, import_react11.useEffect)(() => {
|
|
@@ -1619,35 +1695,35 @@ function GenrePlaylistTracks({
|
|
|
1619
1695
|
const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1620
1696
|
started.catch((error) => {
|
|
1621
1697
|
console.error("Failed to play genre playlist:", error);
|
|
1622
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1698
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
|
|
1623
1699
|
});
|
|
1624
1700
|
};
|
|
1625
|
-
return /* @__PURE__ */ (0,
|
|
1701
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { children: [
|
|
1626
1702
|
renderTracksToolbar?.(),
|
|
1627
|
-
isPending ? /* @__PURE__ */ (0,
|
|
1703
|
+
isPending ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { role: "alert", children: [
|
|
1628
1704
|
"Couldn't load ",
|
|
1629
1705
|
trackNoun.plural,
|
|
1630
1706
|
".",
|
|
1631
1707
|
" ",
|
|
1632
|
-
/* @__PURE__ */ (0,
|
|
1633
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1708
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1709
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${genrePlaylist.name} ${trackNoun.plural}`, children: [
|
|
1634
1710
|
tracks.map((track, index) => {
|
|
1635
1711
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1636
1712
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1637
1713
|
const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
|
|
1638
|
-
const text = /* @__PURE__ */ (0,
|
|
1639
|
-
/* @__PURE__ */ (0,
|
|
1640
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1641
|
-
/* @__PURE__ */ (0,
|
|
1642
|
-
track.album && /* @__PURE__ */ (0,
|
|
1714
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree9.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1716
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1717
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_genre_tree11.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1718
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1643
1719
|
] });
|
|
1644
1720
|
const leading = renderSongLeading?.(track, index);
|
|
1645
1721
|
const actions = renderSongActions?.(track);
|
|
1646
|
-
const isUnplayable = (0,
|
|
1722
|
+
const isUnplayable = (0, import_genre_tree9.isSong)(track) && (0, import_genre_tree9.songUnplayableReason)(track) !== null;
|
|
1647
1723
|
if (isUnplayable) {
|
|
1648
|
-
return /* @__PURE__ */ (0,
|
|
1724
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1649
1725
|
leading,
|
|
1650
|
-
/* @__PURE__ */ (0,
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1651
1727
|
"button",
|
|
1652
1728
|
{
|
|
1653
1729
|
type: "button",
|
|
@@ -1655,27 +1731,27 @@ function GenrePlaylistTracks({
|
|
|
1655
1731
|
"aria-disabled": "true",
|
|
1656
1732
|
"aria-label": label,
|
|
1657
1733
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1658
|
-
children: /* @__PURE__ */ (0,
|
|
1734
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1659
1735
|
}
|
|
1660
1736
|
),
|
|
1661
1737
|
text,
|
|
1662
1738
|
actions
|
|
1663
1739
|
] }, track.uuid);
|
|
1664
1740
|
}
|
|
1665
|
-
return /* @__PURE__ */ (0,
|
|
1741
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
1666
1742
|
"li",
|
|
1667
1743
|
{
|
|
1668
1744
|
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)]" : ""}`,
|
|
1669
1745
|
children: [
|
|
1670
1746
|
leading,
|
|
1671
|
-
/* @__PURE__ */ (0,
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1672
1748
|
"button",
|
|
1673
1749
|
{
|
|
1674
1750
|
type: "button",
|
|
1675
1751
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1676
1752
|
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"}`,
|
|
1677
1753
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1678
|
-
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1754
|
+
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa4.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1679
1755
|
}
|
|
1680
1756
|
),
|
|
1681
1757
|
text,
|
|
@@ -1685,7 +1761,7 @@ function GenrePlaylistTracks({
|
|
|
1685
1761
|
track.uuid
|
|
1686
1762
|
);
|
|
1687
1763
|
}),
|
|
1688
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1764
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1689
1765
|
] })
|
|
1690
1766
|
] }) });
|
|
1691
1767
|
}
|
|
@@ -1693,7 +1769,7 @@ function GenrePlaylistTracks({
|
|
|
1693
1769
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1694
1770
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
1695
1771
|
var import_si2 = require("react-icons/si");
|
|
1696
|
-
var
|
|
1772
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1697
1773
|
function GenreDetailExtras({
|
|
1698
1774
|
genreUuid,
|
|
1699
1775
|
genrePlaylist,
|
|
@@ -1710,24 +1786,24 @@ function GenreDetailExtras({
|
|
|
1710
1786
|
...trackSlots
|
|
1711
1787
|
}) {
|
|
1712
1788
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
1713
|
-
const { data: overview, isPending } = (0,
|
|
1789
|
+
const { data: overview, isPending } = (0, import_genre_tree12.useFetchGenreOverview)(
|
|
1714
1790
|
genreUuid,
|
|
1715
1791
|
scope,
|
|
1716
1792
|
getBackendBaseUrl,
|
|
1717
1793
|
criteriaOverviewSchema
|
|
1718
1794
|
);
|
|
1719
1795
|
if (isPending) {
|
|
1720
|
-
return /* @__PURE__ */ (0,
|
|
1721
|
-
/* @__PURE__ */ (0,
|
|
1796
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1722
1798
|
family
|
|
1723
1799
|
] });
|
|
1724
1800
|
}
|
|
1725
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1801
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_jsx_runtime29.Fragment, { children: family });
|
|
1726
1802
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1727
1803
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1728
|
-
return /* @__PURE__ */ (0,
|
|
1729
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1730
|
-
summarySourceUrl && /* @__PURE__ */ (0,
|
|
1804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1805
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1806
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1731
1807
|
"a",
|
|
1732
1808
|
{
|
|
1733
1809
|
href: summarySourceUrl,
|
|
@@ -1735,12 +1811,12 @@ function GenreDetailExtras({
|
|
|
1735
1811
|
rel: "noopener noreferrer",
|
|
1736
1812
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1737
1813
|
children: [
|
|
1738
|
-
/* @__PURE__ */ (0,
|
|
1814
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1739
1815
|
"Wikipedia"
|
|
1740
1816
|
]
|
|
1741
1817
|
}
|
|
1742
1818
|
),
|
|
1743
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1819
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1744
1820
|
"a",
|
|
1745
1821
|
{
|
|
1746
1822
|
href: wikidataUrl,
|
|
@@ -1748,17 +1824,17 @@ function GenreDetailExtras({
|
|
|
1748
1824
|
rel: "noopener noreferrer",
|
|
1749
1825
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1750
1826
|
children: [
|
|
1751
|
-
/* @__PURE__ */ (0,
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1752
1828
|
"Wikidata"
|
|
1753
1829
|
]
|
|
1754
1830
|
}
|
|
1755
1831
|
)
|
|
1756
1832
|
] }) }),
|
|
1757
|
-
/* @__PURE__ */ (0,
|
|
1833
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1758
1834
|
family,
|
|
1759
1835
|
renderGenreDetailExtras?.(overview),
|
|
1760
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1761
|
-
/* @__PURE__ */ (0,
|
|
1836
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1837
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1762
1838
|
GenrePlaylistTracks,
|
|
1763
1839
|
{
|
|
1764
1840
|
genreUuid,
|
|
@@ -1774,7 +1850,7 @@ function GenreDetailExtras({
|
|
|
1774
1850
|
}
|
|
1775
1851
|
|
|
1776
1852
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1777
|
-
var
|
|
1853
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1778
1854
|
var import_react12 = require("react");
|
|
1779
1855
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1780
1856
|
const { isPlaying, setIsPlaying } = (0, import_player6.usePlayer)();
|
|
@@ -1788,7 +1864,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1788
1864
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1789
1865
|
});
|
|
1790
1866
|
};
|
|
1791
|
-
return /* @__PURE__ */ (0,
|
|
1867
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
1792
1868
|
GenreActionBand,
|
|
1793
1869
|
{
|
|
1794
1870
|
...bandProps,
|
|
@@ -1809,9 +1885,9 @@ function GenreInfoPanelBody({
|
|
|
1809
1885
|
band,
|
|
1810
1886
|
extras
|
|
1811
1887
|
}) {
|
|
1812
|
-
return /* @__PURE__ */ (0,
|
|
1888
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1813
1889
|
toolbar,
|
|
1814
|
-
/* @__PURE__ */ (0,
|
|
1890
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1815
1891
|
/* @__PURE__ */ (0, import_react12.createElement)(
|
|
1816
1892
|
GenreDetailExtras,
|
|
1817
1893
|
{
|
|
@@ -1830,8 +1906,8 @@ function GenreInfoPanelBody({
|
|
|
1830
1906
|
|
|
1831
1907
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1832
1908
|
var import_react13 = require("react");
|
|
1833
|
-
var
|
|
1834
|
-
var
|
|
1909
|
+
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1910
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1835
1911
|
function ChangeParentPopup({
|
|
1836
1912
|
genrePlaylist,
|
|
1837
1913
|
genrePlaylists,
|
|
@@ -1840,9 +1916,9 @@ function ChangeParentPopup({
|
|
|
1840
1916
|
onClose,
|
|
1841
1917
|
onParentChanged
|
|
1842
1918
|
}) {
|
|
1843
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1919
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree13.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1844
1920
|
const candidates = (0, import_react13.useMemo)(
|
|
1845
|
-
() => (0,
|
|
1921
|
+
() => (0, import_genre_tree13.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1846
1922
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1847
1923
|
);
|
|
1848
1924
|
const handleSelect = (newParent) => {
|
|
@@ -1866,12 +1942,12 @@ function ChangeParentPopup({
|
|
|
1866
1942
|
window.addEventListener("keydown", handleKeyDown);
|
|
1867
1943
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1868
1944
|
}, [onClose]);
|
|
1869
|
-
return /* @__PURE__ */ (0,
|
|
1870
|
-
/* @__PURE__ */ (0,
|
|
1945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1946
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1871
1947
|
"Current parent: ",
|
|
1872
1948
|
genrePlaylist.parent?.name ?? "none"
|
|
1873
1949
|
] }),
|
|
1874
|
-
/* @__PURE__ */ (0,
|
|
1950
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1875
1951
|
GenreSearch,
|
|
1876
1952
|
{
|
|
1877
1953
|
genrePlaylists: candidates,
|
|
@@ -1879,22 +1955,22 @@ function ChangeParentPopup({
|
|
|
1879
1955
|
placeholder: "Search a new parent\u2026"
|
|
1880
1956
|
}
|
|
1881
1957
|
) }),
|
|
1882
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1958
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1883
1959
|
] });
|
|
1884
1960
|
}
|
|
1885
1961
|
|
|
1886
1962
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1887
|
-
var
|
|
1888
|
-
var
|
|
1963
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
1964
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1889
1965
|
|
|
1890
1966
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1891
|
-
var
|
|
1967
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
1892
1968
|
|
|
1893
1969
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
1894
1970
|
var import_lucide_react12 = require("lucide-react");
|
|
1895
1971
|
var import_si3 = require("react-icons/si");
|
|
1896
|
-
var
|
|
1897
|
-
var
|
|
1972
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
1973
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1898
1974
|
var LINK_KINDS = {
|
|
1899
1975
|
official: { label: "Official website", Icon: import_lucide_react12.Globe },
|
|
1900
1976
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -1931,7 +2007,7 @@ function artistLinks(artist) {
|
|
|
1931
2007
|
return links;
|
|
1932
2008
|
}
|
|
1933
2009
|
function ArtistLinks({ links }) {
|
|
1934
|
-
return /* @__PURE__ */ (0,
|
|
2010
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1935
2011
|
}
|
|
1936
2012
|
|
|
1937
2013
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1942,7 +2018,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1942
2018
|
}
|
|
1943
2019
|
|
|
1944
2020
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1945
|
-
var
|
|
2021
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1946
2022
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1947
2023
|
function ArtistHeader({ artist }) {
|
|
1948
2024
|
const fields = [];
|
|
@@ -1952,35 +2028,35 @@ function ArtistHeader({ artist }) {
|
|
|
1952
2028
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1953
2029
|
if (years) fields.push({ label: "Active", value: years });
|
|
1954
2030
|
const links = artistLinks(artist);
|
|
1955
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2031
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ArtistLinks, { links }) });
|
|
1956
2032
|
if (fields.length === 0) return null;
|
|
1957
|
-
return /* @__PURE__ */ (0,
|
|
2033
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.FieldList, { items: fields });
|
|
1958
2034
|
}
|
|
1959
2035
|
|
|
1960
2036
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1961
|
-
var
|
|
2037
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1962
2038
|
function ExternalLink({ href, children }) {
|
|
1963
|
-
return /* @__PURE__ */ (0,
|
|
2039
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1964
2040
|
}
|
|
1965
2041
|
function ArtistPhoto({ image, name }) {
|
|
1966
|
-
return /* @__PURE__ */ (0,
|
|
1967
|
-
/* @__PURE__ */ (0,
|
|
1968
|
-
/* @__PURE__ */ (0,
|
|
2042
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
2043
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
2044
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1969
2045
|
"Photo: ",
|
|
1970
|
-
/* @__PURE__ */ (0,
|
|
2046
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1971
2047
|
",",
|
|
1972
2048
|
" ",
|
|
1973
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
2049
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1974
2050
|
] })
|
|
1975
2051
|
] });
|
|
1976
2052
|
}
|
|
1977
2053
|
|
|
1978
2054
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1979
|
-
var
|
|
1980
|
-
var
|
|
2055
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
2056
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1981
2057
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1982
2058
|
if (memberships.length === 0) return null;
|
|
1983
|
-
return /* @__PURE__ */ (0,
|
|
2059
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
1984
2060
|
const details = [
|
|
1985
2061
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1986
2062
|
...membership.roles
|
|
@@ -1988,9 +2064,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1988
2064
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1989
2065
|
return (
|
|
1990
2066
|
// A member can have several periods in the same group.
|
|
1991
|
-
/* @__PURE__ */ (0,
|
|
1992
|
-
/* @__PURE__ */ (0,
|
|
1993
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2069
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1994
2070
|
" \u2014 ",
|
|
1995
2071
|
detailsText
|
|
1996
2072
|
] })
|
|
@@ -2001,9 +2077,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2001
2077
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2002
2078
|
if (memberships.length === 0) return null;
|
|
2003
2079
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2004
|
-
return /* @__PURE__ */ (0,
|
|
2005
|
-
/* @__PURE__ */ (0,
|
|
2006
|
-
/* @__PURE__ */ (0,
|
|
2080
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_ui12.PanelSection, { title, children: [
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2082
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2007
2083
|
MembershipList,
|
|
2008
2084
|
{
|
|
2009
2085
|
title: "Past",
|
|
@@ -2015,8 +2091,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2015
2091
|
}
|
|
2016
2092
|
|
|
2017
2093
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2018
|
-
var
|
|
2019
|
-
var
|
|
2094
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
2095
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2020
2096
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2021
2097
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2022
2098
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2036,12 +2112,12 @@ function byYear(a, b) {
|
|
|
2036
2112
|
function ArtistDiscography({ albums }) {
|
|
2037
2113
|
if (albums.length === 0) return null;
|
|
2038
2114
|
const sorted = [...albums].sort(byYear);
|
|
2039
|
-
return /* @__PURE__ */ (0,
|
|
2115
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2040
2116
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2041
2117
|
if (groupAlbums.length === 0) return null;
|
|
2042
|
-
return /* @__PURE__ */ (0,
|
|
2043
|
-
/* @__PURE__ */ (0,
|
|
2044
|
-
/* @__PURE__ */ (0,
|
|
2118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2119
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2120
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2045
2121
|
] }, album.uuid)) }) }, group);
|
|
2046
2122
|
}) });
|
|
2047
2123
|
}
|
|
@@ -2050,20 +2126,20 @@ function ArtistDiscography({ albums }) {
|
|
|
2050
2126
|
var import_react14 = require("react");
|
|
2051
2127
|
var import_react_query2 = require("@tanstack/react-query");
|
|
2052
2128
|
var import_lucide_react13 = require("lucide-react");
|
|
2053
|
-
var
|
|
2129
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
2054
2130
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
2055
|
-
var
|
|
2131
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2056
2132
|
var import_transport9 = require("@behindthemusictree/app-kit/transport");
|
|
2057
2133
|
|
|
2058
2134
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2059
2135
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
2060
|
-
var
|
|
2136
|
+
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2061
2137
|
|
|
2062
2138
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
2063
2139
|
var import_fa5 = require("react-icons/fa");
|
|
2064
2140
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
2065
2141
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2066
|
-
var
|
|
2142
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2067
2143
|
function TrackPositionPlayPause({
|
|
2068
2144
|
position,
|
|
2069
2145
|
uuid,
|
|
@@ -2077,14 +2153,14 @@ function TrackPositionPlayPause({
|
|
|
2077
2153
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
2078
2154
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
2079
2155
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2080
|
-
return /* @__PURE__ */ (0,
|
|
2156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2081
2157
|
"div",
|
|
2082
2158
|
{
|
|
2083
2159
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2084
2160
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2085
2161
|
onClick: handlePlayPauseClick,
|
|
2086
2162
|
children: [
|
|
2087
|
-
/* @__PURE__ */ (0,
|
|
2163
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2088
2164
|
"span",
|
|
2089
2165
|
{
|
|
2090
2166
|
className: "track-position-play-pause__bars",
|
|
@@ -2092,18 +2168,18 @@ function TrackPositionPlayPause({
|
|
|
2092
2168
|
role: "img",
|
|
2093
2169
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2094
2170
|
children: [
|
|
2095
|
-
/* @__PURE__ */ (0,
|
|
2096
|
-
/* @__PURE__ */ (0,
|
|
2097
|
-
/* @__PURE__ */ (0,
|
|
2171
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2173
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2098
2174
|
]
|
|
2099
2175
|
}
|
|
2100
2176
|
) : position }),
|
|
2101
|
-
/* @__PURE__ */ (0,
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2102
2178
|
"div",
|
|
2103
2179
|
{
|
|
2104
2180
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2105
2181
|
"aria-hidden": "true",
|
|
2106
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2182
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fa5.FaPlay, {})
|
|
2107
2183
|
}
|
|
2108
2184
|
)
|
|
2109
2185
|
]
|
|
@@ -2112,9 +2188,9 @@ function TrackPositionPlayPause({
|
|
|
2112
2188
|
}
|
|
2113
2189
|
|
|
2114
2190
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2115
|
-
var
|
|
2116
|
-
var
|
|
2117
|
-
var
|
|
2191
|
+
var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2192
|
+
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2193
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2118
2194
|
function TrackItem({
|
|
2119
2195
|
track,
|
|
2120
2196
|
position,
|
|
@@ -2124,11 +2200,11 @@ function TrackItem({
|
|
|
2124
2200
|
renderSongLink
|
|
2125
2201
|
}) {
|
|
2126
2202
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
2127
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
2203
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree14.useTrackList)();
|
|
2128
2204
|
const scope = trackList?.origin?.scope ?? null;
|
|
2129
2205
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
2130
2206
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
2131
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
2207
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
|
|
2132
2208
|
const handlePlayPauseClick = (event) => {
|
|
2133
2209
|
event.stopPropagation();
|
|
2134
2210
|
if (isCurrentTrack) {
|
|
@@ -2138,12 +2214,12 @@ function TrackItem({
|
|
|
2138
2214
|
loadTrackForPlayer(track.uuid);
|
|
2139
2215
|
}
|
|
2140
2216
|
};
|
|
2141
|
-
return /* @__PURE__ */ (0,
|
|
2217
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2142
2218
|
"div",
|
|
2143
2219
|
{
|
|
2144
2220
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2145
2221
|
children: [
|
|
2146
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2222
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2147
2223
|
"div",
|
|
2148
2224
|
{
|
|
2149
2225
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2151,7 +2227,7 @@ function TrackItem({
|
|
|
2151
2227
|
"aria-disabled": "true",
|
|
2152
2228
|
children: position
|
|
2153
2229
|
}
|
|
2154
|
-
) : /* @__PURE__ */ (0,
|
|
2230
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2155
2231
|
TrackPositionPlayPause,
|
|
2156
2232
|
{
|
|
2157
2233
|
position,
|
|
@@ -2160,12 +2236,12 @@ function TrackItem({
|
|
|
2160
2236
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2161
2237
|
}
|
|
2162
2238
|
),
|
|
2163
|
-
/* @__PURE__ */ (0,
|
|
2164
|
-
/* @__PURE__ */ (0,
|
|
2165
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2239
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2240
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.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_runtime38.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2241
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2166
2242
|
] }),
|
|
2167
|
-
/* @__PURE__ */ (0,
|
|
2168
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2243
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.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_runtime38.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
2244
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2169
2245
|
"div",
|
|
2170
2246
|
{
|
|
2171
2247
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2173,15 +2249,15 @@ function TrackItem({
|
|
|
2173
2249
|
children: renderDuration(track)
|
|
2174
2250
|
}
|
|
2175
2251
|
),
|
|
2176
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2252
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2177
2253
|
]
|
|
2178
2254
|
}
|
|
2179
2255
|
);
|
|
2180
2256
|
}
|
|
2181
2257
|
|
|
2182
2258
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2183
|
-
var
|
|
2184
|
-
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(
|
|
2259
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2260
|
+
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree17.SongDetailedSchema);
|
|
2185
2261
|
var SORTS = [
|
|
2186
2262
|
{ ordering: "title", label: "Title" },
|
|
2187
2263
|
{ ordering: "-albumYear", label: "Year" }
|
|
@@ -2190,11 +2266,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2190
2266
|
const [page, setPage] = (0, import_react14.useState)(1);
|
|
2191
2267
|
const [ordering, setOrdering] = (0, import_react14.useState)("title");
|
|
2192
2268
|
const { fetch } = (0, import_transport9.useFetchWrapper)(getBackendBaseUrl);
|
|
2193
|
-
const { playNewTrackListFromArtist } = (0,
|
|
2269
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree17.useTrackList)();
|
|
2194
2270
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
2195
2271
|
const { data, isPending, isError } = (0, import_transport9.useQueryWithParse)({
|
|
2196
2272
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
2197
|
-
queryFn: () => fetch(
|
|
2273
|
+
queryFn: () => fetch(import_genre_tree17.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
2198
2274
|
artist: artist.uuid,
|
|
2199
2275
|
ordering,
|
|
2200
2276
|
page
|
|
@@ -2209,25 +2285,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2209
2285
|
});
|
|
2210
2286
|
const handleRowClickCapture = (event, song) => {
|
|
2211
2287
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
2212
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2288
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree17.songUnplayableReason)(song) !== null) return;
|
|
2213
2289
|
event.stopPropagation();
|
|
2214
2290
|
void play(song.uuid);
|
|
2215
2291
|
};
|
|
2216
|
-
return /* @__PURE__ */ (0,
|
|
2217
|
-
/* @__PURE__ */ (0,
|
|
2218
|
-
/* @__PURE__ */ (0,
|
|
2292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2293
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2294
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2219
2295
|
"button",
|
|
2220
2296
|
{
|
|
2221
2297
|
type: "button",
|
|
2222
2298
|
onClick: () => void play(),
|
|
2223
2299
|
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",
|
|
2224
2300
|
children: [
|
|
2225
|
-
/* @__PURE__ */ (0,
|
|
2301
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react13.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2226
2302
|
"Play all"
|
|
2227
2303
|
]
|
|
2228
2304
|
}
|
|
2229
2305
|
),
|
|
2230
|
-
/* @__PURE__ */ (0,
|
|
2306
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2231
2307
|
"button",
|
|
2232
2308
|
{
|
|
2233
2309
|
type: "button",
|
|
@@ -2242,7 +2318,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2242
2318
|
sort.ordering
|
|
2243
2319
|
)) })
|
|
2244
2320
|
] }),
|
|
2245
|
-
/* @__PURE__ */ (0,
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2246
2322
|
TrackItem,
|
|
2247
2323
|
{
|
|
2248
2324
|
track: song,
|
|
@@ -2251,12 +2327,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2251
2327
|
renderSongLink
|
|
2252
2328
|
}
|
|
2253
2329
|
) }, song.uuid)) }),
|
|
2254
|
-
/* @__PURE__ */ (0,
|
|
2330
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2255
2331
|
] }) });
|
|
2256
2332
|
}
|
|
2257
2333
|
|
|
2258
2334
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2259
|
-
var
|
|
2335
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2260
2336
|
function useArtistPanel({
|
|
2261
2337
|
uuid,
|
|
2262
2338
|
scope,
|
|
@@ -2268,7 +2344,7 @@ function useArtistPanel({
|
|
|
2268
2344
|
onExpandedChange,
|
|
2269
2345
|
onClose
|
|
2270
2346
|
}) {
|
|
2271
|
-
const result = (0,
|
|
2347
|
+
const result = (0, import_genre_tree18.useArtist)(uuid, getBackendBaseUrl);
|
|
2272
2348
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2273
2349
|
switch (result.status) {
|
|
2274
2350
|
case "idle":
|
|
@@ -2277,26 +2353,26 @@ function useArtistPanel({
|
|
|
2277
2353
|
return {
|
|
2278
2354
|
...panel,
|
|
2279
2355
|
title: "Artist",
|
|
2280
|
-
content: /* @__PURE__ */ (0,
|
|
2281
|
-
/* @__PURE__ */ (0,
|
|
2282
|
-
/* @__PURE__ */ (0,
|
|
2283
|
-
/* @__PURE__ */ (0,
|
|
2284
|
-
/* @__PURE__ */ (0,
|
|
2285
|
-
/* @__PURE__ */ (0,
|
|
2286
|
-
/* @__PURE__ */ (0,
|
|
2356
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2357
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2358
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-4 w-32" }),
|
|
2359
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2360
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-4 w-24" }),
|
|
2361
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2362
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-16" })
|
|
2287
2363
|
] })
|
|
2288
2364
|
};
|
|
2289
2365
|
case "not-found":
|
|
2290
2366
|
return {
|
|
2291
2367
|
...panel,
|
|
2292
2368
|
title: "Artist not found",
|
|
2293
|
-
content: /* @__PURE__ */ (0,
|
|
2369
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2294
2370
|
};
|
|
2295
2371
|
case "error":
|
|
2296
2372
|
return {
|
|
2297
2373
|
...panel,
|
|
2298
2374
|
title: "Artist",
|
|
2299
|
-
content: /* @__PURE__ */ (0,
|
|
2375
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2300
2376
|
};
|
|
2301
2377
|
case "success": {
|
|
2302
2378
|
const { artist } = result;
|
|
@@ -2305,13 +2381,13 @@ function useArtistPanel({
|
|
|
2305
2381
|
return {
|
|
2306
2382
|
...panel,
|
|
2307
2383
|
title: artist.name,
|
|
2308
|
-
content: /* @__PURE__ */ (0,
|
|
2309
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2310
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2311
|
-
/* @__PURE__ */ (0,
|
|
2312
|
-
/* @__PURE__ */ (0,
|
|
2313
|
-
/* @__PURE__ */ (0,
|
|
2314
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2384
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
2385
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2386
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistHeader, { artist }),
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2389
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2390
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2315
2391
|
ArtistSongs,
|
|
2316
2392
|
{
|
|
2317
2393
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2321,56 +2397,56 @@ function useArtistPanel({
|
|
|
2321
2397
|
}
|
|
2322
2398
|
)
|
|
2323
2399
|
] }),
|
|
2324
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2400
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2325
2401
|
};
|
|
2326
2402
|
}
|
|
2327
2403
|
}
|
|
2328
2404
|
}
|
|
2329
2405
|
|
|
2330
2406
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2331
|
-
var
|
|
2332
|
-
var
|
|
2407
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2408
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2333
2409
|
|
|
2334
2410
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2335
2411
|
var import_react15 = require("react");
|
|
2336
2412
|
var import_lucide_react14 = require("lucide-react");
|
|
2337
|
-
var
|
|
2413
|
+
var import_ui16 = require("@behindthemusictree/ui");
|
|
2338
2414
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2339
|
-
var
|
|
2340
|
-
var
|
|
2415
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2416
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2341
2417
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2342
2418
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2343
2419
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2344
2420
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2345
2421
|
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
2346
|
-
const history = (0,
|
|
2422
|
+
const history = (0, import_genre_tree19.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2347
2423
|
let body = null;
|
|
2348
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2349
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2424
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2425
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2350
2426
|
else if (history.status === "success")
|
|
2351
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2352
|
-
/* @__PURE__ */ (0,
|
|
2353
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2427
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2428
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2429
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2354
2430
|
" ",
|
|
2355
2431
|
"\u2014 ",
|
|
2356
2432
|
entry.oldValue ?? "none",
|
|
2357
2433
|
" \u2192 ",
|
|
2358
2434
|
entry.newValue ?? "none"
|
|
2359
2435
|
] }) : null,
|
|
2360
|
-
/* @__PURE__ */ (0,
|
|
2436
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2361
2437
|
entry.actorPseudo ?? "Anonymous",
|
|
2362
2438
|
" \xB7 ",
|
|
2363
2439
|
dateFormat.format(new Date(entry.createdOn))
|
|
2364
2440
|
] })
|
|
2365
2441
|
] }, entry.uuid)) });
|
|
2366
|
-
return /* @__PURE__ */ (0,
|
|
2442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2367
2443
|
"details",
|
|
2368
2444
|
{
|
|
2369
2445
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2370
2446
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2371
2447
|
children: [
|
|
2372
|
-
/* @__PURE__ */ (0,
|
|
2373
|
-
/* @__PURE__ */ (0,
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2449
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "mt-1.5", children: body })
|
|
2374
2450
|
]
|
|
2375
2451
|
}
|
|
2376
2452
|
);
|
|
@@ -2384,25 +2460,25 @@ function SongPanelContent({
|
|
|
2384
2460
|
genrePath,
|
|
2385
2461
|
onSelectGenre
|
|
2386
2462
|
}) {
|
|
2387
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2463
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree19.useTrackList)();
|
|
2388
2464
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2389
|
-
const unplayableReason = (0,
|
|
2465
|
+
const unplayableReason = (0, import_genre_tree19.songUnplayableReason)(song);
|
|
2390
2466
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2391
2467
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2392
2468
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2393
|
-
const musicBrainzUrl = (0,
|
|
2469
|
+
const musicBrainzUrl = (0, import_genre_tree19.musicBrainzRecordingUrl)(song);
|
|
2394
2470
|
const fields = [];
|
|
2395
2471
|
if (song.genre) {
|
|
2396
2472
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2397
2473
|
fields.push({
|
|
2398
2474
|
label: "Genre",
|
|
2399
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2400
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2401
|
-
/* @__PURE__ */ (0,
|
|
2475
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
2476
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2477
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2402
2478
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2403
2479
|
});
|
|
2404
2480
|
} else {
|
|
2405
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2481
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2406
2482
|
}
|
|
2407
2483
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2408
2484
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2412,11 +2488,11 @@ function SongPanelContent({
|
|
|
2412
2488
|
const links = [];
|
|
2413
2489
|
if (musicBrainzUrl)
|
|
2414
2490
|
links.push(
|
|
2415
|
-
/* @__PURE__ */ (0,
|
|
2491
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2416
2492
|
);
|
|
2417
2493
|
if (song.youtubeVideo)
|
|
2418
2494
|
links.push(
|
|
2419
|
-
/* @__PURE__ */ (0,
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2420
2496
|
"a",
|
|
2421
2497
|
{
|
|
2422
2498
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2431,33 +2507,33 @@ function SongPanelContent({
|
|
|
2431
2507
|
if (links.length > 0)
|
|
2432
2508
|
fields.push({
|
|
2433
2509
|
label: "Links",
|
|
2434
|
-
value: /* @__PURE__ */ (0,
|
|
2510
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex gap-3", children: links })
|
|
2435
2511
|
});
|
|
2436
|
-
return /* @__PURE__ */ (0,
|
|
2437
|
-
/* @__PURE__ */ (0,
|
|
2438
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2514
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2439
2515
|
"button",
|
|
2440
2516
|
{
|
|
2441
2517
|
type: "button",
|
|
2442
2518
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2443
2519
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2444
2520
|
children: [
|
|
2445
|
-
/* @__PURE__ */ (0,
|
|
2521
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.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_runtime41.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2446
2522
|
isPlaying ? "Pause" : "Play"
|
|
2447
2523
|
]
|
|
2448
2524
|
}
|
|
2449
2525
|
),
|
|
2450
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2526
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2451
2527
|
] }) }),
|
|
2452
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2453
|
-
/* @__PURE__ */ (0,
|
|
2454
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2455
|
-
/* @__PURE__ */ (0,
|
|
2528
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_genre_tree19.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2529
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.FieldList, { items: fields }),
|
|
2530
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2531
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2456
2532
|
] });
|
|
2457
2533
|
}
|
|
2458
2534
|
|
|
2459
2535
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2460
|
-
var
|
|
2536
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2461
2537
|
function useSongPanel({
|
|
2462
2538
|
uuid,
|
|
2463
2539
|
scope,
|
|
@@ -2470,7 +2546,7 @@ function useSongPanel({
|
|
|
2470
2546
|
onExpandedChange,
|
|
2471
2547
|
onClose
|
|
2472
2548
|
}) {
|
|
2473
|
-
const result = (0,
|
|
2549
|
+
const result = (0, import_genre_tree20.useSong)(uuid, getBackendBaseUrl);
|
|
2474
2550
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2475
2551
|
switch (result.status) {
|
|
2476
2552
|
case "idle":
|
|
@@ -2479,24 +2555,24 @@ function useSongPanel({
|
|
|
2479
2555
|
return {
|
|
2480
2556
|
...panel,
|
|
2481
2557
|
title: "Song",
|
|
2482
|
-
content: /* @__PURE__ */ (0,
|
|
2483
|
-
/* @__PURE__ */ (0,
|
|
2484
|
-
/* @__PURE__ */ (0,
|
|
2485
|
-
/* @__PURE__ */ (0,
|
|
2486
|
-
/* @__PURE__ */ (0,
|
|
2558
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2562
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" })
|
|
2487
2563
|
] })
|
|
2488
2564
|
};
|
|
2489
2565
|
case "not-found":
|
|
2490
2566
|
return {
|
|
2491
2567
|
...panel,
|
|
2492
2568
|
title: "Song not found",
|
|
2493
|
-
content: /* @__PURE__ */ (0,
|
|
2569
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2494
2570
|
};
|
|
2495
2571
|
case "error":
|
|
2496
2572
|
return {
|
|
2497
2573
|
...panel,
|
|
2498
2574
|
title: "Song",
|
|
2499
|
-
content: /* @__PURE__ */ (0,
|
|
2575
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2500
2576
|
};
|
|
2501
2577
|
case "success": {
|
|
2502
2578
|
const { song } = result;
|
|
@@ -2504,7 +2580,7 @@ function useSongPanel({
|
|
|
2504
2580
|
return {
|
|
2505
2581
|
...panel,
|
|
2506
2582
|
title: song.title,
|
|
2507
|
-
content: /* @__PURE__ */ (0,
|
|
2583
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2508
2584
|
SongPanelContent,
|
|
2509
2585
|
{
|
|
2510
2586
|
song,
|
|
@@ -2523,29 +2599,29 @@ function useSongPanel({
|
|
|
2523
2599
|
|
|
2524
2600
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2525
2601
|
var import_md2 = require("react-icons/md");
|
|
2526
|
-
var
|
|
2527
|
-
var
|
|
2602
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
2603
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2528
2604
|
function SizeMetricToggle({
|
|
2529
2605
|
value,
|
|
2530
2606
|
onChange
|
|
2531
2607
|
}) {
|
|
2532
|
-
return /* @__PURE__ */ (0,
|
|
2533
|
-
|
|
2608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2609
|
+
import_ui18.SegmentedControl,
|
|
2534
2610
|
{
|
|
2535
2611
|
variant: "floating",
|
|
2536
2612
|
"aria-label": "Size nodes by",
|
|
2537
2613
|
value,
|
|
2538
2614
|
onChange,
|
|
2539
2615
|
options: [
|
|
2540
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2541
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2616
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2617
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2542
2618
|
]
|
|
2543
2619
|
}
|
|
2544
2620
|
);
|
|
2545
2621
|
}
|
|
2546
2622
|
|
|
2547
2623
|
// src/genre-tree/GenreTreeView.tsx
|
|
2548
|
-
var
|
|
2624
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2549
2625
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2550
2626
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2551
2627
|
var noop = () => {
|
|
@@ -2579,6 +2655,7 @@ function GenreTreeView({
|
|
|
2579
2655
|
artistExpanded: controlledArtistExpanded,
|
|
2580
2656
|
onArtistExpandedChange,
|
|
2581
2657
|
getShareUrl,
|
|
2658
|
+
extraSearchGroups = [],
|
|
2582
2659
|
artistSearch = true,
|
|
2583
2660
|
onArtistSelect,
|
|
2584
2661
|
artistSearchBaseUrl,
|
|
@@ -2644,7 +2721,7 @@ function GenreTreeView({
|
|
|
2644
2721
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2645
2722
|
toggleWheelRotation,
|
|
2646
2723
|
onNodeHover: handleNodeHover
|
|
2647
|
-
} = (0,
|
|
2724
|
+
} = (0, import_genre_tree21.useGenreTreeViewState)({
|
|
2648
2725
|
scope,
|
|
2649
2726
|
getBackendBaseUrl,
|
|
2650
2727
|
viewMode: controlledViewMode,
|
|
@@ -2663,12 +2740,12 @@ function GenreTreeView({
|
|
|
2663
2740
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2664
2741
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2665
2742
|
}
|
|
2666
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2667
|
-
const { mutate: updateGenre } = (0,
|
|
2743
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree21.genreUpdateMutationKey }) > 0;
|
|
2744
|
+
const { mutate: updateGenre } = (0, import_genre_tree21.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2668
2745
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2669
2746
|
const isArtistFiltered = artistUuid !== null;
|
|
2670
2747
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2671
|
-
const artistResult = (0,
|
|
2748
|
+
const artistResult = (0, import_genre_tree21.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2672
2749
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2673
2750
|
const filterArtist = (0, import_react16.useMemo)(
|
|
2674
2751
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -2688,7 +2765,7 @@ function GenreTreeView({
|
|
|
2688
2765
|
const openSongRef = (0, import_react16.useRef)(openSong);
|
|
2689
2766
|
openSongRef.current = openSong;
|
|
2690
2767
|
const resolvedRenderSongLink = (0, import_react16.useCallback)(
|
|
2691
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2768
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2692
2769
|
[renderSongLink]
|
|
2693
2770
|
);
|
|
2694
2771
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react16.useState)(null);
|
|
@@ -2700,7 +2777,7 @@ function GenreTreeView({
|
|
|
2700
2777
|
scope,
|
|
2701
2778
|
getBackendBaseUrl: artistBaseUrl,
|
|
2702
2779
|
getShareUrl: getShareUrlRef.current,
|
|
2703
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2780
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2704
2781
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2705
2782
|
onExpandedChange: (on) => {
|
|
2706
2783
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2709,7 +2786,7 @@ function GenreTreeView({
|
|
|
2709
2786
|
renderSongLink: resolvedRenderSongLink,
|
|
2710
2787
|
onClose: () => changeArtist(null)
|
|
2711
2788
|
});
|
|
2712
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2789
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2713
2790
|
const getGenrePath = (genre) => {
|
|
2714
2791
|
const path = [];
|
|
2715
2792
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2765,7 +2842,7 @@ function GenreTreeView({
|
|
|
2765
2842
|
[]
|
|
2766
2843
|
);
|
|
2767
2844
|
const panelRef = (0, import_react16.useRef)(null);
|
|
2768
|
-
const obscuredInset = (0,
|
|
2845
|
+
const obscuredInset = (0, import_ui19.useObscuredInset)(panelRef);
|
|
2769
2846
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2770
2847
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2771
2848
|
const treeNodes = (0, import_react16.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
@@ -2788,52 +2865,27 @@ function GenreTreeView({
|
|
|
2788
2865
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2789
2866
|
const { showPopup, hidePopup } = (0, import_popup6.usePopup)();
|
|
2790
2867
|
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2791
|
-
const
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
onArtistSelect?.(artist);
|
|
2813
|
-
}
|
|
2814
|
-
})),
|
|
2815
|
-
status: artistSearchResult.isError ? "Search failed" : artistSearchResult.settled && artistSearchResult.artists.length === 0 ? "No artists found" : "",
|
|
2816
|
-
loading: artistSearchResult.pending
|
|
2817
|
-
} : void 0;
|
|
2818
|
-
const songSearchResult = (0, import_genre_tree18.useSongSearch)(songBaseUrl, songSearch ? searchText : "");
|
|
2819
|
-
const songGroup = songSearch ? {
|
|
2820
|
-
key: "songs",
|
|
2821
|
-
label: "Songs",
|
|
2822
|
-
items: songSearchResult.songs.map((song) => ({
|
|
2823
|
-
key: song.uuid,
|
|
2824
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: (0, import_genre_tree18.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
2825
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
2826
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.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 ") })
|
|
2827
|
-
] }),
|
|
2828
|
-
onSelect: () => {
|
|
2829
|
-
openSong(song.uuid);
|
|
2830
|
-
onSongSelect?.(song);
|
|
2831
|
-
}
|
|
2832
|
-
})),
|
|
2833
|
-
status: songSearchResult.isError ? "Search failed" : songSearchResult.settled && songSearchResult.songs.length === 0 ? "No songs found" : "",
|
|
2834
|
-
loading: songSearchResult.pending
|
|
2835
|
-
} : void 0;
|
|
2836
|
-
const extraGroups = [artistGroup, songGroup].filter((group) => group !== void 0);
|
|
2868
|
+
const artistGroup = useArtistSearchGroup(
|
|
2869
|
+
artistBaseUrl,
|
|
2870
|
+
searchText,
|
|
2871
|
+
(artist) => {
|
|
2872
|
+
openArtist(artist.uuid);
|
|
2873
|
+
onArtistSelect?.(artist);
|
|
2874
|
+
},
|
|
2875
|
+
Boolean(artistSearch)
|
|
2876
|
+
);
|
|
2877
|
+
const songGroup = useSongSearchGroup(
|
|
2878
|
+
songBaseUrl,
|
|
2879
|
+
searchText,
|
|
2880
|
+
(song) => {
|
|
2881
|
+
openSong(song.uuid);
|
|
2882
|
+
onSongSelect?.(song);
|
|
2883
|
+
},
|
|
2884
|
+
Boolean(songSearch)
|
|
2885
|
+
);
|
|
2886
|
+
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2887
|
+
(group) => group !== void 0
|
|
2888
|
+
);
|
|
2837
2889
|
const handleSelectedNodeChange = (node) => {
|
|
2838
2890
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2839
2891
|
selectNode(node);
|
|
@@ -2857,7 +2909,7 @@ function GenreTreeView({
|
|
|
2857
2909
|
};
|
|
2858
2910
|
const renderGenreBody = (relation) => {
|
|
2859
2911
|
const { node } = relation;
|
|
2860
|
-
const family = /* @__PURE__ */ (0,
|
|
2912
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2861
2913
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2862
2914
|
{
|
|
2863
2915
|
node,
|
|
@@ -2868,10 +2920,10 @@ function GenreTreeView({
|
|
|
2868
2920
|
onSelectNode: selectGenreFromPanel
|
|
2869
2921
|
}
|
|
2870
2922
|
);
|
|
2871
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2923
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2872
2924
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2873
2925
|
const criteria = genrePlaylist?.criteria;
|
|
2874
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0,
|
|
2926
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
2875
2927
|
family,
|
|
2876
2928
|
ancestors
|
|
2877
2929
|
] }) };
|
|
@@ -2891,11 +2943,11 @@ function GenreTreeView({
|
|
|
2891
2943
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2892
2944
|
) : void 0;
|
|
2893
2945
|
return {
|
|
2894
|
-
primary: /* @__PURE__ */ (0,
|
|
2895
|
-
/* @__PURE__ */ (0,
|
|
2946
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
2947
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2896
2948
|
GenreInfoPanelBody,
|
|
2897
2949
|
{
|
|
2898
|
-
family: /* @__PURE__ */ (0,
|
|
2950
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
2899
2951
|
family,
|
|
2900
2952
|
ancestors
|
|
2901
2953
|
] }),
|
|
@@ -2913,7 +2965,7 @@ function GenreTreeView({
|
|
|
2913
2965
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2914
2966
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2915
2967
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2916
|
-
/* @__PURE__ */ (0,
|
|
2968
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2917
2969
|
ChangeParentPopup,
|
|
2918
2970
|
{
|
|
2919
2971
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2988,19 +3040,19 @@ function GenreTreeView({
|
|
|
2988
3040
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2989
3041
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2990
3042
|
} : void 0;
|
|
2991
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
2992
|
-
|
|
3043
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3044
|
+
import_ui19.IconButton,
|
|
2993
3045
|
{
|
|
2994
3046
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2995
3047
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2996
3048
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2997
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3049
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react16.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react16.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2998
3050
|
}
|
|
2999
3051
|
) : null;
|
|
3000
3052
|
const renderArtistFilter = () => {
|
|
3001
3053
|
if (isLoading || !filterArtist) return null;
|
|
3002
|
-
return /* @__PURE__ */ (0,
|
|
3003
|
-
/* @__PURE__ */ (0,
|
|
3054
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
3055
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3004
3056
|
"button",
|
|
3005
3057
|
{
|
|
3006
3058
|
type: "button",
|
|
@@ -3012,7 +3064,7 @@ function GenreTreeView({
|
|
|
3012
3064
|
children: filterArtist.name
|
|
3013
3065
|
}
|
|
3014
3066
|
),
|
|
3015
|
-
/* @__PURE__ */ (0,
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3016
3068
|
"button",
|
|
3017
3069
|
{
|
|
3018
3070
|
type: "button",
|
|
@@ -3024,8 +3076,8 @@ function GenreTreeView({
|
|
|
3024
3076
|
)
|
|
3025
3077
|
] });
|
|
3026
3078
|
};
|
|
3027
|
-
const actions = /* @__PURE__ */ (0,
|
|
3028
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
3079
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
3080
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3029
3081
|
GenreSearch,
|
|
3030
3082
|
{
|
|
3031
3083
|
genrePlaylists: visibleGenrePlaylists,
|
|
@@ -3033,14 +3085,14 @@ function GenreTreeView({
|
|
|
3033
3085
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
3034
3086
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
3035
3087
|
onPrevious: handlePrevious,
|
|
3036
|
-
onQueryChange: artistSearch || songSearch ? setSearchText : void 0,
|
|
3088
|
+
onQueryChange: artistSearch || songSearch || extraSearchGroups.length > 0 ? setSearchText : void 0,
|
|
3037
3089
|
extraGroups
|
|
3038
3090
|
}
|
|
3039
3091
|
),
|
|
3040
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3041
|
-
/* @__PURE__ */ (0,
|
|
3042
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
3043
|
-
|
|
3092
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3093
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.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: [
|
|
3094
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3095
|
+
import_ui19.SegmentedControl,
|
|
3044
3096
|
{
|
|
3045
3097
|
"aria-label": "Tree view mode",
|
|
3046
3098
|
value: viewMode,
|
|
@@ -3053,14 +3105,14 @@ function GenreTreeView({
|
|
|
3053
3105
|
]
|
|
3054
3106
|
}
|
|
3055
3107
|
),
|
|
3056
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3108
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3057
3109
|
"div",
|
|
3058
3110
|
{
|
|
3059
3111
|
className: "flex items-center gap-1",
|
|
3060
3112
|
role: "group",
|
|
3061
3113
|
"aria-label": "Tree display options",
|
|
3062
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3063
|
-
|
|
3114
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3115
|
+
import_ui19.Button,
|
|
3064
3116
|
{
|
|
3065
3117
|
variant: allowWheelRotation ? "default" : "outline",
|
|
3066
3118
|
size: "sm",
|
|
@@ -3070,8 +3122,8 @@ function GenreTreeView({
|
|
|
3070
3122
|
)
|
|
3071
3123
|
}
|
|
3072
3124
|
),
|
|
3073
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3074
|
-
|
|
3125
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3126
|
+
import_ui19.IconTextButton,
|
|
3075
3127
|
{
|
|
3076
3128
|
icon: import_lucide_react15.Plus,
|
|
3077
3129
|
text: "Add root",
|
|
@@ -3080,34 +3132,34 @@ function GenreTreeView({
|
|
|
3080
3132
|
)
|
|
3081
3133
|
] })
|
|
3082
3134
|
] });
|
|
3083
|
-
return /* @__PURE__ */ (0,
|
|
3135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
3084
3136
|
"div",
|
|
3085
3137
|
{
|
|
3086
3138
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3087
3139
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3088
3140
|
children: [
|
|
3089
|
-
/* @__PURE__ */ (0,
|
|
3141
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3090
3142
|
"div",
|
|
3091
3143
|
{
|
|
3092
3144
|
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"}`,
|
|
3093
3145
|
children: actions
|
|
3094
3146
|
}
|
|
3095
3147
|
),
|
|
3096
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3148
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3097
3149
|
"div",
|
|
3098
3150
|
{
|
|
3099
3151
|
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"}`,
|
|
3100
3152
|
children: renderArtistFilter()
|
|
3101
3153
|
}
|
|
3102
3154
|
),
|
|
3103
|
-
/* @__PURE__ */ (0,
|
|
3104
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
3105
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3155
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3156
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3157
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
3106
3158
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3107
3159
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3108
3160
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3109
3161
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3110
|
-
/* @__PURE__ */ (0,
|
|
3162
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3111
3163
|
GenrePlaylistTreeWheel,
|
|
3112
3164
|
{
|
|
3113
3165
|
scope,
|
|
@@ -3124,9 +3176,9 @@ function GenreTreeView({
|
|
|
3124
3176
|
allowWheelRotation,
|
|
3125
3177
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3126
3178
|
busy,
|
|
3127
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3179
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3128
3180
|
floatingControlsLeading,
|
|
3129
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3181
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3130
3182
|
] }) : void 0,
|
|
3131
3183
|
defaultViewport,
|
|
3132
3184
|
viewportRestore,
|
|
@@ -3135,7 +3187,7 @@ function GenreTreeView({
|
|
|
3135
3187
|
) }) })
|
|
3136
3188
|
) : viewMode === "pop-core" ? (
|
|
3137
3189
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3138
|
-
/* @__PURE__ */ (0,
|
|
3190
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3139
3191
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3140
3192
|
{
|
|
3141
3193
|
scope,
|
|
@@ -3152,9 +3204,9 @@ function GenreTreeView({
|
|
|
3152
3204
|
allowWheelRotation,
|
|
3153
3205
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3154
3206
|
busy,
|
|
3155
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3207
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3156
3208
|
floatingControlsLeading,
|
|
3157
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3209
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3158
3210
|
] }) : void 0,
|
|
3159
3211
|
defaultViewport,
|
|
3160
3212
|
viewportRestore,
|
|
@@ -3162,7 +3214,7 @@ function GenreTreeView({
|
|
|
3162
3214
|
...focus
|
|
3163
3215
|
}
|
|
3164
3216
|
) }) })
|
|
3165
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3217
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3166
3218
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3167
3219
|
{
|
|
3168
3220
|
outline: true,
|
|
@@ -3178,13 +3230,13 @@ function GenreTreeView({
|
|
|
3178
3230
|
selectedNodeId,
|
|
3179
3231
|
readOnly
|
|
3180
3232
|
}
|
|
3181
|
-
) }) : /* @__PURE__ */ (0,
|
|
3233
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime44.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(
|
|
3182
3234
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3183
|
-
return /* @__PURE__ */ (0,
|
|
3235
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3184
3236
|
"div",
|
|
3185
3237
|
{
|
|
3186
3238
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3187
|
-
children: /* @__PURE__ */ (0,
|
|
3239
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3188
3240
|
GenrePlaylistTreePerRoot,
|
|
3189
3241
|
{
|
|
3190
3242
|
scope,
|
|
@@ -3210,8 +3262,8 @@ function GenreTreeView({
|
|
|
3210
3262
|
);
|
|
3211
3263
|
}
|
|
3212
3264
|
) }),
|
|
3213
|
-
/* @__PURE__ */ (0,
|
|
3214
|
-
|
|
3265
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3266
|
+
import_ui19.DetailPanel,
|
|
3215
3267
|
{
|
|
3216
3268
|
ref: panelRef,
|
|
3217
3269
|
side: "left",
|
|
@@ -3227,7 +3279,7 @@ function GenreTreeView({
|
|
|
3227
3279
|
aside: activePanel?.aside,
|
|
3228
3280
|
edgeTab: detailTab,
|
|
3229
3281
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
3230
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3282
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
3231
3283
|
activePanel.content,
|
|
3232
3284
|
activePanel.aside
|
|
3233
3285
|
] }) : activePanel?.content
|
|
@@ -3242,10 +3294,10 @@ function GenreTreeView({
|
|
|
3242
3294
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
3243
3295
|
var import_react17 = require("react");
|
|
3244
3296
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3245
|
-
var
|
|
3297
|
+
var import_ui20 = require("@behindthemusictree/ui");
|
|
3246
3298
|
var import_lucide_react17 = require("lucide-react");
|
|
3247
|
-
var
|
|
3248
|
-
var
|
|
3299
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3300
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
3249
3301
|
var RELATIVE_ITEM_COUNT = 1;
|
|
3250
3302
|
function toNode(genre, parentId) {
|
|
3251
3303
|
return {
|
|
@@ -3272,7 +3324,7 @@ function GenreDetailPanel({
|
|
|
3272
3324
|
edgeTab,
|
|
3273
3325
|
extras
|
|
3274
3326
|
}) {
|
|
3275
|
-
const { data: detail, isPending, isError } = (0,
|
|
3327
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree22.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3276
3328
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3277
3329
|
const view = (0, import_react17.useMemo)(() => {
|
|
3278
3330
|
if (!detail) return null;
|
|
@@ -3300,17 +3352,17 @@ function GenreDetailPanel({
|
|
|
3300
3352
|
};
|
|
3301
3353
|
}, [detail]);
|
|
3302
3354
|
if (isError) {
|
|
3303
|
-
return /* @__PURE__ */ (0,
|
|
3355
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3304
3356
|
}
|
|
3305
3357
|
if (isPending || !detail || !view) {
|
|
3306
|
-
return /* @__PURE__ */ (0,
|
|
3307
|
-
/* @__PURE__ */ (0,
|
|
3308
|
-
/* @__PURE__ */ (0,
|
|
3309
|
-
/* @__PURE__ */ (0,
|
|
3358
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3359
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui20.Skeleton, { className: "h-6 w-2/3" }),
|
|
3360
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui20.Skeleton, { className: "h-4 w-full" }),
|
|
3361
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui20.Skeleton, { className: "h-4 w-3/4" })
|
|
3310
3362
|
] });
|
|
3311
3363
|
}
|
|
3312
3364
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3313
|
-
const family = /* @__PURE__ */ (0,
|
|
3365
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3314
3366
|
import_genre_tree_view5.GenreNodeFamily,
|
|
3315
3367
|
{
|
|
3316
3368
|
node: view.node,
|
|
@@ -3321,20 +3373,20 @@ function GenreDetailPanel({
|
|
|
3321
3373
|
onSelectNode: selectGenre
|
|
3322
3374
|
}
|
|
3323
3375
|
);
|
|
3324
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
3325
|
-
return /* @__PURE__ */ (0,
|
|
3326
|
-
|
|
3376
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3377
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
3378
|
+
import_ui20.DetailPanel,
|
|
3327
3379
|
{
|
|
3328
3380
|
side: "left",
|
|
3329
3381
|
title: view.node.name,
|
|
3330
3382
|
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3331
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
3332
|
-
|
|
3383
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3384
|
+
import_ui20.IconButton,
|
|
3333
3385
|
{
|
|
3334
3386
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3335
3387
|
style: { color: "inherit" },
|
|
3336
3388
|
onClick: () => onExpandedChange(!expanded),
|
|
3337
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
3389
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react17.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react17.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3338
3390
|
}
|
|
3339
3391
|
) : void 0,
|
|
3340
3392
|
onClose,
|
|
@@ -3343,10 +3395,10 @@ function GenreDetailPanel({
|
|
|
3343
3395
|
onExpandedChange,
|
|
3344
3396
|
edgeTab,
|
|
3345
3397
|
children: [
|
|
3346
|
-
/* @__PURE__ */ (0,
|
|
3398
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3347
3399
|
GenreInfoPanelBody,
|
|
3348
3400
|
{
|
|
3349
|
-
family: /* @__PURE__ */ (0,
|
|
3401
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
3350
3402
|
family,
|
|
3351
3403
|
ancestors
|
|
3352
3404
|
] }),
|
|
@@ -3372,10 +3424,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3372
3424
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3373
3425
|
var import_react18 = require("react");
|
|
3374
3426
|
var import_lucide_react18 = require("lucide-react");
|
|
3375
|
-
var
|
|
3376
|
-
var
|
|
3427
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3428
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3377
3429
|
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
3378
|
-
var
|
|
3430
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
3379
3431
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
|
|
3380
3432
|
function TrackListSidebar({
|
|
3381
3433
|
className: className2 = "",
|
|
@@ -3388,7 +3440,7 @@ function TrackListSidebar({
|
|
|
3388
3440
|
renderArtistLink,
|
|
3389
3441
|
renderSongLink
|
|
3390
3442
|
}) {
|
|
3391
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3443
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree23.useTrackList)();
|
|
3392
3444
|
const trackNoun = (0, import_track_noun4.useTrackNoun)();
|
|
3393
3445
|
const trackListRef = (0, import_react18.useRef)(null);
|
|
3394
3446
|
const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
|
|
@@ -3477,25 +3529,25 @@ function TrackListSidebar({
|
|
|
3477
3529
|
}
|
|
3478
3530
|
};
|
|
3479
3531
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3480
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3532
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3481
3533
|
"div",
|
|
3482
3534
|
{
|
|
3483
3535
|
"data-theme": "dark",
|
|
3484
3536
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3485
3537
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3486
3538
|
children: [
|
|
3487
|
-
/* @__PURE__ */ (0,
|
|
3488
|
-
/* @__PURE__ */ (0,
|
|
3489
|
-
/* @__PURE__ */ (0,
|
|
3490
|
-
/* @__PURE__ */ (0,
|
|
3491
|
-
/* @__PURE__ */ (0,
|
|
3539
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3540
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3541
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3542
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3543
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.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_runtime46.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3492
3544
|
] }),
|
|
3493
|
-
/* @__PURE__ */ (0,
|
|
3494
|
-
trackList.origin.type ===
|
|
3545
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3546
|
+
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 `,
|
|
3495
3547
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3496
3548
|
] })
|
|
3497
3549
|
] }),
|
|
3498
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3550
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3499
3551
|
"button",
|
|
3500
3552
|
{
|
|
3501
3553
|
type: "button",
|
|
@@ -3504,11 +3556,11 @@ function TrackListSidebar({
|
|
|
3504
3556
|
"aria-expanded": !isCollapsed,
|
|
3505
3557
|
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"}`,
|
|
3506
3558
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3507
|
-
children: /* @__PURE__ */ (0,
|
|
3559
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react18.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3508
3560
|
}
|
|
3509
3561
|
)
|
|
3510
3562
|
] }),
|
|
3511
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3563
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3512
3564
|
"ul",
|
|
3513
3565
|
{
|
|
3514
3566
|
ref: trackListRef,
|
|
@@ -3518,7 +3570,7 @@ function TrackListSidebar({
|
|
|
3518
3570
|
onPointerDown: markUserScrollIntent,
|
|
3519
3571
|
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",
|
|
3520
3572
|
children: [
|
|
3521
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3573
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3522
3574
|
TrackItem,
|
|
3523
3575
|
{
|
|
3524
3576
|
track,
|
|
@@ -3529,7 +3581,7 @@ function TrackListSidebar({
|
|
|
3529
3581
|
renderSongLink
|
|
3530
3582
|
}
|
|
3531
3583
|
) }, track.uuid)),
|
|
3532
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3584
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3533
3585
|
]
|
|
3534
3586
|
}
|
|
3535
3587
|
)
|
|
@@ -3542,14 +3594,14 @@ function TrackListSidebar({
|
|
|
3542
3594
|
var import_react19 = require("react");
|
|
3543
3595
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3544
3596
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3545
|
-
var
|
|
3546
|
-
var
|
|
3597
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3598
|
+
var import_ui21 = require("@behindthemusictree/ui");
|
|
3547
3599
|
|
|
3548
3600
|
// src/genre-tree/PlayerControls.tsx
|
|
3549
3601
|
var import_lucide_react19 = require("lucide-react");
|
|
3550
|
-
var
|
|
3602
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3551
3603
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3552
|
-
var
|
|
3604
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
3553
3605
|
function PlayerControls() {
|
|
3554
3606
|
const {
|
|
3555
3607
|
duration,
|
|
@@ -3561,22 +3613,22 @@ function PlayerControls() {
|
|
|
3561
3613
|
setVolume,
|
|
3562
3614
|
volume
|
|
3563
3615
|
} = (0, import_player12.usePlayer)();
|
|
3564
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3616
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree25.useTrackList)();
|
|
3565
3617
|
const currentTime = (0, import_player12.useCurrentTime)();
|
|
3566
3618
|
const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
|
|
3567
3619
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3568
3620
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3569
3621
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3570
3622
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3571
|
-
return /* @__PURE__ */ (0,
|
|
3623
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3572
3624
|
"section",
|
|
3573
3625
|
{
|
|
3574
3626
|
"aria-label": "Player controls",
|
|
3575
3627
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3576
3628
|
children: [
|
|
3577
|
-
/* @__PURE__ */ (0,
|
|
3578
|
-
/* @__PURE__ */ (0,
|
|
3579
|
-
/* @__PURE__ */ (0,
|
|
3629
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3630
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree25.formatTime)(safeCurrentTime) }),
|
|
3631
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3580
3632
|
"input",
|
|
3581
3633
|
{
|
|
3582
3634
|
"aria-label": "Seek",
|
|
@@ -3590,11 +3642,11 @@ function PlayerControls() {
|
|
|
3590
3642
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3591
3643
|
}
|
|
3592
3644
|
),
|
|
3593
|
-
/* @__PURE__ */ (0,
|
|
3645
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree25.formatTime)(safeDuration) })
|
|
3594
3646
|
] }),
|
|
3595
|
-
/* @__PURE__ */ (0,
|
|
3596
|
-
/* @__PURE__ */ (0,
|
|
3597
|
-
/* @__PURE__ */ (0,
|
|
3647
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3648
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3649
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3598
3650
|
"button",
|
|
3599
3651
|
{
|
|
3600
3652
|
type: "button",
|
|
@@ -3603,10 +3655,10 @@ function PlayerControls() {
|
|
|
3603
3655
|
disabled: !isReady || !hasPreviousTrack,
|
|
3604
3656
|
onClick: playPreviousTrack,
|
|
3605
3657
|
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",
|
|
3606
|
-
children: /* @__PURE__ */ (0,
|
|
3658
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3607
3659
|
}
|
|
3608
3660
|
),
|
|
3609
|
-
/* @__PURE__ */ (0,
|
|
3661
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3610
3662
|
"button",
|
|
3611
3663
|
{
|
|
3612
3664
|
type: "button",
|
|
@@ -3615,10 +3667,10 @@ function PlayerControls() {
|
|
|
3615
3667
|
disabled: !isReady,
|
|
3616
3668
|
onClick: handlePlayPauseAction,
|
|
3617
3669
|
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",
|
|
3618
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3670
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3619
3671
|
}
|
|
3620
3672
|
),
|
|
3621
|
-
/* @__PURE__ */ (0,
|
|
3673
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3622
3674
|
"button",
|
|
3623
3675
|
{
|
|
3624
3676
|
type: "button",
|
|
@@ -3627,14 +3679,14 @@ function PlayerControls() {
|
|
|
3627
3679
|
disabled: !isReady || !hasNextTrack,
|
|
3628
3680
|
onClick: playNextTrack,
|
|
3629
3681
|
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",
|
|
3630
|
-
children: /* @__PURE__ */ (0,
|
|
3682
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3631
3683
|
}
|
|
3632
3684
|
)
|
|
3633
3685
|
] }),
|
|
3634
|
-
/* @__PURE__ */ (0,
|
|
3635
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3636
|
-
/* @__PURE__ */ (0,
|
|
3637
|
-
/* @__PURE__ */ (0,
|
|
3686
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.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: [
|
|
3687
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3688
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3689
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3638
3690
|
"input",
|
|
3639
3691
|
{
|
|
3640
3692
|
"aria-label": "Volume",
|
|
@@ -3656,7 +3708,7 @@ function PlayerControls() {
|
|
|
3656
3708
|
}
|
|
3657
3709
|
|
|
3658
3710
|
// src/genre-tree/PlayerPanel.tsx
|
|
3659
|
-
var
|
|
3711
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
3660
3712
|
var PANEL_ID = "player-panel";
|
|
3661
3713
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3662
3714
|
function PlayerPanel({
|
|
@@ -3668,8 +3720,8 @@ function PlayerPanel({
|
|
|
3668
3720
|
renderArtistLink,
|
|
3669
3721
|
renderSongLink
|
|
3670
3722
|
}) {
|
|
3671
|
-
const { playerTrackObject } = (0, import_player13.usePlayer)();
|
|
3672
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3723
|
+
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3724
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree26.useTrackListSidebarVisibility)();
|
|
3673
3725
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3674
3726
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3675
3727
|
(0, import_react19.useEffect)(() => {
|
|
@@ -3696,7 +3748,7 @@ function PlayerPanel({
|
|
|
3696
3748
|
onClick: () => setIsExtendRequested(true),
|
|
3697
3749
|
expanded: false
|
|
3698
3750
|
} : null;
|
|
3699
|
-
return /* @__PURE__ */ (0,
|
|
3751
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3700
3752
|
"div",
|
|
3701
3753
|
{
|
|
3702
3754
|
id: PANEL_ID,
|
|
@@ -3709,8 +3761,8 @@ function PlayerPanel({
|
|
|
3709
3761
|
),
|
|
3710
3762
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3711
3763
|
children: [
|
|
3712
|
-
/* @__PURE__ */ (0,
|
|
3713
|
-
|
|
3764
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3765
|
+
import_ui21.PanelEdgeTab,
|
|
3714
3766
|
{
|
|
3715
3767
|
side: "right",
|
|
3716
3768
|
edge: tabEdge,
|
|
@@ -3719,7 +3771,7 @@ function PlayerPanel({
|
|
|
3719
3771
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3720
3772
|
}
|
|
3721
3773
|
),
|
|
3722
|
-
/* @__PURE__ */ (0,
|
|
3774
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3723
3775
|
"div",
|
|
3724
3776
|
{
|
|
3725
3777
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3727,15 +3779,20 @@ function PlayerPanel({
|
|
|
3727
3779
|
isExtended ? "flex-row" : "flex-col"
|
|
3728
3780
|
),
|
|
3729
3781
|
children: [
|
|
3730
|
-
/* @__PURE__ */ (0,
|
|
3731
|
-
/* @__PURE__ */ (0,
|
|
3732
|
-
/* @__PURE__ */ (0,
|
|
3733
|
-
/* @__PURE__ */ (0,
|
|
3734
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3782
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3783
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3784
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3785
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3786
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3787
|
+
playerTrackObject.loadError,
|
|
3788
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3789
|
+
void retryPlayback();
|
|
3790
|
+
}, children: "Retry" })
|
|
3791
|
+
] })
|
|
3735
3792
|
] }),
|
|
3736
|
-
/* @__PURE__ */ (0,
|
|
3793
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PlayerControls, {})
|
|
3737
3794
|
] }),
|
|
3738
|
-
/* @__PURE__ */ (0,
|
|
3795
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3739
3796
|
TrackListSidebar,
|
|
3740
3797
|
{
|
|
3741
3798
|
layout: "inline",
|
|
@@ -3761,33 +3818,33 @@ function PlayerPanel({
|
|
|
3761
3818
|
// src/genre-tree/Rating.tsx
|
|
3762
3819
|
var import_uuid = require("uuid");
|
|
3763
3820
|
var import_ai = require("react-icons/ai");
|
|
3764
|
-
var
|
|
3765
|
-
var
|
|
3821
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3822
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
3766
3823
|
function Rating({ rating, handleChange }) {
|
|
3767
3824
|
const instanceId = (0, import_uuid.v4)();
|
|
3768
|
-
return /* @__PURE__ */ (0,
|
|
3769
|
-
/* @__PURE__ */ (0,
|
|
3825
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3826
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3770
3827
|
"input",
|
|
3771
3828
|
{
|
|
3772
3829
|
id: `star-${instanceId}-${i}`,
|
|
3773
3830
|
className: "w-6 h-6 opacity-0",
|
|
3774
3831
|
type: "radio",
|
|
3775
3832
|
name: "rating",
|
|
3776
|
-
value: i === 0 ?
|
|
3833
|
+
value: i === 0 ? import_genre_tree27.FORM_RATING_NULL_VALUE : i,
|
|
3777
3834
|
checked: rating === (i === 0 ? null : i),
|
|
3778
3835
|
onChange: handleChange
|
|
3779
3836
|
}
|
|
3780
3837
|
),
|
|
3781
|
-
/* @__PURE__ */ (0,
|
|
3782
|
-
/* @__PURE__ */ (0,
|
|
3838
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex", children: [
|
|
3839
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3783
3840
|
import_ai.AiOutlineStar,
|
|
3784
3841
|
{
|
|
3785
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3842
|
+
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)]"}`
|
|
3786
3843
|
}
|
|
3787
3844
|
),
|
|
3788
|
-
/* @__PURE__ */ (0,
|
|
3845
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-grow w-8" }),
|
|
3789
3846
|
" "
|
|
3790
|
-
] }) : /* @__PURE__ */ (0,
|
|
3847
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3791
3848
|
import_ai.AiFillStar,
|
|
3792
3849
|
{
|
|
3793
3850
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3817,12 +3874,18 @@ function Rating({ rating, handleChange }) {
|
|
|
3817
3874
|
PopupButtons,
|
|
3818
3875
|
PopupTitle,
|
|
3819
3876
|
Rating,
|
|
3877
|
+
SEARCH_FAILED_STATUS,
|
|
3878
|
+
SearchBox,
|
|
3820
3879
|
SpotifyAuthErrorPopup,
|
|
3821
3880
|
Toast,
|
|
3822
3881
|
TrackItem,
|
|
3823
3882
|
TrackListSidebar,
|
|
3824
3883
|
TrackPositionPlayPause,
|
|
3825
3884
|
TrackUploadPopup,
|
|
3826
|
-
|
|
3885
|
+
searchStatus,
|
|
3886
|
+
useArtistSearchGroup,
|
|
3887
|
+
useGenrePlaylistPlayback,
|
|
3888
|
+
useGenreRemoteSearchGroup,
|
|
3889
|
+
useSongSearchGroup
|
|
3827
3890
|
});
|
|
3828
3891
|
//# sourceMappingURL=index.cjs.map
|