@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
package/dist/index.cjs
CHANGED
|
@@ -589,14 +589,14 @@ function AuthCallbackHandler({ renderStatus, ...options }) {
|
|
|
589
589
|
}
|
|
590
590
|
|
|
591
591
|
// src/genre-tree/GenreTreeView.tsx
|
|
592
|
-
var
|
|
592
|
+
var import_react20 = require("react");
|
|
593
593
|
var import_react_query3 = require("@tanstack/react-query");
|
|
594
594
|
var import_lucide_react17 = require("lucide-react");
|
|
595
595
|
var import_lucide_react18 = require("lucide-react");
|
|
596
|
-
var
|
|
596
|
+
var import_ui26 = require("@behindthemusictree/ui");
|
|
597
597
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
598
|
-
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
599
598
|
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
599
|
+
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
600
600
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
601
601
|
|
|
602
602
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -1049,6 +1049,8 @@ function SearchBox({
|
|
|
1049
1049
|
onClear,
|
|
1050
1050
|
clearLabel,
|
|
1051
1051
|
leading,
|
|
1052
|
+
trailingAction,
|
|
1053
|
+
trailingActionBreakpoint,
|
|
1052
1054
|
className: className2,
|
|
1053
1055
|
onFocus
|
|
1054
1056
|
}) {
|
|
@@ -1081,6 +1083,8 @@ function SearchBox({
|
|
|
1081
1083
|
onClear,
|
|
1082
1084
|
clearLabel,
|
|
1083
1085
|
leading,
|
|
1086
|
+
trailingAction,
|
|
1087
|
+
trailingActionBreakpoint,
|
|
1084
1088
|
inputClassName: "focus-visible:ring-0"
|
|
1085
1089
|
}
|
|
1086
1090
|
) });
|
|
@@ -1181,7 +1185,10 @@ function GenreSearch({
|
|
|
1181
1185
|
placeholder,
|
|
1182
1186
|
onQueryChange,
|
|
1183
1187
|
extraGroups = [],
|
|
1184
|
-
onFocus
|
|
1188
|
+
onFocus,
|
|
1189
|
+
trailingAction,
|
|
1190
|
+
trailingActionBreakpoint,
|
|
1191
|
+
fullWidthBreakpoint = "md"
|
|
1185
1192
|
}) {
|
|
1186
1193
|
const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree9.useGenreSearch)({
|
|
1187
1194
|
genrePlaylists,
|
|
@@ -1209,7 +1216,7 @@ function GenreSearch({
|
|
|
1209
1216
|
(group) => active ? group : { ...group, items: [], status: "", loading: false }
|
|
1210
1217
|
)
|
|
1211
1218
|
];
|
|
1212
|
-
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className:
|
|
1219
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)(
|
|
1213
1220
|
SearchBox,
|
|
1214
1221
|
{
|
|
1215
1222
|
label,
|
|
@@ -1230,7 +1237,9 @@ function GenreSearch({
|
|
|
1230
1237
|
onQueryChange?.("");
|
|
1231
1238
|
} : void 0,
|
|
1232
1239
|
leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui7.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
|
|
1233
|
-
onFocus
|
|
1240
|
+
onFocus,
|
|
1241
|
+
trailingAction,
|
|
1242
|
+
trailingActionBreakpoint
|
|
1234
1243
|
}
|
|
1235
1244
|
) });
|
|
1236
1245
|
}
|
|
@@ -1274,38 +1283,86 @@ function GenreTreeSearch({
|
|
|
1274
1283
|
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
|
|
1275
1284
|
}
|
|
1276
1285
|
|
|
1286
|
+
// src/genre-tree/GenreOverviewDetailPanel.tsx
|
|
1287
|
+
var import_react10 = require("react");
|
|
1288
|
+
var import_ui8 = require("@behindthemusictree/ui");
|
|
1289
|
+
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1290
|
+
|
|
1291
|
+
// src/genre-tree/CommonsPhoto.tsx
|
|
1292
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1293
|
+
function ExternalLink({ href, children }) {
|
|
1294
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1295
|
+
}
|
|
1296
|
+
function CommonsPhotoCredit({ image, creditLabel = "Photo" }) {
|
|
1297
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
1298
|
+
creditLabel,
|
|
1299
|
+
": ",
|
|
1300
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1301
|
+
",",
|
|
1302
|
+
" ",
|
|
1303
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1304
|
+
] });
|
|
1305
|
+
}
|
|
1306
|
+
function CommonsCover({ image, alt }) {
|
|
1307
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-cover bg-[var(--color-surface-muted)]" });
|
|
1308
|
+
}
|
|
1309
|
+
function CommonsPhoto({
|
|
1310
|
+
image,
|
|
1311
|
+
alt,
|
|
1312
|
+
creditLabel = "Photo"
|
|
1313
|
+
}) {
|
|
1314
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
|
|
1315
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1316
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
1317
|
+
creditLabel,
|
|
1318
|
+
": ",
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1320
|
+
",",
|
|
1321
|
+
" ",
|
|
1322
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1323
|
+
] })
|
|
1324
|
+
] });
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
// src/genre-tree/GenreOverviewDetailPanel.tsx
|
|
1328
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1329
|
+
var GenreOverviewDetailPanel = (0, import_react10.forwardRef)(function GenreOverviewDetailPanel2({ genreUuid, scope, getBackendBaseUrl, criteriaOverviewSchema, cover, ...props }, ref) {
|
|
1330
|
+
const { data: overview } = (0, import_genre_tree10.useFetchGenreOverview)(genreUuid, scope, getBackendBaseUrl, criteriaOverviewSchema);
|
|
1331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui8.DetailPanel, { ...props, ref, cover: genreUuid && overview?.image ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(CommonsCover, { image: overview.image, alt: overview.name }) : cover });
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1277
1334
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
1278
|
-
var
|
|
1335
|
+
var import_react16 = require("react");
|
|
1279
1336
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
1280
|
-
var
|
|
1337
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
1281
1338
|
var import_lucide_react13 = require("lucide-react");
|
|
1282
|
-
var
|
|
1339
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1283
1340
|
|
|
1284
1341
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1285
|
-
var
|
|
1342
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1286
1343
|
|
|
1287
1344
|
// src/genre-tree/GenreActionBand.tsx
|
|
1288
1345
|
var import_lucide_react11 = require("lucide-react");
|
|
1289
|
-
var
|
|
1346
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
1290
1347
|
|
|
1291
1348
|
// src/genre-tree/ShareActionButton.tsx
|
|
1292
1349
|
var import_lucide_react10 = require("lucide-react");
|
|
1293
|
-
var
|
|
1350
|
+
var import_ui9 = require("@behindthemusictree/ui");
|
|
1294
1351
|
var import_popup3 = require("@behindthemusictree/app-kit/popup");
|
|
1295
1352
|
|
|
1296
1353
|
// src/genre-tree/SharePopup.tsx
|
|
1297
|
-
var
|
|
1354
|
+
var import_react11 = require("react");
|
|
1298
1355
|
var import_lucide_react9 = require("lucide-react");
|
|
1299
1356
|
var import_fa3 = require("react-icons/fa");
|
|
1300
1357
|
var import_si = require("react-icons/si");
|
|
1301
1358
|
|
|
1302
1359
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
1303
|
-
var
|
|
1360
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1304
1361
|
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";
|
|
1305
1362
|
function Contents({ icon, label }) {
|
|
1306
|
-
return /* @__PURE__ */ (0,
|
|
1307
|
-
/* @__PURE__ */ (0,
|
|
1308
|
-
/* @__PURE__ */ (0,
|
|
1363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
|
|
1364
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.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 }),
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "max-w-full truncate", children: label })
|
|
1309
1366
|
] });
|
|
1310
1367
|
}
|
|
1311
1368
|
function ShareDestinationButton({
|
|
@@ -1317,29 +1374,29 @@ function ShareDestinationButton({
|
|
|
1317
1374
|
ariaLabel
|
|
1318
1375
|
}) {
|
|
1319
1376
|
if (href) {
|
|
1320
|
-
return /* @__PURE__ */ (0,
|
|
1377
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Contents, { icon, label }) });
|
|
1321
1378
|
}
|
|
1322
|
-
return /* @__PURE__ */ (0,
|
|
1379
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Contents, { icon, label }) });
|
|
1323
1380
|
}
|
|
1324
1381
|
|
|
1325
1382
|
// src/genre-tree/SharePreviewCard.tsx
|
|
1326
1383
|
var import_lucide_react8 = require("lucide-react");
|
|
1327
|
-
var
|
|
1384
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1328
1385
|
function SharePreviewCard({ target, url }) {
|
|
1329
1386
|
const Icon = { genre: import_lucide_react8.Music2, artist: import_lucide_react8.UserRound, song: import_lucide_react8.Music }[target.kind];
|
|
1330
1387
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
1331
|
-
return /* @__PURE__ */ (0,
|
|
1332
|
-
/* @__PURE__ */ (0,
|
|
1333
|
-
/* @__PURE__ */ (0,
|
|
1334
|
-
/* @__PURE__ */ (0,
|
|
1335
|
-
/* @__PURE__ */ (0,
|
|
1336
|
-
/* @__PURE__ */ (0,
|
|
1388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.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_runtime26.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
1391
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
1392
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
1393
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
|
|
1337
1394
|
] })
|
|
1338
1395
|
] }) });
|
|
1339
1396
|
}
|
|
1340
1397
|
|
|
1341
1398
|
// src/genre-tree/SharePopup.tsx
|
|
1342
|
-
var
|
|
1399
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1343
1400
|
function resolveAbsoluteUrl(url) {
|
|
1344
1401
|
try {
|
|
1345
1402
|
const parsed = new URL(url, window.location.href);
|
|
@@ -1372,20 +1429,20 @@ async function copyText(text) {
|
|
|
1372
1429
|
}
|
|
1373
1430
|
}
|
|
1374
1431
|
function SharePopup({ target, url, onClose }) {
|
|
1375
|
-
const [absoluteUrl, setAbsoluteUrl] = (0,
|
|
1376
|
-
const [copyStatus, setCopyStatus] = (0,
|
|
1377
|
-
const [canUseNativeShare, setCanUseNativeShare] = (0,
|
|
1378
|
-
const [shareError, setShareError] = (0,
|
|
1379
|
-
const [toastId, setToastId] = (0,
|
|
1380
|
-
(0,
|
|
1432
|
+
const [absoluteUrl, setAbsoluteUrl] = (0, import_react11.useState)(url);
|
|
1433
|
+
const [copyStatus, setCopyStatus] = (0, import_react11.useState)("idle");
|
|
1434
|
+
const [canUseNativeShare, setCanUseNativeShare] = (0, import_react11.useState)(false);
|
|
1435
|
+
const [shareError, setShareError] = (0, import_react11.useState)(null);
|
|
1436
|
+
const [toastId, setToastId] = (0, import_react11.useState)(0);
|
|
1437
|
+
(0, import_react11.useEffect)(() => {
|
|
1381
1438
|
setAbsoluteUrl(resolveAbsoluteUrl(url) ?? "");
|
|
1382
1439
|
setCopyStatus("idle");
|
|
1383
1440
|
setShareError(null);
|
|
1384
1441
|
}, [url]);
|
|
1385
|
-
(0,
|
|
1442
|
+
(0, import_react11.useEffect)(() => {
|
|
1386
1443
|
setCanUseNativeShare(typeof navigator !== "undefined" && typeof navigator.share === "function");
|
|
1387
1444
|
}, []);
|
|
1388
|
-
const destinationUrls = (0,
|
|
1445
|
+
const destinationUrls = (0, import_react11.useMemo)(() => {
|
|
1389
1446
|
if (!absoluteUrl) return null;
|
|
1390
1447
|
const message = `${target.name} \u2014 ${absoluteUrl}`;
|
|
1391
1448
|
return {
|
|
@@ -1403,7 +1460,7 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1403
1460
|
setCopyStatus(copied ? "idle" : "failed");
|
|
1404
1461
|
if (copied) setToastId((id) => id + 1);
|
|
1405
1462
|
};
|
|
1406
|
-
const dismissToast = (0,
|
|
1463
|
+
const dismissToast = (0, import_react11.useCallback)(() => setToastId(0), []);
|
|
1407
1464
|
const handleNativeShare = async () => {
|
|
1408
1465
|
if (!absoluteUrl || !navigator.share) return;
|
|
1409
1466
|
setShareError(null);
|
|
@@ -1414,8 +1471,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1414
1471
|
setShareError("Could not open the share options.");
|
|
1415
1472
|
}
|
|
1416
1473
|
};
|
|
1417
|
-
return /* @__PURE__ */ (0,
|
|
1418
|
-
/* @__PURE__ */ (0,
|
|
1474
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_jsx_runtime27.Fragment, { children: [
|
|
1475
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1419
1476
|
BasePopup_default,
|
|
1420
1477
|
{
|
|
1421
1478
|
title: "Share",
|
|
@@ -1430,70 +1487,70 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1430
1487
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
1431
1488
|
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)]",
|
|
1432
1489
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
1433
|
-
children: /* @__PURE__ */ (0,
|
|
1434
|
-
/* @__PURE__ */ (0,
|
|
1435
|
-
/* @__PURE__ */ (0,
|
|
1436
|
-
/* @__PURE__ */ (0,
|
|
1490
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1491
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1437
1494
|
ShareDestinationButton,
|
|
1438
1495
|
{
|
|
1439
1496
|
label: "Copy Link",
|
|
1440
|
-
icon: /* @__PURE__ */ (0,
|
|
1497
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1441
1498
|
onClick: () => void handleCopy(),
|
|
1442
1499
|
ariaLabel: "Copy link",
|
|
1443
1500
|
disabled: !absoluteUrl
|
|
1444
1501
|
}
|
|
1445
1502
|
),
|
|
1446
|
-
/* @__PURE__ */ (0,
|
|
1503
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1447
1504
|
ShareDestinationButton,
|
|
1448
1505
|
{
|
|
1449
1506
|
label: "WhatsApp",
|
|
1450
|
-
icon: /* @__PURE__ */ (0,
|
|
1507
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
1451
1508
|
href: destinationUrls?.whatsapp,
|
|
1452
1509
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
1453
1510
|
disabled: !absoluteUrl
|
|
1454
1511
|
}
|
|
1455
1512
|
),
|
|
1456
|
-
/* @__PURE__ */ (0,
|
|
1513
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1457
1514
|
ShareDestinationButton,
|
|
1458
1515
|
{
|
|
1459
1516
|
label: "Gmail",
|
|
1460
|
-
icon: /* @__PURE__ */ (0,
|
|
1517
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
1461
1518
|
href: destinationUrls?.gmail,
|
|
1462
1519
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
1463
1520
|
disabled: !absoluteUrl
|
|
1464
1521
|
}
|
|
1465
1522
|
),
|
|
1466
|
-
canUseNativeShare && /* @__PURE__ */ (0,
|
|
1523
|
+
canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1467
1524
|
ShareDestinationButton,
|
|
1468
1525
|
{
|
|
1469
1526
|
label: "More",
|
|
1470
|
-
icon: /* @__PURE__ */ (0,
|
|
1527
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1471
1528
|
onClick: () => void handleNativeShare(),
|
|
1472
1529
|
ariaLabel: "More sharing options",
|
|
1473
1530
|
disabled: !absoluteUrl
|
|
1474
1531
|
}
|
|
1475
1532
|
)
|
|
1476
1533
|
] }),
|
|
1477
|
-
copyStatus === "failed" && /* @__PURE__ */ (0,
|
|
1478
|
-
shareError && /* @__PURE__ */ (0,
|
|
1534
|
+
copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
1535
|
+
shareError && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
|
|
1479
1536
|
] })
|
|
1480
1537
|
}
|
|
1481
1538
|
),
|
|
1482
|
-
toastId > 0 && /* @__PURE__ */ (0,
|
|
1539
|
+
toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
1483
1540
|
] });
|
|
1484
1541
|
}
|
|
1485
1542
|
|
|
1486
1543
|
// src/genre-tree/ShareActionButton.tsx
|
|
1487
|
-
var
|
|
1544
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1488
1545
|
function ShareActionButton({ target, url }) {
|
|
1489
1546
|
const { showPopup, hidePopup } = (0, import_popup3.usePopup)();
|
|
1490
1547
|
const openSharePopup = () => {
|
|
1491
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1548
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SharePopup, { target, url, onClose: hidePopup }));
|
|
1492
1549
|
};
|
|
1493
|
-
return /* @__PURE__ */ (0,
|
|
1494
|
-
|
|
1550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1551
|
+
import_ui9.ActionButton,
|
|
1495
1552
|
{
|
|
1496
|
-
icon: /* @__PURE__ */ (0,
|
|
1553
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1497
1554
|
label: "Share",
|
|
1498
1555
|
"aria-label": `Share ${target.name}`,
|
|
1499
1556
|
title: `Share ${target.name}`,
|
|
@@ -1503,7 +1560,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1503
1560
|
}
|
|
1504
1561
|
|
|
1505
1562
|
// src/genre-tree/GenreActionBand.tsx
|
|
1506
|
-
var
|
|
1563
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1507
1564
|
function GenreActionBand({
|
|
1508
1565
|
node,
|
|
1509
1566
|
playActions,
|
|
@@ -1524,24 +1581,24 @@ function GenreActionBand({
|
|
|
1524
1581
|
items.push({
|
|
1525
1582
|
key: "zoom-in",
|
|
1526
1583
|
label: "Focus",
|
|
1527
|
-
icon: /* @__PURE__ */ (0,
|
|
1584
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1528
1585
|
onClick: onZoomIn
|
|
1529
1586
|
});
|
|
1530
1587
|
}
|
|
1531
1588
|
if (onAddChild) {
|
|
1532
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0,
|
|
1589
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react11.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
1533
1590
|
}
|
|
1534
1591
|
if (onRename) {
|
|
1535
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0,
|
|
1592
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react11.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
1536
1593
|
}
|
|
1537
1594
|
if (onChangeParent) {
|
|
1538
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0,
|
|
1595
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react11.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
1539
1596
|
}
|
|
1540
1597
|
if (onToggleSide) {
|
|
1541
1598
|
adminItems.push({
|
|
1542
1599
|
key: "toggle-side",
|
|
1543
1600
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
1544
|
-
icon: /* @__PURE__ */ (0,
|
|
1601
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react11.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1545
1602
|
onClick: onToggleSide,
|
|
1546
1603
|
disabled: !!toggleSideDisabledReason,
|
|
1547
1604
|
title: toggleSideDisabledReason
|
|
@@ -1556,17 +1613,17 @@ function GenreActionBand({
|
|
|
1556
1613
|
disabled: action.enabled ? !action.enabled(node) : false
|
|
1557
1614
|
});
|
|
1558
1615
|
}
|
|
1559
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0,
|
|
1616
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.ActionButton, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
|
|
1560
1617
|
if (!playActions && items.length === 0 && adminItems.length === 0 && !(shareTarget && shareUrl)) return null;
|
|
1561
|
-
return /* @__PURE__ */ (0,
|
|
1562
|
-
|
|
1618
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1619
|
+
import_ui10.ActionBand,
|
|
1563
1620
|
{
|
|
1564
1621
|
className: "genre-action-band",
|
|
1565
1622
|
section: { title: "Admin", label: "Admin actions", children: renderItems(adminItems) },
|
|
1566
1623
|
children: [
|
|
1567
1624
|
playActions,
|
|
1568
1625
|
renderItems(items),
|
|
1569
|
-
shareTarget && shareUrl && /* @__PURE__ */ (0,
|
|
1626
|
+
shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1570
1627
|
]
|
|
1571
1628
|
}
|
|
1572
1629
|
) });
|
|
@@ -1574,12 +1631,12 @@ function GenreActionBand({
|
|
|
1574
1631
|
|
|
1575
1632
|
// src/genre-tree/GenrePlayActions.tsx
|
|
1576
1633
|
var import_lucide_react12 = require("lucide-react");
|
|
1577
|
-
var
|
|
1578
|
-
var
|
|
1634
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
1635
|
+
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1579
1636
|
var import_player4 = require("@behindthemusictree/app-kit/player");
|
|
1580
1637
|
|
|
1581
1638
|
// src/genre-tree/RegionGlobeIcon.tsx
|
|
1582
|
-
var
|
|
1639
|
+
var import_react12 = require("react");
|
|
1583
1640
|
|
|
1584
1641
|
// src/genre-tree/regionGlobePaths.ts
|
|
1585
1642
|
var REGION_GLOBE_PATHS = {
|
|
@@ -1590,11 +1647,11 @@ var REGION_GLOBE_PATHS = {
|
|
|
1590
1647
|
var REGION_GLOBES = ["africa", "americas", "asia"];
|
|
1591
1648
|
|
|
1592
1649
|
// src/genre-tree/RegionGlobeIcon.tsx
|
|
1593
|
-
var
|
|
1650
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1594
1651
|
var ROTATION_INTERVAL_MS = 3e3;
|
|
1595
1652
|
function RegionGlobeIcon({ className: className2 }) {
|
|
1596
|
-
const [index, setIndex] = (0,
|
|
1597
|
-
(0,
|
|
1653
|
+
const [index, setIndex] = (0, import_react12.useState)(0);
|
|
1654
|
+
(0, import_react12.useEffect)(() => {
|
|
1598
1655
|
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
1599
1656
|
let timer;
|
|
1600
1657
|
const sync = () => {
|
|
@@ -1608,51 +1665,51 @@ function RegionGlobeIcon({ className: className2 }) {
|
|
|
1608
1665
|
clearInterval(timer);
|
|
1609
1666
|
};
|
|
1610
1667
|
}, []);
|
|
1611
|
-
return /* @__PURE__ */ (0,
|
|
1668
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
|
|
1612
1669
|
}
|
|
1613
1670
|
|
|
1614
1671
|
// src/genre-tree/treeMarkPath.ts
|
|
1615
1672
|
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";
|
|
1616
1673
|
|
|
1617
1674
|
// src/genre-tree/TreeMarkIcon.tsx
|
|
1618
|
-
var
|
|
1675
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1619
1676
|
function TreeMarkIcon({ className: className2 }) {
|
|
1620
|
-
return /* @__PURE__ */ (0,
|
|
1677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
|
|
1621
1678
|
}
|
|
1622
1679
|
|
|
1623
1680
|
// src/genre-tree/GenrePlayActions.tsx
|
|
1624
|
-
var
|
|
1681
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1625
1682
|
function PlayIcon({ kind }) {
|
|
1626
|
-
return /* @__PURE__ */ (0,
|
|
1627
|
-
kind === "tree" && /* @__PURE__ */ (0,
|
|
1628
|
-
kind === "map" && /* @__PURE__ */ (0,
|
|
1629
|
-
kind === "tree-map" && /* @__PURE__ */ (0,
|
|
1630
|
-
/* @__PURE__ */ (0,
|
|
1631
|
-
/* @__PURE__ */ (0,
|
|
1683
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
|
|
1684
|
+
kind === "tree" && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
|
|
1685
|
+
kind === "map" && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
|
|
1686
|
+
kind === "tree-map" && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
1687
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
|
|
1688
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
|
|
1632
1689
|
] }),
|
|
1633
|
-
/* @__PURE__ */ (0,
|
|
1690
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react12.Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
|
|
1634
1691
|
] });
|
|
1635
1692
|
}
|
|
1636
1693
|
function playOptions(playlist) {
|
|
1637
1694
|
if (!("genreOnlyTracksCount" in playlist)) return [{ key: "all", label: "Play", icon: "play", count: playlist.tracksCount }];
|
|
1638
|
-
const direct = { key: "direct", label: "Genre", icon: "play", selection: { coverage: "genreOnly" }, count: (0,
|
|
1695
|
+
const direct = { key: "direct", label: "Genre", icon: "play", selection: { coverage: "genreOnly" }, count: (0, import_genre_tree11.genreMusicCount)(playlist, { coverage: "genreOnly" }) };
|
|
1639
1696
|
const scopes = playlist.genreAndSubgenresTracksCount;
|
|
1640
1697
|
const inclusive = (label, icon, subgenres) => {
|
|
1641
1698
|
const selection = { coverage: "genreAndSubgenres", subgenres };
|
|
1642
|
-
return { key: subgenres, label, icon, selection, count: (0,
|
|
1699
|
+
return { key: subgenres, label, icon, selection, count: (0, import_genre_tree11.genreMusicCount)(playlist, selection) };
|
|
1643
1700
|
};
|
|
1644
1701
|
return "canonical" in scopes ? [direct, inclusive("Canonical", "tree", "canonical"), inclusive("Regional", "map", "regional"), inclusive("All", "tree-map", "all")] : [direct, inclusive("All", "map", "all")];
|
|
1645
1702
|
}
|
|
1646
1703
|
function GenrePlayActions({ genrePlaylist, playback }) {
|
|
1647
1704
|
const { isPlaying, setIsPlaying } = (0, import_player4.usePlayer)();
|
|
1648
1705
|
const options = playOptions(genrePlaylist);
|
|
1649
|
-
return /* @__PURE__ */ (0,
|
|
1650
|
-
const isCurrent = playback.playing?.uuid === genrePlaylist.uuid && (option.selection === void 0 || playback.playing.selectionKey === "" || playback.playing.selectionKey === (0,
|
|
1706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_jsx_runtime32.Fragment, { children: options.map((option) => {
|
|
1707
|
+
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());
|
|
1651
1708
|
const playing = isCurrent && isPlaying;
|
|
1652
|
-
return /* @__PURE__ */ (0,
|
|
1653
|
-
|
|
1709
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1710
|
+
import_ui11.ActionButton,
|
|
1654
1711
|
{
|
|
1655
|
-
icon: playing ? /* @__PURE__ */ (0,
|
|
1712
|
+
icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(PlayIcon, { kind: option.icon }),
|
|
1656
1713
|
label: playing ? "Pause" : option.label,
|
|
1657
1714
|
caption: String(option.count),
|
|
1658
1715
|
disabled: option.count === 0,
|
|
@@ -1671,24 +1728,24 @@ function GenrePlayActions({ genrePlaylist, playback }) {
|
|
|
1671
1728
|
}
|
|
1672
1729
|
|
|
1673
1730
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1674
|
-
var
|
|
1675
|
-
var
|
|
1731
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
1732
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1676
1733
|
|
|
1677
1734
|
// src/genre-tree/ClampedText.tsx
|
|
1678
|
-
var
|
|
1679
|
-
var
|
|
1735
|
+
var import_react13 = require("react");
|
|
1736
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1680
1737
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1681
|
-
const ref = (0,
|
|
1682
|
-
const [expanded, setExpanded] = (0,
|
|
1683
|
-
const [overflows, setOverflows] = (0,
|
|
1684
|
-
(0,
|
|
1738
|
+
const ref = (0, import_react13.useRef)(null);
|
|
1739
|
+
const [expanded, setExpanded] = (0, import_react13.useState)(false);
|
|
1740
|
+
const [overflows, setOverflows] = (0, import_react13.useState)(false);
|
|
1741
|
+
(0, import_react13.useLayoutEffect)(() => {
|
|
1685
1742
|
const element = ref.current;
|
|
1686
1743
|
if (!element || expanded) return;
|
|
1687
1744
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1688
1745
|
}, [children, expanded]);
|
|
1689
|
-
return /* @__PURE__ */ (0,
|
|
1690
|
-
/* @__PURE__ */ (0,
|
|
1691
|
-
(overflows || expanded) && /* @__PURE__ */ (0,
|
|
1746
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_jsx_runtime33.Fragment, { children: [
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1748
|
+
(overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1692
1749
|
"button",
|
|
1693
1750
|
{
|
|
1694
1751
|
type: "button",
|
|
@@ -1701,35 +1758,12 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1701
1758
|
] });
|
|
1702
1759
|
}
|
|
1703
1760
|
|
|
1704
|
-
// src/genre-tree/CommonsPhoto.tsx
|
|
1705
|
-
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1706
|
-
function ExternalLink({ href, children }) {
|
|
1707
|
-
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1708
|
-
}
|
|
1709
|
-
function CommonsPhoto({
|
|
1710
|
-
image,
|
|
1711
|
-
alt,
|
|
1712
|
-
creditLabel = "Photo"
|
|
1713
|
-
}) {
|
|
1714
|
-
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
|
|
1715
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1716
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
1717
|
-
creditLabel,
|
|
1718
|
-
": ",
|
|
1719
|
-
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1720
|
-
",",
|
|
1721
|
-
" ",
|
|
1722
|
-
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1723
|
-
] })
|
|
1724
|
-
] });
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
1761
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1728
|
-
var
|
|
1762
|
+
var import_react14 = require("react");
|
|
1729
1763
|
var import_react_query = require("@tanstack/react-query");
|
|
1730
1764
|
var import_zod = require("zod");
|
|
1731
1765
|
var import_fa4 = require("react-icons/fa");
|
|
1732
|
-
var
|
|
1766
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
1733
1767
|
var import_auth2 = require("@behindthemusictree/app-kit/auth");
|
|
1734
1768
|
var import_transport4 = require("@behindthemusictree/app-kit/transport");
|
|
1735
1769
|
var import_transport5 = require("@behindthemusictree/app-kit/transport");
|
|
@@ -1738,23 +1772,23 @@ var import_player5 = require("@behindthemusictree/app-kit/player");
|
|
|
1738
1772
|
var import_player6 = require("@behindthemusictree/app-kit/player");
|
|
1739
1773
|
var import_popup4 = require("@behindthemusictree/app-kit/popup");
|
|
1740
1774
|
var import_transport7 = require("@behindthemusictree/app-kit/transport");
|
|
1741
|
-
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1742
1775
|
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1743
1776
|
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1744
1777
|
var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1745
1778
|
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1779
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1746
1780
|
|
|
1747
1781
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1748
|
-
var
|
|
1749
|
-
var
|
|
1782
|
+
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1783
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1750
1784
|
function MusicBrainzRecordingLink({
|
|
1751
1785
|
song,
|
|
1752
1786
|
children,
|
|
1753
1787
|
className: className2 = ""
|
|
1754
1788
|
}) {
|
|
1755
|
-
const url = (0,
|
|
1756
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1757
|
-
return /* @__PURE__ */ (0,
|
|
1789
|
+
const url = (0, import_genre_tree12.musicBrainzRecordingUrl)(song);
|
|
1790
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_jsx_runtime34.Fragment, { children });
|
|
1791
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
1758
1792
|
"a",
|
|
1759
1793
|
{
|
|
1760
1794
|
href: url,
|
|
@@ -1769,11 +1803,11 @@ function MusicBrainzRecordingLink({
|
|
|
1769
1803
|
}
|
|
1770
1804
|
|
|
1771
1805
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1772
|
-
var
|
|
1806
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1773
1807
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1774
|
-
var
|
|
1808
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1775
1809
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1776
|
-
var SongPageSchema = (0, import_transport5.PaginatedResponseSchema)(
|
|
1810
|
+
var SongPageSchema = (0, import_transport5.PaginatedResponseSchema)(import_genre_tree16.SongDetailedSchema).extend({
|
|
1777
1811
|
overallTotal: import_zod.z.number().nullable(),
|
|
1778
1812
|
page: import_zod.z.number().nullable(),
|
|
1779
1813
|
totalPages: import_zod.z.number().nullable()
|
|
@@ -1802,7 +1836,7 @@ function GenrePlaylistTracks({
|
|
|
1802
1836
|
renderTracksToolbar,
|
|
1803
1837
|
artist = null
|
|
1804
1838
|
}) {
|
|
1805
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0,
|
|
1839
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree17.useTrackList)();
|
|
1806
1840
|
const { session, sessionRestored } = (0, import_auth2.useSession)();
|
|
1807
1841
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player5.usePlayer)();
|
|
1808
1842
|
const { showPopup } = (0, import_popup4.usePopup)();
|
|
@@ -1812,18 +1846,18 @@ function GenrePlaylistTracks({
|
|
|
1812
1846
|
const { fetchBound } = (0, import_transport4.usePlaybackFetch)(getBackendBaseUrl);
|
|
1813
1847
|
const playback = (0, import_transport4.usePlaybackContext)();
|
|
1814
1848
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1815
|
-
queryKey: [...scope === "me" ? uuid ?
|
|
1849
|
+
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] : []],
|
|
1816
1850
|
queryFn: async ({ pageParam }) => {
|
|
1817
1851
|
if (scope === "me") {
|
|
1818
1852
|
if (!uuid) throw new Error("Personal genre has no playlist");
|
|
1819
1853
|
return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1820
1854
|
}
|
|
1821
1855
|
const response = await fetchBound(
|
|
1822
|
-
|
|
1856
|
+
import_genre_tree15.libraryEndpoints.reference.songs.list(),
|
|
1823
1857
|
true,
|
|
1824
1858
|
false,
|
|
1825
1859
|
{},
|
|
1826
|
-
{ genre: genreUuid, ...(0,
|
|
1860
|
+
{ genre: genreUuid, ...(0, import_genre_tree13.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1827
1861
|
);
|
|
1828
1862
|
const parsed = (0, import_transport6.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
|
|
1829
1863
|
return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
|
|
@@ -1834,8 +1868,8 @@ function GenrePlaylistTracks({
|
|
|
1834
1868
|
retry: (failureCount, error) => failureCount < 1 && error instanceof DOMException && error.name === "AbortError",
|
|
1835
1869
|
retryDelay: 0
|
|
1836
1870
|
});
|
|
1837
|
-
const [sentinel, setSentinel] = (0,
|
|
1838
|
-
(0,
|
|
1871
|
+
const [sentinel, setSentinel] = (0, import_react14.useState)(null);
|
|
1872
|
+
(0, import_react14.useEffect)(() => {
|
|
1839
1873
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1840
1874
|
const observer = new IntersectionObserver((entries) => {
|
|
1841
1875
|
if (entries.some((entry) => entry.isIntersecting)) {
|
|
@@ -1857,36 +1891,36 @@ function GenrePlaylistTracks({
|
|
|
1857
1891
|
}) : 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"));
|
|
1858
1892
|
started.catch((error) => {
|
|
1859
1893
|
console.error("Failed to play genre playlist:", error);
|
|
1860
|
-
if (error instanceof
|
|
1861
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1894
|
+
if (error instanceof import_genre_tree17.TracksFailureAlreadyReportedError) return;
|
|
1895
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport7.AppError ? error.code : import_transport7.ErrorCode.CLIENT_UNKNOWN }));
|
|
1862
1896
|
});
|
|
1863
1897
|
};
|
|
1864
|
-
return /* @__PURE__ */ (0,
|
|
1898
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { children: [
|
|
1865
1899
|
renderTracksToolbar?.(),
|
|
1866
|
-
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0,
|
|
1900
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("p", { role: "alert", children: [
|
|
1867
1901
|
"Couldn't load ",
|
|
1868
1902
|
trackNoun.plural,
|
|
1869
1903
|
".",
|
|
1870
1904
|
" ",
|
|
1871
|
-
/* @__PURE__ */ (0,
|
|
1872
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1905
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1906
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
|
|
1873
1907
|
tracks.map((track, index) => {
|
|
1874
1908
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1875
1909
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1876
1910
|
const isPlaying = isCurrent && playState === import_player6.PlayStates.PLAYING;
|
|
1877
|
-
const text = /* @__PURE__ */ (0,
|
|
1878
|
-
/* @__PURE__ */ (0,
|
|
1879
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1880
|
-
/* @__PURE__ */ (0,
|
|
1881
|
-
track.album && /* @__PURE__ */ (0,
|
|
1911
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree16.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1913
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree18.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1915
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1882
1916
|
] });
|
|
1883
1917
|
const leading = renderSongLeading?.(track, index);
|
|
1884
1918
|
const actions = renderSongActions?.(track);
|
|
1885
|
-
const isUnplayable = (0,
|
|
1919
|
+
const isUnplayable = (0, import_genre_tree16.isSong)(track) && (0, import_genre_tree16.songUnplayableReason)(track) !== null;
|
|
1886
1920
|
if (isUnplayable) {
|
|
1887
|
-
return /* @__PURE__ */ (0,
|
|
1921
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1888
1922
|
leading,
|
|
1889
|
-
/* @__PURE__ */ (0,
|
|
1923
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1890
1924
|
"button",
|
|
1891
1925
|
{
|
|
1892
1926
|
type: "button",
|
|
@@ -1894,27 +1928,27 @@ function GenrePlaylistTracks({
|
|
|
1894
1928
|
"aria-disabled": "true",
|
|
1895
1929
|
"aria-label": label,
|
|
1896
1930
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1897
|
-
children: /* @__PURE__ */ (0,
|
|
1931
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1898
1932
|
}
|
|
1899
1933
|
),
|
|
1900
1934
|
text,
|
|
1901
1935
|
actions
|
|
1902
1936
|
] }, track.uuid);
|
|
1903
1937
|
}
|
|
1904
|
-
return /* @__PURE__ */ (0,
|
|
1938
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
1905
1939
|
"li",
|
|
1906
1940
|
{
|
|
1907
1941
|
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)]" : ""}`,
|
|
1908
1942
|
children: [
|
|
1909
1943
|
leading,
|
|
1910
|
-
/* @__PURE__ */ (0,
|
|
1944
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
1911
1945
|
"button",
|
|
1912
1946
|
{
|
|
1913
1947
|
type: "button",
|
|
1914
1948
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1915
1949
|
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"}`,
|
|
1916
1950
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1917
|
-
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1951
|
+
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_fa4.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1918
1952
|
}
|
|
1919
1953
|
),
|
|
1920
1954
|
text,
|
|
@@ -1924,7 +1958,7 @@ function GenrePlaylistTracks({
|
|
|
1924
1958
|
track.uuid
|
|
1925
1959
|
);
|
|
1926
1960
|
}),
|
|
1927
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1961
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1928
1962
|
] })
|
|
1929
1963
|
] }) });
|
|
1930
1964
|
}
|
|
@@ -1932,8 +1966,9 @@ function GenrePlaylistTracks({
|
|
|
1932
1966
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1933
1967
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
1934
1968
|
var import_si2 = require("react-icons/si");
|
|
1935
|
-
var
|
|
1969
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
1936
1970
|
function GenreDetailExtras({
|
|
1971
|
+
imageInCover = false,
|
|
1937
1972
|
genreUuid,
|
|
1938
1973
|
genrePlaylist,
|
|
1939
1974
|
genreName,
|
|
@@ -1951,25 +1986,25 @@ function GenreDetailExtras({
|
|
|
1951
1986
|
...trackSlots
|
|
1952
1987
|
}) {
|
|
1953
1988
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
1954
|
-
const { data: overview, isPending } = (0,
|
|
1989
|
+
const { data: overview, isPending } = (0, import_genre_tree19.useFetchGenreOverview)(
|
|
1955
1990
|
genreUuid,
|
|
1956
1991
|
scope,
|
|
1957
1992
|
getBackendBaseUrl,
|
|
1958
1993
|
criteriaOverviewSchema
|
|
1959
1994
|
);
|
|
1960
1995
|
if (isPending) {
|
|
1961
|
-
return /* @__PURE__ */ (0,
|
|
1962
|
-
/* @__PURE__ */ (0,
|
|
1996
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
1997
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1963
1998
|
family
|
|
1964
1999
|
] });
|
|
1965
2000
|
}
|
|
1966
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
2001
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_jsx_runtime36.Fragment, { children: family });
|
|
1967
2002
|
const { summary, summarySourceUrl, essentialSongs, image } = overview;
|
|
1968
2003
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1969
|
-
return /* @__PURE__ */ (0,
|
|
1970
|
-
image && /* @__PURE__ */ (0,
|
|
1971
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1972
|
-
summarySourceUrl && /* @__PURE__ */ (0,
|
|
2004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
2005
|
+
image && (imageInCover ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommonsPhotoCredit, { image, creditLabel: "Image" }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommonsPhoto, { image, alt: overview.name, creditLabel: "Image" })),
|
|
2006
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
|
|
2007
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
1973
2008
|
"a",
|
|
1974
2009
|
{
|
|
1975
2010
|
href: summarySourceUrl,
|
|
@@ -1977,12 +2012,12 @@ function GenreDetailExtras({
|
|
|
1977
2012
|
rel: "noopener noreferrer",
|
|
1978
2013
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1979
2014
|
children: [
|
|
1980
|
-
/* @__PURE__ */ (0,
|
|
2015
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1981
2016
|
"Wikipedia"
|
|
1982
2017
|
]
|
|
1983
2018
|
}
|
|
1984
2019
|
),
|
|
1985
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
2020
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
1986
2021
|
"a",
|
|
1987
2022
|
{
|
|
1988
2023
|
href: wikidataUrl,
|
|
@@ -1990,17 +2025,17 @@ function GenreDetailExtras({
|
|
|
1990
2025
|
rel: "noopener noreferrer",
|
|
1991
2026
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1992
2027
|
children: [
|
|
1993
|
-
/* @__PURE__ */ (0,
|
|
2028
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1994
2029
|
"Wikidata"
|
|
1995
2030
|
]
|
|
1996
2031
|
}
|
|
1997
2032
|
)
|
|
1998
2033
|
] }) }),
|
|
1999
|
-
/* @__PURE__ */ (0,
|
|
2034
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
2000
2035
|
family,
|
|
2001
2036
|
renderGenreDetailExtras?.(overview),
|
|
2002
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
2003
|
-
/* @__PURE__ */ (0,
|
|
2037
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
|
|
2038
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2004
2039
|
GenrePlaylistTracks,
|
|
2005
2040
|
{
|
|
2006
2041
|
genreUuid,
|
|
@@ -2018,10 +2053,10 @@ function GenreDetailExtras({
|
|
|
2018
2053
|
}
|
|
2019
2054
|
|
|
2020
2055
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
2021
|
-
var
|
|
2022
|
-
var
|
|
2056
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2057
|
+
var import_react15 = require("react");
|
|
2023
2058
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
2024
|
-
return /* @__PURE__ */ (0,
|
|
2059
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
|
|
2025
2060
|
}
|
|
2026
2061
|
function GenreInfoPanelBody({
|
|
2027
2062
|
toolbar,
|
|
@@ -2036,12 +2071,12 @@ function GenreInfoPanelBody({
|
|
|
2036
2071
|
band,
|
|
2037
2072
|
extras
|
|
2038
2073
|
}) {
|
|
2039
|
-
const summary = scope === "reference" ?
|
|
2040
|
-
const selection = summary ? (0,
|
|
2041
|
-
return /* @__PURE__ */ (0,
|
|
2074
|
+
const summary = scope === "reference" ? import_genre_tree20.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
|
|
2075
|
+
const selection = summary ? (0, import_genre_tree20.defaultGenreMusicSelection)(summary) : void 0;
|
|
2076
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
2042
2077
|
toolbar,
|
|
2043
|
-
genrePlaylist ? /* @__PURE__ */ (0,
|
|
2044
|
-
/* @__PURE__ */ (0,
|
|
2078
|
+
genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(GenreActionBand, { ...band }),
|
|
2079
|
+
/* @__PURE__ */ (0, import_react15.createElement)(
|
|
2045
2080
|
GenreDetailExtras,
|
|
2046
2081
|
{
|
|
2047
2082
|
...extras,
|
|
@@ -2060,7 +2095,7 @@ function GenreInfoPanelBody({
|
|
|
2060
2095
|
}
|
|
2061
2096
|
|
|
2062
2097
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
2063
|
-
var
|
|
2098
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2064
2099
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2065
2100
|
function toNode(genre, parentId) {
|
|
2066
2101
|
return {
|
|
@@ -2085,11 +2120,11 @@ function useGenreDetailContent({
|
|
|
2085
2120
|
expanded,
|
|
2086
2121
|
treeStyles
|
|
2087
2122
|
}) {
|
|
2088
|
-
const result = (0,
|
|
2123
|
+
const result = (0, import_genre_tree21.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
2089
2124
|
const detail = result.status === "success" ? result.genre : void 0;
|
|
2090
2125
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
2091
2126
|
const playback = hostPlayback ?? defaultPlayback;
|
|
2092
|
-
const view = (0,
|
|
2127
|
+
const view = (0, import_react16.useMemo)(() => {
|
|
2093
2128
|
if (!detail) return null;
|
|
2094
2129
|
const rootColor = (0, import_genre_tree_view4.getGenreTreeColor)(detail.root.wikidataId);
|
|
2095
2130
|
const relative = (genre, parentId) => ({
|
|
@@ -2103,7 +2138,7 @@ function useGenreDetailContent({
|
|
|
2103
2138
|
id: detail.uuid,
|
|
2104
2139
|
parentId: detail.parent?.uuid ?? null,
|
|
2105
2140
|
name: detail.name,
|
|
2106
|
-
itemCount: scope === "reference" ? (0,
|
|
2141
|
+
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,
|
|
2107
2142
|
wikidataId: detail.wikidataId
|
|
2108
2143
|
},
|
|
2109
2144
|
fill: own.fill,
|
|
@@ -2118,24 +2153,24 @@ function useGenreDetailContent({
|
|
|
2118
2153
|
if (result.status === "not-found") return {
|
|
2119
2154
|
name: null,
|
|
2120
2155
|
title: "Genre not found",
|
|
2121
|
-
content: /* @__PURE__ */ (0,
|
|
2156
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This genre does not exist." })
|
|
2122
2157
|
};
|
|
2123
2158
|
if (result.status === "error") return {
|
|
2124
2159
|
name: null,
|
|
2125
2160
|
title: "Genre unavailable",
|
|
2126
|
-
content: /* @__PURE__ */ (0,
|
|
2161
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
2127
2162
|
};
|
|
2128
2163
|
if (!detail || !view) return {
|
|
2129
2164
|
name: null,
|
|
2130
2165
|
title: "Loading genre\u2026",
|
|
2131
|
-
content: /* @__PURE__ */ (0,
|
|
2132
|
-
/* @__PURE__ */ (0,
|
|
2133
|
-
/* @__PURE__ */ (0,
|
|
2134
|
-
/* @__PURE__ */ (0,
|
|
2166
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2167
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.Skeleton, { className: "h-6 w-2/3" }),
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.Skeleton, { className: "h-4 w-full" }),
|
|
2169
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.Skeleton, { className: "h-4 w-3/4" })
|
|
2135
2170
|
] })
|
|
2136
2171
|
};
|
|
2137
|
-
const selectGenre = (nodeId) => onSelectGenre(
|
|
2138
|
-
const family = /* @__PURE__ */ (0,
|
|
2172
|
+
const selectGenre = (nodeId) => onSelectGenre(import_genre_tree21.GenreUuidSchema.parse(nodeId));
|
|
2173
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2139
2174
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2140
2175
|
{
|
|
2141
2176
|
node: view.node,
|
|
@@ -2146,29 +2181,38 @@ function useGenreDetailContent({
|
|
|
2146
2181
|
onSelectNode: selectGenre
|
|
2147
2182
|
}
|
|
2148
2183
|
);
|
|
2149
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2184
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2185
|
+
import_genre_tree_view4.GenreNodeAncestors,
|
|
2186
|
+
{
|
|
2187
|
+
node: view.node,
|
|
2188
|
+
fill: view.fill,
|
|
2189
|
+
textColor: view.textColor,
|
|
2190
|
+
ancestorNodes: view.ancestorNodes,
|
|
2191
|
+
onSelectNode: selectGenre
|
|
2192
|
+
}
|
|
2193
|
+
);
|
|
2150
2194
|
return {
|
|
2151
2195
|
name: detail.name,
|
|
2152
2196
|
title: detail.name,
|
|
2153
2197
|
headerAccent: { background: view.fill, color: view.textColor },
|
|
2154
2198
|
aside: expanded ? ancestors : void 0,
|
|
2155
|
-
content: /* @__PURE__ */ (0,
|
|
2156
|
-
/* @__PURE__ */ (0,
|
|
2199
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2200
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2157
2201
|
GenreInfoPanelBody,
|
|
2158
2202
|
{
|
|
2159
|
-
family: /* @__PURE__ */ (0,
|
|
2203
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
|
|
2160
2204
|
family,
|
|
2161
2205
|
!expanded && ancestors
|
|
2162
2206
|
] }),
|
|
2163
2207
|
genreUuid: detail.uuid,
|
|
2164
|
-
genrePlaylist: hostPlaylist ?? (detail.criteriaPlaylist === null ? null : scope === "reference" ? { ...
|
|
2208
|
+
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 }),
|
|
2165
2209
|
catalogue: detail.catalogue,
|
|
2166
2210
|
wikidataId: detail.wikidataId,
|
|
2167
2211
|
scope,
|
|
2168
2212
|
getBackendBaseUrl,
|
|
2169
2213
|
playback,
|
|
2170
2214
|
band: { node: view.node, ...band, shareTarget: band?.shareTarget?.kind === "genre" ? { ...band.shareTarget, name: detail.name } : band?.shareTarget },
|
|
2171
|
-
extras
|
|
2215
|
+
extras: { ...extras, imageInCover: true }
|
|
2172
2216
|
}
|
|
2173
2217
|
),
|
|
2174
2218
|
trailing
|
|
@@ -2200,9 +2244,13 @@ function GenreDetailPanel({
|
|
|
2200
2244
|
expanded,
|
|
2201
2245
|
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
2202
2246
|
});
|
|
2203
|
-
return /* @__PURE__ */ (0,
|
|
2204
|
-
|
|
2247
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2248
|
+
GenreOverviewDetailPanel,
|
|
2205
2249
|
{
|
|
2250
|
+
genreUuid: panel.name ? import_genre_tree21.GenreUuidSchema.parse(genreUuid) : null,
|
|
2251
|
+
scope,
|
|
2252
|
+
getBackendBaseUrl,
|
|
2253
|
+
criteriaOverviewSchema: extras?.criteriaOverviewSchema,
|
|
2206
2254
|
side: "left",
|
|
2207
2255
|
title: panel.title,
|
|
2208
2256
|
headerAccent: panel.headerAccent,
|
|
@@ -2213,16 +2261,17 @@ function GenreDetailPanel({
|
|
|
2213
2261
|
edgeTab,
|
|
2214
2262
|
aside: panel.aside,
|
|
2215
2263
|
titleInSearch,
|
|
2216
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
2264
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
2217
2265
|
children: panel.content
|
|
2218
2266
|
}
|
|
2219
2267
|
);
|
|
2220
2268
|
}
|
|
2221
2269
|
|
|
2222
2270
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
2223
|
-
var
|
|
2224
|
-
var
|
|
2225
|
-
var
|
|
2271
|
+
var import_react17 = require("react");
|
|
2272
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2273
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
2274
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2226
2275
|
function ChangeParentPopup({
|
|
2227
2276
|
genrePlaylist,
|
|
2228
2277
|
genrePlaylists,
|
|
@@ -2231,38 +2280,52 @@ function ChangeParentPopup({
|
|
|
2231
2280
|
onClose,
|
|
2232
2281
|
onParentChanged
|
|
2233
2282
|
}) {
|
|
2234
|
-
const { mutate, isPending, formErrors } = (0,
|
|
2235
|
-
const candidates = (0,
|
|
2236
|
-
() => (0,
|
|
2283
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree22.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2284
|
+
const candidates = (0, import_react17.useMemo)(
|
|
2285
|
+
() => (0, import_genre_tree22.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
2237
2286
|
[genrePlaylists, genrePlaylist.uuid]
|
|
2238
2287
|
);
|
|
2239
|
-
const
|
|
2288
|
+
const [confirmingRoot, setConfirmingRoot] = (0, import_react17.useState)(false);
|
|
2289
|
+
const handleMove = (to) => {
|
|
2240
2290
|
const genre = genrePlaylist.criteria.uuid;
|
|
2241
2291
|
const parentPlaylistUuid = genrePlaylist.parent?.uuid;
|
|
2242
2292
|
const from = parentPlaylistUuid ? genrePlaylists.find(({ uuid }) => uuid === parentPlaylistUuid)?.criteria?.uuid ?? null : null;
|
|
2243
2293
|
mutate(
|
|
2244
|
-
{ uuid: genre, data: { parent:
|
|
2294
|
+
{ uuid: genre, data: { parent: to } },
|
|
2245
2295
|
{
|
|
2246
2296
|
onSuccess: () => {
|
|
2247
2297
|
onClose();
|
|
2248
|
-
onParentChanged?.({ genre, from, to
|
|
2298
|
+
onParentChanged?.({ genre, from, to });
|
|
2249
2299
|
}
|
|
2250
2300
|
}
|
|
2251
2301
|
);
|
|
2252
2302
|
};
|
|
2253
|
-
|
|
2303
|
+
const handleSelect = (newParent) => handleMove(newParent.criteria.uuid);
|
|
2304
|
+
(0, import_react17.useEffect)(() => {
|
|
2254
2305
|
const handleKeyDown = (event) => {
|
|
2255
2306
|
if (event.key === "Escape") onClose();
|
|
2256
2307
|
};
|
|
2257
2308
|
window.addEventListener("keydown", handleKeyDown);
|
|
2258
2309
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2259
2310
|
}, [onClose]);
|
|
2260
|
-
return /* @__PURE__ */ (0,
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
2311
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
2312
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
|
|
2262
2313
|
"Current parent: ",
|
|
2263
2314
|
genrePlaylist.parent?.name ?? "none"
|
|
2264
2315
|
] }),
|
|
2265
|
-
/* @__PURE__ */ (0,
|
|
2316
|
+
genrePlaylist.parent && (confirmingRoot ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "mb-3 text-sm", children: [
|
|
2317
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("p", { className: "mb-2", children: [
|
|
2318
|
+
genrePlaylist.name,
|
|
2319
|
+
" and its descendants will no longer be under ",
|
|
2320
|
+
genrePlaylist.parent.name,
|
|
2321
|
+
"."
|
|
2322
|
+
] }),
|
|
2323
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(PopupButtons, { alignment: "left", children: [
|
|
2324
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui15.Button, { disabled: isPending, onClick: () => handleMove(null), children: "Confirm" }),
|
|
2325
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui15.Button, { variant: "secondary", disabled: isPending, onClick: () => setConfirmingRoot(false), children: "Back" })
|
|
2326
|
+
] })
|
|
2327
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui15.Button, { variant: "secondary", onClick: () => setConfirmingRoot(true), children: "Make root" }) })),
|
|
2328
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2266
2329
|
GenreSearch,
|
|
2267
2330
|
{
|
|
2268
2331
|
genrePlaylists: candidates,
|
|
@@ -2270,25 +2333,25 @@ function ChangeParentPopup({
|
|
|
2270
2333
|
placeholder: "Search a new parent\u2026"
|
|
2271
2334
|
}
|
|
2272
2335
|
) }),
|
|
2273
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
2336
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
2274
2337
|
] });
|
|
2275
2338
|
}
|
|
2276
2339
|
|
|
2277
2340
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2278
|
-
var
|
|
2279
|
-
var
|
|
2341
|
+
var import_ui22 = require("@behindthemusictree/ui");
|
|
2342
|
+
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2280
2343
|
|
|
2281
2344
|
// src/genre-tree/artist/ArtistActionBand.tsx
|
|
2282
2345
|
var import_lucide_react14 = require("lucide-react");
|
|
2283
|
-
var
|
|
2284
|
-
var
|
|
2346
|
+
var import_ui16 = require("@behindthemusictree/ui");
|
|
2347
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2285
2348
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
2286
|
-
var
|
|
2349
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2287
2350
|
function ArtistActionBand({ artist, share }) {
|
|
2288
|
-
const { trackList, playNewTrackListFromArtist } = (0,
|
|
2351
|
+
const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree23.useTrackList)();
|
|
2289
2352
|
const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
|
|
2290
2353
|
const origin = trackList?.origin;
|
|
2291
|
-
const isCurrent = origin?.type ===
|
|
2354
|
+
const isCurrent = origin?.type === import_genre_tree23.TrackListOriginType.ARTIST && origin.uuid === artist.uuid;
|
|
2292
2355
|
const playing = isCurrent && isPlaying;
|
|
2293
2356
|
const handleClick = () => {
|
|
2294
2357
|
if (isCurrent) {
|
|
@@ -2299,11 +2362,11 @@ function ArtistActionBand({ artist, share }) {
|
|
|
2299
2362
|
console.error("Failed to load artist tracks:", error);
|
|
2300
2363
|
});
|
|
2301
2364
|
};
|
|
2302
|
-
return /* @__PURE__ */ (0,
|
|
2303
|
-
/* @__PURE__ */ (0,
|
|
2304
|
-
|
|
2365
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui16.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_ui16.ActionBand, { children: [
|
|
2366
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2367
|
+
import_ui16.ActionButton,
|
|
2305
2368
|
{
|
|
2306
|
-
icon: playing ? /* @__PURE__ */ (0,
|
|
2369
|
+
icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
2307
2370
|
label: playing ? "Pause" : "Play",
|
|
2308
2371
|
caption: String(artist.playableTracksCount),
|
|
2309
2372
|
disabled: artist.playableTracksCount === 0,
|
|
@@ -2316,13 +2379,13 @@ function ArtistActionBand({ artist, share }) {
|
|
|
2316
2379
|
}
|
|
2317
2380
|
|
|
2318
2381
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2319
|
-
var
|
|
2382
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
2320
2383
|
|
|
2321
2384
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
2322
2385
|
var import_lucide_react15 = require("lucide-react");
|
|
2323
2386
|
var import_si3 = require("react-icons/si");
|
|
2324
|
-
var
|
|
2325
|
-
var
|
|
2387
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2388
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2326
2389
|
var LINK_KINDS = {
|
|
2327
2390
|
official: { label: "Official website", Icon: import_lucide_react15.Globe },
|
|
2328
2391
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -2359,7 +2422,7 @@ function artistLinks(artist) {
|
|
|
2359
2422
|
return links;
|
|
2360
2423
|
}
|
|
2361
2424
|
function ArtistLinks({ links }) {
|
|
2362
|
-
return /* @__PURE__ */ (0,
|
|
2425
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui17.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
2363
2426
|
}
|
|
2364
2427
|
|
|
2365
2428
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2370,7 +2433,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2370
2433
|
}
|
|
2371
2434
|
|
|
2372
2435
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2373
|
-
var
|
|
2436
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2374
2437
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2375
2438
|
function originLabel(artist, origin) {
|
|
2376
2439
|
if (origin.source === "area") return "From";
|
|
@@ -2384,7 +2447,7 @@ function OriginValue({
|
|
|
2384
2447
|
renderPlaceLink
|
|
2385
2448
|
}) {
|
|
2386
2449
|
const place = renderPlaceLink && origin.regionId !== null ? renderPlaceLink({ ...origin, regionId: origin.regionId }) : origin.name;
|
|
2387
|
-
return /* @__PURE__ */ (0,
|
|
2450
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2388
2451
|
place,
|
|
2389
2452
|
country && origin.kind !== "country" && `, ${country}`
|
|
2390
2453
|
] });
|
|
@@ -2399,30 +2462,24 @@ function ArtistHeader({
|
|
|
2399
2462
|
if (artist.origin) {
|
|
2400
2463
|
fields.push({
|
|
2401
2464
|
label: originLabel(artist, artist.origin),
|
|
2402
|
-
value: /* @__PURE__ */ (0,
|
|
2465
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(OriginValue, { origin: artist.origin, country, renderPlaceLink })
|
|
2403
2466
|
});
|
|
2404
2467
|
}
|
|
2405
2468
|
if (country && !artist.origin) fields.push({ label: "Country", value: country });
|
|
2406
2469
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2407
2470
|
if (years) fields.push({ label: "Active", value: years });
|
|
2408
2471
|
const links = artistLinks(artist);
|
|
2409
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2472
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistLinks, { links }) });
|
|
2410
2473
|
if (fields.length === 0) return null;
|
|
2411
|
-
return /* @__PURE__ */ (0,
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2415
|
-
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2416
|
-
function ArtistPhoto({ image, name }) {
|
|
2417
|
-
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(CommonsPhoto, { image, alt: name });
|
|
2474
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui18.FieldList, { items: fields });
|
|
2418
2475
|
}
|
|
2419
2476
|
|
|
2420
2477
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2421
|
-
var
|
|
2478
|
+
var import_ui19 = require("@behindthemusictree/ui");
|
|
2422
2479
|
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2423
2480
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2424
2481
|
if (memberships.length === 0) return null;
|
|
2425
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2482
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui19.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2426
2483
|
const details = [
|
|
2427
2484
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2428
2485
|
...membership.roles
|
|
@@ -2443,7 +2500,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2443
2500
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2444
2501
|
if (memberships.length === 0) return null;
|
|
2445
2502
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2446
|
-
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
2503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui19.PanelSection, { title, children: [
|
|
2447
2504
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2448
2505
|
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2449
2506
|
MembershipList,
|
|
@@ -2457,7 +2514,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2457
2514
|
}
|
|
2458
2515
|
|
|
2459
2516
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2460
|
-
var
|
|
2517
|
+
var import_ui20 = require("@behindthemusictree/ui");
|
|
2461
2518
|
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2462
2519
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2463
2520
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
@@ -2478,10 +2535,10 @@ function byYear(a, b) {
|
|
|
2478
2535
|
function ArtistDiscography({ albums }) {
|
|
2479
2536
|
if (albums.length === 0) return null;
|
|
2480
2537
|
const sorted = [...albums].sort(byYear);
|
|
2481
|
-
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2538
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui20.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2482
2539
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2483
2540
|
if (groupAlbums.length === 0) return null;
|
|
2484
|
-
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2541
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui20.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2485
2542
|
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2486
2543
|
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2487
2544
|
] }, album.uuid)) }) }, group);
|
|
@@ -2489,16 +2546,16 @@ function ArtistDiscography({ albums }) {
|
|
|
2489
2546
|
}
|
|
2490
2547
|
|
|
2491
2548
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2492
|
-
var
|
|
2549
|
+
var import_react18 = require("react");
|
|
2493
2550
|
var import_react_query2 = require("@tanstack/react-query");
|
|
2494
|
-
var
|
|
2551
|
+
var import_ui21 = require("@behindthemusictree/ui");
|
|
2495
2552
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2496
|
-
var
|
|
2553
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2497
2554
|
var import_transport8 = require("@behindthemusictree/app-kit/transport");
|
|
2498
2555
|
|
|
2499
2556
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2500
2557
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
2501
|
-
var
|
|
2558
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2502
2559
|
|
|
2503
2560
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
2504
2561
|
var import_fa5 = require("react-icons/fa");
|
|
@@ -2553,8 +2610,8 @@ function TrackPositionPlayPause({
|
|
|
2553
2610
|
}
|
|
2554
2611
|
|
|
2555
2612
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2556
|
-
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2557
2613
|
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2614
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2558
2615
|
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2559
2616
|
function TrackItem({
|
|
2560
2617
|
track,
|
|
@@ -2565,11 +2622,11 @@ function TrackItem({
|
|
|
2565
2622
|
renderSongLink
|
|
2566
2623
|
}) {
|
|
2567
2624
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player10.usePlayer)();
|
|
2568
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
2625
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree24.useTrackList)();
|
|
2569
2626
|
const scope = trackList?.origin?.scope ?? null;
|
|
2570
2627
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
2571
2628
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
2572
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
2629
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree25.isSong)(track) && (0, import_genre_tree25.songUnplayableReason)(track) !== null;
|
|
2573
2630
|
const handlePlayPauseClick = (event) => {
|
|
2574
2631
|
event.stopPropagation();
|
|
2575
2632
|
if (isCurrentTrack) {
|
|
@@ -2602,8 +2659,8 @@ function TrackItem({
|
|
|
2602
2659
|
}
|
|
2603
2660
|
),
|
|
2604
2661
|
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2605
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0,
|
|
2606
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2662
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime46.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_runtime46.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2663
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree26.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2607
2664
|
] }),
|
|
2608
2665
|
/* @__PURE__ */ (0, import_jsx_runtime46.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_runtime46.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
|
|
2609
2666
|
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
@@ -2622,20 +2679,20 @@ function TrackItem({
|
|
|
2622
2679
|
|
|
2623
2680
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2624
2681
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
2625
|
-
var SongPageSchema2 = (0, import_transport8.PaginatedResponseSchema)(
|
|
2682
|
+
var SongPageSchema2 = (0, import_transport8.PaginatedResponseSchema)(import_genre_tree27.SongDetailedSchema);
|
|
2626
2683
|
var SORTS = [
|
|
2627
2684
|
{ ordering: "title", label: "Title" },
|
|
2628
2685
|
{ ordering: "-albumYear", label: "Year" }
|
|
2629
2686
|
];
|
|
2630
2687
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
2631
|
-
const [page, setPage] = (0,
|
|
2632
|
-
const [ordering, setOrdering] = (0,
|
|
2633
|
-
const { playNewTrackListFromArtist } = (0,
|
|
2688
|
+
const [page, setPage] = (0, import_react18.useState)(1);
|
|
2689
|
+
const [ordering, setOrdering] = (0, import_react18.useState)("title");
|
|
2690
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree27.useTrackList)();
|
|
2634
2691
|
const { playerTrackObject } = (0, import_player11.usePlayer)();
|
|
2635
2692
|
const { data, isPending, isError } = (0, import_transport8.usePlaybackQuery)({
|
|
2636
2693
|
getBackendBaseUrl,
|
|
2637
2694
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
2638
|
-
queryFn: (fetchBound) => fetchBound(
|
|
2695
|
+
queryFn: (fetchBound) => fetchBound(import_genre_tree27.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
2639
2696
|
artist: artist.uuid,
|
|
2640
2697
|
ordering,
|
|
2641
2698
|
page
|
|
@@ -2650,11 +2707,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2650
2707
|
});
|
|
2651
2708
|
const handleRowClickCapture = (event, song) => {
|
|
2652
2709
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
2653
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2710
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree27.songUnplayableReason)(song) !== null) return;
|
|
2654
2711
|
event.stopPropagation();
|
|
2655
2712
|
void play(song.uuid);
|
|
2656
2713
|
};
|
|
2657
|
-
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { children: [
|
|
2658
2715
|
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2659
2716
|
"button",
|
|
2660
2717
|
{
|
|
@@ -2678,7 +2735,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2678
2735
|
renderSongLink
|
|
2679
2736
|
}
|
|
2680
2737
|
) }, song.uuid)) }),
|
|
2681
|
-
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
2738
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_ui21.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2682
2739
|
] }) });
|
|
2683
2740
|
}
|
|
2684
2741
|
|
|
@@ -2696,7 +2753,7 @@ function useArtistPanel({
|
|
|
2696
2753
|
onExpandedChange,
|
|
2697
2754
|
onClose
|
|
2698
2755
|
}) {
|
|
2699
|
-
const result = (0,
|
|
2756
|
+
const result = (0, import_genre_tree28.useArtist)(uuid, getBackendBaseUrl);
|
|
2700
2757
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2701
2758
|
switch (result.status) {
|
|
2702
2759
|
case "idle":
|
|
@@ -2706,12 +2763,12 @@ function useArtistPanel({
|
|
|
2706
2763
|
...panel,
|
|
2707
2764
|
title: "Artist",
|
|
2708
2765
|
content: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2709
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2710
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2711
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2712
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2713
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2714
|
-
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2766
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
|
|
2767
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-4 w-32" }),
|
|
2768
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-3 w-16" }),
|
|
2769
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-4 w-24" }),
|
|
2770
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2771
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_ui22.Skeleton, { className: "h-16" })
|
|
2715
2772
|
] })
|
|
2716
2773
|
};
|
|
2717
2774
|
case "not-found":
|
|
@@ -2733,6 +2790,7 @@ function useArtistPanel({
|
|
|
2733
2790
|
return {
|
|
2734
2791
|
...panel,
|
|
2735
2792
|
title: artist.name,
|
|
2793
|
+
cover: artist.image ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(CommonsCover, { image: artist.image, alt: artist.name }) : void 0,
|
|
2736
2794
|
content: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_jsx_runtime48.Fragment, { children: [
|
|
2737
2795
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2738
2796
|
ArtistActionBand,
|
|
@@ -2741,7 +2799,7 @@ function useArtistPanel({
|
|
|
2741
2799
|
share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
|
|
2742
2800
|
}
|
|
2743
2801
|
),
|
|
2744
|
-
artist.image && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
2802
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(CommonsPhotoCredit, { image: artist.image }),
|
|
2745
2803
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistHeader, { artist, renderPlaceLink }),
|
|
2746
2804
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2747
2805
|
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
@@ -2762,24 +2820,24 @@ function useArtistPanel({
|
|
|
2762
2820
|
}
|
|
2763
2821
|
|
|
2764
2822
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2765
|
-
var
|
|
2766
|
-
var
|
|
2823
|
+
var import_ui24 = require("@behindthemusictree/ui");
|
|
2824
|
+
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2767
2825
|
|
|
2768
2826
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2769
|
-
var
|
|
2827
|
+
var import_react19 = require("react");
|
|
2770
2828
|
var import_lucide_react16 = require("lucide-react");
|
|
2771
|
-
var
|
|
2829
|
+
var import_ui23 = require("@behindthemusictree/ui");
|
|
2772
2830
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
2773
|
-
var
|
|
2831
|
+
var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2774
2832
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
2775
2833
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2776
2834
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2777
2835
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2778
2836
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2779
|
-
const [open, setOpen] = (0,
|
|
2780
|
-
const history = (0,
|
|
2837
|
+
const [open, setOpen] = (0, import_react19.useState)(false);
|
|
2838
|
+
const history = (0, import_genre_tree29.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2781
2839
|
let body = null;
|
|
2782
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2840
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui23.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2783
2841
|
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2784
2842
|
else if (history.status === "success")
|
|
2785
2843
|
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("li", { className: "py-1.5 text-body", children: [
|
|
@@ -2797,20 +2855,20 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
2797
2855
|
dateFormat.format(new Date(entry.createdOn))
|
|
2798
2856
|
] })
|
|
2799
2857
|
] }, entry.uuid)) });
|
|
2800
|
-
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2801
|
-
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("summary", { className: `mt-2.5 cursor-pointer ${
|
|
2858
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui23.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
|
|
2859
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui23.PANEL_LABEL_CLASS}`, children: "History" }),
|
|
2802
2860
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "mt-1.5", children: body })
|
|
2803
2861
|
] }) });
|
|
2804
2862
|
}
|
|
2805
2863
|
function SongPanelContent(props) {
|
|
2806
2864
|
const { song, scope, getBackendBaseUrl, shareUrl, renderArtistLink } = props;
|
|
2807
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2865
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree29.useTrackList)();
|
|
2808
2866
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player12.usePlayer)();
|
|
2809
|
-
const unplayableReason = (0,
|
|
2867
|
+
const unplayableReason = (0, import_genre_tree29.songUnplayableReason)(song);
|
|
2810
2868
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2811
2869
|
const isPlaying = isCurrent && playState === import_player12.PlayStates.PLAYING;
|
|
2812
2870
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2813
|
-
const musicBrainzUrl = (0,
|
|
2871
|
+
const musicBrainzUrl = (0, import_genre_tree29.musicBrainzRecordingUrl)(song);
|
|
2814
2872
|
const fields = [];
|
|
2815
2873
|
if (song.genre) {
|
|
2816
2874
|
const path = props.genrePath && props.genrePath.length > 0 ? props.genrePath : null;
|
|
@@ -2856,9 +2914,9 @@ function SongPanelContent(props) {
|
|
|
2856
2914
|
value: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex gap-3", children: links })
|
|
2857
2915
|
});
|
|
2858
2916
|
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
|
|
2859
|
-
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2917
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui23.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_ui23.ActionBand, { children: [
|
|
2860
2918
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2861
|
-
|
|
2919
|
+
import_ui23.ActionButton,
|
|
2862
2920
|
{
|
|
2863
2921
|
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react16.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_lucide_react16.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
2864
2922
|
label: isPlaying ? "Pause" : "Play",
|
|
@@ -2869,9 +2927,9 @@ function SongPanelContent(props) {
|
|
|
2869
2927
|
),
|
|
2870
2928
|
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2871
2929
|
] }) }),
|
|
2872
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2873
|
-
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2874
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
2930
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_genre_tree29.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2931
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui23.FieldList, { items: fields }),
|
|
2932
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_ui23.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2875
2933
|
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2876
2934
|
] });
|
|
2877
2935
|
}
|
|
@@ -2889,7 +2947,7 @@ function useSongPanel({
|
|
|
2889
2947
|
onClose,
|
|
2890
2948
|
...navigation
|
|
2891
2949
|
}) {
|
|
2892
|
-
const result = (0,
|
|
2950
|
+
const result = (0, import_genre_tree30.useSong)(uuid, getBackendBaseUrl);
|
|
2893
2951
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2894
2952
|
switch (result.status) {
|
|
2895
2953
|
case "idle":
|
|
@@ -2899,10 +2957,10 @@ function useSongPanel({
|
|
|
2899
2957
|
...panel,
|
|
2900
2958
|
title: "Song",
|
|
2901
2959
|
content: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2902
|
-
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
2903
|
-
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
2904
|
-
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
2905
|
-
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
2960
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui24.Skeleton, { className: "h-3 w-16" }),
|
|
2961
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui24.Skeleton, { className: "h-4 w-32" }),
|
|
2962
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui24.Skeleton, { className: "h-3 w-16" }),
|
|
2963
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_ui24.Skeleton, { className: "h-4 w-24" })
|
|
2906
2964
|
] })
|
|
2907
2965
|
};
|
|
2908
2966
|
case "not-found":
|
|
@@ -2977,14 +3035,14 @@ function useEntityPanel({
|
|
|
2977
3035
|
|
|
2978
3036
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2979
3037
|
var import_md2 = require("react-icons/md");
|
|
2980
|
-
var
|
|
3038
|
+
var import_ui25 = require("@behindthemusictree/ui");
|
|
2981
3039
|
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
2982
3040
|
function SizeMetricToggle({
|
|
2983
3041
|
value,
|
|
2984
3042
|
onChange
|
|
2985
3043
|
}) {
|
|
2986
3044
|
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
2987
|
-
|
|
3045
|
+
import_ui25.SegmentedControl,
|
|
2988
3046
|
{
|
|
2989
3047
|
variant: "floating",
|
|
2990
3048
|
"aria-label": "Size nodes by",
|
|
@@ -3025,6 +3083,8 @@ function GenreTreeView({
|
|
|
3025
3083
|
criteriaOverviewSchema,
|
|
3026
3084
|
renderGenreDetailExtras,
|
|
3027
3085
|
floatingControlsLeading,
|
|
3086
|
+
searchTrailingAction,
|
|
3087
|
+
searchFullWidthBreakpoint = "md",
|
|
3028
3088
|
sizeMetric: controlledSizeMetric,
|
|
3029
3089
|
onSizeMetricChange,
|
|
3030
3090
|
renderArtistLink,
|
|
@@ -3062,21 +3122,21 @@ function GenreTreeView({
|
|
|
3062
3122
|
placement = "default",
|
|
3063
3123
|
...focus
|
|
3064
3124
|
}) {
|
|
3065
|
-
const getShareUrlRef = (0,
|
|
3125
|
+
const getShareUrlRef = (0, import_react20.useRef)(getShareUrl);
|
|
3066
3126
|
getShareUrlRef.current = getShareUrl;
|
|
3067
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
3127
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react20.useState)(null);
|
|
3068
3128
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
3069
3129
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
3070
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
3130
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react20.useState)(null);
|
|
3071
3131
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
3072
3132
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
3073
3133
|
const changeSong = (uuid) => {
|
|
3074
3134
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
3075
3135
|
onSongUuidChange?.(uuid);
|
|
3076
3136
|
};
|
|
3077
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
3137
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react20.useState)("songs");
|
|
3078
3138
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
3079
|
-
const deferredSizeMetric = (0,
|
|
3139
|
+
const deferredSizeMetric = (0, import_react20.useDeferredValue)(sizeMetric);
|
|
3080
3140
|
const changeSizeMetric = (metric) => {
|
|
3081
3141
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
3082
3142
|
onSizeMetricChange?.(metric);
|
|
@@ -3108,7 +3168,7 @@ function GenreTreeView({
|
|
|
3108
3168
|
allowWheelRotation: internalAllowWheelRotation,
|
|
3109
3169
|
toggleWheelRotation,
|
|
3110
3170
|
onNodeHover: handleNodeHover
|
|
3111
|
-
} = (0,
|
|
3171
|
+
} = (0, import_genre_tree32.useGenreTreeViewState)({
|
|
3112
3172
|
scope,
|
|
3113
3173
|
getBackendBaseUrl,
|
|
3114
3174
|
viewMode: controlledViewMode,
|
|
@@ -3118,7 +3178,7 @@ function GenreTreeView({
|
|
|
3118
3178
|
artistUuid
|
|
3119
3179
|
});
|
|
3120
3180
|
const restoreRevision = viewportRestore?.revision;
|
|
3121
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
3181
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react20.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
3122
3182
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
3123
3183
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
3124
3184
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -3127,14 +3187,14 @@ function GenreTreeView({
|
|
|
3127
3187
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
3128
3188
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
3129
3189
|
}
|
|
3130
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
3131
|
-
const { mutate: updateGenre } = (0,
|
|
3190
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree32.genreUpdateMutationKey }) > 0;
|
|
3191
|
+
const { mutate: updateGenre } = (0, import_genre_tree32.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
3132
3192
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
3133
3193
|
const isArtistFiltered = artistUuid !== null;
|
|
3134
3194
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
3135
|
-
const artistResult = (0,
|
|
3195
|
+
const artistResult = (0, import_genre_tree32.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
3136
3196
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
3137
|
-
const filterArtist = (0,
|
|
3197
|
+
const filterArtist = (0, import_react20.useMemo)(
|
|
3138
3198
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
3139
3199
|
[artistUuid, filterArtistName]
|
|
3140
3200
|
);
|
|
@@ -3149,14 +3209,14 @@ function GenreTreeView({
|
|
|
3149
3209
|
if (selectedGenreUuid !== null) clearSelection();
|
|
3150
3210
|
changeSong(uuid);
|
|
3151
3211
|
};
|
|
3152
|
-
const openSongRef = (0,
|
|
3212
|
+
const openSongRef = (0, import_react20.useRef)(openSong);
|
|
3153
3213
|
openSongRef.current = openSong;
|
|
3154
|
-
const resolvedRenderSongLink = (0,
|
|
3214
|
+
const resolvedRenderSongLink = (0, import_react20.useCallback)(
|
|
3155
3215
|
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
3156
3216
|
[renderSongLink]
|
|
3157
3217
|
);
|
|
3158
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
3159
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
3218
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react20.useState)(null);
|
|
3219
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react20.useState)(null);
|
|
3160
3220
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
3161
3221
|
const songBaseUrl = getBackendBaseUrl;
|
|
3162
3222
|
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
@@ -3203,8 +3263,8 @@ function GenreTreeView({
|
|
|
3203
3263
|
}
|
|
3204
3264
|
});
|
|
3205
3265
|
const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
|
|
3206
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0,
|
|
3207
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
3266
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react20.useState)(false);
|
|
3267
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react20.useState)(false);
|
|
3208
3268
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
3209
3269
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
3210
3270
|
const changeCollapsed = (next) => {
|
|
@@ -3215,30 +3275,30 @@ function GenreTreeView({
|
|
|
3215
3275
|
setUncontrolledExpanded(next);
|
|
3216
3276
|
onExpandedChange?.(next);
|
|
3217
3277
|
};
|
|
3218
|
-
(0,
|
|
3278
|
+
(0, import_react20.useEffect)(() => {
|
|
3219
3279
|
setUncontrolledCollapsed(false);
|
|
3220
3280
|
}, [selectedGenreUuid, artistUuid, songUuid]);
|
|
3221
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0,
|
|
3281
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react20.useState)(false);
|
|
3222
3282
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
3223
3283
|
const isTreeReady = !isLoading && !isError && (!usesHandoff || isWheelRevealed);
|
|
3224
|
-
const [resolveStyle, setResolveStyle] = (0,
|
|
3225
|
-
const handleNodeStyleResolverChange = (0,
|
|
3284
|
+
const [resolveStyle, setResolveStyle] = (0, import_react20.useState)(null);
|
|
3285
|
+
const handleNodeStyleResolverChange = (0, import_react20.useCallback)(
|
|
3226
3286
|
(resolver) => setResolveStyle(() => resolver),
|
|
3227
3287
|
[]
|
|
3228
3288
|
);
|
|
3229
|
-
const panelRef = (0,
|
|
3230
|
-
const obscuredInset = (0,
|
|
3289
|
+
const panelRef = (0, import_react20.useRef)(null);
|
|
3290
|
+
const obscuredInset = (0, import_ui26.useObscuredInset)(panelRef);
|
|
3231
3291
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
3232
3292
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
3233
|
-
const treeNodes = (0,
|
|
3293
|
+
const treeNodes = (0, import_react20.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
|
|
3234
3294
|
const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
|
|
3235
3295
|
treeNodes,
|
|
3236
3296
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
3237
3297
|
resolveStyle
|
|
3238
3298
|
);
|
|
3239
|
-
const treeStyles = (0,
|
|
3240
|
-
const validSelectedGenreUuid = (0,
|
|
3241
|
-
const [selectionHistory, setSelectionHistory] = (0,
|
|
3299
|
+
const treeStyles = (0, import_react20.useMemo)(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
|
|
3300
|
+
const validSelectedGenreUuid = (0, import_genre_tree31.toGenreUuid)(selectedGenreUuid);
|
|
3301
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react20.useState)({ current: validSelectedGenreUuid, previous: null });
|
|
3242
3302
|
if (selectionHistory.current !== validSelectedGenreUuid) {
|
|
3243
3303
|
setSelectionHistory({ current: validSelectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
3244
3304
|
}
|
|
@@ -3264,7 +3324,7 @@ function GenreTreeView({
|
|
|
3264
3324
|
const handleGenreSearchZoomIn = () => {
|
|
3265
3325
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
3266
3326
|
};
|
|
3267
|
-
const zoomInRef = (0,
|
|
3327
|
+
const zoomInRef = (0, import_react20.useRef)(void 0);
|
|
3268
3328
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
3269
3329
|
const genrePlaylist = selectedGenrePlaylist;
|
|
3270
3330
|
const criteria = genrePlaylist?.criteria;
|
|
@@ -3332,6 +3392,7 @@ function GenreTreeView({
|
|
|
3332
3392
|
activePanel = {
|
|
3333
3393
|
title: otherPanel.title,
|
|
3334
3394
|
content: otherPanel.content,
|
|
3395
|
+
cover: otherPanel.cover,
|
|
3335
3396
|
aside: otherPanel.extraContent,
|
|
3336
3397
|
expanded: otherPanel.expanded ?? false,
|
|
3337
3398
|
onExpandedChange: otherPanel.onExpandedChange,
|
|
@@ -3356,7 +3417,7 @@ function GenreTreeView({
|
|
|
3356
3417
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
3357
3418
|
} : void 0;
|
|
3358
3419
|
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3359
|
-
|
|
3420
|
+
import_ui26.IconButton,
|
|
3360
3421
|
{
|
|
3361
3422
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
3362
3423
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
@@ -3422,13 +3483,16 @@ function GenreTreeView({
|
|
|
3422
3483
|
onPrevious: handlePrevious,
|
|
3423
3484
|
onQueryChange: onSearchQueryChange,
|
|
3424
3485
|
onFocus: onSearchFocus,
|
|
3486
|
+
trailingAction: searchTrailingAction,
|
|
3487
|
+
trailingActionBreakpoint: "md",
|
|
3488
|
+
fullWidthBreakpoint: searchFullWidthBreakpoint,
|
|
3425
3489
|
extraGroups: extraSearchGroups
|
|
3426
3490
|
}
|
|
3427
3491
|
),
|
|
3428
3492
|
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3429
3493
|
/* @__PURE__ */ (0, import_jsx_runtime52.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: [
|
|
3430
3494
|
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3431
|
-
|
|
3495
|
+
import_ui26.SegmentedControl,
|
|
3432
3496
|
{
|
|
3433
3497
|
"aria-label": "Tree view mode",
|
|
3434
3498
|
value: viewMode,
|
|
@@ -3448,7 +3512,7 @@ function GenreTreeView({
|
|
|
3448
3512
|
role: "group",
|
|
3449
3513
|
"aria-label": "Tree display options",
|
|
3450
3514
|
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3451
|
-
|
|
3515
|
+
import_ui26.Button,
|
|
3452
3516
|
{
|
|
3453
3517
|
variant: allowWheelRotation ? "default" : "outline",
|
|
3454
3518
|
size: "sm",
|
|
@@ -3459,7 +3523,7 @@ function GenreTreeView({
|
|
|
3459
3523
|
}
|
|
3460
3524
|
),
|
|
3461
3525
|
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3462
|
-
|
|
3526
|
+
import_ui26.IconTextButton,
|
|
3463
3527
|
{
|
|
3464
3528
|
icon: import_lucide_react17.Plus,
|
|
3465
3529
|
text: "Add root",
|
|
@@ -3472,12 +3536,21 @@ function GenreTreeView({
|
|
|
3472
3536
|
"div",
|
|
3473
3537
|
{
|
|
3474
3538
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3475
|
-
className: [
|
|
3539
|
+
className: [
|
|
3540
|
+
"genre-tree-view relative flex flex-col h-full",
|
|
3541
|
+
placement === "regions" && REGIONS_CLASS,
|
|
3542
|
+
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))]",
|
|
3543
|
+
className2
|
|
3544
|
+
].filter(Boolean).join(" "),
|
|
3476
3545
|
children: [
|
|
3477
3546
|
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3478
3547
|
"div",
|
|
3479
3548
|
{
|
|
3480
|
-
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ?
|
|
3549
|
+
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? [
|
|
3550
|
+
"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",
|
|
3551
|
+
searchFullWidthBreakpoint === "sm" ? "max-sm:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))]" : "max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))]",
|
|
3552
|
+
searchTrailingAction ? "max-md:top-[max(var(--layout-panel-inset),env(safe-area-inset-top))]" : "max-md:top-[var(--layout-band-top)]"
|
|
3553
|
+
].join(" ") : "left-5 top-5"}`,
|
|
3481
3554
|
children: actions
|
|
3482
3555
|
}
|
|
3483
3556
|
),
|
|
@@ -3607,11 +3680,16 @@ function GenreTreeView({
|
|
|
3607
3680
|
}
|
|
3608
3681
|
) }),
|
|
3609
3682
|
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
3610
|
-
|
|
3683
|
+
GenreOverviewDetailPanel,
|
|
3611
3684
|
{
|
|
3685
|
+
genreUuid: !otherPanel && genreContent.name && selectedGenreUuid ? (0, import_genre_tree31.toGenreUuid)(selectedGenreUuid) : null,
|
|
3686
|
+
scope,
|
|
3687
|
+
getBackendBaseUrl,
|
|
3688
|
+
criteriaOverviewSchema,
|
|
3612
3689
|
ref: panelRef,
|
|
3613
3690
|
side: "left",
|
|
3614
3691
|
title: activePanel?.title ?? "",
|
|
3692
|
+
cover: activePanel?.cover,
|
|
3615
3693
|
titleInSearch: !isLoading && !isError,
|
|
3616
3694
|
headerAccent: activePanel?.headerAccent,
|
|
3617
3695
|
headerActions: expandButton,
|
|
@@ -3637,13 +3715,13 @@ function GenreTreeView({
|
|
|
3637
3715
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3638
3716
|
|
|
3639
3717
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3640
|
-
var
|
|
3718
|
+
var import_react21 = require("react");
|
|
3641
3719
|
var import_lucide_react19 = require("lucide-react");
|
|
3642
|
-
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3643
3720
|
var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3721
|
+
var import_genre_tree34 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3644
3722
|
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
3645
3723
|
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
3646
|
-
var useBrowserLayoutEffect = typeof window === "undefined" ?
|
|
3724
|
+
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react21.useEffect : import_react21.useLayoutEffect;
|
|
3647
3725
|
function TrackListSidebar({
|
|
3648
3726
|
className: className2 = "",
|
|
3649
3727
|
renderDuration,
|
|
@@ -3655,18 +3733,18 @@ function TrackListSidebar({
|
|
|
3655
3733
|
renderArtistLink,
|
|
3656
3734
|
renderSongLink
|
|
3657
3735
|
}) {
|
|
3658
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3736
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree33.useTrackList)();
|
|
3659
3737
|
const trackNoun = (0, import_track_noun4.useTrackNoun)();
|
|
3660
|
-
const trackListRef = (0,
|
|
3661
|
-
const previousOriginRef = (0,
|
|
3662
|
-
const handledAutoAdvanceRequestIdRef = (0,
|
|
3663
|
-
const shouldFollowPlaybackRef = (0,
|
|
3664
|
-
const programmaticScrollTopRef = (0,
|
|
3665
|
-
const userScrollIntentRef = (0,
|
|
3666
|
-
const userScrollIntentTimerRef = (0,
|
|
3667
|
-
const loadMoreSentinelRef = (0,
|
|
3738
|
+
const trackListRef = (0, import_react21.useRef)(null);
|
|
3739
|
+
const previousOriginRef = (0, import_react21.useRef)(trackList?.origin ?? null);
|
|
3740
|
+
const handledAutoAdvanceRequestIdRef = (0, import_react21.useRef)(null);
|
|
3741
|
+
const shouldFollowPlaybackRef = (0, import_react21.useRef)(true);
|
|
3742
|
+
const programmaticScrollTopRef = (0, import_react21.useRef)(null);
|
|
3743
|
+
const userScrollIntentRef = (0, import_react21.useRef)(false);
|
|
3744
|
+
const userScrollIntentTimerRef = (0, import_react21.useRef)(null);
|
|
3745
|
+
const loadMoreSentinelRef = (0, import_react21.useRef)(null);
|
|
3668
3746
|
const hasMore = !!trackList && trackList.nextPage !== null;
|
|
3669
|
-
(0,
|
|
3747
|
+
(0, import_react21.useEffect)(() => {
|
|
3670
3748
|
const sentinel = loadMoreSentinelRef.current;
|
|
3671
3749
|
if (!hasMore || !sentinel) return;
|
|
3672
3750
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -3686,11 +3764,11 @@ function TrackListSidebar({
|
|
|
3686
3764
|
programmaticScrollTopRef.current = null;
|
|
3687
3765
|
if (trackListRef.current) trackListRef.current.scrollTop = 0;
|
|
3688
3766
|
}, [trackList?.origin]);
|
|
3689
|
-
(0,
|
|
3767
|
+
(0, import_react21.useEffect)(() => {
|
|
3690
3768
|
const list = trackListRef.current;
|
|
3691
3769
|
if (!isCollapsed && list?.scrollTop === 0) shouldFollowPlaybackRef.current = true;
|
|
3692
3770
|
}, [isCollapsed]);
|
|
3693
|
-
(0,
|
|
3771
|
+
(0, import_react21.useEffect)(
|
|
3694
3772
|
() => () => {
|
|
3695
3773
|
if (userScrollIntentTimerRef.current !== null) clearTimeout(userScrollIntentTimerRef.current);
|
|
3696
3774
|
},
|
|
@@ -3758,7 +3836,7 @@ function TrackListSidebar({
|
|
|
3758
3836
|
/* @__PURE__ */ (0, import_jsx_runtime53.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_runtime53.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3759
3837
|
] }),
|
|
3760
3838
|
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
|
|
3761
|
-
trackList.origin.type ===
|
|
3839
|
+
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 `,
|
|
3762
3840
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3763
3841
|
] })
|
|
3764
3842
|
] }),
|
|
@@ -3806,15 +3884,16 @@ function TrackListSidebar({
|
|
|
3806
3884
|
}
|
|
3807
3885
|
|
|
3808
3886
|
// src/genre-tree/PlayerPanel.tsx
|
|
3809
|
-
var
|
|
3887
|
+
var import_react22 = require("react");
|
|
3888
|
+
var import_lucide_react21 = require("lucide-react");
|
|
3810
3889
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3811
3890
|
var import_player14 = require("@behindthemusictree/app-kit/player");
|
|
3812
|
-
var
|
|
3813
|
-
var
|
|
3891
|
+
var import_genre_tree36 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3892
|
+
var import_ui27 = require("@behindthemusictree/ui");
|
|
3814
3893
|
|
|
3815
3894
|
// src/genre-tree/PlayerControls.tsx
|
|
3816
3895
|
var import_lucide_react20 = require("lucide-react");
|
|
3817
|
-
var
|
|
3896
|
+
var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3818
3897
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3819
3898
|
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
3820
3899
|
function PlayerControls() {
|
|
@@ -3828,7 +3907,7 @@ function PlayerControls() {
|
|
|
3828
3907
|
setVolume,
|
|
3829
3908
|
volume
|
|
3830
3909
|
} = (0, import_player13.usePlayer)();
|
|
3831
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3910
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree35.useTrackList)();
|
|
3832
3911
|
const currentTime = (0, import_player13.useCurrentTime)();
|
|
3833
3912
|
const isBusy = isLoading || playState === import_player13.PlayStates.LOADING;
|
|
3834
3913
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
@@ -3842,7 +3921,7 @@ function PlayerControls() {
|
|
|
3842
3921
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3843
3922
|
children: [
|
|
3844
3923
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3845
|
-
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { "aria-label": "Elapsed time", children: (0,
|
|
3924
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree35.formatTime)(safeCurrentTime) }),
|
|
3846
3925
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
3847
3926
|
"input",
|
|
3848
3927
|
{
|
|
@@ -3857,7 +3936,7 @@ function PlayerControls() {
|
|
|
3857
3936
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3858
3937
|
}
|
|
3859
3938
|
),
|
|
3860
|
-
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { "aria-label": "Duration", children: (0,
|
|
3939
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree35.formatTime)(safeDuration) })
|
|
3861
3940
|
] }),
|
|
3862
3941
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3863
3942
|
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
@@ -3935,14 +4014,14 @@ function PlayerPanel({
|
|
|
3935
4014
|
renderArtistLink,
|
|
3936
4015
|
renderSongLink
|
|
3937
4016
|
}) {
|
|
3938
|
-
const { playerTrackObject, retryPlayback } = (0, import_player14.usePlayer)();
|
|
3939
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3940
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0,
|
|
4017
|
+
const { playerTrackObject, retryPlayback, stop } = (0, import_player14.usePlayer)();
|
|
4018
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree36.useTrackListSidebarVisibility)();
|
|
4019
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react22.useState)("");
|
|
3941
4020
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3942
|
-
(0,
|
|
4021
|
+
(0, import_react22.useEffect)(() => {
|
|
3943
4022
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3944
4023
|
}, [loadedTrackTitle]);
|
|
3945
|
-
const [uncontrolledExtended, setUncontrolledExtended] = (0,
|
|
4024
|
+
const [uncontrolledExtended, setUncontrolledExtended] = (0, import_react22.useState)(false);
|
|
3946
4025
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3947
4026
|
const setIsExtendRequested = (next) => {
|
|
3948
4027
|
setUncontrolledExtended(next);
|
|
@@ -3977,7 +4056,7 @@ function PlayerPanel({
|
|
|
3977
4056
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3978
4057
|
children: [
|
|
3979
4058
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
3980
|
-
|
|
4059
|
+
import_ui27.PanelEdgeTab,
|
|
3981
4060
|
{
|
|
3982
4061
|
side: "right",
|
|
3983
4062
|
edge: tabEdge,
|
|
@@ -3995,7 +4074,10 @@ function PlayerPanel({
|
|
|
3995
4074
|
),
|
|
3996
4075
|
children: [
|
|
3997
4076
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3998
|
-
/* @__PURE__ */ (0, import_jsx_runtime55.
|
|
4077
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: [
|
|
4078
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }),
|
|
4079
|
+
playerTrackObject && /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_ui27.IconButton, { "aria-label": "Close player", onClick: stop, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_lucide_react21.X, { "aria-hidden": "true", className: "h-4 w-4" }) })
|
|
4080
|
+
] }),
|
|
3999
4081
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
4000
4082
|
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
4001
4083
|
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
@@ -4033,7 +4115,7 @@ function PlayerPanel({
|
|
|
4033
4115
|
// src/genre-tree/Rating.tsx
|
|
4034
4116
|
var import_uuid = require("uuid");
|
|
4035
4117
|
var import_ai = require("react-icons/ai");
|
|
4036
|
-
var
|
|
4118
|
+
var import_genre_tree37 = require("@behindthemusictree/app-kit/genre-tree");
|
|
4037
4119
|
var import_jsx_runtime56 = require("react/jsx-runtime");
|
|
4038
4120
|
function Rating({ rating, handleChange }) {
|
|
4039
4121
|
const instanceId = (0, import_uuid.v4)();
|
|
@@ -4045,7 +4127,7 @@ function Rating({ rating, handleChange }) {
|
|
|
4045
4127
|
className: "w-6 h-6 opacity-0",
|
|
4046
4128
|
type: "radio",
|
|
4047
4129
|
name: "rating",
|
|
4048
|
-
value: i === 0 ?
|
|
4130
|
+
value: i === 0 ? import_genre_tree37.FORM_RATING_NULL_VALUE : i,
|
|
4049
4131
|
checked: rating === (i === 0 ? null : i),
|
|
4050
4132
|
onChange: handleChange
|
|
4051
4133
|
}
|
|
@@ -4054,7 +4136,7 @@ function Rating({ rating, handleChange }) {
|
|
|
4054
4136
|
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
4055
4137
|
import_ai.AiOutlineStar,
|
|
4056
4138
|
{
|
|
4057
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
4139
|
+
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)]"}`
|
|
4058
4140
|
}
|
|
4059
4141
|
),
|
|
4060
4142
|
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { className: "flex-grow w-8" }),
|
|
@@ -4069,8 +4151,8 @@ function Rating({ rating, handleChange }) {
|
|
|
4069
4151
|
}
|
|
4070
4152
|
|
|
4071
4153
|
// src/genre-tree/EntityDetailPanel.tsx
|
|
4072
|
-
var
|
|
4073
|
-
var
|
|
4154
|
+
var import_react23 = require("react");
|
|
4155
|
+
var import_ui28 = require("@behindthemusictree/ui");
|
|
4074
4156
|
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
4075
4157
|
function EntityDetailPanel({
|
|
4076
4158
|
scope,
|
|
@@ -4088,8 +4170,8 @@ function EntityDetailPanel({
|
|
|
4088
4170
|
edgeTab,
|
|
4089
4171
|
...navigation
|
|
4090
4172
|
}) {
|
|
4091
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
4092
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
4173
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react23.useState)(null);
|
|
4174
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react23.useState)(null);
|
|
4093
4175
|
const panel = useEntityPanel({
|
|
4094
4176
|
scope,
|
|
4095
4177
|
getBackendBaseUrl,
|
|
@@ -4114,10 +4196,11 @@ function EntityDetailPanel({
|
|
|
4114
4196
|
});
|
|
4115
4197
|
if (!panel) return null;
|
|
4116
4198
|
return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
4117
|
-
|
|
4199
|
+
import_ui28.DetailPanel,
|
|
4118
4200
|
{
|
|
4119
4201
|
side: "left",
|
|
4120
4202
|
title: panel.title,
|
|
4203
|
+
cover: panel.cover,
|
|
4121
4204
|
titleInSearch: true,
|
|
4122
4205
|
onClose: panel.onClose,
|
|
4123
4206
|
collapsed,
|