@behindthemusictree/app-kit-ui 39.0.0 → 40.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,10 +41,26 @@ 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
 
49
+ // src/genre-tree/playlist-tree/toGenreNodeStyles.ts
50
+ function toGenreNodeStyles(relations, genrePlaylists) {
51
+ const styles = /* @__PURE__ */ new Map();
52
+ if (!relations) return styles;
53
+ const genreUuidByPlaylistUuid = new Map(
54
+ genrePlaylists.flatMap((genrePlaylist) => genrePlaylist.criteria ? [[genrePlaylist.uuid, genrePlaylist.criteria.uuid]] : [])
55
+ );
56
+ const drawn = [relations, relations.parentNode, ...relations.childNodes, ...relations.ancestorNodes];
57
+ for (const entry of drawn) {
58
+ const genreUuid = entry && genreUuidByPlaylistUuid.get(entry.node.id);
59
+ if (entry && genreUuid) styles.set(genreUuid, { fill: entry.fill, textColor: entry.textColor });
60
+ }
61
+ return styles;
62
+ }
63
+
48
64
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
49
65
  import { genrePlaylistTrackCount as genrePlaylistTrackCount2 } from "@behindthemusictree/app-kit/genre-tree";
50
66
  import { useCallback as useCallback2, useMemo as useMemo2 } from "react";
@@ -53,24 +69,27 @@ import {
53
69
  getGenreTreeColor
54
70
  } from "@behindthemusictree/genre-tree-view";
55
71
  import { usePlayer } from "@behindthemusictree/app-kit/player";
56
- import { usePopup } from "@behindthemusictree/app-kit/popup";
57
- import { ErrorCode } from "@behindthemusictree/app-kit/transport";
58
72
 
59
- // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
73
+ // src/genre-tree/playlist-tree/useGenrePlaylistPlayback.tsx
60
74
  import { useCallback, useMemo } from "react";
61
75
  import {
62
76
  TrackListOriginType,
77
+ TracksFailureAlreadyReportedError,
63
78
  useTrackList,
64
79
  GenreCatalogueCountsSchema,
65
80
  defaultGenreMusicSelection,
66
81
  genreMusicSelectionKey
67
82
  } from "@behindthemusictree/app-kit/genre-tree";
83
+ import { usePopup } from "@behindthemusictree/app-kit/popup";
84
+ import { AppError, ErrorCode } from "@behindthemusictree/app-kit/transport";
85
+ import { jsx } from "react/jsx-runtime";
68
86
  function useGenrePlaylistPlayback({
69
87
  scope,
70
88
  genrePlaylists,
71
89
  artist = null
72
90
  }) {
73
91
  const { trackList, playNewTrackListFromGenrePlaylist } = useTrackList();
92
+ const { showPopup } = usePopup();
74
93
  const origin = trackList?.origin;
75
94
  const playing = useMemo(() => {
76
95
  if (!origin || origin.type !== TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
@@ -82,14 +101,20 @@ function useGenrePlaylistPlayback({
82
101
  }, [origin, artist, genrePlaylists, scope]);
83
102
  const play = useCallback(async (playlist, requested) => {
84
103
  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]);
104
+ try {
105
+ const selection = scope === "reference" && playlist.criteria !== null ? requested ?? defaultGenreMusicSelection(GenreCatalogueCountsSchema.parse(playlist)) : void 0;
106
+ await (selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope));
107
+ } catch (error) {
108
+ console.error("Failed to load genre playlist tracks:", error);
109
+ if (error instanceof TracksFailureAlreadyReportedError) return;
110
+ showPopup(/* @__PURE__ */ jsx(InternalErrorPopup, { errorCode: error instanceof AppError ? error.code : ErrorCode.CLIENT_UNKNOWN }));
111
+ }
112
+ }, [artist, scope, playNewTrackListFromGenrePlaylist, showPopup]);
88
113
  return useMemo(() => ({ playing, play }), [playing, play]);
89
114
  }
90
115
 
91
116
  // src/genre-tree/playlist-tree/TreePerRoot.tsx
