@behindthemusictree/app-kit-ui 33.3.0 → 34.1.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-QMELESFB.js → chunk-JP7PID7L.js} +413 -335
- package/dist/chunk-JP7PID7L.js.map +1 -0
- package/dist/genre-tree/index.cjs +572 -503
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +38 -31
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +567 -498
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-QMELESFB.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -587,19 +587,20 @@ function AuthCallbackHandler({ renderStatus, ...options }) {
|
|
|
587
587
|
}
|
|
588
588
|
|
|
589
589
|
// src/genre-tree/GenreTreeView.tsx
|
|
590
|
-
var
|
|
590
|
+
var import_react17 = require("react");
|
|
591
591
|
var import_react_query3 = require("@tanstack/react-query");
|
|
592
592
|
var import_lucide_react15 = require("lucide-react");
|
|
593
593
|
var import_lucide_react16 = require("lucide-react");
|
|
594
594
|
var import_ui19 = require("@behindthemusictree/ui");
|
|
595
595
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
596
|
-
var
|
|
596
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
597
597
|
var import_popup6 = require("@behindthemusictree/app-kit/popup");
|
|
598
598
|
|
|
599
599
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
600
|
+
var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
|
|
600
601
|
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
601
602
|
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
602
|
-
if (metric === "songs") return genrePlaylist
|
|
603
|
+
if (metric === "songs") return (0, import_genre_tree.genrePlaylistTrackCount)(genrePlaylist);
|
|
603
604
|
if (genrePlaylist.viewsCount === void 0) {
|
|
604
605
|
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
605
606
|
}
|
|
@@ -623,6 +624,7 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
|
|
|
623
624
|
}
|
|
624
625
|
|
|
625
626
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
627
|
+
var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
|
|
626
628
|
var import_react4 = require("react");
|
|
627
629
|
var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
|
|
628
630
|
var import_player = require("@behindthemusictree/app-kit/player");
|
|
@@ -631,29 +633,28 @@ var import_transport3 = require("@behindthemusictree/app-kit/transport");
|
|
|
631
633
|
|
|
632
634
|
// src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
|
|
633
635
|
var import_react3 = require("react");
|
|
634
|
-
var
|
|
636
|
+
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
635
637
|
function useGenrePlaylistPlayback({
|
|
636
638
|
scope,
|
|
637
639
|
genrePlaylists,
|
|
638
640
|
artist = null
|
|
639
641
|
}) {
|
|
640
|
-
const { trackList, playNewTrackListFromGenrePlaylist
|
|
642
|
+
const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
|
|
641
643
|
const origin = trackList?.origin;
|
|
642
644
|
const playingNodeId = (0, import_react3.useMemo)(() => {
|
|
643
|
-
if (!origin) return null;
|
|
644
|
-
|
|
645
|
-
if (
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
},
|
|
655
|
-
|
|
656
|
-
);
|
|
645
|
+
if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
|
|
646
|
+
const playlistOrigin = origin;
|
|
647
|
+
if (playlistOrigin.artistUuid !== artist?.uuid) return null;
|
|
648
|
+
const playlist = genrePlaylists.find((item) => item.uuid === origin.uuid) ?? playlistOrigin.criteriaPlaylist;
|
|
649
|
+
const expected = scope === "reference" && playlist && !("criteria" in playlist && playlist.criteria === null) ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
|
|
650
|
+
if (playlistOrigin.selection && expected && (0, import_genre_tree2.genreMusicSelectionKey)(playlistOrigin.selection).join() !== (0, import_genre_tree2.genreMusicSelectionKey)(expected).join()) return null;
|
|
651
|
+
return origin.uuid;
|
|
652
|
+
}, [origin, artist, genrePlaylists, scope]);
|
|
653
|
+
const play = (0, import_react3.useCallback)(async (playlist) => {
|
|
654
|
+
if (artist && !playlist.criteria) return;
|
|
655
|
+
const selection = scope === "reference" && playlist.criteria !== null ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
|
|
656
|
+
return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
|
|
657
|
+
}, [artist, scope, playNewTrackListFromGenrePlaylist]);
|
|
657
658
|
return (0, import_react3.useMemo)(() => ({ playingNodeId, play }), [playingNodeId, play]);
|
|
658
659
|
}
|
|
659
660
|
|
|
@@ -688,7 +689,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
688
689
|
id: genrePlaylist.uuid,
|
|
689
690
|
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
690
691
|
name: genrePlaylist.name,
|
|
691
|
-
itemCount: genrePlaylist
|
|
692
|
+
itemCount: (0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist),
|
|
692
693
|
actionable: Boolean(genrePlaylist.criteria),
|
|
693
694
|
side: genrePlaylist.criteria?.side ?? void 0,
|
|
694
695
|
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
@@ -704,7 +705,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
704
705
|
setIsPlaying(!isPlaying);
|
|
705
706
|
return;
|
|
706
707
|
}
|
|
707
|
-
if (genrePlaylist
|
|
708
|
+
if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
|
|
708
709
|
return;
|
|
709
710
|
}
|
|
710
711
|
play(genrePlaylist).catch((error) => {
|
|
@@ -768,6 +769,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
768
769
|
}
|
|
769
770
|
|
|
770
771
|
// src/genre-tree/playlist-tree/TreeWheel.tsx
|
|
772
|
+
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
771
773
|
var import_react5 = require("react");
|
|
772
774
|
var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
|
|
773
775
|
var import_player2 = require("@behindthemusictree/app-kit/player");
|
|
@@ -820,7 +822,7 @@ function GenrePlaylistTreeWheel({
|
|
|
820
822
|
setIsPlaying(!isPlaying);
|
|
821
823
|
return;
|
|
822
824
|
}
|
|
823
|
-
if (genrePlaylist
|
|
825
|
+
if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
|
|
824
826
|
return;
|
|
825
827
|
}
|
|
826
828
|
play(genrePlaylist).catch((error) => {
|
|
@@ -885,6 +887,7 @@ function GenrePlaylistTreeWheel({
|
|
|
885
887
|
}
|
|
886
888
|
|
|
887
889
|
// src/genre-tree/playlist-tree/TreeWheelRadialPopCore.tsx
|
|
890
|
+
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
888
891
|
var import_react6 = require("react");
|
|
889
892
|
var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
|
|
890
893
|
var import_player3 = require("@behindthemusictree/app-kit/player");
|
|
@@ -929,7 +932,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
929
932
|
setIsPlaying(!isPlaying);
|
|
930
933
|
return;
|
|
931
934
|
}
|
|
932
|
-
if (genrePlaylist
|
|
935
|
+
if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
|
|
933
936
|
return;
|
|
934
937
|
}
|
|
935
938
|
play(genrePlaylist).catch((error) => {
|
|
@@ -1024,10 +1027,13 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
1024
1027
|
] });
|
|
1025
1028
|
}
|
|
1026
1029
|
|
|
1030
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
1031
|
+
var import_react9 = require("react");
|
|
1032
|
+
|
|
1027
1033
|
// src/genre-tree/GenreSearch.tsx
|
|
1028
1034
|
var import_lucide_react7 = require("lucide-react");
|
|
1029
1035
|
var import_ui7 = require("@behindthemusictree/ui");
|
|
1030
|
-
var
|
|
1036
|
+
var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1031
1037
|
|
|
1032
1038
|
// src/genre-tree/search/SearchBox.tsx
|
|
1033
1039
|
var import_react8 = require("react");
|
|
@@ -1083,10 +1089,10 @@ var SEARCH_FAILED_STATUS = "Search failed";
|
|
|
1083
1089
|
var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
|
|
1084
1090
|
|
|
1085
1091
|
// src/genre-tree/search/useArtistSearchGroup.tsx
|
|
1086
|
-
var
|
|
1092
|
+
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1087
1093
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1088
1094
|
function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
1089
|
-
const search = (0,
|
|
1095
|
+
const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
|
|
1090
1096
|
if (!enabled) return void 0;
|
|
1091
1097
|
return {
|
|
1092
1098
|
key: "artists",
|
|
@@ -1114,17 +1120,17 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
|
1114
1120
|
}
|
|
1115
1121
|
|
|
1116
1122
|
// src/genre-tree/search/useSongSearchGroup.tsx
|
|
1117
|
-
var
|
|
1123
|
+
var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1118
1124
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1119
1125
|
function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
1120
|
-
const search = (0,
|
|
1126
|
+
const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
|
|
1121
1127
|
if (!enabled) return void 0;
|
|
1122
1128
|
return {
|
|
1123
1129
|
key: "songs",
|
|
1124
1130
|
label: "Songs",
|
|
1125
1131
|
items: search.songs.map((song) => ({
|
|
1126
1132
|
key: song.uuid,
|
|
1127
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: (0,
|
|
1133
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
1128
1134
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
1129
1135
|
/* @__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
1136
|
] }),
|
|
@@ -1136,10 +1142,10 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
|
1136
1142
|
}
|
|
1137
1143
|
|
|
1138
1144
|
// src/genre-tree/search/useGenreRemoteSearchGroup.tsx
|
|
1139
|
-
var
|
|
1145
|
+
var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1140
1146
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1141
1147
|
function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
|
|
1142
|
-
const search = (0,
|
|
1148
|
+
const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
|
|
1143
1149
|
const genres = filter ? search.genres.filter(filter) : search.genres;
|
|
1144
1150
|
return {
|
|
1145
1151
|
key,
|
|
@@ -1173,7 +1179,7 @@ function GenreSearch({
|
|
|
1173
1179
|
extraGroups = [],
|
|
1174
1180
|
onFocus
|
|
1175
1181
|
}) {
|
|
1176
|
-
const { query, setQuery, results, canClear, clear, select } = (0,
|
|
1182
|
+
const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree9.useGenreSearch)({
|
|
1177
1183
|
genrePlaylists,
|
|
1178
1184
|
onSelect,
|
|
1179
1185
|
selectedName,
|
|
@@ -1224,7 +1230,47 @@ function GenreSearch({
|
|
|
1224
1230
|
) });
|
|
1225
1231
|
}
|
|
1226
1232
|
|
|
1233
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
1234
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1235
|
+
function GenreTreeSearch({
|
|
1236
|
+
artistBaseUrl,
|
|
1237
|
+
songBaseUrl,
|
|
1238
|
+
onArtistSelect,
|
|
1239
|
+
onSongSelect,
|
|
1240
|
+
artistSearch,
|
|
1241
|
+
songSearch,
|
|
1242
|
+
SearchGroups,
|
|
1243
|
+
extraGroups = [],
|
|
1244
|
+
onQueryChange,
|
|
1245
|
+
onFocus,
|
|
1246
|
+
...props
|
|
1247
|
+
}) {
|
|
1248
|
+
const [query, setQuery] = (0, import_react9.useState)("");
|
|
1249
|
+
const [focused, setFocused] = (0, import_react9.useState)(false);
|
|
1250
|
+
const deferredQuery = (0, import_react9.useDeferredValue)(query);
|
|
1251
|
+
const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
|
|
1252
|
+
const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
|
|
1253
|
+
const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1254
|
+
GenreSearch,
|
|
1255
|
+
{
|
|
1256
|
+
...props,
|
|
1257
|
+
onQueryChange: (text) => {
|
|
1258
|
+
setQuery(text);
|
|
1259
|
+
onQueryChange?.(text);
|
|
1260
|
+
},
|
|
1261
|
+
onFocus: () => {
|
|
1262
|
+
setFocused(true);
|
|
1263
|
+
onFocus?.();
|
|
1264
|
+
},
|
|
1265
|
+
extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
|
|
1266
|
+
}
|
|
1267
|
+
);
|
|
1268
|
+
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SearchGroups, { query: deferredQuery, focused, children: render }) : render([]);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1227
1271
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1272
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1273
|
+
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
1228
1274
|
var import_player6 = require("@behindthemusictree/app-kit/player");
|
|
1229
1275
|
|
|
1230
1276
|
// src/genre-tree/GenreActionBand.tsx
|
|
@@ -1235,18 +1281,18 @@ var import_lucide_react10 = require("lucide-react");
|
|
|
1235
1281
|
var import_popup4 = require("@behindthemusictree/app-kit/popup");
|
|
1236
1282
|
|
|
1237
1283
|
// src/genre-tree/SharePopup.tsx
|
|
1238
|
-
var
|
|
1284
|
+
var import_react10 = require("react");
|
|
1239
1285
|
var import_lucide_react9 = require("lucide-react");
|
|
1240
1286
|
var import_fa3 = require("react-icons/fa");
|
|
1241
1287
|
var import_si = require("react-icons/si");
|
|
1242
1288
|
|
|
1243
1289
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
1244
|
-
var
|
|
1290
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1245
1291
|
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";
|
|
1246
1292
|
function Contents({ icon, label }) {
|
|
1247
|
-
return /* @__PURE__ */ (0,
|
|
1248
|
-
/* @__PURE__ */ (0,
|
|
1249
|
-
/* @__PURE__ */ (0,
|
|
1293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.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 }),
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "max-w-full truncate", children: label })
|
|
1250
1296
|
] });
|
|
1251
1297
|
}
|
|
1252
1298
|
function ShareDestinationButton({
|
|
@@ -1258,29 +1304,29 @@ function ShareDestinationButton({
|
|
|
1258
1304
|
ariaLabel
|
|
1259
1305
|
}) {
|
|
1260
1306
|
if (href) {
|
|
1261
|
-
return /* @__PURE__ */ (0,
|
|
1307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Contents, { icon, label }) });
|
|
1262
1308
|
}
|
|
1263
|
-
return /* @__PURE__ */ (0,
|
|
1309
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Contents, { icon, label }) });
|
|
1264
1310
|
}
|
|
1265
1311
|
|
|
1266
1312
|
// src/genre-tree/SharePreviewCard.tsx
|
|
1267
1313
|
var import_lucide_react8 = require("lucide-react");
|
|
1268
|
-
var
|
|
1314
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1269
1315
|
function SharePreviewCard({ target, url }) {
|
|
1270
1316
|
const Icon = { genre: import_lucide_react8.Music2, artist: import_lucide_react8.UserRound, song: import_lucide_react8.Music }[target.kind];
|
|
1271
1317
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
1272
|
-
return /* @__PURE__ */ (0,
|
|
1273
|
-
/* @__PURE__ */ (0,
|
|
1274
|
-
/* @__PURE__ */ (0,
|
|
1275
|
-
/* @__PURE__ */ (0,
|
|
1276
|
-
/* @__PURE__ */ (0,
|
|
1277
|
-
/* @__PURE__ */ (0,
|
|
1318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.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_runtime23.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
1320
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
1321
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
1278
1324
|
] })
|
|
1279
1325
|
] }) });
|
|
1280
1326
|
}
|
|
1281
1327
|
|
|
1282
1328
|
// src/genre-tree/SharePopup.tsx
|
|
1283
|
-
var
|
|
1329
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1284
1330
|
function resolveAbsoluteUrl(url) {
|
|
1285
1331
|
try {
|
|
1286
1332
|
const parsed = new URL(url, window.location.href);
|
|
@@ -1313,20 +1359,20 @@ async function copyText(text) {
|
|
|
1313
1359
|
}
|
|
1314
1360
|
}
|
|
1315
1361
|
function SharePopup({ target, url, onClose }) {
|
|
1316
|
-
const [absoluteUrl, setAbsoluteUrl] = (0,
|
|
1317
|
-
const [copyStatus, setCopyStatus] = (0,
|
|
1318
|
-
const [canUseNativeShare, setCanUseNativeShare] = (0,
|
|
1319
|
-
const [shareError, setShareError] = (0,
|
|
1320
|
-
const [toastId, setToastId] = (0,
|
|
1321
|
-
(0,
|
|
1362
|
+
const [absoluteUrl, setAbsoluteUrl] = (0, import_react10.useState)(url);
|
|
1363
|
+
const [copyStatus, setCopyStatus] = (0, import_react10.useState)("idle");
|
|
1364
|
+
const [canUseNativeShare, setCanUseNativeShare] = (0, import_react10.useState)(false);
|
|
1365
|
+
const [shareError, setShareError] = (0, import_react10.useState)(null);
|
|
1366
|
+
const [toastId, setToastId] = (0, import_react10.useState)(0);
|
|
1367
|
+
(0, import_react10.useEffect)(() => {
|
|
1322
1368
|
setAbsoluteUrl(resolveAbsoluteUrl(url) ?? "");
|
|
1323
1369
|
setCopyStatus("idle");
|
|
1324
1370
|
setShareError(null);
|
|
1325
1371
|
}, [url]);
|
|
1326
|
-
(0,
|
|
1372
|
+
(0, import_react10.useEffect)(() => {
|
|
1327
1373
|
setCanUseNativeShare(typeof navigator !== "undefined" && typeof navigator.share === "function");
|
|
1328
1374
|
}, []);
|
|
1329
|
-
const destinationUrls = (0,
|
|
1375
|
+
const destinationUrls = (0, import_react10.useMemo)(() => {
|
|
1330
1376
|
if (!absoluteUrl) return null;
|
|
1331
1377
|
const message = `${target.name} \u2014 ${absoluteUrl}`;
|
|
1332
1378
|
return {
|
|
@@ -1344,7 +1390,7 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1344
1390
|
setCopyStatus(copied ? "idle" : "failed");
|
|
1345
1391
|
if (copied) setToastId((id) => id + 1);
|
|
1346
1392
|
};
|
|
1347
|
-
const dismissToast = (0,
|
|
1393
|
+
const dismissToast = (0, import_react10.useCallback)(() => setToastId(0), []);
|
|
1348
1394
|
const handleNativeShare = async () => {
|
|
1349
1395
|
if (!absoluteUrl || !navigator.share) return;
|
|
1350
1396
|
setShareError(null);
|
|
@@ -1355,8 +1401,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1355
1401
|
setShareError("Could not open the share options.");
|
|
1356
1402
|
}
|
|
1357
1403
|
};
|
|
1358
|
-
return /* @__PURE__ */ (0,
|
|
1359
|
-
/* @__PURE__ */ (0,
|
|
1404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
1405
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1360
1406
|
BasePopup_default,
|
|
1361
1407
|
{
|
|
1362
1408
|
title: "Share",
|
|
@@ -1371,67 +1417,67 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1371
1417
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
1372
1418
|
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)]",
|
|
1373
1419
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
1374
|
-
children: /* @__PURE__ */ (0,
|
|
1375
|
-
/* @__PURE__ */ (0,
|
|
1376
|
-
/* @__PURE__ */ (0,
|
|
1377
|
-
/* @__PURE__ */ (0,
|
|
1420
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
1422
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1378
1424
|
ShareDestinationButton,
|
|
1379
1425
|
{
|
|
1380
1426
|
label: "Copy Link",
|
|
1381
|
-
icon: /* @__PURE__ */ (0,
|
|
1427
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1382
1428
|
onClick: () => void handleCopy(),
|
|
1383
1429
|
ariaLabel: "Copy link",
|
|
1384
1430
|
disabled: !absoluteUrl
|
|
1385
1431
|
}
|
|
1386
1432
|
),
|
|
1387
|
-
/* @__PURE__ */ (0,
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1388
1434
|
ShareDestinationButton,
|
|
1389
1435
|
{
|
|
1390
1436
|
label: "WhatsApp",
|
|
1391
|
-
icon: /* @__PURE__ */ (0,
|
|
1437
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
1392
1438
|
href: destinationUrls?.whatsapp,
|
|
1393
1439
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
1394
1440
|
disabled: !absoluteUrl
|
|
1395
1441
|
}
|
|
1396
1442
|
),
|
|
1397
|
-
/* @__PURE__ */ (0,
|
|
1443
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1398
1444
|
ShareDestinationButton,
|
|
1399
1445
|
{
|
|
1400
1446
|
label: "Gmail",
|
|
1401
|
-
icon: /* @__PURE__ */ (0,
|
|
1447
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
1402
1448
|
href: destinationUrls?.gmail,
|
|
1403
1449
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
1404
1450
|
disabled: !absoluteUrl
|
|
1405
1451
|
}
|
|
1406
1452
|
),
|
|
1407
|
-
canUseNativeShare && /* @__PURE__ */ (0,
|
|
1453
|
+
canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1408
1454
|
ShareDestinationButton,
|
|
1409
1455
|
{
|
|
1410
1456
|
label: "More",
|
|
1411
|
-
icon: /* @__PURE__ */ (0,
|
|
1457
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1412
1458
|
onClick: () => void handleNativeShare(),
|
|
1413
1459
|
ariaLabel: "More sharing options",
|
|
1414
1460
|
disabled: !absoluteUrl
|
|
1415
1461
|
}
|
|
1416
1462
|
)
|
|
1417
1463
|
] }),
|
|
1418
|
-
copyStatus === "failed" && /* @__PURE__ */ (0,
|
|
1419
|
-
shareError && /* @__PURE__ */ (0,
|
|
1464
|
+
copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
1465
|
+
shareError && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
1420
1466
|
] })
|
|
1421
1467
|
}
|
|
1422
1468
|
),
|
|
1423
|
-
toastId > 0 && /* @__PURE__ */ (0,
|
|
1469
|
+
toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
1424
1470
|
] });
|
|
1425
1471
|
}
|
|
1426
1472
|
|
|
1427
1473
|
// src/genre-tree/ShareActionButton.tsx
|
|
1428
|
-
var
|
|
1474
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1429
1475
|
function ShareActionButton({ target, url }) {
|
|
1430
1476
|
const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
|
|
1431
1477
|
const openSharePopup = () => {
|
|
1432
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1478
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SharePopup, { target, url, onClose: hidePopup }));
|
|
1433
1479
|
};
|
|
1434
|
-
return /* @__PURE__ */ (0,
|
|
1480
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1435
1481
|
"button",
|
|
1436
1482
|
{
|
|
1437
1483
|
type: "button",
|
|
@@ -1440,7 +1486,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1440
1486
|
"aria-label": `Share ${target.name}`,
|
|
1441
1487
|
title: `Share ${target.name}`,
|
|
1442
1488
|
children: [
|
|
1443
|
-
/* @__PURE__ */ (0,
|
|
1489
|
+
/* @__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)]", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1444
1490
|
"Share"
|
|
1445
1491
|
]
|
|
1446
1492
|
}
|
|
@@ -1448,7 +1494,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1448
1494
|
}
|
|
1449
1495
|
|
|
1450
1496
|
// src/genre-tree/GenreActionBand.tsx
|
|
1451
|
-
var
|
|
1497
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1452
1498
|
function GenreActionBand({
|
|
1453
1499
|
node,
|
|
1454
1500
|
onPlayPause,
|
|
@@ -1470,7 +1516,7 @@ function GenreActionBand({
|
|
|
1470
1516
|
items.push({
|
|
1471
1517
|
key: "play-pause",
|
|
1472
1518
|
label: isPlaying ? "Pause" : "Play",
|
|
1473
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
1519
|
+
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1474
1520
|
onClick: onPlayPause
|
|
1475
1521
|
});
|
|
1476
1522
|
}
|
|
@@ -1478,24 +1524,24 @@ function GenreActionBand({
|
|
|
1478
1524
|
items.push({
|
|
1479
1525
|
key: "zoom-in",
|
|
1480
1526
|
label: "Focus",
|
|
1481
|
-
icon: /* @__PURE__ */ (0,
|
|
1527
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1482
1528
|
onClick: onZoomIn
|
|
1483
1529
|
});
|
|
1484
1530
|
}
|
|
1485
1531
|
if (onAddChild) {
|
|
1486
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0,
|
|
1532
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
1487
1533
|
}
|
|
1488
1534
|
if (onRename) {
|
|
1489
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0,
|
|
1535
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
1490
1536
|
}
|
|
1491
1537
|
if (onChangeParent) {
|
|
1492
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0,
|
|
1538
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
1493
1539
|
}
|
|
1494
1540
|
if (onToggleSide) {
|
|
1495
1541
|
adminItems.push({
|
|
1496
1542
|
key: "toggle-side",
|
|
1497
1543
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
1498
|
-
icon: /* @__PURE__ */ (0,
|
|
1544
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react11.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1499
1545
|
onClick: onToggleSide,
|
|
1500
1546
|
disabled: !!toggleSideDisabledReason,
|
|
1501
1547
|
title: toggleSideDisabledReason
|
|
@@ -1511,7 +1557,7 @@ function GenreActionBand({
|
|
|
1511
1557
|
});
|
|
1512
1558
|
}
|
|
1513
1559
|
if (items.length === 0 && (!shareTarget || !shareUrl) && adminItems.length === 0) return null;
|
|
1514
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0,
|
|
1560
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1515
1561
|
"button",
|
|
1516
1562
|
{
|
|
1517
1563
|
type: "button",
|
|
@@ -1520,25 +1566,25 @@ function GenreActionBand({
|
|
|
1520
1566
|
onClick: item.onClick,
|
|
1521
1567
|
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",
|
|
1522
1568
|
children: [
|
|
1523
|
-
/* @__PURE__ */ (0,
|
|
1569
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.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 }),
|
|
1524
1570
|
item.label
|
|
1525
1571
|
]
|
|
1526
1572
|
},
|
|
1527
1573
|
item.key
|
|
1528
1574
|
));
|
|
1529
|
-
return /* @__PURE__ */ (0,
|
|
1530
|
-
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0,
|
|
1575
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
1576
|
+
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
1531
1577
|
renderItems(items),
|
|
1532
|
-
shareTarget && shareUrl && /* @__PURE__ */ (0,
|
|
1578
|
+
shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1533
1579
|
] }),
|
|
1534
|
-
adminItems.length > 0 && /* @__PURE__ */ (0,
|
|
1580
|
+
adminItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1535
1581
|
"section",
|
|
1536
1582
|
{
|
|
1537
1583
|
"aria-label": "Admin actions",
|
|
1538
1584
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
1539
1585
|
children: [
|
|
1540
|
-
/* @__PURE__ */ (0,
|
|
1541
|
-
/* @__PURE__ */ (0,
|
|
1586
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
1587
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
1542
1588
|
]
|
|
1543
1589
|
}
|
|
1544
1590
|
)
|
|
@@ -1547,23 +1593,23 @@ function GenreActionBand({
|
|
|
1547
1593
|
|
|
1548
1594
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1549
1595
|
var import_ui9 = require("@behindthemusictree/ui");
|
|
1550
|
-
var
|
|
1596
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1551
1597
|
|
|
1552
1598
|
// src/genre-tree/ClampedText.tsx
|
|
1553
|
-
var
|
|
1554
|
-
var
|
|
1599
|
+
var import_react11 = require("react");
|
|
1600
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1555
1601
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1556
|
-
const ref = (0,
|
|
1557
|
-
const [expanded, setExpanded] = (0,
|
|
1558
|
-
const [overflows, setOverflows] = (0,
|
|
1559
|
-
(0,
|
|
1602
|
+
const ref = (0, import_react11.useRef)(null);
|
|
1603
|
+
const [expanded, setExpanded] = (0, import_react11.useState)(false);
|
|
1604
|
+
const [overflows, setOverflows] = (0, import_react11.useState)(false);
|
|
1605
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
1560
1606
|
const element = ref.current;
|
|
1561
1607
|
if (!element || expanded) return;
|
|
1562
1608
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1563
1609
|
}, [children, expanded]);
|
|
1564
|
-
return /* @__PURE__ */ (0,
|
|
1565
|
-
/* @__PURE__ */ (0,
|
|
1566
|
-
(overflows || expanded) && /* @__PURE__ */ (0,
|
|
1610
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
1611
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1612
|
+
(overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1567
1613
|
"button",
|
|
1568
1614
|
{
|
|
1569
1615
|
type: "button",
|
|
@@ -1577,7 +1623,7 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1577
1623
|
}
|
|
1578
1624
|
|
|
1579
1625
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1580
|
-
var
|
|
1626
|
+
var import_react12 = require("react");
|
|
1581
1627
|
var import_react_query = require("@tanstack/react-query");
|
|
1582
1628
|
var import_zod = require("zod");
|
|
1583
1629
|
var import_fa4 = require("react-icons/fa");
|
|
@@ -1590,22 +1636,23 @@ var import_player4 = require("@behindthemusictree/app-kit/player");
|
|
|
1590
1636
|
var import_player5 = require("@behindthemusictree/app-kit/player");
|
|
1591
1637
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
1592
1638
|
var import_transport8 = require("@behindthemusictree/app-kit/transport");
|
|
1593
|
-
var
|
|
1594
|
-
var
|
|
1595
|
-
var
|
|
1596
|
-
var
|
|
1639
|
+
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1640
|
+
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1641
|
+
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1642
|
+
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1643
|
+
var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1597
1644
|
|
|
1598
1645
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1599
|
-
var
|
|
1600
|
-
var
|
|
1646
|
+
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1647
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1601
1648
|
function MusicBrainzRecordingLink({
|
|
1602
1649
|
song,
|
|
1603
1650
|
children,
|
|
1604
1651
|
className: className2 = ""
|
|
1605
1652
|
}) {
|
|
1606
|
-
const url = (0,
|
|
1607
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1608
|
-
return /* @__PURE__ */ (0,
|
|
1653
|
+
const url = (0, import_genre_tree10.musicBrainzRecordingUrl)(song);
|
|
1654
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_jsx_runtime28.Fragment, { children });
|
|
1655
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1609
1656
|
"a",
|
|
1610
1657
|
{
|
|
1611
1658
|
href: url,
|
|
@@ -1620,11 +1667,11 @@ function MusicBrainzRecordingLink({
|
|
|
1620
1667
|
}
|
|
1621
1668
|
|
|
1622
1669
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1623
|
-
var
|
|
1670
|
+
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1624
1671
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1625
|
-
var
|
|
1672
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1626
1673
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1627
|
-
var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(
|
|
1674
|
+
var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema).extend({
|
|
1628
1675
|
overallTotal: import_zod.z.number().nullable(),
|
|
1629
1676
|
page: import_zod.z.number().nullable(),
|
|
1630
1677
|
totalPages: import_zod.z.number().nullable()
|
|
@@ -1642,6 +1689,7 @@ var nextCursor = (next) => {
|
|
|
1642
1689
|
function GenrePlaylistTracks({
|
|
1643
1690
|
genreUuid,
|
|
1644
1691
|
genrePlaylist,
|
|
1692
|
+
selection,
|
|
1645
1693
|
scope,
|
|
1646
1694
|
getBackendBaseUrl,
|
|
1647
1695
|
renderArtistLink,
|
|
@@ -1651,7 +1699,7 @@ function GenrePlaylistTracks({
|
|
|
1651
1699
|
renderTracksToolbar,
|
|
1652
1700
|
artist = null
|
|
1653
1701
|
}) {
|
|
1654
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0,
|
|
1702
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree15.useTrackList)();
|
|
1655
1703
|
const { session, sessionRestored } = (0, import_auth2.useSession)();
|
|
1656
1704
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1657
1705
|
const { showPopup } = (0, import_popup5.usePopup)();
|
|
@@ -1660,15 +1708,15 @@ function GenrePlaylistTracks({
|
|
|
1660
1708
|
const { fetch } = (0, import_transport5.useFetchWrapper)(getBackendBaseUrl);
|
|
1661
1709
|
const playback = (0, import_transport5.usePlaybackContext)();
|
|
1662
1710
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1663
|
-
queryKey: [...scope === "me" ?
|
|
1711
|
+
queryKey: [...scope === "me" ? import_genre_tree12.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1664
1712
|
queryFn: async ({ pageParam }) => {
|
|
1665
1713
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1666
1714
|
const response = await fetch(
|
|
1667
|
-
|
|
1715
|
+
import_genre_tree13.libraryEndpoints.reference.songs.list(),
|
|
1668
1716
|
true,
|
|
1669
1717
|
false,
|
|
1670
1718
|
{},
|
|
1671
|
-
{ genre: genreUuid, ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1719
|
+
{ genre: genreUuid, ...(0, import_genre_tree11.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1672
1720
|
);
|
|
1673
1721
|
const parsed = (0, import_transport7.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
|
|
1674
1722
|
return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
|
|
@@ -1677,8 +1725,8 @@ function GenrePlaylistTracks({
|
|
|
1677
1725
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1678
1726
|
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1679
1727
|
});
|
|
1680
|
-
const [sentinel, setSentinel] = (0,
|
|
1681
|
-
(0,
|
|
1728
|
+
const [sentinel, setSentinel] = (0, import_react12.useState)(null);
|
|
1729
|
+
(0, import_react12.useEffect)(() => {
|
|
1682
1730
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1683
1731
|
const observer = new IntersectionObserver((entries) => {
|
|
1684
1732
|
if (entries.some((entry) => entry.isIntersecting)) {
|
|
@@ -1692,38 +1740,43 @@ function GenrePlaylistTracks({
|
|
|
1692
1740
|
const tracks = pages.flatMap((page) => page.tracks);
|
|
1693
1741
|
const total = pages[0]?.total ?? void 0;
|
|
1694
1742
|
const play = (startSongUuid) => {
|
|
1695
|
-
const started = artist ? playNewTrackListFromArtist(artist, scope, {
|
|
1743
|
+
const started = artist ? playNewTrackListFromArtist(artist, scope, {
|
|
1744
|
+
startSongUuid,
|
|
1745
|
+
ordering: "title",
|
|
1746
|
+
genreUuid,
|
|
1747
|
+
...selection && { selection }
|
|
1748
|
+
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genrePlaylist.name }, scope, { startSongUuid, ...selection && { selection } }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1696
1749
|
started.catch((error) => {
|
|
1697
1750
|
console.error("Failed to play genre playlist:", error);
|
|
1698
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1751
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
|
|
1699
1752
|
});
|
|
1700
1753
|
};
|
|
1701
|
-
return /* @__PURE__ */ (0,
|
|
1754
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { children: [
|
|
1702
1755
|
renderTracksToolbar?.(),
|
|
1703
|
-
isPending ? /* @__PURE__ */ (0,
|
|
1756
|
+
isPending ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { role: "alert", children: [
|
|
1704
1757
|
"Couldn't load ",
|
|
1705
1758
|
trackNoun.plural,
|
|
1706
1759
|
".",
|
|
1707
1760
|
" ",
|
|
1708
|
-
/* @__PURE__ */ (0,
|
|
1709
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1761
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1762
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime29.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: [
|
|
1710
1763
|
tracks.map((track, index) => {
|
|
1711
1764
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1712
1765
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1713
1766
|
const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
|
|
1714
|
-
const text = /* @__PURE__ */ (0,
|
|
1715
|
-
/* @__PURE__ */ (0,
|
|
1716
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1717
|
-
/* @__PURE__ */ (0,
|
|
1718
|
-
track.album && /* @__PURE__ */ (0,
|
|
1767
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1768
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree14.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1769
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1770
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1771
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1719
1772
|
] });
|
|
1720
1773
|
const leading = renderSongLeading?.(track, index);
|
|
1721
1774
|
const actions = renderSongActions?.(track);
|
|
1722
|
-
const isUnplayable = (0,
|
|
1775
|
+
const isUnplayable = (0, import_genre_tree14.isSong)(track) && (0, import_genre_tree14.songUnplayableReason)(track) !== null;
|
|
1723
1776
|
if (isUnplayable) {
|
|
1724
|
-
return /* @__PURE__ */ (0,
|
|
1777
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1725
1778
|
leading,
|
|
1726
|
-
/* @__PURE__ */ (0,
|
|
1779
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1727
1780
|
"button",
|
|
1728
1781
|
{
|
|
1729
1782
|
type: "button",
|
|
@@ -1731,27 +1784,27 @@ function GenrePlaylistTracks({
|
|
|
1731
1784
|
"aria-disabled": "true",
|
|
1732
1785
|
"aria-label": label,
|
|
1733
1786
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1734
|
-
children: /* @__PURE__ */ (0,
|
|
1787
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1735
1788
|
}
|
|
1736
1789
|
),
|
|
1737
1790
|
text,
|
|
1738
1791
|
actions
|
|
1739
1792
|
] }, track.uuid);
|
|
1740
1793
|
}
|
|
1741
|
-
return /* @__PURE__ */ (0,
|
|
1794
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1742
1795
|
"li",
|
|
1743
1796
|
{
|
|
1744
1797
|
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)]" : ""}`,
|
|
1745
1798
|
children: [
|
|
1746
1799
|
leading,
|
|
1747
|
-
/* @__PURE__ */ (0,
|
|
1800
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1748
1801
|
"button",
|
|
1749
1802
|
{
|
|
1750
1803
|
type: "button",
|
|
1751
1804
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1752
1805
|
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"}`,
|
|
1753
1806
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1754
|
-
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1807
|
+
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui8.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_fa4.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1755
1808
|
}
|
|
1756
1809
|
),
|
|
1757
1810
|
text,
|
|
@@ -1761,7 +1814,7 @@ function GenrePlaylistTracks({
|
|
|
1761
1814
|
track.uuid
|
|
1762
1815
|
);
|
|
1763
1816
|
}),
|
|
1764
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1817
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1765
1818
|
] })
|
|
1766
1819
|
] }) });
|
|
1767
1820
|
}
|
|
@@ -1769,10 +1822,11 @@ function GenrePlaylistTracks({
|
|
|
1769
1822
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1770
1823
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
1771
1824
|
var import_si2 = require("react-icons/si");
|
|
1772
|
-
var
|
|
1825
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1773
1826
|
function GenreDetailExtras({
|
|
1774
1827
|
genreUuid,
|
|
1775
1828
|
genrePlaylist,
|
|
1829
|
+
selection,
|
|
1776
1830
|
wikidataId,
|
|
1777
1831
|
scope,
|
|
1778
1832
|
getBackendBaseUrl,
|
|
@@ -1786,24 +1840,24 @@ function GenreDetailExtras({
|
|
|
1786
1840
|
...trackSlots
|
|
1787
1841
|
}) {
|
|
1788
1842
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
1789
|
-
const { data: overview, isPending } = (0,
|
|
1843
|
+
const { data: overview, isPending } = (0, import_genre_tree17.useFetchGenreOverview)(
|
|
1790
1844
|
genreUuid,
|
|
1791
1845
|
scope,
|
|
1792
1846
|
getBackendBaseUrl,
|
|
1793
1847
|
criteriaOverviewSchema
|
|
1794
1848
|
);
|
|
1795
1849
|
if (isPending) {
|
|
1796
|
-
return /* @__PURE__ */ (0,
|
|
1797
|
-
/* @__PURE__ */ (0,
|
|
1850
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1851
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui9.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1798
1852
|
family
|
|
1799
1853
|
] });
|
|
1800
1854
|
}
|
|
1801
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1855
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_jsx_runtime30.Fragment, { children: family });
|
|
1802
1856
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1803
1857
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1804
|
-
return /* @__PURE__ */ (0,
|
|
1805
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1806
|
-
summarySourceUrl && /* @__PURE__ */ (0,
|
|
1858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1859
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui9.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1860
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1807
1861
|
"a",
|
|
1808
1862
|
{
|
|
1809
1863
|
href: summarySourceUrl,
|
|
@@ -1811,12 +1865,12 @@ function GenreDetailExtras({
|
|
|
1811
1865
|
rel: "noopener noreferrer",
|
|
1812
1866
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1813
1867
|
children: [
|
|
1814
|
-
/* @__PURE__ */ (0,
|
|
1868
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1815
1869
|
"Wikipedia"
|
|
1816
1870
|
]
|
|
1817
1871
|
}
|
|
1818
1872
|
),
|
|
1819
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1873
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1820
1874
|
"a",
|
|
1821
1875
|
{
|
|
1822
1876
|
href: wikidataUrl,
|
|
@@ -1824,21 +1878,22 @@ function GenreDetailExtras({
|
|
|
1824
1878
|
rel: "noopener noreferrer",
|
|
1825
1879
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1826
1880
|
children: [
|
|
1827
|
-
/* @__PURE__ */ (0,
|
|
1881
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1828
1882
|
"Wikidata"
|
|
1829
1883
|
]
|
|
1830
1884
|
}
|
|
1831
1885
|
)
|
|
1832
1886
|
] }) }),
|
|
1833
|
-
/* @__PURE__ */ (0,
|
|
1887
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui9.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1834
1888
|
family,
|
|
1835
1889
|
renderGenreDetailExtras?.(overview),
|
|
1836
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1837
|
-
/* @__PURE__ */ (0,
|
|
1890
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui9.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
1838
1892
|
GenrePlaylistTracks,
|
|
1839
1893
|
{
|
|
1840
1894
|
genreUuid,
|
|
1841
1895
|
genrePlaylist,
|
|
1896
|
+
selection,
|
|
1842
1897
|
scope,
|
|
1843
1898
|
getBackendBaseUrl,
|
|
1844
1899
|
renderArtistLink,
|
|
@@ -1850,8 +1905,8 @@ function GenreDetailExtras({
|
|
|
1850
1905
|
}
|
|
1851
1906
|
|
|
1852
1907
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1853
|
-
var
|
|
1854
|
-
var
|
|
1908
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1909
|
+
var import_react13 = require("react");
|
|
1855
1910
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1856
1911
|
const { isPlaying, setIsPlaying } = (0, import_player6.usePlayer)();
|
|
1857
1912
|
const isCurrentTrackList = playback.playingNodeId === genrePlaylist.uuid;
|
|
@@ -1864,11 +1919,11 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1864
1919
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1865
1920
|
});
|
|
1866
1921
|
};
|
|
1867
|
-
return /* @__PURE__ */ (0,
|
|
1922
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1868
1923
|
GenreActionBand,
|
|
1869
1924
|
{
|
|
1870
1925
|
...bandProps,
|
|
1871
|
-
onPlayPause: genrePlaylist.tracksCount > 0 ? handlePlayPause : void 0,
|
|
1926
|
+
onPlayPause: ("genreOnlyTracksCount" in genrePlaylist ? (0, import_genre_tree18.genreMusicCount)(genrePlaylist) : genrePlaylist.tracksCount) > 0 ? handlePlayPause : void 0,
|
|
1872
1927
|
isPlaying: isCurrentTrackList && isPlaying
|
|
1873
1928
|
}
|
|
1874
1929
|
);
|
|
@@ -1878,6 +1933,7 @@ function GenreInfoPanelBody({
|
|
|
1878
1933
|
family,
|
|
1879
1934
|
genreUuid,
|
|
1880
1935
|
genrePlaylist,
|
|
1936
|
+
catalogue,
|
|
1881
1937
|
wikidataId,
|
|
1882
1938
|
scope,
|
|
1883
1939
|
getBackendBaseUrl,
|
|
@@ -1885,10 +1941,31 @@ function GenreInfoPanelBody({
|
|
|
1885
1941
|
band,
|
|
1886
1942
|
extras
|
|
1887
1943
|
}) {
|
|
1888
|
-
|
|
1944
|
+
const noun = (0, import_track_noun4.useTrackNoun)();
|
|
1945
|
+
const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist) : void 0;
|
|
1946
|
+
const selection = summary ? (0, import_genre_tree18.defaultGenreMusicSelection)(summary) : void 0;
|
|
1947
|
+
const describeCounts = (counts) => {
|
|
1948
|
+
const scopes = counts.genreAndSubgenresTracksCount;
|
|
1949
|
+
return "canonical" in scopes ? `${counts.genreOnlyTracksCount} this genre only \xB7 ${scopes.canonical} with canonical subgenres \xB7 ${scopes.regional} with regional subgenres \xB7 ${scopes.all} with all subgenres` : `${counts.genreOnlyTracksCount} this genre only \xB7 ${scopes.all} with subgenres`;
|
|
1950
|
+
};
|
|
1951
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1889
1952
|
toolbar,
|
|
1890
|
-
/* @__PURE__ */ (0,
|
|
1891
|
-
/* @__PURE__ */ (0,
|
|
1953
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1954
|
+
summary && catalogue && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
|
|
1955
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { children: [
|
|
1956
|
+
"Catalogue ",
|
|
1957
|
+
noun.plural,
|
|
1958
|
+
": ",
|
|
1959
|
+
describeCounts(catalogue)
|
|
1960
|
+
] }),
|
|
1961
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { children: [
|
|
1962
|
+
"Playlist ",
|
|
1963
|
+
noun.plural,
|
|
1964
|
+
": ",
|
|
1965
|
+
describeCounts(summary)
|
|
1966
|
+
] })
|
|
1967
|
+
] }),
|
|
1968
|
+
/* @__PURE__ */ (0, import_react13.createElement)(
|
|
1892
1969
|
GenreDetailExtras,
|
|
1893
1970
|
{
|
|
1894
1971
|
...extras,
|
|
@@ -1896,6 +1973,7 @@ function GenreInfoPanelBody({
|
|
|
1896
1973
|
family,
|
|
1897
1974
|
genreUuid,
|
|
1898
1975
|
genrePlaylist,
|
|
1976
|
+
selection,
|
|
1899
1977
|
wikidataId,
|
|
1900
1978
|
scope,
|
|
1901
1979
|
getBackendBaseUrl
|
|
@@ -1905,9 +1983,9 @@ function GenreInfoPanelBody({
|
|
|
1905
1983
|
}
|
|
1906
1984
|
|
|
1907
1985
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1908
|
-
var
|
|
1909
|
-
var
|
|
1910
|
-
var
|
|
1986
|
+
var import_react14 = require("react");
|
|
1987
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1988
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1911
1989
|
function ChangeParentPopup({
|
|
1912
1990
|
genrePlaylist,
|
|
1913
1991
|
genrePlaylists,
|
|
@@ -1916,9 +1994,9 @@ function ChangeParentPopup({
|
|
|
1916
1994
|
onClose,
|
|
1917
1995
|
onParentChanged
|
|
1918
1996
|
}) {
|
|
1919
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1920
|
-
const candidates = (0,
|
|
1921
|
-
() => (0,
|
|
1997
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree19.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1998
|
+
const candidates = (0, import_react14.useMemo)(
|
|
1999
|
+
() => (0, import_genre_tree19.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1922
2000
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1923
2001
|
);
|
|
1924
2002
|
const handleSelect = (newParent) => {
|
|
@@ -1935,19 +2013,19 @@ function ChangeParentPopup({
|
|
|
1935
2013
|
}
|
|
1936
2014
|
);
|
|
1937
2015
|
};
|
|
1938
|
-
(0,
|
|
2016
|
+
(0, import_react14.useEffect)(() => {
|
|
1939
2017
|
const handleKeyDown = (event) => {
|
|
1940
2018
|
if (event.key === "Escape") onClose();
|
|
1941
2019
|
};
|
|
1942
2020
|
window.addEventListener("keydown", handleKeyDown);
|
|
1943
2021
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1944
2022
|
}, [onClose]);
|
|
1945
|
-
return /* @__PURE__ */ (0,
|
|
1946
|
-
/* @__PURE__ */ (0,
|
|
2023
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
2024
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1947
2025
|
"Current parent: ",
|
|
1948
2026
|
genrePlaylist.parent?.name ?? "none"
|
|
1949
2027
|
] }),
|
|
1950
|
-
/* @__PURE__ */ (0,
|
|
2028
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1951
2029
|
GenreSearch,
|
|
1952
2030
|
{
|
|
1953
2031
|
genrePlaylists: candidates,
|
|
@@ -1955,13 +2033,13 @@ function ChangeParentPopup({
|
|
|
1955
2033
|
placeholder: "Search a new parent\u2026"
|
|
1956
2034
|
}
|
|
1957
2035
|
) }),
|
|
1958
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
2036
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1959
2037
|
] });
|
|
1960
2038
|
}
|
|
1961
2039
|
|
|
1962
2040
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1963
2041
|
var import_ui15 = require("@behindthemusictree/ui");
|
|
1964
|
-
var
|
|
2042
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1965
2043
|
|
|
1966
2044
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1967
2045
|
var import_ui11 = require("@behindthemusictree/ui");
|
|
@@ -1970,7 +2048,7 @@ var import_ui11 = require("@behindthemusictree/ui");
|
|
|
1970
2048
|
var import_lucide_react12 = require("lucide-react");
|
|
1971
2049
|
var import_si3 = require("react-icons/si");
|
|
1972
2050
|
var import_ui10 = require("@behindthemusictree/ui");
|
|
1973
|
-
var
|
|
2051
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1974
2052
|
var LINK_KINDS = {
|
|
1975
2053
|
official: { label: "Official website", Icon: import_lucide_react12.Globe },
|
|
1976
2054
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -2007,7 +2085,7 @@ function artistLinks(artist) {
|
|
|
2007
2085
|
return links;
|
|
2008
2086
|
}
|
|
2009
2087
|
function ArtistLinks({ links }) {
|
|
2010
|
-
return /* @__PURE__ */ (0,
|
|
2088
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui10.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
2011
2089
|
}
|
|
2012
2090
|
|
|
2013
2091
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2018,7 +2096,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2018
2096
|
}
|
|
2019
2097
|
|
|
2020
2098
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2021
|
-
var
|
|
2099
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
2022
2100
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2023
2101
|
function ArtistHeader({ artist }) {
|
|
2024
2102
|
const fields = [];
|
|
@@ -2028,35 +2106,35 @@ function ArtistHeader({ artist }) {
|
|
|
2028
2106
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2029
2107
|
if (years) fields.push({ label: "Active", value: years });
|
|
2030
2108
|
const links = artistLinks(artist);
|
|
2031
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2109
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ArtistLinks, { links }) });
|
|
2032
2110
|
if (fields.length === 0) return null;
|
|
2033
|
-
return /* @__PURE__ */ (0,
|
|
2111
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.FieldList, { items: fields });
|
|
2034
2112
|
}
|
|
2035
2113
|
|
|
2036
2114
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2037
|
-
var
|
|
2115
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2038
2116
|
function ExternalLink({ href, children }) {
|
|
2039
|
-
return /* @__PURE__ */ (0,
|
|
2117
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
2040
2118
|
}
|
|
2041
2119
|
function ArtistPhoto({ image, name }) {
|
|
2042
|
-
return /* @__PURE__ */ (0,
|
|
2043
|
-
/* @__PURE__ */ (0,
|
|
2044
|
-
/* @__PURE__ */ (0,
|
|
2120
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
2122
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
2045
2123
|
"Photo: ",
|
|
2046
|
-
/* @__PURE__ */ (0,
|
|
2124
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
2047
2125
|
",",
|
|
2048
2126
|
" ",
|
|
2049
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
2127
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
2050
2128
|
] })
|
|
2051
2129
|
] });
|
|
2052
2130
|
}
|
|
2053
2131
|
|
|
2054
2132
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2055
2133
|
var import_ui12 = require("@behindthemusictree/ui");
|
|
2056
|
-
var
|
|
2134
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2057
2135
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2058
2136
|
if (memberships.length === 0) return null;
|
|
2059
|
-
return /* @__PURE__ */ (0,
|
|
2137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui12.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2060
2138
|
const details = [
|
|
2061
2139
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2062
2140
|
...membership.roles
|
|
@@ -2064,9 +2142,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2064
2142
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
2065
2143
|
return (
|
|
2066
2144
|
// A member can have several periods in the same group.
|
|
2067
|
-
/* @__PURE__ */ (0,
|
|
2068
|
-
/* @__PURE__ */ (0,
|
|
2069
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2145
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2146
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2147
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2070
2148
|
" \u2014 ",
|
|
2071
2149
|
detailsText
|
|
2072
2150
|
] })
|
|
@@ -2077,9 +2155,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2077
2155
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2078
2156
|
if (memberships.length === 0) return null;
|
|
2079
2157
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2080
|
-
return /* @__PURE__ */ (0,
|
|
2081
|
-
/* @__PURE__ */ (0,
|
|
2082
|
-
/* @__PURE__ */ (0,
|
|
2158
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_ui12.PanelSection, { title, children: [
|
|
2159
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2160
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2083
2161
|
MembershipList,
|
|
2084
2162
|
{
|
|
2085
2163
|
title: "Past",
|
|
@@ -2092,7 +2170,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2092
2170
|
|
|
2093
2171
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2094
2172
|
var import_ui13 = require("@behindthemusictree/ui");
|
|
2095
|
-
var
|
|
2173
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2096
2174
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2097
2175
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2098
2176
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2112,34 +2190,34 @@ function byYear(a, b) {
|
|
|
2112
2190
|
function ArtistDiscography({ albums }) {
|
|
2113
2191
|
if (albums.length === 0) return null;
|
|
2114
2192
|
const sorted = [...albums].sort(byYear);
|
|
2115
|
-
return /* @__PURE__ */ (0,
|
|
2193
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui13.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2116
2194
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2117
2195
|
if (groupAlbums.length === 0) return null;
|
|
2118
|
-
return /* @__PURE__ */ (0,
|
|
2119
|
-
/* @__PURE__ */ (0,
|
|
2120
|
-
/* @__PURE__ */ (0,
|
|
2196
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui13.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2197
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2198
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2121
2199
|
] }, album.uuid)) }) }, group);
|
|
2122
2200
|
}) });
|
|
2123
2201
|
}
|
|
2124
2202
|
|
|
2125
2203
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2126
|
-
var
|
|
2204
|
+
var import_react15 = require("react");
|
|
2127
2205
|
var import_react_query2 = require("@tanstack/react-query");
|
|
2128
2206
|
var import_lucide_react13 = require("lucide-react");
|
|
2129
2207
|
var import_ui14 = require("@behindthemusictree/ui");
|
|
2130
2208
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
2131
|
-
var
|
|
2209
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2132
2210
|
var import_transport9 = require("@behindthemusictree/app-kit/transport");
|
|
2133
2211
|
|
|
2134
2212
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2135
2213
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
2136
|
-
var
|
|
2214
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2137
2215
|
|
|
2138
2216
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
2139
2217
|
var import_fa5 = require("react-icons/fa");
|
|
2140
2218
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
2141
2219
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2142
|
-
var
|
|
2220
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2143
2221
|
function TrackPositionPlayPause({
|
|
2144
2222
|
position,
|
|
2145
2223
|
uuid,
|
|
@@ -2153,14 +2231,14 @@ function TrackPositionPlayPause({
|
|
|
2153
2231
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
2154
2232
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
2155
2233
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2156
|
-
return /* @__PURE__ */ (0,
|
|
2234
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2157
2235
|
"div",
|
|
2158
2236
|
{
|
|
2159
2237
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2160
2238
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2161
2239
|
onClick: handlePlayPauseClick,
|
|
2162
2240
|
children: [
|
|
2163
|
-
/* @__PURE__ */ (0,
|
|
2241
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2164
2242
|
"span",
|
|
2165
2243
|
{
|
|
2166
2244
|
className: "track-position-play-pause__bars",
|
|
@@ -2168,18 +2246,18 @@ function TrackPositionPlayPause({
|
|
|
2168
2246
|
role: "img",
|
|
2169
2247
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2170
2248
|
children: [
|
|
2171
|
-
/* @__PURE__ */ (0,
|
|
2172
|
-
/* @__PURE__ */ (0,
|
|
2173
|
-
/* @__PURE__ */ (0,
|
|
2249
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2250
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2251
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2174
2252
|
]
|
|
2175
2253
|
}
|
|
2176
2254
|
) : position }),
|
|
2177
|
-
/* @__PURE__ */ (0,
|
|
2255
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2178
2256
|
"div",
|
|
2179
2257
|
{
|
|
2180
2258
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2181
2259
|
"aria-hidden": "true",
|
|
2182
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2260
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_fa5.FaPlay, {})
|
|
2183
2261
|
}
|
|
2184
2262
|
)
|
|
2185
2263
|
]
|
|
@@ -2188,9 +2266,9 @@ function TrackPositionPlayPause({
|
|
|
2188
2266
|
}
|
|
2189
2267
|
|
|
2190
2268
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2191
|
-
var
|
|
2192
|
-
var
|
|
2193
|
-
var
|
|
2269
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2270
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2271
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2194
2272
|
function TrackItem({
|
|
2195
2273
|
track,
|
|
2196
2274
|
position,
|
|
@@ -2200,11 +2278,11 @@ function TrackItem({
|
|
|
2200
2278
|
renderSongLink
|
|
2201
2279
|
}) {
|
|
2202
2280
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
2203
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
2281
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree20.useTrackList)();
|
|
2204
2282
|
const scope = trackList?.origin?.scope ?? null;
|
|
2205
2283
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
2206
2284
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
2207
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
2285
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree21.isSong)(track) && (0, import_genre_tree21.songUnplayableReason)(track) !== null;
|
|
2208
2286
|
const handlePlayPauseClick = (event) => {
|
|
2209
2287
|
event.stopPropagation();
|
|
2210
2288
|
if (isCurrentTrack) {
|
|
@@ -2214,12 +2292,12 @@ function TrackItem({
|
|
|
2214
2292
|
loadTrackForPlayer(track.uuid);
|
|
2215
2293
|
}
|
|
2216
2294
|
};
|
|
2217
|
-
return /* @__PURE__ */ (0,
|
|
2295
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2218
2296
|
"div",
|
|
2219
2297
|
{
|
|
2220
2298
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2221
2299
|
children: [
|
|
2222
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2300
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2223
2301
|
"div",
|
|
2224
2302
|
{
|
|
2225
2303
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2227,7 +2305,7 @@ function TrackItem({
|
|
|
2227
2305
|
"aria-disabled": "true",
|
|
2228
2306
|
children: position
|
|
2229
2307
|
}
|
|
2230
|
-
) : /* @__PURE__ */ (0,
|
|
2308
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2231
2309
|
TrackPositionPlayPause,
|
|
2232
2310
|
{
|
|
2233
2311
|
position,
|
|
@@ -2236,12 +2314,12 @@ function TrackItem({
|
|
|
2236
2314
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2237
2315
|
}
|
|
2238
2316
|
),
|
|
2239
|
-
/* @__PURE__ */ (0,
|
|
2240
|
-
/* @__PURE__ */ (0,
|
|
2241
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2317
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2318
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree21.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2319
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree22.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2242
2320
|
] }),
|
|
2243
|
-
/* @__PURE__ */ (0,
|
|
2244
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.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_runtime39.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
2322
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2245
2323
|
"div",
|
|
2246
2324
|
{
|
|
2247
2325
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2249,28 +2327,28 @@ function TrackItem({
|
|
|
2249
2327
|
children: renderDuration(track)
|
|
2250
2328
|
}
|
|
2251
2329
|
),
|
|
2252
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2330
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2253
2331
|
]
|
|
2254
2332
|
}
|
|
2255
2333
|
);
|
|
2256
2334
|
}
|
|
2257
2335
|
|
|
2258
2336
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2259
|
-
var
|
|
2260
|
-
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(
|
|
2337
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2338
|
+
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree23.SongDetailedSchema);
|
|
2261
2339
|
var SORTS = [
|
|
2262
2340
|
{ ordering: "title", label: "Title" },
|
|
2263
2341
|
{ ordering: "-albumYear", label: "Year" }
|
|
2264
2342
|
];
|
|
2265
2343
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
2266
|
-
const [page, setPage] = (0,
|
|
2267
|
-
const [ordering, setOrdering] = (0,
|
|
2344
|
+
const [page, setPage] = (0, import_react15.useState)(1);
|
|
2345
|
+
const [ordering, setOrdering] = (0, import_react15.useState)("title");
|
|
2268
2346
|
const { fetch } = (0, import_transport9.useFetchWrapper)(getBackendBaseUrl);
|
|
2269
|
-
const { playNewTrackListFromArtist } = (0,
|
|
2347
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree23.useTrackList)();
|
|
2270
2348
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
2271
2349
|
const { data, isPending, isError } = (0, import_transport9.useQueryWithParse)({
|
|
2272
2350
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
2273
|
-
queryFn: () => fetch(
|
|
2351
|
+
queryFn: () => fetch(import_genre_tree23.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
2274
2352
|
artist: artist.uuid,
|
|
2275
2353
|
ordering,
|
|
2276
2354
|
page
|
|
@@ -2285,25 +2363,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2285
2363
|
});
|
|
2286
2364
|
const handleRowClickCapture = (event, song) => {
|
|
2287
2365
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
2288
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2366
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree23.songUnplayableReason)(song) !== null) return;
|
|
2289
2367
|
event.stopPropagation();
|
|
2290
2368
|
void play(song.uuid);
|
|
2291
2369
|
};
|
|
2292
|
-
return /* @__PURE__ */ (0,
|
|
2293
|
-
/* @__PURE__ */ (0,
|
|
2294
|
-
/* @__PURE__ */ (0,
|
|
2370
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui14.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui14.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
2371
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2372
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
2295
2373
|
"button",
|
|
2296
2374
|
{
|
|
2297
2375
|
type: "button",
|
|
2298
2376
|
onClick: () => void play(),
|
|
2299
2377
|
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",
|
|
2300
2378
|
children: [
|
|
2301
|
-
/* @__PURE__ */ (0,
|
|
2379
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2302
2380
|
"Play all"
|
|
2303
2381
|
]
|
|
2304
2382
|
}
|
|
2305
2383
|
),
|
|
2306
|
-
/* @__PURE__ */ (0,
|
|
2384
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2307
2385
|
"button",
|
|
2308
2386
|
{
|
|
2309
2387
|
type: "button",
|
|
@@ -2318,7 +2396,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2318
2396
|
sort.ordering
|
|
2319
2397
|
)) })
|
|
2320
2398
|
] }),
|
|
2321
|
-
/* @__PURE__ */ (0,
|
|
2399
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2322
2400
|
TrackItem,
|
|
2323
2401
|
{
|
|
2324
2402
|
track: song,
|
|
@@ -2327,12 +2405,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2327
2405
|
renderSongLink
|
|
2328
2406
|
}
|
|
2329
2407
|
) }, song.uuid)) }),
|
|
2330
|
-
/* @__PURE__ */ (0,
|
|
2408
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui14.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2331
2409
|
] }) });
|
|
2332
2410
|
}
|
|
2333
2411
|
|
|
2334
2412
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2335
|
-
var
|
|
2413
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2336
2414
|
function useArtistPanel({
|
|
2337
2415
|
uuid,
|
|
2338
2416
|
scope,
|
|
@@ -2344,7 +2422,7 @@ function useArtistPanel({
|
|
|
2344
2422
|
onExpandedChange,
|
|
2345
2423
|
onClose
|
|
2346
2424
|
}) {
|
|
2347
|
-
const result = (0,
|
|
2425
|
+
const result = (0, import_genre_tree24.useArtist)(uuid, getBackendBaseUrl);
|
|
2348
2426
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2349
2427
|
switch (result.status) {
|
|
2350
2428
|
case "idle":
|
|
@@ -2353,26 +2431,26 @@ function useArtistPanel({
|
|
|
2353
2431
|
return {
|
|
2354
2432
|
...panel,
|
|
2355
2433
|
title: "Artist",
|
|
2356
|
-
content: /* @__PURE__ */ (0,
|
|
2357
|
-
/* @__PURE__ */ (0,
|
|
2358
|
-
/* @__PURE__ */ (0,
|
|
2359
|
-
/* @__PURE__ */ (0,
|
|
2360
|
-
/* @__PURE__ */ (0,
|
|
2361
|
-
/* @__PURE__ */ (0,
|
|
2362
|
-
/* @__PURE__ */ (0,
|
|
2434
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2435
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2436
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Skeleton, { className: "h-4 w-32" }),
|
|
2437
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2438
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Skeleton, { className: "h-4 w-24" }),
|
|
2439
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Skeleton, { className: "h-16" })
|
|
2363
2441
|
] })
|
|
2364
2442
|
};
|
|
2365
2443
|
case "not-found":
|
|
2366
2444
|
return {
|
|
2367
2445
|
...panel,
|
|
2368
2446
|
title: "Artist not found",
|
|
2369
|
-
content: /* @__PURE__ */ (0,
|
|
2447
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2370
2448
|
};
|
|
2371
2449
|
case "error":
|
|
2372
2450
|
return {
|
|
2373
2451
|
...panel,
|
|
2374
2452
|
title: "Artist",
|
|
2375
|
-
content: /* @__PURE__ */ (0,
|
|
2453
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2376
2454
|
};
|
|
2377
2455
|
case "success": {
|
|
2378
2456
|
const { artist } = result;
|
|
@@ -2381,13 +2459,13 @@ function useArtistPanel({
|
|
|
2381
2459
|
return {
|
|
2382
2460
|
...panel,
|
|
2383
2461
|
title: artist.name,
|
|
2384
|
-
content: /* @__PURE__ */ (0,
|
|
2385
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2386
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2387
|
-
/* @__PURE__ */ (0,
|
|
2388
|
-
/* @__PURE__ */ (0,
|
|
2389
|
-
/* @__PURE__ */ (0,
|
|
2390
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2462
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2463
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2464
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2465
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistHeader, { artist }),
|
|
2466
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2467
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2468
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2391
2469
|
ArtistSongs,
|
|
2392
2470
|
{
|
|
2393
2471
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2397,7 +2475,7 @@ function useArtistPanel({
|
|
|
2397
2475
|
}
|
|
2398
2476
|
)
|
|
2399
2477
|
] }),
|
|
2400
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2478
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2401
2479
|
};
|
|
2402
2480
|
}
|
|
2403
2481
|
}
|
|
@@ -2405,48 +2483,48 @@ function useArtistPanel({
|
|
|
2405
2483
|
|
|
2406
2484
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2407
2485
|
var import_ui17 = require("@behindthemusictree/ui");
|
|
2408
|
-
var
|
|
2486
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2409
2487
|
|
|
2410
2488
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2411
|
-
var
|
|
2489
|
+
var import_react16 = require("react");
|
|
2412
2490
|
var import_lucide_react14 = require("lucide-react");
|
|
2413
2491
|
var import_ui16 = require("@behindthemusictree/ui");
|
|
2414
2492
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2415
|
-
var
|
|
2416
|
-
var
|
|
2493
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2494
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2417
2495
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2418
2496
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2419
2497
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2420
2498
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2421
|
-
const [open, setOpen] = (0,
|
|
2422
|
-
const history = (0,
|
|
2499
|
+
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
2500
|
+
const history = (0, import_genre_tree25.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2423
2501
|
let body = null;
|
|
2424
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2425
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2502
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2503
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2426
2504
|
else if (history.status === "success")
|
|
2427
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2428
|
-
/* @__PURE__ */ (0,
|
|
2429
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2505
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2506
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2507
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2430
2508
|
" ",
|
|
2431
2509
|
"\u2014 ",
|
|
2432
2510
|
entry.oldValue ?? "none",
|
|
2433
2511
|
" \u2192 ",
|
|
2434
2512
|
entry.newValue ?? "none"
|
|
2435
2513
|
] }) : null,
|
|
2436
|
-
/* @__PURE__ */ (0,
|
|
2514
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2437
2515
|
entry.actorPseudo ?? "Anonymous",
|
|
2438
2516
|
" \xB7 ",
|
|
2439
2517
|
dateFormat.format(new Date(entry.createdOn))
|
|
2440
2518
|
] })
|
|
2441
2519
|
] }, entry.uuid)) });
|
|
2442
|
-
return /* @__PURE__ */ (0,
|
|
2520
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2443
2521
|
"details",
|
|
2444
2522
|
{
|
|
2445
2523
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2446
2524
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2447
2525
|
children: [
|
|
2448
|
-
/* @__PURE__ */ (0,
|
|
2449
|
-
/* @__PURE__ */ (0,
|
|
2526
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2527
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "mt-1.5", children: body })
|
|
2450
2528
|
]
|
|
2451
2529
|
}
|
|
2452
2530
|
);
|
|
@@ -2460,25 +2538,25 @@ function SongPanelContent({
|
|
|
2460
2538
|
genrePath,
|
|
2461
2539
|
onSelectGenre
|
|
2462
2540
|
}) {
|
|
2463
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2541
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree25.useTrackList)();
|
|
2464
2542
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2465
|
-
const unplayableReason = (0,
|
|
2543
|
+
const unplayableReason = (0, import_genre_tree25.songUnplayableReason)(song);
|
|
2466
2544
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2467
2545
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2468
2546
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2469
|
-
const musicBrainzUrl = (0,
|
|
2547
|
+
const musicBrainzUrl = (0, import_genre_tree25.musicBrainzRecordingUrl)(song);
|
|
2470
2548
|
const fields = [];
|
|
2471
2549
|
if (song.genre) {
|
|
2472
2550
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2473
2551
|
fields.push({
|
|
2474
2552
|
label: "Genre",
|
|
2475
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2476
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2477
|
-
/* @__PURE__ */ (0,
|
|
2553
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { children: [
|
|
2554
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2555
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2478
2556
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2479
2557
|
});
|
|
2480
2558
|
} else {
|
|
2481
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2559
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2482
2560
|
}
|
|
2483
2561
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2484
2562
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2488,11 +2566,11 @@ function SongPanelContent({
|
|
|
2488
2566
|
const links = [];
|
|
2489
2567
|
if (musicBrainzUrl)
|
|
2490
2568
|
links.push(
|
|
2491
|
-
/* @__PURE__ */ (0,
|
|
2569
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2492
2570
|
);
|
|
2493
2571
|
if (song.youtubeVideo)
|
|
2494
2572
|
links.push(
|
|
2495
|
-
/* @__PURE__ */ (0,
|
|
2573
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2496
2574
|
"a",
|
|
2497
2575
|
{
|
|
2498
2576
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2507,33 +2585,33 @@ function SongPanelContent({
|
|
|
2507
2585
|
if (links.length > 0)
|
|
2508
2586
|
fields.push({
|
|
2509
2587
|
label: "Links",
|
|
2510
|
-
value: /* @__PURE__ */ (0,
|
|
2588
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "flex gap-3", children: links })
|
|
2511
2589
|
});
|
|
2512
|
-
return /* @__PURE__ */ (0,
|
|
2513
|
-
/* @__PURE__ */ (0,
|
|
2514
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2592
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
2515
2593
|
"button",
|
|
2516
2594
|
{
|
|
2517
2595
|
type: "button",
|
|
2518
2596
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2519
2597
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2520
2598
|
children: [
|
|
2521
|
-
/* @__PURE__ */ (0,
|
|
2599
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.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_runtime42.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2522
2600
|
isPlaying ? "Pause" : "Play"
|
|
2523
2601
|
]
|
|
2524
2602
|
}
|
|
2525
2603
|
),
|
|
2526
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2604
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2527
2605
|
] }) }),
|
|
2528
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
2530
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2531
|
-
/* @__PURE__ */ (0,
|
|
2606
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_genre_tree25.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2607
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.FieldList, { items: fields }),
|
|
2608
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2609
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2532
2610
|
] });
|
|
2533
2611
|
}
|
|
2534
2612
|
|
|
2535
2613
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2536
|
-
var
|
|
2614
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2537
2615
|
function useSongPanel({
|
|
2538
2616
|
uuid,
|
|
2539
2617
|
scope,
|
|
@@ -2546,7 +2624,7 @@ function useSongPanel({
|
|
|
2546
2624
|
onExpandedChange,
|
|
2547
2625
|
onClose
|
|
2548
2626
|
}) {
|
|
2549
|
-
const result = (0,
|
|
2627
|
+
const result = (0, import_genre_tree26.useSong)(uuid, getBackendBaseUrl);
|
|
2550
2628
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2551
2629
|
switch (result.status) {
|
|
2552
2630
|
case "idle":
|
|
@@ -2555,24 +2633,24 @@ function useSongPanel({
|
|
|
2555
2633
|
return {
|
|
2556
2634
|
...panel,
|
|
2557
2635
|
title: "Song",
|
|
2558
|
-
content: /* @__PURE__ */ (0,
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2560
|
-
/* @__PURE__ */ (0,
|
|
2561
|
-
/* @__PURE__ */ (0,
|
|
2562
|
-
/* @__PURE__ */ (0,
|
|
2636
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2637
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2638
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
|
|
2639
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2640
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" })
|
|
2563
2641
|
] })
|
|
2564
2642
|
};
|
|
2565
2643
|
case "not-found":
|
|
2566
2644
|
return {
|
|
2567
2645
|
...panel,
|
|
2568
2646
|
title: "Song not found",
|
|
2569
|
-
content: /* @__PURE__ */ (0,
|
|
2647
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2570
2648
|
};
|
|
2571
2649
|
case "error":
|
|
2572
2650
|
return {
|
|
2573
2651
|
...panel,
|
|
2574
2652
|
title: "Song",
|
|
2575
|
-
content: /* @__PURE__ */ (0,
|
|
2653
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2576
2654
|
};
|
|
2577
2655
|
case "success": {
|
|
2578
2656
|
const { song } = result;
|
|
@@ -2580,7 +2658,7 @@ function useSongPanel({
|
|
|
2580
2658
|
return {
|
|
2581
2659
|
...panel,
|
|
2582
2660
|
title: song.title,
|
|
2583
|
-
content: /* @__PURE__ */ (0,
|
|
2661
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2584
2662
|
SongPanelContent,
|
|
2585
2663
|
{
|
|
2586
2664
|
song,
|
|
@@ -2600,12 +2678,12 @@ function useSongPanel({
|
|
|
2600
2678
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2601
2679
|
var import_md2 = require("react-icons/md");
|
|
2602
2680
|
var import_ui18 = require("@behindthemusictree/ui");
|
|
2603
|
-
var
|
|
2681
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2604
2682
|
function SizeMetricToggle({
|
|
2605
2683
|
value,
|
|
2606
2684
|
onChange
|
|
2607
2685
|
}) {
|
|
2608
|
-
return /* @__PURE__ */ (0,
|
|
2686
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2609
2687
|
import_ui18.SegmentedControl,
|
|
2610
2688
|
{
|
|
2611
2689
|
variant: "floating",
|
|
@@ -2613,15 +2691,15 @@ function SizeMetricToggle({
|
|
|
2613
2691
|
value,
|
|
2614
2692
|
onChange,
|
|
2615
2693
|
options: [
|
|
2616
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2617
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2694
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2695
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2618
2696
|
]
|
|
2619
2697
|
}
|
|
2620
2698
|
);
|
|
2621
2699
|
}
|
|
2622
2700
|
|
|
2623
2701
|
// src/genre-tree/GenreTreeView.tsx
|
|
2624
|
-
var
|
|
2702
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2625
2703
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2626
2704
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2627
2705
|
var noop = () => {
|
|
@@ -2656,6 +2734,7 @@ function GenreTreeView({
|
|
|
2656
2734
|
onArtistExpandedChange,
|
|
2657
2735
|
getShareUrl,
|
|
2658
2736
|
extraSearchGroups = [],
|
|
2737
|
+
SearchGroups,
|
|
2659
2738
|
onSearchQueryChange,
|
|
2660
2739
|
onSearchFocus,
|
|
2661
2740
|
artistSearch = true,
|
|
@@ -2682,21 +2761,21 @@ function GenreTreeView({
|
|
|
2682
2761
|
placement = "default",
|
|
2683
2762
|
...focus
|
|
2684
2763
|
}) {
|
|
2685
|
-
const getShareUrlRef = (0,
|
|
2764
|
+
const getShareUrlRef = (0, import_react17.useRef)(getShareUrl);
|
|
2686
2765
|
getShareUrlRef.current = getShareUrl;
|
|
2687
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
2766
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react17.useState)(null);
|
|
2688
2767
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2689
2768
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2690
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
2769
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react17.useState)(null);
|
|
2691
2770
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2692
2771
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2693
2772
|
const changeSong = (uuid) => {
|
|
2694
2773
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2695
2774
|
onSongUuidChange?.(uuid);
|
|
2696
2775
|
};
|
|
2697
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
2776
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react17.useState)("songs");
|
|
2698
2777
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2699
|
-
const deferredSizeMetric = (0,
|
|
2778
|
+
const deferredSizeMetric = (0, import_react17.useDeferredValue)(sizeMetric);
|
|
2700
2779
|
const changeSizeMetric = (metric) => {
|
|
2701
2780
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2702
2781
|
onSizeMetricChange?.(metric);
|
|
@@ -2723,7 +2802,7 @@ function GenreTreeView({
|
|
|
2723
2802
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2724
2803
|
toggleWheelRotation,
|
|
2725
2804
|
onNodeHover: handleNodeHover
|
|
2726
|
-
} = (0,
|
|
2805
|
+
} = (0, import_genre_tree27.useGenreTreeViewState)({
|
|
2727
2806
|
scope,
|
|
2728
2807
|
getBackendBaseUrl,
|
|
2729
2808
|
viewMode: controlledViewMode,
|
|
@@ -2733,7 +2812,7 @@ function GenreTreeView({
|
|
|
2733
2812
|
artistUuid
|
|
2734
2813
|
});
|
|
2735
2814
|
const restoreRevision = viewportRestore?.revision;
|
|
2736
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
2815
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react17.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2737
2816
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2738
2817
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2739
2818
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2742,14 +2821,14 @@ function GenreTreeView({
|
|
|
2742
2821
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2743
2822
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2744
2823
|
}
|
|
2745
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2746
|
-
const { mutate: updateGenre } = (0,
|
|
2824
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree27.genreUpdateMutationKey }) > 0;
|
|
2825
|
+
const { mutate: updateGenre } = (0, import_genre_tree27.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2747
2826
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2748
2827
|
const isArtistFiltered = artistUuid !== null;
|
|
2749
2828
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2750
|
-
const artistResult = (0,
|
|
2829
|
+
const artistResult = (0, import_genre_tree27.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2751
2830
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2752
|
-
const filterArtist = (0,
|
|
2831
|
+
const filterArtist = (0, import_react17.useMemo)(
|
|
2753
2832
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2754
2833
|
[artistUuid, filterArtistName]
|
|
2755
2834
|
);
|
|
@@ -2764,14 +2843,14 @@ function GenreTreeView({
|
|
|
2764
2843
|
if (selectedNodeId !== null) clearSelection();
|
|
2765
2844
|
changeSong(uuid);
|
|
2766
2845
|
};
|
|
2767
|
-
const openSongRef = (0,
|
|
2846
|
+
const openSongRef = (0, import_react17.useRef)(openSong);
|
|
2768
2847
|
openSongRef.current = openSong;
|
|
2769
|
-
const resolvedRenderSongLink = (0,
|
|
2770
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2848
|
+
const resolvedRenderSongLink = (0, import_react17.useCallback)(
|
|
2849
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2771
2850
|
[renderSongLink]
|
|
2772
2851
|
);
|
|
2773
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
2774
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
2852
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react17.useState)(null);
|
|
2853
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react17.useState)(null);
|
|
2775
2854
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2776
2855
|
const songBaseUrl = getBackendBaseUrl;
|
|
2777
2856
|
const artistPanel = useArtistPanel({
|
|
@@ -2779,7 +2858,7 @@ function GenreTreeView({
|
|
|
2779
2858
|
scope,
|
|
2780
2859
|
getBackendBaseUrl: artistBaseUrl,
|
|
2781
2860
|
getShareUrl: getShareUrlRef.current,
|
|
2782
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2861
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2783
2862
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2784
2863
|
onExpandedChange: (on) => {
|
|
2785
2864
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2788,7 +2867,7 @@ function GenreTreeView({
|
|
|
2788
2867
|
renderSongLink: resolvedRenderSongLink,
|
|
2789
2868
|
onClose: () => changeArtist(null)
|
|
2790
2869
|
});
|
|
2791
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2870
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2792
2871
|
const getGenrePath = (genre) => {
|
|
2793
2872
|
const path = [];
|
|
2794
2873
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2820,8 +2899,8 @@ function GenreTreeView({
|
|
|
2820
2899
|
const builtInSongPanel = overlayPanel || hasSelectedGenreNode ? void 0 : songPanel;
|
|
2821
2900
|
const builtInArtistPanel = overlayPanel || hasSelectedGenreNode || builtInSongPanel ? void 0 : artistPanel;
|
|
2822
2901
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2823
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0,
|
|
2824
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
2902
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react17.useState)(false);
|
|
2903
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react17.useState)(false);
|
|
2825
2904
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2826
2905
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2827
2906
|
const changeCollapsed = (next) => {
|
|
@@ -2832,28 +2911,28 @@ function GenreTreeView({
|
|
|
2832
2911
|
setUncontrolledExpanded(next);
|
|
2833
2912
|
onExpandedChange?.(next);
|
|
2834
2913
|
};
|
|
2835
|
-
(0,
|
|
2914
|
+
(0, import_react17.useEffect)(() => {
|
|
2836
2915
|
setUncontrolledCollapsed(false);
|
|
2837
2916
|
}, [selectedNodeId, artistUuid, songUuid]);
|
|
2838
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0,
|
|
2917
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react17.useState)(false);
|
|
2839
2918
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2840
2919
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2841
|
-
const [resolveStyle, setResolveStyle] = (0,
|
|
2842
|
-
const handleNodeStyleResolverChange = (0,
|
|
2920
|
+
const [resolveStyle, setResolveStyle] = (0, import_react17.useState)(null);
|
|
2921
|
+
const handleNodeStyleResolverChange = (0, import_react17.useCallback)(
|
|
2843
2922
|
(resolver) => setResolveStyle(() => resolver),
|
|
2844
2923
|
[]
|
|
2845
2924
|
);
|
|
2846
|
-
const panelRef = (0,
|
|
2925
|
+
const panelRef = (0, import_react17.useRef)(null);
|
|
2847
2926
|
const obscuredInset = (0, import_ui19.useObscuredInset)(panelRef);
|
|
2848
2927
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2849
2928
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2850
|
-
const treeNodes = (0,
|
|
2929
|
+
const treeNodes = (0, import_react17.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
2851
2930
|
const relations = (0, import_genre_tree_view4.useGenreNodeRelations)(
|
|
2852
2931
|
treeNodes,
|
|
2853
2932
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2854
2933
|
resolveStyle
|
|
2855
2934
|
);
|
|
2856
|
-
const [selectionHistory, setSelectionHistory] = (0,
|
|
2935
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react17.useState)({
|
|
2857
2936
|
current: selectedNodeId,
|
|
2858
2937
|
previous: null
|
|
2859
2938
|
});
|
|
@@ -2866,28 +2945,6 @@ function GenreTreeView({
|
|
|
2866
2945
|
const previousPlaylist = selectionHistory.previous !== null && selectionHistory.previous !== selectedNodeId ? genrePlaylistByNodeId.get(selectionHistory.previous) : void 0;
|
|
2867
2946
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2868
2947
|
const { showPopup, hidePopup } = (0, import_popup6.usePopup)();
|
|
2869
|
-
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2870
|
-
const artistGroup = useArtistSearchGroup(
|
|
2871
|
-
artistBaseUrl,
|
|
2872
|
-
searchText,
|
|
2873
|
-
(artist) => {
|
|
2874
|
-
openArtist(artist.uuid);
|
|
2875
|
-
onArtistSelect?.(artist);
|
|
2876
|
-
},
|
|
2877
|
-
Boolean(artistSearch)
|
|
2878
|
-
);
|
|
2879
|
-
const songGroup = useSongSearchGroup(
|
|
2880
|
-
songBaseUrl,
|
|
2881
|
-
searchText,
|
|
2882
|
-
(song) => {
|
|
2883
|
-
openSong(song.uuid);
|
|
2884
|
-
onSongSelect?.(song);
|
|
2885
|
-
},
|
|
2886
|
-
Boolean(songSearch)
|
|
2887
|
-
);
|
|
2888
|
-
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2889
|
-
(group) => group !== void 0
|
|
2890
|
-
);
|
|
2891
2948
|
const handleSelectedNodeChange = (node) => {
|
|
2892
2949
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2893
2950
|
selectNode(node);
|
|
@@ -2903,7 +2960,7 @@ function GenreTreeView({
|
|
|
2903
2960
|
const handleGenreSearchZoomIn = () => {
|
|
2904
2961
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2905
2962
|
};
|
|
2906
|
-
const zoomInRef = (0,
|
|
2963
|
+
const zoomInRef = (0, import_react17.useRef)(void 0);
|
|
2907
2964
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2908
2965
|
const selectGenreFromPanel = (nodeId) => {
|
|
2909
2966
|
const genrePlaylist = genrePlaylistByNodeId.get(nodeId);
|
|
@@ -2911,7 +2968,7 @@ function GenreTreeView({
|
|
|
2911
2968
|
};
|
|
2912
2969
|
const renderGenreBody = (relation) => {
|
|
2913
2970
|
const { node } = relation;
|
|
2914
|
-
const family = /* @__PURE__ */ (0,
|
|
2971
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2915
2972
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2916
2973
|
{
|
|
2917
2974
|
node,
|
|
@@ -2922,10 +2979,10 @@ function GenreTreeView({
|
|
|
2922
2979
|
onSelectNode: selectGenreFromPanel
|
|
2923
2980
|
}
|
|
2924
2981
|
);
|
|
2925
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2982
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2926
2983
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2927
2984
|
const criteria = genrePlaylist?.criteria;
|
|
2928
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0,
|
|
2985
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
2929
2986
|
family,
|
|
2930
2987
|
ancestors
|
|
2931
2988
|
] }) };
|
|
@@ -2945,16 +3002,17 @@ function GenreTreeView({
|
|
|
2945
3002
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2946
3003
|
) : void 0;
|
|
2947
3004
|
return {
|
|
2948
|
-
primary: /* @__PURE__ */ (0,
|
|
2949
|
-
/* @__PURE__ */ (0,
|
|
3005
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2950
3007
|
GenreInfoPanelBody,
|
|
2951
3008
|
{
|
|
2952
|
-
family: /* @__PURE__ */ (0,
|
|
3009
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
2953
3010
|
family,
|
|
2954
3011
|
ancestors
|
|
2955
3012
|
] }),
|
|
2956
3013
|
genreUuid,
|
|
2957
3014
|
genrePlaylist,
|
|
3015
|
+
catalogue: genrePlaylist.catalogue,
|
|
2958
3016
|
wikidataId: criteria.wikidataId,
|
|
2959
3017
|
scope,
|
|
2960
3018
|
getBackendBaseUrl,
|
|
@@ -2967,7 +3025,7 @@ function GenreTreeView({
|
|
|
2967
3025
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2968
3026
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2969
3027
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2970
|
-
/* @__PURE__ */ (0,
|
|
3028
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2971
3029
|
ChangeParentPopup,
|
|
2972
3030
|
{
|
|
2973
3031
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -3042,19 +3100,19 @@ function GenreTreeView({
|
|
|
3042
3100
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
3043
3101
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
3044
3102
|
} : void 0;
|
|
3045
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
3103
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3046
3104
|
import_ui19.IconButton,
|
|
3047
3105
|
{
|
|
3048
3106
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
3049
3107
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
3050
3108
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
3051
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3109
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react16.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react16.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3052
3110
|
}
|
|
3053
3111
|
) : null;
|
|
3054
3112
|
const renderArtistFilter = () => {
|
|
3055
3113
|
if (isLoading || !filterArtist) return null;
|
|
3056
|
-
return /* @__PURE__ */ (0,
|
|
3057
|
-
/* @__PURE__ */ (0,
|
|
3114
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
3115
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3058
3116
|
"button",
|
|
3059
3117
|
{
|
|
3060
3118
|
type: "button",
|
|
@@ -3066,7 +3124,7 @@ function GenreTreeView({
|
|
|
3066
3124
|
children: filterArtist.name
|
|
3067
3125
|
}
|
|
3068
3126
|
),
|
|
3069
|
-
/* @__PURE__ */ (0,
|
|
3127
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3070
3128
|
"button",
|
|
3071
3129
|
{
|
|
3072
3130
|
type: "button",
|
|
@@ -3078,26 +3136,36 @@ function GenreTreeView({
|
|
|
3078
3136
|
)
|
|
3079
3137
|
] });
|
|
3080
3138
|
};
|
|
3081
|
-
const actions = /* @__PURE__ */ (0,
|
|
3082
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
3083
|
-
|
|
3139
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
3140
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3141
|
+
GenreTreeSearch,
|
|
3084
3142
|
{
|
|
3143
|
+
artistBaseUrl,
|
|
3144
|
+
songBaseUrl,
|
|
3145
|
+
artistSearch: Boolean(artistSearch),
|
|
3146
|
+
songSearch: Boolean(songSearch),
|
|
3147
|
+
onArtistSelect: (artist) => {
|
|
3148
|
+
openArtist(artist.uuid);
|
|
3149
|
+
onArtistSelect?.(artist);
|
|
3150
|
+
},
|
|
3151
|
+
onSongSelect: (song) => {
|
|
3152
|
+
openSong(song.uuid);
|
|
3153
|
+
onSongSelect?.(song);
|
|
3154
|
+
},
|
|
3155
|
+
SearchGroups,
|
|
3085
3156
|
genrePlaylists: visibleGenrePlaylists,
|
|
3086
3157
|
onSelect: handleGenreSearchSelect,
|
|
3087
3158
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
3088
3159
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
3089
3160
|
onPrevious: handlePrevious,
|
|
3090
|
-
onQueryChange:
|
|
3091
|
-
setSearchText(text);
|
|
3092
|
-
onSearchQueryChange?.(text);
|
|
3093
|
-
},
|
|
3161
|
+
onQueryChange: onSearchQueryChange,
|
|
3094
3162
|
onFocus: onSearchFocus,
|
|
3095
|
-
extraGroups
|
|
3163
|
+
extraGroups: extraSearchGroups
|
|
3096
3164
|
}
|
|
3097
3165
|
),
|
|
3098
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3099
|
-
/* @__PURE__ */ (0,
|
|
3100
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
3166
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3167
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.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: [
|
|
3168
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3101
3169
|
import_ui19.SegmentedControl,
|
|
3102
3170
|
{
|
|
3103
3171
|
"aria-label": "Tree view mode",
|
|
@@ -3111,13 +3179,13 @@ function GenreTreeView({
|
|
|
3111
3179
|
]
|
|
3112
3180
|
}
|
|
3113
3181
|
),
|
|
3114
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3182
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3115
3183
|
"div",
|
|
3116
3184
|
{
|
|
3117
3185
|
className: "flex items-center gap-1",
|
|
3118
3186
|
role: "group",
|
|
3119
3187
|
"aria-label": "Tree display options",
|
|
3120
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3188
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3121
3189
|
import_ui19.Button,
|
|
3122
3190
|
{
|
|
3123
3191
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -3128,7 +3196,7 @@ function GenreTreeView({
|
|
|
3128
3196
|
)
|
|
3129
3197
|
}
|
|
3130
3198
|
),
|
|
3131
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3199
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3132
3200
|
import_ui19.IconTextButton,
|
|
3133
3201
|
{
|
|
3134
3202
|
icon: import_lucide_react15.Plus,
|
|
@@ -3138,34 +3206,34 @@ function GenreTreeView({
|
|
|
3138
3206
|
)
|
|
3139
3207
|
] })
|
|
3140
3208
|
] });
|
|
3141
|
-
return /* @__PURE__ */ (0,
|
|
3209
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
3142
3210
|
"div",
|
|
3143
3211
|
{
|
|
3144
3212
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3145
3213
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3146
3214
|
children: [
|
|
3147
|
-
/* @__PURE__ */ (0,
|
|
3215
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3148
3216
|
"div",
|
|
3149
3217
|
{
|
|
3150
3218
|
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"}`,
|
|
3151
3219
|
children: actions
|
|
3152
3220
|
}
|
|
3153
3221
|
),
|
|
3154
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3222
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3155
3223
|
"div",
|
|
3156
3224
|
{
|
|
3157
3225
|
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"}`,
|
|
3158
3226
|
children: renderArtistFilter()
|
|
3159
3227
|
}
|
|
3160
3228
|
),
|
|
3161
|
-
/* @__PURE__ */ (0,
|
|
3162
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
3163
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3229
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3230
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3231
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
3164
3232
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3165
3233
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3166
3234
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3167
3235
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3168
|
-
/* @__PURE__ */ (0,
|
|
3236
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3169
3237
|
GenrePlaylistTreeWheel,
|
|
3170
3238
|
{
|
|
3171
3239
|
scope,
|
|
@@ -3182,9 +3250,9 @@ function GenreTreeView({
|
|
|
3182
3250
|
allowWheelRotation,
|
|
3183
3251
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3184
3252
|
busy,
|
|
3185
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3253
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3186
3254
|
floatingControlsLeading,
|
|
3187
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3255
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3188
3256
|
] }) : void 0,
|
|
3189
3257
|
defaultViewport,
|
|
3190
3258
|
viewportRestore,
|
|
@@ -3193,7 +3261,7 @@ function GenreTreeView({
|
|
|
3193
3261
|
) }) })
|
|
3194
3262
|
) : viewMode === "pop-core" ? (
|
|
3195
3263
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3196
|
-
/* @__PURE__ */ (0,
|
|
3264
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3197
3265
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3198
3266
|
{
|
|
3199
3267
|
scope,
|
|
@@ -3210,9 +3278,9 @@ function GenreTreeView({
|
|
|
3210
3278
|
allowWheelRotation,
|
|
3211
3279
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3212
3280
|
busy,
|
|
3213
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3281
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3214
3282
|
floatingControlsLeading,
|
|
3215
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3283
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3216
3284
|
] }) : void 0,
|
|
3217
3285
|
defaultViewport,
|
|
3218
3286
|
viewportRestore,
|
|
@@ -3220,7 +3288,7 @@ function GenreTreeView({
|
|
|
3220
3288
|
...focus
|
|
3221
3289
|
}
|
|
3222
3290
|
) }) })
|
|
3223
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3291
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3224
3292
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3225
3293
|
{
|
|
3226
3294
|
outline: true,
|
|
@@ -3236,13 +3304,13 @@ function GenreTreeView({
|
|
|
3236
3304
|
selectedNodeId,
|
|
3237
3305
|
readOnly
|
|
3238
3306
|
}
|
|
3239
|
-
) }) : /* @__PURE__ */ (0,
|
|
3307
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime45.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(
|
|
3240
3308
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3241
|
-
return /* @__PURE__ */ (0,
|
|
3309
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3242
3310
|
"div",
|
|
3243
3311
|
{
|
|
3244
3312
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3245
|
-
children: /* @__PURE__ */ (0,
|
|
3313
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3246
3314
|
GenrePlaylistTreePerRoot,
|
|
3247
3315
|
{
|
|
3248
3316
|
scope,
|
|
@@ -3268,7 +3336,7 @@ function GenreTreeView({
|
|
|
3268
3336
|
);
|
|
3269
3337
|
}
|
|
3270
3338
|
) }),
|
|
3271
|
-
/* @__PURE__ */ (0,
|
|
3339
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3272
3340
|
import_ui19.DetailPanel,
|
|
3273
3341
|
{
|
|
3274
3342
|
ref: panelRef,
|
|
@@ -3285,7 +3353,7 @@ function GenreTreeView({
|
|
|
3285
3353
|
aside: activePanel?.aside,
|
|
3286
3354
|
edgeTab: detailTab,
|
|
3287
3355
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
3288
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3356
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
3289
3357
|
activePanel.content,
|
|
3290
3358
|
activePanel.aside
|
|
3291
3359
|
] }) : activePanel?.content
|
|
@@ -3298,12 +3366,12 @@ function GenreTreeView({
|
|
|
3298
3366
|
}
|
|
3299
3367
|
|
|
3300
3368
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
3301
|
-
var
|
|
3369
|
+
var import_react18 = require("react");
|
|
3302
3370
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3303
3371
|
var import_ui20 = require("@behindthemusictree/ui");
|
|
3304
3372
|
var import_lucide_react17 = require("lucide-react");
|
|
3305
|
-
var
|
|
3306
|
-
var
|
|
3373
|
+
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3374
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
3307
3375
|
var RELATIVE_ITEM_COUNT = 1;
|
|
3308
3376
|
function toNode(genre, parentId) {
|
|
3309
3377
|
return {
|
|
@@ -3330,9 +3398,9 @@ function GenreDetailPanel({
|
|
|
3330
3398
|
edgeTab,
|
|
3331
3399
|
extras
|
|
3332
3400
|
}) {
|
|
3333
|
-
const { data: detail, isPending, isError } = (0,
|
|
3401
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree28.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3334
3402
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3335
|
-
const view = (0,
|
|
3403
|
+
const view = (0, import_react18.useMemo)(() => {
|
|
3336
3404
|
if (!detail) return null;
|
|
3337
3405
|
const rootColor = (0, import_genre_tree_view5.getGenreTreeColor)(detail.root.wikidataId);
|
|
3338
3406
|
const relative = (genre, parentId) => ({
|
|
@@ -3346,7 +3414,7 @@ function GenreDetailPanel({
|
|
|
3346
3414
|
id: detail.uuid,
|
|
3347
3415
|
parentId: detail.parent?.uuid ?? null,
|
|
3348
3416
|
name: detail.name,
|
|
3349
|
-
itemCount: detail.tracksCount,
|
|
3417
|
+
itemCount: scope === "reference" ? (0, import_genre_tree28.genreMusicCount)(import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
|
|
3350
3418
|
wikidataId: detail.wikidataId
|
|
3351
3419
|
},
|
|
3352
3420
|
fill: rootColor,
|
|
@@ -3356,19 +3424,19 @@ function GenreDetailPanel({
|
|
|
3356
3424
|
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
3357
3425
|
)
|
|
3358
3426
|
};
|
|
3359
|
-
}, [detail]);
|
|
3427
|
+
}, [detail, scope]);
|
|
3360
3428
|
if (isError) {
|
|
3361
|
-
return /* @__PURE__ */ (0,
|
|
3429
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3362
3430
|
}
|
|
3363
3431
|
if (isPending || !detail || !view) {
|
|
3364
|
-
return /* @__PURE__ */ (0,
|
|
3365
|
-
/* @__PURE__ */ (0,
|
|
3366
|
-
/* @__PURE__ */ (0,
|
|
3367
|
-
/* @__PURE__ */ (0,
|
|
3432
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3433
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-6 w-2/3" }),
|
|
3434
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-4 w-full" }),
|
|
3435
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-4 w-3/4" })
|
|
3368
3436
|
] });
|
|
3369
3437
|
}
|
|
3370
3438
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3371
|
-
const family = /* @__PURE__ */ (0,
|
|
3439
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3372
3440
|
import_genre_tree_view5.GenreNodeFamily,
|
|
3373
3441
|
{
|
|
3374
3442
|
node: view.node,
|
|
@@ -3379,20 +3447,20 @@ function GenreDetailPanel({
|
|
|
3379
3447
|
onSelectNode: selectGenre
|
|
3380
3448
|
}
|
|
3381
3449
|
);
|
|
3382
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
3383
|
-
return /* @__PURE__ */ (0,
|
|
3450
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3451
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3384
3452
|
import_ui20.DetailPanel,
|
|
3385
3453
|
{
|
|
3386
3454
|
side: "left",
|
|
3387
3455
|
title: view.node.name,
|
|
3388
3456
|
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3389
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
3457
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3390
3458
|
import_ui20.IconButton,
|
|
3391
3459
|
{
|
|
3392
3460
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3393
3461
|
style: { color: "inherit" },
|
|
3394
3462
|
onClick: () => onExpandedChange(!expanded),
|
|
3395
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
3463
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3396
3464
|
}
|
|
3397
3465
|
) : void 0,
|
|
3398
3466
|
onClose,
|
|
@@ -3401,15 +3469,16 @@ function GenreDetailPanel({
|
|
|
3401
3469
|
onExpandedChange,
|
|
3402
3470
|
edgeTab,
|
|
3403
3471
|
children: [
|
|
3404
|
-
/* @__PURE__ */ (0,
|
|
3472
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3405
3473
|
GenreInfoPanelBody,
|
|
3406
3474
|
{
|
|
3407
|
-
family: /* @__PURE__ */ (0,
|
|
3475
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3408
3476
|
family,
|
|
3409
3477
|
ancestors
|
|
3410
3478
|
] }),
|
|
3411
3479
|
genreUuid: detail.uuid,
|
|
3412
|
-
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3480
|
+
genrePlaylist: scope === "reference" ? import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3481
|
+
catalogue: detail.catalogue,
|
|
3413
3482
|
wikidataId: detail.wikidataId,
|
|
3414
3483
|
scope,
|
|
3415
3484
|
getBackendBaseUrl,
|
|
@@ -3428,13 +3497,13 @@ function GenreDetailPanel({
|
|
|
3428
3497
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3429
3498
|
|
|
3430
3499
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3431
|
-
var
|
|
3500
|
+
var import_react19 = require("react");
|
|
3432
3501
|
var import_lucide_react18 = require("lucide-react");
|
|
3433
|
-
var
|
|
3434
|
-
var
|
|
3435
|
-
var
|
|
3436
|
-
var
|
|
3437
|
-
var useBrowserLayoutEffect = typeof window === "undefined" ?
|
|
3502
|
+
var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3503
|
+
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3504
|
+
var import_track_noun5 = require("@behindthemusictree/app-kit/track-noun");
|
|
3505
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
3506
|
+
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react19.useEffect : import_react19.useLayoutEffect;
|
|
3438
3507
|
function TrackListSidebar({
|
|
3439
3508
|
className: className2 = "",
|
|
3440
3509
|
renderDuration,
|
|
@@ -3446,18 +3515,18 @@ function TrackListSidebar({
|
|
|
3446
3515
|
renderArtistLink,
|
|
3447
3516
|
renderSongLink
|
|
3448
3517
|
}) {
|
|
3449
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3450
|
-
const trackNoun = (0,
|
|
3451
|
-
const trackListRef = (0,
|
|
3452
|
-
const previousOriginRef = (0,
|
|
3453
|
-
const handledAutoAdvanceRequestIdRef = (0,
|
|
3454
|
-
const shouldFollowPlaybackRef = (0,
|
|
3455
|
-
const programmaticScrollTopRef = (0,
|
|
3456
|
-
const userScrollIntentRef = (0,
|
|
3457
|
-
const userScrollIntentTimerRef = (0,
|
|
3458
|
-
const loadMoreSentinelRef = (0,
|
|
3518
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree29.useTrackList)();
|
|
3519
|
+
const trackNoun = (0, import_track_noun5.useTrackNoun)();
|
|
3520
|
+
const trackListRef = (0, import_react19.useRef)(null);
|
|
3521
|
+
const previousOriginRef = (0, import_react19.useRef)(trackList?.origin ?? null);
|
|
3522
|
+
const handledAutoAdvanceRequestIdRef = (0, import_react19.useRef)(null);
|
|
3523
|
+
const shouldFollowPlaybackRef = (0, import_react19.useRef)(true);
|
|
3524
|
+
const programmaticScrollTopRef = (0, import_react19.useRef)(null);
|
|
3525
|
+
const userScrollIntentRef = (0, import_react19.useRef)(false);
|
|
3526
|
+
const userScrollIntentTimerRef = (0, import_react19.useRef)(null);
|
|
3527
|
+
const loadMoreSentinelRef = (0, import_react19.useRef)(null);
|
|
3459
3528
|
const hasMore = !!trackList && trackList.nextPage !== null;
|
|
3460
|
-
(0,
|
|
3529
|
+
(0, import_react19.useEffect)(() => {
|
|
3461
3530
|
const sentinel = loadMoreSentinelRef.current;
|
|
3462
3531
|
if (!hasMore || !sentinel) return;
|
|
3463
3532
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -3477,11 +3546,11 @@ function TrackListSidebar({
|
|
|
3477
3546
|
programmaticScrollTopRef.current = null;
|
|
3478
3547
|
if (trackListRef.current) trackListRef.current.scrollTop = 0;
|
|
3479
3548
|
}, [trackList?.origin]);
|
|
3480
|
-
(0,
|
|
3549
|
+
(0, import_react19.useEffect)(() => {
|
|
3481
3550
|
const list = trackListRef.current;
|
|
3482
3551
|
if (!isCollapsed && list?.scrollTop === 0) shouldFollowPlaybackRef.current = true;
|
|
3483
3552
|
}, [isCollapsed]);
|
|
3484
|
-
(0,
|
|
3553
|
+
(0, import_react19.useEffect)(
|
|
3485
3554
|
() => () => {
|
|
3486
3555
|
if (userScrollIntentTimerRef.current !== null) clearTimeout(userScrollIntentTimerRef.current);
|
|
3487
3556
|
},
|
|
@@ -3535,25 +3604,25 @@ function TrackListSidebar({
|
|
|
3535
3604
|
}
|
|
3536
3605
|
};
|
|
3537
3606
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3538
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3607
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3539
3608
|
"div",
|
|
3540
3609
|
{
|
|
3541
3610
|
"data-theme": "dark",
|
|
3542
3611
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3543
3612
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3544
3613
|
children: [
|
|
3545
|
-
/* @__PURE__ */ (0,
|
|
3546
|
-
/* @__PURE__ */ (0,
|
|
3547
|
-
/* @__PURE__ */ (0,
|
|
3548
|
-
/* @__PURE__ */ (0,
|
|
3549
|
-
/* @__PURE__ */ (0,
|
|
3614
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3615
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3616
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3617
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3618
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.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_runtime47.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3550
3619
|
] }),
|
|
3551
|
-
/* @__PURE__ */ (0,
|
|
3552
|
-
trackList.origin.type ===
|
|
3620
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3621
|
+
trackList.origin.type === import_genre_tree30.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree30.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3553
3622
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3554
3623
|
] })
|
|
3555
3624
|
] }),
|
|
3556
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3625
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3557
3626
|
"button",
|
|
3558
3627
|
{
|
|
3559
3628
|
type: "button",
|
|
@@ -3562,11 +3631,11 @@ function TrackListSidebar({
|
|
|
3562
3631
|
"aria-expanded": !isCollapsed,
|
|
3563
3632
|
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"}`,
|
|
3564
3633
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3565
|
-
children: /* @__PURE__ */ (0,
|
|
3634
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react18.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3566
3635
|
}
|
|
3567
3636
|
)
|
|
3568
3637
|
] }),
|
|
3569
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3638
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3570
3639
|
"ul",
|
|
3571
3640
|
{
|
|
3572
3641
|
ref: trackListRef,
|
|
@@ -3576,7 +3645,7 @@ function TrackListSidebar({
|
|
|
3576
3645
|
onPointerDown: markUserScrollIntent,
|
|
3577
3646
|
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",
|
|
3578
3647
|
children: [
|
|
3579
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3648
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3580
3649
|
TrackItem,
|
|
3581
3650
|
{
|
|
3582
3651
|
track,
|
|
@@ -3587,7 +3656,7 @@ function TrackListSidebar({
|
|
|
3587
3656
|
renderSongLink
|
|
3588
3657
|
}
|
|
3589
3658
|
) }, track.uuid)),
|
|
3590
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3659
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3591
3660
|
]
|
|
3592
3661
|
}
|
|
3593
3662
|
)
|
|
@@ -3597,17 +3666,17 @@ function TrackListSidebar({
|
|
|
3597
3666
|
}
|
|
3598
3667
|
|
|
3599
3668
|
// src/genre-tree/PlayerPanel.tsx
|
|
3600
|
-
var
|
|
3669
|
+
var import_react20 = require("react");
|
|
3601
3670
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3602
3671
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3603
|
-
var
|
|
3672
|
+
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3604
3673
|
var import_ui21 = require("@behindthemusictree/ui");
|
|
3605
3674
|
|
|
3606
3675
|
// src/genre-tree/PlayerControls.tsx
|
|
3607
3676
|
var import_lucide_react19 = require("lucide-react");
|
|
3608
|
-
var
|
|
3677
|
+
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3609
3678
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3610
|
-
var
|
|
3679
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
3611
3680
|
function PlayerControls() {
|
|
3612
3681
|
const {
|
|
3613
3682
|
duration,
|
|
@@ -3619,22 +3688,22 @@ function PlayerControls() {
|
|
|
3619
3688
|
setVolume,
|
|
3620
3689
|
volume
|
|
3621
3690
|
} = (0, import_player12.usePlayer)();
|
|
3622
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3691
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree31.useTrackList)();
|
|
3623
3692
|
const currentTime = (0, import_player12.useCurrentTime)();
|
|
3624
3693
|
const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
|
|
3625
3694
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3626
3695
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3627
3696
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3628
3697
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3629
|
-
return /* @__PURE__ */ (0,
|
|
3698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3630
3699
|
"section",
|
|
3631
3700
|
{
|
|
3632
3701
|
"aria-label": "Player controls",
|
|
3633
3702
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3634
3703
|
children: [
|
|
3635
|
-
/* @__PURE__ */ (0,
|
|
3636
|
-
/* @__PURE__ */ (0,
|
|
3637
|
-
/* @__PURE__ */ (0,
|
|
3704
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3705
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree31.formatTime)(safeCurrentTime) }),
|
|
3706
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3638
3707
|
"input",
|
|
3639
3708
|
{
|
|
3640
3709
|
"aria-label": "Seek",
|
|
@@ -3648,11 +3717,11 @@ function PlayerControls() {
|
|
|
3648
3717
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3649
3718
|
}
|
|
3650
3719
|
),
|
|
3651
|
-
/* @__PURE__ */ (0,
|
|
3720
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree31.formatTime)(safeDuration) })
|
|
3652
3721
|
] }),
|
|
3653
|
-
/* @__PURE__ */ (0,
|
|
3654
|
-
/* @__PURE__ */ (0,
|
|
3655
|
-
/* @__PURE__ */ (0,
|
|
3722
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3723
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3724
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3656
3725
|
"button",
|
|
3657
3726
|
{
|
|
3658
3727
|
type: "button",
|
|
@@ -3661,10 +3730,10 @@ function PlayerControls() {
|
|
|
3661
3730
|
disabled: !isReady || !hasPreviousTrack,
|
|
3662
3731
|
onClick: playPreviousTrack,
|
|
3663
3732
|
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",
|
|
3664
|
-
children: /* @__PURE__ */ (0,
|
|
3733
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react19.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3665
3734
|
}
|
|
3666
3735
|
),
|
|
3667
|
-
/* @__PURE__ */ (0,
|
|
3736
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3668
3737
|
"button",
|
|
3669
3738
|
{
|
|
3670
3739
|
type: "button",
|
|
@@ -3673,10 +3742,10 @@ function PlayerControls() {
|
|
|
3673
3742
|
disabled: !isReady,
|
|
3674
3743
|
onClick: handlePlayPauseAction,
|
|
3675
3744
|
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",
|
|
3676
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3745
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react19.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react19.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3677
3746
|
}
|
|
3678
3747
|
),
|
|
3679
|
-
/* @__PURE__ */ (0,
|
|
3748
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3680
3749
|
"button",
|
|
3681
3750
|
{
|
|
3682
3751
|
type: "button",
|
|
@@ -3685,14 +3754,14 @@ function PlayerControls() {
|
|
|
3685
3754
|
disabled: !isReady || !hasNextTrack,
|
|
3686
3755
|
onClick: playNextTrack,
|
|
3687
3756
|
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",
|
|
3688
|
-
children: /* @__PURE__ */ (0,
|
|
3757
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react19.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3689
3758
|
}
|
|
3690
3759
|
)
|
|
3691
3760
|
] }),
|
|
3692
|
-
/* @__PURE__ */ (0,
|
|
3693
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3694
|
-
/* @__PURE__ */ (0,
|
|
3695
|
-
/* @__PURE__ */ (0,
|
|
3761
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.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: [
|
|
3762
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react19.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react19.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3763
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3764
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3696
3765
|
"input",
|
|
3697
3766
|
{
|
|
3698
3767
|
"aria-label": "Volume",
|
|
@@ -3714,7 +3783,7 @@ function PlayerControls() {
|
|
|
3714
3783
|
}
|
|
3715
3784
|
|
|
3716
3785
|
// src/genre-tree/PlayerPanel.tsx
|
|
3717
|
-
var
|
|
3786
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
3718
3787
|
var PANEL_ID = "player-panel";
|
|
3719
3788
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3720
3789
|
function PlayerPanel({
|
|
@@ -3727,13 +3796,13 @@ function PlayerPanel({
|
|
|
3727
3796
|
renderSongLink
|
|
3728
3797
|
}) {
|
|
3729
3798
|
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3730
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3731
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0,
|
|
3799
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree32.useTrackListSidebarVisibility)();
|
|
3800
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react20.useState)("");
|
|
3732
3801
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3733
|
-
(0,
|
|
3802
|
+
(0, import_react20.useEffect)(() => {
|
|
3734
3803
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3735
3804
|
}, [loadedTrackTitle]);
|
|
3736
|
-
const [uncontrolledExtended, setUncontrolledExtended] = (0,
|
|
3805
|
+
const [uncontrolledExtended, setUncontrolledExtended] = (0, import_react20.useState)(false);
|
|
3737
3806
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3738
3807
|
const setIsExtendRequested = (next) => {
|
|
3739
3808
|
setUncontrolledExtended(next);
|
|
@@ -3754,7 +3823,7 @@ function PlayerPanel({
|
|
|
3754
3823
|
onClick: () => setIsExtendRequested(true),
|
|
3755
3824
|
expanded: false
|
|
3756
3825
|
} : null;
|
|
3757
|
-
return /* @__PURE__ */ (0,
|
|
3826
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
3758
3827
|
"div",
|
|
3759
3828
|
{
|
|
3760
3829
|
id: PANEL_ID,
|
|
@@ -3767,7 +3836,7 @@ function PlayerPanel({
|
|
|
3767
3836
|
),
|
|
3768
3837
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3769
3838
|
children: [
|
|
3770
|
-
/* @__PURE__ */ (0,
|
|
3839
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3771
3840
|
import_ui21.PanelEdgeTab,
|
|
3772
3841
|
{
|
|
3773
3842
|
side: "right",
|
|
@@ -3777,7 +3846,7 @@ function PlayerPanel({
|
|
|
3777
3846
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3778
3847
|
}
|
|
3779
3848
|
),
|
|
3780
|
-
/* @__PURE__ */ (0,
|
|
3849
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
3781
3850
|
"div",
|
|
3782
3851
|
{
|
|
3783
3852
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3785,20 +3854,20 @@ function PlayerPanel({
|
|
|
3785
3854
|
isExtended ? "flex-row" : "flex-col"
|
|
3786
3855
|
),
|
|
3787
3856
|
children: [
|
|
3788
|
-
/* @__PURE__ */ (0,
|
|
3789
|
-
/* @__PURE__ */ (0,
|
|
3790
|
-
/* @__PURE__ */ (0,
|
|
3791
|
-
/* @__PURE__ */ (0,
|
|
3792
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3857
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3858
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3859
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3860
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3861
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3793
3862
|
playerTrackObject.loadError,
|
|
3794
|
-
/* @__PURE__ */ (0,
|
|
3863
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3795
3864
|
void retryPlayback();
|
|
3796
3865
|
}, children: "Retry" })
|
|
3797
3866
|
] })
|
|
3798
3867
|
] }),
|
|
3799
|
-
/* @__PURE__ */ (0,
|
|
3868
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(PlayerControls, {})
|
|
3800
3869
|
] }),
|
|
3801
|
-
/* @__PURE__ */ (0,
|
|
3870
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3802
3871
|
TrackListSidebar,
|
|
3803
3872
|
{
|
|
3804
3873
|
layout: "inline",
|
|
@@ -3824,33 +3893,33 @@ function PlayerPanel({
|
|
|
3824
3893
|
// src/genre-tree/Rating.tsx
|
|
3825
3894
|
var import_uuid = require("uuid");
|
|
3826
3895
|
var import_ai = require("react-icons/ai");
|
|
3827
|
-
var
|
|
3828
|
-
var
|
|
3896
|
+
var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3897
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
3829
3898
|
function Rating({ rating, handleChange }) {
|
|
3830
3899
|
const instanceId = (0, import_uuid.v4)();
|
|
3831
|
-
return /* @__PURE__ */ (0,
|
|
3832
|
-
/* @__PURE__ */ (0,
|
|
3900
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3901
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3833
3902
|
"input",
|
|
3834
3903
|
{
|
|
3835
3904
|
id: `star-${instanceId}-${i}`,
|
|
3836
3905
|
className: "w-6 h-6 opacity-0",
|
|
3837
3906
|
type: "radio",
|
|
3838
3907
|
name: "rating",
|
|
3839
|
-
value: i === 0 ?
|
|
3908
|
+
value: i === 0 ? import_genre_tree33.FORM_RATING_NULL_VALUE : i,
|
|
3840
3909
|
checked: rating === (i === 0 ? null : i),
|
|
3841
3910
|
onChange: handleChange
|
|
3842
3911
|
}
|
|
3843
3912
|
),
|
|
3844
|
-
/* @__PURE__ */ (0,
|
|
3845
|
-
/* @__PURE__ */ (0,
|
|
3913
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex", children: [
|
|
3914
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3846
3915
|
import_ai.AiOutlineStar,
|
|
3847
3916
|
{
|
|
3848
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3917
|
+
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree33.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3849
3918
|
}
|
|
3850
3919
|
),
|
|
3851
|
-
/* @__PURE__ */ (0,
|
|
3920
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "flex-grow w-8" }),
|
|
3852
3921
|
" "
|
|
3853
|
-
] }) : /* @__PURE__ */ (0,
|
|
3922
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3854
3923
|
import_ai.AiFillStar,
|
|
3855
3924
|
{
|
|
3856
3925
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|