@behindthemusictree/app-kit-ui 32.0.3 → 33.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QGGVPIXY.js → chunk-2M4Q2IJU.js} +28 -16
- package/dist/chunk-2M4Q2IJU.js.map +1 -0
- package/dist/{chunk-XUC4UJDQ.js → chunk-K4WMHUNM.js} +7 -4
- package/dist/chunk-K4WMHUNM.js.map +1 -0
- package/dist/genre-tree/index.cjs +25 -13
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +47 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/popup/index.cjs +8 -5
- package/dist/popup/index.cjs.map +1 -1
- package/dist/popup/index.d.ts +4 -2
- package/dist/popup/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-QGGVPIXY.js.map +0 -1
- package/dist/chunk-XUC4UJDQ.js.map +0 -1
|
@@ -117,18 +117,21 @@ function AuthErrorPopup({ message, onClose, ...rest }) {
|
|
|
117
117
|
|
|
118
118
|
// src/popup/NetworkErrorPopup.tsx
|
|
119
119
|
import { AlertTriangle } from "lucide-react";
|
|
120
|
+
import { ErrorCode } from "@behindthemusictree/app-kit/transport";
|
|
120
121
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
121
|
-
|
|
122
|
+
var OFFLINE_MESSAGE = "It seems that you are not connected to the internet. Please check your connection and try again.";
|
|
123
|
+
var UNREACHABLE_MESSAGE = "We couldn't reach the server. Please try again in a moment.";
|
|
124
|
+
function NetworkErrorPopup({ errorCode, ...rest }) {
|
|
122
125
|
return /* @__PURE__ */ jsx3(
|
|
123
126
|
BasePopup,
|
|
124
127
|
{
|
|
125
|
-
...
|
|
128
|
+
...rest,
|
|
126
129
|
title: "Network Error",
|
|
127
130
|
isDismissable: false,
|
|
128
131
|
icon: AlertTriangle,
|
|
129
132
|
children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center space-y-6 py-4", children: [
|
|
130
133
|
/* @__PURE__ */ jsx3(AlertTriangle, { className: "h-16 w-16 text-[var(--color-neutral-600)]", strokeWidth: 1.5 }),
|
|
131
|
-
/* @__PURE__ */ jsx3("p", { className: "text-center text-[var(--color-neutral-700)]", children:
|
|
134
|
+
/* @__PURE__ */ jsx3("p", { className: "text-center text-[var(--color-neutral-700)]", children: errorCode === ErrorCode.NETWORK_OFFLINE ? OFFLINE_MESSAGE : UNREACHABLE_MESSAGE })
|
|
132
135
|
] })
|
|
133
136
|
}
|
|
134
137
|
);
|
|
@@ -271,4 +274,4 @@ export {
|
|
|
271
274
|
AuthPopup,
|
|
272
275
|
SpotifyAuthErrorPopup
|
|
273
276
|
};
|
|
274
|
-
//# sourceMappingURL=chunk-
|
|
277
|
+
//# sourceMappingURL=chunk-K4WMHUNM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/popup/TrackUploadPopup.tsx","../src/popup/AuthErrorPopup.tsx","../src/popup/NetworkErrorPopup.tsx","../src/popup/AuthPopup.tsx","../src/popup/SpotifyAuthErrorPopup.tsx"],"sourcesContent":["\"use client\";\n\nimport { MdError, MdCheckCircle, MdUpload } from \"react-icons/md\";\nimport { useTrackUpload, type UploadStatus, type UseTrackUploadOptions } from \"@behindthemusictree/app-kit/popup\";\nimport { BasePopup, BasePopupProps } from \"./BasePopup\";\nimport { capitalize, useTrackNoun } from \"@behindthemusictree/app-kit/track-noun\";\n\ntype TrackUploadPopupProps = Omit<BasePopupProps, \"title\" | \"children\" | \"icon\" | \"isDismissable\"> &\n UseTrackUploadOptions & {\n onClose?: () => void;\n };\n\nfunction getStatusIcon(status: UploadStatus) {\n switch (status) {\n case \"success\":\n return <MdCheckCircle className=\"text-[var(--color-green-500)]\" size={20} />;\n case \"error\":\n return <MdError className=\"text-[var(--color-red-500)]\" size={20} />;\n case \"uploading\":\n return <MdUpload className=\"text-[var(--color-blue-500)] animate-pulse\" size={20} />;\n default:\n return <div className=\"w-5 h-5 rounded-full border-2 border-[var(--color-neutral-300)]\" />;\n }\n}\n\nfunction getStatusText(status: UploadStatus) {\n switch (status) {\n case \"success\":\n return \"Uploaded\";\n case \"error\":\n return \"Failed\";\n case \"uploading\":\n return \"Uploading...\";\n default:\n return \"Pending\";\n }\n}\n\nexport default function TrackUploadPopup({\n files,\n genre,\n onProcessFile,\n onComplete,\n onClose,\n uploadTimeoutMs,\n ...rest\n}: TrackUploadPopupProps) {\n const { items, isUploading, allComplete, successfulCount, errorCount } = useTrackUpload({\n files,\n genre,\n onProcessFile,\n onComplete,\n uploadTimeoutMs,\n });\n\n const displayTotal = items.length > 0 ? items.length : files.length;\n const trackNoun = useTrackNoun();\n\n return (\n <BasePopup\n {...rest}\n onClose={onClose}\n title={`Upload ${capitalize(trackNoun.plural)} (${successfulCount}/${displayTotal})`}\n isDismissable\n showOkButton\n okButtonText=\"OK\"\n okButtonDisabled={isUploading || !allComplete}\n onOk={onClose}\n >\n <div className=\"space-y-4\">\n {items.map((item) => (\n <div key={item.id} className=\"border rounded-lg p-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"flex items-center space-x-3\">\n {getStatusIcon(item.status)}\n <div>\n <div className=\"font-medium text-sm truncate max-w-xs\" title={item.file.name}>\n {item.file.name}\n </div>\n <div className=\"text-xs text-[var(--color-neutral-500)]\">{(item.file.size / 1024 / 1024).toFixed(1)} MB</div>\n </div>\n </div>\n <div className=\"text-sm text-[var(--color-neutral-600)]\">{getStatusText(item.status)}</div>\n </div>\n\n {item.status === \"uploading\" && (\n <div className=\"w-full bg-[var(--color-neutral-200)] rounded-full h-2\">\n <div\n className=\"bg-[var(--color-blue-500)] h-2 rounded-full transition-all duration-300\"\n style={{ width: `${item.progress}%` }}\n />\n </div>\n )}\n\n {item.status === \"error\" && item.error && (\n <div className=\"text-[var(--color-red-500)] text-sm mt-2\">\n {item.error.name === \"InvalidInputError\"\n ? \"Upload failed due to invalid file data. Please check your file and try again.\"\n : item.error.name === \"ZodError\"\n ? \"Upload failed due to a server error. Please try again later.\"\n : item.error.message}\n </div>\n )}\n </div>\n ))}\n\n {allComplete && (\n <div className=\"flex justify-between items-center\">\n <div className=\"text-sm text-[var(--color-neutral-600)]\">\n {successfulCount} successful, {errorCount} failed\n </div>\n </div>\n )}\n </div>\n </BasePopup>\n );\n}\n","\"use client\";\n\nimport { AlertCircle } from \"lucide-react\";\nimport { BasePopup, BasePopupProps } from \"./BasePopup\";\nimport { Button } from \"@behindthemusictree/ui\";\n\ntype AuthErrorPopupProps = Omit<BasePopupProps, \"title\" | \"children\" | \"icon\" | \"isDismissable\"> & {\n message: string;\n onClose: () => void;\n};\n\nexport default function AuthErrorPopup({ message, onClose, ...rest }: AuthErrorPopupProps) {\n return (\n <BasePopup\n {...rest}\n title=\"Sign-in error\"\n isDismissable\n icon={AlertCircle}\n children={\n <div className=\"flex flex-col items-center space-y-6 py-4\">\n <p className=\"text-center text-lg font-medium text-[var(--color-text)]\">{message}</p>\n <Button onClick={onClose} variant=\"secondary\" className=\"w-full max-w-xs\">\n Close\n </Button>\n </div>\n }\n />\n );\n}\n","\"use client\";\n\nimport { AlertTriangle } from \"lucide-react\";\nimport { BasePopup, BasePopupProps } from \"./BasePopup\";\nimport { ErrorCode } from \"@behindthemusictree/app-kit/transport\";\n\ntype NetworkErrorPopupProps = Omit<BasePopupProps, \"title\" | \"children\" | \"icon\" | \"isDismissable\"> & {\n errorCode: ErrorCode;\n};\n\nconst OFFLINE_MESSAGE = \"It seems that you are not connected to the internet. Please check your connection and try again.\";\nconst UNREACHABLE_MESSAGE = \"We couldn't reach the server. Please try again in a moment.\";\n\nexport default function NetworkErrorPopup({ errorCode, ...rest }: NetworkErrorPopupProps) {\n return (\n <BasePopup\n {...rest}\n title=\"Network Error\"\n isDismissable={false}\n icon={AlertTriangle}\n children={\n <div className=\"flex flex-col items-center space-y-6 py-4\">\n <AlertTriangle className=\"h-16 w-16 text-[var(--color-neutral-600)]\" strokeWidth={1.5} />\n <p className=\"text-center text-[var(--color-neutral-700)]\">\n {errorCode === ErrorCode.NETWORK_OFFLINE ? OFFLINE_MESSAGE : UNREACHABLE_MESSAGE}\n </p>\n </div>\n }\n />\n );\n}\n","\"use client\";\n\nimport { ReactNode } from \"react\";\nimport { User } from \"lucide-react\";\nimport { BasePopup, BasePopupProps } from \"./BasePopup\";\nimport { Button } from \"@behindthemusictree/ui\";\nimport { FaSpotify } from \"react-icons/fa\";\nimport { FcGoogle } from \"react-icons/fc\";\n\ntype AuthPopupProps = Omit<BasePopupProps, \"title\" | \"children\" | \"icon\" | \"isDismissable\"> & {\n handleSpotifyOAuth: (redirectAfterAuthPath?: string) => void;\n handleGoogleOAuth?: (redirectAfterAuthPath?: string) => void;\n redirectAfterAuthPath?: string;\n spotifyOnly?: boolean;\n message?: string;\n /**\n * Body copy shown when `spotifyOnly` is true. Wording (app name, feature description) is\n * app-specific, so each consuming app supplies its own instead of this package assuming one.\n */\n spotifyOnlyDescription: ReactNode;\n /** Body copy shown when `spotifyOnly` is false, for the same reason as `spotifyOnlyDescription`. */\n defaultDescription: ReactNode;\n};\n\nexport default function AuthPopup({\n handleSpotifyOAuth,\n handleGoogleOAuth,\n redirectAfterAuthPath,\n spotifyOnly,\n message,\n spotifyOnlyDescription,\n defaultDescription,\n ...rest\n}: AuthPopupProps) {\n const showGoogle = !spotifyOnly && handleGoogleOAuth;\n\n return (\n <BasePopup\n {...rest}\n title={spotifyOnly ? \"Connect with Spotify\" : \"Sign in\"}\n isDismissable={false}\n icon={User}\n type=\"auth\"\n children={\n <div className=\"flex flex-col items-center space-y-7\">\n <div className=\"px-2 text-center\">\n {message && <p className=\"mb-3 text-base font-medium leading-relaxed text-amber-200\">{message}</p>}\n <p className=\"text-lg font-medium leading-relaxed text-white\">\n {spotifyOnly ? spotifyOnlyDescription : defaultDescription}\n </p>\n </div>\n <div className=\"flex w-full flex-col items-center gap-3\">\n <Button\n onClick={() => handleSpotifyOAuth(redirectAfterAuthPath)}\n className=\"relative mb-2 w-fit transform bg-green-600 px-8 py-3 text-white shadow-lg transition-all duration-200 hover:scale-[1.02] hover:bg-green-700 hover:shadow-xl\"\n >\n <span className=\"flex items-center justify-center\">\n <FaSpotify className=\"absolute left-6 text-3xl\" />\n <span className=\"w-full pl-14 pr-8 text-center text-lg font-medium\">Sign in with Spotify</span>\n </span>\n </Button>\n {showGoogle && (\n <Button\n variant=\"outline\"\n onClick={() => handleGoogleOAuth!(redirectAfterAuthPath)}\n className=\"relative w-fit transform border-white bg-white px-8 py-3 text-black shadow-lg transition-all duration-200 hover:scale-[1.02] hover:bg-[var(--color-neutral-100)] hover:shadow-xl\"\n >\n <span className=\"flex items-center justify-center\">\n <FcGoogle className=\"absolute left-6 text-2xl\" />\n <span className=\"w-full pl-14 pr-8 text-center text-lg font-medium text-black\">\n Sign in with Google\n </span>\n </span>\n </Button>\n )}\n </div>\n </div>\n }\n />\n );\n}\n","\"use client\";\n\nimport { FaSpotify } from \"react-icons/fa\";\nimport { BasePopup, BasePopupProps } from \"./BasePopup\";\nimport { Button } from \"@behindthemusictree/ui\";\nimport { User } from \"lucide-react\";\nimport { getSpotifyAllowlistMailtoHref } from \"@behindthemusictree/app-kit/transport\";\n\ntype SpotifyAuthErrorPopupProps = Omit<BasePopupProps, \"title\" | \"children\" | \"icon\" | \"isDismissable\"> & {\n message: string;\n details?: string;\n onClose: () => void;\n /**\n * Support contact email used to build the allowlist-request mailto link. Passed by the\n * consuming app (from whatever env var / config it uses) so this package doesn't assume a\n * specific env var name.\n */\n contactEmail?: string | null;\n};\n\nexport default function SpotifyAuthErrorPopup({\n message,\n details,\n onClose,\n contactEmail = null,\n ...rest\n}: SpotifyAuthErrorPopupProps) {\n const requestAccessHref = getSpotifyAllowlistMailtoHref(contactEmail);\n\n return (\n <BasePopup\n {...rest}\n title=\"Authentication Failed\"\n isDismissable\n icon={User}\n children={\n <div className=\"flex flex-col items-center space-y-8 py-4\">\n <FaSpotify className=\"text-[#1DB954] text-7xl\" />\n <div className=\"space-y-3 text-center\">\n <p className=\"text-xl font-medium text-[var(--color-text)]\">{message}</p>\n {details && (\n <p className=\"text-sm text-[var(--color-neutral-500)] bg-[var(--color-neutral-100)] p-3 rounded-lg border border-[var(--color-neutral-100)]\">{details}</p>\n )}\n <div className=\"space-y-2 text-center\">\n <p className=\"text-base text-[var(--color-neutral-700)]\">\n The Spotify app is in development mode, so only allowlisted accounts can connect.\n </p>\n {requestAccessHref && contactEmail ? (\n <p className=\"text-base text-[var(--color-neutral-700)]\">\n Request access by emailing your{\" \"}\n <a\n href={requestAccessHref}\n className=\"text-[#1DB954] font-medium underline hover:no-underline focus:outline-none focus:ring-2 focus:ring-[#1DB954] focus:ring-offset-1 rounded\"\n >\n Spotify full name and Spotify email address\n </a>{\" \"}\n to{\" \"}\n <a\n href={requestAccessHref}\n className=\"text-[#1DB954] font-medium underline hover:no-underline focus:outline-none focus:ring-2 focus:ring-[#1DB954] focus:ring-offset-1 rounded\"\n >\n {contactEmail}\n </a>\n .\n </p>\n ) : (\n <p className=\"text-base text-[var(--color-neutral-700)]\">To request access, contact the app owner.</p>\n )}\n </div>\n </div>\n <Button\n onClick={onClose}\n className=\"bg-[#1DB954] hover:bg-[#1DB954]/90 text-white w-full max-w-xs transform transition-all duration-200 hover:scale-[1.02] shadow-lg hover:shadow-xl\"\n >\n <FaSpotify className=\"mr-2 text-lg\" />\n Try Again\n </Button>\n </div>\n }\n />\n );\n}\n"],"mappings":"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","names":["jsx","jsxs","jsx","jsxs","Button","jsx","jsxs","FaSpotify","Button","User","jsx","jsxs","FaSpotify"]}
|
|
@@ -784,7 +784,7 @@ function GenreSearch({
|
|
|
784
784
|
onPick: handlePick,
|
|
785
785
|
status,
|
|
786
786
|
loading: loading ? "Searching\u2026" : void 0,
|
|
787
|
-
inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md`,
|
|
787
|
+
inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md focus-visible:ring-0`,
|
|
788
788
|
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
789
789
|
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
790
790
|
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
@@ -1285,8 +1285,9 @@ function GenrePlaylistTracks({
|
|
|
1285
1285
|
const trackNoun = (0, import_track_noun.useTrackNoun)();
|
|
1286
1286
|
const { uuid } = genrePlaylist;
|
|
1287
1287
|
const { fetch } = (0, import_transport3.useFetchWrapper)(getBackendBaseUrl);
|
|
1288
|
+
const playback = (0, import_transport3.usePlaybackContext)();
|
|
1288
1289
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1289
|
-
queryKey: scope === "me" ? import_genre_tree4.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid),
|
|
1290
|
+
queryKey: [...scope === "me" ? import_genre_tree4.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : import_genre_tree5.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), ...playback.key ? [playback.key] : []],
|
|
1290
1291
|
queryFn: async ({ pageParam }) => {
|
|
1291
1292
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1292
1293
|
const response = await fetch(
|
|
@@ -1301,7 +1302,7 @@ function GenrePlaylistTracks({
|
|
|
1301
1302
|
},
|
|
1302
1303
|
initialPageParam: scope === "me" ? 1 : "",
|
|
1303
1304
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1304
|
-
enabled: scope !== "me" || sessionRestored && !!session?.accessToken
|
|
1305
|
+
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1305
1306
|
});
|
|
1306
1307
|
const [sentinel, setSentinel] = (0, import_react11.useState)(null);
|
|
1307
1308
|
(0, import_react11.useEffect)(() => {
|
|
@@ -2573,7 +2574,10 @@ function GenreTreeView({
|
|
|
2573
2574
|
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2574
2575
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2575
2576
|
const criteria = genrePlaylist?.criteria;
|
|
2576
|
-
if (!genrePlaylist || !criteria) return { primary:
|
|
2577
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2578
|
+
family,
|
|
2579
|
+
ancestors
|
|
2580
|
+
] }) };
|
|
2577
2581
|
const genreUuid = criteria.uuid;
|
|
2578
2582
|
const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist.parent?.uuid);
|
|
2579
2583
|
const canToggleSide = !readOnly && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
@@ -2594,7 +2598,10 @@ function GenreTreeView({
|
|
|
2594
2598
|
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2595
2599
|
GenreInfoPanelBody,
|
|
2596
2600
|
{
|
|
2597
|
-
family,
|
|
2601
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2602
|
+
family,
|
|
2603
|
+
ancestors
|
|
2604
|
+
] }),
|
|
2598
2605
|
genreUuid,
|
|
2599
2606
|
genrePlaylist,
|
|
2600
2607
|
wikidataId: criteria.wikidataId,
|
|
@@ -2641,8 +2648,7 @@ function GenreTreeView({
|
|
|
2641
2648
|
}
|
|
2642
2649
|
),
|
|
2643
2650
|
renderDangerZone?.(node)
|
|
2644
|
-
] })
|
|
2645
|
-
aside: ancestors
|
|
2651
|
+
] })
|
|
2646
2652
|
};
|
|
2647
2653
|
};
|
|
2648
2654
|
const isRotationControlled = controlledAllowWheelRotation !== void 0;
|
|
@@ -2659,11 +2665,10 @@ function GenreTreeView({
|
|
|
2659
2665
|
compact: false
|
|
2660
2666
|
};
|
|
2661
2667
|
} else if (relations) {
|
|
2662
|
-
const { primary
|
|
2668
|
+
const { primary } = renderGenreBody(relations);
|
|
2663
2669
|
activePanel = {
|
|
2664
2670
|
title: relations.node.name,
|
|
2665
2671
|
content: primary,
|
|
2666
|
-
aside,
|
|
2667
2672
|
expanded: isExpanded,
|
|
2668
2673
|
onExpandedChange: changeExpanded,
|
|
2669
2674
|
onClose: clearSelection,
|
|
@@ -3040,12 +3045,14 @@ function GenreDetailPanel({
|
|
|
3040
3045
|
expanded,
|
|
3041
3046
|
onExpandedChange,
|
|
3042
3047
|
edgeTab,
|
|
3043
|
-
aside: ancestors,
|
|
3044
3048
|
children: [
|
|
3045
3049
|
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
3046
3050
|
GenreInfoPanelBody,
|
|
3047
3051
|
{
|
|
3048
|
-
family,
|
|
3052
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
3053
|
+
family,
|
|
3054
|
+
ancestors
|
|
3055
|
+
] }),
|
|
3049
3056
|
genreUuid: detail.uuid,
|
|
3050
3057
|
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3051
3058
|
wikidataId: detail.wikidataId,
|
|
@@ -3364,7 +3371,7 @@ function PlayerPanel({
|
|
|
3364
3371
|
renderArtistLink,
|
|
3365
3372
|
renderSongLink
|
|
3366
3373
|
}) {
|
|
3367
|
-
const { playerTrackObject } = (0, import_player13.usePlayer)();
|
|
3374
|
+
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3368
3375
|
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree24.useTrackListSidebarVisibility)();
|
|
3369
3376
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3370
3377
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
@@ -3427,7 +3434,12 @@ function PlayerPanel({
|
|
|
3427
3434
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3428
3435
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3429
3436
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3430
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime38.
|
|
3437
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3438
|
+
playerTrackObject.loadError,
|
|
3439
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3440
|
+
void retryPlayback();
|
|
3441
|
+
}, children: "Retry" })
|
|
3442
|
+
] })
|
|
3431
3443
|
] }),
|
|
3432
3444
|
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PlayerControls, {})
|
|
3433
3445
|
] }),
|