92
- import { jsx } from "react/jsx-runtime";
117
+ import { jsx as jsx2 } from "react/jsx-runtime";
93
118
  function GenrePlaylistTreePerRoot({
94
119
  scope,
95
120
  className: className2,
@@ -108,25 +133,16 @@ function GenrePlaylistTreePerRoot({
108
133
  viewportRestore,
109
134
  onViewportChange,
110
135
  playback,
136
+ focusUuids,
111
137
  ...hostPanel
112
138
  }) {
113
139
  const { isPlaying, setIsPlaying } = usePlayer();
114
140
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
115
141
  const { playing, play } = playback ?? defaultPlayback;
116
142
  const playingNodeId = playing?.uuid ?? null;
117
- const { showPopup } = usePopup();
118
143
  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]
144
+ () => toTreeNodes(genrePlaylistTreePerRoot, "songs", focusUuids),
145
+ [genrePlaylistTreePerRoot, focusUuids]
130
146
  );
131
147
  const handlePlayPause = useCallback2(
132
148
  (nodeId) => {
@@ -139,18 +155,14 @@ function GenrePlaylistTreePerRoot({
139
155
  if (genrePlaylistTrackCount2(genrePlaylist) === 0) {
140
156
  return;
141
157
  }
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
- });
158
+ void play(genrePlaylist);
146
159
  },
147
160
  [
148
161
  genrePlaylistTreePerRoot,
149
162
  playingNodeId,
150
163
  isPlaying,
151
164
  setIsPlaying,
152
- play,
153
- showPopup
165
+ play
154
166
  ]
155
167
  );
156
168
  const handleAddChild = useCallback2(
@@ -173,7 +185,7 @@ function GenrePlaylistTreePerRoot({
173
185
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
174
186
  }
175
187
  }, []);
176
- return /* @__PURE__ */ jsx(
188
+ return /* @__PURE__ */ jsx2(
177
189
  GenreTree,
178
190
  {
179
191
  className: className2,
@@ -206,9 +218,7 @@ import {
206
218
  GenreTreeWheel
207
219
  } from "@behindthemusictree/genre-tree-view";
208
220
  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";
221
+ import { jsx as jsx3 } from "react/jsx-runtime";
212
222
  function GenrePlaylistTreeWheel({
213
223
  scope,
214
224
  className: className2,
@@ -229,24 +239,16 @@ function GenrePlaylistTreeWheel({
229
239
  viewportRestore,
230
240
  onViewportChange,
231
241
  playback,
242
+ focusUuids,
232
243
  ...hostPanel
233
244
  }) {
234
245
  const { isPlaying, setIsPlaying } = usePlayer2();
235
246
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
236
247
  const { playing, play } = playback ?? defaultPlayback;
237
248
  const playingNodeId = playing?.uuid ?? null;
238
- const { showPopup } = usePopup2();
239
249
  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]
250
+ () => toTreeNodes(genrePlaylists, sizeMetric, focusUuids),
251
+ [genrePlaylists, sizeMetric, focusUuids]
250
252
  );
251
253
  const handlePlayPause = useCallback3(
252
254
  (nodeId) => {
@@ -259,18 +261,14 @@ function GenrePlaylistTreeWheel({
259
261
  if (genrePlaylistTrackCount3(genrePlaylist) === 0) {
260
262
  return;
261
263
  }
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
- });
264
+ void play(genrePlaylist);
266
265
  },
267
266
  [
268
267
  genrePlaylists,
269
268
  playingNodeId,
270
269
  isPlaying,
271
270
  setIsPlaying,
272
- play,
273
- showPopup
271
+ play
274
272
  ]
275
273
  );
276
274
  const handleAddChild = useCallback3(
@@ -293,7 +291,7 @@ function GenrePlaylistTreeWheel({
293
291
  if (confirm(`Are you sure you want to delete "${node.name}"?`)) {
294
292
  }
295
293
  }, []);
296
- return /* @__PURE__ */ jsx2(
294
+ return /* @__PURE__ */ jsx3(
297
295
  GenreTreeWheel,
298
296
  {
299
297
  className: className2,
@@ -328,7 +326,7 @@ import {
328
326
  GenreTreeWheelRadialPopCore
329
327
  } from "@behindthemusictree/genre-tree-view";
330
328
  import { usePlayer as usePlayer3 } from "@behindthemusictree/app-kit/player";
331
- import { jsx as jsx3 } from "react/jsx-runtime";
329
+ import { jsx as jsx4 } from "react/jsx-runtime";
332
330
  function GenrePlaylistTreeWheelRadialPopCore(props) {
333
331
  const {
334
332
  scope,
@@ -361,7 +359,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
361
359
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
362
360
  const { playing, play } = playback ?? defaultPlayback;
363
361
  const playingNodeId = playing?.uuid ?? null;
364
- const nodes = useMemo4(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
362
+ const nodes = useMemo4(() => toTreeNodes(genrePlaylists, sizeMetric, props.focusUuids), [genrePlaylists, sizeMetric, props.focusUuids]);
365
363
  const handlePlayPause = useCallback4(
366
364
  (nodeId) => {
367
365
  const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
@@ -373,9 +371,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
373
371
  if (genrePlaylistTrackCount4(genrePlaylist) === 0) {
374
372
  return;
375
373
  }
376
- play(genrePlaylist).catch((error) => {
377
- console.error("Failed to load genre playlist tracks:", error);
378
- });
374
+ void play(genrePlaylist);
379
375
  },
380
376
  [genrePlaylists, playingNodeId, isPlaying, setIsPlaying, play]
381
377
  );
@@ -415,7 +411,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
415
411
  onSelectedNodeChange,
416
412
  ...hostPanel
417
413
  };
418
- return outline ? /* @__PURE__ */ jsx3(GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ jsx3(
414
+ return outline ? /* @__PURE__ */ jsx4(GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ jsx4(
419
415
  GenreTreeWheelRadialPopCore,
420
416
  {
421
417
  ...treeProps,
@@ -432,7 +428,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
432
428
 
433
429
  // src/genre-tree/GenreTreeWheelHandoff.tsx
434
430
  import { useEffect, useRef, useState } from "react";
435
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
431
+ import { jsx as jsx5, jsxs } from "react/jsx-runtime";
436
432
  function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
437
433
  const [revealed, setRevealed] = useState(false);
438
434
  const onRevealedChangeRef = useRef(onRevealedChange);
@@ -452,7 +448,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
452
448
  };
453
449
  }, []);
454
450
  return /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full", children: [
455
- /* @__PURE__ */ jsx4(
451
+ /* @__PURE__ */ jsx5(
456
452
  "div",
457
453
  {
458
454
  className: "absolute inset-0 transition-opacity duration-150 ease-out",
@@ -461,7 +457,7 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
461
457
  children
462
458
  }
463
459
  ),
464
- !revealed && /* @__PURE__ */ jsx4("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
460
+ !revealed && /* @__PURE__ */ jsx5("div", { className: "absolute inset-0", "aria-hidden": "true", children: skeleton })
465
461
  ] });
466
462
  }
467
463
 
@@ -476,7 +472,7 @@ import { useGenreSearch } from "@behindthemusictree/app-kit/genre-tree";
476
472
  // src/genre-tree/search/SearchBox.tsx
477
473
  import { useState as useState2 } from "react";
478
474
  import { SearchCombobox } from "@behindthemusictree/ui";
479
- import { jsx as jsx5 } from "react/jsx-runtime";
475
+ import { jsx as jsx6 } from "react/jsx-runtime";
480
476
  function SearchBox({
481
477
  label,
482
478
  placeholder,
@@ -498,7 +494,7 @@ function SearchBox({
498
494
  groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
499
495
  onPicked?.(groupKey, itemKey);
500
496
  };
501
- return /* @__PURE__ */ jsx5("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ jsx5(
497
+ return /* @__PURE__ */ jsx6("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ jsx6(
502
498
  SearchCombobox,
503
499
  {
504
500
  ariaLabel: label,
@@ -564,7 +560,7 @@ import {
564
560
  isSongSearchResultPlayable,
565
561
  useSongSearch
566
562
  } from "@behindthemusictree/app-kit/genre-tree";
567
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
563
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
568
564
  function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
569
565
  const search = useSongSearch(getBaseUrl, enabled ? text : "");
570
566
  if (!enabled) return void 0;
@@ -574,8 +570,8 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
574
570
  items: search.songs.map((song) => ({
575
571
  key: song.uuid,
576
572
  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 ") })
573
+ /* @__PURE__ */ jsx7("span", { className: "block truncate font-medium", children: song.title }),
574
+ /* @__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
575
  ] }),
580
576
  onSelect: () => onSelect(song)
581
577
  })),
@@ -588,7 +584,7 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
588
584
  import {
589
585
  useGenreRemoteSearch
590
586
  } from "@behindthemusictree/app-kit/genre-tree";
591
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
587
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
592
588
  function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
593
589
  const search = useGenreRemoteSearch(getBaseUrl, text, options);
594
590
  const genres = filter ? search.genres.filter(filter) : search.genres;
@@ -600,9 +596,9 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
600
596
  content: /* @__PURE__ */ jsxs4(Fragment2, { children: [
601
597
  /* @__PURE__ */ jsxs4("span", { className: "block truncate font-medium", children: [
602
598
  genre.name,
603
- genre.isUmbrella && /* @__PURE__ */ jsx7("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
599
+ genre.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-caption font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
604
600
  ] }),
605
- /* @__PURE__ */ jsx7("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
601
+ /* @__PURE__ */ jsx8("span", { className: "block truncate text-caption text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
606
602
  ] }),
607
603
  onSelect: () => onSelect(genre)
608
604
  })),
@@ -612,7 +608,7 @@ function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres",
612
608
  }
613
609
 
614
610
  // src/genre-tree/GenreSearch.tsx
615
- import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
611
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
616
612
  function GenreSearch({
617
613
  genrePlaylists,
618
614
  onSelect,
@@ -643,7 +639,7 @@ function GenreSearch({
643
639
  key: genrePlaylist.uuid,
644
640
  content: /* @__PURE__ */ jsxs5(Fragment3, { children: [
645
641
  genrePlaylist.name,
646
- genrePlaylist.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
642
+ genrePlaylist.isUmbrella && /* @__PURE__ */ jsx9("span", { className: "ml-2 text-caption italic text-[var(--color-text-muted)]", children: "umbrella" })
647
643
  ] }),
648
644
  onSelect: () => select(genrePlaylist)
649
645
  }))
@@ -652,7 +648,7 @@ function GenreSearch({
652
648
  (group) => active ? group : { ...group, items: [], status: "", loading: false }
653
649
  )
654
650
  ];
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(
651
+ 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
652
  SearchBox,
657
653
  {
658
654
  label,
@@ -672,14 +668,14 @@ function GenreSearch({
672
668
  clear();
673
669
  onQueryChange?.("");
674
670
  } : 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" }) }),
671
+ 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
672
  onFocus
677
673
  }
678
674
  ) });
679
675
  }
680
676
 
681
677
  // src/genre-tree/GenreTreeSearch.tsx
682
- import { jsx as jsx9 } from "react/jsx-runtime";
678
+ import { jsx as jsx10 } from "react/jsx-runtime";
683
679
  function GenreTreeSearch({
684
680
  artistBaseUrl,
685
681
  songBaseUrl,
@@ -699,7 +695,7 @@ function GenreTreeSearch({
699
695
  const deferredQuery = useDeferredValue(query);
700
696
  const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
701
697
  const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
702
- const render = (hostGroups) => /* @__PURE__ */ jsx9(
698
+ const render = (hostGroups) => /* @__PURE__ */ jsx10(
703
699
  GenreSearch,
704
700
  {
705
701
  ...props,
@@ -714,7 +710,7 @@ function GenreTreeSearch({
714
710
  extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
715
711
  }
716
712
  );
717
- return SearchGroups ? /* @__PURE__ */ jsx9(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
713
+ return SearchGroups ? /* @__PURE__ */ jsx10(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
718
714
  }
719
715
 
720
716
  // src/genre-tree/GenreDetailPanel.tsx
@@ -751,7 +747,7 @@ import { ActionBand, ActionButton as ActionButton2, PanelBleed } from "@behindth
751
747
  // src/genre-tree/ShareActionButton.tsx
752
748
  import { Share2 as Share22 } from "lucide-react";
753
749
  import { ActionButton } from "@behindthemusictree/ui";
754
- import { usePopup as usePopup3 } from "@behindthemusictree/app-kit/popup";
750
+ import { usePopup as usePopup2 } from "@behindthemusictree/app-kit/popup";
755
751
 
756
752
  // src/genre-tree/SharePopup.tsx
757
753
  import { useCallback as useCallback5, useEffect as useEffect2, useMemo as useMemo5, useState as useState4 } from "react";
@@ -760,12 +756,12 @@ import { FaWhatsapp } from "react-icons/fa";
760
756
  import { SiGmail } from "react-icons/si";
761
757
 
762
758
  // src/genre-tree/ShareDestinationButton.tsx
763
- import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
759
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
764
760
  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
761
  function Contents({ icon, label }) {
766
762
  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 })
763
+ /* @__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 }),
764
+ /* @__PURE__ */ jsx11("span", { className: "max-w-full truncate", children: label })
769
765
  ] });
770
766
  }
771
767
  function ShareDestinationButton({
@@ -777,29 +773,29 @@ function ShareDestinationButton({
777
773
  ariaLabel
778
774
  }) {
779
775
  if (href) {
780
- return /* @__PURE__ */ jsx10("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
776
+ return /* @__PURE__ */ jsx11("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx11(Contents, { icon, label }) });
781
777
  }
782
- return /* @__PURE__ */ jsx10("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
778
+ return /* @__PURE__ */ jsx11("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx11(Contents, { icon, label }) });
783
779
  }
784
780
 
785
781
  // src/genre-tree/SharePreviewCard.tsx
786
782
  import { Music, Music2, UserRound } from "lucide-react";
787
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
783
+ import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
788
784
  function SharePreviewCard({ target, url }) {
789
785
  const Icon = { genre: Music2, artist: UserRound, song: Music }[target.kind];
790
786
  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" }) }),
787
+ 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: [
788
+ /* @__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
789
  /* @__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 })
790
+ /* @__PURE__ */ jsx12("span", { className: "text-caption font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
791
+ /* @__PURE__ */ jsx12("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
792
+ /* @__PURE__ */ jsx12("span", { className: "truncate text-body text-[var(--color-primary)]", title: url, children: url })
797
793
  ] })
798
794
  ] }) });
799
795
  }
800
796
 
801
797
  // src/genre-tree/SharePopup.tsx
802
- import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
798
+ import { Fragment as Fragment5, jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
803
799
  function resolveAbsoluteUrl(url) {
804
800
  try {
805
801
  const parsed = new URL(url, window.location.href);
@@ -875,7 +871,7 @@ function SharePopup({ target, url, onClose }) {
875
871
  }
876
872
  };
877
873
  return /* @__PURE__ */ jsxs8(Fragment5, { children: [
878
- /* @__PURE__ */ jsx12(
874
+ /* @__PURE__ */ jsx13(
879
875
  BasePopup_default,
880
876
  {
881
877
  title: "Share",
@@ -891,69 +887,69 @@ function SharePopup({ target, url, onClose }) {
891
887
  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
888
  contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
893
889
  children: /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-4", children: [
894
- /* @__PURE__ */ jsx12(SharePreviewCard, { target, url: absoluteUrl || url }),
890
+ /* @__PURE__ */ jsx13(SharePreviewCard, { target, url: absoluteUrl || url }),
895
891
  /* @__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(
892
+ /* @__PURE__ */ jsx13(
897
893
  ShareDestinationButton,
898
894
  {
899
895
  label: "Copy Link",
900
- icon: /* @__PURE__ */ jsx12(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
896
+ icon: /* @__PURE__ */ jsx13(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
901
897
  onClick: () => void handleCopy(),
902
898
  ariaLabel: "Copy link",
903
899
  disabled: !absoluteUrl
904
900
  }
905
901
  ),
906
- /* @__PURE__ */ jsx12(
902
+ /* @__PURE__ */ jsx13(
907
903
  ShareDestinationButton,
908
904
  {
909
905
  label: "WhatsApp",
910
- icon: /* @__PURE__ */ jsx12(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
906
+ icon: /* @__PURE__ */ jsx13(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
911
907
  href: destinationUrls?.whatsapp,
912
908
  ariaLabel: `Share ${target.name} via WhatsApp`,
913
909
  disabled: !absoluteUrl
914
910
  }
915
911
  ),
916
- /* @__PURE__ */ jsx12(
912
+ /* @__PURE__ */ jsx13(
917
913
  ShareDestinationButton,
918
914
  {
919
915
  label: "Gmail",
920
- icon: /* @__PURE__ */ jsx12(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
916
+ icon: /* @__PURE__ */ jsx13(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
921
917
  href: destinationUrls?.gmail,
922
918
  ariaLabel: `Email ${target.name} with Gmail`,
923
919
  disabled: !absoluteUrl
924
920
  }
925
921
  ),
926
- canUseNativeShare && /* @__PURE__ */ jsx12(
922
+ canUseNativeShare && /* @__PURE__ */ jsx13(
927
923
  ShareDestinationButton,
928
924
  {
929
925
  label: "More",
930
- icon: /* @__PURE__ */ jsx12(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
926
+ icon: /* @__PURE__ */ jsx13(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
931
927
  onClick: () => void handleNativeShare(),
932
928
  ariaLabel: "More sharing options",
933
929
  disabled: !absoluteUrl
934
930
  }
935
931
  )
936
932
  ] }),
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 })
933
+ copyStatus === "failed" && /* @__PURE__ */ jsx13("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: "Could not copy the link." }),
934
+ shareError && /* @__PURE__ */ jsx13("p", { role: "alert", className: "text-center text-body text-[var(--color-red-500)]", children: shareError })
939
935
  ] })
940
936
  }
941
937
  ),
942
- toastId > 0 && /* @__PURE__ */ jsx12(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
938
+ toastId > 0 && /* @__PURE__ */ jsx13(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
943
939
  ] });
944
940
  }
945
941
 
946
942
  // src/genre-tree/ShareActionButton.tsx
947
- import { jsx as jsx13 } from "react/jsx-runtime";
943
+ import { jsx as jsx14 } from "react/jsx-runtime";
948
944
  function ShareActionButton({ target, url }) {
949
- const { showPopup, hidePopup } = usePopup3();
945
+ const { showPopup, hidePopup } = usePopup2();
950
946
  const openSharePopup = () => {
951
- showPopup(/* @__PURE__ */ jsx13(SharePopup, { target, url, onClose: hidePopup }));
947
+ showPopup(/* @__PURE__ */ jsx14(SharePopup, { target, url, onClose: hidePopup }));
952
948
  };
953
- return /* @__PURE__ */ jsx13(
949
+ return /* @__PURE__ */ jsx14(
954
950
  ActionButton,
955
951
  {
956
- icon: /* @__PURE__ */ jsx13(Share22, { "aria-hidden": "true", className: "w-5 h-5" }),
952
+ icon: /* @__PURE__ */ jsx14(Share22, { "aria-hidden": "true", className: "w-5 h-5" }),
957
953
  label: "Share",
958
954
  "aria-label": `Share ${target.name}`,
959
955
  title: `Share ${target.name}`,
@@ -963,7 +959,7 @@ function ShareActionButton({ target, url }) {
963
959
  }
964
960
 
965
961
  // src/genre-tree/GenreActionBand.tsx
966
- import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
962
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
967
963
  function GenreActionBand({
968
964
  node,
969
965
  playActions,
@@ -984,24 +980,24 @@ function GenreActionBand({
984
980
  items.push({
985
981
  key: "zoom-in",
986
982
  label: "Focus",
987
- icon: /* @__PURE__ */ jsx14(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
983
+ icon: /* @__PURE__ */ jsx15(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
988
984
  onClick: onZoomIn
989
985
  });
990
986
  }
991
987
  if (onAddChild) {
992
- adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx14(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
988
+ adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx15(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
993
989
  }
994
990
  if (onRename) {
995
- adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx14(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
991
+ adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx15(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
996
992
  }
997
993
  if (onChangeParent) {
998
- adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx14(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
994
+ adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx15(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
999
995
  }
1000
996
  if (onToggleSide) {
1001
997
  adminItems.push({
1002
998
  key: "toggle-side",
1003
999
  label: side === "pop" ? "Make core" : "Make pop",
1004
- icon: /* @__PURE__ */ jsx14(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1000
+ icon: /* @__PURE__ */ jsx15(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
1005
1001
  onClick: onToggleSide,
1006
1002
  disabled: !!toggleSideDisabledReason,
1007
1003
  title: toggleSideDisabledReason
@@ -1016,9 +1012,9 @@ function GenreActionBand({
1016
1012
  disabled: action.enabled ? !action.enabled(node) : false
1017
1013
  });
1018
1014
  }
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));
1015
+ 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
1016
  if (!playActions && items.length === 0 && adminItems.length === 0 && !(shareTarget && shareUrl)) return null;
1021
- return /* @__PURE__ */ jsx14(PanelBleed, { children: /* @__PURE__ */ jsxs9(
1017
+ return /* @__PURE__ */ jsx15(PanelBleed, { children: /* @__PURE__ */ jsxs9(
1022
1018
  ActionBand,
1023
1019
  {
1024
1020
  className: "genre-action-band",
@@ -1026,7 +1022,7 @@ function GenreActionBand({
1026
1022
  children: [
1027
1023
  playActions,
1028
1024
  renderItems(items),
1029
- shareTarget && shareUrl && /* @__PURE__ */ jsx14(ShareActionButton, { target: shareTarget, url: shareUrl })
1025
+ shareTarget && shareUrl && /* @__PURE__ */ jsx15(ShareActionButton, { target: shareTarget, url: shareUrl })
1030
1026
  ]
1031
1027
  }
1032
1028
  ) });
@@ -1053,7 +1049,7 @@ var REGION_GLOBE_PATHS = {
1053
1049
  var REGION_GLOBES = ["africa", "americas", "asia"];
1054
1050
 
1055
1051
  // src/genre-tree/RegionGlobeIcon.tsx
1056
- import { jsx as jsx15 } from "react/jsx-runtime";
1052
+ import { jsx as jsx16 } from "react/jsx-runtime";
1057
1053
  var ROTATION_INTERVAL_MS = 3e3;
1058
1054
  function RegionGlobeIcon({ className: className2 }) {
1059
1055
  const [index, setIndex] = useState5(0);
@@ -1071,29 +1067,29 @@ function RegionGlobeIcon({ className: className2 }) {
1071
1067
  clearInterval(timer);
1072
1068
  };
1073
1069
  }, []);
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]] }) });
1070
+ 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
1071
  }
1076
1072
 
1077
1073
  // src/genre-tree/treeMarkPath.ts
1078
1074
  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
1075
 
1080
1076
  // src/genre-tree/TreeMarkIcon.tsx
1081
- import { jsx as jsx16 } from "react/jsx-runtime";
1077
+ import { jsx as jsx17 } from "react/jsx-runtime";
1082
1078
  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 }) });
1079
+ 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
1080
  }
1085
1081
 
1086
1082
  // src/genre-tree/GenrePlayActions.tsx
1087
- import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
1083
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs10 } from "react/jsx-runtime";
1088
1084
  function PlayIcon({ kind }) {
1089
1085
  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" }),
1086
+ kind === "tree" && /* @__PURE__ */ jsx18(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1087
+ kind === "map" && /* @__PURE__ */ jsx18(RegionGlobeIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35" }),
1092
1088
  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%)]" })
1089
+ /* @__PURE__ */ jsx18(TreeMarkIcon, { className: "absolute -left-1.5 -top-1.5 w-8 h-8 opacity-35 [clip-path:inset(0_50%_0_0)]" }),
1090
+ /* @__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
1091
  ] }),
1096
- /* @__PURE__ */ jsx17(Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1092
+ /* @__PURE__ */ jsx18(Play, { className: "relative w-5 h-5 fill-[var(--color-surface)]" })
1097
1093
  ] });
1098
1094
  }
1099
1095
  function playOptions(playlist) {
@@ -1109,13 +1105,13 @@ function playOptions(playlist) {
1109
1105
  function GenrePlayActions({ genrePlaylist, playback }) {
1110
1106
  const { isPlaying, setIsPlaying } = usePlayer4();
1111
1107
  const options = playOptions(genrePlaylist);
1112
- return /* @__PURE__ */ jsx17(Fragment6, { children: options.map((option) => {
1108
+ return /* @__PURE__ */ jsx18(Fragment6, { children: options.map((option) => {
1113
1109
  const isCurrent = playback.playing?.uuid === genrePlaylist.uuid && (option.selection === void 0 || playback.playing.selectionKey === "" || playback.playing.selectionKey === genreMusicSelectionKey2(option.selection).join());
1114
1110
  const playing = isCurrent && isPlaying;
1115
- return /* @__PURE__ */ jsx17(
1111
+ return /* @__PURE__ */ jsx18(
1116
1112
  ActionButton3,
1117
1113
  {
1118
- icon: playing ? /* @__PURE__ */ jsx17(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx17(PlayIcon, { kind: option.icon }),
1114
+ icon: playing ? /* @__PURE__ */ jsx18(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx18(PlayIcon, { kind: option.icon }),
1119
1115
  label: playing ? "Pause" : option.label,
1120
1116
  caption: String(option.count),
1121
1117
  disabled: option.count === 0,
@@ -1125,9 +1121,7 @@ function GenrePlayActions({ genrePlaylist, playback }) {
1125
1121
  setIsPlaying(!isPlaying);
1126
1122
  return;
1127
1123
  }
1128
- playback.play(genrePlaylist, option.selection).catch((error) => {
1129
- console.error("Failed to load genre playlist tracks:", error);
1130
- });
1124
+ void playback.play(genrePlaylist, option.selection);
1131
1125
  }
1132
1126
  },
1133
1127
  option.key
@@ -1141,7 +1135,7 @@ import { useFetchGenreOverview } from "@behindthemusictree/app-kit/genre-tree";
1141
1135
 
1142
1136
  // src/genre-tree/ClampedText.tsx
1143
1137
  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";
1138
+ import { Fragment as Fragment7, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
1145
1139
  function ClampedText({ children, className: className2 = "" }) {
1146
1140
  const ref = useRef2(null);
1147
1141
  const [expanded, setExpanded] = useState6(false);
@@ -1152,8 +1146,8 @@ function ClampedText({ children, className: className2 = "" }) {
1152
1146
  setOverflows(element.scrollHeight > element.clientHeight);
1153
1147
  }, [children, expanded]);
1154
1148
  return /* @__PURE__ */ jsxs11(Fragment7, { children: [
1155
- /* @__PURE__ */ jsx18("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1156
- (overflows || expanded) && /* @__PURE__ */ jsx18(
1149
+ /* @__PURE__ */ jsx19("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
1150
+ (overflows || expanded) && /* @__PURE__ */ jsx19(
1157
1151
  "button",
1158
1152
  {
1159
1153
  type: "button",
@@ -1178,25 +1172,25 @@ import { PaginatedResponseSchema } from "@behindthemusictree/app-kit/transport";
1178
1172
  import { parseWithLog } from "@behindthemusictree/app-kit/transport";
1179
1173
  import { usePlayer as usePlayer5 } from "@behindthemusictree/app-kit/player";
1180
1174
  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";
1175
+ import { usePopup as usePopup3 } from "@behindthemusictree/app-kit/popup";
1176
+ import { AppError as AppError2, ErrorCode as ErrorCode2 } from "@behindthemusictree/app-kit/transport";
1183
1177
  import { genreMusicQueryParams } from "@behindthemusictree/app-kit/genre-tree";
1184
1178
  import { genrePlaylistQueryKeys } from "@behindthemusictree/app-kit/genre-tree";
1185
1179
  import { libraryEndpoints, libraryQueryKeys } from "@behindthemusictree/app-kit/genre-tree";
1186
1180
  import { isSong, songUnplayableReason, SongDetailedSchema } from "@behindthemusictree/app-kit/genre-tree";
1187
- import { useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre-tree";
1181
+ import { TracksFailureAlreadyReportedError as TracksFailureAlreadyReportedError2, useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre-tree";
1188
1182
 
1189
1183
  // src/genre-tree/MusicBrainzRecordingLink.tsx
1190
1184
  import { musicBrainzRecordingUrl } from "@behindthemusictree/app-kit/genre-tree";
1191
- import { Fragment as Fragment8, jsx as jsx19 } from "react/jsx-runtime";
1185
+ import { Fragment as Fragment8, jsx as jsx20 } from "react/jsx-runtime";
1192
1186
  function MusicBrainzRecordingLink({
1193
1187
  song,
1194
1188
  children,
1195
1189
  className: className2 = ""
1196
1190
  }) {
1197
1191
  const url = musicBrainzRecordingUrl(song);
1198
- if (!url) return /* @__PURE__ */ jsx19(Fragment8, { children });
1199
- return /* @__PURE__ */ jsx19(
1192
+ if (!url) return /* @__PURE__ */ jsx20(Fragment8, { children });
1193
+ return /* @__PURE__ */ jsx20(
1200
1194
  "a",
1201
1195
  {
1202
1196
  href: url,
@@ -1213,7 +1207,7 @@ function MusicBrainzRecordingLink({
1213
1207
  // src/genre-tree/GenrePlaylistTracks.tsx
1214
1208
  import { ArtistNames } from "@behindthemusictree/app-kit/genre-tree";
1215
1209
  import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
1216
- import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
1210
+ import { jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
1217
1211
  var CATALOGUE_PAGE_SIZE = 100;
1218
1212
  var SongPageSchema = PaginatedResponseSchema(SongDetailedSchema).extend({
1219
1213
  overallTotal: z.number().nullable(),
@@ -1247,7 +1241,7 @@ function GenrePlaylistTracks({
1247
1241
  const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = useTrackList2();
1248
1242
  const { session, sessionRestored } = useSession();
1249
1243
  const { playerTrackObject, playState, handlePlayPauseAction } = usePlayer5();
1250
- const { showPopup } = usePopup4();
1244
+ const { showPopup } = usePopup3();
1251
1245
  const trackNoun = useTrackNoun();
1252
1246
  const uuid = genrePlaylist?.uuid;
1253
1247
  const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
@@ -1299,27 +1293,28 @@ function GenrePlaylistTracks({
1299
1293
  }) : 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
1294
  started.catch((error) => {
1301
1295
  console.error("Failed to play genre playlist:", error);
1302
- showPopup(/* @__PURE__ */ jsx20(InternalErrorPopup, { errorCode: ErrorCode3.CLIENT_UNKNOWN }));
1296
+ if (error instanceof TracksFailureAlreadyReportedError2) return;
1297
+ showPopup(/* @__PURE__ */ jsx21(InternalErrorPopup, { errorCode: error instanceof AppError2 ? error.code : ErrorCode2.CLIENT_UNKNOWN }));
1303
1298
  });
1304
1299
  };
1305
- return /* @__PURE__ */ jsx20(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
1300
+ return /* @__PURE__ */ jsx21(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
1306
1301
  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: [
1302
+ 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
1303
  "Couldn't load ",
1309
1304
  trackNoun.plural,
1310
1305
  ".",
1311
1306
  " ",
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: [
1307
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
1308
+ ] }) : 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
1309
  tracks.map((track, index) => {
1315
1310
  const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
1316
1311
  const isCurrent = playerTrackObject?.track.id === track.uuid;
1317
1312
  const isPlaying = isCurrent && playState === PlayStates.PLAYING;
1318
1313
  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 })
1314
+ /* @__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 }),
1315
+ track.artists?.length ? /* @__PURE__ */ jsx21("span", { className: SEPARATOR, children: " \u2014 " }) : null,
1316
+ /* @__PURE__ */ jsx21("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ jsx21(ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
1317
+ track.album && /* @__PURE__ */ jsx21("span", { className: ALBUM_CELL, children: track.album.name })
1323
1318
  ] });
1324
1319
  const leading = renderSongLeading?.(track, index);
1325
1320
  const actions = renderSongActions?.(track);
@@ -1327,7 +1322,7 @@ function GenrePlaylistTracks({
1327
1322
  if (isUnplayable) {
1328
1323
  return /* @__PURE__ */ jsxs12("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
1329
1324
  leading,
1330
- /* @__PURE__ */ jsx20(
1325
+ /* @__PURE__ */ jsx21(
1331
1326
  "button",
1332
1327
  {
1333
1328
  type: "button",
@@ -1335,7 +1330,7 @@ function GenrePlaylistTracks({
1335
1330
  "aria-disabled": "true",
1336
1331
  "aria-label": label,
1337
1332
  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 })
1333
+ children: /* @__PURE__ */ jsx21(FaPlay, { size: 10 })
1339
1334
  }
1340
1335
  ),
1341
1336
  text,
@@ -1348,14 +1343,14 @@ function GenrePlaylistTracks({
1348
1343
  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
1344
  children: [
1350
1345
  leading,
1351
- /* @__PURE__ */ jsx20(
1346
+ /* @__PURE__ */ jsx21(
1352
1347
  "button",
1353
1348
  {
1354
1349
  type: "button",
1355
1350
  "aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
1356
1351
  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
1352
  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 })
1353
+ children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */ jsx21(RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ jsx21(FaPause, { size: 10 }) : /* @__PURE__ */ jsx21(FaPlay, { size: 10 })
1359
1354
  }
1360
1355
  ),
1361
1356
  text,
@@ -1365,7 +1360,7 @@ function GenrePlaylistTracks({
1365
1360
  track.uuid
1366
1361
  );
1367
1362
  }),
1368
- hasNextPage && /* @__PURE__ */ jsx20("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1363
+ hasNextPage && /* @__PURE__ */ jsx21("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
1369
1364
  ] })
1370
1365
  ] }) });
1371
1366
  }
@@ -1373,7 +1368,7 @@ function GenrePlaylistTracks({
1373
1368
  // src/genre-tree/GenreDetailExtras.tsx
1374
1369
  import { useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
1375
1370
  import { SiWikidata, SiWikipedia } from "react-icons/si";
1376
- import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
1371
+ import { Fragment as Fragment9, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
1377
1372
  function GenreDetailExtras({
1378
1373
  genreUuid,
1379
1374
  genrePlaylist,
@@ -1400,15 +1395,15 @@ function GenreDetailExtras({
1400
1395
  );
1401
1396
  if (isPending) {
1402
1397
  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" }) }) }),
1398
+ /* @__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
1399
  family
1405
1400
  ] });
1406
1401
  }
1407
- if (!overview) return /* @__PURE__ */ jsx21(Fragment9, { children: family });
1402
+ if (!overview) return /* @__PURE__ */ jsx22(Fragment9, { children: family });
1408
1403
  const { summary, summarySourceUrl, essentialSongs } = overview;
1409
1404
  const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
1410
1405
  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: [
1406
+ (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
1407
  summarySourceUrl && /* @__PURE__ */ jsxs13(
1413
1408
  "a",
1414
1409
  {
@@ -1417,7 +1412,7 @@ function GenreDetailExtras({
1417
1412
  rel: "noopener noreferrer",
1418
1413
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1419
1414
  children: [
1420
- /* @__PURE__ */ jsx21(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1415
+ /* @__PURE__ */ jsx22(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1421
1416
  "Wikipedia"
1422
1417
  ]
1423
1418
  }
@@ -1430,17 +1425,17 @@ function GenreDetailExtras({
1430
1425
  rel: "noopener noreferrer",
1431
1426
  className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
1432
1427
  children: [
1433
- /* @__PURE__ */ jsx21(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1428
+ /* @__PURE__ */ jsx22(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
1434
1429
  "Wikidata"
1435
1430
  ]
1436
1431
  }
1437
1432
  )
1438
1433
  ] }) }),
1439
- /* @__PURE__ */ jsx21(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx21(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1434
+ /* @__PURE__ */ jsx22(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx22(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
1440
1435
  family,
1441
1436
  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(
1437
+ (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" }) }),
1438
+ /* @__PURE__ */ jsx22(
1444
1439
  GenrePlaylistTracks,
1445
1440
  {
1446
1441
  genreUuid,
@@ -1458,10 +1453,10 @@ function GenreDetailExtras({
1458
1453
  }
1459
1454
 
1460
1455
  // src/genre-tree/GenreInfoPanelBody.tsx
1461
- import { Fragment as Fragment10, jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
1456
+ import { Fragment as Fragment10, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
1462
1457
  import { createElement } from "react";
1463
1458
  function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
1464
- return /* @__PURE__ */ jsx22(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ jsx22(GenrePlayActions, { genrePlaylist, playback }) });
1459
+ return /* @__PURE__ */ jsx23(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ jsx23(GenrePlayActions, { genrePlaylist, playback }) });
1465
1460
  }
1466
1461
  function GenreInfoPanelBody({
1467
1462
  toolbar,
@@ -1480,7 +1475,7 @@ function GenreInfoPanelBody({
1480
1475
  const selection = summary ? defaultGenreMusicSelection2(summary) : void 0;
1481
1476
  return /* @__PURE__ */ jsxs14(Fragment10, { children: [
1482
1477
  toolbar,
1483
- genrePlaylist ? /* @__PURE__ */ jsx22(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ jsx22(GenreActionBand, { ...band }),
1478
+ genrePlaylist ? /* @__PURE__ */ jsx23(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ jsx23(GenreActionBand, { ...band }),
1484
1479
  /* @__PURE__ */ createElement(
1485
1480
  GenreDetailExtras,
1486
1481
  {
@@ -1500,7 +1495,7 @@ function GenreInfoPanelBody({
1500
1495
  }
1501
1496
 
1502
1497
  // src/genre-tree/GenreDetailPanel.tsx
1503
- import { Fragment as Fragment11, jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
1498
+ import { Fragment as Fragment11, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
1504
1499
  var RELATIVE_ITEM_COUNT = 1;
1505
1500
  function toNode(genre, parentId) {
1506
1501
  return {
@@ -1523,7 +1518,7 @@ function useGenreDetailContent({
1523
1518
  trailing,
1524
1519
  genrePlaylist: hostPlaylist,
1525
1520
  expanded,
1526
- treeRelations
1521
+ treeStyles
1527
1522
  }) {
1528
1523
  const { data: detail, isPending, isError } = useFetchGenreDetail(genreUuid, scope, getBackendBaseUrl);
1529
1524
  const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
@@ -1531,19 +1526,12 @@ function useGenreDetailContent({
1531
1526
  const view = useMemo6(() => {
1532
1527
  if (!detail) return null;
1533
1528
  const rootColor = getGenreTreeColor2(detail.root.wikidataId);
1534
- const treeStyles = /* @__PURE__ */ new Map();
1535
- if (treeRelations) {
1536
- const drawn = [treeRelations, treeRelations.parentNode, ...treeRelations.childNodes, ...treeRelations.ancestorNodes];
1537
- for (const entry of drawn) {
1538
- if (entry) treeStyles.set(entry.node.id, { fill: entry.fill, textColor: entry.textColor });
1539
- }
1540
- }
1541
1529
  const relative = (genre, parentId) => ({
1542
1530
  node: toNode(genre, parentId),
1543
- ...treeStyles.get(genre.uuid) ?? { fill: tintSurface(rootColor), textColor: TEXT_COLOR }
1531
+ ...treeStyles?.get(genre.uuid) ?? { fill: tintSurface(rootColor), textColor: TEXT_COLOR }
1544
1532
  });
1545
1533
  const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
1546
- const own = treeStyles.get(detail.uuid) ?? { fill: rootColor, textColor: getTextColorOnFill(rootColor) };
1534
+ const own = treeStyles?.get(detail.uuid) ?? { fill: rootColor, textColor: getTextColorOnFill(rootColor) };
1547
1535
  return {
1548
1536
  node: {
1549
1537
  id: detail.uuid,
@@ -1560,23 +1548,23 @@ function useGenreDetailContent({
1560
1548
  ({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
1561
1549
  )
1562
1550
  };
1563
- }, [detail, scope, treeRelations]);
1551
+ }, [detail, scope, treeStyles]);
1564
1552
  if (isError) return {
1565
1553
  name: null,
1566
1554
  title: "Genre unavailable",
1567
- content: /* @__PURE__ */ jsx23("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
1555
+ content: /* @__PURE__ */ jsx24("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
1568
1556
  };
1569
1557
  if (isPending || !detail || !view) return {
1570
1558
  name: null,
1571
1559
  title: "Loading genre\u2026",
1572
1560
  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" })
1561
+ /* @__PURE__ */ jsx24(Skeleton3, { className: "h-6 w-2/3" }),
1562
+ /* @__PURE__ */ jsx24(Skeleton3, { className: "h-4 w-full" }),
1563
+ /* @__PURE__ */ jsx24(Skeleton3, { className: "h-4 w-3/4" })
1576
1564
  ] })
1577
1565
  };
1578
1566
  const selectGenre = (nodeId) => onSelectGenre(GenreUuidSchema.parse(nodeId));
1579
- const family = /* @__PURE__ */ jsx23(
1567
+ const family = /* @__PURE__ */ jsx24(
1580
1568
  GenreNodeFamily,
1581
1569
  {
1582
1570
  node: view.node,
@@ -1587,14 +1575,14 @@ function useGenreDetailContent({
1587
1575
  onSelectNode: selectGenre
1588
1576
  }
1589
1577
  );
1590
- const ancestors = /* @__PURE__ */ jsx23(GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1578
+ const ancestors = /* @__PURE__ */ jsx24(GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
1591
1579
  return {
1592
1580
  name: detail.name,
1593
1581
  title: detail.name,
1594
1582
  headerAccent: { background: view.fill, color: view.textColor },
1595
1583
  aside: expanded ? ancestors : void 0,
1596
1584
  content: /* @__PURE__ */ jsxs15(Fragment11, { children: [
1597
- /* @__PURE__ */ jsx23(
1585
+ /* @__PURE__ */ jsx24(
1598
1586
  GenreInfoPanelBody,
1599
1587
  {
1600
1588
  family: /* @__PURE__ */ jsxs15(Fragment11, { children: [
@@ -1641,7 +1629,7 @@ function GenreDetailPanel({
1641
1629
  expanded,
1642
1630
  band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
1643
1631
  });
1644
- return /* @__PURE__ */ jsx23(
1632
+ return /* @__PURE__ */ jsx24(
1645
1633
  DetailPanel,
1646
1634
  {
1647
1635
  side: "left",
@@ -1654,7 +1642,7 @@ function GenreDetailPanel({
1654
1642
  edgeTab,
1655
1643
  aside: panel.aside,
1656
1644
  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,
1645
+ 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
1646
  children: panel.content
1659
1647
  }
1660
1648
  );
@@ -1666,7 +1654,7 @@ import {
1666
1654
  getReparentCandidates,
1667
1655
  useUpdateGenre
1668
1656
  } from "@behindthemusictree/app-kit/genre-tree";
1669
- import { jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
1657
+ import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
1670
1658
  function ChangeParentPopup({
1671
1659
  genrePlaylist,
1672
1660
  genrePlaylists,
@@ -1706,7 +1694,7 @@ function ChangeParentPopup({
1706
1694
  "Current parent: ",
1707
1695
  genrePlaylist.parent?.name ?? "none"
1708
1696
  ] }),
1709
- /* @__PURE__ */ jsx24("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx24(
1697
+ /* @__PURE__ */ jsx25("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx25(
1710
1698
  GenreSearch,
1711
1699
  {
1712
1700
  genrePlaylists: candidates,
@@ -1714,7 +1702,7 @@ function ChangeParentPopup({
1714
1702
  placeholder: "Search a new parent\u2026"
1715
1703
  }
1716
1704
  ) }),
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(" ") })
1705
+ 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
1706
  ] });
1719
1707
  }
1720
1708
 
@@ -1727,7 +1715,7 @@ import { Pause as Pause2, Play as Play2 } from "lucide-react";
1727
1715
  import { ActionBand as ActionBand2, ActionButton as ActionButton4, PanelBleed as PanelBleed2 } from "@behindthemusictree/ui";
1728
1716
  import { TrackListOriginType as TrackListOriginType2, useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre-tree";
1729
1717
  import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
1730
- import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
1718
+ import { jsx as jsx26, jsxs as jsxs17 } from "react/jsx-runtime";
1731
1719
  function ArtistActionBand({ artist, share }) {
1732
1720
  const { trackList, playNewTrackListFromArtist } = useTrackList3();
1733
1721
  const { isPlaying, setIsPlaying } = usePlayer6();
@@ -1743,11 +1731,11 @@ function ArtistActionBand({ artist, share }) {
1743
1731
  console.error("Failed to load artist tracks:", error);
1744
1732
  });
1745
1733
  };
1746
- return /* @__PURE__ */ jsx25(PanelBleed2, { children: /* @__PURE__ */ jsxs17(ActionBand2, { children: [
1747
- /* @__PURE__ */ jsx25(
1734
+ return /* @__PURE__ */ jsx26(PanelBleed2, { children: /* @__PURE__ */ jsxs17(ActionBand2, { children: [
1735
+ /* @__PURE__ */ jsx26(
1748
1736
  ActionButton4,
1749
1737
  {
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" }),
1738
+ 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
1739
  label: playing ? "Pause" : "Play",
1752
1740
  caption: String(artist.playableTracksCount),
1753
1741
  disabled: artist.playableTracksCount === 0,
@@ -1775,7 +1763,7 @@ import {
1775
1763
  SiYoutube
1776
1764
  } from "react-icons/si";
1777
1765
  import { Chip } from "@behindthemusictree/ui";
1778
- import { jsx as jsx26 } from "react/jsx-runtime";
1766
+ import { jsx as jsx27 } from "react/jsx-runtime";
1779
1767
  var LINK_KINDS = {
1780
1768
  official: { label: "Official website", Icon: Globe },
1781
1769
  wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
@@ -1812,7 +1800,7 @@ function artistLinks(artist) {
1812
1800
  return links;
1813
1801
  }
1814
1802
  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)) });
1803
+ 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
1804
  }
1817
1805
 
1818
1806
  // src/genre-tree/artist/formatYears.ts
@@ -1823,7 +1811,7 @@ function formatYears(beginYear, endYear, ended) {
1823
1811
  }
1824
1812
 
1825
1813
  // src/genre-tree/artist/ArtistHeader.tsx
1826
- import { jsx as jsx27 } from "react/jsx-runtime";
1814
+ import { jsx as jsx28 } from "react/jsx-runtime";
1827
1815
  var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
1828
1816
  function ArtistHeader({ artist }) {
1829
1817
  const fields = [];
@@ -1833,35 +1821,35 @@ function ArtistHeader({ artist }) {
1833
1821
  const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
1834
1822
  if (years) fields.push({ label: "Active", value: years });
1835
1823
  const links = artistLinks(artist);
1836
- if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx27(ArtistLinks, { links }) });
1824
+ if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx28(ArtistLinks, { links }) });
1837
1825
  if (fields.length === 0) return null;
1838
- return /* @__PURE__ */ jsx27(FieldList, { items: fields });
1826
+ return /* @__PURE__ */ jsx28(FieldList, { items: fields });
1839
1827
  }
1840
1828
 
1841
1829
  // src/genre-tree/artist/ArtistPhoto.tsx
1842
- import { jsx as jsx28, jsxs as jsxs18 } from "react/jsx-runtime";
1830
+ import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
1843
1831
  function ExternalLink({ href, children }) {
1844
- return /* @__PURE__ */ jsx28("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
1832
+ return /* @__PURE__ */ jsx29("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
1845
1833
  }
1846
1834
  function ArtistPhoto({ image, name }) {
1847
1835
  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" }),
1836
+ /* @__PURE__ */ jsx29("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
1849
1837
  /* @__PURE__ */ jsxs18("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
1850
1838
  "Photo: ",
1851
- /* @__PURE__ */ jsx28(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
1839
+ /* @__PURE__ */ jsx29(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
1852
1840
  ",",
1853
1841
  " ",
1854
- image.licenseUrl ? /* @__PURE__ */ jsx28(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
1842
+ image.licenseUrl ? /* @__PURE__ */ jsx29(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
1855
1843
  ] })
1856
1844
  ] });
1857
1845
  }
1858
1846
 
1859
1847
  // src/genre-tree/artist/ArtistMembers.tsx
1860
1848
  import { PanelGroup, PanelSection as PanelSection3 } from "@behindthemusictree/ui";
1861
- import { jsx as jsx29, jsxs as jsxs19 } from "react/jsx-runtime";
1849
+ import { jsx as jsx30, jsxs as jsxs19 } from "react/jsx-runtime";
1862
1850
  function MembershipList({ title, memberships, renderArtistLink }) {
1863
1851
  if (memberships.length === 0) return null;
1864
- return /* @__PURE__ */ jsx29(PanelGroup, { title, children: /* @__PURE__ */ jsx29("ul", { children: memberships.map((membership, index) => {
1852
+ return /* @__PURE__ */ jsx30(PanelGroup, { title, children: /* @__PURE__ */ jsx30("ul", { children: memberships.map((membership, index) => {
1865
1853
  const details = [
1866
1854
  formatYears(membership.beginYear, membership.endYear, membership.ended),
1867
1855
  ...membership.roles
@@ -1870,7 +1858,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
1870
1858
  return (
1871
1859
  // A member can have several periods in the same group.
1872
1860
  /* @__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) }),
1861
+ /* @__PURE__ */ jsx30("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
1874
1862
  detailsText && /* @__PURE__ */ jsxs19("span", { className: "text-[var(--color-text-muted)]", children: [
1875
1863
  " \u2014 ",
1876
1864
  detailsText
@@ -1883,8 +1871,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
1883
1871
  if (memberships.length === 0) return null;
1884
1872
  const isCurrent = (membership) => !membership.ended && membership.endYear === null;
1885
1873
  return /* @__PURE__ */ jsxs19(PanelSection3, { title, children: [
1886
- /* @__PURE__ */ jsx29(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
1887
- /* @__PURE__ */ jsx29(
1874
+ /* @__PURE__ */ jsx30(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
1875
+ /* @__PURE__ */ jsx30(
1888
1876
  MembershipList,
1889
1877
  {
1890
1878
  title: "Past",
@@ -1897,7 +1885,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
1897
1885
 
1898
1886
  // src/genre-tree/artist/ArtistDiscography.tsx
1899
1887
  import { PanelGroup as PanelGroup2, PanelSection as PanelSection4 } from "@behindthemusictree/ui";
1900
- import { jsx as jsx30, jsxs as jsxs20 } from "react/jsx-runtime";
1888
+ import { jsx as jsx31, jsxs as jsxs20 } from "react/jsx-runtime";
1901
1889
  var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
1902
1890
  var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
1903
1891
  function discographyGroup({ primaryType, secondaryTypes }) {
@@ -1917,12 +1905,12 @@ function byYear(a, b) {
1917
1905
  function ArtistDiscography({ albums }) {
1918
1906
  if (albums.length === 0) return null;
1919
1907
  const sorted = [...albums].sort(byYear);
1920
- return /* @__PURE__ */ jsx30(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
1908
+ return /* @__PURE__ */ jsx31(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
1921
1909
  const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
1922
1910
  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 })
1911
+ 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: [
1912
+ /* @__PURE__ */ jsx31("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
1913
+ /* @__PURE__ */ jsx31("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
1926
1914
  ] }, album.uuid)) }) }, group);
1927
1915
  }) });
1928
1916
  }
@@ -1930,7 +1918,6 @@ function ArtistDiscography({ albums }) {
1930
1918
  // src/genre-tree/artist/ArtistSongs.tsx
1931
1919
  import { useState as useState8 } from "react";
1932
1920
  import { keepPreviousData } from "@tanstack/react-query";
1933
- import { Play as Play3 } from "lucide-react";
1934
1921
  import { Pagination, PanelSection as PanelSection5, Skeleton as Skeleton4 } from "@behindthemusictree/ui";
1935
1922
  import { usePlayer as usePlayer9 } from "@behindthemusictree/app-kit/player";
1936
1923
  import {
@@ -1949,7 +1936,7 @@ import { useTrackList as useTrackList4 } from "@behindthemusictree/app-kit/genre
1949
1936
  import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
1950
1937
  import { usePlayer as usePlayer7 } from "@behindthemusictree/app-kit/player";
1951
1938
  import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
1952
- import { jsx as jsx31, jsxs as jsxs21 } from "react/jsx-runtime";
1939
+ import { jsx as jsx32, jsxs as jsxs21 } from "react/jsx-runtime";
1953
1940
  function TrackPositionPlayPause({
1954
1941
  position,
1955
1942
  uuid,
@@ -1970,7 +1957,7 @@ function TrackPositionPlayPause({
1970
1957
  style: { minWidth: "4rem", maxWidth: "4rem" },
1971
1958
  onClick: handlePlayPauseClick,
1972
1959
  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(
1960
+ /* @__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
1961
  "span",
1975
1962
  {
1976
1963
  className: "track-position-play-pause__bars",
@@ -1978,18 +1965,18 @@ function TrackPositionPlayPause({
1978
1965
  role: "img",
1979
1966
  "aria-label": isPlaying ? "Playing" : "Paused",
1980
1967
  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" })
1968
+ /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__bar" }),
1969
+ /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__bar" }),
1970
+ /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__bar" })
1984
1971
  ]
1985
1972
  }
1986
1973
  ) : position }),
1987
- /* @__PURE__ */ jsx31(
1974
+ /* @__PURE__ */ jsx32(
1988
1975
  "div",
1989
1976
  {
1990
1977
  className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
1991
1978
  "aria-hidden": "true",
1992
- children: isAwaitingPlayback ? /* @__PURE__ */ jsx31("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx31(FaPause2, {}) : /* @__PURE__ */ jsx31(FaPlay2, {})
1979
+ children: isAwaitingPlayback ? /* @__PURE__ */ jsx32("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx32(FaPause2, {}) : /* @__PURE__ */ jsx32(FaPlay2, {})
1993
1980
  }
1994
1981
  )
1995
1982
  ]
@@ -2000,7 +1987,7 @@ function TrackPositionPlayPause({
2000
1987
  // src/genre-tree/track-list-sidebar/TrackItem.tsx
2001
1988
  import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
2002
1989
  import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
2003
- import { jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
1990
+ import { jsx as jsx33, jsxs as jsxs22 } from "react/jsx-runtime";
2004
1991
  function TrackItem({
2005
1992
  track,
2006
1993
  position,
@@ -2029,7 +2016,7 @@ function TrackItem({
2029
2016
  {
2030
2017
  className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
2031
2018
  children: [
2032
- isUnplayable ? /* @__PURE__ */ jsx32(
2019
+ isUnplayable ? /* @__PURE__ */ jsx33(
2033
2020
  "div",
2034
2021
  {
2035
2022
  className: "flex items-center justify-center text-lg w-16",
@@ -2037,7 +2024,7 @@ function TrackItem({
2037
2024
  "aria-disabled": "true",
2038
2025
  children: position
2039
2026
  }
2040
- ) : /* @__PURE__ */ jsx32(
2027
+ ) : /* @__PURE__ */ jsx33(
2041
2028
  TrackPositionPlayPause,
2042
2029
  {
2043
2030
  position,
@@ -2047,11 +2034,11 @@ function TrackItem({
2047
2034
  }
2048
2035
  ),
2049
2036
  /* @__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 }) }) : ""
2037
+ /* @__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 }) }),
2038
+ 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
2039
  ] }),
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(
2040
+ /* @__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 }) : "" }),
2041
+ renderDuration && /* @__PURE__ */ jsx33(
2055
2042
  "div",
2056
2043
  {
2057
2044
  className: "duration flex text-base w-16 items-center justify-center",
@@ -2059,14 +2046,14 @@ function TrackItem({
2059
2046
  children: renderDuration(track)
2060
2047
  }
2061
2048
  ),
2062
- renderActions && /* @__PURE__ */ jsx32("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2049
+ renderActions && /* @__PURE__ */ jsx33("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
2063
2050
  ]
2064
2051
  }
2065
2052
  );
2066
2053
  }
2067
2054
 
2068
2055
  // src/genre-tree/artist/ArtistSongs.tsx
2069
- import { Fragment as Fragment12, jsx as jsx33, jsxs as jsxs23 } from "react/jsx-runtime";
2056
+ import { Fragment as Fragment12, jsx as jsx34, jsxs as jsxs23 } from "react/jsx-runtime";
2070
2057
  var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
2071
2058
  var SORTS = [
2072
2059
  { ordering: "title", label: "Title" },
@@ -2099,36 +2086,22 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2099
2086
  event.stopPropagation();
2100
2087
  void play(song.uuid);
2101
2088
  };
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: [
2103
- /* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
2104
- /* @__PURE__ */ jsxs23(
2105
- "button",
2106
- {
2107
- type: "button",
2108
- onClick: () => void play(),
2109
- 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
- children: [
2111
- /* @__PURE__ */ jsx33(Play3, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2112
- "Play all"
2113
- ]
2114
- }
2115
- ),
2116
- /* @__PURE__ */ jsx33("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ jsx33(
2117
- "button",
2118
- {
2119
- type: "button",
2120
- "aria-pressed": ordering === sort.ordering,
2121
- onClick: () => {
2122
- setOrdering(sort.ordering);
2123
- setPage(1);
2124
- },
2125
- className: "px-2.5 py-0.5 text-[var(--color-text)] rounded-full hover:bg-[var(--color-surface-muted)] aria-pressed:bg-gray-900 aria-pressed:text-white",
2126
- children: sort.label
2089
+ 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: [
2090
+ /* @__PURE__ */ jsx34("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ jsx34("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ jsx34(
2091
+ "button",
2092
+ {
2093
+ type: "button",
2094
+ "aria-pressed": ordering === sort.ordering,
2095
+ onClick: () => {
2096
+ setOrdering(sort.ordering);
2097
+ setPage(1);
2127
2098
  },
2128
- sort.ordering
2129
- )) })
2130
- ] }),
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(
2099
+ className: "px-2.5 py-0.5 text-[var(--color-text)] rounded-full hover:bg-[var(--color-surface-muted)] aria-pressed:bg-gray-900 aria-pressed:text-white",
2100
+ children: sort.label
2101
+ },
2102
+ sort.ordering
2103
+ )) }) }),
2104
+ /* @__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
2105
  TrackItem,
2133
2106
  {
2134
2107
  track: song,
@@ -2137,12 +2110,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
2137
2110
  renderSongLink
2138
2111
  }
2139
2112
  ) }, song.uuid)) }),
2140
- /* @__PURE__ */ jsx33(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2113
+ /* @__PURE__ */ jsx34(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
2141
2114
  ] }) });
2142
2115
  }
2143
2116
 
2144
2117
  // src/genre-tree/artist/useArtistPanel.tsx
2145
- import { Fragment as Fragment13, jsx as jsx34, jsxs as jsxs24 } from "react/jsx-runtime";
2118
+ import { Fragment as Fragment13, jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
2146
2119
  function useArtistPanel({
2147
2120
  uuid,
2148
2121
  scope,
@@ -2164,25 +2137,25 @@ function useArtistPanel({
2164
2137
  ...panel,
2165
2138
  title: "Artist",
2166
2139
  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" })
2140
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-3 w-16" }),
2141
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-4 w-32" }),
2142
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-3 w-16" }),
2143
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-4 w-24" }),
2144
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-16 mt-2.5" }),
2145
+ /* @__PURE__ */ jsx35(Skeleton5, { className: "h-16" })
2173
2146
  ] })
2174
2147
  };
2175
2148
  case "not-found":
2176
2149
  return {
2177
2150
  ...panel,
2178
2151
  title: "Artist not found",
2179
- content: /* @__PURE__ */ jsx34("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2152
+ content: /* @__PURE__ */ jsx35("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
2180
2153
  };
2181
2154
  case "error":
2182
2155
  return {
2183
2156
  ...panel,
2184
2157
  title: "Artist",
2185
- content: /* @__PURE__ */ jsx34("p", { className: "text-red-600", children: "Could not load this artist." })
2158
+ content: /* @__PURE__ */ jsx35("p", { className: "text-red-600", children: "Could not load this artist." })
2186
2159
  };
2187
2160
  case "success": {
2188
2161
  const { artist } = result;
@@ -2192,18 +2165,18 @@ function useArtistPanel({
2192
2165
  ...panel,
2193
2166
  title: artist.name,
2194
2167
  content: /* @__PURE__ */ jsxs24(Fragment13, { children: [
2195
- /* @__PURE__ */ jsx34(
2168
+ /* @__PURE__ */ jsx35(
2196
2169
  ArtistActionBand,
2197
2170
  {
2198
2171
  artist,
2199
- share: shareTarget && shareUrl ? /* @__PURE__ */ jsx34(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2172
+ share: shareTarget && shareUrl ? /* @__PURE__ */ jsx35(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
2200
2173
  }
2201
2174
  ),
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(
2175
+ artist.image && /* @__PURE__ */ jsx35(ArtistPhoto, { image: artist.image, name: artist.name }),
2176
+ /* @__PURE__ */ jsx35(ArtistHeader, { artist }),
2177
+ /* @__PURE__ */ jsx35(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2178
+ /* @__PURE__ */ jsx35(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2179
+ artist.tracksCount > 0 && /* @__PURE__ */ jsx35(
2207
2180
  ArtistSongs,
2208
2181
  {
2209
2182
  artist: { uuid: artist.uuid, name: artist.name },
@@ -2213,7 +2186,7 @@ function useArtistPanel({
2213
2186
  }
2214
2187
  )
2215
2188
  ] }),
2216
- extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx34(ArtistDiscography, { albums: artist.albums }) : void 0
2189
+ extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx35(ArtistDiscography, { albums: artist.albums }) : void 0
2217
2190
  };
2218
2191
  }
2219
2192
  }
@@ -2227,7 +2200,7 @@ import {
2227
2200
 
2228
2201
  // src/genre-tree/song/SongPanelContent.tsx
2229
2202
  import { useState as useState9 } from "react";
2230
- import { Pause as Pause3, Play as Play4 } from "lucide-react";
2203
+ import { Pause as Pause3, Play as Play3 } from "lucide-react";
2231
2204
  import { ActionBand as ActionBand3, ActionButton as ActionButton5, FieldList as FieldList2, PANEL_LABEL_CLASS, PanelBleed as PanelBleed3, PanelSection as PanelSection6, Skeleton as Skeleton6 } from "@behindthemusictree/ui";
2232
2205
  import { PlayStates as PlayStates3, usePlayer as usePlayer10 } from "@behindthemusictree/app-kit/player";
2233
2206
  import {
@@ -2237,7 +2210,7 @@ import {
2237
2210
  useSongHistory,
2238
2211
  useTrackList as useTrackList6
2239
2212
  } from "@behindthemusictree/app-kit/genre-tree";
2240
- import { Fragment as Fragment14, jsx as jsx35, jsxs as jsxs25 } from "react/jsx-runtime";
2213
+ import { Fragment as Fragment14, jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
2241
2214
  var numberFormat = new Intl.NumberFormat("en");
2242
2215
  var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
2243
2216
  var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
@@ -2245,11 +2218,11 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2245
2218
  const [open, setOpen] = useState9(false);
2246
2219
  const history = useSongHistory(uuid, getBackendBaseUrl, open);
2247
2220
  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." });
2221
+ if (history.status === "loading") body = /* @__PURE__ */ jsx36(Skeleton6, { className: "h-10", "aria-busy": "true" });
2222
+ else if (history.status === "error") body = /* @__PURE__ */ jsx36("p", { className: "text-red-600", children: "Could not load the history." });
2250
2223
  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 }),
2224
+ 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: [
2225
+ /* @__PURE__ */ jsx36("span", { className: "font-medium", children: entry.action }),
2253
2226
  entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs25("span", { className: "text-[var(--color-text-muted)]", children: [
2254
2227
  " ",
2255
2228
  "\u2014 ",
@@ -2263,9 +2236,9 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
2263
2236
  dateFormat.format(new Date(entry.createdOn))
2264
2237
  ] })
2265
2238
  ] }, 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 })
2239
+ 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: [
2240
+ /* @__PURE__ */ jsx36("summary", { className: `mt-2.5 cursor-pointer ${PANEL_LABEL_CLASS}`, children: "History" }),
2241
+ /* @__PURE__ */ jsx36("div", { className: "mt-1.5", children: body })
2269
2242
  ] }) });
2270
2243
  }
2271
2244
  function SongPanelContent(props) {
@@ -2284,13 +2257,13 @@ function SongPanelContent(props) {
2284
2257
  const genre = song.genre;
2285
2258
  fields.push({
2286
2259
  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 })
2260
+ 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: [
2261
+ index > 0 && /* @__PURE__ */ jsx36("span", { "aria-hidden": "true", children: "\u203A " }),
2262
+ /* @__PURE__ */ jsx36("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
2290
2263
  ] }, genrePlaylist.uuid)) }) : song.genre.name
2291
2264
  });
2292
2265
  } else {
2293
- fields.push({ label: "Genre", value: /* @__PURE__ */ jsx35("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2266
+ fields.push({ label: "Genre", value: /* @__PURE__ */ jsx36("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
2294
2267
  }
2295
2268
  if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
2296
2269
  const viewCount = song.youtubeVideo?.viewCount;
@@ -2300,11 +2273,11 @@ function SongPanelContent(props) {
2300
2273
  const links = [];
2301
2274
  if (musicBrainzUrl)
2302
2275
  links.push(
2303
- /* @__PURE__ */ jsx35("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2276
+ /* @__PURE__ */ jsx36("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
2304
2277
  );
2305
2278
  if (song.youtubeVideo)
2306
2279
  links.push(
2307
- /* @__PURE__ */ jsx35(
2280
+ /* @__PURE__ */ jsx36(
2308
2281
  "a",
2309
2282
  {
2310
2283
  href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
@@ -2319,31 +2292,31 @@ function SongPanelContent(props) {
2319
2292
  if (links.length > 0)
2320
2293
  fields.push({
2321
2294
  label: "Links",
2322
- value: /* @__PURE__ */ jsx35("span", { className: "flex gap-3", children: links })
2295
+ value: /* @__PURE__ */ jsx36("span", { className: "flex gap-3", children: links })
2323
2296
  });
2324
2297
  return /* @__PURE__ */ jsxs25(Fragment14, { children: [
2325
- /* @__PURE__ */ jsx35(PanelBleed3, { children: /* @__PURE__ */ jsxs25(ActionBand3, { children: [
2326
- /* @__PURE__ */ jsx35(
2298
+ /* @__PURE__ */ jsx36(PanelBleed3, { children: /* @__PURE__ */ jsxs25(ActionBand3, { children: [
2299
+ /* @__PURE__ */ jsx36(
2327
2300
  ActionButton5,
2328
2301
  {
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" }),
2302
+ icon: isPlaying ? /* @__PURE__ */ jsx36(Pause3, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx36(Play3, { "aria-hidden": "true", className: "w-5 h-5" }),
2330
2303
  label: isPlaying ? "Pause" : "Play",
2331
2304
  disabled: !!unplayableReason,
2332
2305
  title: unplayableReason ?? void 0,
2333
2306
  onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
2334
2307
  }
2335
2308
  ),
2336
- shareUrl && /* @__PURE__ */ jsx35(ShareActionButton, { target: shareTarget, url: shareUrl })
2309
+ shareUrl && /* @__PURE__ */ jsx36(ShareActionButton, { target: shareTarget, url: shareUrl })
2337
2310
  ] }) }),
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 })
2311
+ song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx36("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx36(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
2312
+ /* @__PURE__ */ jsx36(FieldList2, { items: fields }),
2313
+ 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)) }) }),
2314
+ /* @__PURE__ */ jsx36(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
2342
2315
  ] });
2343
2316
  }
2344
2317
 
2345
2318
  // src/genre-tree/song/useSongPanel.tsx
2346
- import { jsx as jsx36, jsxs as jsxs26 } from "react/jsx-runtime";
2319
+ import { jsx as jsx37, jsxs as jsxs26 } from "react/jsx-runtime";
2347
2320
  function useSongPanel({
2348
2321
  uuid,
2349
2322
  scope,
@@ -2365,23 +2338,23 @@ function useSongPanel({
2365
2338
  ...panel,
2366
2339
  title: "Song",
2367
2340
  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" })
2341
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-3 w-16" }),
2342
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-4 w-32" }),
2343
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-3 w-16" }),
2344
+ /* @__PURE__ */ jsx37(Skeleton7, { className: "h-4 w-24" })
2372
2345
  ] })
2373
2346
  };
2374
2347
  case "not-found":
2375
2348
  return {
2376
2349
  ...panel,
2377
2350
  title: "Song not found",
2378
- content: /* @__PURE__ */ jsx36("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2351
+ content: /* @__PURE__ */ jsx37("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
2379
2352
  };
2380
2353
  case "error":
2381
2354
  return {
2382
2355
  ...panel,
2383
2356
  title: "Song",
2384
- content: /* @__PURE__ */ jsx36("p", { className: "text-red-600", children: "Could not load this song." })
2357
+ content: /* @__PURE__ */ jsx37("p", { className: "text-red-600", children: "Could not load this song." })
2385
2358
  };
2386
2359
  case "success": {
2387
2360
  const { song } = result;
@@ -2389,7 +2362,7 @@ function useSongPanel({
2389
2362
  return {
2390
2363
  ...panel,
2391
2364
  title: song.title,
2392
- content: /* @__PURE__ */ jsx36(
2365
+ content: /* @__PURE__ */ jsx37(
2393
2366
  SongPanelContent,
2394
2367
  {
2395
2368
  song,
@@ -2442,12 +2415,12 @@ function useEntityPanel({
2442
2415
  // src/genre-tree/SizeMetricToggle.tsx
2443
2416
  import { MdMusicNote, MdVisibility } from "react-icons/md";
2444
2417
  import { SegmentedControl } from "@behindthemusictree/ui";
2445
- import { jsx as jsx37 } from "react/jsx-runtime";
2418
+ import { jsx as jsx38 } from "react/jsx-runtime";
2446
2419
  function SizeMetricToggle({
2447
2420
  value,
2448
2421
  onChange
2449
2422
  }) {
2450
- return /* @__PURE__ */ jsx37(
2423
+ return /* @__PURE__ */ jsx38(
2451
2424
  SegmentedControl,
2452
2425
  {
2453
2426
  variant: "floating",
@@ -2455,15 +2428,15 @@ function SizeMetricToggle({
2455
2428
  value,
2456
2429
  onChange,
2457
2430
  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" }
2431
+ { value: "songs", label: /* @__PURE__ */ jsx38(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
2432
+ { value: "views", label: /* @__PURE__ */ jsx38(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
2460
2433
  ]
2461
2434
  }
2462
2435
  );
2463
2436
  }
2464
2437
 
2465
2438
  // src/genre-tree/GenreTreeView.tsx
2466
- import { Fragment as Fragment15, jsx as jsx38, jsxs as jsxs27 } from "react/jsx-runtime";
2439
+ import { Fragment as Fragment15, jsx as jsx39, jsxs as jsxs27 } from "react/jsx-runtime";
2467
2440
  var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
2468
2441
  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
2442
  var noop = () => {
@@ -2555,6 +2528,7 @@ function GenreTreeView({
2555
2528
  isFetching: isTreeFetching,
2556
2529
  genrePlaylistByNodeId,
2557
2530
  visibleGenrePlaylists,
2531
+ focusUuids,
2558
2532
  groupedGenrePlaylistsByRoot,
2559
2533
  selectedGenreUuid,
2560
2534
  selectGenre,
@@ -2614,14 +2588,14 @@ function GenreTreeView({
2614
2588
  const openSongRef = useRef3(openSong);
2615
2589
  openSongRef.current = openSong;
2616
2590
  const resolvedRenderSongLink = useCallback6(
2617
- (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx38("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
2591
+ (song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx39("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
2618
2592
  [renderSongLink]
2619
2593
  );
2620
2594
  const [expandedSongUuid, setExpandedSongUuid] = useState10(null);
2621
2595
  const [expandedArtistUuid, setExpandedArtistUuid] = useState10(null);
2622
2596
  const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
2623
2597
  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 }));
2598
+ const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx39("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
2625
2599
  const getGenrePath = (genre) => {
2626
2600
  const path = [];
2627
2601
  let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
@@ -2663,7 +2637,7 @@ function GenreTreeView({
2663
2637
  selectGenrePlaylist(genrePlaylist2);
2664
2638
  }
2665
2639
  });
2666
- const builtInPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : entityPanel;
2640
+ const otherPanel = overlayPanel ?? (selectedGenreUuid !== null ? void 0 : entityPanel);
2667
2641
  const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState10(false);
2668
2642
  const [uncontrolledExpanded, setUncontrolledExpanded] = useState10(false);
2669
2643
  const isCollapsed = collapsed ?? uncontrolledCollapsed;
@@ -2691,18 +2665,19 @@ function GenreTreeView({
2691
2665
  const obscuredInset = useObscuredInset(panelRef);
2692
2666
  const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
2693
2667
  const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
2694
- const treeNodes = useMemo8(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
2668
+ const treeNodes = useMemo8(() => toTreeNodes(visibleGenrePlaylists, nodeMetric, focusUuids), [visibleGenrePlaylists, nodeMetric, focusUuids]);
2695
2669
  const relations = useGenreNodeRelations(
2696
2670
  treeNodes,
2697
2671
  !overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
2698
2672
  resolveStyle
2699
2673
  );
2674
+ const treeStyles = useMemo8(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
2700
2675
  const [selectionHistory, setSelectionHistory] = useState10({ current: selectedGenreUuid, previous: null });
2701
2676
  if (selectionHistory.current !== selectedGenreUuid) {
2702
2677
  setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
2703
2678
  }
2704
2679
  const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
2705
- const { showPopup, hidePopup } = usePopup5();
2680
+ const { showPopup, hidePopup } = usePopup4();
2706
2681
  const reopenPanel = () => {
2707
2682
  setUncontrolledCollapsed(false);
2708
2683
  onCollapsedChange?.(false);
@@ -2742,7 +2717,7 @@ function GenreTreeView({
2742
2717
  selectGenre(uuid);
2743
2718
  },
2744
2719
  expanded: isExpanded,
2745
- treeRelations: relations,
2720
+ treeStyles,
2746
2721
  playback,
2747
2722
  genrePlaylist: selectedGenrePlaylist,
2748
2723
  band: {
@@ -2753,7 +2728,7 @@ function GenreTreeView({
2753
2728
  onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
2754
2729
  onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
2755
2730
  onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
2756
- /* @__PURE__ */ jsx38(
2731
+ /* @__PURE__ */ jsx39(
2757
2732
  ChangeParentPopup,
2758
2733
  {
2759
2734
  genrePlaylist: { ...genrePlaylist, criteria },
@@ -2787,15 +2762,15 @@ function GenreTreeView({
2787
2762
  const isRotationControlled = controlledAllowWheelRotation !== void 0;
2788
2763
  const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
2789
2764
  let activePanel = null;
2790
- if (overlayPanel) {
2765
+ if (otherPanel) {
2791
2766
  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
2767
+ title: otherPanel.title,
2768
+ content: otherPanel.content,
2769
+ aside: otherPanel.extraContent,
2770
+ expanded: otherPanel.expanded ?? false,
2771
+ onExpandedChange: otherPanel.onExpandedChange,
2772
+ onClose: otherPanel.onClose,
2773
+ searchName: otherPanel.title
2799
2774
  };
2800
2775
  } else if (selectedGenreUuid) {
2801
2776
  activePanel = {
@@ -2805,38 +2780,28 @@ function GenreTreeView({
2805
2780
  expanded: isExpanded,
2806
2781
  onExpandedChange: changeExpanded,
2807
2782
  onClose: clearSelection,
2808
- titleInSearch: true,
2783
+ searchName: genreContent.name,
2809
2784
  headerAccent: genreContent.headerAccent
2810
2785
  };
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
2786
  }
2822
2787
  const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
2823
2788
  const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
2824
2789
  edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
2825
2790
  center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
2826
2791
  } : void 0;
2827
- const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx38(
2792
+ const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx39(
2828
2793
  IconButton2,
2829
2794
  {
2830
2795
  "aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
2831
2796
  style: activePanel.headerAccent && { color: "inherit" },
2832
2797
  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" })
2798
+ 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
2799
  }
2835
2800
  ) : null;
2836
2801
  const renderArtistFilter = () => {
2837
2802
  if (isLoading || !filterArtist) return null;
2838
2803
  return /* @__PURE__ */ jsxs27("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
2839
- /* @__PURE__ */ jsx38(
2804
+ /* @__PURE__ */ jsx39(
2840
2805
  "button",
2841
2806
  {
2842
2807
  type: "button",
@@ -2848,7 +2813,7 @@ function GenreTreeView({
2848
2813
  children: filterArtist.name
2849
2814
  }
2850
2815
  ),
2851
- /* @__PURE__ */ jsx38(
2816
+ /* @__PURE__ */ jsx39(
2852
2817
  "button",
2853
2818
  {
2854
2819
  type: "button",
@@ -2861,7 +2826,7 @@ function GenreTreeView({
2861
2826
  ] });
2862
2827
  };
2863
2828
  const actions = /* @__PURE__ */ jsxs27(Fragment15, { children: [
2864
- !isLoading && !isError && /* @__PURE__ */ jsx38(
2829
+ !isLoading && !isError && /* @__PURE__ */ jsx39(
2865
2830
  GenreTreeSearch,
2866
2831
  {
2867
2832
  artistBaseUrl,
@@ -2884,9 +2849,9 @@ function GenreTreeView({
2884
2849
  if (songUuid) changeSong(null);
2885
2850
  selectGenre(uuid);
2886
2851
  },
2887
- selectedName: selectedGenreUuid ? genreContent.name : typeof builtInPanel?.title === "string" ? builtInPanel.title : null,
2852
+ selectedName: activePanel?.searchName ?? null,
2888
2853
  onClear: activePanel?.onClose,
2889
- showClear: activePanel?.titleInSearch ?? false,
2854
+ showClear: activePanel !== null,
2890
2855
  clearLabel: "Close panel",
2891
2856
  onPrevious: handlePrevious,
2892
2857
  onQueryChange: onSearchQueryChange,
@@ -2894,9 +2859,9 @@ function GenreTreeView({
2894
2859
  extraGroups: extraSearchGroups
2895
2860
  }
2896
2861
  ),
2897
- !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx38("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
2862
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx39("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
2898
2863
  /* @__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(
2864
+ !isLoading && !isError && !isControlled && /* @__PURE__ */ jsx39(
2900
2865
  SegmentedControl2,
2901
2866
  {
2902
2867
  "aria-label": "Tree view mode",
@@ -2910,13 +2875,13 @@ function GenreTreeView({
2910
2875
  ]
2911
2876
  }
2912
2877
  ),
2913
- !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ jsx38(
2878
+ !isLoading && !isError && showDisplayOptions && /* @__PURE__ */ jsx39(
2914
2879
  "div",
2915
2880
  {
2916
2881
  className: "flex items-center gap-1",
2917
2882
  role: "group",
2918
2883
  "aria-label": "Tree display options",
2919
- children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx38(
2884
+ children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx39(
2920
2885
  Button,
2921
2886
  {
2922
2887
  variant: allowWheelRotation ? "default" : "outline",
@@ -2927,7 +2892,7 @@ function GenreTreeView({
2927
2892
  )
2928
2893
  }
2929
2894
  ),
2930
- !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ jsx38(
2895
+ !isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ jsx39(
2931
2896
  IconTextButton,
2932
2897
  {
2933
2898
  icon: Plus2,
@@ -2943,36 +2908,37 @@ function GenreTreeView({
2943
2908
  "data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
2944
2909
  className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
2945
2910
  children: [
2946
- /* @__PURE__ */ jsx38(
2911
+ /* @__PURE__ */ jsx39(
2947
2912
  "div",
2948
2913
  {
2949
2914
  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
2915
  children: actions
2951
2916
  }
2952
2917
  ),
2953
- !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx38(
2918
+ !isLoading && !isError && filterArtist && /* @__PURE__ */ jsx39(
2954
2919
  "div",
2955
2920
  {
2956
2921
  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
2922
  children: renderArtistFilter()
2958
2923
  }
2959
2924
  ),
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: [
2925
+ /* @__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: [
2926
+ !isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx39("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
2927
+ isLoading ? /* @__PURE__ */ jsx39(GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ jsxs27("p", { role: "alert", className: "m-auto text-center", children: [
2963
2928
  "Couldn't load the genre tree.",
2964
2929
  " ",
2965
- /* @__PURE__ */ jsx38("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
2930
+ /* @__PURE__ */ jsx39("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
2966
2931
  ] }) : viewMode === "wheel" ? (
2967
2932
  // The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
2968
2933
  // and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
2969
2934
  // Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
2970
2935
  // 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(
2936
+ /* @__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
2937
  GenrePlaylistTreeWheel,
2973
2938
  {
2974
2939
  scope,
2975
2940
  genrePlaylists: visibleGenrePlaylists,
2941
+ focusUuids,
2976
2942
  handleGenreCreationAction,
2977
2943
  handleGenreRenameAction,
2978
2944
  additionalActions,
@@ -2987,7 +2953,7 @@ function GenreTreeView({
2987
2953
  busy,
2988
2954
  floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
2989
2955
  floatingControlsLeading,
2990
- canSizeByViews && /* @__PURE__ */ jsx38(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
2956
+ canSizeByViews && /* @__PURE__ */ jsx39(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
2991
2957
  ] }) : void 0,
2992
2958
  defaultViewport,
2993
2959
  viewportRestore,
@@ -2996,11 +2962,12 @@ function GenreTreeView({
2996
2962
  ) }) })
2997
2963
  ) : viewMode === "pop-core" ? (
2998
2964
  // 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(
2965
+ /* @__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
2966
  GenrePlaylistTreeWheelRadialPopCore,
3001
2967
  {
3002
2968
  scope,
3003
2969
  genrePlaylists: visibleGenrePlaylists,
2970
+ focusUuids,
3004
2971
  handleGenreCreationAction,
3005
2972
  handleGenreRenameAction,
3006
2973
  additionalActions,
@@ -3015,7 +2982,7 @@ function GenreTreeView({
3015
2982
  busy,
3016
2983
  floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs27("div", { className: "flex items-center gap-2", children: [
3017
2984
  floatingControlsLeading,
3018
- canSizeByViews && /* @__PURE__ */ jsx38(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
2985
+ canSizeByViews && /* @__PURE__ */ jsx39(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
3019
2986
  ] }) : void 0,
3020
2987
  defaultViewport,
3021
2988
  viewportRestore,
@@ -3023,12 +2990,13 @@ function GenreTreeView({
3023
2990
  ...focus
3024
2991
  }
3025
2992
  ) }) })
3026
- ) : viewMode === "outline" ? /* @__PURE__ */ jsx38("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx38(
2993
+ ) : viewMode === "outline" ? /* @__PURE__ */ jsx39("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx39(
3027
2994
  GenrePlaylistTreeWheelRadialPopCore,
3028
2995
  {
3029
2996
  outline: true,
3030
2997
  scope,
3031
2998
  genrePlaylists: visibleGenrePlaylists,
2999
+ focusUuids,
3032
3000
  handleGenreCreationAction,
3033
3001
  handleGenreRenameAction,
3034
3002
  additionalActions,
@@ -3039,13 +3007,13 @@ function GenreTreeView({
3039
3007
  selectedNodeId,
3040
3008
  readOnly
3041
3009
  }
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(
3010
+ ) }) : /* @__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
3011
  ([uuid, genrePlaylistTreePerRoot]) => {
3044
- return /* @__PURE__ */ jsx38(
3012
+ return /* @__PURE__ */ jsx39(
3045
3013
  "div",
3046
3014
  {
3047
3015
  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(
3016
+ children: /* @__PURE__ */ jsx39("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx39(
3049
3017
  GenrePlaylistTreePerRoot,
3050
3018
  {
3051
3019
  scope,
@@ -3058,6 +3026,7 @@ function GenreTreeView({
3058
3026
  onNodeHover: handleNodeHover,
3059
3027
  ...hostPanel,
3060
3028
  playback,
3029
+ focusUuids,
3061
3030
  selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
3062
3031
  readOnly,
3063
3032
  wheelZoom: "modifier",
@@ -3071,13 +3040,13 @@ function GenreTreeView({
3071
3040
  );
3072
3041
  }
3073
3042
  ) }),
3074
- /* @__PURE__ */ jsx38(
3043
+ /* @__PURE__ */ jsx39(
3075
3044
  DetailPanel2,
3076
3045
  {
3077
3046
  ref: panelRef,
3078
3047
  side: "left",
3079
3048
  title: activePanel?.title ?? "",
3080
- titleInSearch: activePanel?.titleInSearch,
3049
+ titleInSearch: !isLoading && !isError,
3081
3050
  headerAccent: activePanel?.headerAccent,
3082
3051
  headerActions: expandButton,
3083
3052
  onClose: activePanel?.onClose ?? noop,
@@ -3086,7 +3055,6 @@ function GenreTreeView({
3086
3055
  onExpandedChange: activePanel?.onExpandedChange,
3087
3056
  aside: activePanel?.aside,
3088
3057
  edgeTab: detailTab,
3089
- headerClassName: activePanel?.titleInSearch ? void 0 : "pt-search-clear",
3090
3058
  children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ jsxs27(Fragment15, { children: [
3091
3059
  activePanel.content,
3092
3060
  activePanel.aside
@@ -3113,7 +3081,7 @@ import { ListMusic } from "lucide-react";
3113
3081
  import { useTrackList as useTrackList7 } from "@behindthemusictree/app-kit/genre-tree";
3114
3082
  import { TrackListOriginType as TrackListOriginType3 } from "@behindthemusictree/app-kit/genre-tree";
3115
3083
  import { useTrackNoun as useTrackNoun3 } from "@behindthemusictree/app-kit/track-noun";
3116
- import { jsx as jsx39, jsxs as jsxs28 } from "react/jsx-runtime";
3084
+ import { jsx as jsx40, jsxs as jsxs28 } from "react/jsx-runtime";
3117
3085
  var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect7 : useLayoutEffect2;
3118
3086
  function TrackListSidebar({
3119
3087
  className: className2 = "",
@@ -3225,15 +3193,15 @@ function TrackListSidebar({
3225
3193
  /* @__PURE__ */ jsxs28("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
3226
3194
  /* @__PURE__ */ jsxs28("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
3227
3195
  /* @__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 }) })
3196
+ /* @__PURE__ */ jsx40("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
3197
+ /* @__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
3198
  ] }),
3231
3199
  /* @__PURE__ */ jsxs28("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
3232
3200
  trackList.origin.type === TrackListOriginType3.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType3.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
3233
3201
  `${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
3234
3202
  ] })
3235
3203
  ] }),
3236
- (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx39(
3204
+ (isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx40(
3237
3205
  "button",
3238
3206
  {
3239
3207
  type: "button",
@@ -3242,7 +3210,7 @@ function TrackListSidebar({
3242
3210
  "aria-expanded": !isCollapsed,
3243
3211
  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
3212
  onClick: isCollapsed ? onExpand : onMinimize,
3245
- children: /* @__PURE__ */ jsx39(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3213
+ children: /* @__PURE__ */ jsx40(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
3246
3214
  }
3247
3215
  )
3248
3216
  ] }),
@@ -3256,7 +3224,7 @@ function TrackListSidebar({
3256
3224
  onPointerDown: markUserScrollIntent,
3257
3225
  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
3226
  children: [
3259
- trackList.tracks.map((track, index) => /* @__PURE__ */ jsx39("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx39(
3227
+ trackList.tracks.map((track, index) => /* @__PURE__ */ jsx40("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx40(
3260
3228
  TrackItem,
3261
3229
  {
3262
3230
  track,
@@ -3267,7 +3235,7 @@ function TrackListSidebar({
3267
3235
  renderSongLink
3268
3236
  }
3269
3237
  ) }, track.uuid)),
3270
- hasMore && /* @__PURE__ */ jsx39("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3238
+ hasMore && /* @__PURE__ */ jsx40("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
3271
3239
  ]
3272
3240
  }
3273
3241
  )
@@ -3289,10 +3257,10 @@ import {
3289
3257
  import { PanelEdgeTab } from "@behindthemusictree/ui";
3290
3258
 
3291
3259
  // src/genre-tree/PlayerControls.tsx
3292
- import { Pause as Pause4, Play as Play5, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
3260
+ import { Pause as Pause4, Play as Play4, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
3293
3261
  import { formatTime, useTrackList as useTrackList8 } from "@behindthemusictree/app-kit/genre-tree";
3294
3262
  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";
3263
+ import { jsx as jsx41, jsxs as jsxs29 } from "react/jsx-runtime";
3296
3264
  function PlayerControls() {
3297
3265
  const {
3298
3266
  duration,
@@ -3318,8 +3286,8 @@ function PlayerControls() {
3318
3286
  className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
3319
3287
  children: [
3320
3288
  /* @__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(
3289
+ /* @__PURE__ */ jsx41("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
3290
+ /* @__PURE__ */ jsx41(
3323
3291
  "input",
3324
3292
  {
3325
3293
  "aria-label": "Seek",
@@ -3333,11 +3301,11 @@ function PlayerControls() {
3333
3301
  onChange: (event) => seekTo(Number(event.currentTarget.value))
3334
3302
  }
3335
3303
  ),
3336
- /* @__PURE__ */ jsx40("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
3304
+ /* @__PURE__ */ jsx41("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
3337
3305
  ] }),
3338
3306
  /* @__PURE__ */ jsxs29("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
3339
3307
  /* @__PURE__ */ jsxs29("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
3340
- /* @__PURE__ */ jsx40(
3308
+ /* @__PURE__ */ jsx41(
3341
3309
  "button",
3342
3310
  {
3343
3311
  type: "button",
@@ -3346,10 +3314,10 @@ function PlayerControls() {
3346
3314
  disabled: !isReady || !hasPreviousTrack,
3347
3315
  onClick: playPreviousTrack,
3348
3316
  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" })
3317
+ children: /* @__PURE__ */ jsx41(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
3350
3318
  }
3351
3319
  ),
3352
- /* @__PURE__ */ jsx40(
3320
+ /* @__PURE__ */ jsx41(
3353
3321
  "button",
3354
3322
  {
3355
3323
  type: "button",
@@ -3358,10 +3326,10 @@ function PlayerControls() {
3358
3326
  disabled: !isReady,
3359
3327
  onClick: handlePlayPauseAction,
3360
3328
  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" })
3329
+ children: isPlaying ? /* @__PURE__ */ jsx41(Pause4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx41(Play4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
3362
3330
  }
3363
3331
  ),
3364
- /* @__PURE__ */ jsx40(
3332
+ /* @__PURE__ */ jsx41(
3365
3333
  "button",
3366
3334
  {
3367
3335
  type: "button",
@@ -3370,14 +3338,14 @@ function PlayerControls() {
3370
3338
  disabled: !isReady || !hasNextTrack,
3371
3339
  onClick: playNextTrack,
3372
3340
  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" })
3341
+ children: /* @__PURE__ */ jsx41(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
3374
3342
  }
3375
3343
  )
3376
3344
  ] }),
3377
3345
  /* @__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(
3346
+ 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" }),
3347
+ /* @__PURE__ */ jsx41("span", { className: "sr-only", children: "Volume" }),
3348
+ /* @__PURE__ */ jsx41(
3381
3349
  "input",
3382
3350
  {
3383
3351
  "aria-label": "Volume",
@@ -3399,7 +3367,7 @@ function PlayerControls() {
3399
3367
  }
3400
3368
 
3401
3369
  // src/genre-tree/PlayerPanel.tsx
3402
- import { jsx as jsx41, jsxs as jsxs30 } from "react/jsx-runtime";
3370
+ import { jsx as jsx42, jsxs as jsxs30 } from "react/jsx-runtime";
3403
3371
  var PANEL_ID = "player-panel";
3404
3372
  var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
3405
3373
  function PlayerPanel({
@@ -3452,7 +3420,7 @@ function PlayerPanel({
3452
3420
  ),
3453
3421
  style: isExtended ? { width: extendedWidth } : void 0,
3454
3422
  children: [
3455
- /* @__PURE__ */ jsx41(
3423
+ /* @__PURE__ */ jsx42(
3456
3424
  PanelEdgeTab,
3457
3425
  {
3458
3426
  side: "right",
@@ -3471,19 +3439,19 @@ function PlayerPanel({
3471
3439
  ),
3472
3440
  children: [
3473
3441
  /* @__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 }) }),
3442
+ /* @__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
3443
  /* @__PURE__ */ jsxs30("div", { className: "relative w-full shrink-0", children: [
3476
- /* @__PURE__ */ jsx41(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3444
+ /* @__PURE__ */ jsx42(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
3477
3445
  playerTrackObject?.loadError && /* @__PURE__ */ jsxs30("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
3478
3446
  playerTrackObject.loadError,
3479
- /* @__PURE__ */ jsx41("button", { type: "button", className: "ml-2 underline", onClick: () => {
3447
+ /* @__PURE__ */ jsx42("button", { type: "button", className: "ml-2 underline", onClick: () => {
3480
3448
  void retryPlayback();
3481
3449
  }, children: "Retry" })
3482
3450
  ] })
3483
3451
  ] }),
3484
- /* @__PURE__ */ jsx41(PlayerControls, {})
3452
+ /* @__PURE__ */ jsx42(PlayerControls, {})
3485
3453
  ] }),
3486
- /* @__PURE__ */ jsx41(
3454
+ /* @__PURE__ */ jsx42(
3487
3455
  TrackListSidebar,
3488
3456
  {
3489
3457
  layout: "inline",
@@ -3510,11 +3478,11 @@ function PlayerPanel({
3510
3478
  import { v4 as uuidv4 } from "uuid";
3511
3479
  import { AiFillStar, AiOutlineStar } from "react-icons/ai";
3512
3480
  import { FORM_RATING_NULL_VALUE } from "@behindthemusictree/app-kit/genre-tree";
3513
- import { jsx as jsx42, jsxs as jsxs31 } from "react/jsx-runtime";
3481
+ import { jsx as jsx43, jsxs as jsxs31 } from "react/jsx-runtime";
3514
3482
  function Rating({ rating, handleChange }) {
3515
3483
  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(
3484
+ 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: [
3485
+ /* @__PURE__ */ jsx43(
3518
3486
  "input",
3519
3487
  {
3520
3488
  id: `star-${instanceId}-${i}`,
@@ -3526,16 +3494,16 @@ function Rating({ rating, handleChange }) {
3526
3494
  onChange: handleChange
3527
3495
  }
3528
3496
  ),
3529
- /* @__PURE__ */ jsx42("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs31("div", { className: "flex", children: [
3530
- /* @__PURE__ */ jsx42(
3497
+ /* @__PURE__ */ jsx43("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs31("div", { className: "flex", children: [
3498
+ /* @__PURE__ */ jsx43(
3531
3499
  AiOutlineStar,
3532
3500
  {
3533
3501
  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
3502
  }
3535
3503
  ),
3536
- /* @__PURE__ */ jsx42("span", { className: "flex-grow w-8" }),
3504
+ /* @__PURE__ */ jsx43("span", { className: "flex-grow w-8" }),
3537
3505
  " "
3538
- ] }) : /* @__PURE__ */ jsx42(
3506
+ ] }) : /* @__PURE__ */ jsx43(
3539
3507
  AiFillStar,
3540
3508
  {
3541
3509
  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 +3515,7 @@ function Rating({ rating, handleChange }) {
3547
3515
  // src/genre-tree/EntityDetailPanel.tsx
3548
3516
  import { useState as useState12 } from "react";
3549
3517
  import { DetailPanel as DetailPanel3 } from "@behindthemusictree/ui";
3550
- import { Fragment as Fragment16, jsx as jsx43, jsxs as jsxs32 } from "react/jsx-runtime";
3518
+ import { Fragment as Fragment16, jsx as jsx44, jsxs as jsxs32 } from "react/jsx-runtime";
3551
3519
  function EntityDetailPanel({
3552
3520
  scope,
3553
3521
  getBackendBaseUrl,
@@ -3570,8 +3538,8 @@ function EntityDetailPanel({
3570
3538
  getBackendBaseUrl,
3571
3539
  getArtistBackendBaseUrl,
3572
3540
  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 })),
3541
+ renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx44("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
3542
+ renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ jsx44("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
3575
3543
  song: {
3576
3544
  uuid: songUuid,
3577
3545
  expanded: songUuid !== null && expandedSongUuid === songUuid,
@@ -3587,7 +3555,7 @@ function EntityDetailPanel({
3587
3555
  ...navigation
3588
3556
  });
3589
3557
  if (!panel) return null;
3590
- return /* @__PURE__ */ jsx43(
3558
+ return /* @__PURE__ */ jsx44(
3591
3559
  DetailPanel3,
3592
3560
  {
3593
3561
  side: "left",
@@ -3633,4 +3601,4 @@ export {
3633
3601
  GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
3634
3602
  GenreTreeOutlineSkeleton
3635
3603
  };
3636
- //# sourceMappingURL=chunk-2K2SQOUJ.js.map
3604
+ //# sourceMappingURL=chunk-DPYUE6WD.js.map