@behindthemusictree/app-kit-ui 39.0.0 → 40.0.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.
@@ -16,7 +16,7 @@ import {
16
16
  useGenreNodeRelations
17
17
  } from "@behindthemusictree/genre-tree-view";
18
18
  import { useGenreTreeViewState, useArtist as useArtist2, useUpdateGenre as useUpdateGenre2, genreUpdateMutationKey } from "@behindthemusictree/app-kit/genre-tree";
19
- import { usePopup as usePopup5 } from "@behindthemusictree/app-kit/popup";
19
+ import { usePopup as usePopup4 } from "@behindthemusictree/app-kit/popup";
20
20
 
21
21
  // src/genre-tree/playlist-tree/size-metric.ts
22
22
  import { genrePlaylistTrackCount } from "@behindthemusictree/app-kit/genre-tree";
@@ -30,7 +30,7 @@ var genrePlaylistSize = (genrePlaylist, metric) => {
30
30
  };
31
31
 
32
32
  // src/genre-tree/playlist-tree/toTreeNodes.ts
33
- function toTreeNodes(genrePlaylists, sizeMetric) {
33
+ function toTreeNodes(genrePlaylists, sizeMetric, focusUuids) {
34
34
  return genrePlaylists.map((genrePlaylist) => ({
35
35
  id: genrePlaylist.uuid,
36
36
  parentId: genrePlaylist.parent?.uuid ?? null,
@@ -41,7 +41,8 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
41
41
  side: genrePlaylist.criteria?.side ?? void 0,
42
42
  wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
43
43
  umbrella: genrePlaylist.isUmbrella,
44
- scene: genrePlaylist.scene ?? void 0
44
+ scene: genrePlaylist.scene ?? void 0,
45
+ muted: focusUuids ? !focusUuids.has(genrePlaylist.uuid) : void 0
45
46
  }));
46
47
  }
47
48
 
@@ -53,24 +54,27 @@ import {
53
54
  getGenreTreeColor
54
55
  } from "@behindthemusictree/genre-tree-view";
55
56
  import { usePlayer } from "@behindthemusictree/app-kit/player";
56
- import { usePopup } from "@behindthemusictree/app-kit/popup";
57
- import { ErrorCode } from "@behindthemusictree/app-kit/transport";
58
57
 
59
- // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
58
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
60
59
  import { useCallback, useMemo } from "react";
61
60
  import {
62
61
  TrackListOriginType,
62
+ TracksFailureAlreadyReportedError,
63
63
  useTrackList,
64
64
  GenreCatalogueCountsSchema,
65
65
  defaultGenreMusicSelection,
66
66
  genreMusicSelectionKey
67
67
  } from "@behindthemusictree/app-kit/genre-tree";
68
+ import { usePopup } from "@behindthemusictree/app-kit/popup";
69
+ import { AppError, ErrorCode } from "@behindthemusictree/app-kit/transport";
70
+ import { jsx } from "react/jsx-runtime";
68
71
  function useGenrePlaylistPlayback({
69
72
  scope,
70
73
  genrePlaylists,
71
74
  artist = null
72
75
  }) {
73
76
  const { trackList, playNewTrackListFromGenrePlaylist } = useTrackList();
77
+ const { showPopup } = usePopup();
74
78
  const origin = trackList?.origin;
75
79
  const playing = useMemo(() => {
76
80
  if (!origin || origin.type !== TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
@@ -82,14 +86,20 @@ function useGenrePlaylistPlayback({
82
86
  }, [origin, artist, genrePlaylists, scope]);
83
87
  const play = useCallback(async (playlist, requested) => {
84
88
  if (artist && !playlist.criteria) return;
85
- const selection = scope === "reference" && playlist.criteria !== null ? requested ?? defaultGenreMusicSelection(GenreCatalogueCountsSchema.parse(playlist)) : void 0;
86
- return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
87
- }, [artist, scope, playNewTrackListFromGenrePlaylist]);
89
+ try {
90
+ const selection = scope === "reference" && playlist.criteria !== null ? requested ?? defaultGenreMusicSelection(GenreCatalogueCountsSchema.parse(playlist)) : void 0;
91
+ await (selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope));
92
+ } catch (error) {
93
+ console.error("Failed to load genre playlist tracks:", error);
94
+ if (error instanceof TracksFailureAlreadyReportedError) return;
95
+ showPopup(/* @__PURE__ */ jsx(InternalErrorPopup, { errorCode: error instanceof AppError ? error.code : ErrorCode.CLIENT_UNKNOWN }));
96
+ }
97
+ }, [artist, scope, playNewTrackListFromGenrePlaylist, showPopup]);
88
98
  return useMemo(() => ({ playing, play }), [playing, play]);
89
99
  }
90
100
 
91
101
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
92
- import { jsx } from "react/jsx-runtime";
102
+ import { jsx as jsx2 } from "react/jsx-runtime";
93
103
  function GenrePlaylistTreePerRoot({
94
104
  scope,
95
105
  className: className2,
@@ -108,25 +118,16 @@ function GenrePlaylistTreePerRoot({
108
118
  viewportRestore,
109
119
  onViewportChange,
110
120
  playback,
121
+ focusUuids,
111
122
  ...hostPanel
112
123
  }) {
113
124
  const { isPlaying, setIsPlaying } = usePlayer();
114
125
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
115
126
  const { playing, play } = playback ?? defaultPlayback;
116
127
  const playingNodeId = playing?.uuid ?? null;
117
- const { showPopup } = usePopup();
118
128
  const nodes = useMemo2(
119
- () => genrePlaylistTreePerRoot.map((genrePlaylist) => ({
120
- id: genrePlaylist.uuid,
121
- parentId: genrePlaylist.parent?.uuid ?? null,
122
- name: genrePlaylist.name,
123
- itemCount: genrePlaylistTrackCount2(genrePlaylist),
124
- actionable: Boolean(genrePlaylist.criteria),
125
- side: genrePlaylist.criteria?.side ?? void 0,
126
- wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
127
- umbrella: genrePlaylist.isUmbrella
128
- })),
129
- [genrePlaylistTreePerRoot]
129
+ () => toTreeNodes(genrePlaylistTreePerRoot, "songs", focusUuids),
130
+ [genrePlaylistTreePerRoot, focusUuids]
130
131
  );
131
132
  const handlePlayPause = useCallback2(
132
133
  (nodeId) => {
@@ -139,18 +140,14 @@ function GenrePlaylistTreePerRoot({
139
140
  if (genrePlaylistTrackCount2(genrePlaylist) === 0) {
140
141
  return;
141
142
  }
142
- play(genrePlaylist).catch((error) => {
143
- console.error("Failed to load genre playlist tracks:", error);
144
- showPopup(/* @__PURE__ */ jsx(InternalErrorPopup, { errorCode: ErrorCode.CLIENT_UNKNOWN }));
145
- });
143
+ void play(genrePlaylist);
146
144
  },
147
145
  [
148
146
  genrePlaylistTreePerRoot,
149
147
  playingNodeId,
150
148
  isPlaying,
151
149
  setIsPlaying,
152
- play,
153
- showPopup
150
+ play
154
151
  ]
155
152
  );
156
153
  const handleAddChild = useCallback2(
@@ -173,7 +170,7 @@ function GenrePlaylistTreePerRoot({
173
170
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
174
171
  }
175
172
  }, []);
176
- return /* @__PURE__ */ jsx(
173
+ return /* @__PURE__ */ jsx2(
177
174
  GenreTree,
178
175
  {
179
176
  className: className2,
@@ -206,9 +203,7 @@ import {
206
203
  GenreTreeWheel
207
204
  } from "@behindthemusictree/genre-tree-view";
208
205
  import { usePlayer as usePlayer2 } from "@behindthemusictree/app-kit/player";
209
- import { usePopup as usePopup2 } from "@behindthemusictree/app-kit/popup";
210
- import { ErrorCode as ErrorCode2 } from "@behindthemusictree/app-kit/transport";
211
- import { jsx as jsx2 } from "react/jsx-runtime";
206
+ import { jsx as jsx3 } from "react/jsx-runtime";
212
207
  function GenrePlaylistTreeWheel({
213
208
  scope,
214
209
  className: className2,
@@ -229,24 +224,16 @@ function GenrePlaylistTreeWheel({
229
224
  viewportRestore,
230
225
  onViewportChange,
231
226
  playback,
227
+ focusUuids,
232
228
  ...hostPanel
233
229
  }) {
234
230
  const { isPlaying, setIsPlaying } = usePlayer2();
235
231
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
236
232
  const { playing, play } = playback ?? defaultPlayback;
237
233
  const playingNodeId = playing?.uuid ?? null;
238
- const { showPopup } = usePopup2();
239
234
  const nodes = useMemo3(
240
- () => genrePlaylists.map((genrePlaylist) => ({
241
- id: genrePlaylist.uuid,
242
- parentId: genrePlaylist.parent?.uuid ?? null,
243
- name: genrePlaylist.name,
244
- itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
245
- actionable: Boolean(genrePlaylist.criteria),
246
- side: genrePlaylist.criteria?.side ?? void 0,
247
- wikidataId: genrePlaylist.criteria?.wikidataId ?? null
248
- })),
249
- [genrePlaylists, sizeMetric]
235
+ () => toTreeNodes(genrePlaylists, sizeMetric, focusUuids),
236
+ [genrePlaylists, sizeMetric, focusUuids]
250
237
  );
251
238
  const handlePlayPause = useCallback3(
252
239
  (nodeId) => {
@@ -259,18 +246,14 @@ function GenrePlaylistTreeWheel({
259
246
  if (genrePlaylistTrackCount3(genrePlaylist) === 0) {
260
247
  return;
261
248
  }
262
- play(genrePlaylist).catch((error) => {
263
- console.error("Failed to load genre playlist tracks:", error);
264
- showPopup(/* @__PURE__ */ jsx2(InternalErrorPopup, { errorCode: ErrorCode2.CLIENT_UNKNOWN }));
265
- });
249
+ void play(genrePlaylist);
266
250
  },
267
251
  [
268
252
  genrePlaylists,
269
253
  playingNodeId,
270
254
  isPlaying,
271
255
  setIsPlaying,
272
- play,
273
- showPopup
256
+ play
274
257
  ]
275
258
  );
276
259
  const handleAddChild = useCallback3(
@@ -293,7 +276,7 @@ function GenrePlaylistTreeWheel({
293
276
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
294
277
  }
295
278
  }, []);
296
- return /* @__PURE__ */ jsx2(
279
+ return /* @__PURE__ */ jsx3(
297
280
  GenreTreeWheel,
298
281
  {
299
282
  className: className2,
@@ -328,7 +311,7 @@ import {
328
311
  GenreTreeWheelRadialPopCore
329
312
  } from "@behindthemusictree/genre-tree-view";
330
313
  import { usePlayer as usePlayer3 } from "@behindthemusictree/app-kit/player";
331
- import { jsx as jsx3 } from "react/jsx-runtime";
314
+ import { jsx as jsx4 } from "react/jsx-runtime";
332
315
  function GenrePlaylistTreeWheelRadialPopCore(props) {
333
316
  const {
334
317
  scope,
@@ -361,7 +344,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
361
344
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
362
345
  const { playing, play } = playback ?? defaultPlayback;
363
346
  const playingNodeId = playing?.uuid ?? null;
364
- const nodes = useMemo4(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
347
+ const nodes = useMemo4(() => toTreeNodes(genrePlaylists, sizeMetric, props.focusUuids), [genrePlaylists, sizeMetric, props.focusUuids]);
365
348
  const handlePlayPause = useCallback4(
366
349
  (nodeId) => {
367
350
  const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
@@ -373,9 +356,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
373
356
  if (genrePlaylistTrackCount4(genrePlaylist) === 0) {
374
357
  return;
375
358
  }
376
- play(genrePlaylist).catch((error) => {
377
- console.error("Failed to load genre playlist tracks:", error);
378
- });
359
+ void play(genrePlaylist);
379
360
  },
380
361
  [genrePlaylists, playingNodeId, isPlaying, setIsPlaying, play]
381
362
  );
@@ -415,7 +396,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
415
396
  onSelectedNodeChange,
416
397
  ...hostPanel
417
398
  };
418
- return outline ? /* @__PURE__ */ jsx3(GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ jsx3(
399
+ return outline ? /* @__PURE__ */ jsx4(GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ jsx4(
419
400
  GenreTreeWheelRadialPopCore,
420
401
  {
421
402
  ...treeProps,
@@ -432,7 +413,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
432
413
 
433
414
  // src/genre-tree/GenreTreeWheelHandoff.tsx
434
415
  import { useEffect, useRef, useState } from "react";
435
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
416
+ import { jsx as jsx5, jsxs } from "react/jsx-runtime";
436
417
  function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
437
418
  const [revealed, setRevealed] = useState(false);
438
419
  const onRevealedChangeRef = useRef(onRevealedChange);
@@ -452,7 +433,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
452
433
  };
453
434
  }, []);
454
435
  return /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full", children: [
455
- /* @__PURE__ */ jsx4(
436
+ /* @__PURE__ */ jsx5(
456
437
  "div",
457
438
  {
458
439
  className: "absolute inset-0 transition-opacity duration-150 ease-out",
@@ -461,7 +442,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
461
442
  children
462
443
  }
463
444
  ),
464
- !revealed && /* @__PURE__ */ jsx4("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
445
+ !revealed && /* @__PURE__ */ jsx5("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
465
446
  ] });
466
447
  }
467
448
 
@@ -476,7 +457,7 @@ import { useGenreSearch } from "@behindthemusictree/app-kit/genre-tree";
476
457
  // src/genre-tree/search/SearchBox.tsx
477
458
  import { useState as useState2 } from "react";
478
459
  import { SearchCombobox } from "@behindthemusictree/ui";
479
- import { jsx as jsx5 } from "react/jsx-runtime";
460
+ import { jsx as jsx6 } from "react/jsx-runtime";
480
461
  function SearchBox({
481
462
  label,
482
463
  placeholder,
@@ -498,7 +479,7 @@ function SearchBox({
498
479
  groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
499
480
  onPicked?.(groupKey, itemKey);
500
481
  };
501
- return /* @__PURE__ */ jsx5("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ jsx5(
482
+ return /* @__PURE__ */ jsx6("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ jsx6(
502
483
  SearchCombobox,
503
484
  {
504
485
  ariaLabel: label,
@@ -564,7 +545,7 @@ import {
564
545
  isSongSearchResultPlayable,
565
546
  useSongSearch
566
547
  } from "@behindthemusictree/app-kit/genre-tree";
567
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
548
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
568
549
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
569
550
  const search = useSongSearch(getBaseUrl, enabled ? text : "");
570
551
  if (!enabled) return void 0;
@@ -574,8 +555,8 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
574
555
  items: search.songs.map((song) => ({
575
556
  key: song.uuid,
576
557
  content: /* @__PURE__ */ jsxs3("span", { className: isSongSearchResultPlayable(song) ? void 0 : "opacity-60", children: [
577
- /* @__PURE__ */ jsx6("span", { className: "block truncate font-medium", children: song.title }),
578
- /* @__PURE__ */ jsx6("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
558
+ /* @__PURE__ */ jsx7("span", { className: "block truncate font-medium", children: song.title }),
559
+ /* @__PURE__ */ jsx7("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
579
560
  ] }),
580
561
  onSelect: () => onSelect(song)
581
562
  })),
@@ -588,7 +569,7 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
588
569
  import {
589
570
  useGenreRemoteSearch
590
571
  } from "@behindthemusictree/app-kit/genre-tree";
591
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
572
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
592
573
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
593
574
  const search = useGenreRemoteSearch(getBaseUrl, text, options);
594
575
  const genres = filter ? search.genres.filter(filter) : search.genres;
@@ -600,9 +581,9 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
600
581
  content: /* @__PURE__ */ jsxs4(Fragment2, { children: [
601
582
  /* @__PURE__ */ jsxs4("span", { className: "block truncate font-medium", children: [
602
583
  genre.name,
603
- genre.isUmbrella && /* @__PURE__ */ jsx7("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
584
+ genre.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
604
585
  ] }),
605
- /* @__PURE__ */ jsx7("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
586
+ /* @__PURE__ */ jsx8("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
606
587
  ] }),
607
588
  onSelect: () => onSelect(genre)
608
589
  })),
@@ -612,7 +593,7 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
612
593
  }
613
594
 
614
595
  // src/genre-tree/GenreSearch.tsx
615
- import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
596
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
616
597
  function GenreSearch({
617
598
  genrePlaylists,
618
599
  onSelect,
@@ -643,7 +624,7 @@ function GenreSearch({
643
624
  key: genrePlaylist.uuid,
644
625
  content: /* @__PURE__ */ jsxs5(Fragment3, { children: [
645
626
  genrePlaylist.name,
646
- genrePlaylist.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
627
+ genrePlaylist.isUmbrella && /* @__PURE__ */ jsx9("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
647
628
  ] }),
648
629
  onSelect: () => select(genrePlaylist)
649
630
  }))
@@ -652,7 +633,7 @@ function GenreSearch({
652
633
  (group) => active ? group : { ...group, items: [], status: "", loading: false }
653
634
  )
654
635
  ];
655
- return /* @__PURE__ */ jsx8("div", { className: "genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: /* @__PURE__ */ jsx8(
636
+ return /* @__PURE__ */ jsx9("div", { className: "genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: /* @__PURE__ */ jsx9(
656
637
  SearchBox,
657
638
  {
658
639
  label,
@@ -672,14 +653,14 @@ function GenreSearch({
672
653
  clear();
673
654
  onQueryChange?.("");
674
655
  } : void 0,
675
- leading: onPrevious && /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Previous", title: "Previous", className: SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ jsx8(ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
656
+ leading: onPrevious && /* @__PURE__ */ jsx9("button", { type: "button", "aria-label": "Previous", title: "Previous", className: SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ jsx9(ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
676
657
  onFocus
677
658
  }
678
659
  ) });
679
660
  }
680
661
 
681
662
  // src/genre-tree/GenreTreeSearch.tsx
682
- import { jsx as jsx9 } from "react/jsx-runtime";
663
+ import { jsx as jsx10 } from "react/jsx-runtime";
683
664
  function GenreTreeSearch({
684
665
  artistBaseUrl,
685
666
  songBaseUrl,
@@ -699,7 +680,7 @@ function GenreTreeSearch({
699
680
  const deferredQuery = useDeferredValue(query);
700
681
  const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
701
682
  const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
702
- const render = (hostGroups) => /* @__PURE__ */ jsx9(
683
+ const render = (hostGroups) => /* @__PURE__ */ jsx10(
703
684
  GenreSearch,
704
685
  {
705
686
  ...props,
@@ -714,7 +695,7 @@ function GenreTreeSearch({
714
695
  extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
715
696
  }
716
697
  );
717
- return SearchGroups ? /* @__PURE__ */ jsx9(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
698
+ return SearchGroups ? /* @__PURE__ */ jsx10(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
718
699
  }
719
700
 
720
701
  // src/genre-tree/GenreDetailPanel.tsx
@@ -751,7 +732,7 @@ import { ActionBand, ActionButton as ActionButton2, PanelBleed } from "@behindth
751
732
  // src/genre-tree/ShareActionButton.tsx
752
733
  import { Share2 as Share22 } from "lucide-react";
753
734
  import { ActionButton } from "@behindthemusictree/ui";
754
- import { usePopup as usePopup3 } from "@behindthemusictree/app-kit/popup";
735
+ import { usePopup as usePopup2 } from "@behindthemusictree/app-kit/popup";
755
736
 
756
737
  // src/genre-tree/SharePopup.tsx
757
738
  import { useCallback as useCallback5, useEffect as useEffect2, useMemo as useMemo5, useState as useState4 } from "react";
@@ -760,12 +741,12 @@ import { FaWhatsapp } from "react-icons/fa";
760
741
  import { SiGmail } from "react-icons/si";
761
742
 
762
743
  // src/genre-tree/ShareDestinationButton.tsx
763
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
744
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
764
745
  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";
765
746
  function Contents({ icon, label }) {
766
747
  return /* @__PURE__ */ jsxs6(Fragment4, { children: [
767
- /* @__PURE__ */ jsx10("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 }),
768
- /* @__PURE__ */ jsx10("span", { className: "max-w-full truncate", children: label })
748
+ /* @__PURE__ */ jsx11("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 }),
749
+ /* @__PURE__ */ jsx11("span", { className: "max-w-full truncate", children: label })
769
750
  ] });
770
751
  }
771
752
  function ShareDestinationButton({
@@ -777,29 +758,29 @@ function ShareDestinationButton({
777
758
  ariaLabel
778
759
  }) {
779
760
  if (href) {
780
- return /* @__PURE__ */ jsx10("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
761
+ return /* @__PURE__ */ jsx11("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx11(Contents, { icon, label }) });
781
762
  }
782
- return /* @__PURE__ */ jsx10("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
763
+ return /* @__PURE__ */ jsx11("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx11(Contents, { icon, label }) });
783
764
  }
784
765
 
785
766
  // src/genre-tree/SharePreviewCard.tsx
786
767
  import { Music, Music2, UserRound } from "lucide-react";
787
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
768
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
788
769
  function SharePreviewCard({ target, url }) {
789
770
  const Icon = { genre: Music2, artist: UserRound, song: Music }[target.kind];
790
771
  const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
791
- return /* @__PURE__ */ jsx11("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
792
- /* @__PURE__ */ jsx11("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ jsx11(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
772
+ return /* @__PURE__ */ jsx12("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
773
+ /* @__PURE__ */ jsx12("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ jsx12(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
793
774
  /* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
794
- /* @__PURE__ */ jsx11("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
795
- /* @__PURE__ */ jsx11("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
796
- /* @__PURE__ */ jsx11("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
775
+ /* @__PURE__ */ jsx12("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
776
+ /* @__PURE__ */ jsx12("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
777
+ /* @__PURE__ */ jsx12("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
797
778
  ] })
798
779
  ] }) });
799
780
  }
800
781
 
801
782
  // src/genre-tree/SharePopup.tsx
802
- import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
783
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
803
784
  function resolveAbsoluteUrl(url) {
804
785
  try {
805
786
  const parsed = new URL(url, window.location.href);
@@ -875,7 +856,7 @@ function SharePopup({ target, url, onClose }) {
875
856
  }
876
857
  };
877
858
  return /* @__PURE__ */ jsxs8(Fragment5, { children: [
878
- /* @__PURE__ */ jsx12(
859
+ /* @__PURE__ */ jsx13(
879
860
  BasePopup_default,
880
861
  {
881
862
  title: "Share",
@@ -891,69 +872,69 @@ function SharePopup({ target, url, onClose }) {
891
872
  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)]",
892
873
  contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
893
874
  children: /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-4", children: [
894
- /* @__PURE__ */ jsx12(SharePreviewCard, { target, url: absoluteUrl || url }),
875
+ /* @__PURE__ */ jsx13(SharePreviewCard, { target, url: absoluteUrl || url }),
895
876
  /* @__PURE__ */ jsxs8("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
896
- /* @__PURE__ */ jsx12(
877
+ /* @__PURE__ */ jsx13(
897
878
  ShareDestinationButton,
898
879
  {
899
880
  label: "Copy Link",
900
- icon: /* @__PURE__ */ jsx12(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
881
+ icon: /* @__PURE__ */ jsx13(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
901
882
  onClick: () => void handleCopy(),
902
883
  ariaLabel: "Copy link",
903
884
  disabled: !absoluteUrl
904
885
  }
905
886
  ),
906
- /* @__PURE__ */ jsx12(
887
+ /* @__PURE__ */ jsx13(
907
888
  ShareDestinationButton,
908
889
  {
909
890
  label: "WhatsApp",
910
- icon: /* @__PURE__ */ jsx12(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
891
+ icon: /* @__PURE__ */ jsx13(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
911
892
  href: destinationUrls?.whatsapp,
912
893
  ariaLabel: `Share ${target.name} via WhatsApp`,
913
894
  disabled: !absoluteUrl
914
895
  }
915
896
  ),
916
- /* @__PURE__ */ jsx12(
897
+ /* @__PURE__ */ jsx13(
917
898
  ShareDestinationButton,
918
899
  {
919
900
  label: "Gmail",
920
- icon: /* @__PURE__ */ jsx12(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
901
+ icon: /* @__PURE__ */ jsx13(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
921
902
  href: destinationUrls?.gmail,
922
903
  ariaLabel: `Email ${target.name} with Gmail`,
923
904
  disabled: !absoluteUrl
924
905
  }
925
906
  ),
926
- canUseNativeShare && /* @__PURE__ */ jsx12(
907
+ canUseNativeShare && /* @__PURE__ */ jsx13(
927
908
  ShareDestinationButton,
928
909
  {
929
910
  label: "More",
930
- icon: /* @__PURE__ */ jsx12(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
911
+ icon: /* @__PURE__ */ jsx13(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
931
912
  onClick: () => void handleNativeShare(),
932
913
  ariaLabel: "More sharing options",
933
914
  disabled: !absoluteUrl
934
915
  }
935
916
  )
936
917
  ] }),
937
- copyStatus === "failed" && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
938
- shareError && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
918
+ copyStatus === "failed" && /* @__PURE__ */ jsx13("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
919
+ shareError && /* @__PURE__ */ jsx13("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
939
920
  ] })
940
921
  }
941
922
  ),
942
- toastId > 0 && /* @__PURE__ */ jsx12(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
923
+ toastId > 0 && /* @__PURE__ */ jsx13(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
943
924
  ] });
944
925
  }
945
926
 
946
927
  // src/genre-tree/ShareActionButton.tsx
947
- import { jsx as jsx13 } from "react/jsx-runtime";
928
+ import { jsx as jsx14 } from "react/jsx-runtime";
948
929
  function ShareActionButton({ target, url }) {
949
- const { showPopup, hidePopup } = usePopup3();
930
+ const { showPopup, hidePopup } = usePopup2();
950
931
  const openSharePopup = () => {
951
- showPopup(/* @__PURE__ */ jsx13(SharePopup, { target, url, onClose: hidePopup }));
932
+ showPopup(/* @__PURE__ */ jsx14(SharePopup, { target, url, onClose: hidePopup }));
952
933
  };
953
- return /* @__PURE__ */ jsx13(
934
+ return /* @__PURE__ */ jsx14(
954
935
  ActionButton,
955
936
  {
956
- icon: /* @__PURE__ */ jsx13(Share22, { "aria-hidden": "true", className: "w-5 h-5" }),
937
+ icon: /* @__PURE__ */ jsx14(Share22, { "aria-hidden": "true", className: "w-5 h-5" }),
957
938
  label: "Share",
958
939
  "aria-label": `Share ${target.name}`,
959
940
  title: `Share ${target.name}`,
@@ -963,7 +944,7 @@ function ShareActionButton({ target, url }) {
963
944
  }
964
945
 
965
946
  // src/genre-tree/GenreActionBand.tsx
966
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
947
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
967
948
  function GenreActionBand({
968
949
  node,
969
950
  playActions,
@@ -984,24 +965,24 @@ function GenreActionBand({
984
965
  items.push({
985
966
  key: "zoom-in",
986
967
  label: "Focus",
987
- icon: /* @__PURE__ */ jsx14(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
968
+ icon: /* @__PURE__ */ jsx15(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
988
969
  onClick: onZoomIn
989
970
  });
990
971
  }
991
972
  if (onAddChild) {
992
- adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx14(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
973
+ adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx15(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
993
974
  }
994
975
  if (onRename) {
995
- adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx14(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
976
+ adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx15(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
996
977
  }
997
978
  if (onChangeParent) {
998
- adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx14(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
979
+ adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx15(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
999
980
  }
1000
981
  if (onToggleSide) {
1001
982
  adminItems.push({
1002
983
  key: "toggle-side",
1003
984
  label: side === "pop" ? "Make core" : "Make pop",
1004
- icon: /* @__PURE__ */ jsx14(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
985
+ icon: /* @__PURE__ */ jsx15(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1005
986
  onClick: onToggleSide,
1006
987
  disabled: !!toggleSideDisabledReason,
1007
988
  title: toggleSideDisabledReason
@@ -1016,9 +997,9 @@ function GenreActionBand({
1016
997
  disabled: action.enabled ? !action.enabled(node) : false
1017
998
  });
1018
999
  }
1019
- const renderItems = (list) => list.map((item) => /* @__PURE__ */ jsx14(ActionButton2, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
1000
+ const renderItems = (list) => list.map((item) => /* @__PURE__ */ jsx15(ActionButton2, { icon: item.icon, label: item.label, disabled: item.disabled, title: item.title, onClick: item.onClick }, item.key));
1020
1001
  if (!playActions && items.length === 0 && adminItems.length === 0 && !(shareTarget && shareUrl)) return null;
1021
- return /* @__PURE__ */ jsx14(PanelBleed, { children: /* @__PURE__ */ jsxs9(
1002
+ return /* @__PURE__ */ jsx15(PanelBleed, { children: /* @__PURE__ */ jsxs9(
1022
1003
  ActionBand,
1023
1004
  {
1024
1005
  className: "genre-action-band",
@@ -1026,7 +1007,7 @@ function GenreActionBand({
1026
1007
  children: [
1027
1008
  playActions,
1028
1009
  renderItems(items),
1029
- shareTarget && shareUrl && /* @__PURE__ */ jsx14(ShareActionButton, { target: shareTarget, url: shareUrl })
1010
+ shareTarget && shareUrl && /* @__PURE__ */ jsx15(ShareActionButton, { target: shareTarget, url: shareUrl })
1030
1011
  ]
1031
1012
  }
1032
1013
  ) });
@@ -1053,7 +1034,7 @@ var REGION_GLOBE_PATHS = {
1053
1034
  var REGION_GLOBES = ["africa", "americas", "asia"];
1054
1035
 
1055
1036
  // src/genre-tree/RegionGlobeIcon.tsx
1056
- import { jsx as jsx15 } from "react/jsx-runtime";
1037
+ import { jsx as jsx16 } from "react/jsx-runtime";
1057
1038
  var ROTATION_INTERVAL_MS = 3e3;
1058
1039
  function RegionGlobeIcon({ className: className2 }) {
1059
1040
  const [index, setIndex] = useState5(0);
@@ -1071,29 +1052,29 @@ function RegionGlobeIcon({ className: className2 }) {
1071
1052
  clearInterval(timer);
1072
1053
  };
1073
1054
  }, []);
1074
- return /* @__PURE__ */ jsx15("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ jsx15("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
1055
+ return /* @__PURE__ */ jsx16("svg", { viewBox: "0 0 512 512", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ jsx16("path", { "data-testid": "region-globe-path", d: REGION_GLOBE_PATHS[REGION_GLOBES[index]] }) });
1075
1056
  }
1076
1057
 
1077
1058
  // src/genre-tree/treeMarkPath.ts
1078
1059
  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";
1079
1060
 
1080
1061
  // src/genre-tree/TreeMarkIcon.tsx
1081
- import { jsx as jsx16 } from "react/jsx-runtime";
1062
+ import { jsx as jsx17 } from "react/jsx-runtime";
1082
1063
  function TreeMarkIcon({ className: className2 }) {
1083
- return /* @__PURE__ */ jsx16("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ jsx16("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
1064
+ return /* @__PURE__ */ jsx17("svg", { viewBox: "0 0 551 567", fill: "currentColor", className: className2, "aria-hidden": true, children: /* @__PURE__ */ jsx17("path", { transform: "translate(0 567) scale(0.1 -0.1)", d: TREE_MARK_PATH }) });
1084
1065
  }
1085
1066
 
1086
1067
  // src/genre-tree/GenrePlayActions.tsx
1087
- import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
1068
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
1088
1069
  function PlayIcon({ kind }) {
1089
1070
  return /* @__PURE__ */ jsxs10("span", { "aria-hidden": "true", className: "relative flex items-center justify-center w-5 h-5", children: [
1090
- kind === "tree" && /* @__PURE__ */ jsx17(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1091
- kind === "map" && /* @__PURE__ */ jsx17(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1071
+ kind === "tree" && /* @__PURE__ */ jsx18(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1072
+ kind === "map" && /* @__PURE__ */ jsx18(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1092
1073
  kind === "tree-map" && /* @__PURE__ */ jsxs10(Fragment6, { children: [
1093
- /* @__PURE__ */ jsx17(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
1094
- /* @__PURE__ */ jsx17(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
1074
+ /* @__PURE__ */ jsx18(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
1075
+ /* @__PURE__ */ jsx18(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_0_0_50%)]" })
1095
1076
  ] }),
1096
- /* @__PURE__ */ jsx17(Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1077
+ /* @__PURE__ */ jsx18(Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1097
1078
  ] });
1098
1079
  }
1099
1080
  function playOptions(playlist) {
@@ -1109,13 +1090,13 @@ function playOptions(playlist) {
1109
1090
  function GenrePlayActions({ genrePlaylist, playback }) {
1110
1091
  const { isPlaying, setIsPlaying } = usePlayer4();
1111
1092
  const options = playOptions(genrePlaylist);
1112
- return /* @__PURE__ */ jsx17(Fragment6, { children: options.map((option) => {
1093
+ return /* @__PURE__ */ jsx18(Fragment6, { children: options.map((option) => {
1113
1094
  const isCurrent = playback.playing?.uuid === genrePlaylist.uuid && (option.selection === void 0 || playback.playing.selectionKey === "" || playback.playing.selectionKey === genreMusicSelectionKey2(option.selection).join());
1114
1095
  const playing = isCurrent && isPlaying;
1115
- return /* @__PURE__ */ jsx17(
1096
+ return /* @__PURE__ */ jsx18(
1116
1097
  ActionButton3,
1117
1098
  {
1118
- icon: playing ? /* @__PURE__ */ jsx17(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx17(PlayIcon, { kind: option.icon }),
1099
+ icon: playing ? /* @__PURE__ */ jsx18(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx18(PlayIcon, { kind: option.icon }),
1119
1100
  label: playing ? "Pause" : option.label,
1120
1101
  caption: String(option.count),
1121
1102
  disabled: option.count === 0,
@@ -1125,9 +1106,7 @@ function GenrePlayActions({ genrePlaylist, playback }) {
1125
1106
  setIsPlaying(!isPlaying);
1126
1107
  return;
1127
1108
  }
1128
- playback.play(genrePlaylist, option.selection).catch((error) => {
1129
- console.error("Failed to load genre playlist tracks:", error);
1130
- });
1109
+ void playback.play(genrePlaylist, option.selection);
1131
1110
  }
1132
1111
  },
1133
1112
  option.key
@@ -1141,7 +1120,7 @@ import { useFetchGenreOverview } from "@behindthemusictree/app-kit/genre-tree";
1141
1120
 
1142
1121
  // src/genre-tree/ClampedText.tsx
1143
1122
  import { useLayoutEffect, useRef as useRef2, useState as useState6 } from "react";
1144
- import { Fragment as Fragment7, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
1123
+ import { Fragment as Fragment7, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
1145
1124
  function ClampedText({ children, className: className2 = "" }) {
1146
1125
  const ref = useRef2(null);
1147
1126
  const [expanded, setExpanded] = useState6(false);
@@ -1152,8 +1131,8 @@ function ClampedText({ children, className: className2 = "" }) {
1152
1131
  setOverflows(element.scrollHeight > element.clientHeight);
1153
1132
  }, [children, expanded]);
1154
1133
  return /* @__PURE__ */ jsxs11(Fragment7, { children: [
1155
- /* @__PURE__ */ jsx18("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1156
- (overflows || expanded) && /* @__PURE__ */ jsx18(
1134
+ /* @__PURE__ */ jsx19("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1135
+ (overflows || expanded) && /* @__PURE__ */ jsx19(
1157
1136
  "button",
1158
1137
  {
1159
1138
  type: "button",
@@ -1178,25 +1157,25 @@ import { PaginatedResponseSchema } from "@behindthemusictree/app-kit/transport";
1178
1157
  import { parseWithLog } from "@behindthemusictree/app-kit/transport";
1179
1158
  import { usePlayer as usePlayer5 } from "@behindthemusictree/app-kit/player";
1180
1159
  import { PlayStates } from "@behindthemusictree/app-kit/player";
1181
- import { usePopup as usePopup4 } from "@behindthemusictree/app-kit/popup";
1182
- import { ErrorCode as ErrorCode3 } from "@behindthemusictree/app-kit/transport";
1160
+ import { usePopup as usePopup3 } from "@behindthemusictree/app-kit/popup";
1161
+ import { AppError as AppError2, ErrorCode as ErrorCode2 } from "@behindthemusictree/app-kit/transport";
1183
1162
  import { genreMusicQueryParams } from "@behindthemusictree/app-kit/genre-tree";
1184
1163
  import { genrePlaylistQueryKeys } from "@behindthemusictree/app-kit/genre-tree";
1185
1164
  import { libraryEndpoints, libraryQueryKeys } from "@behindthemusictree/app-kit/genre-tree";
1186
1165
  import { isSong, songUnplayableReason, SongDetailedSchema } from "@behindthemusictree/app-kit/genre-tree";
1187
- import { useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre-tree";
1166
+ import { TracksFailureAlreadyReportedError as TracksFailureAlreadyReportedError2, useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre-tree";
1188
1167
 
1189
1168
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1190
1169
  import { musicBrainzRecordingUrl } from "@behindthemusictree/app-kit/genre-tree";
1191
- import { Fragment as Fragment8, jsx as jsx19 } from "react/jsx-runtime";
1170
+ import { Fragment as Fragment8, jsx as jsx20 } from "react/jsx-runtime";
1192
1171
  function MusicBrainzRecordingLink({
1193
1172
  song,
1194
1173
  children,
1195
1174
  className: className2 = ""
1196
1175
  }) {
1197
1176
  const url = musicBrainzRecordingUrl(song);
1198
- if (!url) return /* @__PURE__ */ jsx19(Fragment8, { children });
1199
- return /* @__PURE__ */ jsx19(
1177
+ if (!url) return /* @__PURE__ */ jsx20(Fragment8, { children });
1178
+ return /* @__PURE__ */ jsx20(
1200
1179
  "a",
1201
1180
  {
1202
1181
  href: url,
@@ -1213,7 +1192,7 @@ function MusicBrainzRecordingLink({
1213
1192
  // src/genre-tree/GenrePlaylistTracks.tsx
1214
1193
  import { ArtistNames } from "@behindthemusictree/app-kit/genre-tree";
1215
1194
  import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
1216
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
1195
+ import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
1217
1196
  var CATALOGUE_PAGE_SIZE = 100;
1218
1197
  var SongPageSchema = PaginatedResponseSchema(SongDetailedSchema).extend({
1219
1198
  overallTotal: z.number().nullable(),
@@ -1247,7 +1226,7 @@ function GenrePlaylistTracks({
1247
1226
  const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = useTrackList2();
1248
1227
  const { session, sessionRestored } = useSession();
1249
1228
  const { playerTrackObject, playState, handlePlayPauseAction } = usePlayer5();
1250
- const { showPopup } = usePopup4();
1229
+ const { showPopup } = usePopup3();
1251
1230
  const trackNoun = useTrackNoun();
1252
1231
  const uuid = genrePlaylist?.uuid;
1253
1232
  const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
@@ -1299,27 +1278,28 @@ function GenrePlaylistTracks({
1299
1278
  }) : 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"));
1300
1279
  started.catch((error) => {
1301
1280
  console.error("Failed to play genre playlist:", error);
1302
- showPopup(/* @__PURE__ */ jsx20(InternalErrorPopup, { errorCode: ErrorCode3.CLIENT_UNKNOWN }));
1281
+ if (error instanceof TracksFailureAlreadyReportedError2) return;
1282
+ showPopup(/* @__PURE__ */ jsx21(InternalErrorPopup, { errorCode: error instanceof AppError2 ? error.code : ErrorCode2.CLIENT_UNKNOWN }));
1303
1283
  });
1304
1284
  };
1305
- return /* @__PURE__ */ jsx20(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
1285
+ return /* @__PURE__ */ jsx21(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
1306
1286
  renderTracksToolbar?.(),
1307
- scope === "me" && !genrePlaylist ? /* @__PURE__ */ jsx20("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ jsx20(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
1287
+ scope === "me" && !genrePlaylist ? /* @__PURE__ */ jsx21("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ jsx21(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
1308
1288
  "Couldn't load ",
1309
1289
  trackNoun.plural,
1310
1290
  ".",
1311
1291
  " ",
1312
- /* @__PURE__ */ jsx20("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1313
- ] }) : tracks.length === 0 ? /* @__PURE__ */ jsx20("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1292
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1293
+ ] }) : tracks.length === 0 ? /* @__PURE__ */ jsx21("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
1314
1294
  tracks.map((track, index) => {
1315
1295
  const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
1316
1296
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1317
1297
  const isPlaying = isCurrent && playState === PlayStates.PLAYING;
1318
1298
  const text = /* @__PURE__ */ jsxs12("span", { className: TEXT_CELL, children: [
1319
- /* @__PURE__ */ jsx20("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : isSong(track) ? /* @__PURE__ */ jsx20(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1320
- track.artists?.length ? /* @__PURE__ */ jsx20("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1321
- /* @__PURE__ */ jsx20("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ jsx20(ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1322
- track.album && /* @__PURE__ */ jsx20("span", { className: ALBUM_CELL, children: track.album.name })
1299
+ /* @__PURE__ */ jsx21("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : isSong(track) ? /* @__PURE__ */ jsx21(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
1300
+ track.artists?.length ? /* @__PURE__ */ jsx21("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1301
+ /* @__PURE__ */ jsx21("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ jsx21(ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1302
+ track.album && /* @__PURE__ */ jsx21("span", { className: ALBUM_CELL, children: track.album.name })
1323
1303
  ] });
1324
1304
  const leading = renderSongLeading?.(track, index);
1325
1305
  const actions = renderSongActions?.(track);
@@ -1327,7 +1307,7 @@ function GenrePlaylistTracks({
1327
1307
  if (isUnplayable) {
1328
1308
  return /* @__PURE__ */ jsxs12("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1329
1309
  leading,
1330
- /* @__PURE__ */ jsx20(
1310
+ /* @__PURE__ */ jsx21(
1331
1311
  "button",
1332
1312
  {
1333
1313
  type: "button",
@@ -1335,7 +1315,7 @@ function GenrePlaylistTracks({
1335
1315
  "aria-disabled": "true",
1336
1316
  "aria-label": label,
1337
1317
  className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
1338
- children: /* @__PURE__ */ jsx20(FaPlay, { size: 10 })
1318
+ children: /* @__PURE__ */ jsx21(FaPlay, { size: 10 })
1339
1319
  }
1340
1320
  ),
1341
1321
  text,
@@ -1348,14 +1328,14 @@ function GenrePlaylistTracks({
1348
1328
  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)]" : ""}`,
1349
1329
  children: [
1350
1330
  leading,
1351
- /* @__PURE__ */ jsx20(
1331
+ /* @__PURE__ */ jsx21(
1352
1332
  "button",
1353
1333
  {
1354
1334
  type: "button",
1355
1335
  "aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
1356
1336
  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 !== PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
1357
1337
  onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
1358
- children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */ jsx20(RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ jsx20(FaPause, { size: 10 }) : /* @__PURE__ */ jsx20(FaPlay, { size: 10 })
1338
+ children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */ jsx21(RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ jsx21(FaPause, { size: 10 }) : /* @__PURE__ */ jsx21(FaPlay, { size: 10 })
1359
1339
  }
1360
1340
  ),
1361
1341
  text,
@@ -1365,7 +1345,7 @@ function GenrePlaylistTracks({
1365
1345
  track.uuid
1366
1346
  );
1367
1347
  }),
1368
- hasNextPage && /* @__PURE__ */ jsx20("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1348
+ hasNextPage && /* @__PURE__ */ jsx21("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1369
1349
  ] })
1370
1350
  ] }) });
1371
1351
  }
@@ -1373,7 +1353,7 @@ function GenrePlaylistTracks({
1373
1353
  // src/genre-tree/GenreDetailExtras.tsx
1374
1354
  import { useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
1375
1355
  import { SiWikidata, SiWikipedia } from "react-icons/si";
1376
- import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
1356
+ import { Fragment as Fragment9, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1377
1357
  function GenreDetailExtras({
1378
1358
  genreUuid,
1379
1359
  genrePlaylist,
@@ -1400,15 +1380,15 @@ function GenreDetailExtras({
1400
1380
  );
1401
1381
  if (isPending) {
1402
1382
  return /* @__PURE__ */ jsxs13(Fragment9, { children: [
1403
- /* @__PURE__ */ jsx21(PanelSection2, { title: "Summary", children: /* @__PURE__ */ jsx21("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ jsx21(Skeleton2, { className: "h-4 w-3/4" }) }) }),
1383
+ /* @__PURE__ */ jsx22(PanelSection2, { title: "Summary", children: /* @__PURE__ */ jsx22("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ jsx22(Skeleton2, { className: "h-4 w-3/4" }) }) }),
1404
1384
  family
1405
1385
  ] });
1406
1386
  }
1407
- if (!overview) return /* @__PURE__ */ jsx21(Fragment9, { children: family });
1387
+ if (!overview) return /* @__PURE__ */ jsx22(Fragment9, { children: family });
1408
1388
  const { summary, summarySourceUrl, essentialSongs } = overview;
1409
1389
  const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
1410
1390
  return /* @__PURE__ */ jsxs13(Fragment9, { children: [
1411
- (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx21(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
1391
+ (summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx22(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
1412
1392
  summarySourceUrl && /* @__PURE__ */ jsxs13(
1413
1393
  "a",
1414
1394
  {
@@ -1417,7 +1397,7 @@ function GenreDetailExtras({
1417
1397
  rel: "noopener noreferrer",
1418
1398
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1419
1399
  children: [
1420
- /* @__PURE__ */ jsx21(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1400
+ /* @__PURE__ */ jsx22(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1421
1401
  "Wikipedia"
1422
1402
  ]
1423
1403
  }
@@ -1430,17 +1410,17 @@ function GenreDetailExtras({
1430
1410
  rel: "noopener noreferrer",
1431
1411
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1432
1412
  children: [
1433
- /* @__PURE__ */ jsx21(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1413
+ /* @__PURE__ */ jsx22(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1434
1414
  "Wikidata"
1435
1415
  ]
1436
1416
  }
1437
1417
  )
1438
1418
  ] }) }),
1439
- /* @__PURE__ */ jsx21(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx21(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1419
+ /* @__PURE__ */ jsx22(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx22(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1440
1420
  family,
1441
1421
  renderGenreDetailExtras?.(overview),
1442
- (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx21(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx21("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx21("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx21("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
1443
- /* @__PURE__ */ jsx21(
1422
+ (renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx22(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx22("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx22("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx22("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
1423
+ /* @__PURE__ */ jsx22(
1444
1424
  GenrePlaylistTracks,
1445
1425
  {
1446
1426
  genreUuid,
@@ -1458,10 +1438,10 @@ function GenreDetailExtras({
1458
1438
  }
1459
1439
 
1460
1440
  // src/genre-tree/GenreInfoPanelBody.tsx
1461
- import { Fragment as Fragment10, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1441
+ import { Fragment as Fragment10, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
1462
1442
  import { createElement } from "react";
1463
1443
  function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
1464
- return /* @__PURE__ */ jsx22(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ jsx22(GenrePlayActions, { genrePlaylist, playback }) });
1444
+ return /* @__PURE__ */ jsx23(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ jsx23(GenrePlayActions, { genrePlaylist, playback }) });
1465
1445
  }
1466
1446
  function GenreInfoPanelBody({
1467
1447
  toolbar,
@@ -1480,7 +1460,7 @@ function GenreInfoPanelBody({
1480
1460
  const selection = summary ? defaultGenreMusicSelection2(summary) : void 0;
1481
1461
  return /* @__PURE__ */ jsxs14(Fragment10, { children: [
1482
1462
  toolbar,
1483
- genrePlaylist ? /* @__PURE__ */ jsx22(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ jsx22(GenreActionBand, { ...band }),
1463
+ genrePlaylist ? /* @__PURE__ */ jsx23(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ jsx23(GenreActionBand, { ...band }),
1484
1464
  /* @__PURE__ */ createElement(
1485
1465
  GenreDetailExtras,
1486
1466
  {
@@ -1500,7 +1480,7 @@ function GenreInfoPanelBody({
1500
1480
  }
1501
1481
 
1502
1482
  // src/genre-tree/GenreDetailPanel.tsx
1503
- import { Fragment as Fragment11, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
1483
+ import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
1504
1484
  var RELATIVE_ITEM_COUNT = 1;
1505
1485
  function toNode(genre, parentId) {
1506
1486
  return {
@@ -1564,19 +1544,19 @@ function useGenreDetailContent({
1564
1544
  if (isError) return {
1565
1545
  name: null,
1566
1546
  title: "Genre unavailable",
1567
- content: /* @__PURE__ */ jsx23("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
1547
+ content: /* @__PURE__ */ jsx24("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
1568
1548
  };
1569
1549
  if (isPending || !detail || !view) return {
1570
1550
  name: null,
1571
1551
  title: "Loading genre\u2026",
1572
1552
  content: /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
1573
- /* @__PURE__ */ jsx23(Skeleton3, { className: "h-6 w-2/3" }),
1574
- /* @__PURE__ */ jsx23(Skeleton3, { className: "h-4 w-full" }),
1575
- /* @__PURE__ */ jsx23(Skeleton3, { className: "h-4 w-3/4" })
1553
+ /* @__PURE__ */ jsx24(Skeleton3, { className: "h-6 w-2/3" }),
1554
+ /* @__PURE__ */ jsx24(Skeleton3, { className: "h-4 w-full" }),
1555
+ /* @__PURE__ */ jsx24(Skeleton3, { className: "h-4 w-3/4" })
1576
1556
  ] })
1577
1557
  };
1578
1558
  const selectGenre = (nodeId) => onSelectGenre(GenreUuidSchema.parse(nodeId));
1579
- const family = /* @__PURE__ */ jsx23(
1559
+ const family = /* @__PURE__ */ jsx24(
1580
1560
  GenreNodeFamily,
1581
1561
  {
1582
1562
  node: view.node,
@@ -1587,14 +1567,14 @@ function useGenreDetailContent({
1587
1567
  onSelectNode: selectGenre
1588
1568
  }
1589
1569
  );
1590
- const ancestors = /* @__PURE__ */ jsx23(GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1570
+ const ancestors = /* @__PURE__ */ jsx24(GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1591
1571
  return {
1592
1572
  name: detail.name,
1593
1573
  title: detail.name,
1594
1574
  headerAccent: { background: view.fill, color: view.textColor },
1595
1575
  aside: expanded ? ancestors : void 0,
1596
1576
  content: /* @__PURE__ */ jsxs15(Fragment11, { children: [
1597
- /* @__PURE__ */ jsx23(
1577
+ /* @__PURE__ */ jsx24(
1598
1578
  GenreInfoPanelBody,
1599
1579
  {
1600
1580
  family: /* @__PURE__ */ jsxs15(Fragment11, { children: [
@@ -1641,7 +1621,7 @@ function GenreDetailPanel({
1641
1621
  expanded,
1642
1622
  band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
1643
1623
  });
1644
- return /* @__PURE__ */ jsx23(
1624
+ return /* @__PURE__ */ jsx24(
1645
1625
  DetailPanel,
1646
1626
  {
1647
1627
  side: "left",
@@ -1654,7 +1634,7 @@ function GenreDetailPanel({
1654
1634
  edgeTab,
1655
1635
  aside: panel.aside,
1656
1636
  titleInSearch,
1657
- headerActions: onExpandedChange ? /* @__PURE__ */ jsx23(IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ jsx23(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx23(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
1637
+ headerActions: onExpandedChange ? /* @__PURE__ */ jsx24(IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ jsx24(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx24(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
1658
1638
  children: panel.content
1659
1639
  }
1660
1640
  );
@@ -1666,7 +1646,7 @@ import {
1666
1646
  getReparentCandidates,
1667
1647
  useUpdateGenre
1668
1648
  } from "@behindthemusictree/app-kit/genre-tree";
1669
- import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
1649
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1670
1650
  function ChangeParentPopup({
1671
1651
  genrePlaylist,
1672
1652
  genrePlaylists,
@@ -1706,7 +1686,7 @@ function ChangeParentPopup({
1706
1686
  "Current parent: ",
1707
1687
  genrePlaylist.parent?.name ?? "none"
1708
1688
  ] }),
1709
- /* @__PURE__ */ jsx24("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx24(
1689
+ /* @__PURE__ */ jsx25("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx25(
1710
1690
  GenreSearch,
1711
1691
  {
1712
1692
  genrePlaylists: candidates,
@@ -1714,7 +1694,7 @@ function ChangeParentPopup({
1714
1694
  placeholder: "Search a new parent\u2026"
1715
1695
  }
1716
1696
  ) }),
1717
- formErrors.length > 0 && /* @__PURE__ */ jsx24("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
1697
+ formErrors.length > 0 && /* @__PURE__ */ jsx25("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
1718
1698
  ] });
1719
1699
  }
1720
1700
 
@@ -1727,7 +1707,7 @@ import { Pause as Pause2, Play as Play2 } from "lucide-react";
1727
1707
  import { ActionBand as ActionBand2, ActionButton as ActionButton4, PanelBleed as PanelBleed2 } from "@behindthemusictree/ui";
1728
1708
  import { TrackListOriginType as TrackListOriginType2, useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre-tree";
1729
1709
  import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
1730
- import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
1710
+ import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
1731
1711
  function ArtistActionBand({ artist, share }) {
1732
1712
  const { trackList, playNewTrackListFromArtist } = useTrackList3();
1733
1713
  const { isPlaying, setIsPlaying } = usePlayer6();
@@ -1743,11 +1723,11 @@ function ArtistActionBand({ artist, share }) {
1743
1723
  console.error("Failed to load artist tracks:", error);
1744
1724
  });
1745
1725
  };
1746
- return /* @__PURE__ */ jsx25(PanelBleed2, { children: /* @__PURE__ */ jsxs17(ActionBand2, { children: [
1747
- /* @__PURE__ */ jsx25(
1726
+ return /* @__PURE__ */ jsx26(PanelBleed2, { children: /* @__PURE__ */ jsxs17(ActionBand2, { children: [
1727
+ /* @__PURE__ */ jsx26(
1748
1728
  ActionButton4,
1749
1729
  {
1750
- icon: playing ? /* @__PURE__ */ jsx25(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx25(Play2, { "aria-hidden": "true", className: "w-5 h-5" }),
1730
+ icon: playing ? /* @__PURE__ */ jsx26(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx26(Play2, { "aria-hidden": "true", className: "w-5 h-5" }),
1751
1731
  label: playing ? "Pause" : "Play",
1752
1732
  caption: String(artist.playableTracksCount),
1753
1733
  disabled: artist.playableTracksCount === 0,
@@ -1775,7 +1755,7 @@ import {
1775
1755
  SiYoutube
1776
1756
  } from "react-icons/si";
1777
1757
  import { Chip } from "@behindthemusictree/ui";
1778
- import { jsx as jsx26 } from "react/jsx-runtime";
1758
+ import { jsx as jsx27 } from "react/jsx-runtime";
1779
1759
  var LINK_KINDS = {
1780
1760
  official: { label: "Official website", Icon: Globe },
1781
1761
  wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
@@ -1812,7 +1792,7 @@ function artistLinks(artist) {
1812
1792
  return links;
1813
1793
  }
1814
1794
  function ArtistLinks({ links }) {
1815
- return /* @__PURE__ */ jsx26("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx26("li", { children: /* @__PURE__ */ jsx26(Chip, { href: url, icon: /* @__PURE__ */ jsx26(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
1795
+ return /* @__PURE__ */ jsx27("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx27("li", { children: /* @__PURE__ */ jsx27(Chip, { href: url, icon: /* @__PURE__ */ jsx27(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
1816
1796
  }
1817
1797
 
1818
1798
  // src/genre-tree/artist/formatYears.ts
@@ -1823,7 +1803,7 @@ function formatYears(beginYear, endYear, ended) {
1823
1803
  }
1824
1804
 
1825
1805
  // src/genre-tree/artist/ArtistHeader.tsx
1826
- import { jsx as jsx27 } from "react/jsx-runtime";
1806
+ import { jsx as jsx28 } from "react/jsx-runtime";
1827
1807
  var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
1828
1808
  function ArtistHeader({ artist }) {
1829
1809
  const fields = [];
@@ -1833,35 +1813,35 @@ function ArtistHeader({ artist }) {
1833
1813
  const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
1834
1814
  if (years) fields.push({ label: "Active", value: years });
1835
1815
  const links = artistLinks(artist);
1836
- if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx27(ArtistLinks, { links }) });
1816
+ if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx28(ArtistLinks, { links }) });
1837
1817
  if (fields.length === 0) return null;
1838
- return /* @__PURE__ */ jsx27(FieldList, { items: fields });
1818
+ return /* @__PURE__ */ jsx28(FieldList, { items: fields });
1839
1819
  }
1840
1820
 
1841
1821
  // src/genre-tree/artist/ArtistPhoto.tsx
1842
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1822
+ import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
1843
1823
  function ExternalLink({ href, children }) {
1844
- return /* @__PURE__ */ jsx28("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
1824
+ return /* @__PURE__ */ jsx29("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
1845
1825
  }
1846
1826
  function ArtistPhoto({ image, name }) {
1847
1827
  return /* @__PURE__ */ jsxs18("figure", { className: "flex flex-col gap-1.5", children: [
1848
- /* @__PURE__ */ jsx28("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
1828
+ /* @__PURE__ */ jsx29("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
1849
1829
  /* @__PURE__ */ jsxs18("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
1850
1830
  "Photo: ",
1851
- /* @__PURE__ */ jsx28(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
1831
+ /* @__PURE__ */ jsx29(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
1852
1832
  ",",
1853
1833
  " ",
1854
- image.licenseUrl ? /* @__PURE__ */ jsx28(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
1834
+ image.licenseUrl ? /* @__PURE__ */ jsx29(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
1855
1835
  ] })
1856
1836
  ] });
1857
1837
  }
1858
1838
 
1859
1839
  // src/genre-tree/artist/ArtistMembers.tsx
1860
1840
  import { PanelGroup, PanelSection as PanelSection3 } from "@behindthemusictree/ui";
1861
- import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
1841
+ import { jsx as jsx30, jsxs as jsxs19 } from "react/jsx-runtime";
1862
1842
  function MembershipList({ title, memberships, renderArtistLink }) {
1863
1843
  if (memberships.length === 0) return null;
1864
- return /* @__PURE__ */ jsx29(PanelGroup, { title, children: /* @__PURE__ */ jsx29("ul", { children: memberships.map((membership, index) => {
1844
+ return /* @__PURE__ */ jsx30(PanelGroup, { title, children: /* @__PURE__ */ jsx30("ul", { children: memberships.map((membership, index) => {
1865
1845
  const details = [
1866
1846
  formatYears(membership.beginYear, membership.endYear, membership.ended),
1867
1847
  ...membership.roles
@@ -1870,7 +1850,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
1870
1850
  return (
1871
1851
  // A member can have several periods in the same group.
1872
1852
  /* @__PURE__ */ jsxs19("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
1873
- /* @__PURE__ */ jsx29("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
1853
+ /* @__PURE__ */ jsx30("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
1874
1854
  detailsText && /* @__PURE__ */ jsxs19("span", { className: "text-[var(--color-text-muted)]", children: [
1875
1855
  " \u2014 ",
1876
1856
  detailsText
@@ -1883,8 +1863,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
1883
1863
  if (memberships.length === 0) return null;
1884
1864
  const isCurrent = (membership) => !membership.ended && membership.endYear === null;
1885
1865
  return /* @__PURE__ */ jsxs19(PanelSection3, { title, children: [
1886
- /* @__PURE__ */ jsx29(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
1887
- /* @__PURE__ */ jsx29(
1866
+ /* @__PURE__ */ jsx30(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
1867
+ /* @__PURE__ */ jsx30(
1888
1868
  MembershipList,
1889
1869
  {
1890
1870
  title: "Past",
@@ -1897,7 +1877,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
1897
1877
 
1898
1878
  // src/genre-tree/artist/ArtistDiscography.tsx
1899
1879
  import { PanelGroup as PanelGroup2, PanelSection as PanelSection4 } from "@behindthemusictree/ui";
1900
- import { jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
1880
+ import { jsx as jsx31, jsxs as jsxs20 } from "react/jsx-runtime";
1901
1881
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
1902
1882
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
1903
1883
  function discographyGroup({ primaryType, secondaryTypes }) {
@@ -1917,12 +1897,12 @@ function byYear(a, b) {
1917
1897
  function ArtistDiscography({ albums }) {
1918
1898
  if (albums.length === 0) return null;
1919
1899
  const sorted = [...albums].sort(byYear);
1920
- return /* @__PURE__ */ jsx30(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
1900
+ return /* @__PURE__ */ jsx31(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
1921
1901
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
1922
1902
  if (groupAlbums.length === 0) return null;
1923
- return /* @__PURE__ */ jsx30(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx30("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs20("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
1924
- /* @__PURE__ */ jsx30("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
1925
- /* @__PURE__ */ jsx30("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
1903
+ return /* @__PURE__ */ jsx31(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx31("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs20("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
1904
+ /* @__PURE__ */ jsx31("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
1905
+ /* @__PURE__ */ jsx31("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
1926
1906
  ] }, album.uuid)) }) }, group);
1927
1907
  }) });
1928
1908
  }
@@ -1949,7 +1929,7 @@ import { useTrackList as useTrackList4 } from "@behindthemusictree/app-kit/genre
1949
1929
  import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
1950
1930
  import { usePlayer as usePlayer7 } from "@behindthemusictree/app-kit/player";
1951
1931
  import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
1952
- import { jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
1932
+ import { jsx as jsx32, jsxs as jsxs21 } from "react/jsx-runtime";
1953
1933
  function TrackPositionPlayPause({
1954
1934
  position,
1955
1935
  uuid,
@@ -1970,7 +1950,7 @@ function TrackPositionPlayPause({
1970
1950
  style: { minWidth: "4rem", maxWidth: "4rem" },
1971
1951
  onClick: handlePlayPauseClick,
1972
1952
  children: [
1973
- /* @__PURE__ */ jsx31("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx31("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs21(
1953
+ /* @__PURE__ */ jsx32("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs21(
1974
1954
  "span",
1975
1955
  {
1976
1956
  className: "track-position-play-pause__bars",
@@ -1978,18 +1958,18 @@ function TrackPositionPlayPause({
1978
1958
  role: "img",
1979
1959
  "aria-label": isPlaying ? "Playing" : "Paused",
1980
1960
  children: [
1981
- /* @__PURE__ */ jsx31("span", { className: "track-position-play-pause__bar" }),
1982
- /* @__PURE__ */ jsx31("span", { className: "track-position-play-pause__bar" }),
1983
- /* @__PURE__ */ jsx31("span", { className: "track-position-play-pause__bar" })
1961
+ /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__bar" }),
1962
+ /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__bar" }),
1963
+ /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__bar" })
1984
1964
  ]
1985
1965
  }
1986
1966
  ) : position }),
1987
- /* @__PURE__ */ jsx31(
1967
+ /* @__PURE__ */ jsx32(
1988
1968
  "div",
1989
1969
  {
1990
1970
  className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
1991
1971
  "aria-hidden": "true",
1992
- children: isAwaitingPlayback ? /* @__PURE__ */ jsx31("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx31(FaPause2, {}) : /* @__PURE__ */ jsx31(FaPlay2, {})
1972
+ children: isAwaitingPlayback ? /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx32(FaPause2, {}) : /* @__PURE__ */ jsx32(FaPlay2, {})
1993
1973
  }
1994
1974
  )
1995
1975
  ]
@@ -2000,7 +1980,7 @@ function TrackPositionPlayPause({
2000
1980
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2001
1981
  import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
2002
1982
  import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
2003
- import { jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
1983
+ import { jsx as jsx33, jsxs as jsxs22 } from "react/jsx-runtime";
2004
1984
  function TrackItem({
2005
1985
  track,
2006
1986
  position,
@@ -2029,7 +2009,7 @@ function TrackItem({
2029
2009
  {
2030
2010
  className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
2031
2011
  children: [
2032
- isUnplayable ? /* @__PURE__ */ jsx32(
2012
+ isUnplayable ? /* @__PURE__ */ jsx33(
2033
2013
  "div",
2034
2014
  {
2035
2015
  className: "flex items-center justify-center text-lg w-16",
@@ -2037,7 +2017,7 @@ function TrackItem({
2037
2017
  "aria-disabled": "true",
2038
2018
  children: position
2039
2019
  }
2040
- ) : /* @__PURE__ */ jsx32(
2020
+ ) : /* @__PURE__ */ jsx33(
2041
2021
  TrackPositionPlayPause,
2042
2022
  {
2043
2023
  position,
@@ -2047,11 +2027,11 @@ function TrackItem({
2047
2027
  }
2048
2028
  ),
2049
2029
  /* @__PURE__ */ jsxs22("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
2050
- /* @__PURE__ */ jsx32("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx32("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx32(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2051
- track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx32("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx32(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
2030
+ /* @__PURE__ */ jsx33("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx33("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx33(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
2031
+ track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx33("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx33(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
2052
2032
  ] }),
2053
- /* @__PURE__ */ jsx32("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx32("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
2054
- renderDuration && /* @__PURE__ */ jsx32(
2033
+ /* @__PURE__ */ jsx33("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx33("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
2034
+ renderDuration && /* @__PURE__ */ jsx33(
2055
2035
  "div",
2056
2036
  {
2057
2037
  className: "duration flex text-base w-16 items-center justify-center",
@@ -2059,14 +2039,14 @@ function TrackItem({
2059
2039
  children: renderDuration(track)
2060
2040
  }
2061
2041
  ),
2062
- renderActions && /* @__PURE__ */ jsx32("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2042
+ renderActions && /* @__PURE__ */ jsx33("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2063
2043
  ]
2064
2044
  }
2065
2045
  );
2066
2046
  }
2067
2047
 
2068
2048
  // src/genre-tree/artist/ArtistSongs.tsx
2069
- import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs23 } from "react/jsx-runtime";
2049
+ import { Fragment as Fragment12, jsx as jsx34, jsxs as jsxs23 } from "react/jsx-runtime";
2070
2050
  var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
2071
2051
  var SORTS = [
2072
2052
  { ordering: "title", label: "Title" },
@@ -2099,7 +2079,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2099
2079
  event.stopPropagation();
2100
2080
  void play(song.uuid);
2101
2081
  };
2102
- return /* @__PURE__ */ jsx33(PanelSection5, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx33(Skeleton4, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx33("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx33("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs23(Fragment12, { children: [
2082
+ return /* @__PURE__ */ jsx34(PanelSection5, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx34(Skeleton4, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx34("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx34("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs23(Fragment12, { children: [
2103
2083
  /* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
2104
2084
  /* @__PURE__ */ jsxs23(
2105
2085
  "button",
@@ -2108,12 +2088,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2108
2088
  onClick: () => void play(),
2109
2089
  className: "flex w-fit items-center gap-1.5 px-3 py-1 text-caption font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
2110
2090
  children: [
2111
- /* @__PURE__ */ jsx33(Play3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2091
+ /* @__PURE__ */ jsx34(Play3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2112
2092
  "Play all"
2113
2093
  ]
2114
2094
  }
2115
2095
  ),
2116
- /* @__PURE__ */ jsx33("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ jsx33(
2096
+ /* @__PURE__ */ jsx34("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ jsx34(
2117
2097
  "button",
2118
2098
  {
2119
2099
  type: "button",
@@ -2128,7 +2108,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2128
2108
  sort.ordering
2129
2109
  )) })
2130
2110
  ] }),
2131
- /* @__PURE__ */ jsx33("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx33("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx33(
2111
+ /* @__PURE__ */ jsx34("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx34("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx34(
2132
2112
  TrackItem,
2133
2113
  {
2134
2114
  track: song,
@@ -2137,12 +2117,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2137
2117
  renderSongLink
2138
2118
  }
2139
2119
  ) }, song.uuid)) }),
2140
- /* @__PURE__ */ jsx33(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2120
+ /* @__PURE__ */ jsx34(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2141
2121
  ] }) });
2142
2122
  }
2143
2123
 
2144
2124
  // src/genre-tree/artist/useArtistPanel.tsx
2145
- import { Fragment as Fragment13, jsx as jsx34, jsxs as jsxs24 } from "react/jsx-runtime";
2125
+ import { Fragment as Fragment13, jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
2146
2126
  function useArtistPanel({
2147
2127
  uuid,
2148
2128
  scope,
@@ -2164,25 +2144,25 @@ function useArtistPanel({
2164
2144
  ...panel,
2165
2145
  title: "Artist",
2166
2146
  content: /* @__PURE__ */ jsxs24("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2167
- /* @__PURE__ */ jsx34(Skeleton5, { className: "h-3 w-16" }),
2168
- /* @__PURE__ */ jsx34(Skeleton5, { className: "h-4 w-32" }),
2169
- /* @__PURE__ */ jsx34(Skeleton5, { className: "h-3 w-16" }),
2170
- /* @__PURE__ */ jsx34(Skeleton5, { className: "h-4 w-24" }),
2171
- /* @__PURE__ */ jsx34(Skeleton5, { className: "h-16 mt-2.5" }),
2172
- /* @__PURE__ */ jsx34(Skeleton5, { className: "h-16" })
2147
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-3 w-16" }),
2148
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-4 w-32" }),
2149
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-3 w-16" }),
2150
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-4 w-24" }),
2151
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-16 mt-2.5" }),
2152
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-16" })
2173
2153
  ] })
2174
2154
  };
2175
2155
  case "not-found":
2176
2156
  return {
2177
2157
  ...panel,
2178
2158
  title: "Artist not found",
2179
- content: /* @__PURE__ */ jsx34("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2159
+ content: /* @__PURE__ */ jsx35("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2180
2160
  };
2181
2161
  case "error":
2182
2162
  return {
2183
2163
  ...panel,
2184
2164
  title: "Artist",
2185
- content: /* @__PURE__ */ jsx34("p", { className: "text-red-600", children: "Could not load this artist." })
2165
+ content: /* @__PURE__ */ jsx35("p", { className: "text-red-600", children: "Could not load this artist." })
2186
2166
  };
2187
2167
  case "success": {
2188
2168
  const { artist } = result;
@@ -2192,18 +2172,18 @@ function useArtistPanel({
2192
2172
  ...panel,
2193
2173
  title: artist.name,
2194
2174
  content: /* @__PURE__ */ jsxs24(Fragment13, { children: [
2195
- /* @__PURE__ */ jsx34(
2175
+ /* @__PURE__ */ jsx35(
2196
2176
  ArtistActionBand,
2197
2177
  {
2198
2178
  artist,
2199
- share: shareTarget && shareUrl ? /* @__PURE__ */ jsx34(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2179
+ share: shareTarget && shareUrl ? /* @__PURE__ */ jsx35(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2200
2180
  }
2201
2181
  ),
2202
- artist.image && /* @__PURE__ */ jsx34(ArtistPhoto, { image: artist.image, name: artist.name }),
2203
- /* @__PURE__ */ jsx34(ArtistHeader, { artist }),
2204
- /* @__PURE__ */ jsx34(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2205
- /* @__PURE__ */ jsx34(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2206
- artist.tracksCount > 0 && /* @__PURE__ */ jsx34(
2182
+ artist.image && /* @__PURE__ */ jsx35(ArtistPhoto, { image: artist.image, name: artist.name }),
2183
+ /* @__PURE__ */ jsx35(ArtistHeader, { artist }),
2184
+ /* @__PURE__ */ jsx35(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2185
+ /* @__PURE__ */ jsx35(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2186
+ artist.tracksCount > 0 && /* @__PURE__ */ jsx35(
2207
2187
  ArtistSongs,
2208
2188
  {
2209
2189
  artist: { uuid: artist.uuid, name: artist.name },
@@ -2213,7 +2193,7 @@ function useArtistPanel({
2213
2193
  }
2214
2194
  )
2215
2195
  ] }),
2216
- extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx34(ArtistDiscography, { albums: artist.albums }) : void 0
2196
+ extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx35(ArtistDiscography, { albums: artist.albums }) : void 0
2217
2197
  };
2218
2198
  }
2219
2199
  }
@@ -2237,7 +2217,7 @@ import {
2237
2217
  useSongHistory,
2238
2218
  useTrackList as useTrackList6
2239
2219
  } from "@behindthemusictree/app-kit/genre-tree";
2240
- import { Fragment as Fragment14, jsx as jsx35, jsxs as jsxs25 } from "react/jsx-runtime";
2220
+ import { Fragment as Fragment14, jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
2241
2221
  var numberFormat = new Intl.NumberFormat("en");
2242
2222
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2243
2223
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
@@ -2245,11 +2225,11 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2245
2225
  const [open, setOpen] = useState9(false);
2246
2226
  const history = useSongHistory(uuid, getBackendBaseUrl, open);
2247
2227
  let body = null;
2248
- if (history.status === "loading") body = /* @__PURE__ */ jsx35(Skeleton6, { className: "h-10", "aria-busy": "true" });
2249
- else if (history.status === "error") body = /* @__PURE__ */ jsx35("p", { className: "text-red-600", children: "Could not load the history." });
2228
+ if (history.status === "loading") body = /* @__PURE__ */ jsx36(Skeleton6, { className: "h-10", "aria-busy": "true" });
2229
+ else if (history.status === "error") body = /* @__PURE__ */ jsx36("p", { className: "text-red-600", children: "Could not load the history." });
2250
2230
  else if (history.status === "success")
2251
- body = history.entries.length === 0 ? /* @__PURE__ */ jsx35("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx35("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs25("li", { className: "py-1.5 text-body", children: [
2252
- /* @__PURE__ */ jsx35("span", { className: "font-medium", children: entry.action }),
2231
+ body = history.entries.length === 0 ? /* @__PURE__ */ jsx36("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx36("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs25("li", { className: "py-1.5 text-body", children: [
2232
+ /* @__PURE__ */ jsx36("span", { className: "font-medium", children: entry.action }),
2253
2233
  entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs25("span", { className: "text-[var(--color-text-muted)]", children: [
2254
2234
  " ",
2255
2235
  "\u2014 ",
@@ -2263,9 +2243,9 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2263
2243
  dateFormat.format(new Date(entry.createdOn))
2264
2244
  ] })
2265
2245
  ] }, entry.uuid)) });
2266
- return /* @__PURE__ */ jsx35(PanelBleed3, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ jsxs25("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2267
- /* @__PURE__ */ jsx35("summary", { className: `mt-2.5 cursor-pointer ${PANEL_LABEL_CLASS}`, children: "History" }),
2268
- /* @__PURE__ */ jsx35("div", { className: "mt-1.5", children: body })
2246
+ return /* @__PURE__ */ jsx36(PanelBleed3, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ jsxs25("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
2247
+ /* @__PURE__ */ jsx36("summary", { className: `mt-2.5 cursor-pointer ${PANEL_LABEL_CLASS}`, children: "History" }),
2248
+ /* @__PURE__ */ jsx36("div", { className: "mt-1.5", children: body })
2269
2249
  ] }) });
2270
2250
  }
2271
2251
  function SongPanelContent(props) {
@@ -2284,13 +2264,13 @@ function SongPanelContent(props) {
2284
2264
  const genre = song.genre;
2285
2265
  fields.push({
2286
2266
  label: "Genre",
2287
- value: onSelectGenre ? /* @__PURE__ */ jsx35("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ jsx35("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs25("span", { children: [
2288
- index > 0 && /* @__PURE__ */ jsx35("span", { "aria-hidden": "true", children: "\u203A " }),
2289
- /* @__PURE__ */ jsx35("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2267
+ value: onSelectGenre ? /* @__PURE__ */ jsx36("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ jsx36("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs25("span", { children: [
2268
+ index > 0 && /* @__PURE__ */ jsx36("span", { "aria-hidden": "true", children: "\u203A " }),
2269
+ /* @__PURE__ */ jsx36("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2290
2270
  ] }, genrePlaylist.uuid)) }) : song.genre.name
2291
2271
  });
2292
2272
  } else {
2293
- fields.push({ label: "Genre", value: /* @__PURE__ */ jsx35("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2273
+ fields.push({ label: "Genre", value: /* @__PURE__ */ jsx36("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2294
2274
  }
2295
2275
  if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
2296
2276
  const viewCount = song.youtubeVideo?.viewCount;
@@ -2300,11 +2280,11 @@ function SongPanelContent(props) {
2300
2280
  const links = [];
2301
2281
  if (musicBrainzUrl)
2302
2282
  links.push(
2303
- /* @__PURE__ */ jsx35("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2283
+ /* @__PURE__ */ jsx36("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2304
2284
  );
2305
2285
  if (song.youtubeVideo)
2306
2286
  links.push(
2307
- /* @__PURE__ */ jsx35(
2287
+ /* @__PURE__ */ jsx36(
2308
2288
  "a",
2309
2289
  {
2310
2290
  href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
@@ -2319,31 +2299,31 @@ function SongPanelContent(props) {
2319
2299
  if (links.length > 0)
2320
2300
  fields.push({
2321
2301
  label: "Links",
2322
- value: /* @__PURE__ */ jsx35("span", { className: "flex gap-3", children: links })
2302
+ value: /* @__PURE__ */ jsx36("span", { className: "flex gap-3", children: links })
2323
2303
  });
2324
2304
  return /* @__PURE__ */ jsxs25(Fragment14, { children: [
2325
- /* @__PURE__ */ jsx35(PanelBleed3, { children: /* @__PURE__ */ jsxs25(ActionBand3, { children: [
2326
- /* @__PURE__ */ jsx35(
2305
+ /* @__PURE__ */ jsx36(PanelBleed3, { children: /* @__PURE__ */ jsxs25(ActionBand3, { children: [
2306
+ /* @__PURE__ */ jsx36(
2327
2307
  ActionButton5,
2328
2308
  {
2329
- icon: isPlaying ? /* @__PURE__ */ jsx35(Pause3, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx35(Play4, { "aria-hidden": "true", className: "w-5 h-5" }),
2309
+ icon: isPlaying ? /* @__PURE__ */ jsx36(Pause3, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx36(Play4, { "aria-hidden": "true", className: "w-5 h-5" }),
2330
2310
  label: isPlaying ? "Pause" : "Play",
2331
2311
  disabled: !!unplayableReason,
2332
2312
  title: unplayableReason ?? void 0,
2333
2313
  onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
2334
2314
  }
2335
2315
  ),
2336
- shareUrl && /* @__PURE__ */ jsx35(ShareActionButton, { target: shareTarget, url: shareUrl })
2316
+ shareUrl && /* @__PURE__ */ jsx36(ShareActionButton, { target: shareTarget, url: shareUrl })
2337
2317
  ] }) }),
2338
- song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx35("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx35(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
2339
- /* @__PURE__ */ jsx35(FieldList2, { items: fields }),
2340
- song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx35(PanelSection6, { title: "Essential in", children: /* @__PURE__ */ jsx35("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx35("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2341
- /* @__PURE__ */ jsx35(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2318
+ song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx36("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx36(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
2319
+ /* @__PURE__ */ jsx36(FieldList2, { items: fields }),
2320
+ song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx36(PanelSection6, { title: "Essential in", children: /* @__PURE__ */ jsx36("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx36("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
2321
+ /* @__PURE__ */ jsx36(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2342
2322
  ] });
2343
2323
  }
2344
2324
 
2345
2325
  // src/genre-tree/song/useSongPanel.tsx
2346
- import { jsx as jsx36, jsxs as jsxs26 } from "react/jsx-runtime";
2326
+ import { jsx as jsx37, jsxs as jsxs26 } from "react/jsx-runtime";
2347
2327
  function useSongPanel({
2348
2328
  uuid,
2349
2329
  scope,
@@ -2365,23 +2345,23 @@ function useSongPanel({
2365
2345
  ...panel,
2366
2346
  title: "Song",
2367
2347
  content: /* @__PURE__ */ jsxs26("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
2368
- /* @__PURE__ */ jsx36(Skeleton7, { className: "h-3 w-16" }),
2369
- /* @__PURE__ */ jsx36(Skeleton7, { className: "h-4 w-32" }),
2370
- /* @__PURE__ */ jsx36(Skeleton7, { className: "h-3 w-16" }),
2371
- /* @__PURE__ */ jsx36(Skeleton7, { className: "h-4 w-24" })
2348
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-3 w-16" }),
2349
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-4 w-32" }),
2350
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-3 w-16" }),
2351
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-4 w-24" })
2372
2352
  ] })
2373
2353
  };
2374
2354
  case "not-found":
2375
2355
  return {
2376
2356
  ...panel,
2377
2357
  title: "Song not found",
2378
- content: /* @__PURE__ */ jsx36("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2358
+ content: /* @__PURE__ */ jsx37("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2379
2359
  };
2380
2360
  case "error":
2381
2361
  return {
2382
2362
  ...panel,
2383
2363
  title: "Song",
2384
- content: /* @__PURE__ */ jsx36("p", { className: "text-red-600", children: "Could not load this song." })
2364
+ content: /* @__PURE__ */ jsx37("p", { className: "text-red-600", children: "Could not load this song." })
2385
2365
  };
2386
2366
  case "success": {
2387
2367
  const { song } = result;
@@ -2389,7 +2369,7 @@ function useSongPanel({
2389
2369
  return {
2390
2370
  ...panel,
2391
2371
  title: song.title,
2392
- content: /* @__PURE__ */ jsx36(
2372
+ content: /* @__PURE__ */ jsx37(
2393
2373
  SongPanelContent,
2394
2374
  {
2395
2375
  song,
@@ -2442,12 +2422,12 @@ function useEntityPanel({
2442
2422
  // src/genre-tree/SizeMetricToggle.tsx
2443
2423
  import { MdMusicNote, MdVisibility } from "react-icons/md";
2444
2424
  import { SegmentedControl } from "@behindthemusictree/ui";
2445
- import { jsx as jsx37 } from "react/jsx-runtime";
2425
+ import { jsx as jsx38 } from "react/jsx-runtime";
2446
2426
  function SizeMetricToggle({
2447
2427
  value,
2448
2428
  onChange
2449
2429
  }) {
2450
- return /* @__PURE__ */ jsx37(
2430
+ return /* @__PURE__ */ jsx38(
2451
2431
  SegmentedControl,
2452
2432
  {
2453
2433
  variant: "floating",
@@ -2455,15 +2435,15 @@ function SizeMetricToggle({
2455
2435
  value,
2456
2436
  onChange,
2457
2437
  options: [
2458
- { value: "songs", label: /* @__PURE__ */ jsx37(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
2459
- { value: "views", label: /* @__PURE__ */ jsx37(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
2438
+ { value: "songs", label: /* @__PURE__ */ jsx38(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
2439
+ { value: "views", label: /* @__PURE__ */ jsx38(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
2460
2440
  ]
2461
2441
  }
2462
2442
  );
2463
2443
  }
2464
2444
 
2465
2445
  // src/genre-tree/GenreTreeView.tsx
2466
- import { Fragment as Fragment15, jsx as jsx38, jsxs as jsxs27 } from "react/jsx-runtime";
2446
+ import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
2467
2447
  var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
2468
2448
  var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
2469
2449
  var noop = () => {
@@ -2555,6 +2535,7 @@ function GenreTreeView({
2555
2535
  isFetching: isTreeFetching,
2556
2536
  genrePlaylistByNodeId,
2557
2537
  visibleGenrePlaylists,
2538
+ focusUuids,
2558
2539
  groupedGenrePlaylistsByRoot,
2559
2540
  selectedGenreUuid,
2560
2541
  selectGenre,
@@ -2614,14 +2595,14 @@ function GenreTreeView({
2614
2595
  const openSongRef = useRef3(openSong);
2615
2596
  openSongRef.current = openSong;
2616
2597
  const resolvedRenderSongLink = useCallback6(
2617
- (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx38("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
2598
+ (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx39("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
2618
2599
  [renderSongLink]
2619
2600
  );
2620
2601
  const [expandedSongUuid, setExpandedSongUuid] = useState10(null);
2621
2602
  const [expandedArtistUuid, setExpandedArtistUuid] = useState10(null);
2622
2603
  const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
2623
2604
  const songBaseUrl = getBackendBaseUrl;
2624
- const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx38("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
2605
+ const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx39("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
2625
2606
  const getGenrePath = (genre) => {
2626
2607
  const path = [];
2627
2608
  let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
@@ -2663,7 +2644,7 @@ function GenreTreeView({
2663
2644
  selectGenrePlaylist(genrePlaylist2);
2664
2645
  }
2665
2646
  });
2666
- const builtInPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : entityPanel;
2647
+ const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
2667
2648
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState10(false);
2668
2649
  const [uncontrolledExpanded, setUncontrolledExpanded] = useState10(false);
2669
2650
  const isCollapsed = collapsed ?? uncontrolledCollapsed;
@@ -2691,7 +2672,7 @@ function GenreTreeView({
2691
2672
  const obscuredInset = useObscuredInset(panelRef);
2692
2673
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
2693
2674
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
2694
- const treeNodes = useMemo8(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
2675
+ const treeNodes = useMemo8(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
2695
2676
  const relations = useGenreNodeRelations(
2696
2677
  treeNodes,
2697
2678
  !overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
@@ -2702,7 +2683,7 @@ function GenreTreeView({
2702
2683
  setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
2703
2684
  }
2704
2685
  const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
2705
- const { showPopup, hidePopup } = usePopup5();
2686
+ const { showPopup, hidePopup } = usePopup4();
2706
2687
  const reopenPanel = () => {
2707
2688
  setUncontrolledCollapsed(false);
2708
2689
  onCollapsedChange?.(false);
@@ -2753,7 +2734,7 @@ function GenreTreeView({
2753
2734
  onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
2754
2735
  onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
2755
2736
  onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
2756
- /* @__PURE__ */ jsx38(
2737
+ /* @__PURE__ */ jsx39(
2757
2738
  ChangeParentPopup,
2758
2739
  {
2759
2740
  genrePlaylist: { ...genrePlaylist, criteria },
@@ -2787,15 +2768,15 @@ function GenreTreeView({
2787
2768
  const isRotationControlled = controlledAllowWheelRotation !== void 0;
2788
2769
  const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
2789
2770
  let activePanel = null;
2790
- if (overlayPanel) {
2771
+ if (otherPanel) {
2791
2772
  activePanel = {
2792
- title: overlayPanel.title,
2793
- content: overlayPanel.content,
2794
- aside: overlayPanel.extraContent,
2795
- expanded: overlayPanel.expanded ?? false,
2796
- onExpandedChange: overlayPanel.onExpandedChange,
2797
- onClose: overlayPanel.onClose,
2798
- titleInSearch: false
2773
+ title: otherPanel.title,
2774
+ content: otherPanel.content,
2775
+ aside: otherPanel.extraContent,
2776
+ expanded: otherPanel.expanded ?? false,
2777
+ onExpandedChange: otherPanel.onExpandedChange,
2778
+ onClose: otherPanel.onClose,
2779
+ searchName: otherPanel.title
2799
2780
  };
2800
2781
  } else if (selectedGenreUuid) {
2801
2782
  activePanel = {
@@ -2805,38 +2786,28 @@ function GenreTreeView({
2805
2786
  expanded: isExpanded,
2806
2787
  onExpandedChange: changeExpanded,
2807
2788
  onClose: clearSelection,
2808
- titleInSearch: true,
2789
+ searchName: genreContent.name,
2809
2790
  headerAccent: genreContent.headerAccent
2810
2791
  };
2811
- } else if (builtInPanel) {
2812
- activePanel = {
2813
- title: builtInPanel.title,
2814
- content: builtInPanel.content,
2815
- aside: builtInPanel.extraContent,
2816
- expanded: builtInPanel.expanded,
2817
- onExpandedChange: builtInPanel.onExpandedChange,
2818
- onClose: builtInPanel.onClose,
2819
- titleInSearch: true
2820
- };
2821
2792
  }
2822
2793
  const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
2823
2794
  const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
2824
2795
  edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
2825
2796
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
2826
2797
  } : void 0;
2827
- const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx38(
2798
+ const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx39(
2828
2799
  IconButton2,
2829
2800
  {
2830
2801
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
2831
2802
  style: activePanel.headerAccent && { color: "inherit" },
2832
2803
  onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
2833
- children: activePanel.expanded ? /* @__PURE__ */ jsx38(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx38(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
2804
+ children: activePanel.expanded ? /* @__PURE__ */ jsx39(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx39(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
2834
2805
  }
2835
2806
  ) : null;
2836
2807
  const renderArtistFilter = () => {
2837
2808
  if (isLoading || !filterArtist) return null;
2838
2809
  return /* @__PURE__ */ jsxs27("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
2839
- /* @__PURE__ */ jsx38(
2810
+ /* @__PURE__ */ jsx39(
2840
2811
  "button",
2841
2812
  {
2842
2813
  type: "button",
@@ -2848,7 +2819,7 @@ function GenreTreeView({
2848
2819
  children: filterArtist.name
2849
2820
  }
2850
2821
  ),
2851
- /* @__PURE__ */ jsx38(
2822
+ /* @__PURE__ */ jsx39(
2852
2823
  "button",
2853
2824
  {
2854
2825
  type: "button",
@@ -2861,7 +2832,7 @@ function GenreTreeView({
2861
2832
  ] });
2862
2833
  };
2863
2834
  const actions = /* @__PURE__ */ jsxs27(Fragment15, { children: [
2864
- !isLoading && !isError && /* @__PURE__ */ jsx38(
2835
+ !isLoading && !isError && /* @__PURE__ */ jsx39(
2865
2836
  GenreTreeSearch,
2866
2837
  {
2867
2838
  artistBaseUrl,
@@ -2884,9 +2855,9 @@ function GenreTreeView({
2884
2855
  if (songUuid) changeSong(null);
2885
2856
  selectGenre(uuid);
2886
2857
  },
2887
- selectedName: selectedGenreUuid ? genreContent.name : typeof builtInPanel?.title === "string" ? builtInPanel.title : null,
2858
+ selectedName: activePanel?.searchName ?? null,
2888
2859
  onClear: activePanel?.onClose,
2889
- showClear: activePanel?.titleInSearch ?? false,
2860
+ showClear: activePanel !== null,
2890
2861
  clearLabel: "Close panel",
2891
2862
  onPrevious: handlePrevious,
2892
2863
  onQueryChange: onSearchQueryChange,
@@ -2894,9 +2865,9 @@ function GenreTreeView({
2894
2865
  extraGroups: extraSearchGroups
2895
2866
  }
2896
2867
  ),
2897
- !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx38("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
2868
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx39("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
2898
2869
  /* @__PURE__ */ jsxs27("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: [
2899
- !isLoading && !isError && !isControlled && /* @__PURE__ */ jsx38(
2870
+ !isLoading && !isError && !isControlled && /* @__PURE__ */ jsx39(
2900
2871
  SegmentedControl2,
2901
2872
  {
2902
2873
  "aria-label": "Tree view mode",
@@ -2910,13 +2881,13 @@ function GenreTreeView({
2910
2881
  ]
2911
2882
  }
2912
2883
  ),
2913
- !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ jsx38(
2884
+ !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ jsx39(
2914
2885
  "div",
2915
2886
  {
2916
2887
  className: "flex items-center gap-1",
2917
2888
  role: "group",
2918
2889
  "aria-label": "Tree display options",
2919
- children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx38(
2890
+ children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx39(
2920
2891
  Button,
2921
2892
  {
2922
2893
  variant: allowWheelRotation ? "default" : "outline",
@@ -2927,7 +2898,7 @@ function GenreTreeView({
2927
2898
  )
2928
2899
  }
2929
2900
  ),
2930
- !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ jsx38(
2901
+ !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ jsx39(
2931
2902
  IconTextButton,
2932
2903
  {
2933
2904
  icon: Plus2,
@@ -2943,36 +2914,37 @@ function GenreTreeView({
2943
2914
  "data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
2944
2915
  className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
2945
2916
  children: [
2946
- /* @__PURE__ */ jsx38(
2917
+ /* @__PURE__ */ jsx39(
2947
2918
  "div",
2948
2919
  {
2949
2920
  className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] md:left-[var(--layout-panel-inset)] md:top-[var(--layout-panel-inset)] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
2950
2921
  children: actions
2951
2922
  }
2952
2923
  ),
2953
- !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx38(
2924
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx39(
2954
2925
  "div",
2955
2926
  {
2956
2927
  className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] md:top-[var(--layout-panel-inset)]" : "top-5"}`,
2957
2928
  children: renderArtistFilter()
2958
2929
  }
2959
2930
  ),
2960
- /* @__PURE__ */ jsx38("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs27("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
2961
- !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx38("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
2962
- isLoading ? /* @__PURE__ */ jsx38(GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ jsxs27("p", { role: "alert", className: "m-auto text-center", children: [
2931
+ /* @__PURE__ */ jsx39("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs27("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
2932
+ !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx39("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
2933
+ isLoading ? /* @__PURE__ */ jsx39(GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ jsxs27("p", { role: "alert", className: "m-auto text-center", children: [
2963
2934
  "Couldn't load the genre tree.",
2964
2935
  " ",
2965
- /* @__PURE__ */ jsx38("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
2936
+ /* @__PURE__ */ jsx39("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
2966
2937
  ] }) : viewMode === "wheel" ? (
2967
2938
  // The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
2968
2939
  // and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
2969
2940
  // Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
2970
2941
  // The panel's own Zoom in button is hidden: the search bar carries it.
2971
- /* @__PURE__ */ jsx38("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx38(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx38(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx38(
2942
+ /* @__PURE__ */ jsx39("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx39(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx39(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx39(
2972
2943
  GenrePlaylistTreeWheel,
2973
2944
  {
2974
2945
  scope,
2975
2946
  genrePlaylists: visibleGenrePlaylists,
2947
+ focusUuids,
2976
2948
  handleGenreCreationAction,
2977
2949
  handleGenreRenameAction,
2978
2950
  additionalActions,
@@ -2987,7 +2959,7 @@ function GenreTreeView({
2987
2959
  busy,
2988
2960
  floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
2989
2961
  floatingControlsLeading,
2990
- canSizeByViews && /* @__PURE__ */ jsx38(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
2962
+ canSizeByViews && /* @__PURE__ */ jsx39(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
2991
2963
  ] }) : void 0,
2992
2964
  defaultViewport,
2993
2965
  viewportRestore,
@@ -2996,11 +2968,12 @@ function GenreTreeView({
2996
2968
  ) }) })
2997
2969
  ) : viewMode === "pop-core" ? (
2998
2970
  // Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
2999
- /* @__PURE__ */ jsx38("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx38(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx38(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx38(
2971
+ /* @__PURE__ */ jsx39("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx39(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx39(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx39(
3000
2972
  GenrePlaylistTreeWheelRadialPopCore,
3001
2973
  {
3002
2974
  scope,
3003
2975
  genrePlaylists: visibleGenrePlaylists,
2976
+ focusUuids,
3004
2977
  handleGenreCreationAction,
3005
2978
  handleGenreRenameAction,
3006
2979
  additionalActions,
@@ -3015,7 +2988,7 @@ function GenreTreeView({
3015
2988
  busy,
3016
2989
  floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
3017
2990
  floatingControlsLeading,
3018
- canSizeByViews && /* @__PURE__ */ jsx38(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
2991
+ canSizeByViews && /* @__PURE__ */ jsx39(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3019
2992
  ] }) : void 0,
3020
2993
  defaultViewport,
3021
2994
  viewportRestore,
@@ -3023,12 +2996,13 @@ function GenreTreeView({
3023
2996
  ...focus
3024
2997
  }
3025
2998
  ) }) })
3026
- ) : viewMode === "outline" ? /* @__PURE__ */ jsx38("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx38(
2999
+ ) : viewMode === "outline" ? /* @__PURE__ */ jsx39("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx39(
3027
3000
  GenrePlaylistTreeWheelRadialPopCore,
3028
3001
  {
3029
3002
  outline: true,
3030
3003
  scope,
3031
3004
  genrePlaylists: visibleGenrePlaylists,
3005
+ focusUuids,
3032
3006
  handleGenreCreationAction,
3033
3007
  handleGenreRenameAction,
3034
3008
  additionalActions,
@@ -3039,13 +3013,13 @@ function GenreTreeView({
3039
3013
  selectedNodeId,
3040
3014
  readOnly
3041
3015
  }
3042
- ) }) : /* @__PURE__ */ jsx38("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
3016
+ ) }) : /* @__PURE__ */ jsx39("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
3043
3017
  ([uuid, genrePlaylistTreePerRoot]) => {
3044
- return /* @__PURE__ */ jsx38(
3018
+ return /* @__PURE__ */ jsx39(
3045
3019
  "div",
3046
3020
  {
3047
3021
  className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
3048
- children: /* @__PURE__ */ jsx38("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx38(
3022
+ children: /* @__PURE__ */ jsx39("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx39(
3049
3023
  GenrePlaylistTreePerRoot,
3050
3024
  {
3051
3025
  scope,
@@ -3058,6 +3032,7 @@ function GenreTreeView({
3058
3032
  onNodeHover: handleNodeHover,
3059
3033
  ...hostPanel,
3060
3034
  playback,
3035
+ focusUuids,
3061
3036
  selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
3062
3037
  readOnly,
3063
3038
  wheelZoom: "modifier",
@@ -3071,13 +3046,13 @@ function GenreTreeView({
3071
3046
  );
3072
3047
  }
3073
3048
  ) }),
3074
- /* @__PURE__ */ jsx38(
3049
+ /* @__PURE__ */ jsx39(
3075
3050
  DetailPanel2,
3076
3051
  {
3077
3052
  ref: panelRef,
3078
3053
  side: "left",
3079
3054
  title: activePanel?.title ?? "",
3080
- titleInSearch: activePanel?.titleInSearch,
3055
+ titleInSearch: !isLoading && !isError,
3081
3056
  headerAccent: activePanel?.headerAccent,
3082
3057
  headerActions: expandButton,
3083
3058
  onClose: activePanel?.onClose ?? noop,
@@ -3086,7 +3061,6 @@ function GenreTreeView({
3086
3061
  onExpandedChange: activePanel?.onExpandedChange,
3087
3062
  aside: activePanel?.aside,
3088
3063
  edgeTab: detailTab,
3089
- headerClassName: activePanel?.titleInSearch ? void 0 : "pt-search-clear",
3090
3064
  children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ jsxs27(Fragment15, { children: [
3091
3065
  activePanel.content,
3092
3066
  activePanel.aside
@@ -3113,7 +3087,7 @@ import { ListMusic } from "lucide-react";
3113
3087
  import { useTrackList as useTrackList7 } from "@behindthemusictree/app-kit/genre-tree";
3114
3088
  import { TrackListOriginType as TrackListOriginType3 } from "@behindthemusictree/app-kit/genre-tree";
3115
3089
  import { useTrackNoun as useTrackNoun3 } from "@behindthemusictree/app-kit/track-noun";
3116
- import { jsx as jsx39, jsxs as jsxs28 } from "react/jsx-runtime";
3090
+ import { jsx as jsx40, jsxs as jsxs28 } from "react/jsx-runtime";
3117
3091
  var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect7 : useLayoutEffect2;
3118
3092
  function TrackListSidebar({
3119
3093
  className: className2 = "",
@@ -3225,15 +3199,15 @@ function TrackListSidebar({
3225
3199
  /* @__PURE__ */ jsxs28("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3226
3200
  /* @__PURE__ */ jsxs28("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3227
3201
  /* @__PURE__ */ jsxs28("div", { className: "origin flex text-xl", children: [
3228
- /* @__PURE__ */ jsx39("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3229
- /* @__PURE__ */ jsx39("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__ */ jsx39("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3202
+ /* @__PURE__ */ jsx40("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3203
+ /* @__PURE__ */ jsx40("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__ */ jsx40("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
3230
3204
  ] }),
3231
3205
  /* @__PURE__ */ jsxs28("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3232
3206
  trackList.origin.type === TrackListOriginType3.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType3.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
3233
3207
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3234
3208
  ] })
3235
3209
  ] }),
3236
- (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx39(
3210
+ (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx40(
3237
3211
  "button",
3238
3212
  {
3239
3213
  type: "button",
@@ -3242,7 +3216,7 @@ function TrackListSidebar({
3242
3216
  "aria-expanded": !isCollapsed,
3243
3217
  className: `ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-md hover:bg-[var(--color-surface-muted)] ${isCollapsed ? "text-[var(--color-text-muted)]" : "text-white"}`,
3244
3218
  onClick: isCollapsed ? onExpand : onMinimize,
3245
- children: /* @__PURE__ */ jsx39(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3219
+ children: /* @__PURE__ */ jsx40(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3246
3220
  }
3247
3221
  )
3248
3222
  ] }),
@@ -3256,7 +3230,7 @@ function TrackListSidebar({
3256
3230
  onPointerDown: markUserScrollIntent,
3257
3231
  className: layout === "inline" ? "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0" : "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0",
3258
3232
  children: [
3259
- trackList.tracks.map((track, index) => /* @__PURE__ */ jsx39("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx39(
3233
+ trackList.tracks.map((track, index) => /* @__PURE__ */ jsx40("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx40(
3260
3234
  TrackItem,
3261
3235
  {
3262
3236
  track,
@@ -3267,7 +3241,7 @@ function TrackListSidebar({
3267
3241
  renderSongLink
3268
3242
  }
3269
3243
  ) }, track.uuid)),
3270
- hasMore && /* @__PURE__ */ jsx39("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3244
+ hasMore && /* @__PURE__ */ jsx40("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3271
3245
  ]
3272
3246
  }
3273
3247
  )
@@ -3292,7 +3266,7 @@ import { PanelEdgeTab } from "@behindthemusictree/ui";
3292
3266
  import { Pause as Pause4, Play as Play5, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
3293
3267
  import { formatTime, useTrackList as useTrackList8 } from "@behindthemusictree/app-kit/genre-tree";
3294
3268
  import { PlayStates as PlayStates4, useCurrentTime, usePlayer as usePlayer11 } from "@behindthemusictree/app-kit/player";
3295
- import { jsx as jsx40, jsxs as jsxs29 } from "react/jsx-runtime";
3269
+ import { jsx as jsx41, jsxs as jsxs29 } from "react/jsx-runtime";
3296
3270
  function PlayerControls() {
3297
3271
  const {
3298
3272
  duration,
@@ -3318,8 +3292,8 @@ function PlayerControls() {
3318
3292
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3319
3293
  children: [
3320
3294
  /* @__PURE__ */ jsxs29("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
3321
- /* @__PURE__ */ jsx40("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
3322
- /* @__PURE__ */ jsx40(
3295
+ /* @__PURE__ */ jsx41("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
3296
+ /* @__PURE__ */ jsx41(
3323
3297
  "input",
3324
3298
  {
3325
3299
  "aria-label": "Seek",
@@ -3333,11 +3307,11 @@ function PlayerControls() {
3333
3307
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3334
3308
  }
3335
3309
  ),
3336
- /* @__PURE__ */ jsx40("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
3310
+ /* @__PURE__ */ jsx41("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
3337
3311
  ] }),
3338
3312
  /* @__PURE__ */ jsxs29("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3339
3313
  /* @__PURE__ */ jsxs29("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3340
- /* @__PURE__ */ jsx40(
3314
+ /* @__PURE__ */ jsx41(
3341
3315
  "button",
3342
3316
  {
3343
3317
  type: "button",
@@ -3346,10 +3320,10 @@ function PlayerControls() {
3346
3320
  disabled: !isReady || !hasPreviousTrack,
3347
3321
  onClick: playPreviousTrack,
3348
3322
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
3349
- children: /* @__PURE__ */ jsx40(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3323
+ children: /* @__PURE__ */ jsx41(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3350
3324
  }
3351
3325
  ),
3352
- /* @__PURE__ */ jsx40(
3326
+ /* @__PURE__ */ jsx41(
3353
3327
  "button",
3354
3328
  {
3355
3329
  type: "button",
@@ -3358,10 +3332,10 @@ function PlayerControls() {
3358
3332
  disabled: !isReady,
3359
3333
  onClick: handlePlayPauseAction,
3360
3334
  className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-white text-zinc-950 hover:bg-zinc-200 disabled:cursor-not-allowed disabled:opacity-40",
3361
- children: isPlaying ? /* @__PURE__ */ jsx40(Pause4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx40(Play5, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3335
+ children: isPlaying ? /* @__PURE__ */ jsx41(Pause4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx41(Play5, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3362
3336
  }
3363
3337
  ),
3364
- /* @__PURE__ */ jsx40(
3338
+ /* @__PURE__ */ jsx41(
3365
3339
  "button",
3366
3340
  {
3367
3341
  type: "button",
@@ -3370,14 +3344,14 @@ function PlayerControls() {
3370
3344
  disabled: !isReady || !hasNextTrack,
3371
3345
  onClick: playNextTrack,
3372
3346
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
3373
- children: /* @__PURE__ */ jsx40(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3347
+ children: /* @__PURE__ */ jsx41(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3374
3348
  }
3375
3349
  )
3376
3350
  ] }),
3377
3351
  /* @__PURE__ */ jsxs29("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
3378
- volume === 0 ? /* @__PURE__ */ jsx40(VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ jsx40(Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3379
- /* @__PURE__ */ jsx40("span", { className: "sr-only", children: "Volume" }),
3380
- /* @__PURE__ */ jsx40(
3352
+ volume === 0 ? /* @__PURE__ */ jsx41(VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ jsx41(Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
3353
+ /* @__PURE__ */ jsx41("span", { className: "sr-only", children: "Volume" }),
3354
+ /* @__PURE__ */ jsx41(
3381
3355
  "input",
3382
3356
  {
3383
3357
  "aria-label": "Volume",
@@ -3399,7 +3373,7 @@ function PlayerControls() {
3399
3373
  }
3400
3374
 
3401
3375
  // src/genre-tree/PlayerPanel.tsx
3402
- import { jsx as jsx41, jsxs as jsxs30 } from "react/jsx-runtime";
3376
+ import { jsx as jsx42, jsxs as jsxs30 } from "react/jsx-runtime";
3403
3377
  var PANEL_ID = "player-panel";
3404
3378
  var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
3405
3379
  function PlayerPanel({
@@ -3452,7 +3426,7 @@ function PlayerPanel({
3452
3426
  ),
3453
3427
  style: isExtended ? { width: extendedWidth } : void 0,
3454
3428
  children: [
3455
- /* @__PURE__ */ jsx41(
3429
+ /* @__PURE__ */ jsx42(
3456
3430
  PanelEdgeTab,
3457
3431
  {
3458
3432
  side: "right",
@@ -3471,19 +3445,19 @@ function PlayerPanel({
3471
3445
  ),
3472
3446
  children: [
3473
3447
  /* @__PURE__ */ jsxs30("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
3474
- /* @__PURE__ */ jsx41("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ jsx41("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3448
+ /* @__PURE__ */ jsx42("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ jsx42("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
3475
3449
  /* @__PURE__ */ jsxs30("div", { className: "relative w-full shrink-0", children: [
3476
- /* @__PURE__ */ jsx41(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3450
+ /* @__PURE__ */ jsx42(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3477
3451
  playerTrackObject?.loadError && /* @__PURE__ */ jsxs30("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3478
3452
  playerTrackObject.loadError,
3479
- /* @__PURE__ */ jsx41("button", { type: "button", className: "ml-2 underline", onClick: () => {
3453
+ /* @__PURE__ */ jsx42("button", { type: "button", className: "ml-2 underline", onClick: () => {
3480
3454
  void retryPlayback();
3481
3455
  }, children: "Retry" })
3482
3456
  ] })
3483
3457
  ] }),
3484
- /* @__PURE__ */ jsx41(PlayerControls, {})
3458
+ /* @__PURE__ */ jsx42(PlayerControls, {})
3485
3459
  ] }),
3486
- /* @__PURE__ */ jsx41(
3460
+ /* @__PURE__ */ jsx42(
3487
3461
  TrackListSidebar,
3488
3462
  {
3489
3463
  layout: "inline",
@@ -3510,11 +3484,11 @@ function PlayerPanel({
3510
3484
  import { v4 as uuidv4 } from "uuid";
3511
3485
  import { AiFillStar, AiOutlineStar } from "react-icons/ai";
3512
3486
  import { FORM_RATING_NULL_VALUE } from "@behindthemusictree/app-kit/genre-tree";
3513
- import { jsx as jsx42, jsxs as jsxs31 } from "react/jsx-runtime";
3487
+ import { jsx as jsx43, jsxs as jsxs31 } from "react/jsx-runtime";
3514
3488
  function Rating({ rating, handleChange }) {
3515
3489
  const instanceId = uuidv4();
3516
- return /* @__PURE__ */ jsx42("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs31("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
3517
- /* @__PURE__ */ jsx42(
3490
+ return /* @__PURE__ */ jsx43("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs31("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
3491
+ /* @__PURE__ */ jsx43(
3518
3492
  "input",
3519
3493
  {
3520
3494
  id: `star-${instanceId}-${i}`,
@@ -3526,16 +3500,16 @@ function Rating({ rating, handleChange }) {
3526
3500
  onChange: handleChange
3527
3501
  }
3528
3502
  ),
3529
- /* @__PURE__ */ jsx42("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs31("div", { className: "flex", children: [
3530
- /* @__PURE__ */ jsx42(
3503
+ /* @__PURE__ */ jsx43("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs31("div", { className: "flex", children: [
3504
+ /* @__PURE__ */ jsx43(
3531
3505
  AiOutlineStar,
3532
3506
  {
3533
3507
  className: `w-7 h-7 absolute top-0 left-0 ${rating === FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
3534
3508
  }
3535
3509
  ),
3536
- /* @__PURE__ */ jsx42("span", { className: "flex-grow w-8" }),
3510
+ /* @__PURE__ */ jsx43("span", { className: "flex-grow w-8" }),
3537
3511
  " "
3538
- ] }) : /* @__PURE__ */ jsx42(
3512
+ ] }) : /* @__PURE__ */ jsx43(
3539
3513
  AiFillStar,
3540
3514
  {
3541
3515
  className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
@@ -3547,7 +3521,7 @@ function Rating({ rating, handleChange }) {
3547
3521
  // src/genre-tree/EntityDetailPanel.tsx
3548
3522
  import { useState as useState12 } from "react";
3549
3523
  import { DetailPanel as DetailPanel3 } from "@behindthemusictree/ui";
3550
- import { Fragment as Fragment16, jsx as jsx43, jsxs as jsxs32 } from "react/jsx-runtime";
3524
+ import { Fragment as Fragment16, jsx as jsx44, jsxs as jsxs32 } from "react/jsx-runtime";
3551
3525
  function EntityDetailPanel({
3552
3526
  scope,
3553
3527
  getBackendBaseUrl,
@@ -3570,8 +3544,8 @@ function EntityDetailPanel({
3570
3544
  getBackendBaseUrl,
3571
3545
  getArtistBackendBaseUrl,
3572
3546
  getShareUrl,
3573
- renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx43("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
3574
- renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ jsx43("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
3547
+ renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx44("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
3548
+ renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ jsx44("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
3575
3549
  song: {
3576
3550
  uuid: songUuid,
3577
3551
  expanded: songUuid !== null && expandedSongUuid === songUuid,
@@ -3587,7 +3561,7 @@ function EntityDetailPanel({
3587
3561
  ...navigation
3588
3562
  });
3589
3563
  if (!panel) return null;
3590
- return /* @__PURE__ */ jsx43(
3564
+ return /* @__PURE__ */ jsx44(
3591
3565
  DetailPanel3,
3592
3566
  {
3593
3567
  side: "left",
@@ -3633,4 +3607,4 @@ export {
3633
3607
  GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
3634
3608
  GenreTreeOutlineSkeleton
3635
3609
  };
3636
- //# sourceMappingURL=chunk-2K2SQOUJ.js.map
3610
+ //# sourceMappingURL=chunk-I2TEOHVG.js.map