@behindthemusictree/app-kit-ui 42.0.2 → 43.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/README.md +7 -0
- package/dist/{chunk-PVYUC2PN.js → chunk-24PBIPA4.js} +275 -191
- package/dist/chunk-24PBIPA4.js.map +1 -0
- package/dist/genre-tree/index.cjs +462 -379
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +19 -7
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +457 -374
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +6 -6
- package/dist/chunk-PVYUC2PN.js.map +0 -1
|
@@ -48,14 +48,14 @@ __export(genre_tree_exports, {
|
|
|
48
48
|
module.exports = __toCommonJS(genre_tree_exports);
|
|
49
49
|
|
|
50
50
|
// src/genre-tree/GenreTreeView.tsx
|
|
51
|
-
var
|
|
51
|
+
var import_react20 = require("react");
|
|
52
52
|
var import_react_query3 = require("@tanstack/react-query");
|
|
53
53
|
var import_lucide_react13 = require("lucide-react");
|
|
54
54
|
var import_lucide_react14 = require("lucide-react");
|
|
55
|
-
var
|
|
55
|
+
var import_ui23 = require("@behindthemusictree/ui");
|
|
56
56
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
57
|
-
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
58
57
|
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
58
|
+
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
59
59
|
var import_popup4 = require("@behindthemusictree/app-kit/popup");
|
|
60
60
|
|
|
61
61
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -724,6 +724,8 @@ function SearchBox({
|
|
|
724
724
|
onClear,
|
|
725
725
|
clearLabel,
|
|
726
726
|
leading,
|
|
727
|
+
trailingAction,
|
|
728
|
+
trailingActionBreakpoint,
|
|
727
729
|
className: className2,
|
|
728
730
|
onFocus
|
|
729
731
|
}) {
|
|
@@ -756,6 +758,8 @@ function SearchBox({
|
|
|
756
758
|
onClear,
|
|
757
759
|
clearLabel,
|
|
758
760
|
leading,
|
|
761
|
+
trailingAction,
|
|
762
|
+
trailingActionBreakpoint,
|
|
759
763
|
inputClassName: "focus-visible:ring-0"
|
|
760
764
|
}
|
|
761
765
|
) });
|
|
@@ -856,7 +860,10 @@ function GenreSearch({
|
|
|
856
860
|
placeholder,
|
|
857
861
|
onQueryChange,
|
|
858
862
|
extraGroups = [],
|
|
859
|
-
onFocus
|
|
863
|
+
onFocus,
|
|
864
|
+
trailingAction,
|
|
865
|
+
trailingActionBreakpoint,
|
|
866
|
+
fullWidthBreakpoint = "md"
|
|
860
867
|
}) {
|
|
861
868
|
const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree9.useGenreSearch)({
|
|
862
869
|
genrePlaylists,
|
|
@@ -884,7 +891,7 @@ function GenreSearch({
|
|
|
884
891
|
(group) => active ? group : { ...group, items: [], status: "", loading: false }
|
|
885
892
|
)
|
|
886
893
|
];
|
|
887
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className:
|
|
894
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: `genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 ${fullWidthBreakpoint === "sm" ? "max-sm:w-full sm:max-md:max-w-sm" : "max-md:w-full"}`, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
888
895
|
SearchBox,
|
|
889
896
|
{
|
|
890
897
|
label,
|
|
@@ -905,7 +912,9 @@ function GenreSearch({
|
|
|
905
912
|
onQueryChange?.("");
|
|
906
913
|
} : void 0,
|
|
907
914
|
leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui4.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react3.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
|
|
908
|
-
onFocus
|
|
915
|
+
onFocus,
|
|
916
|
+
trailingAction,
|
|
917
|
+
trailingActionBreakpoint
|
|
909
918
|
}
|
|
910
919
|
) });
|
|
911
920
|
}
|
|
@@ -949,45 +958,93 @@ function GenreTreeSearch({
|
|
|
949
958
|
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
|
|
950
959
|
}
|
|
951
960
|
|
|
961
|
+
// src/genre-tree/GenreOverviewDetailPanel.tsx
|
|
962
|
+
var import_react9 = require("react");
|
|
963
|
+
var import_ui5 = require("@behindthemusictree/ui");
|
|
964
|
+
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
965
|
+
|
|
966
|
+
// src/genre-tree/CommonsPhoto.tsx
|
|
967
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
968
|
+
function ExternalLink({ href, children }) {
|
|
969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
970
|
+
}
|
|
971
|
+
function CommonsPhotoCredit({ image, creditLabel = "Photo" }) {
|
|
972
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("p", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
973
|
+
creditLabel,
|
|
974
|
+
": ",
|
|
975
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
976
|
+
",",
|
|
977
|
+
" ",
|
|
978
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
979
|
+
] });
|
|
980
|
+
}
|
|
981
|
+
function CommonsCover({ image, alt }) {
|
|
982
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-cover bg-[var(--color-surface-muted)]" });
|
|
983
|
+
}
|
|
984
|
+
function CommonsPhoto({
|
|
985
|
+
image,
|
|
986
|
+
alt,
|
|
987
|
+
creditLabel = "Photo"
|
|
988
|
+
}) {
|
|
989
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
|
|
990
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
991
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
992
|
+
creditLabel,
|
|
993
|
+
": ",
|
|
994
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
995
|
+
",",
|
|
996
|
+
" ",
|
|
997
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
998
|
+
] })
|
|
999
|
+
] });
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/genre-tree/GenreOverviewDetailPanel.tsx
|
|
1003
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1004
|
+
var GenreOverviewDetailPanel = (0, import_react9.forwardRef)(function GenreOverviewDetailPanel2({ genreUuid, scope, getBackendBaseUrl, criteriaOverviewSchema, cover, ...props }, ref) {
|
|
1005
|
+
const { data: overview } = (0, import_genre_tree10.useFetchGenreOverview)(genreUuid, scope, getBackendBaseUrl, criteriaOverviewSchema);
|
|
1006
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_ui5.DetailPanel, { ...props, ref, cover: genreUuid && overview?.image ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(CommonsCover, { image: overview.image, alt: overview.name }) : cover });
|
|
1007
|
+
});
|
|
1008
|
+
|
|
952
1009
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
953
|
-
var
|
|
1010
|
+
var import_react16 = require("react");
|
|
954
1011
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
955
|
-
var
|
|
1012
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
956
1013
|
var import_lucide_react9 = require("lucide-react");
|
|
957
|
-
var
|
|
1014
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
958
1015
|
|
|
959
1016
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
960
|
-
var
|
|
1017
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
961
1018
|
|
|
962
1019
|
// src/genre-tree/GenreActionBand.tsx
|
|
963
1020
|
var import_lucide_react7 = require("lucide-react");
|
|
964
|
-
var
|
|
1021
|
+
var import_ui7 = require("@behindthemusictree/ui");
|
|
965
1022
|
|
|
966
1023
|
// src/genre-tree/ShareActionButton.tsx
|
|
967
1024
|
var import_lucide_react6 = require("lucide-react");
|
|
968
|
-
var
|
|
1025
|
+
var import_ui6 = require("@behindthemusictree/ui");
|
|
969
1026
|
var import_popup2 = require("@behindthemusictree/app-kit/popup");
|
|
970
1027
|
|
|
971
1028
|
// src/genre-tree/SharePopup.tsx
|
|
972
|
-
var
|
|
1029
|
+
var import_react11 = require("react");
|
|
973
1030
|
var import_lucide_react5 = require("lucide-react");
|
|
974
1031
|
var import_fa = require("react-icons/fa");
|
|
975
1032
|
var import_si = require("react-icons/si");
|
|
976
1033
|
|
|
977
1034
|
// src/popup/Toast.tsx
|
|
978
|
-
var
|
|
1035
|
+
var import_react10 = require("react");
|
|
979
1036
|
var import_react_dom2 = require("react-dom");
|
|
980
|
-
var
|
|
1037
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
981
1038
|
function Toast({ message, onClose, durationMs = 2200 }) {
|
|
982
|
-
const [mounted, setMounted] = (0,
|
|
983
|
-
(0,
|
|
1039
|
+
const [mounted, setMounted] = (0, import_react10.useState)(false);
|
|
1040
|
+
(0, import_react10.useEffect)(() => {
|
|
984
1041
|
setMounted(true);
|
|
985
1042
|
const timeout = window.setTimeout(onClose, durationMs);
|
|
986
1043
|
return () => window.clearTimeout(timeout);
|
|
987
1044
|
}, [durationMs, onClose]);
|
|
988
1045
|
if (!mounted) return null;
|
|
989
1046
|
return (0, import_react_dom2.createPortal)(
|
|
990
|
-
/* @__PURE__ */ (0,
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "pointer-events-none fixed inset-x-0 bottom-8 z-[60] flex justify-center px-4", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
991
1048
|
"div",
|
|
992
1049
|
{
|
|
993
1050
|
role: "status",
|
|
@@ -1002,12 +1059,12 @@ function Toast({ message, onClose, durationMs = 2200 }) {
|
|
|
1002
1059
|
}
|
|
1003
1060
|
|
|
1004
1061
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
1005
|
-
var
|
|
1062
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1006
1063
|
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-caption 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";
|
|
1007
1064
|
function Contents({ icon, label }) {
|
|
1008
|
-
return /* @__PURE__ */ (0,
|
|
1009
|
-
/* @__PURE__ */ (0,
|
|
1010
|
-
/* @__PURE__ */ (0,
|
|
1065
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.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 }),
|
|
1067
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "max-w-full truncate", children: label })
|
|
1011
1068
|
] });
|
|
1012
1069
|
}
|
|
1013
1070
|
function ShareDestinationButton({
|
|
@@ -1019,29 +1076,29 @@ function ShareDestinationButton({
|
|
|
1019
1076
|
ariaLabel
|
|
1020
1077
|
}) {
|
|
1021
1078
|
if (href) {
|
|
1022
|
-
return /* @__PURE__ */ (0,
|
|
1079
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Contents, { icon, label }) });
|
|
1023
1080
|
}
|
|
1024
|
-
return /* @__PURE__ */ (0,
|
|
1081
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Contents, { icon, label }) });
|
|
1025
1082
|
}
|
|
1026
1083
|
|
|
1027
1084
|
// src/genre-tree/SharePreviewCard.tsx
|
|
1028
1085
|
var import_lucide_react4 = require("lucide-react");
|
|
1029
|
-
var
|
|
1086
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1030
1087
|
function SharePreviewCard({ target, url }) {
|
|
1031
1088
|
const Icon = { genre: import_lucide_react4.Music2, artist: import_lucide_react4.UserRound, song: import_lucide_react4.Music }[target.kind];
|
|
1032
1089
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
1033
|
-
return /* @__PURE__ */ (0,
|
|
1034
|
-
/* @__PURE__ */ (0,
|
|
1035
|
-
/* @__PURE__ */ (0,
|
|
1036
|
-
/* @__PURE__ */ (0,
|
|
1037
|
-
/* @__PURE__ */ (0,
|
|
1038
|
-
/* @__PURE__ */ (0,
|
|
1090
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
1091
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.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_runtime20.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
1092
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
1093
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
1094
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
1095
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
|
|
1039
1096
|
] })
|
|
1040
1097
|
] }) });
|
|
1041
1098
|
}
|
|
1042
1099
|
|
|
1043
1100
|
// src/genre-tree/SharePopup.tsx
|
|
1044
|
-
var
|
|
1101
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1045
1102
|
function resolveAbsoluteUrl(url) {
|
|
1046
1103
|
try {
|
|
1047
1104
|
const parsed = new URL(url, window.location.href);
|
|
@@ -1074,20 +1131,20 @@ async function copyText(text) {
|
|
|
1074
1131
|
}
|
|
1075
1132
|
}
|
|
1076
1133
|
function SharePopup({ target, url, onClose }) {
|
|
1077
|
-
const [absoluteUrl, setAbsoluteUrl] = (0,
|
|
1078
|
-
const [copyStatus, setCopyStatus] = (0,
|
|
1079
|
-
const [canUseNativeShare, setCanUseNativeShare] = (0,
|
|
1080
|
-
const [shareError, setShareError] = (0,
|
|
1081
|
-
const [toastId, setToastId] = (0,
|
|
1082
|
-
(0,
|
|
1134
|
+
const [absoluteUrl, setAbsoluteUrl] = (0, import_react11.useState)(url);
|
|
1135
|
+
const [copyStatus, setCopyStatus] = (0, import_react11.useState)("idle");
|
|
1136
|
+
const [canUseNativeShare, setCanUseNativeShare] = (0, import_react11.useState)(false);
|
|
1137
|
+
const [shareError, setShareError] = (0, import_react11.useState)(null);
|
|
1138
|
+
const [toastId, setToastId] = (0, import_react11.useState)(0);
|
|
1139
|
+
(0, import_react11.useEffect)(() => {
|
|
1083
1140
|
setAbsoluteUrl(resolveAbsoluteUrl(url) ?? "");
|
|
1084
1141
|
setCopyStatus("idle");
|
|
1085
1142
|
setShareError(null);
|
|
1086
1143
|
}, [url]);
|
|
1087
|
-
(0,
|
|
1144
|
+
(0, import_react11.useEffect)(() => {
|
|
1088
1145
|
setCanUseNativeShare(typeof navigator !== "undefined" && typeof navigator.share === "function");
|
|
1089
1146
|
}, []);
|
|
1090
|
-
const destinationUrls = (0,
|
|
1147
|
+
const destinationUrls = (0, import_react11.useMemo)(() => {
|
|
1091
1148
|
if (!absoluteUrl) return null;
|
|
1092
1149
|
const message = `${target.name} \u2014 ${absoluteUrl}`;
|
|
1093
1150
|
return {
|
|
@@ -1105,7 +1162,7 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1105
1162
|
setCopyStatus(copied ? "idle" : "failed");
|
|
1106
1163
|
if (copied) setToastId((id) => id + 1);
|
|
1107
1164
|
};
|
|
1108
|
-
const dismissToast = (0,
|
|
1165
|
+
const dismissToast = (0, import_react11.useCallback)(() => setToastId(0), []);
|
|
1109
1166
|
const handleNativeShare = async () => {
|
|
1110
1167
|
if (!absoluteUrl || !navigator.share) return;
|
|
1111
1168
|
setShareError(null);
|
|
@@ -1116,8 +1173,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1116
1173
|
setShareError("Could not open the share options.");
|
|
1117
1174
|
}
|
|
1118
1175
|
};
|
|
1119
|
-
return /* @__PURE__ */ (0,
|
|
1120
|
-
/* @__PURE__ */ (0,
|
|
1176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
1177
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1121
1178
|
BasePopup_default,
|
|
1122
1179
|
{
|
|
1123
1180
|
title: "Share",
|
|
@@ -1132,70 +1189,70 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1132
1189
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
1133
1190
|
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)]",
|
|
1134
1191
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
1135
|
-
children: /* @__PURE__ */ (0,
|
|
1136
|
-
/* @__PURE__ */ (0,
|
|
1137
|
-
/* @__PURE__ */ (0,
|
|
1138
|
-
/* @__PURE__ */ (0,
|
|
1192
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
1194
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
1195
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1139
1196
|
ShareDestinationButton,
|
|
1140
1197
|
{
|
|
1141
1198
|
label: "Copy Link",
|
|
1142
|
-
icon: /* @__PURE__ */ (0,
|
|
1199
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react5.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1143
1200
|
onClick: () => void handleCopy(),
|
|
1144
1201
|
ariaLabel: "Copy link",
|
|
1145
1202
|
disabled: !absoluteUrl
|
|
1146
1203
|
}
|
|
1147
1204
|
),
|
|
1148
|
-
/* @__PURE__ */ (0,
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1149
1206
|
ShareDestinationButton,
|
|
1150
1207
|
{
|
|
1151
1208
|
label: "WhatsApp",
|
|
1152
|
-
icon: /* @__PURE__ */ (0,
|
|
1209
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_fa.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
1153
1210
|
href: destinationUrls?.whatsapp,
|
|
1154
1211
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
1155
1212
|
disabled: !absoluteUrl
|
|
1156
1213
|
}
|
|
1157
1214
|
),
|
|
1158
|
-
/* @__PURE__ */ (0,
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1159
1216
|
ShareDestinationButton,
|
|
1160
1217
|
{
|
|
1161
1218
|
label: "Gmail",
|
|
1162
|
-
icon: /* @__PURE__ */ (0,
|
|
1219
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
1163
1220
|
href: destinationUrls?.gmail,
|
|
1164
1221
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
1165
1222
|
disabled: !absoluteUrl
|
|
1166
1223
|
}
|
|
1167
1224
|
),
|
|
1168
|
-
canUseNativeShare && /* @__PURE__ */ (0,
|
|
1225
|
+
canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1169
1226
|
ShareDestinationButton,
|
|
1170
1227
|
{
|
|
1171
1228
|
label: "More",
|
|
1172
|
-
icon: /* @__PURE__ */ (0,
|
|
1229
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1173
1230
|
onClick: () => void handleNativeShare(),
|
|
1174
1231
|
ariaLabel: "More sharing options",
|
|
1175
1232
|
disabled: !absoluteUrl
|
|
1176
1233
|
}
|
|
1177
1234
|
)
|
|
1178
1235
|
] }),
|
|
1179
|
-
copyStatus === "failed" && /* @__PURE__ */ (0,
|
|
1180
|
-
shareError && /* @__PURE__ */ (0,
|
|
1236
|
+
copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
1237
|
+
shareError && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
|
|
1181
1238
|
] })
|
|
1182
1239
|
}
|
|
1183
1240
|
),
|
|
1184
|
-
toastId > 0 && /* @__PURE__ */ (0,
|
|
1241
|
+
toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
1185
1242
|
] });
|
|
1186
1243
|
}
|
|
1187
1244
|
|
|
1188
1245
|
// src/genre-tree/ShareActionButton.tsx
|
|
1189
|
-
var
|
|
1246
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1190
1247
|
function ShareActionButton({ target, url }) {
|
|
1191
1248
|
const { showPopup, hidePopup } = (0, import_popup2.usePopup)();
|
|
1192
1249
|
const openSharePopup = () => {
|
|
1193
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1250
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SharePopup, { target, url, onClose: hidePopup }));
|
|
1194
1251
|
};
|
|
1195
|
-
return /* @__PURE__ */ (0,
|
|
1196
|
-
|
|
1252
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1253
|
+
import_ui6.ActionButton,
|
|
1197
1254
|
{
|
|
1198
|
-
icon: /* @__PURE__ */ (0,
|
|
1255
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react6.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1199
1256
|
label: "Share",
|
|
1200
1257
|
"aria-label": `Share ${target.name}`,
|
|
1201
1258
|
title: `Share ${target.name}`,
|
|
@@ -1205,7 +1262,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1205
1262
|
}
|
|
1206
1263
|
|
|
1207
1264
|
// src/genre-tree/GenreActionBand.tsx
|
|
1208
|
-
var
|
|
1265
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1209
1266
|
function GenreActionBand({
|
|
1210
1267
|
node,
|
|
1211
1268
|
playActions,
|
|
@@ -1226,24 +1283,24 @@ function GenreActionBand({
|
|
|
1226
1283
|
items.push({
|
|
1227
1284
|
key: "zoom-in",
|
|
1228
1285
|
label: "Focus",
|
|
1229
|
-
icon: /* @__PURE__ */ (0,
|
|
1286
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react7.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1230
1287
|
onClick: onZoomIn
|
|
1231
1288
|
});
|
|
1232
1289
|
}
|
|
1233
1290
|
if (onAddChild) {
|
|
1234
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0,
|
|
1291
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react7.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
1235
1292
|
}
|
|
1236
1293
|
if (onRename) {
|
|
1237
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0,
|
|
1294
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react7.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
1238
1295
|
}
|
|
1239
1296
|
if (onChangeParent) {
|
|
1240
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0,
|
|
1297
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react7.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
1241
1298
|
}
|
|
1242
1299
|
if (onToggleSide) {
|
|
1243
1300
|
adminItems.push({
|
|
1244
1301
|
key: "toggle-side",
|
|
1245
1302
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
1246
|
-
icon: /* @__PURE__ */ (0,
|
|
1303
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react7.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1247
1304
|
onClick: onToggleSide,
|
|
1248
1305
|
disabled: !!toggleSideDisabledReason,
|
|
1249
1306
|
title: toggleSideDisabledReason
|
|
@@ -1258,17 +1315,17 @@ function GenreActionBand({
|
|
|
1258
1315
|
disabled: action.enabled ? !action.enabled(node) : false
|
|
1259
1316
|
});
|
|
1260
1317
|
}
|
|
1261
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0,
|
|
1318
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui7.ActionButton, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
|
|
1262
1319
|
if (!playActions && items.length === 0 && adminItems.length === 0 && !(shareTarget && shareUrl)) return null;
|
|
1263
|
-
return /* @__PURE__ */ (0,
|
|
1264
|
-
|
|
1320
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui7.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1321
|
+
import_ui7.ActionBand,
|
|
1265
1322
|
{
|
|
1266
1323
|
className: "genre-action-band",
|
|
1267
1324
|
section: { title: "Admin", label: "Admin actions", children: renderItems(adminItems) },
|
|
1268
1325
|
children: [
|
|
1269
1326
|
playActions,
|
|
1270
1327
|
renderItems(items),
|
|
1271
|
-
shareTarget && shareUrl && /* @__PURE__ */ (0,
|
|
1328
|
+
shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1272
1329
|
]
|
|
1273
1330
|
}
|
|
1274
1331
|
) });
|
|
@@ -1276,12 +1333,12 @@ function GenreActionBand({
|
|
|
1276
1333
|
|
|
1277
1334
|
// src/genre-tree/GenrePlayActions.tsx
|
|
1278
1335
|
var import_lucide_react8 = require("lucide-react");
|
|
1279
|
-
var
|
|
1280
|
-
var
|
|
1336
|
+
var import_ui8 = require("@behindthemusictree/ui");
|
|
1337
|
+
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1281
1338
|
var import_player4 = require("@behindthemusictree/app-kit/player");
|
|
1282
1339
|
|
|
1283
1340
|
// src/genre-tree/RegionGlobeIcon.tsx
|
|
1284
|
-
var
|
|
1341
|
+
var import_react12 = require("react");
|
|
1285
1342
|
|
|
1286
1343
|
// src/genre-tree/regionGlobePaths.ts
|
|
1287
1344
|
var REGION_GLOBE_PATHS = {
|
|
@@ -1292,11 +1349,11 @@ var REGION_GLOBE_PATHS = {
|
|
|
1292
1349
|
var REGION_GLOBES = ["africa", "americas", "asia"];
|
|
1293
1350
|
|
|
1294
1351
|
// src/genre-tree/RegionGlobeIcon.tsx
|
|
1295
|
-
var
|
|
1352
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1296
1353
|
var ROTATION_INTERVAL_MS = 3e3;
|
|
1297
1354
|
function RegionGlobeIcon({ className: className2 }) {
|
|
1298
|
-
const [index, setIndex] = (0,
|
|
1299
|
-
(0,
|
|
1355
|
+
const [index, setIndex] = (0, import_react12.useState)(0);
|
|
1356
|
+
(0, import_react12.useEffect)(() => {
|
|
1300
1357
|
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1301
1358
|
let timer;
|
|
1302
1359
|
const sync = () => {
|
|
@@ -1310,51 +1367,51 @@ function RegionGlobeIcon({ className: className2 }) {
|
|
|
1310
1367
|
clearInterval(timer);
|
|
1311
1368
|
};
|
|
1312
1369
|
}, []);
|
|
1313
|
-
return /* @__PURE__ */ (0,
|
|
1370
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
|
|
1314
1371
|
}
|
|
1315
1372
|
|
|
1316
1373
|
// src/genre-tree/treeMarkPath.ts
|
|
1317
1374
|
var TREE_MARK_PATH = "M3171 5598 c-5 -13 -5 -24 0 -26 5 -2 9 -16 9 -31 0 -25 -3 -28 -25 -23 l-25 4 0 -116 c0 -64 3 -116 8 -116 10 0 32 51 26 61 -3 5 -1 9 4 9 5 0 15 12 21 28 7 15 26 36 43 47 30 20 88 105 71 105 -4 0 -33 -21 -62 -47 l-54 -47 6 63 c4 35 7 72 7 82 0 10 9 19 23 22 16 3 13 5 -11 6 -25 1 -35 -4 -41 -21z M2893 5485 c8 -76 24 -100 71 -100 l36 0 0 53 c0 58 -15 69 -28 20 -8 -29 -10 -30 -16 -13 -15 43 -42 89 -55 93 -11 4 -13 -6 -8 -53z M2484 5438 c8 -70 49 -169 54 -131 2 12 10 27 18 33 17 15 19 84 3 129 -9 27 -16 31 -46 31 l-36 0 7 -62z M2625 5448 c-7 -22 -24 -121 -31 -179 l-6 -57 -27 11 c-15 5 -32 19 -37 31 -9 21 -88 86 -105 86 -39 0 38 -104 118 -158 17 -12 56 -59 86 -105 30 -45 67 -92 81 -104 14 -13 26 -30 26 -38 0 -13 -9 -11 -45 11 -67 39 -58 10 21 -66 67 -65 96 -110 108 -167 4 -17 13 -34 20 -36 20 -8 66 -186 75 -289 13 -161 -8 -272 -72 -365 -12 -18 -30 -33 -39 -33 -23 0 -53 33 -79 89 -25 54 -69 112 -121 162 -35 33 -38 40 -38 92 0 58 36 172 79 252 25 47 29 125 6 125 -19 0 -30 -22 -50 -100 -9 -35 -24 -78 -32 -94 l-16 -30 -19 24 -20 25 7 -58 c8 -62 0 -127 -16 -127 -19 0 -122 107 -166 172 -52 79 -68 150 -60 273 8 108 17 129 67 149 58 23 97 64 105 109 9 47 -5 49 -63 6 l-42 -30 15 35 c17 41 20 102 5 111 -18 11 -45 -4 -64 -36 -11 -19 -20 -60 -23 -110 -4 -54 -10 -79 -18 -79 -20 0 -27 28 -30 108 -2 40 -6 76 -9 79 -3 4 -6 51 -6 105 0 86 -3 101 -21 119 -27 27 -42 20 -53 -24 -7 -28 -5 -51 7 -90 9 -30 17 -76 17 -103 l0 -49 -29 51 c-29 49 -92 114 -111 114 -6 0 -10 -11 -10 -25 0 -14 5 -25 10 -25 6 0 10 -6 10 -13 0 -7 23 -44 51 -82 61 -84 59 -82 52 -57 -3 14 2 24 15 31 12 6 23 23 25 38 2 16 5 2 6 -29 1 -46 -2 -58 -14 -58 -21 0 -19 -13 10 -70 22 -44 25 -61 25 -165 l0 -116 -47 27 c-65 36 -142 118 -176 187 -15 31 -37 62 -49 68 -21 11 -21 10 -15 -41 l6 -52 -29 6 c-37 7 -39 -7 -4 -31 14 -10 53 -40 86 -68 92 -76 130 -104 163 -119 39 -19 101 -84 109 -116 10 -42 50 -88 217 -245 50 -47 94 -102 134 -167 33 -54 65 -98 71 -98 17 0 45 -79 51 -145 4 -47 0 -76 -20 -135 -14 -41 -39 -121 -56 -177 -18 -57 -35 -103 -40 -103 -4 0 -26 13 -49 30 -31 22 -50 49 -82 117 -99 209 -116 241 -185 337 -123 172 -123 173 -128 311 -5 140 -29 245 -57 245 -12 0 -20 -11 -24 -31 -9 -46 -7 -108 3 -127 14 -26 25 -162 14 -162 -20 0 -160 99 -192 135 -19 22 -42 58 -52 80 -10 23 -27 52 -37 66 -10 13 -26 38 -34 54 -9 17 -23 45 -33 64 -15 29 -16 40 -6 80 31 116 34 139 27 205 -7 57 -11 69 -28 74 -14 3 -32 30 -54 79 -21 46 -39 73 -47 70 -13 -5 -14 -15 -3 -65 l8 -33 -49 25 c-61 31 -93 40 -93 27 0 -13 43 -41 122 -79 47 -22 72 -41 83 -61 19 -38 19 -51 0 -51 -12 0 -15 -15 -16 -72 -1 -79 -12 -134 -22 -108 -3 8 -10 48 -16 89 -14 89 -25 111 -54 111 -38 0 -50 -15 -45 -54 4 -40 2 -42 -26 -21 -29 22 -36 19 -36 -20 0 -39 39 -85 125 -145 38 -27 62 -56 99 -120 45 -79 82 -177 70 -189 -2 -3 -42 3 -87 13 -45 10 -99 21 -119 26 -47 9 -103 51 -125 93 -24 44 -83 203 -94 252 -5 25 -4 59 3 91 14 68 3 140 -23 155 -12 6 -23 8 -27 5 -14 -15 -13 -115 2 -159 39 -116 0 -116 -70 1 -22 36 -31 42 -59 42 -20 0 -35 6 -39 15 -6 16 -56 21 -56 6 0 -5 23 -25 51 -44 53 -36 158 -156 188 -216 10 -20 26 -60 36 -88 11 -29 25 -53 32 -53 8 0 13 -12 13 -30 0 -26 -4 -30 -27 -31 l-28 -1 25 -7 c26 -8 177 -87 196 -102 5 -4 50 -18 100 -29 122 -29 277 -78 300 -95 10 -8 26 -15 34 -15 35 0 160 -166 160 -212 0 -22 -32 -24 -50 -4 -11 14 -100 46 -165 61 -16 3 -68 25 -115 48 -102 49 -163 56 -168 19 -9 -62 58 -104 178 -112 63 -5 103 -14 155 -37 39 -16 91 -38 117 -47 26 -9 74 -35 106 -56 52 -33 65 -48 96 -112 20 -40 39 -95 43 -122 6 -47 6 -48 -18 -41 -33 10 -69 33 -69 45 0 6 -10 10 -23 10 -13 0 -27 4 -32 9 -6 5 -27 14 -47 20 -20 7 -53 23 -73 37 -43 29 -45 29 -45 7 0 -26 80 -96 145 -127 119 -56 274 -185 311 -259 55 -107 104 -410 104 -640 0 -162 -22 -325 -45 -333 -25 -10 -72 25 -168 123 -129 131 -140 148 -182 274 -20 60 -50 142 -66 181 -16 39 -45 131 -65 202 -24 93 -55 173 -104 274 -77 158 -78 166 -32 256 31 64 27 129 -12 181 -31 40 -39 42 -65 18 -51 -49 -55 -120 -11 -208 37 -74 37 -85 3 -85 -36 0 -94 65 -137 154 -18 39 -37 80 -40 92 -3 11 -14 29 -24 40 -10 10 -28 35 -42 54 -13 19 -40 55 -61 80 -20 25 -41 63 -47 85 -6 22 -16 62 -23 89 -20 81 -95 147 -113 100 -18 -47 -6 -92 39 -147 25 -31 45 -61 45 -67 0 -15 -42 -3 -116 31 -94 44 -156 124 -184 235 -9 36 -13 41 -38 40 -39 -2 -144 113 -186 205 -31 66 -33 68 -53 54 -11 -8 -24 -11 -27 -7 -4 4 11 -24 33 -62 39 -67 43 -86 18 -86 -8 0 -8 3 1 9 11 6 9 9 -7 14 -11 3 -25 -1 -31 -7 -7 -10 0 -16 29 -24 39 -12 203 -147 215 -178 13 -34 4 -35 -48 -6 -64 37 -126 51 -126 30 0 -26 55 -67 125 -92 46 -16 80 -36 100 -58 l29 -33 -54 -3 c-79 -5 -203 14 -249 38 -47 24 -70 54 -105 140 -43 105 -115 163 -103 83 3 -21 9 -47 13 -57 4 -11 -15 4 -42 32 -62 64 -84 67 -84 14 0 -41 24 -76 61 -87 16 -6 18 -9 8 -15 -23 -15 15 -45 117 -91 52 -24 93 -48 91 -54 -10 -28 -150 -28 -190 0 -21 15 -47 5 -47 -17 0 -5 -10 -19 -22 -29 -23 -20 -23 -20 112 -14 174 8 208 8 430 0 l185 -7 84 -41 c115 -57 180 -118 225 -212 20 -41 36 -77 36 -80 0 -14 89 -126 150 -190 74 -78 120 -144 120 -175 0 -30 10 -33 -201 52 -94 38 -131 71 -218 195 -27 37 -59 77 -71 88 l-23 20 7 -40 c4 -22 12 -44 17 -49 13 -14 11 -26 -4 -26 -7 0 -29 21 -48 48 -33 45 -34 51 -39 159 -4 89 -9 116 -22 130 -17 16 -18 16 -18 0 0 -9 -7 -17 -16 -17 -14 0 -16 -9 -11 -62 3 -35 8 -74 12 -88 l7 -25 -25 27 c-41 44 -127 118 -137 118 -18 0 -10 -57 12 -85 18 -24 20 -32 10 -57 -10 -28 -9 -30 22 -43 18 -8 54 -19 79 -26 36 -9 60 -24 99 -66 80 -83 80 -83 1 -83 -80 0 -176 21 -236 51 -107 54 -234 169 -252 228 -4 14 -12 29 -17 35 -5 6 -18 25 -28 43 -24 40 -90 64 -116 42 -22 -18 -13 -84 17 -116 13 -13 42 -32 64 -42 22 -10 74 -53 115 -96 l75 -77 -75 5 c-135 8 -243 76 -336 207 -31 44 -65 87 -77 95 -37 26 -34 -9 7 -88 20 -39 34 -72 31 -75 -2 -2 -26 11 -52 31 -90 65 -143 83 -143 49 0 -32 71 -88 150 -117 41 -15 113 -47 159 -71 46 -24 99 -46 118 -50 18 -3 33 -12 33 -18 0 -12 -111 -46 -149 -46 -12 0 -29 -8 -39 -17 -10 -10 -27 -19 -37 -19 -11 -1 -45 -9 -75 -17 -58 -16 -110 -62 -110 -97 0 -39 116 -45 167 -9 11 8 34 31 52 51 28 32 50 43 144 74 145 47 222 48 404 5 70 -17 177 -38 237 -47 128 -19 202 -43 250 -79 18 -14 73 -42 122 -61 164 -66 208 -112 243 -251 36 -141 27 -167 -44 -123 -19 12 -63 23 -111 28 -117 13 -134 25 -275 193 -63 74 -193 151 -266 157 -29 2 -66 -1 -81 -7 -24 -10 -37 -9 -80 8 -66 26 -70 9 -11 -44 72 -65 119 -80 239 -78 60 1 109 -2 117 -8 6 -6 26 -35 42 -64 17 -30 46 -64 64 -77 19 -13 49 -40 67 -61 l34 -39 -79 3 c-57 3 -80 0 -83 -9 -3 -10 -7 -10 -18 -1 -8 6 -11 15 -8 20 3 5 0 9 -5 9 -16 0 -13 -16 6 -36 36 -36 78 -44 227 -44 119 0 152 -3 188 -19 56 -24 95 -64 125 -126 13 -27 35 -68 48 -90 14 -22 25 -53 25 -69 l0 -29 -107 5 c-131 6 -193 28 -290 100 -37 28 -82 56 -98 63 -75 32 -176 123 -277 247 -34 42 -122 98 -134 85 -5 -5 103 -138 184 -226 52 -57 46 -61 -49 -31 -87 28 -173 85 -325 214 -144 123 -189 151 -254 159 -25 2 -39 3 -32 0 6 -3 12 -16 12 -29 0 -17 -4 -23 -12 -19 -7 4 -1 -5 15 -19 15 -14 46 -30 70 -36 23 -6 60 -17 82 -25 l40 -13 -52 -1 c-49 -1 -53 -3 -53 -25 0 -28 28 -36 122 -36 54 0 62 -3 91 -32 l32 -33 -52 -3 c-88 -5 -75 -26 39 -62 130 -41 178 -63 206 -93 l22 -24 -20 -18 c-12 -11 -41 -20 -74 -23 -48 -4 -60 0 -116 32 -118 69 -241 96 -279 62 -13 -12 -40 -16 -104 -16 -48 0 -95 4 -105 9 -9 5 -34 16 -55 25 -20 9 -76 53 -122 97 -47 45 -88 79 -91 76 -12 -12 6 -54 41 -100 l36 -47 -74 0 c-88 0 -112 15 -78 49 26 26 21 39 -34 84 -87 73 -132 32 -50 -45 25 -23 45 -46 45 -50 0 -4 -21 2 -46 12 -45 20 -114 27 -114 12 0 -4 16 -22 35 -40 39 -38 78 -47 255 -61 164 -14 178 -17 172 -47 -2 -13 -2 -30 2 -39 5 -13 9 -13 40 2 19 9 33 20 30 25 -4 5 4 4 15 -4 21 -13 20 -15 -44 -74 -36 -33 -65 -66 -65 -72 0 -21 68 15 142 74 38 30 84 62 103 70 73 30 203 11 312 -46 32 -17 82 -33 111 -37 l53 -6 -28 -30 c-15 -16 -44 -40 -63 -52 -63 -42 -100 -82 -100 -111 0 -25 2 -26 37 -21 28 5 47 17 73 49 65 78 175 183 224 216 34 22 61 32 88 32 43 0 122 -36 152 -69 10 -11 39 -32 62 -46 43 -25 53 -46 36 -72 -11 -17 -200 -86 -232 -85 -28 1 -122 -47 -127 -65 -3 -8 14 -13 54 -15 l58 -3 -65 -44 c-36 -25 -68 -49 -71 -53 -11 -17 -115 -10 -188 12 -105 32 -112 11 -14 -39 l56 -28 -40 -6 c-52 -9 -96 0 -154 29 -31 15 -50 19 -60 13 -15 -10 -16 -10 44 -47 20 -13 37 -27 37 -31 0 -15 -144 -71 -205 -81 -33 -5 -85 -10 -116 -10 -32 0 -63 -6 -75 -14 -18 -13 -15 -15 34 -22 l53 -7 -31 -18 c-18 -12 -30 -27 -30 -40 0 -21 4 -21 61 -16 50 4 75 14 145 57 46 28 112 63 146 77 62 24 191 62 215 63 22 0 -47 -103 -107 -161 -42 -41 -60 -66 -65 -92 -10 -49 0 -60 47 -54 65 9 99 53 124 160 20 83 49 133 94 163 22 15 40 30 40 34 0 4 8 10 18 13 9 3 31 15 49 27 31 21 31 21 26 0 -3 -11 -21 -37 -40 -58 -43 -45 -72 -106 -73 -150 0 -39 18 -49 56 -30 38 18 63 64 75 137 13 82 40 142 89 194 22 24 40 49 40 54 0 6 24 21 53 34 49 22 63 23 237 20 193 -2 278 -14 348 -50 21 -11 44 -27 52 -36 7 -9 32 -27 55 -41 49 -28 138 -147 184 -243 16 -35 42 -114 57 -175 35 -147 31 -268 -14 -436 -17 -63 -37 -166 -45 -227 -12 -94 -21 -127 -54 -195 -22 -46 -52 -94 -67 -108 -15 -14 -46 -44 -68 -67 -23 -24 -47 -43 -53 -43 -7 0 -25 -12 -41 -26 -129 -121 -526 -267 -721 -268 -104 0 -109 -15 -19 -57 75 -35 67 -35 221 -10 125 21 235 27 235 13 0 -16 -66 -72 -85 -72 -24 0 -85 -36 -85 -49 0 -14 12 -19 58 -27 35 -5 42 -4 42 10 0 9 5 17 10 17 28 4 74 20 80 29 9 14 226 119 288 139 63 21 215 43 239 34 32 -12 12 -50 -52 -102 -36 -28 -73 -55 -83 -58 -17 -7 -66 -39 -92 -60 -8 -7 -38 -32 -67 -57 l-52 -45 42 -6 c36 -6 54 -1 122 33 44 22 120 56 169 76 112 45 222 107 289 164 46 40 84 62 107 62 18 0 130 -83 130 -96 0 -8 4 -14 10 -14 5 0 39 -26 75 -59 36 -32 96 -76 132 -97 36 -21 68 -46 71 -56 5 -19 21 -22 59 -12 18 5 20 9 10 19 -7 7 -69 71 -139 143 -147 149 -161 179 -80 168 136 -19 195 -39 378 -128 78 -37 147 -68 152 -68 6 0 15 -7 20 -16 8 -14 14 -15 46 -4 l38 12 -104 57 c-125 69 -148 84 -148 93 0 16 94 7 223 -22 74 -17 146 -30 160 -30 14 0 29 -4 32 -10 7 -11 54 6 115 43 l40 24 -38 1 c-22 1 -68 11 -103 21 -35 11 -107 30 -159 41 -131 31 -295 99 -406 171 -235 153 -355 366 -357 634 -1 211 65 343 285 568 117 119 206 167 368 198 153 29 267 59 328 86 80 35 166 44 264 27 148 -26 167 -38 251 -169 71 -110 136 -165 196 -165 26 0 31 4 31 23 -1 32 -28 62 -87 91 -39 20 -66 45 -118 112 -95 123 -95 135 -5 92 25 -12 77 -31 116 -43 56 -17 76 -19 97 -10 27 11 26 11 -25 29 -113 39 -138 49 -138 57 0 14 91 55 153 68 33 7 75 10 95 7 27 -4 31 -3 17 4 -19 8 -19 9 3 9 12 1 22 7 22 15 0 10 7 12 22 8 12 -3 17 -7 10 -10 -6 -2 -12 -10 -12 -18 0 -8 -6 -14 -12 -15 -27 -2 39 -17 80 -18 29 -1 42 3 42 13 0 20 -19 32 -67 45 -49 13 -53 22 -20 44 49 32 128 39 235 21 56 -9 112 -14 126 -10 26 6 96 69 96 86 0 6 -32 10 -77 10 -84 0 -126 -7 -118 -20 9 -14 -17 -24 -67 -24 -34 -1 -48 3 -48 13 0 8 -7 11 -20 8 -17 -4 -18 -2 -9 14 5 11 14 17 20 14 5 -4 9 0 9 8 0 8 16 35 35 60 35 45 35 46 14 57 -18 10 -23 8 -37 -12 -67 -95 -162 -167 -297 -225 -55 -23 -99 -47 -98 -53 2 -5 -6 -10 -18 -10 -27 0 -23 8 31 71 100 117 116 131 161 155 80 40 80 70 1 41 l-47 -18 36 38 c47 49 59 73 59 120 0 67 -24 80 -82 46 -49 -29 -76 -85 -89 -183 -10 -81 -9 -80 -89 -174 -20 -22 -43 -53 -53 -68 -28 -45 -89 -61 -217 -56 -70 2 -114 8 -118 15 -4 6 -28 13 -54 15 l-46 3 53 71 c51 68 100 104 140 104 50 0 125 73 125 122 0 30 0 31 -45 26 -28 -4 -59 -16 -82 -34 -30 -23 -35 -24 -28 -8 22 51 37 105 31 111 -13 13 -47 -34 -105 -146 -72 -138 -171 -248 -261 -292 -62 -30 -164 -44 -242 -34 -48 7 -50 8 -45 34 14 70 51 144 95 190 42 43 56 51 127 69 44 12 98 24 120 27 99 15 155 57 155 117 l0 31 -51 -5 c-40 -4 -58 -12 -87 -40 -56 -52 -66 -58 -131 -78 l-60 -18 10 27 c6 14 36 62 68 106 70 96 91 136 91 170 0 41 -18 29 -50 -35 -30 -59 -31 -60 -74 -60 -56 0 -71 -18 -101 -115 -23 -75 -43 -114 -72 -145 -8 -8 -41 -61 -73 -117 -35 -59 -75 -113 -95 -128 -37 -29 -177 -85 -210 -85 -35 0 -222 -134 -282 -202 -31 -35 -64 -64 -72 -66 -22 -4 -68 116 -88 223 -20 113 -5 256 37 340 37 76 124 182 195 238 140 110 221 137 416 137 136 0 171 6 186 28 9 15 2 17 -76 18 -47 0 -127 4 -178 9 -167 15 -274 -28 -423 -170 -72 -68 -114 -95 -128 -81 -12 12 17 138 44 194 64 132 229 243 439 298 182 46 319 21 425 -80 68 -65 144 -115 202 -135 119 -41 155 -32 72 17 -88 52 -224 144 -224 152 0 14 51 20 210 22 115 2 160 0 175 -10 19 -12 18 -13 -8 -9 -36 6 -45 -9 -11 -17 56 -15 117 -51 183 -110 66 -59 119 -86 169 -86 54 0 31 59 -35 90 -167 77 -176 82 -158 93 25 13 138 -8 201 -37 27 -13 67 -43 89 -66 50 -53 84 -70 134 -70 37 0 71 19 71 40 0 24 -62 57 -129 70 -40 7 -74 17 -77 22 -3 4 24 8 60 8 36 0 68 -4 71 -10 3 -5 26 -10 51 -10 50 0 57 15 23 46 -24 21 -113 25 -226 10 -50 -7 -65 -6 -77 7 -19 18 -100 37 -160 37 -74 0 -136 12 -136 26 0 17 11 23 53 28 32 4 83 29 97 47 8 10 71 40 99 47 28 7 159 8 309 1 50 -2 92 -1 92 4 0 4 14 7 30 7 23 0 30 -4 30 -20 0 -15 -7 -20 -25 -20 -42 0 -28 -17 30 -39 75 -28 103 -27 117 4 8 18 19 25 38 25 34 0 47 35 23 62 -24 27 -70 31 -190 18 -101 -11 -112 -10 -173 10 -36 12 -104 24 -152 27 -49 3 -88 9 -88 13 0 8 35 26 66 35 12 3 37 21 55 40 19 20 67 58 106 86 57 39 86 52 134 60 71 11 170 58 193 91 34 49 10 51 -66 5 -52 -31 -154 -65 -165 -54 -11 12 9 25 67 47 33 12 60 26 60 30 0 5 15 17 34 28 31 18 67 84 53 98 -15 15 -69 -17 -124 -75 -35 -36 -63 -63 -63 -58 0 4 14 31 30 60 43 77 39 180 -7 151 -39 -24 -56 -62 -63 -141 -7 -87 -27 -127 -98 -200 -50 -52 -120 -103 -139 -103 -8 0 -22 -8 -31 -17 -31 -31 -26 11 12 94 33 74 61 171 53 180 -3 2 -17 1 -32 -3 -38 -10 -65 -74 -75 -185 -9 -97 -31 -147 -79 -176 -31 -20 -81 -71 -81 -83 0 -5 -24 -16 -52 -26 -29 -9 -58 -24 -64 -32 -7 -9 -33 -17 -70 -19 l-59 -4 55 48 c120 105 133 126 66 103 -19 -6 -58 -36 -88 -66 -29 -30 -72 -64 -96 -76 -41 -19 -50 -20 -163 -9 -168 16 -237 52 -196 103 21 27 121 88 143 88 60 0 183 34 234 65 30 18 74 36 97 40 85 14 180 99 131 118 -16 5 -19 13 -14 33 7 25 5 25 -26 19 -52 -11 -83 -31 -122 -76 -54 -63 -94 -86 -197 -109 -52 -12 -102 -24 -111 -26 -28 -8 -22 22 10 44 15 11 35 34 45 51 16 27 16 31 2 31 -8 0 -18 -7 -22 -16 -8 -23 -23 2 -23 40 0 23 -3 27 -12 18 -14 -14 -42 -16 -33 -2 3 6 4 10 1 10 -9 0 -37 -61 -62 -133 -29 -84 -85 -162 -137 -189 -24 -12 -67 -21 -110 -24 -40 -2 -100 -6 -134 -9 l-62 -4 36 72 c20 39 48 84 62 98 29 29 86 122 96 157 9 32 -20 29 -42 -5 -17 -27 -128 -174 -222 -297 -45 -58 -101 -96 -141 -96 -48 0 -9 184 59 284 41 59 113 107 234 156 51 20 141 67 198 102 57 36 118 72 134 80 40 21 155 46 240 53 48 5 82 14 113 32 58 34 150 44 258 28 41 -6 57 -4 75 9 17 12 46 16 111 16 88 0 116 12 57 24 -17 3 -48 6 -69 6 -21 0 -43 6 -49 14 -14 17 -8 22 40 31 30 5 36 10 32 26 -4 13 0 19 12 19 23 0 95 44 129 79 34 35 30 67 -6 55 -13 -4 -31 -7 -41 -6 -11 0 -38 -25 -68 -64 -28 -35 -53 -64 -55 -64 -2 0 7 22 21 50 14 27 25 60 25 74 0 14 5 37 11 53 7 19 7 29 0 31 -6 2 -11 17 -11 33 0 16 -4 29 -10 29 -5 0 -10 -19 -10 -42 0 -50 -37 -158 -74 -222 -28 -48 -90 -86 -141 -86 l-29 0 36 38 c20 20 43 52 52 70 16 31 16 32 -5 32 -25 0 -94 -47 -109 -75 -19 -35 -161 -104 -245 -120 -42 -8 -85 -15 -96 -15 -18 0 -18 1 -2 35 9 19 20 35 25 35 4 0 8 6 8 13 0 7 13 34 30 60 30 48 75 77 182 117 58 21 112 66 176 146 26 31 57 57 84 69 124 52 167 75 177 93 17 33 -14 35 -71 6 -29 -14 -69 -32 -89 -39 l-38 -14 20 51 c11 28 24 59 29 68 10 19 -20 60 -45 60 -18 0 -21 46 -4 52 6 2 14 26 16 53 4 43 2 52 -16 63 -20 11 -21 10 -21 -25 -1 -209 -5 -248 -32 -305 -34 -73 -116 -188 -133 -188 -8 0 -20 -6 -27 -13 -7 -7 -29 -18 -48 -24 -19 -6 -52 -18 -72 -27 -34 -15 -38 -15 -38 -1 0 35 32 73 89 109 60 37 77 51 45 39 -14 -5 -14 -3 -3 14 7 13 9 28 4 39 -5 11 -7 24 -6 29 1 6 -19 -11 -44 -37 -25 -26 -45 -44 -45 -40 0 4 18 34 40 67 30 45 40 69 40 100 0 53 -11 67 -46 63 -22 -2 -34 -13 -53 -47 -14 -25 -26 -40 -29 -33 -2 7 -10 12 -17 12 -10 0 -14 -33 -17 -142 l-4 -143 -63 -127 c-39 -81 -74 -137 -94 -154 -51 -43 -167 -87 -167 -63 0 6 26 38 58 72 32 34 66 73 75 87 18 26 23 100 8 100 -5 0 -22 -19 -38 -42 -15 -23 -52 -60 -81 -82 -31 -25 -61 -59 -74 -85 -37 -75 -125 -156 -220 -201 -46 -22 -101 -43 -121 -47 -32 -5 -37 -3 -37 13 0 36 50 229 70 269 10 23 38 95 61 160 39 116 84 211 117 248 12 13 13 20 4 29 -9 9 -21 5 -49 -18 -58 -46 -166 -272 -249 -519 -37 -110 -73 -207 -80 -216 -8 -8 -14 -21 -14 -28 0 -8 -19 -54 -41 -105 -23 -50 -61 -146 -84 -212 -49 -144 -77 -193 -152 -274 -71 -77 -89 -77 -115 2 -11 33 -27 59 -36 62 -19 6 -21 27 -23 201 -2 197 49 394 151 580 33 61 72 135 86 165 118 253 140 288 273 462 87 112 121 167 121 195 0 37 -25 27 -75 -29 -42 -48 -45 -50 -38 -23 13 50 9 145 -8 156 -19 12 -30 7 -50 -22 -23 -33 -15 -116 12 -124 28 -9 35 -49 12 -73 -10 -11 -36 -45 -58 -76 -22 -31 -44 -56 -48 -56 -12 0 -3 78 17 160 8 36 15 84 16 107 0 23 4 44 9 47 4 3 11 37 14 75 3 39 14 86 26 109 26 50 80 97 94 83 16 -16 59 -13 85 5 30 21 52 66 52 109 0 33 -2 35 -32 35 -59 0 -78 -12 -118 -69 -21 -31 -55 -74 -74 -96 l-35 -40 -1 41 c0 23 7 60 15 83 15 43 92 151 124 173 20 14 55 77 48 85 -3 2 -8 -5 -12 -16 -7 -24 -35 -17 -35 9 0 23 -13 32 -35 24 -9 -4 -12 -9 -7 -13 17 -10 2 -62 -16 -59 -11 2 -30 -24 -61 -87 -34 -68 -46 -84 -49 -67 -2 12 -8 22 -13 22 -5 0 -9 -10 -9 -22 -1 -23 -1 -23 -16 -3 -9 11 -24 37 -34 58 -11 24 -20 34 -23 25 -3 -7 -6 25 -6 72 -1 105 -21 98 -26 -10 -3 -43 -9 -76 -16 -81 -10 -6 -10 -11 0 -23 14 -16 3 -22 -15 -8 -5 4 2 -2 15 -13 51 -41 78 -76 96 -128 26 -70 25 -187 -3 -292 -13 -49 -21 -109 -20 -155 0 -104 -54 -310 -81 -310 -6 0 -11 3 -11 8 -1 44 -24 191 -41 259 l-22 85 27 108 c30 123 34 211 10 231 -20 16 -40 5 -72 -42 -21 -30 -23 -42 -18 -97 3 -36 1 -60 -4 -57 -17 11 -34 81 -34 150 -1 64 2 76 39 140 22 39 45 84 52 100 17 43 16 92 -2 98 -17 7 -17 8 -44 -73 -12 -36 -34 -87 -50 -113 -33 -56 -41 -48 -41 39 0 41 -4 54 -15 54 -11 0 -15 -18 -17 -80 -2 -45 -7 -80 -13 -80 -5 0 -10 4 -10 8 0 5 -8 16 -18 26 -58 58 -70 132 -53 314 7 69 6 92 -3 92 -6 0 -13 -6 -16 -12z m321 -503 c3 -20 4 -39 0 -42 -3 -4 -6 6 -6 20 0 17 -5 27 -15 27 -16 0 -20 12 -8 23 14 14 22 6 29 -28z m-1006 -80 c0 -8 -9 -15 -19 -15 -29 0 -35 18 -9 23 13 2 24 5 26 6 1 0 2 -6 2 -14z m327 -27 c-3 -7 -5 -2 -5 12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m-462 -358 c3 -5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m-835 -40 c-6 -11 -15 -20 -19 -20 -5 0 -7 9 -4 20 3 11 12 20 20 20 11 0 11 -4 3 -20z m864 -25 c3 -9 0 -15 -9 -15 -9 0 -15 -10 -16 -27 0 -27 -1 -27 -13 7 -10 25 -10 37 -2 42 18 12 33 9 40 -7z m-892 -863 c38 2 68 0 68 -4 0 -5 -33 -8 -73 -8 -49 0 -80 -5 -95 -16 -21 -14 -22 -14 -22 5 0 14 -6 21 -20 21 -11 0 -20 2 -20 4 0 2 -3 12 -6 21 -6 13 1 13 47 -5 40 -16 70 -21 121 -18z m3218 -91 c0 -10 -7 -21 -15 -25 -11 -4 -15 1 -15 19 0 16 6 25 15 25 8 0 15 -9 15 -19z m-3479 -32 c-14 -5 -30 -14 -37 -20 -8 -7 -13 -3 -17 11 -5 18 0 20 36 19 38 0 40 -1 18 -10z m3359 -572 c0 -7 -7 -22 -15 -33 -13 -17 -14 -16 -4 14 11 33 19 41 19 19z m-3345 -198 c7 -22 -7 -39 -33 -39 -10 0 -10 4 0 20 11 17 10 22 -2 30 -12 8 -11 10 7 10 13 0 24 -8 28 -21z m445 -37 c0 -23 -35 -8 -38 16 -3 21 -2 22 17 10 12 -7 21 -19 21 -26z m-310 -647 c-28 -30 -60 -33 -60 -7 0 26 9 32 48 32 l35 0 -23 -25z M3684 5414 c-14 -21 -16 -41 -10 -124 4 -62 2 -100 -3 -100 -9 0 -17 26 -25 84 -1 11 -8 22 -14 24 -9 3 -12 -27 -12 -105 0 -69 -6 -130 -16 -164 -24 -86 -24 -85 -33 -70 -6 9 -10 3 -14 -17 -4 -20 -23 -43 -59 -72 -29 -23 -54 -46 -54 -51 -1 -5 -3 -39 -4 -76 -1 -46 -4 -60 -9 -43 -6 21 -9 15 -20 -35 -16 -74 -29 -99 -97 -197 -35 -49 -54 -86 -54 -105 0 -31 59 -200 60 -171 0 9 6 21 14 25 13 7 13 16 0 68 -17 72 -12 78 75 93 106 19 127 33 145 95 20 70 65 111 186 172 83 42 89 47 75 62 -9 8 -14 23 -11 32 5 14 5 14 -6 1 -7 -8 -41 -25 -75 -37 -54 -19 -61 -19 -56 -5 10 26 43 72 91 126 46 53 77 104 68 114 -6 6 -95 -81 -143 -141 l-30 -38 -7 80 c-4 45 -5 89 -1 98 3 10 31 34 63 53 61 37 147 112 164 144 21 38 -53 8 -126 -52 -98 -80 -100 -81 -92 -46 12 47 54 133 81 166 14 16 25 42 25 56 l-1 27 -25 -30 -26 -30 6 53 c3 28 9 74 12 100 5 40 3 50 -10 55 -10 4 -22 -2 -32 -19z m-79 -654 c0 -125 0 -126 -33 -176 -22 -36 -56 -67 -109 -103 -42 -28 -82 -51 -90 -51 -20 0 -16 22 11 59 13 18 30 46 36 62 7 16 16 29 20 29 5 0 13 33 20 73 16 102 29 149 40 142 6 -4 10 -2 10 3 0 5 13 25 30 43 16 19 30 38 30 42 0 5 8 7 18 5 15 -3 17 -17 17 -128z M2287 5348 c-10 -10 -22 -16 -27 -13 -5 3 -8 -3 -8 -12 1 -10 -2 -37 -6 -59 -6 -32 -4 -43 7 -47 23 -9 67 61 67 105 0 44 -9 51 -33 26z M1853 5256 c4 -22 11 -42 17 -46 7 -4 14 -26 17 -49 3 -22 10 -41 15 -41 5 0 7 12 4 28 -3 15 -8 46 -11 69 -4 23 -16 49 -27 60 -21 18 -21 18 -15 -21z M1780 5267 c0 -47 15 -79 42 -93 l30 -16 -7 34 c-4 18 -12 44 -19 58 -12 28 -46 40 -46 17z M1960 5120 c0 -21 30 -70 49 -80 27 -14 31 -13 31 10 0 11 5 20 10 20 6 0 8 -11 4 -25 -6 -23 -4 -25 30 -25 20 0 36 4 36 8 0 18 -68 72 -109 87 -52 18 -51 18 -51 5z M4127 5076 c-4 -10 -7 -45 -7 -79 0 -42 -6 -70 -18 -88 -56 -85 -73 -107 -106 -138 -20 -19 -36 -39 -36 -43 0 -18 34 -6 62 23 29 28 34 30 84 24 41 -6 59 -3 83 11 37 22 44 37 28 59 -16 21 -72 22 -95 2 -21 -19 -32 -22 -32 -7 0 5 38 35 84 66 77 51 106 84 106 119 0 26 -43 16 -77 -17 l-33 -32 0 47 c0 34 -4 47 -14 47 -8 0 -16 5 -18 12 -3 7 -7 5 -11 -6z M1049 5043 c-8 -10 -9 -14 -2 -10 7 5 28 -18 57 -63 48 -74 62 -86 52 -45 -4 15 -2 25 4 25 15 0 -17 72 -41 93 -27 22 -53 22 -70 0z M3400 4964 c-14 -37 -12 -60 6 -75 14 -11 18 -10 30 6 19 26 18 81 -2 89 -23 9 -24 8 -34 -20z M3910 4933 c-29 -20 -40 -33 -40 -50 0 -33 63 6 75 45 8 26 -2 28 -35 5z M3401 4844 c0 -11 3 -14 6 -6 3 7 2 16 -1 19 -3 4 -6 -2 -5 -13z M4532 4829 c-8 -12 -9 -18 -3 -14 7 4 11 -8 12 -32 1 -21 4 -51 8 -68 6 -26 8 -22 13 29 5 39 3 67 -5 83 l-12 23 -13 -21z M1017 4820 c-8 -30 16 -52 65 -60 41 -6 45 -5 40 12 -5 20 -68 68 -88 68 -6 0 -14 -9 -17 -20z M2529 4778 c-11 -21 -7 -134 7 -185 8 -30 24 -19 24 17 0 16 7 35 15 44 18 17 20 70 4 115 -13 36 -33 40 -50 9z M830 4767 c0 -24 30 -116 40 -122 11 -7 1 92 -12 118 -10 20 -28 23 -28 4z M4660 4751 c0 -5 -9 -12 -19 -16 -23 -7 -101 -81 -101 -95 0 -26 50 -6 98 38 51 47 67 82 37 82 -8 0 -15 -4 -15 -9z M764 4719 c-7 -13 82 -106 92 -96 11 11 -9 43 -46 75 -27 23 -41 29 -46 21z M1887 4634 c-18 -18 -6 -91 21 -131 79 -118 125 -171 138 -159 10 9 8 18 -9 46 -12 19 -25 37 -29 38 -4 2 -8 25 -8 50 0 60 -15 115 -38 135 -20 19 -65 31 -75 21z M1030 4579 c0 -35 54 -169 68 -169 6 0 12 -8 12 -17 1 -17 2 -17 15 0 11 16 11 26 -5 71 -25 75 -47 114 -70 126 -17 9 -20 8 -20 -11z M697 4553 c19 -54 69 -92 112 -85 36 5 46 34 20 58 -11 9 -22 14 -25 11 -4 -4 -18 3 -32 13 -32 26 -84 28 -75 3z M1150 4486 c0 -36 52 -76 101 -76 47 0 57 29 12 32 -16 1 6 4 47 8 l75 6 -75 2 c-41 1 -85 8 -96 15 -12 6 -31 18 -43 25 -19 12 -21 11 -21 -12z M3909 4427 c-16 -27 -19 -51 -19 -149 0 -111 1 -118 20 -118 15 0 24 14 40 68 39 124 39 215 -1 225 -15 4 -25 -2 -40 -26z M4723 4375 c-30 -13 -33 -18 -33 -60 l0 -45 24 15 c27 18 49 48 60 83 9 26 -4 28 -51 7z M5002 4348 c-5 -7 -23 -40 -40 -73 -32 -65 -106 -146 -156 -172 -17 -9 -51 -21 -75 -28 -46 -11 -63 -28 -38 -38 7 -3 47 2 87 10 58 12 85 13 128 4 96 -19 162 -13 162 15 0 11 -19 14 -82 15 -46 0 -91 4 -101 8 -17 6 -17 8 2 25 11 10 44 25 73 32 34 8 43 13 27 13 -31 1 -44 24 -23 41 8 7 14 21 14 31 0 11 10 36 23 57 33 52 39 72 23 72 -7 0 -18 -6 -24 -12z M3632 4290 c-52 -37 -68 -69 -74 -145 -3 -33 -3 -54 0 -47 3 10 7 9 16 -4 12 -15 18 -14 79 15 69 33 101 66 95 98 -4 22 -37 30 -49 11 -6 -8 -7 10 -3 45 8 68 -2 72 -64 27z M5016 4271 c-11 -12 -16 -24 -12 -28 9 -9 39 22 34 37 -2 7 -10 4 -22 -9z M470 4264 c0 -9 8 -28 18 -42 10 -15 22 -33 27 -40 8 -15 -67 31 -102 62 -13 11 -23 14 -23 8 0 -7 -7 -12 -15 -12 -19 0 -19 -15 2 -50 20 -35 48 -48 118 -56 37 -5 55 -11 55 -20 0 -8 11 -14 25 -14 14 0 25 4 25 10 0 5 -22 43 -50 84 -49 73 -80 100 -80 70z M2382 4102 c-5 -9 -12 -42 -14 -72 -9 -96 18 -125 39 -43 17 66 17 120 -2 127 -8 3 -19 -2 -23 -12z M4235 3850 c36 -39 118 -80 162 -80 30 0 93 41 93 61 0 5 -14 15 -30 24 -25 13 -44 14 -107 6 -43 -5 -80 -6 -83 -1 -3 5 -19 12 -35 16 l-30 7 30 -33z M133 3667 c-19 -41 -15 -59 18 -86 30 -24 135 -55 151 -45 11 7 10 42 -2 49 -5 3 -10 11 -10 17 0 6 7 5 17 -3 12 -10 15 -10 9 -1 -13 22 -84 52 -125 52 -33 0 -40 4 -43 22 -3 21 -4 21 -15 -5z M5265 3631 c-16 -10 -43 -33 -60 -51 l-30 -32 83 4 c77 3 86 6 107 30 35 41 24 68 -29 68 -23 0 -54 -8 -71 -19z M400 3512 c0 -5 -53 -12 -117 -16 -64 -4 -120 -9 -125 -12 -16 -10 -8 -53 13 -65 51 -27 183 0 213 43 13 17 19 18 68 9 29 -5 66 -13 81 -16 22 -6 26 -4 21 9 -5 12 0 16 17 17 13 1 -3 7 -36 14 -106 22 -135 26 -135 17z M5090 3480 c0 -5 9 -10 20 -10 11 0 47 -13 80 -30 57 -29 121 -39 147 -22 22 14 19 45 -7 59 -30 15 -240 18 -240 3z M926 3272 c-12 -11 -6 -94 9 -122 16 -31 39 -47 86 -60 24 -7 29 -5 39 21 19 44 5 94 -34 124 -35 27 -90 47 -100 37z M500 3049 c0 -23 9 -62 20 -85 17 -33 30 -46 70 -62 34 -14 56 -32 70 -54 16 -28 109 -108 125 -108 3 0 5 7 5 15 0 12 7 14 28 9 15 -3 -8 11 -50 32 -42 20 -80 44 -83 53 -4 9 -9 46 -12 84 -5 61 -9 70 -41 98 -34 30 -59 43 -107 55 -24 6 -25 4 -25 -37z M5203 2779 c-18 -11 -35 -29 -38 -40 -7 -22 -20 -25 -32 -6 -8 13 -20 -26 -22 -74 -1 -30 25 -36 35 -7 4 15 9 17 20 7 8 -6 14 -18 14 -26 0 -11 7 -9 27 7 43 33 63 71 63 118 0 49 -14 53 -67 21z M5307 2622 c-34 -32 -86 -72 -115 -88 -51 -29 -61 -46 -29 -52 25 -5 198 81 229 114 15 16 30 41 33 57 7 25 5 27 -24 27 -25 0 -45 -13 -94 -58z M447 2652 c-10 -10 -15 -21 -12 -24 7 -7 75 24 75 34 0 15 -47 8 -63 -10z M263 2600 c-12 -5 -26 -18 -32 -28 -8 -15 -6 -24 13 -41 33 -31 84 -27 160 13 68 36 68 47 1 51 -16 1 -50 5 -75 8 -25 4 -55 3 -67 -3z M996 2540 c-37 -11 -102 -74 -115 -112 l-10 -28 58 0 c48 0 65 5 98 29 49 35 87 86 79 106 -6 17 -62 19 -110 5z M673 2324 c-43 -18 -42 -43 1 -88 33 -35 35 -36 114 -36 70 0 80 2 78 18 0 10 6 16 16 16 13 -1 9 8 -18 40 -19 22 -45 46 -57 53 -29 16 -94 15 -134 -3z M4790 2120 c-9 -17 -5 -20 43 -29 72 -15 166 -19 169 -9 2 5 11 6 21 3 20 -8 23 -35 5 -35 -7 -1 -2 -7 12 -15 29 -17 113 -20 141 -5 24 13 25 36 2 48 -10 5 -58 12 -108 15 -49 3 -117 12 -150 20 -110 28 -124 28 -135 7z M4881 2021 c6 -17 30 -50 53 -75 33 -36 49 -46 74 -46 25 0 32 4 32 19 0 25 -60 77 -99 86 -17 4 -36 15 -41 26 -17 30 -30 23 -19 -10z";
|
|
1318
1375
|
|
|
1319
1376
|
// src/genre-tree/TreeMarkIcon.tsx
|
|
1320
|
-
var
|
|
1377
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1321
1378
|
function TreeMarkIcon({ className: className2 }) {
|
|
1322
|
-
return /* @__PURE__ */ (0,
|
|
1379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
|
|
1323
1380
|
}
|
|
1324
1381
|
|
|
1325
1382
|
// src/genre-tree/GenrePlayActions.tsx
|
|
1326
|
-
var
|
|
1383
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1327
1384
|
function PlayIcon({ kind }) {
|
|
1328
|
-
return /* @__PURE__ */ (0,
|
|
1329
|
-
kind === "tree" && /* @__PURE__ */ (0,
|
|
1330
|
-
kind === "map" && /* @__PURE__ */ (0,
|
|
1331
|
-
kind === "tree-map" && /* @__PURE__ */ (0,
|
|
1332
|
-
/* @__PURE__ */ (0,
|
|
1333
|
-
/* @__PURE__ */ (0,
|
|
1385
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
|
|
1386
|
+
kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
|
|
1387
|
+
kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
|
|
1388
|
+
kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
|
|
1334
1391
|
] }),
|
|
1335
|
-
/* @__PURE__ */ (0,
|
|
1392
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react8.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
|
|
1336
1393
|
] });
|
|
1337
1394
|
}
|
|
1338
1395
|
function playOptions(playlist) {
|
|
1339
1396
|
if (!("genreOnlyTracksCount" in playlist)) return [{ key: "all", label: "Play", icon: "play", count: playlist.tracksCount }];
|
|
1340
|
-
const direct = { key: "direct", label: "Genre", icon: "play", selection: { coverage: "genreOnly" }, count: (0,
|
|
1397
|
+
const direct = { key: "direct", label: "Genre", icon: "play", selection: { coverage: "genreOnly" }, count: (0, import_genre_tree11.genreMusicCount)(playlist, { coverage: "genreOnly" }) };
|
|
1341
1398
|
const scopes = playlist.genreAndSubgenresTracksCount;
|
|
1342
1399
|
const inclusive = (label, icon, subgenres) => {
|
|
1343
1400
|
const selection = { coverage: "genreAndSubgenres", subgenres };
|
|
1344
|
-
return { key: subgenres, label, icon, selection, count: (0,
|
|
1401
|
+
return { key: subgenres, label, icon, selection, count: (0, import_genre_tree11.genreMusicCount)(playlist, selection) };
|
|
1345
1402
|
};
|
|
1346
1403
|
return "canonical" in scopes ? [direct, inclusive("Canonical", "tree", "canonical"), inclusive("Regional", "map", "regional"), inclusive("All", "tree-map", "all")] : [direct, inclusive("All", "map", "all")];
|
|
1347
1404
|
}
|
|
1348
1405
|
function GenrePlayActions({ genrePlaylist, playback }) {
|
|
1349
1406
|
const { isPlaying, setIsPlaying } = (0, import_player4.usePlayer)();
|
|
1350
1407
|
const options = playOptions(genrePlaylist);
|
|
1351
|
-
return /* @__PURE__ */ (0,
|
|
1352
|
-
const isCurrent = playback.playing?.uuid === genrePlaylist.uuid && (option.selection === void 0 || playback.playing.selectionKey === "" || playback.playing.selectionKey === (0,
|
|
1408
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_jsx_runtime26.Fragment, { children: options.map((option) => {
|
|
1409
|
+
const isCurrent = playback.playing?.uuid === genrePlaylist.uuid && (option.selection === void 0 || playback.playing.selectionKey === "" || playback.playing.selectionKey === (0, import_genre_tree11.genreMusicSelectionKey)(option.selection).join());
|
|
1353
1410
|
const playing = isCurrent && isPlaying;
|
|
1354
|
-
return /* @__PURE__ */ (0,
|
|
1355
|
-
|
|
1411
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1412
|
+
import_ui8.ActionButton,
|
|
1356
1413
|
{
|
|
1357
|
-
icon: playing ? /* @__PURE__ */ (0,
|
|
1414
|
+
icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react8.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PlayIcon, { kind: option.icon }),
|
|
1358
1415
|
label: playing ? "Pause" : option.label,
|
|
1359
1416
|
caption: String(option.count),
|
|
1360
1417
|
disabled: option.count === 0,
|
|
@@ -1373,24 +1430,24 @@ function GenrePlayActions({ genrePlaylist, playback }) {
|
|
|
1373
1430
|
}
|
|
1374
1431
|
|
|
1375
1432
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1376
|
-
var
|
|
1377
|
-
var
|
|
1433
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
1434
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1378
1435
|
|
|
1379
1436
|
// src/genre-tree/ClampedText.tsx
|
|
1380
|
-
var
|
|
1381
|
-
var
|
|
1437
|
+
var import_react13 = require("react");
|
|
1438
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1382
1439
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1383
|
-
const ref = (0,
|
|
1384
|
-
const [expanded, setExpanded] = (0,
|
|
1385
|
-
const [overflows, setOverflows] = (0,
|
|
1386
|
-
(0,
|
|
1440
|
+
const ref = (0, import_react13.useRef)(null);
|
|
1441
|
+
const [expanded, setExpanded] = (0, import_react13.useState)(false);
|
|
1442
|
+
const [overflows, setOverflows] = (0, import_react13.useState)(false);
|
|
1443
|
+
(0, import_react13.useLayoutEffect)(() => {
|
|
1387
1444
|
const element = ref.current;
|
|
1388
1445
|
if (!element || expanded) return;
|
|
1389
1446
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1390
1447
|
}, [children, expanded]);
|
|
1391
|
-
return /* @__PURE__ */ (0,
|
|
1392
|
-
/* @__PURE__ */ (0,
|
|
1393
|
-
(overflows || expanded) && /* @__PURE__ */ (0,
|
|
1448
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1450
|
+
(overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1394
1451
|
"button",
|
|
1395
1452
|
{
|
|
1396
1453
|
type: "button",
|
|
@@ -1403,35 +1460,12 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1403
1460
|
] });
|
|
1404
1461
|
}
|
|
1405
1462
|
|
|
1406
|
-
// src/genre-tree/CommonsPhoto.tsx
|
|
1407
|
-
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1408
|
-
function ExternalLink({ href, children }) {
|
|
1409
|
-
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1410
|
-
}
|
|
1411
|
-
function CommonsPhoto({
|
|
1412
|
-
image,
|
|
1413
|
-
alt,
|
|
1414
|
-
creditLabel = "Photo"
|
|
1415
|
-
}) {
|
|
1416
|
-
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
|
|
1417
|
-
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1418
|
-
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
1419
|
-
creditLabel,
|
|
1420
|
-
": ",
|
|
1421
|
-
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1422
|
-
",",
|
|
1423
|
-
" ",
|
|
1424
|
-
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1425
|
-
] })
|
|
1426
|
-
] });
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
1463
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1430
|
-
var
|
|
1464
|
+
var import_react14 = require("react");
|
|
1431
1465
|
var import_react_query = require("@tanstack/react-query");
|
|
1432
1466
|
var import_zod = require("zod");
|
|
1433
1467
|
var import_fa2 = require("react-icons/fa");
|
|
1434
|
-
var
|
|
1468
|
+
var import_ui9 = require("@behindthemusictree/ui");
|
|
1435
1469
|
var import_auth = require("@behindthemusictree/app-kit/auth");
|
|
1436
1470
|
var import_transport2 = require("@behindthemusictree/app-kit/transport");
|
|
1437
1471
|
var import_transport3 = require("@behindthemusictree/app-kit/transport");
|
|
@@ -1440,23 +1474,23 @@ var import_player5 = require("@behindthemusictree/app-kit/player");
|
|
|
1440
1474
|
var import_player6 = require("@behindthemusictree/app-kit/player");
|
|
1441
1475
|
var import_popup3 = require("@behindthemusictree/app-kit/popup");
|
|
1442
1476
|
var import_transport5 = require("@behindthemusictree/app-kit/transport");
|
|
1443
|
-
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1444
1477
|
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1445
1478
|
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1446
1479
|
var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1447
1480
|
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1481
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1448
1482
|
|
|
1449
1483
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1450
|
-
var
|
|
1451
|
-
var
|
|
1484
|
+
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1485
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1452
1486
|
function MusicBrainzRecordingLink({
|
|
1453
1487
|
song,
|
|
1454
1488
|
children,
|
|
1455
1489
|
className: className2 = ""
|
|
1456
1490
|
}) {
|
|
1457
|
-
const url = (0,
|
|
1458
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1459
|
-
return /* @__PURE__ */ (0,
|
|
1491
|
+
const url = (0, import_genre_tree12.musicBrainzRecordingUrl)(song);
|
|
1492
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_jsx_runtime28.Fragment, { children });
|
|
1493
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1460
1494
|
"a",
|
|
1461
1495
|
{
|
|
1462
1496
|
href: url,
|
|
@@ -1471,11 +1505,11 @@ function MusicBrainzRecordingLink({
|
|
|
1471
1505
|
}
|
|
1472
1506
|
|
|
1473
1507
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1474
|
-
var
|
|
1508
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1475
1509
|
var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
|
|
1476
|
-
var
|
|
1510
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1477
1511
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1478
|
-
var SongPageSchema = (0, import_transport3.PaginatedResponseSchema)(
|
|
1512
|
+
var SongPageSchema = (0, import_transport3.PaginatedResponseSchema)(import_genre_tree16.SongDetailedSchema).extend({
|
|
1479
1513
|
overallTotal: import_zod.z.number().nullable(),
|
|
1480
1514
|
page: import_zod.z.number().nullable(),
|
|
1481
1515
|
totalPages: import_zod.z.number().nullable()
|
|
@@ -1504,7 +1538,7 @@ function GenrePlaylistTracks({
|
|
|
1504
1538
|
renderTracksToolbar,
|
|
1505
1539
|
artist = null
|
|
1506
1540
|
}) {
|
|
1507
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0,
|
|
1541
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree17.useTrackList)();
|
|
1508
1542
|
const { session, sessionRestored } = (0, import_auth.useSession)();
|
|
1509
1543
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player5.usePlayer)();
|
|
1510
1544
|
const { showPopup } = (0, import_popup3.usePopup)();
|
|
@@ -1514,18 +1548,18 @@ function GenrePlaylistTracks({
|
|
|
1514
1548
|
const { fetchBound } = (0, import_transport2.usePlaybackFetch)(getBackendBaseUrl);
|
|
1515
1549
|
const playback = (0, import_transport2.usePlaybackContext)();
|
|
1516
1550
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1517
|
-
queryKey: [...scope === "me" ? uuid ?
|
|
1551
|
+
queryKey: [...scope === "me" ? uuid ? import_genre_tree14.genrePlaylistQueryKeys.me.tracks(uuid) : ["missing-personal-genre-playlist", genreUuid] : artist ? [...import_genre_tree15.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : import_genre_tree15.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1518
1552
|
queryFn: async ({ pageParam }) => {
|
|
1519
1553
|
if (scope === "me") {
|
|
1520
1554
|
if (!uuid) throw new Error("Personal genre has no playlist");
|
|
1521
1555
|
return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1522
1556
|
}
|
|
1523
1557
|
const response = await fetchBound(
|
|
1524
|
-
|
|
1558
|
+
import_genre_tree15.libraryEndpoints.reference.songs.list(),
|
|
1525
1559
|
true,
|
|
1526
1560
|
false,
|
|
1527
1561
|
{},
|
|
1528
|
-
{ genre: genreUuid, ...(0,
|
|
1562
|
+
{ genre: genreUuid, ...(0, import_genre_tree13.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1529
1563
|
);
|
|
1530
1564
|
const parsed = (0, import_transport4.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
|
|
1531
1565
|
return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
|
|
@@ -1536,8 +1570,8 @@ function GenrePlaylistTracks({
|
|
|
1536
1570
|
retry: (failureCount, error) => failureCount < 1 && error instanceof DOMException && error.name === "AbortError",
|
|
1537
1571
|
retryDelay: 0
|
|
1538
1572
|
});
|
|
1539
|
-
const [sentinel, setSentinel] = (0,
|
|
1540
|
-
(0,
|
|
1573
|
+
const [sentinel, setSentinel] = (0, import_react14.useState)(null);
|
|
1574
|
+
(0, import_react14.useEffect)(() => {
|
|
1541
1575
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1542
1576
|
const observer = new IntersectionObserver((entries) => {
|
|
1543
1577
|
if (entries.some((entry) => entry.isIntersecting)) {
|
|
@@ -1559,36 +1593,36 @@ function GenrePlaylistTracks({
|
|
|
1559
1593
|
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genreName }, scope, { startSongUuid, ...selection && { selection } }) : genrePlaylist ? playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid }) : Promise.reject(new Error("Personal genre has no playlist"));
|
|
1560
1594
|
started.catch((error) => {
|
|
1561
1595
|
console.error("Failed to play genre playlist:", error);
|
|
1562
|
-
if (error instanceof
|
|
1563
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1596
|
+
if (error instanceof import_genre_tree17.TracksFailureAlreadyReportedError) return;
|
|
1597
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport5.AppError ? error.code : import_transport5.ErrorCode.CLIENT_UNKNOWN }));
|
|
1564
1598
|
});
|
|
1565
1599
|
};
|
|
1566
|
-
return /* @__PURE__ */ (0,
|
|
1600
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { children: [
|
|
1567
1601
|
renderTracksToolbar?.(),
|
|
1568
|
-
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0,
|
|
1602
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { role: "alert", children: [
|
|
1569
1603
|
"Couldn't load ",
|
|
1570
1604
|
trackNoun.plural,
|
|
1571
1605
|
".",
|
|
1572
1606
|
" ",
|
|
1573
|
-
/* @__PURE__ */ (0,
|
|
1574
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1607
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1608
|
+
] }) : 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-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
|
|
1575
1609
|
tracks.map((track, index) => {
|
|
1576
1610
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1577
1611
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1578
1612
|
const isPlaying = isCurrent && playState === import_player6.PlayStates.PLAYING;
|
|
1579
|
-
const text = /* @__PURE__ */ (0,
|
|
1580
|
-
/* @__PURE__ */ (0,
|
|
1581
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1582
|
-
/* @__PURE__ */ (0,
|
|
1583
|
-
track.album && /* @__PURE__ */ (0,
|
|
1613
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1614
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree16.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1615
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1616
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_genre_tree18.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1617
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1584
1618
|
] });
|
|
1585
1619
|
const leading = renderSongLeading?.(track, index);
|
|
1586
1620
|
const actions = renderSongActions?.(track);
|
|
1587
|
-
const isUnplayable = (0,
|
|
1621
|
+
const isUnplayable = (0, import_genre_tree16.isSong)(track) && (0, import_genre_tree16.songUnplayableReason)(track) !== null;
|
|
1588
1622
|
if (isUnplayable) {
|
|
1589
|
-
return /* @__PURE__ */ (0,
|
|
1623
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1590
1624
|
leading,
|
|
1591
|
-
/* @__PURE__ */ (0,
|
|
1625
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1592
1626
|
"button",
|
|
1593
1627
|
{
|
|
1594
1628
|
type: "button",
|
|
@@ -1596,27 +1630,27 @@ function GenrePlaylistTracks({
|
|
|
1596
1630
|
"aria-disabled": "true",
|
|
1597
1631
|
"aria-label": label,
|
|
1598
1632
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1599
|
-
children: /* @__PURE__ */ (0,
|
|
1633
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1600
1634
|
}
|
|
1601
1635
|
),
|
|
1602
1636
|
text,
|
|
1603
1637
|
actions
|
|
1604
1638
|
] }, track.uuid);
|
|
1605
1639
|
}
|
|
1606
|
-
return /* @__PURE__ */ (0,
|
|
1640
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1607
1641
|
"li",
|
|
1608
1642
|
{
|
|
1609
1643
|
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)]" : ""}`,
|
|
1610
1644
|
children: [
|
|
1611
1645
|
leading,
|
|
1612
|
-
/* @__PURE__ */ (0,
|
|
1646
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1613
1647
|
"button",
|
|
1614
1648
|
{
|
|
1615
1649
|
type: "button",
|
|
1616
1650
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1617
1651
|
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_player6.PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1618
1652
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1619
|
-
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1653
|
+
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_fa2.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1620
1654
|
}
|
|
1621
1655
|
),
|
|
1622
1656
|
text,
|
|
@@ -1626,7 +1660,7 @@ function GenrePlaylistTracks({
|
|
|
1626
1660
|
track.uuid
|
|
1627
1661
|
);
|
|
1628
1662
|
}),
|
|
1629
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1663
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1630
1664
|
] })
|
|
1631
1665
|
] }) });
|
|
1632
1666
|
}
|
|
@@ -1634,8 +1668,9 @@ function GenrePlaylistTracks({
|
|
|
1634
1668
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1635
1669
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1636
1670
|
var import_si2 = require("react-icons/si");
|
|
1637
|
-
var
|
|
1671
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1638
1672
|
function GenreDetailExtras({
|
|
1673
|
+
imageInCover = false,
|
|
1639
1674
|
genreUuid,
|
|
1640
1675
|
genrePlaylist,
|
|
1641
1676
|
genreName,
|
|
@@ -1653,25 +1688,25 @@ function GenreDetailExtras({
|
|
|
1653
1688
|
...trackSlots
|
|
1654
1689
|
}) {
|
|
1655
1690
|
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1656
|
-
const { data: overview, isPending } = (0,
|
|
1691
|
+
const { data: overview, isPending } = (0, import_genre_tree19.useFetchGenreOverview)(
|
|
1657
1692
|
genreUuid,
|
|
1658
1693
|
scope,
|
|
1659
1694
|
getBackendBaseUrl,
|
|
1660
1695
|
criteriaOverviewSchema
|
|
1661
1696
|
);
|
|
1662
1697
|
if (isPending) {
|
|
1663
|
-
return /* @__PURE__ */ (0,
|
|
1664
|
-
/* @__PURE__ */ (0,
|
|
1698
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1699
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.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_ui10.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1665
1700
|
family
|
|
1666
1701
|
] });
|
|
1667
1702
|
}
|
|
1668
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1703
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_jsx_runtime30.Fragment, { children: family });
|
|
1669
1704
|
const { summary, summarySourceUrl, essentialSongs, image } = overview;
|
|
1670
1705
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1671
|
-
return /* @__PURE__ */ (0,
|
|
1672
|
-
image && /* @__PURE__ */ (0,
|
|
1673
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1674
|
-
summarySourceUrl && /* @__PURE__ */ (0,
|
|
1706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1707
|
+
image && (imageInCover ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CommonsPhotoCredit, { image, creditLabel: "Image" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CommonsPhoto, { image, alt: overview.name, creditLabel: "Image" })),
|
|
1708
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
|
|
1709
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1675
1710
|
"a",
|
|
1676
1711
|
{
|
|
1677
1712
|
href: summarySourceUrl,
|
|
@@ -1679,12 +1714,12 @@ function GenreDetailExtras({
|
|
|
1679
1714
|
rel: "noopener noreferrer",
|
|
1680
1715
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1681
1716
|
children: [
|
|
1682
|
-
/* @__PURE__ */ (0,
|
|
1717
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1683
1718
|
"Wikipedia"
|
|
1684
1719
|
]
|
|
1685
1720
|
}
|
|
1686
1721
|
),
|
|
1687
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1722
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
|
|
1688
1723
|
"a",
|
|
1689
1724
|
{
|
|
1690
1725
|
href: wikidataUrl,
|
|
@@ -1692,17 +1727,17 @@ function GenreDetailExtras({
|
|
|
1692
1727
|
rel: "noopener noreferrer",
|
|
1693
1728
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1694
1729
|
children: [
|
|
1695
|
-
/* @__PURE__ */ (0,
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1696
1731
|
"Wikidata"
|
|
1697
1732
|
]
|
|
1698
1733
|
}
|
|
1699
1734
|
)
|
|
1700
1735
|
] }) }),
|
|
1701
|
-
/* @__PURE__ */ (0,
|
|
1736
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1702
1737
|
family,
|
|
1703
1738
|
renderGenreDetailExtras?.(overview),
|
|
1704
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1705
|
-
/* @__PURE__ */ (0,
|
|
1739
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui10.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("ul", { className: "text-body 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-body text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1740
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
1706
1741
|
GenrePlaylistTracks,
|
|
1707
1742
|
{
|
|
1708
1743
|
genreUuid,
|
|
@@ -1720,10 +1755,10 @@ function GenreDetailExtras({
|
|
|
1720
1755
|
}
|
|
1721
1756
|
|
|
1722
1757
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1723
|
-
var
|
|
1724
|
-
var
|
|
1758
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1759
|
+
var import_react15 = require("react");
|
|
1725
1760
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1726
|
-
return /* @__PURE__ */ (0,
|
|
1761
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
|
|
1727
1762
|
}
|
|
1728
1763
|
function GenreInfoPanelBody({
|
|
1729
1764
|
toolbar,
|
|
@@ -1738,12 +1773,12 @@ function GenreInfoPanelBody({
|
|
|
1738
1773
|
band,
|
|
1739
1774
|
extras
|
|
1740
1775
|
}) {
|
|
1741
|
-
const summary = scope === "reference" ?
|
|
1742
|
-
const selection = summary ? (0,
|
|
1743
|
-
return /* @__PURE__ */ (0,
|
|
1776
|
+
const summary = scope === "reference" ? import_genre_tree20.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
|
|
1777
|
+
const selection = summary ? (0, import_genre_tree20.defaultGenreMusicSelection)(summary) : void 0;
|
|
1778
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1744
1779
|
toolbar,
|
|
1745
|
-
genrePlaylist ? /* @__PURE__ */ (0,
|
|
1746
|
-
/* @__PURE__ */ (0,
|
|
1780
|
+
genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenreActionBand, { ...band }),
|
|
1781
|
+
/* @__PURE__ */ (0, import_react15.createElement)(
|
|
1747
1782
|
GenreDetailExtras,
|
|
1748
1783
|
{
|
|
1749
1784
|
...extras,
|
|
@@ -1762,7 +1797,7 @@ function GenreInfoPanelBody({
|
|
|
1762
1797
|
}
|
|
1763
1798
|
|
|
1764
1799
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
1765
|
-
var
|
|
1800
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1766
1801
|
var RELATIVE_ITEM_COUNT = 1;
|
|
1767
1802
|
function toNode(genre, parentId) {
|
|
1768
1803
|
return {
|
|
@@ -1787,11 +1822,11 @@ function useGenreDetailContent({
|
|
|
1787
1822
|
expanded,
|
|
1788
1823
|
treeStyles
|
|
1789
1824
|
}) {
|
|
1790
|
-
const result = (0,
|
|
1825
|
+
const result = (0, import_genre_tree21.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
1791
1826
|
const detail = result.status === "success" ? result.genre : void 0;
|
|
1792
1827
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
1793
1828
|
const playback = hostPlayback ?? defaultPlayback;
|
|
1794
|
-
const view = (0,
|
|
1829
|
+
const view = (0, import_react16.useMemo)(() => {
|
|
1795
1830
|
if (!detail) return null;
|
|
1796
1831
|
const rootColor = (0, import_genre_tree_view4.getGenreTreeColor)(detail.root.wikidataId);
|
|
1797
1832
|
const relative = (genre, parentId) => ({
|
|
@@ -1805,7 +1840,7 @@ function useGenreDetailContent({
|
|
|
1805
1840
|
id: detail.uuid,
|
|
1806
1841
|
parentId: detail.parent?.uuid ?? null,
|
|
1807
1842
|
name: detail.name,
|
|
1808
|
-
itemCount: scope === "reference" ? (0,
|
|
1843
|
+
itemCount: scope === "reference" ? (0, import_genre_tree21.genreMusicCount)(detail.criteriaPlaylist ? import_genre_tree21.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : import_genre_tree21.GenreCatalogueCountsSchema.parse(detail.catalogue)) : detail.tracksCount,
|
|
1809
1844
|
wikidataId: detail.wikidataId
|
|
1810
1845
|
},
|
|
1811
1846
|
fill: own.fill,
|
|
@@ -1820,24 +1855,24 @@ function useGenreDetailContent({
|
|
|
1820
1855
|
if (result.status === "not-found") return {
|
|
1821
1856
|
name: null,
|
|
1822
1857
|
title: "Genre not found",
|
|
1823
|
-
content: /* @__PURE__ */ (0,
|
|
1858
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This genre does not exist." })
|
|
1824
1859
|
};
|
|
1825
1860
|
if (result.status === "error") return {
|
|
1826
1861
|
name: null,
|
|
1827
1862
|
title: "Genre unavailable",
|
|
1828
|
-
content: /* @__PURE__ */ (0,
|
|
1863
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
1829
1864
|
};
|
|
1830
1865
|
if (!detail || !view) return {
|
|
1831
1866
|
name: null,
|
|
1832
1867
|
title: "Loading genre\u2026",
|
|
1833
|
-
content: /* @__PURE__ */ (0,
|
|
1834
|
-
/* @__PURE__ */ (0,
|
|
1835
|
-
/* @__PURE__ */ (0,
|
|
1836
|
-
/* @__PURE__ */ (0,
|
|
1868
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
1869
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.Skeleton, { className: "h-6 w-2/3" }),
|
|
1870
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.Skeleton, { className: "h-4 w-full" }),
|
|
1871
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.Skeleton, { className: "h-4 w-3/4" })
|
|
1837
1872
|
] })
|
|
1838
1873
|
};
|
|
1839
|
-
const selectGenre = (nodeId) => onSelectGenre(
|
|
1840
|
-
const family = /* @__PURE__ */ (0,
|
|
1874
|
+
const selectGenre = (nodeId) => onSelectGenre(import_genre_tree21.GenreUuidSchema.parse(nodeId));
|
|
1875
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1841
1876
|
import_genre_tree_view4.GenreNodeFamily,
|
|
1842
1877
|
{
|
|
1843
1878
|
node: view.node,
|
|
@@ -1848,29 +1883,38 @@ function useGenreDetailContent({
|
|
|
1848
1883
|
onSelectNode: selectGenre
|
|
1849
1884
|
}
|
|
1850
1885
|
);
|
|
1851
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
1886
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1887
|
+
import_genre_tree_view4.GenreNodeAncestors,
|
|
1888
|
+
{
|
|
1889
|
+
node: view.node,
|
|
1890
|
+
fill: view.fill,
|
|
1891
|
+
textColor: view.textColor,
|
|
1892
|
+
ancestorNodes: view.ancestorNodes,
|
|
1893
|
+
onSelectNode: selectGenre
|
|
1894
|
+
}
|
|
1895
|
+
);
|
|
1852
1896
|
return {
|
|
1853
1897
|
name: detail.name,
|
|
1854
1898
|
title: detail.name,
|
|
1855
1899
|
headerAccent: { background: view.fill, color: view.textColor },
|
|
1856
1900
|
aside: expanded ? ancestors : void 0,
|
|
1857
|
-
content: /* @__PURE__ */ (0,
|
|
1858
|
-
/* @__PURE__ */ (0,
|
|
1901
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
1902
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1859
1903
|
GenreInfoPanelBody,
|
|
1860
1904
|
{
|
|
1861
|
-
family: /* @__PURE__ */ (0,
|
|
1905
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
1862
1906
|
family,
|
|
1863
1907
|
!expanded && ancestors
|
|
1864
1908
|
] }),
|
|
1865
1909
|
genreUuid: detail.uuid,
|
|
1866
|
-
genrePlaylist: hostPlaylist ?? (detail.criteriaPlaylist === null ? null : scope === "reference" ? { ...
|
|
1910
|
+
genrePlaylist: hostPlaylist ?? (detail.criteriaPlaylist === null ? null : scope === "reference" ? { ...import_genre_tree21.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist), criteria: import_genre_tree21.CriteriaMinimumSchema.parse(detail) } : { ...detail.criteriaPlaylist, criteria: import_genre_tree21.CriteriaMinimumSchema.parse(detail), tracksCount: detail.tracksCount }),
|
|
1867
1911
|
catalogue: detail.catalogue,
|
|
1868
1912
|
wikidataId: detail.wikidataId,
|
|
1869
1913
|
scope,
|
|
1870
1914
|
getBackendBaseUrl,
|
|
1871
1915
|
playback,
|
|
1872
1916
|
band: { node: view.node, ...band, shareTarget: band?.shareTarget?.kind === "genre" ? { ...band.shareTarget, name: detail.name } : band?.shareTarget },
|
|
1873
|
-
extras
|
|
1917
|
+
extras: { ...extras, imageInCover: true }
|
|
1874
1918
|
}
|
|
1875
1919
|
),
|
|
1876
1920
|
trailing
|
|
@@ -1902,9 +1946,13 @@ function GenreDetailPanel({
|
|
|
1902
1946
|
expanded,
|
|
1903
1947
|
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
1904
1948
|
});
|
|
1905
|
-
return /* @__PURE__ */ (0,
|
|
1906
|
-
|
|
1949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1950
|
+
GenreOverviewDetailPanel,
|
|
1907
1951
|
{
|
|
1952
|
+
genreUuid: panel.name ? import_genre_tree21.GenreUuidSchema.parse(genreUuid) : null,
|
|
1953
|
+
scope,
|
|
1954
|
+
getBackendBaseUrl,
|
|
1955
|
+
criteriaOverviewSchema: extras?.criteriaOverviewSchema,
|
|
1908
1956
|
side: "left",
|
|
1909
1957
|
title: panel.title,
|
|
1910
1958
|
headerAccent: panel.headerAccent,
|
|
@@ -1915,16 +1963,17 @@ function GenreDetailPanel({
|
|
|
1915
1963
|
edgeTab,
|
|
1916
1964
|
aside: panel.aside,
|
|
1917
1965
|
titleInSearch,
|
|
1918
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
1966
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react9.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react9.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
1919
1967
|
children: panel.content
|
|
1920
1968
|
}
|
|
1921
1969
|
);
|
|
1922
1970
|
}
|
|
1923
1971
|
|
|
1924
1972
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1925
|
-
var
|
|
1926
|
-
var
|
|
1927
|
-
var
|
|
1973
|
+
var import_react17 = require("react");
|
|
1974
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1975
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
1976
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1928
1977
|
function ChangeParentPopup({
|
|
1929
1978
|
genrePlaylist,
|
|
1930
1979
|
genrePlaylists,
|
|
@@ -1933,38 +1982,52 @@ function ChangeParentPopup({
|
|
|
1933
1982
|
onClose,
|
|
1934
1983
|
onParentChanged
|
|
1935
1984
|
}) {
|
|
1936
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1937
|
-
const candidates = (0,
|
|
1938
|
-
() => (0,
|
|
1985
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree22.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1986
|
+
const candidates = (0, import_react17.useMemo)(
|
|
1987
|
+
() => (0, import_genre_tree22.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1939
1988
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1940
1989
|
);
|
|
1941
|
-
const
|
|
1990
|
+
const [confirmingRoot, setConfirmingRoot] = (0, import_react17.useState)(false);
|
|
1991
|
+
const handleMove = (to) => {
|
|
1942
1992
|
const genre = genrePlaylist.criteria.uuid;
|
|
1943
1993
|
const parentPlaylistUuid = genrePlaylist.parent?.uuid;
|
|
1944
1994
|
const from = parentPlaylistUuid ? genrePlaylists.find(({ uuid }) => uuid === parentPlaylistUuid)?.criteria?.uuid ?? null : null;
|
|
1945
1995
|
mutate(
|
|
1946
|
-
{ uuid: genre, data: { parent:
|
|
1996
|
+
{ uuid: genre, data: { parent: to } },
|
|
1947
1997
|
{
|
|
1948
1998
|
onSuccess: () => {
|
|
1949
1999
|
onClose();
|
|
1950
|
-
onParentChanged?.({ genre, from, to
|
|
2000
|
+
onParentChanged?.({ genre, from, to });
|
|
1951
2001
|
}
|
|
1952
2002
|
}
|
|
1953
2003
|
);
|
|
1954
2004
|
};
|
|
1955
|
-
|
|
2005
|
+
const handleSelect = (newParent) => handleMove(newParent.criteria.uuid);
|
|
2006
|
+
(0, import_react17.useEffect)(() => {
|
|
1956
2007
|
const handleKeyDown = (event) => {
|
|
1957
2008
|
if (event.key === "Escape") onClose();
|
|
1958
2009
|
};
|
|
1959
2010
|
window.addEventListener("keydown", handleKeyDown);
|
|
1960
2011
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1961
2012
|
}, [onClose]);
|
|
1962
|
-
return /* @__PURE__ */ (0,
|
|
1963
|
-
/* @__PURE__ */ (0,
|
|
2013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
2014
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
|
|
1964
2015
|
"Current parent: ",
|
|
1965
2016
|
genrePlaylist.parent?.name ?? "none"
|
|
1966
2017
|
] }),
|
|
1967
|
-
/* @__PURE__ */ (0,
|
|
2018
|
+
genrePlaylist.parent && (confirmingRoot ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "mb-3 text-sm", children: [
|
|
2019
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "mb-2", children: [
|
|
2020
|
+
genrePlaylist.name,
|
|
2021
|
+
" and its descendants will no longer be under ",
|
|
2022
|
+
genrePlaylist.parent.name,
|
|
2023
|
+
"."
|
|
2024
|
+
] }),
|
|
2025
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(PopupButtons, { alignment: "left", children: [
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.Button, { disabled: isPending, onClick: () => handleMove(null), children: "Confirm" }),
|
|
2027
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.Button, { variant: "secondary", disabled: isPending, onClick: () => setConfirmingRoot(false), children: "Back" })
|
|
2028
|
+
] })
|
|
2029
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui12.Button, { variant: "secondary", onClick: () => setConfirmingRoot(true), children: "Make root" }) })),
|
|
2030
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1968
2031
|
GenreSearch,
|
|
1969
2032
|
{
|
|
1970
2033
|
genrePlaylists: candidates,
|
|
@@ -1972,25 +2035,25 @@ function ChangeParentPopup({
|
|
|
1972
2035
|
placeholder: "Search a new parent\u2026"
|
|
1973
2036
|
}
|
|
1974
2037
|
) }),
|
|
1975
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
2038
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1976
2039
|
] });
|
|
1977
2040
|
}
|
|
1978
2041
|
|
|
1979
2042
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1980
|
-
var
|
|
1981
|
-
var
|
|
2043
|
+
var import_ui19 = require("@behindthemusictree/ui");
|
|
2044
|
+
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1982
2045
|
|
|
1983
2046
|
// src/genre-tree/artist/ArtistActionBand.tsx
|
|
1984
2047
|
var import_lucide_react10 = require("lucide-react");
|
|
1985
|
-
var
|
|
1986
|
-
var
|
|
2048
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
2049
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1987
2050
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1988
|
-
var
|
|
2051
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1989
2052
|
function ArtistActionBand({ artist, share }) {
|
|
1990
|
-
const { trackList, playNewTrackListFromArtist } = (0,
|
|
2053
|
+
const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree23.useTrackList)();
|
|
1991
2054
|
const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
|
|
1992
2055
|
const origin = trackList?.origin;
|
|
1993
|
-
const isCurrent = origin?.type ===
|
|
2056
|
+
const isCurrent = origin?.type === import_genre_tree23.TrackListOriginType.ARTIST && origin.uuid === artist.uuid;
|
|
1994
2057
|
const playing = isCurrent && isPlaying;
|
|
1995
2058
|
const handleClick = () => {
|
|
1996
2059
|
if (isCurrent) {
|
|
@@ -2001,11 +2064,11 @@ function ArtistActionBand({ artist, share }) {
|
|
|
2001
2064
|
console.error("Failed to load artist tracks:", error);
|
|
2002
2065
|
});
|
|
2003
2066
|
};
|
|
2004
|
-
return /* @__PURE__ */ (0,
|
|
2005
|
-
/* @__PURE__ */ (0,
|
|
2006
|
-
|
|
2067
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui13.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_ui13.ActionBand, { children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2069
|
+
import_ui13.ActionButton,
|
|
2007
2070
|
{
|
|
2008
|
-
icon: playing ? /* @__PURE__ */ (0,
|
|
2071
|
+
icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
2009
2072
|
label: playing ? "Pause" : "Play",
|
|
2010
2073
|
caption: String(artist.playableTracksCount),
|
|
2011
2074
|
disabled: artist.playableTracksCount === 0,
|
|
@@ -2018,13 +2081,13 @@ function ArtistActionBand({ artist, share }) {
|
|
|
2018
2081
|
}
|
|
2019
2082
|
|
|
2020
2083
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2021
|
-
var
|
|
2084
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
2022
2085
|
|
|
2023
2086
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
2024
2087
|
var import_lucide_react11 = require("lucide-react");
|
|
2025
2088
|
var import_si3 = require("react-icons/si");
|
|
2026
|
-
var
|
|
2027
|
-
var
|
|
2089
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
2090
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2028
2091
|
var LINK_KINDS = {
|
|
2029
2092
|
official: { label: "Official website", Icon: import_lucide_react11.Globe },
|
|
2030
2093
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -2061,7 +2124,7 @@ function artistLinks(artist) {
|
|
|
2061
2124
|
return links;
|
|
2062
2125
|
}
|
|
2063
2126
|
function ArtistLinks({ links }) {
|
|
2064
|
-
return /* @__PURE__ */ (0,
|
|
2127
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui14.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
2065
2128
|
}
|
|
2066
2129
|
|
|
2067
2130
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2072,7 +2135,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2072
2135
|
}
|
|
2073
2136
|
|
|
2074
2137
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2075
|
-
var
|
|
2138
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2076
2139
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2077
2140
|
function originLabel(artist, origin) {
|
|
2078
2141
|
if (origin.source === "area") return "From";
|
|
@@ -2086,7 +2149,7 @@ function OriginValue({
|
|
|
2086
2149
|
renderPlaceLink
|
|
2087
2150
|
}) {
|
|
2088
2151
|
const place = renderPlaceLink && origin.regionId !== null ? renderPlaceLink({ ...origin, regionId: origin.regionId }) : origin.name;
|
|
2089
|
-
return /* @__PURE__ */ (0,
|
|
2152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
2090
2153
|
place,
|
|
2091
2154
|
country && origin.kind !== "country" && `, ${country}`
|
|
2092
2155
|
] });
|
|
@@ -2101,30 +2164,24 @@ function ArtistHeader({
|
|
|
2101
2164
|
if (artist.origin) {
|
|
2102
2165
|
fields.push({
|
|
2103
2166
|
label: originLabel(artist, artist.origin),
|
|
2104
|
-
value: /* @__PURE__ */ (0,
|
|
2167
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(OriginValue, { origin: artist.origin, country, renderPlaceLink })
|
|
2105
2168
|
});
|
|
2106
2169
|
}
|
|
2107
2170
|
if (country && !artist.origin) fields.push({ label: "Country", value: country });
|
|
2108
2171
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2109
2172
|
if (years) fields.push({ label: "Active", value: years });
|
|
2110
2173
|
const links = artistLinks(artist);
|
|
2111
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2174
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistLinks, { links }) });
|
|
2112
2175
|
if (fields.length === 0) return null;
|
|
2113
|
-
return /* @__PURE__ */ (0,
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2117
|
-
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2118
|
-
function ArtistPhoto({ image, name }) {
|
|
2119
|
-
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommonsPhoto, { image, alt: name });
|
|
2176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui15.FieldList, { items: fields });
|
|
2120
2177
|
}
|
|
2121
2178
|
|
|
2122
2179
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2123
|
-
var
|
|
2180
|
+
var import_ui16 = require("@behindthemusictree/ui");
|
|
2124
2181
|
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2125
2182
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2126
2183
|
if (memberships.length === 0) return null;
|
|
2127
|
-
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2184
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui16.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2128
2185
|
const details = [
|
|
2129
2186
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2130
2187
|
...membership.roles
|
|
@@ -2145,7 +2202,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2145
2202
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2146
2203
|
if (memberships.length === 0) return null;
|
|
2147
2204
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2148
|
-
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ui16.PanelSection, { title, children: [
|
|
2149
2206
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2150
2207
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2151
2208
|
MembershipList,
|
|
@@ -2159,7 +2216,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2159
2216
|
}
|
|
2160
2217
|
|
|
2161
2218
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2162
|
-
var
|
|
2219
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2163
2220
|
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2164
2221
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2165
2222
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
@@ -2180,10 +2237,10 @@ function byYear(a, b) {
|
|
|
2180
2237
|
function ArtistDiscography({ albums }) {
|
|
2181
2238
|
if (albums.length === 0) return null;
|
|
2182
2239
|
const sorted = [...albums].sort(byYear);
|
|
2183
|
-
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2240
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui17.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2184
2241
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2185
2242
|
if (groupAlbums.length === 0) return null;
|
|
2186
|
-
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2243
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui17.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2187
2244
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2188
2245
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2189
2246
|
] }, album.uuid)) }) }, group);
|
|
@@ -2191,16 +2248,16 @@ function ArtistDiscography({ albums }) {
|
|
|
2191
2248
|
}
|
|
2192
2249
|
|
|
2193
2250
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2194
|
-
var
|
|
2251
|
+
var import_react18 = require("react");
|
|
2195
2252
|
var import_react_query2 = require("@tanstack/react-query");
|
|
2196
|
-
var
|
|
2253
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
2197
2254
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2198
|
-
var
|
|
2255
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2199
2256
|
var import_transport6 = require("@behindthemusictree/app-kit/transport");
|
|
2200
2257
|
|
|
2201
2258
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2202
2259
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
2203
|
-
var
|
|
2260
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2204
2261
|
|
|
2205
2262
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
2206
2263
|
var import_fa3 = require("react-icons/fa");
|
|
@@ -2255,8 +2312,8 @@ function TrackPositionPlayPause({
|
|
|
2255
2312
|
}
|
|
2256
2313
|
|
|
2257
2314
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2258
|
-
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2259
2315
|
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2316
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2260
2317
|
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2261
2318
|
function TrackItem({
|
|
2262
2319
|
track,
|
|
@@ -2267,11 +2324,11 @@ function TrackItem({
|
|
|
2267
2324
|
renderSongLink
|
|
2268
2325
|
}) {
|
|
2269
2326
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player10.usePlayer)();
|
|
2270
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
2327
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree24.useTrackList)();
|
|
2271
2328
|
const scope = trackList?.origin?.scope ?? null;
|
|
2272
2329
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
2273
2330
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
2274
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
2331
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree25.isSong)(track) && (0, import_genre_tree25.songUnplayableReason)(track) !== null;
|
|
2275
2332
|
const handlePlayPauseClick = (event) => {
|
|
2276
2333
|
event.stopPropagation();
|
|
2277
2334
|
if (isCurrentTrack) {
|
|
@@ -2304,8 +2361,8 @@ function TrackItem({
|
|
|
2304
2361
|
}
|
|
2305
2362
|
),
|
|
2306
2363
|
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2307
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0,
|
|
2308
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2364
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree25.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2365
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree26.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2309
2366
|
] }),
|
|
2310
2367
|
/* @__PURE__ */ (0, import_jsx_runtime40.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_runtime40.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
|
|
2311
2368
|
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
@@ -2324,20 +2381,20 @@ function TrackItem({
|
|
|
2324
2381
|
|
|
2325
2382
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2326
2383
|
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2327
|
-
var SongPageSchema2 = (0, import_transport6.PaginatedResponseSchema)(
|
|
2384
|
+
var SongPageSchema2 = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree27.SongDetailedSchema);
|
|
2328
2385
|
var SORTS = [
|
|
2329
2386
|
{ ordering: "title", label: "Title" },
|
|
2330
2387
|
{ ordering: "-albumYear", label: "Year" }
|
|
2331
2388
|
];
|
|
2332
2389
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
2333
|
-
const [page, setPage] = (0,
|
|
2334
|
-
const [ordering, setOrdering] = (0,
|
|
2335
|
-
const { playNewTrackListFromArtist } = (0,
|
|
2390
|
+
const [page, setPage] = (0, import_react18.useState)(1);
|
|
2391
|
+
const [ordering, setOrdering] = (0, import_react18.useState)("title");
|
|
2392
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree27.useTrackList)();
|
|
2336
2393
|
const { playerTrackObject } = (0, import_player11.usePlayer)();
|
|
2337
2394
|
const { data, isPending, isError } = (0, import_transport6.usePlaybackQuery)({
|
|
2338
2395
|
getBackendBaseUrl,
|
|
2339
2396
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
2340
|
-
queryFn: (fetchBound) => fetchBound(
|
|
2397
|
+
queryFn: (fetchBound) => fetchBound(import_genre_tree27.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
2341
2398
|
artist: artist.uuid,
|
|
2342
2399
|
ordering,
|
|
2343
2400
|
page
|
|
@@ -2352,11 +2409,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2352
2409
|
});
|
|
2353
2410
|
const handleRowClickCapture = (event, song) => {
|
|
2354
2411
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
2355
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2412
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree27.songUnplayableReason)(song) !== null) return;
|
|
2356
2413
|
event.stopPropagation();
|
|
2357
2414
|
void play(song.uuid);
|
|
2358
2415
|
};
|
|
2359
|
-
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2416
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2360
2417
|
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2361
2418
|
"button",
|
|
2362
2419
|
{
|
|
@@ -2380,7 +2437,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2380
2437
|
renderSongLink
|
|
2381
2438
|
}
|
|
2382
2439
|
) }, song.uuid)) }),
|
|
2383
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui18.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2384
2441
|
] }) });
|
|
2385
2442
|
}
|
|
2386
2443
|
|
|
@@ -2398,7 +2455,7 @@ function useArtistPanel({
|
|
|
2398
2455
|
onExpandedChange,
|
|
2399
2456
|
onClose
|
|
2400
2457
|
}) {
|
|
2401
|
-
const result = (0,
|
|
2458
|
+
const result = (0, import_genre_tree28.useArtist)(uuid, getBackendBaseUrl);
|
|
2402
2459
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2403
2460
|
switch (result.status) {
|
|
2404
2461
|
case "idle":
|
|
@@ -2408,12 +2465,12 @@ function useArtistPanel({
|
|
|
2408
2465
|
...panel,
|
|
2409
2466
|
title: "Artist",
|
|
2410
2467
|
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2411
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2412
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2413
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2414
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2415
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2416
|
-
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2468
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
|
|
2469
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-4 w-32" }),
|
|
2470
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
|
|
2471
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-4 w-24" }),
|
|
2472
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2473
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui19.Skeleton, { className: "h-16" })
|
|
2417
2474
|
] })
|
|
2418
2475
|
};
|
|
2419
2476
|
case "not-found":
|
|
@@ -2435,6 +2492,7 @@ function useArtistPanel({
|
|
|
2435
2492
|
return {
|
|
2436
2493
|
...panel,
|
|
2437
2494
|
title: artist.name,
|
|
2495
|
+
cover: artist.image ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(CommonsCover, { image: artist.image, alt: artist.name }) : void 0,
|
|
2438
2496
|
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2439
2497
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2440
2498
|
ArtistActionBand,
|
|
@@ -2443,7 +2501,7 @@ function useArtistPanel({
|
|
|
2443
2501
|
share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
|
|
2444
2502
|
}
|
|
2445
2503
|
),
|
|
2446
|
-
artist.image && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2504
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(CommonsPhotoCredit, { image: artist.image }),
|
|
2447
2505
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistHeader, { artist, renderPlaceLink }),
|
|
2448
2506
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2449
2507
|
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
@@ -2464,24 +2522,24 @@ function useArtistPanel({
|
|
|
2464
2522
|
}
|
|
2465
2523
|
|
|
2466
2524
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2467
|
-
var
|
|
2468
|
-
var
|
|
2525
|
+
var import_ui21 = require("@behindthemusictree/ui");
|
|
2526
|
+
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2469
2527
|
|
|
2470
2528
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2471
|
-
var
|
|
2529
|
+
var import_react19 = require("react");
|
|
2472
2530
|
var import_lucide_react12 = require("lucide-react");
|
|
2473
|
-
var
|
|
2531
|
+
var import_ui20 = require("@behindthemusictree/ui");
|
|
2474
2532
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
2475
|
-
var
|
|
2533
|
+
var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2476
2534
|
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2477
2535
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2478
2536
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2479
2537
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2480
2538
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2481
|
-
const [open, setOpen] = (0,
|
|
2482
|
-
const history = (0,
|
|
2539
|
+
const [open, setOpen] = (0, import_react19.useState)(false);
|
|
2540
|
+
const history = (0, import_genre_tree29.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2483
2541
|
let body = null;
|
|
2484
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2542
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui20.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2485
2543
|
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2486
2544
|
else if (history.status === "success")
|
|
2487
2545
|
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: "py-1.5 text-body", children: [
|
|
@@ -2499,20 +2557,20 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
2499
2557
|
dateFormat.format(new Date(entry.createdOn))
|
|
2500
2558
|
] })
|
|
2501
2559
|
] }, entry.uuid)) });
|
|
2502
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2503
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("summary", { className: `mt-2.5 cursor-pointer ${
|
|
2560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui20.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui20.PANEL_LABEL_CLASS}`, children: "History" }),
|
|
2504
2562
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "mt-1.5", children: body })
|
|
2505
2563
|
] }) });
|
|
2506
2564
|
}
|
|
2507
2565
|
function SongPanelContent(props) {
|
|
2508
2566
|
const { song, scope, getBackendBaseUrl, shareUrl, renderArtistLink } = props;
|
|
2509
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2567
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree29.useTrackList)();
|
|
2510
2568
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player12.usePlayer)();
|
|
2511
|
-
const unplayableReason = (0,
|
|
2569
|
+
const unplayableReason = (0, import_genre_tree29.songUnplayableReason)(song);
|
|
2512
2570
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2513
2571
|
const isPlaying = isCurrent && playState === import_player12.PlayStates.PLAYING;
|
|
2514
2572
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2515
|
-
const musicBrainzUrl = (0,
|
|
2573
|
+
const musicBrainzUrl = (0, import_genre_tree29.musicBrainzRecordingUrl)(song);
|
|
2516
2574
|
const fields = [];
|
|
2517
2575
|
if (song.genre) {
|
|
2518
2576
|
const path = props.genrePath && props.genrePath.length > 0 ? props.genrePath : null;
|
|
@@ -2558,9 +2616,9 @@ function SongPanelContent(props) {
|
|
|
2558
2616
|
value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex gap-3", children: links })
|
|
2559
2617
|
});
|
|
2560
2618
|
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
|
|
2561
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2619
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui20.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui20.ActionBand, { children: [
|
|
2562
2620
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2563
|
-
|
|
2621
|
+
import_ui20.ActionButton,
|
|
2564
2622
|
{
|
|
2565
2623
|
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
2566
2624
|
label: isPlaying ? "Pause" : "Play",
|
|
@@ -2571,9 +2629,9 @@ function SongPanelContent(props) {
|
|
|
2571
2629
|
),
|
|
2572
2630
|
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2573
2631
|
] }) }),
|
|
2574
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2575
|
-
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2576
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2632
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_genre_tree29.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2633
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui20.FieldList, { items: fields }),
|
|
2634
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui20.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2577
2635
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2578
2636
|
] });
|
|
2579
2637
|
}
|
|
@@ -2591,7 +2649,7 @@ function useSongPanel({
|
|
|
2591
2649
|
onClose,
|
|
2592
2650
|
...navigation
|
|
2593
2651
|
}) {
|
|
2594
|
-
const result = (0,
|
|
2652
|
+
const result = (0, import_genre_tree30.useSong)(uuid, getBackendBaseUrl);
|
|
2595
2653
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2596
2654
|
switch (result.status) {
|
|
2597
2655
|
case "idle":
|
|
@@ -2601,10 +2659,10 @@ function useSongPanel({
|
|
|
2601
2659
|
...panel,
|
|
2602
2660
|
title: "Song",
|
|
2603
2661
|
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2604
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2605
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2606
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2607
|
-
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2662
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui21.Skeleton, { className: "h-3 w-16" }),
|
|
2663
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui21.Skeleton, { className: "h-4 w-32" }),
|
|
2664
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui21.Skeleton, { className: "h-3 w-16" }),
|
|
2665
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui21.Skeleton, { className: "h-4 w-24" })
|
|
2608
2666
|
] })
|
|
2609
2667
|
};
|
|
2610
2668
|
case "not-found":
|
|
@@ -2679,14 +2737,14 @@ function useEntityPanel({
|
|
|
2679
2737
|
|
|
2680
2738
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2681
2739
|
var import_md = require("react-icons/md");
|
|
2682
|
-
var
|
|
2740
|
+
var import_ui22 = require("@behindthemusictree/ui");
|
|
2683
2741
|
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2684
2742
|
function SizeMetricToggle({
|
|
2685
2743
|
value,
|
|
2686
2744
|
onChange
|
|
2687
2745
|
}) {
|
|
2688
2746
|
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2689
|
-
|
|
2747
|
+
import_ui22.SegmentedControl,
|
|
2690
2748
|
{
|
|
2691
2749
|
variant: "floating",
|
|
2692
2750
|
"aria-label": "Size nodes by",
|
|
@@ -2727,6 +2785,8 @@ function GenreTreeView({
|
|
|
2727
2785
|
criteriaOverviewSchema,
|
|
2728
2786
|
renderGenreDetailExtras,
|
|
2729
2787
|
floatingControlsLeading,
|
|
2788
|
+
searchTrailingAction,
|
|
2789
|
+
searchFullWidthBreakpoint = "md",
|
|
2730
2790
|
sizeMetric: controlledSizeMetric,
|
|
2731
2791
|
onSizeMetricChange,
|
|
2732
2792
|
renderArtistLink,
|
|
@@ -2764,21 +2824,21 @@ function GenreTreeView({
|
|
|
2764
2824
|
placement = "default",
|
|
2765
2825
|
...focus
|
|
2766
2826
|
}) {
|
|
2767
|
-
const getShareUrlRef = (0,
|
|
2827
|
+
const getShareUrlRef = (0, import_react20.useRef)(getShareUrl);
|
|
2768
2828
|
getShareUrlRef.current = getShareUrl;
|
|
2769
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
2829
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react20.useState)(null);
|
|
2770
2830
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2771
2831
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2772
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
2832
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react20.useState)(null);
|
|
2773
2833
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2774
2834
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2775
2835
|
const changeSong = (uuid) => {
|
|
2776
2836
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2777
2837
|
onSongUuidChange?.(uuid);
|
|
2778
2838
|
};
|
|
2779
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
2839
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react20.useState)("songs");
|
|
2780
2840
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2781
|
-
const deferredSizeMetric = (0,
|
|
2841
|
+
const deferredSizeMetric = (0, import_react20.useDeferredValue)(sizeMetric);
|
|
2782
2842
|
const changeSizeMetric = (metric) => {
|
|
2783
2843
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2784
2844
|
onSizeMetricChange?.(metric);
|
|
@@ -2810,7 +2870,7 @@ function GenreTreeView({
|
|
|
2810
2870
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2811
2871
|
toggleWheelRotation,
|
|
2812
2872
|
onNodeHover: handleNodeHover
|
|
2813
|
-
} = (0,
|
|
2873
|
+
} = (0, import_genre_tree32.useGenreTreeViewState)({
|
|
2814
2874
|
scope,
|
|
2815
2875
|
getBackendBaseUrl,
|
|
2816
2876
|
viewMode: controlledViewMode,
|
|
@@ -2820,7 +2880,7 @@ function GenreTreeView({
|
|
|
2820
2880
|
artistUuid
|
|
2821
2881
|
});
|
|
2822
2882
|
const restoreRevision = viewportRestore?.revision;
|
|
2823
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
2883
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react20.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2824
2884
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2825
2885
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2826
2886
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2829,14 +2889,14 @@ function GenreTreeView({
|
|
|
2829
2889
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2830
2890
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2831
2891
|
}
|
|
2832
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2833
|
-
const { mutate: updateGenre } = (0,
|
|
2892
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree32.genreUpdateMutationKey }) > 0;
|
|
2893
|
+
const { mutate: updateGenre } = (0, import_genre_tree32.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2834
2894
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2835
2895
|
const isArtistFiltered = artistUuid !== null;
|
|
2836
2896
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2837
|
-
const artistResult = (0,
|
|
2897
|
+
const artistResult = (0, import_genre_tree32.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2838
2898
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2839
|
-
const filterArtist = (0,
|
|
2899
|
+
const filterArtist = (0, import_react20.useMemo)(
|
|
2840
2900
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2841
2901
|
[artistUuid, filterArtistName]
|
|
2842
2902
|
);
|
|
@@ -2851,14 +2911,14 @@ function GenreTreeView({
|
|
|
2851
2911
|
if (selectedGenreUuid !== null) clearSelection();
|
|
2852
2912
|
changeSong(uuid);
|
|
2853
2913
|
};
|
|
2854
|
-
const openSongRef = (0,
|
|
2914
|
+
const openSongRef = (0, import_react20.useRef)(openSong);
|
|
2855
2915
|
openSongRef.current = openSong;
|
|
2856
|
-
const resolvedRenderSongLink = (0,
|
|
2916
|
+
const resolvedRenderSongLink = (0, import_react20.useCallback)(
|
|
2857
2917
|
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2858
2918
|
[renderSongLink]
|
|
2859
2919
|
);
|
|
2860
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
2861
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
2920
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react20.useState)(null);
|
|
2921
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react20.useState)(null);
|
|
2862
2922
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2863
2923
|
const songBaseUrl = getBackendBaseUrl;
|
|
2864
2924
|
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
@@ -2905,8 +2965,8 @@ function GenreTreeView({
|
|
|
2905
2965
|
}
|
|
2906
2966
|
});
|
|
2907
2967
|
const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
|
|
2908
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0,
|
|
2909
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
2968
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react20.useState)(false);
|
|
2969
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react20.useState)(false);
|
|
2910
2970
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2911
2971
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2912
2972
|
const changeCollapsed = (next) => {
|
|
@@ -2917,30 +2977,30 @@ function GenreTreeView({
|
|
|
2917
2977
|
setUncontrolledExpanded(next);
|
|
2918
2978
|
onExpandedChange?.(next);
|
|
2919
2979
|
};
|
|
2920
|
-
(0,
|
|
2980
|
+
(0, import_react20.useEffect)(() => {
|
|
2921
2981
|
setUncontrolledCollapsed(false);
|
|
2922
2982
|
}, [selectedGenreUuid, artistUuid, songUuid]);
|
|
2923
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0,
|
|
2983
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react20.useState)(false);
|
|
2924
2984
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2925
2985
|
const isTreeReady = !isLoading && !isError && (!usesHandoff || isWheelRevealed);
|
|
2926
|
-
const [resolveStyle, setResolveStyle] = (0,
|
|
2927
|
-
const handleNodeStyleResolverChange = (0,
|
|
2986
|
+
const [resolveStyle, setResolveStyle] = (0, import_react20.useState)(null);
|
|
2987
|
+
const handleNodeStyleResolverChange = (0, import_react20.useCallback)(
|
|
2928
2988
|
(resolver) => setResolveStyle(() => resolver),
|
|
2929
2989
|
[]
|
|
2930
2990
|
);
|
|
2931
|
-
const panelRef = (0,
|
|
2932
|
-
const obscuredInset = (0,
|
|
2991
|
+
const panelRef = (0, import_react20.useRef)(null);
|
|
2992
|
+
const obscuredInset = (0, import_ui23.useObscuredInset)(panelRef);
|
|
2933
2993
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2934
2994
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2935
|
-
const treeNodes = (0,
|
|
2995
|
+
const treeNodes = (0, import_react20.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
|
|
2936
2996
|
const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
|
|
2937
2997
|
treeNodes,
|
|
2938
2998
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2939
2999
|
resolveStyle
|
|
2940
3000
|
);
|
|
2941
|
-
const treeStyles = (0,
|
|
2942
|
-
const validSelectedGenreUuid = (0,
|
|
2943
|
-
const [selectionHistory, setSelectionHistory] = (0,
|
|
3001
|
+
const treeStyles = (0, import_react20.useMemo)(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
|
|
3002
|
+
const validSelectedGenreUuid = (0, import_genre_tree31.toGenreUuid)(selectedGenreUuid);
|
|
3003
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react20.useState)({ current: validSelectedGenreUuid, previous: null });
|
|
2944
3004
|
if (selectionHistory.current !== validSelectedGenreUuid) {
|
|
2945
3005
|
setSelectionHistory({ current: validSelectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
2946
3006
|
}
|
|
@@ -2966,7 +3026,7 @@ function GenreTreeView({
|
|
|
2966
3026
|
const handleGenreSearchZoomIn = () => {
|
|
2967
3027
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2968
3028
|
};
|
|
2969
|
-
const zoomInRef = (0,
|
|
3029
|
+
const zoomInRef = (0, import_react20.useRef)(void 0);
|
|
2970
3030
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2971
3031
|
const genrePlaylist = selectedGenrePlaylist;
|
|
2972
3032
|
const criteria = genrePlaylist?.criteria;
|
|
@@ -3034,6 +3094,7 @@ function GenreTreeView({
|
|
|
3034
3094
|
activePanel = {
|
|
3035
3095
|
title: otherPanel.title,
|
|
3036
3096
|
content: otherPanel.content,
|
|
3097
|
+
cover: otherPanel.cover,
|
|
3037
3098
|
aside: otherPanel.extraContent,
|
|
3038
3099
|
expanded: otherPanel.expanded ?? false,
|
|
3039
3100
|
onExpandedChange: otherPanel.onExpandedChange,
|
|
@@ -3058,7 +3119,7 @@ function GenreTreeView({
|
|
|
3058
3119
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
3059
3120
|
} : void 0;
|
|
3060
3121
|
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3061
|
-
|
|
3122
|
+
import_ui23.IconButton,
|
|
3062
3123
|
{
|
|
3063
3124
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
3064
3125
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
@@ -3124,13 +3185,16 @@ function GenreTreeView({
|
|
|
3124
3185
|
onPrevious: handlePrevious,
|
|
3125
3186
|
onQueryChange: onSearchQueryChange,
|
|
3126
3187
|
onFocus: onSearchFocus,
|
|
3188
|
+
trailingAction: searchTrailingAction,
|
|
3189
|
+
trailingActionBreakpoint: "md",
|
|
3190
|
+
fullWidthBreakpoint: searchFullWidthBreakpoint,
|
|
3127
3191
|
extraGroups: extraSearchGroups
|
|
3128
3192
|
}
|
|
3129
3193
|
),
|
|
3130
3194
|
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3131
3195
|
/* @__PURE__ */ (0, import_jsx_runtime46.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: [
|
|
3132
3196
|
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3133
|
-
|
|
3197
|
+
import_ui23.SegmentedControl,
|
|
3134
3198
|
{
|
|
3135
3199
|
"aria-label": "Tree view mode",
|
|
3136
3200
|
value: viewMode,
|
|
@@ -3150,7 +3214,7 @@ function GenreTreeView({
|
|
|
3150
3214
|
role: "group",
|
|
3151
3215
|
"aria-label": "Tree display options",
|
|
3152
3216
|
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3153
|
-
|
|
3217
|
+
import_ui23.Button,
|
|
3154
3218
|
{
|
|
3155
3219
|
variant: allowWheelRotation ? "default" : "outline",
|
|
3156
3220
|
size: "sm",
|
|
@@ -3161,7 +3225,7 @@ function GenreTreeView({
|
|
|
3161
3225
|
}
|
|
3162
3226
|
),
|
|
3163
3227
|
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3164
|
-
|
|
3228
|
+
import_ui23.IconTextButton,
|
|
3165
3229
|
{
|
|
3166
3230
|
icon: import_lucide_react13.Plus,
|
|
3167
3231
|
text: "Add root",
|
|
@@ -3174,12 +3238,21 @@ function GenreTreeView({
|
|
|
3174
3238
|
"div",
|
|
3175
3239
|
{
|
|
3176
3240
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3177
|
-
className: [
|
|
3241
|
+
className: [
|
|
3242
|
+
"genre-tree-view relative flex flex-col h-full",
|
|
3243
|
+
placement === "regions" && REGIONS_CLASS,
|
|
3244
|
+
placement === "regions" && searchTrailingAction && "max-md:[&:has([data-detail-panel][data-collapsed=false][data-sheet=true]_[aria-label='Resize_panel'][aria-expanded=true])_.actions-container]:top-[max(var(--layout-touch-target),env(safe-area-inset-top))]",
|
|
3245
|
+
className2
|
|
3246
|
+
].filter(Boolean).join(" "),
|
|
3178
3247
|
children: [
|
|
3179
3248
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3180
3249
|
"div",
|
|
3181
3250
|
{
|
|
3182
|
-
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ?
|
|
3251
|
+
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? [
|
|
3252
|
+
"left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] md:left-[var(--layout-panel-inset)] md:top-[var(--layout-panel-inset)] max-md:flex-col max-md:items-stretch",
|
|
3253
|
+
searchFullWidthBreakpoint === "sm" ? "max-sm:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))]" : "max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))]",
|
|
3254
|
+
searchTrailingAction ? "max-md:top-[max(var(--layout-panel-inset),env(safe-area-inset-top))]" : "max-md:top-[var(--layout-band-top)]"
|
|
3255
|
+
].join(" ") : "left-5 top-5"}`,
|
|
3183
3256
|
children: actions
|
|
3184
3257
|
}
|
|
3185
3258
|
),
|
|
@@ -3309,11 +3382,16 @@ function GenreTreeView({
|
|
|
3309
3382
|
}
|
|
3310
3383
|
) }),
|
|
3311
3384
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3312
|
-
|
|
3385
|
+
GenreOverviewDetailPanel,
|
|
3313
3386
|
{
|
|
3387
|
+
genreUuid: !otherPanel && genreContent.name && selectedGenreUuid ? (0, import_genre_tree31.toGenreUuid)(selectedGenreUuid) : null,
|
|
3388
|
+
scope,
|
|
3389
|
+
getBackendBaseUrl,
|
|
3390
|
+
criteriaOverviewSchema,
|
|
3314
3391
|
ref: panelRef,
|
|
3315
3392
|
side: "left",
|
|
3316
3393
|
title: activePanel?.title ?? "",
|
|
3394
|
+
cover: activePanel?.cover,
|
|
3317
3395
|
titleInSearch: !isLoading && !isError,
|
|
3318
3396
|
headerAccent: activePanel?.headerAccent,
|
|
3319
3397
|
headerActions: expandButton,
|
|
@@ -3339,13 +3417,13 @@ function GenreTreeView({
|
|
|
3339
3417
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3340
3418
|
|
|
3341
3419
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3342
|
-
var
|
|
3420
|
+
var import_react21 = require("react");
|
|
3343
3421
|
var import_lucide_react15 = require("lucide-react");
|
|
3344
|
-
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3345
3422
|
var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3423
|
+
var import_genre_tree34 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3346
3424
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
3347
3425
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
3348
|
-
var useBrowserLayoutEffect = typeof window === "undefined" ?
|
|
3426
|
+
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react21.useEffect : import_react21.useLayoutEffect;
|
|
3349
3427
|
function TrackListSidebar({
|
|
3350
3428
|
className: className2 = "",
|
|
3351
3429
|
renderDuration,
|
|
@@ -3357,18 +3435,18 @@ function TrackListSidebar({
|
|
|
3357
3435
|
renderArtistLink,
|
|
3358
3436
|
renderSongLink
|
|
3359
3437
|
}) {
|
|
3360
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3438
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree33.useTrackList)();
|
|
3361
3439
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
3362
|
-
const trackListRef = (0,
|
|
3363
|
-
const previousOriginRef = (0,
|
|
3364
|
-
const handledAutoAdvanceRequestIdRef = (0,
|
|
3365
|
-
const shouldFollowPlaybackRef = (0,
|
|
3366
|
-
const programmaticScrollTopRef = (0,
|
|
3367
|
-
const userScrollIntentRef = (0,
|
|
3368
|
-
const userScrollIntentTimerRef = (0,
|
|
3369
|
-
const loadMoreSentinelRef = (0,
|
|
3440
|
+
const trackListRef = (0, import_react21.useRef)(null);
|
|
3441
|
+
const previousOriginRef = (0, import_react21.useRef)(trackList?.origin ?? null);
|
|
3442
|
+
const handledAutoAdvanceRequestIdRef = (0, import_react21.useRef)(null);
|
|
3443
|
+
const shouldFollowPlaybackRef = (0, import_react21.useRef)(true);
|
|
3444
|
+
const programmaticScrollTopRef = (0, import_react21.useRef)(null);
|
|
3445
|
+
const userScrollIntentRef = (0, import_react21.useRef)(false);
|
|
3446
|
+
const userScrollIntentTimerRef = (0, import_react21.useRef)(null);
|
|
3447
|
+
const loadMoreSentinelRef = (0, import_react21.useRef)(null);
|
|
3370
3448
|
const hasMore = !!trackList && trackList.nextPage !== null;
|
|
3371
|
-
(0,
|
|
3449
|
+
(0, import_react21.useEffect)(() => {
|
|
3372
3450
|
const sentinel = loadMoreSentinelRef.current;
|
|
3373
3451
|
if (!hasMore || !sentinel) return;
|
|
3374
3452
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -3388,11 +3466,11 @@ function TrackListSidebar({
|
|
|
3388
3466
|
programmaticScrollTopRef.current = null;
|
|
3389
3467
|
if (trackListRef.current) trackListRef.current.scrollTop = 0;
|
|
3390
3468
|
}, [trackList?.origin]);
|
|
3391
|
-
(0,
|
|
3469
|
+
(0, import_react21.useEffect)(() => {
|
|
3392
3470
|
const list = trackListRef.current;
|
|
3393
3471
|
if (!isCollapsed && list?.scrollTop === 0) shouldFollowPlaybackRef.current = true;
|
|
3394
3472
|
}, [isCollapsed]);
|
|
3395
|
-
(0,
|
|
3473
|
+
(0, import_react21.useEffect)(
|
|
3396
3474
|
() => () => {
|
|
3397
3475
|
if (userScrollIntentTimerRef.current !== null) clearTimeout(userScrollIntentTimerRef.current);
|
|
3398
3476
|
},
|
|
@@ -3460,7 +3538,7 @@ function TrackListSidebar({
|
|
|
3460
3538
|
/* @__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 }) })
|
|
3461
3539
|
] }),
|
|
3462
3540
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
|
|
3463
|
-
trackList.origin.type ===
|
|
3541
|
+
trackList.origin.type === import_genre_tree34.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree34.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3464
3542
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3465
3543
|
] })
|
|
3466
3544
|
] }),
|
|
@@ -3508,15 +3586,16 @@ function TrackListSidebar({
|
|
|
3508
3586
|
}
|
|
3509
3587
|
|
|
3510
3588
|
// src/genre-tree/PlayerPanel.tsx
|
|
3511
|
-
var
|
|
3589
|
+
var import_react22 = require("react");
|
|
3590
|
+
var import_lucide_react17 = require("lucide-react");
|
|
3512
3591
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3513
3592
|
var import_player14 = require("@behindthemusictree/app-kit/player");
|
|
3514
|
-
var
|
|
3515
|
-
var
|
|
3593
|
+
var import_genre_tree36 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3594
|
+
var import_ui24 = require("@behindthemusictree/ui");
|
|
3516
3595
|
|
|
3517
3596
|
// src/genre-tree/PlayerControls.tsx
|
|
3518
3597
|
var import_lucide_react16 = require("lucide-react");
|
|
3519
|
-
var
|
|
3598
|
+
var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3520
3599
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3521
3600
|
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
3522
3601
|
function PlayerControls() {
|
|
@@ -3530,7 +3609,7 @@ function PlayerControls() {
|
|
|
3530
3609
|
setVolume,
|
|
3531
3610
|
volume
|
|
3532
3611
|
} = (0, import_player13.usePlayer)();
|
|
3533
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3612
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree35.useTrackList)();
|
|
3534
3613
|
const currentTime = (0, import_player13.useCurrentTime)();
|
|
3535
3614
|
const isBusy = isLoading || playState === import_player13.PlayStates.LOADING;
|
|
3536
3615
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
@@ -3544,7 +3623,7 @@ function PlayerControls() {
|
|
|
3544
3623
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3545
3624
|
children: [
|
|
3546
3625
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3547
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Elapsed time", children: (0,
|
|
3626
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree35.formatTime)(safeCurrentTime) }),
|
|
3548
3627
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3549
3628
|
"input",
|
|
3550
3629
|
{
|
|
@@ -3559,7 +3638,7 @@ function PlayerControls() {
|
|
|
3559
3638
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3560
3639
|
}
|
|
3561
3640
|
),
|
|
3562
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Duration", children: (0,
|
|
3641
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree35.formatTime)(safeDuration) })
|
|
3563
3642
|
] }),
|
|
3564
3643
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3565
3644
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
@@ -3637,14 +3716,14 @@ function PlayerPanel({
|
|
|
3637
3716
|
renderArtistLink,
|
|
3638
3717
|
renderSongLink
|
|
3639
3718
|
}) {
|
|
3640
|
-
const { playerTrackObject, retryPlayback } = (0, import_player14.usePlayer)();
|
|
3641
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3642
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0,
|
|
3719
|
+
const { playerTrackObject, retryPlayback, stop } = (0, import_player14.usePlayer)();
|
|
3720
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree36.useTrackListSidebarVisibility)();
|
|
3721
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react22.useState)("");
|
|
3643
3722
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3644
|
-
(0,
|
|
3723
|
+
(0, import_react22.useEffect)(() => {
|
|
3645
3724
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3646
3725
|
}, [loadedTrackTitle]);
|
|
3647
|
-
const [uncontrolledExtended, setUncontrolledExtended] = (0,
|
|
3726
|
+
const [uncontrolledExtended, setUncontrolledExtended] = (0, import_react22.useState)(false);
|
|
3648
3727
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3649
3728
|
const setIsExtendRequested = (next) => {
|
|
3650
3729
|
setUncontrolledExtended(next);
|
|
@@ -3679,7 +3758,7 @@ function PlayerPanel({
|
|
|
3679
3758
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3680
3759
|
children: [
|
|
3681
3760
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3682
|
-
|
|
3761
|
+
import_ui24.PanelEdgeTab,
|
|
3683
3762
|
{
|
|
3684
3763
|
side: "right",
|
|
3685
3764
|
edge: tabEdge,
|
|
@@ -3697,7 +3776,10 @@ function PlayerPanel({
|
|
|
3697
3776
|
),
|
|
3698
3777
|
children: [
|
|
3699
3778
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3700
|
-
/* @__PURE__ */ (0, import_jsx_runtime49.
|
|
3779
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: [
|
|
3780
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }),
|
|
3781
|
+
playerTrackObject && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui24.IconButton, { "aria-label": "Close player", onClick: stop, children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react17.X, { "aria-hidden": "true", className: "h-4 w-4" }) })
|
|
3782
|
+
] }),
|
|
3701
3783
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3702
3784
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3703
3785
|
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
@@ -3735,7 +3817,7 @@ function PlayerPanel({
|
|
|
3735
3817
|
// src/genre-tree/Rating.tsx
|
|
3736
3818
|
var import_uuid = require("uuid");
|
|
3737
3819
|
var import_ai = require("react-icons/ai");
|
|
3738
|
-
var
|
|
3820
|
+
var import_genre_tree37 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3739
3821
|
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
3740
3822
|
function Rating({ rating, handleChange }) {
|
|
3741
3823
|
const instanceId = (0, import_uuid.v4)();
|
|
@@ -3747,7 +3829,7 @@ function Rating({ rating, handleChange }) {
|
|
|
3747
3829
|
className: "w-6 h-6 opacity-0",
|
|
3748
3830
|
type: "radio",
|
|
3749
3831
|
name: "rating",
|
|
3750
|
-
value: i === 0 ?
|
|
3832
|
+
value: i === 0 ? import_genre_tree37.FORM_RATING_NULL_VALUE : i,
|
|
3751
3833
|
checked: rating === (i === 0 ? null : i),
|
|
3752
3834
|
onChange: handleChange
|
|
3753
3835
|
}
|
|
@@ -3756,7 +3838,7 @@ function Rating({ rating, handleChange }) {
|
|
|
3756
3838
|
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3757
3839
|
import_ai.AiOutlineStar,
|
|
3758
3840
|
{
|
|
3759
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3841
|
+
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree37.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3760
3842
|
}
|
|
3761
3843
|
),
|
|
3762
3844
|
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "flex-grow w-8" }),
|
|
@@ -3771,8 +3853,8 @@ function Rating({ rating, handleChange }) {
|
|
|
3771
3853
|
}
|
|
3772
3854
|
|
|
3773
3855
|
// src/genre-tree/EntityDetailPanel.tsx
|
|
3774
|
-
var
|
|
3775
|
-
var
|
|
3856
|
+
var import_react23 = require("react");
|
|
3857
|
+
var import_ui25 = require("@behindthemusictree/ui");
|
|
3776
3858
|
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
3777
3859
|
function EntityDetailPanel({
|
|
3778
3860
|
scope,
|
|
@@ -3790,8 +3872,8 @@ function EntityDetailPanel({
|
|
|
3790
3872
|
edgeTab,
|
|
3791
3873
|
...navigation
|
|
3792
3874
|
}) {
|
|
3793
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
3794
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
3875
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react23.useState)(null);
|
|
3876
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react23.useState)(null);
|
|
3795
3877
|
const panel = useEntityPanel({
|
|
3796
3878
|
scope,
|
|
3797
3879
|
getBackendBaseUrl,
|
|
@@ -3816,10 +3898,11 @@ function EntityDetailPanel({
|
|
|
3816
3898
|
});
|
|
3817
3899
|
if (!panel) return null;
|
|
3818
3900
|
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
3819
|
-
|
|
3901
|
+
import_ui25.DetailPanel,
|
|
3820
3902
|
{
|
|
3821
3903
|
side: "left",
|
|
3822
3904
|
title: panel.title,
|
|
3905
|
+
cover: panel.cover,
|
|
3823
3906
|
titleInSearch: true,
|
|
3824
3907
|
onClose: panel.onClose,
|
|
3825
3908
|
collapsed,
|