PageSourceSearch

https://www.hamienet.com/assets/CollectionManager-HlyFhpAC.js

js hamienet.com collected 2026-10-02 05:01:36 UTC 121,538 bytes, 1,869 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports, R as React } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-w5lXls15.js";
3import { b as getGeminiAI, c as getAiImageModel } from "./ai-B0Mlroaa.js";
4import { bv as CircleCheckBig, b1 as CircleAlert, bK as PanelsTopLeft, cf as Plus, b6 as LoaderCircle, dM as Move, bA as Eye, bz as EyeOff, cx as List, b5 as Trash2, aX as ArrowLeft, bR as ExternalLink, b9 as Settings, aW as X, d9 as Image, bi as Sparkles, bn as Globe, cB as ChevronUp, cC as ChevronDown, f9 as Pen, bf as Search, bg as Music, b0 as FileText } from "./vendor-D0P4XgNj.js";
5import "./vendor-i18n-BC7ZMXWQ.js";
6import "./vendor-ai-gQv1oOjO.js";
7function CollectionManager() {
8  var _a;
9  const [collections, setCollections] = reactExports.useState([]);
10  const [loading, setLoading] = reactExports.useState(true);
11  const [error, setError] = reactExports.useState(null);
12  const [notification, setNotification] = reactExports.useState(null);
13  const [expandedSections, setExpandedSections] = reactExports.useState(/* @__PURE__ */ new Set());
14  const [selectedCollection, setSelectedCollection] = reactExports.useState(null);
15  const [editingCollection, setEditingCollection] = reactExports.useState(null);
16  const [editingSection, setEditingSection] = reactExports.useState(null);
17  const [editingSong, setEditingSong] = reactExports.useState(null);
18  const [rightPanelTab, setRightPanelTab] = reactExports.useState("works");
19  const [expandedSongId, setExpandedSongId] = reactExports.useState(null);
20  const [songMidiInput, setSongMidiInput] = reactExports.useState("");
21  const [draggedIdx, setDraggedIdx] = reactExports.useState(null);
22  const [draggedSectionIdx, setDraggedSectionIdx] = reactExports.useState(null);
23  const [draggedTrackIdx, setDraggedTrackIdx] = reactExports.useState(null);
24  const [draggedSongIdx, setDraggedSongIdx] = reactExports.useState(null);
25  const [tagSearch, setTagSearch] = reactExports.useState("");
26  const [tagSuggestions, setTagSuggestions] = reactExports.useState([]);
27  const [allSourceTags, setAllSourceTags] = reactExports.useState([]);
28  const [showTagSuggestions, setShowTagSuggestions] = reactExports.useState(false);
29  const [trackSearch, setTrackSearch] = reactExports.useState("");
30  const [trackResults, setTrackResults] = reactExports.useState([]);
31  const [searchingTracks, setSearchingTracks] = reactExports.useState(false);
32  const [generatingBannerId, setGeneratingBannerId] = reactExports.useState(null);
33  const [batchGenerating, setBatchGenerating] = reactExports.useState(false);
34  const [batchProgress, setBatchProgress] = reactExports.useState("");
35  const [tracksCache, setTracksCache] = reactExports.useState({});
36  const tagInputRef = reactExports.useRef(null);
37  const triggerNotification = (message, type = "success") => {
38    setNotification({ message, type });
39    setTimeout(() => setNotification(null), 4e3);
40  };
41  const fetchCollections = reactExports.useCallback(async () => {
42    setLoading(true);
43    setError(null);
44    try {
45      const data = await api.getAdminCollections();
46      const sorted = [...data].sort((a, b) => a.sortOrder - b.sortOrder);
47      setCollections(sorted);
48    } catch (e) {
49      console.error(e);
50      setError(e.message || "Failed to fetch collections");
51    } finally {
52      setLoading(false);
53    }
54  }, []);
55  const fetchSourceTags = reactExports.useCallback(async () => {
56    try {
57      const data = await api.getTags({ type: "SOURCE" });
58      setAllSourceTags(data);
59    } catch (e) {
60      console.error("Failed to load source tags:", e);
61    }
62  }, []);
63  const updateUrlParams = (updates) => {
64    const params = new URLSearchParams(window.location.search);
65    for (const [key, val] of Object.entries(updates)) {
66      if (val === null) {
67        params.delete(key);
68      } else {
69        params.set(key, val);
70      }
71    }
72    const queryString = params.toString();
73    const newUrl = queryString ? `${window.location.pathname}?${queryString}` : window.location.pathname;
74    window.history.pushState({}, "", newUrl);
75  };
76  reactExports.useEffect(() => {
77    fetchCollections();
78    fetchSourceTags();
79  }, [fetchCollections, fetchSourceTags]);
80  reactExports.useEffect(() => {
81    if (collections.length > 0 && !selectedCollection) {
82      const params = new URLSearchParams(window.location.search);
83      const colParam = params.get("collection");
84      if (colParam) {
85        const targetCol = collections.find((c) => c.slug === colParam || c.id === colParam);
86        if (targetCol) {
87          handleSelectCollection(targetCol);
88        }
89      }
90    }
91  }, [collections]);
92  reactExports.useEffect(() => {
93    var _a2;
94    if (selectedCollection) {
95      const params = new URLSearchParams(window.location.search);
96      const secParam = params.get("section");
97      if (secParam && (!editingSection || editingSection.id !== secParam && ((_a2 = editingSection.composer) == null ? void 0 : _a2.toLowerCase().replace(/[^a-z0-9]+/g, "-")) !== secParam)) {
98        const targetSec = selectedCollection.sections.find(
99          (s) => s.id === secParam || s.composer && s.composer.toLowerCase().replace(/[^a-z0-9]+/g, "-") === secParam || s.title.toLowerCase().replace(/[^a-z0-9]+/g, "-") === secParam
100        );
101        if (targetSec) {
102          setEditingSection(targetSec);
103        }
104      }
105    }
106  }, [selectedCollection]);
107  reactExports.useEffect(() => {
108    const handlePopState = () => {
109      const params = new URLSearchParams(window.location.search);
110      const colParam = params.get("collection");
111      const secParam = params.get("section");
112      if (!colParam) {
113        setSelectedCollection(null);
114        setEditingSection(null);
115      } else if (collections.length > 0) {
116        const targetCol = collections.find((c) => c.slug === colParam || c.id === colParam);
117        if (targetCol) {
118          if ((selectedCollection == null ? void 0 : selectedCollection.id) !== targetCol.id) {
119            handleSelectCollection(targetCol);
120          } else if (!secParam) {
121            setEditingSection(null);
122          }
123        }
124      }
125    };
126    window.addEventListener("popstate", handlePopState);
127    return () => window.removeEventListener("popstate", handlePopState);
128  }, [collections, selectedCollection]);
129  const handleSelectCollection = async (collection) => {
130    setLoading(true);
131    try {
132      const data = await api.getAdminCollection(collection.id);
133      setSelectedCollection(data);
134      setEditingCollection(null);
135      setEditingSection(null);
136      setExpandedSections(/* @__PURE__ */ new Set());
137      const secParam = new URLSearchParams(window.location.search).get("section");
138      updateUrlParams({ collection: collection.slug, section: secParam });
139      const cacheUpdates = {};
140      for (const section of data.sections) {
141        try {
142          const { tracks: sectionTracks } = await api.getSectionTracks(data.slug, section.id, "title", 100);
143          sectionTracks.forEach((t) => {
144            cacheUpdates[String(t.id)] = { title: t.title, artist: t.artist };
145          });
146        } catch (err) {
147          console.warn(`Failed to preload tracks for section ${section.id}:`, err);
148        }
149      }
150      setTracksCache((prev) => ({ ...prev, ...cacheUpdates }));
151    } catch (e) {
152      triggerNotification(e.message || "Failed to load collection details", "error");
153    } finally {
154      setLoading(false);
155    }
156  };
157  const handleSaveCollection = async () => {
158    if (!(editingCollection == null ? void 0 : editingCollection.title) || !(editingCollection == null ? void 0 : editingCollection.slug)) {
159      triggerNotification("Title and Slug are required", "error");
160      return;
161    }
162    setLoading(true);
163    try {
164      if (editingCollection.id) {
165        const updated = await api.updateAdminCollection(editingCollection.id, editingCollection);
166        triggerNotification("Collection updated successfully");
167        await fetchCollections();
168        if (selectedCollection && selectedCollection.id === editingCollection.id) {
169          await handleSelectCollection(updated);
170        }
171      } else {
172        const created = await api.createAdminCollection({
173          ...editingCollection,
174          sortOrder: collections.length,
175          theme: editingCollection.theme || {
176            gradientStart: "#4f46e5",
177            gradientEnd: "#7c3aed",
178            accentColor: "#3b82f6",
179            textColor: "#ffffff"
180          }
181        });
182        triggerNotification("Collection created successfully");
183        await fetchCollections();
184        await handleSelectCollection(created);
185      }
186      setEditingCollection(null);
187    } catch (e) {
188      triggerNotification(e.message || "Failed to save collection", "error");
189    } finally {
190      setLoading(false);
191    }
192  };
193  const handleTogglePublished = async (col, e) => {
194    e.stopPropagation();
195    try {
196      const nextStatus = !col.isPublished;
197      await api.updateAdminCollection(col.id, { isPublished: nextStatus });
198      setCollections((prev) => prev.map((c) => c.id === col.id ? { ...c, isPublished: nextStatus } : c));
199      if (selectedCollection && selectedCollection.id === col.id) {
200        setSelectedCollection((prev) => prev ? { ...prev, isPublished: nextStatus } : null);
201      }
202      triggerNotification(`Collection ${nextStatus ? "published" : "unpublished"} successfully`);
203    } catch (err) {
204      triggerNotification(err.message || "Failed to toggle status", "error");
205    }
206  };
207  const handleDeleteCollection = async (id, title, e) => {
208    e.stopPropagation();
209    if (!confirm(`Are you sure you want to delete the collection "${title}"? All sections inside will be deleted.`)) {
210      return;
211    }
212    setLoading(true);
213    try {
214      await api.deleteAdminCollection(id);
215      triggerNotification("Collection deleted successfully");
216      if ((selectedCollection == null ? void 0 : selectedCollection.id) === id) {
217        setSelectedCollection(null);
218      }
219      setEditingCollection(null);
220      setEditingSection(null);
221      await fetchCollections();
222    } catch (err) {
223      triggerNotification(err.message || "Failed to delete collection", "error");
224    } finally {
225      setLoading(false);
226    }
227  };
228  const handleSaveSong = async () => {
229    if (!editingSong || !editingSong.title || !selectedCollection || !(editingSection == null ? void 0 : editingSection.id)) {
230      triggerNotification("Title is required", "error");
231      return;
232    }
233    setLoading(true);
234    try {
235      if (editingSong.id) {
236        const updated = await api.updateSectionSong(selectedCollection.id, editingSection.id, editingSong.id, editingSong);
237        setEditingSection((prev) => {
238          var _a2;
239          if (!prev) return prev;
240          return { ...prev, songs: (_a2 = prev.songs) == null ? void 0 : _a2.map((s) => s.id === updated.id ? updated : s) };
241        });
242        triggerNotification("Work updated successfully");
243      } else {
244        const created = await api.createSectionSong(selectedCollection.id, editingSection.id, editingSong);
245        setEditingSection((prev) => {
246          if (!prev) return prev;
247          return { ...prev, songs: [...prev.songs || [], created] };
248        });
249        triggerNotification("Work created successfully");
250      }
251      setEditingSong(null);
252    } catch (e) {
253      triggerNotification(e.message || "Failed to save work", "error");
254    } finally {
255      setLoading(false);
256    }
257  };
258  const handleDeleteSong = async (songId, title, e) => {
259    e.stopPropagation();
260    if (!selectedCollection || !(editingSection == null ? void 0 : editingSection.id)) return;
261    if (!confirm(`Are you sure you want to delete the curated work "${title}"?`)) return;
262    setLoading(true);
263    try {
264      await api.deleteSectionSong(selectedCollection.id, editingSection.id, songId);
265      setEditingSection((prev) => {
266        if (!prev) return prev;
267        return { ...prev, songs: (prev.songs || []).filter((s) => s.id !== songId) };
268      });
269      triggerNotification("Work deleted successfully");
270    } catch (e2) {
271      triggerNotification(e2.message || "Failed to delete work", "error");
272    } finally {
273      setLoading(false);
274    }
275  };
276  const handleSongDragStart = (e, index) => {
277    setDraggedSongIdx(index);
278    e.dataTransfer.effectAllowed = "move";
279    e.dataTransfer.setData("text/plain", index.toString());
280  };
281  const handleSongDragOver = (e) => {
282    e.preventDefault();
283    e.dataTransfer.dropEffect = "move";
284  };
285  const handleSongDrop = async (e, dropIndex) => {
286    e.preventDefault();
287    const actualDragIdx = draggedSongIdx !== null ? draggedSongIdx : parseInt(e.dataTransfer.getData("text/plain"), 10);
288    if (actualDragIdx === null || isNaN(actualDragIdx) || actualDragIdx === dropIndex || !(editingSection == null ? void 0 : editingSection.songs) || !selectedCollection || !editingSection.id) return;
289    const newSongs = [...editingSection.songs];
290    const [removed] = newSongs.splice(actualDragIdx, 1);
291    newSongs.splice(dropIndex, 0, removed);
292    setEditingSection((prev) => prev ? { ...prev, songs: newSongs } : null);
293    try {
294      const songIds = newSongs.map((s) => s.id);
295      await api.reorderSectionSongs(selectedCollection.id, editingSection.id, songIds);
296    } catch (err) {
297      triggerNotification(err.message || "Failed to save work order", "error");
298      const collectionData = await api.getAdminCollection(selectedCollection.id);
299      const refetchedSection = collectionData.sections.find((s) => s.id === editingSection.id);
300      if (refetchedSection) setEditingSection(refetchedSection);
301    }
302    setDraggedSongIdx(null);
303  };
304  const handleSaveSection = async () => {
305    if (!selectedCollection) return;
306    if (!(editingSection == null ? void 0 : editingSection.title)) {
307      triggerNotification("Section title is required", "error");
308      return;
309    }
310    setLoading(true);
311    try {
312      const sectionPayload = {
313        title: editingSection.title,
314        parentId: editingSection.parentId || null,
315        subtitle: editingSection.subtitle || "",
316        description: editingSection.description || "",
317        bannerImage: editingSection.bannerImage || "",
318        composer: editingSection.composer || "",
319        tagSlugs: editingSection.tagSlugs || [],
320        curatedTrackIds: editingSection.curatedTrackIds || [],
321        isPublished: editingSection.isPublished !== void 0 ? editingSection.isPublished : true
322      };
323      if (editingSection.id) {
324        await api.updateAdminSection(selectedCollection.id, editingSection.id, sectionPayload);
325        triggerNotification("Section updated successfully");
326      } else {
327        await api.createAdminSection(selectedCollection.id, sectionPayload);
328        triggerNotification("Section created successfully");
329      }
330      await handleSelectCollection(selectedCollection);
331      setEditingSection(null);
332    } catch (err) {
333      triggerNotification(err.message || "Failed to save section", "error");
334    } finally {
335      setLoading(false);
336    }
337  };
338  const handleDeleteSection = async (sectionId, title) => {
339    if (!selectedCollection) return;
340    if (!confirm(`Are you sure you want to delete the section "${title}"?`)) return;
341    setLoading(true);
342    try {
343      await api.deleteAdminSection(selectedCollection.id, sectionId);
344      triggerNotification("Section deleted successfully");
345      await handleSelectCollection(selectedCollection);
346      if ((editingSection == null ? void 0 : editingSection.id) === sectionId) {
347        setEditingSection(null);
348      }
349    } catch (err) {
350      triggerNotification(err.message || "Failed to delete section", "error");
351    } finally {
352      setLoading(false);
353    }
354  };
355  const handleCollDragStart = (e, index) => {
356    setDraggedIdx(index);
357    e.dataTransfer.effectAllowed = "move";
358    e.dataTransfer.setData("text/plain", index.toString());
359  };
360  const handleCollDragOver = (e) => {
361    e.preventDefault();
362  };
363  const handleCollDrop = async (e, dropIndex) => {
364    e.preventDefault();
365    const dragIndexStr = e.dataTransfer.getData("text/plain");
366    const dragIndex = parseInt(dragIndexStr);
367    const actualDragIdx = !isNaN(dragIndex) ? dragIndex : draggedIdx;
368    if (actualDragIdx === null || actualDragIdx === dropIndex) return;
369    const newColls = [...collections];
370    const [draggedItem] = newColls.splice(actualDragIdx, 1);
371    newColls.splice(dropIndex, 0, draggedItem);
372    setCollections(newColls);
373    setDraggedIdx(null);
374    try {
375      await Promise.all(
376        newColls.map((c, index) => api.updateAdminCollection(c.id, { sortOrder: index }))
377      );
378      triggerNotification("Collection order updated");
379    } catch (err) {
380      console.error("Failed to save collection order:", err);
381      triggerNotification("Failed to save collection order", "error");
382      fetchCollections();
383    }
384  };
385  const handleSecDragStart = (e, index) => {
386    setDraggedSectionIdx(index);
387    e.dataTransfer.effectAllowed = "move";
388    e.dataTransfer.setData("text/plain", index.toString());
389  };
390  const handleSecDragOver = (e) => {
391    e.preventDefault();
392  };
393  const handleSecDrop = async (e, dropIndex) => {
394    e.preventDefault();
395    if (!selectedCollection) return;
396    const dragIndexStr = e.dataTransfer.getData("text/plain");
397    const dragIndex = parseInt(dragIndexStr);
398    const actualDragIdx = !isNaN(dragIndex) ? dragIndex : draggedSectionIdx;
399    if (actualDragIdx === null || actualDragIdx === dropIndex) return;
400    const newSections = [...selectedCollection.sections];
401    const [draggedItem] = newSections.splice(actualDragIdx, 1);
402    newSections.splice(dropIndex, 0, draggedItem);
403    setSelectedCollection({
404      ...selectedCollection,
405      sections: newSections
406    });
407    setDraggedSectionIdx(null);
408    try {
409      const sectionIds = newSections.map((s) => s.id);
410      await api.reorderAdminSections(selectedCollection.id, sectionIds);
411      triggerNotification("Section order updated");
412    } catch (err) {
413      console.error("Failed to save section order:", err);
414      triggerNotification("Failed to save section order", "error");
415      handleSelectCollection(selectedCollection);
416    }
417  };
418  reactExports.useEffect(() => {
419    if (!tagSearch.trim()) {
420      setTagSuggestions([]);
421      return;
422    }
423    const query = tagSearch.toLowerCase();
424    const matches = allSourceTags.filter(
425      (t) => (t.name.toLowerCase().includes(query) || t.slug.toLowerCase().includes(query)) && !((editingSection == null ? void 0 : editingSection.tagSlugs) || []).includes(t.slug)
426    );
427    setTagSuggestions(matches.slice(0, 10));
428  }, [tagSearch, allSourceTags, editingSection == null ? void 0 : editingSection.tagSlugs]);
429  const handleAddTag = (slug) => {
430    var _a2;
431    const currentSlugs = (editingSection == null ? void 0 : editingSection.tagSlugs) || [];
432    if (!currentSlugs.includes(slug)) {
433      setEditingSection((prev) => prev ? {
434        ...prev,
435        tagSlugs: [...currentSlugs, slug]
436      } : null);
437    }
438    setTagSearch("");
439    setShowTagSuggestions(false);
440    (_a2 = tagInputRef.current) == null ? void 0 : _a2.focus();
441  };
442  const handleRemoveTag = (slugToRemove) => {
443    setEditingSection((prev) => prev ? {
444      ...prev,
445      tagSlugs: (prev.tagSlugs || []).filter((s) => s !== slugToRemove)
446    } : null);
447  };
448  reactExports.useEffect(() => {
449    if (!trackSearch.trim() || trackSearch.length < 2) {
450      setTrackResults([]);
451      return;
452    }
453    const delayDebounce = setTimeout(async () => {
454      setSearchingTracks(true);
455      try {
456        const tracks = await api.getTracks(trackSearch, "all");
457        setTrackResults(tracks.slice(0, 15));
458      } catch (err) {
459        console.error(err);
460      } finally {
461        setSearchingTracks(false);
462      }
463    }, 300);
464    return () => clearTimeout(delayDebounce);
465  }, [trackSearch]);
466  const handlePinTrack = (track) => {
467    setTracksCache((prev) => ({
468      ...prev,
469      [String(track.id)]: { title: track.title, artist: track.artist }
470    }));
471    const currentPinned = (editingSection == null ? void 0 : editingSection.curatedTrackIds) || [];
472    const trackIdStr = String(track.id);
473    if (!currentPinned.includes(trackIdStr)) {
474      setEditingSection((prev) => prev ? {
475        ...prev,
476        curatedTrackIds: [...currentPinned, trackIdStr]
477      } : null);
478    }
479    setTrackSearch("");
480    setTrackResults([]);
481  };
482  const handleUnpinTrack = (trackIdToRemove) => {
483    setEditingSection((prev) => prev ? {
484      ...prev,
485      curatedTrackIds: (prev.curatedTrackIds || []).filter((id) => id !== trackIdToRemove)
486    } : null);
487  };
488  const handleTrackDragStart = (e, index) => {
489    setDraggedTrackIdx(index);
490    e.dataTransfer.effectAllowed = "move";
491    e.dataTransfer.setData("text/plain", index.toString());
492  };
493  const handleTrackDragOver = (e) => {
494    e.preventDefault();
495  };
496  const handleTrackDrop = (e, dropIndex) => {
497    e.preventDefault();
498    const dragIndexStr = e.dataTransfer.getData("text/plain");
499    const dragIndex = parseInt(dragIndexStr);
500    const actualDragIdx = !isNaN(dragIndex) ? dragIndex : draggedTrackIdx;
501    if (actualDragIdx === null || actualDragIdx === dropIndex || !editingSection) return;
502    const newPinned = [...editingSection.curatedTrackIds || []];
503    const [draggedItem] = newPinned.splice(actualDragIdx, 1);
504    newPinned.splice(dropIndex, 0, draggedItem);
505    setEditingSection({
506      ...editingSection,
507      curatedTrackIds: newPinned
508    });
509    setDraggedTrackIdx(null);
510  };
511  const getThemeColor = (theme, key, fallback) => {
512    if (!theme) return fallback;
513    try {
514      return theme[key] || fallback;
515    } catch {
516      return fallback;
517    }
518  };
519  const updateThemeKey = (key, val) => {
520    setEditingCollection((prev) => {
521      const currentTheme = (prev == null ? void 0 : prev.theme) || {
522        gradientStart: "#4f46e5",
523        gradientEnd: "#7c3aed",
524        accentColor: "#3b82f6",
525        textColor: "#ffffff"
526      };
527      return {
528        ...prev,
529        theme: {
530          ...currentTheme,
531          [key]: val
532        }
533      };
534    });
535  };
536  const buildBannerPrompt = (section, collection) => {
537    let prompt = `Create a cinematic, atmospheric banner image for a music website collection. `;
538    if (collection == null ? void 0 : collection.title) prompt += `The collection topic is '${collection.title}'. `;
539    if (section.title) prompt += `The specific section is about '${section.title}'. `;
540    if (section.subtitle) prompt += `Subtitle: '${section.subtitle}'. `;
541    if (section.composer) prompt += `Composer/Artist: '${section.composer}'. `;
542    if (section.description) prompt += `Context: ${section.description.substring(0, 300)}. `;
543    prompt += `Style: dramatic lighting, warm tones, high quality, artistic illustration suitable for a website header banner. Aspect ratio 16:9, no text, no words.`;
544    return prompt;
545  };
546  const handleGenerateSectionBanner = async (section, isBatch = false) => {
547    var _a2, _b, _c;
548    if (!selectedCollection) return false;
549    const secId = section.id || "new";
550    setGeneratingBannerId(String(secId));
551    try {
552      const prompt = buildBannerPrompt(section, selectedCollection);
553      const ai = getGeminiAI();
554      const model = getAiImageModel();
555      const response = await ai.models.generateContent({
556        model,
557        contents: { parts: [{ text: prompt }] }
558      });
559      let dataUrl = "";
560      for (const part of ((_c = (_b = (_a2 = response.candidates) == null ? void 0 : _a2[0]) == null ? void 0 : _b.content) == null ? void 0 : _c.parts) || []) {
561        if (part.inlineData) {
562          dataUrl = `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`;
563          break;
564        }
565      }
566      if (!dataUrl) {
567        throw new Error("No image generated. The AI model might have returned text instead of an image.");
568      }
569      const safeName = (section.composer || section.title || "banner").toLowerCase().replace(/[^a-z0-9]+/g, "-");
570      const res = await api.saveGeneratedAsset(dataUrl, `col-${selectedCollection.id}-${safeName}`);
571      if (editingSection && (!section.id || editingSection.id === section.id)) {
572        setEditingSection((prev) => prev ? { ...prev, bannerImage: res.url } : null);
573      }
574      if (section.id) {
575        await api.updateAdminSection(selectedCollection.id, section.id, { bannerImage: res.url });
576        setSelectedCollection((prev) => {
577          if (!prev) return prev;
578          return {
579            ...prev,
580            sections: prev.sections.map((s) => s.id === section.id ? { ...s, bannerImage: res.url } : s)
581          };
582        });
583      }
584      if (!isBatch) {
585        triggerNotification("Banner generated successfully!");
586      }
587      return true;
588    } catch (e) {
589      console.error(e);
590      if (!isBatch) {
591        triggerNotification(e.message || "Failed to generate banner", "error");
592      }
593      return false;
594    } finally {
595      setGeneratingBannerId(null);
596    }
597  };
598  const handleGenerateAllMissingBanners = async () => {
599    if (!selectedCollection) return;
600    const missingSections = selectedCollection.sections.filter((s) => !s.bannerImage);
601    if (missingSections.length === 0) {
602      triggerNotification("All sections already have banners!");
603      return;
604    }
605    if (!confirm(`Generate banners for ${missingSections.length} sections? This may take a while.`)) {
606      return;
607    }
608    setBatchGenerating(true);
609    let successCount = 0;
610    for (let i = 0; i < missingSections.length; i++) {
611      setBatchProgress(`${i + 1}/${missingSections.length}`);
612      const success = await handleGenerateSectionBanner(missingSections[i], true);
613      if (success) successCount++;
614    }
615    setBatchGenerating(false);
616    setBatchProgress("");
617    triggerNotification(`Batch generation complete! Created ${successCount} banners.`);
618  };
619  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col h-full bg-zinc-50 dark:bg-zinc-950 text-zinc-800 dark:text-zinc-200", children: [
620    notification && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `fixed top-4 right-4 z-50 px-4 py-3 rounded-lg shadow-lg flex items-center gap-2 border transition-all duration-300 ${notification.type === "success" ? "bg-emerald-50 border-emerald-200 text-emerald-800 dark:bg-emerald-950/80 dark:border-emerald-800 dark:text-emerald-300" : "bg-rose-50 border-rose-200 text-rose-800 dark:bg-rose-950/80 dark:border-rose-800 dark:text-rose-300"}`, children: [
621      notification.type === "success" ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { size: 18 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 18 }),
622      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium", children: notification.message })
623    ] }),
624    !selectedCollection ? (
625      /* ---------------- COLLECTIONS DASHBOARD LIST ---------------- */
626      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 flex-1 flex flex-col overflow-y-auto", children: [
627        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-6", children: [
628          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
629            /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-2xl font-bold text-zinc-950 dark:text-white flex items-center gap-2", children: [
630              /* @__PURE__ */ jsxRuntimeExports.jsx(PanelsTopLeft, { className: "text-indigo-500" }),
631              " MIDI Collections"
632            ] }),
633            /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 text-sm mt-1", children: "Create and curate themed landing pages for MIDI files with custom tags, tracks, and beautiful visual templates." })
634          ] }),
635          /* @__PURE__ */ jsxRuntimeExports.jsxs(
636            "button",
637            {
638              onClick: () => setEditingCollection({
639                title: "",
640                slug: "",
641                subtitle: "",
642                description: "",
643                heroImage: "",
644                theme: {
645                  gradientStart: "#111827",
646                  gradientEnd: "#312e81",
647                  accentColor: "#6366f1",
648                  textColor: "#ffffff"
649                },
650                isPublished: false,
651                metaTitle: "",
652                metaDescription: ""
653              }),
654              className: "bg-indigo-600 hover:bg-indigo-700 text-white font-semibold text-sm px-4 py-2.5 rounded-lg flex items-center gap-2 transition-all shadow-sm shadow-indigo-600/20",
655              children: [
656                /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 16 }),
657                " Create Collection"
658              ]
659            }
660          )
661        ] }),
662        editingCollection && !editingCollection.id && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6 p-6 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl shadow-sm", children: [
663          /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-lg font-bold mb-4 text-zinc-900 dark:text-white flex items-center gap-2", children: [
664            /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 18, className: "text-indigo-500" }),
665            " New Collection Details"
666          ] }),
667          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 mb-4", children: [
668            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
669              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-semibold text-zinc-500 uppercase tracking-wider mb-1", children: "Title *" }),
670              /* @__PURE__ */ jsxRuntimeExports.jsx(
671                "input",
672                {
673                  type: "text",
674                  className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
675                  placeholder: "e.g. Broadway & Musicals",
676                  value: editingCollection.title || "",
677                  onChange: (e) => {
678                    const val = e.target.value;
679                    const autoSlug = val.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
680                    setEditingCollection((prev) => ({ ...prev, title: val, slug: (prev == null ? void 0 : prev.slug) ? prev.slug : autoSlug }));
681                  }
682                }
683              )
684            ] }),
685            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
686              /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-semibold text-zinc-500 uppercase tracking-wider mb-1", children: "Slug *" }),
687              /* @__PURE__ */ jsxRuntimeExports.jsx(
688                "input",
689                {
690                  type: "text",
691                  className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
692                  placeholder: "e.g. broadway-musicals",
693                  value: editingCollection.slug || "",
694                  onChange: (e) => setEditingCollection((prev) => ({ ...prev, slug: e.target.value.toLowerCase() }))
695                }
696              )
697            ] })
698          ] }),
699          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2 mt-6", children: [
700            /* @__PURE__ */ jsxRuntimeExports.jsx(
701              "button",
702              {
703                onClick: () => setEditingCollection(null),
704                className: "px-4 py-2 text-sm border border-zinc-200 dark:border-zinc-800 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-850 font-medium transition-colors",
705                children: "Cancel"
706              }
707            ),
708            /* @__PURE__ */ jsxRuntimeExports.jsx(
709              "button",
710              {
711                onClick: handleSaveCollection,
712                className: "px-4 py-2 text-sm bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors shadow-sm",
713                children: "Create Collection"
714              }
715            )
716          ] })
717        ] }),
718        loading && collections.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col justify-center items-center py-20 text-zinc-500", children: [
719          /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "animate-spin text-indigo-500 mb-2", size: 32 }),
720          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Loading collections..." })
721        ] }) : error ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col justify-center items-center py-20 text-rose-500", children: [
722          /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "mb-2", size: 32 }),
723          /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: error })
724        ] }) : collections.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col justify-center items-center py-20 border-2 border-dashed border-zinc-200 dark:border-zinc-800 rounded-xl text-zinc-500 bg-zinc-50/50 dark:bg-zinc-900/10", children: [
725          /* @__PURE__ */ jsxRuntimeExports.jsx(PanelsTopLeft, { className: "mb-3 text-zinc-400", size: 40 }),
726          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-medium text-sm", children: "No collections created yet" }),
727          /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 mt-1", children: 'Click the "Create Collection" button above to get started.' })
728        ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: collections.map((col, index) => {
729          const gradStart = getThemeColor(col.theme, "gradientStart", "#4f46e5");
730          const gradEnd = getThemeColor(col.theme, "gradientEnd", "#7c3aed");
731          const txtColor = getThemeColor(col.theme, "textColor", "#ffffff");
732          return /* @__PURE__ */ jsxRuntimeExports.jsxs(
733            "div",
734            {
735              draggable: true,
736              onDragStart: (e) => handleCollDragStart(e, index),
737              onDragOver: handleCollDragOver,
738              onDrop: (e) => handleCollDrop(e, index),
739              onClick: () => handleSelectCollection(col),
740              className: "group relative flex flex-col bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 cursor-pointer hover:border-zinc-400 dark:hover:border-zinc-700 select-none",
741              children: [
742                /* @__PURE__ */ jsxRuntimeExports.jsxs(
743                  "div",
744                  {
745                    className: "h-24 p-4 flex flex-col justify-end relative transition-opacity duration-300",
746                    style: {
747                      backgroundImage: col.heroImage ? `url("${col.heroImage}"), linear-gradient(135deg, ${gradStart}, ${gradEnd})` : `linear-gradient(135deg, ${gradStart}, ${gradEnd})`,
748                      backgroundSize: "cover",
749                      backgroundPosition: "center"
750                    },
751                    children: [
752                      col.heroImage && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/40 backdrop-blur-[2px]" }),
753                      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-3 left-3 bg-black/40 backdrop-blur-md rounded-md p-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200 cursor-move z-10", title: "Drag to reorder", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Move, { size: 14, className: "text-white" }) }),
754                      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-3 right-3 flex items-center gap-1.5 z-10", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
755                        "button",
756                        {
757                          onClick: (e) => handleTogglePublished(col, e),
758                          className: `rounded-full p-1.5 backdrop-blur-md transition-colors ${col.isPublished ? "bg-emerald-500/20 text-emerald-300 border border-emerald-500/30" : "bg-zinc-500/20 text-zinc-300 border border-zinc-500/30"}`,
759                          title: col.isPublished ? "Published - Click to Unpublish" : "Unpublished - Click to Publish",
760                          children: col.isPublished ? /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 12 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { size: 12 })
761                        }
762                      ) }),
763                      /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-bold uppercase tracking-widest px-2 py-0.5 rounded bg-black/30 backdrop-blur-md w-fit z-10", style: { color: txtColor }, children: [
764                        "/",
765                        col.slug
766                      ] })
767                    ]
768                  }
769                ),
770                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 flex-1 flex flex-col", children: [
771                  /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-base text-zinc-900 dark:text-white line-clamp-1", children: col.title }),
772                  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-450 dark:text-zinc-500 mt-1 line-clamp-2 min-h-[32px]", children: col.subtitle || "No subtitle provided." }),
773                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mt-4 pt-3 border-t border-zinc-150 dark:border-zinc-800/80", children: [
774                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-zinc-500 font-medium", children: [
775                      /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 13 }),
776                      " ",
777                      col.sectionCount || 0,
778                      " Sections"
779                    ] }),
780                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
781                      /* @__PURE__ */ jsxRuntimeExports.jsx(
782                        "button",
783                        {
784                          onClick: (e) => {
785                            e.stopPropagation();
786                            handleSelectCollection(col);
787                          },
788                          className: "text-xs bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700 text-zinc-700 dark:text-zinc-300 font-semibold px-2.5 py-1.5 rounded-lg transition-colors flex items-center gap-1",
789                          children: "Manage"
790                        }
791                      ),
792                      /* @__PURE__ */ jsxRuntimeExports.jsx(
793                        "button",
794                        {
795                          onClick: (e) => handleDeleteCollection(col.id, col.title, e),
796                          className: "text-zinc-400 hover:text-rose-500 p-1.5 rounded-lg transition-colors",
797                          title: "Delete Collection",
798                          children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 14 })
799                        }
800                      )
801                    ] })
802                  ] })
803                ] })
804              ]
805            },
806            col.id
807          );
808        }) })
809      ] })
810    ) : (
811      /* ---------------- COLLECTION DETAILED WORKSPACE (METADATA + SECTIONS) ---------------- */
812      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
813        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-white dark:bg-zinc-900 border-b border-zinc-200 dark:border-zinc-800 flex justify-between items-center shrink-0", children: [
814          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
815            /* @__PURE__ */ jsxRuntimeExports.jsx(
816              "button",
817              {
818                onClick: () => {
819                  setSelectedCollection(null);
820                  updateUrlParams({ collection: null, section: null });
821                },
822                className: "p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg text-zinc-550 transition-colors",
823                title: "Back to Collections Dashboard",
824                children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 18 })
825              }
826            ),
827            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
828              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
829                /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-lg font-bold text-zinc-900 dark:text-white", children: selectedCollection.title }),
830                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[10px] font-semibold px-2 py-0.5 rounded-full ${selectedCollection.isPublished ? "bg-emerald-100 text-emerald-800 dark:bg-emerald-950/50 dark:text-emerald-300" : "bg-zinc-200 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-400"}`, children: selectedCollection.isPublished ? "Published" : "Draft" })
831              ] }),
832              /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-500 mt-0.5", children: [
833                "Slug: ",
834                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
835                  "/",
836                  selectedCollection.slug
837                ] })
838              ] })
839            ] })
840          ] }),
841          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
842            /* @__PURE__ */ jsxRuntimeExports.jsxs(
843              "a",
844              {
845                href: `https://test.hamienet.hk:8443/collections/${selectedCollection.slug}`,
846                target: "_blank",
847                rel: "noreferrer",
848                className: "border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-100 dark:hover:bg-zinc-800 text-zinc-700 dark:text-zinc-300 px-3.5 py-1.8 text-xs font-semibold rounded-lg flex items-center gap-1.5 transition-colors",
849                children: [
850                  /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 13 }),
851                  " View Live ",
852                  /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { size: 11 })
853                ]
854              }
855            ),
856            /* @__PURE__ */ jsxRuntimeExports.jsxs(
857              "button",
858              {
859                onClick: () => setEditingCollection(selectedCollection),
860                className: "bg-indigo-600 hover:bg-indigo-700 text-white px-3.5 py-1.8 text-xs font-semibold rounded-lg flex items-center gap-1.5 transition-all shadow-sm shadow-indigo-600/10",
861                children: [
862                  /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 13 }),
863                  " Edit Theme & Metadata"
864                ]
865              }
866            )
867          ] })
868        ] }),
869        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex overflow-hidden", children: [
870          editingCollection && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-96 border-r border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 overflow-y-auto p-5 shrink-0 flex flex-col", children: [
871            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
872              /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "font-bold text-zinc-900 dark:text-white text-sm flex items-center gap-1.5", children: [
873                /* @__PURE__ */ jsxRuntimeExports.jsx(Settings, { size: 16, className: "text-indigo-500" }),
874                " Collection Settings"
875              ] }),
876              /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => setEditingCollection(null), className: "p-1 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded text-zinc-500", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 16 }) })
877            ] }),
878            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 flex-1", children: [
879              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
880                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Title" }),
881                /* @__PURE__ */ jsxRuntimeExports.jsx(
882                  "input",
883                  {
884                    type: "text",
885                    className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
886                    value: editingCollection.title || "",
887                    onChange: (e) => setEditingCollection((prev) => ({ ...prev, title: e.target.value }))
888                  }
889                )
890              ] }),
891              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
892                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Slug" }),
893                /* @__PURE__ */ jsxRuntimeExports.jsx(
894                  "input",
895                  {
896                    type: "text",
897                    className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
898                    value: editingCollection.slug || "",
899                    onChange: (e) => setEditingCollection((prev) => ({ ...prev, slug: e.target.value.toLowerCase() }))
900                  }
901                )
902              ] }),
903              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
904                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Subtitle" }),
905                /* @__PURE__ */ jsxRuntimeExports.jsx(
906                  "input",
907                  {
908                    type: "text",
909                    className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
910                    value: editingCollection.subtitle || "",
911                    onChange: (e) => setEditingCollection((prev) => ({ ...prev, subtitle: e.target.value }))
912                  }
913                )
914              ] }),
915              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
916                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Description (Markdown Supported)" }),
917                /* @__PURE__ */ jsxRuntimeExports.jsx(
918                  "textarea",
919                  {
920                    rows: 4,
921                    className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none resize-y",
922                    placeholder: "Write description...",
923                    value: editingCollection.description || "",
924                    onChange: (e) => setEditingCollection((prev) => ({ ...prev, description: e.target.value }))
925                  }
926                )
927              ] }),
928              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
929                /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Hero Image URL / Path" }),
930                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
931                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-zinc-100 dark:bg-zinc-800 p-2 rounded-lg text-zinc-500 self-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { size: 16 }) }),
932                  /* @__PURE__ */ jsxRuntimeExports.jsx(
933                    "input",
934                    {
935                      type: "text",
936                      className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
937                      placeholder: "/images/hero-broadway.jpg",
938                      value: editingCollection.heroImage || "",
939                      onChange: (e) => setEditingCollection((prev) => ({ ...prev, heroImage: e.target.value }))
940                    }
941                  )
942                ] }),
943                editingCollection.heroImage && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 rounded-lg overflow-hidden border border-zinc-200 dark:border-zinc-800 bg-zinc-100 dark:bg-zinc-900 h-32 relative flex items-center justify-center", children: [
944                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold text-zinc-400", children: "Image Preview" }),
945                  /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: editingCollection.heroImage, alt: "Hero Preview", className: "absolute inset-0 w-full h-full object-cover", onError: (e) => e.currentTarget.style.display = "none", onLoad: (e) => e.currentTarget.style.display = "block" })
946                ] })
947              ] }),
948              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-zinc-100 dark:border-zinc-800/60 space-y-2", children: [
949                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-xs font-bold text-zinc-500 uppercase tracking-wide flex items-center gap-1", children: "Layout Settings" }),
950                /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-2 text-xs font-semibold cursor-pointer select-none", children: [
951                  /* @__PURE__ */ jsxRuntimeExports.jsx(
952                    "input",
953                    {
954                      type: "checkbox",
955                      className: "rounded border-zinc-300 dark:border-zinc-700 text-indigo-600 focus:ring-indigo-500",
956                      checked: !!((_a = editingCollection.theme) == null ? void 0 : _a.hideAbout),
957                      onChange: (e) => updateThemeKey("hideAbout", e.target.checked)
958                    }
959                  ),
960                  'Hide "About this Collection" Section'
961                ] })
962              ] }),
963              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-zinc-100 dark:border-zinc-800/60", children: [
964                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "block text-xs font-bold text-zinc-500 uppercase tracking-wide mb-2 flex items-center gap-1", children: [
965                  /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 12 }),
966                  " Theme Palette"
967                ] }),
968                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
969                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
970                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-semibold text-zinc-400 mb-0.5", children: "Gradient Start" }),
971                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5", children: [
972                      /* @__PURE__ */ jsxRuntimeExports.jsx(
973                        "input",
974                        {
975                          type: "color",
976                          className: "w-7 h-7 rounded border border-zinc-300 dark:border-zinc-800 cursor-pointer overflow-hidden bg-transparent",
977                          value: getThemeColor(editingCollection.theme, "gradientStart", "#4f46e5"),
978                          onChange: (e) => updateThemeKey("gradientStart", e.target.value)
979                        }
980                      ),
981                      /* @__PURE__ */ jsxRuntimeExports.jsx(
982                        "input",
983                        {
984                          type: "text",
985                          className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded px-2 py-1 text-xs outline-none",
986                          value: getThemeColor(editingCollection.theme, "gradientStart", "#4f46e5"),
987                          onChange: (e) => updateThemeKey("gradientStart", e.target.value)
988                        }
989                      )
990                    ] })
991                  ] }),
992                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
993                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-semibold text-zinc-400 mb-0.5", children: "Gradient End" }),
994                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5", children: [
995                      /* @__PURE__ */ jsxRuntimeExports.jsx(
996                        "input",
997                        {
998                          type: "color",
999                          className: "w-7 h-7 rounded border border-zinc-300 dark:border-zinc-800 cursor-pointer overflow-hidden bg-transparent",
1000                          value: getThemeColor(editingCollection.theme, "gradientEnd", "#7c3aed"),
1001                          onChange: (e) => updateThemeKey("gradientEnd", e.target.value)
1002                        }
1003                      ),
1004                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1005                        "input",
1006                        {
1007                          type: "text",
1008                          className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded px-2 py-1 text-xs outline-none",
1009                          value: getThemeColor(editingCollection.theme, "gradientEnd", "#7c3aed"),
1010                          onChange: (e) => updateThemeKey("gradientEnd", e.target.value)
1011                        }
1012                      )
1013                    ] })
1014                  ] }),
1015                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1016                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-semibold text-zinc-400 mb-0.5", children: "Accent Color" }),
1017                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5", children: [
1018                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1019                        "input",
1020                        {
1021                          type: "color",
1022                          className: "w-7 h-7 rounded border border-zinc-300 dark:border-zinc-800 cursor-pointer overflow-hidden bg-transparent",
1023                          value: getThemeColor(editingCollection.theme, "accentColor", "#3b82f6"),
1024                          onChange: (e) => updateThemeKey("accentColor", e.target.value)
1025                        }
1026                      ),
1027                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1028                        "input",
1029                        {
1030                          type: "text",
1031                          className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded px-2 py-1 text-xs outline-none",
1032                          value: getThemeColor(editingCollection.theme, "accentColor", "#3b82f6"),
1033                          onChange: (e) => updateThemeKey("accentColor", e.target.value)
1034                        }
1035                      )
1036                    ] })
1037                  ] }),
1038                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1039                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-semibold text-zinc-400 mb-0.5", children: "Text Color" }),
1040                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5", children: [
1041                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1042                        "input",
1043                        {
1044                          type: "color",
1045                          className: "w-7 h-7 rounded border border-zinc-300 dark:border-zinc-800 cursor-pointer overflow-hidden bg-transparent",
1046                          value: getThemeColor(editingCollection.theme, "textColor", "#ffffff"),
1047                          onChange: (e) => updateThemeKey("textColor", e.target.value)
1048                        }
1049                      ),
1050                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1051                        "input",
1052                        {
1053                          type: "text",
1054                          className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded px-2 py-1 text-xs outline-none",
1055                          value: getThemeColor(editingCollection.theme, "textColor", "#ffffff"),
1056                          onChange: (e) => updateThemeKey("textColor", e.target.value)
1057                        }
1058                      )
1059                    ] })
1060                  ] })
1061                ] })
1062              ] }),
1063              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-zinc-100 dark:border-zinc-800/60 space-y-3", children: [
1064                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "block text-xs font-bold text-zinc-500 uppercase tracking-wide flex items-center gap-1", children: [
1065                  /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 12 }),
1066                  " SEO Meta Tags"
1067                ] }),
1068                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1069                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-semibold text-zinc-450 mb-0.5", children: "Meta Title" }),
1070                  /* @__PURE__ */ jsxRuntimeExports.jsx(
1071                    "input",
1072                    {
1073                      type: "text",
1074                      className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-2.5 py-1.5 text-xs outline-none focus:ring-1 focus:ring-indigo-500",
1075                      placeholder: "Defaults to Title",
1076                      value: editingCollection.metaTitle || "",
1077                      onChange: (e) => setEditingCollection((prev) => ({ ...prev, metaTitle: e.target.value }))
1078                    }
1079                  )
1080                ] }),
1081                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1082                  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-semibold text-zinc-450 mb-0.5", children: "Meta Description" }),
1083                  /* @__PURE__ */ jsxRuntimeExports.jsx(
1084                    "textarea",
1085                    {
1086                      rows: 2,
1087                      className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-2.5 py-1.5 text-xs outline-none focus:ring-1 focus:ring-indigo-500 resize-none",
1088                      placeholder: "Provide summaries...",
1089                      value: editingCollection.metaDescription || "",
1090                      onChange: (e) => setEditingCollection((prev) => ({ ...prev, metaDescription: e.target.value }))
1091                    }
1092                  )
1093                ] })
1094              ] })
1095            ] }),
1096            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-6 pt-3 border-t border-zinc-200 dark:border-zinc-800 shrink-0 flex gap-2", children: [
1097              /* @__PURE__ */ jsxRuntimeExports.jsx(
1098                "button",
1099                {
1100                  onClick: () => setEditingCollection(null),
1101                  className: "flex-1 py-2 text-xs border border-zinc-200 dark:border-zinc-850 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded-lg font-semibold transition-colors",
1102                  children: "Discard"
1103                }
1104              ),
1105              /* @__PURE__ */ jsxRuntimeExports.jsx(
1106                "button",
1107                {
1108                  onClick: handleSaveCollection,
1109                  className: "flex-1 py-2 text-xs bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-semibold transition-colors shadow-sm",
1110                  children: "Save Changes"
1111                }
1112              )
1113            ] })
1114          ] }),
1115          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex flex-col overflow-hidden bg-zinc-50 dark:bg-zinc-950/20", children: !editingSection ? (
1116            /* SECTION DRAG & DROP LIST */
1117            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 flex-1 overflow-y-auto flex flex-col", children: [
1118              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
1119                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1120                  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "font-bold text-zinc-900 dark:text-white text-base", children: "Collection Sections" }),
1121                  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 mt-0.5", children: "Drag-and-drop rows using the handle icon to change visual sorting order." })
1122                ] }),
1123                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1124                  selectedCollection.sections.some((s) => !s.bannerImage) && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1125                    "button",
1126                    {
1127                      onClick: handleGenerateAllMissingBanners,
1128                      disabled: batchGenerating,
1129                      className: "bg-indigo-600/10 hover:bg-indigo-600/20 text-indigo-600 dark:text-indigo-400 px-3 py-1.5 text-xs font-semibold rounded-lg flex items-center gap-1.5 transition-colors border border-indigo-600/20 disabled:opacity-50",
1130                      children: [
1131                        batchGenerating ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14 }),
1132                        batchGenerating ? `Generating ${batchProgress}...` : "Generate Missing Banners"
1133                      ]
1134                    }
1135                  ),
1136                  /* @__PURE__ */ jsxRuntimeExports.jsxs(
1137                    "button",
1138                    {
1139                      onClick: () => setEditingSection({
1140                        title: "",
1141                        subtitle: "",
1142                        composer: "",
1143                        description: "",
1144                        bannerImage: "",
1145                        tagSlugs: [],
1146                        curatedTrackIds: [],
1147                        isPublished: true
1148                      }),
1149                      className: "bg-indigo-600/10 hover:bg-indigo-600/20 text-indigo-600 dark:text-indigo-400 px-3 py-1.5 text-xs font-semibold rounded-lg flex items-center gap-1.5 transition-colors border border-indigo-600/20",
1150                      children: [
1151                        /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 14 }),
1152                        " Add Section"
1153                      ]
1154                    }
1155                  )
1156                ] })
1157              ] }),
1158              selectedCollection.sections.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col justify-center items-center py-20 border-2 border-dashed border-zinc-200 dark:border-zinc-800/80 rounded-xl text-zinc-450 bg-white dark:bg-zinc-900/40", children: [
1159                /* @__PURE__ */ jsxRuntimeExports.jsx(List, { className: "mb-2 text-zinc-350", size: 32 }),
1160                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium", children: "No sections added to this collection" }),
1161                /* @__PURE__ */ jsxRuntimeExports.jsx(
1162                  "button",
1163                  {
1164                    onClick: () => setEditingSection({ title: "", tagSlugs: [], curatedTrackIds: [], isPublished: true }),
1165                    className: "mt-3 text-xs bg-indigo-600 hover:bg-indigo-700 text-white font-semibold px-3 py-1.5 rounded-lg transition-colors",
1166                    children: "Create First Section"
1167                  }
1168                )
1169              ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: selectedCollection.sections.map((sec, index) => {
1170                var _a2, _b;
1171                const hasChildren = selectedCollection.sections.some((s) => s.parentId === sec.id);
1172                let depth = 0;
1173                let current = selectedCollection.sections.find((s) => s.id === sec.parentId);
1174                while (current) {
1175                  depth++;
1176                  current = selectedCollection.sections.find((s) => s.id === (current == null ? void 0 : current.parentId));
1177                }
1178                let isVisible = true;
1179                if (sec.parentId) {
1180                  let curr = selectedCollection.sections.find((s) => s.id === sec.parentId);
1181                  while (curr) {
1182                    if (!expandedSections.has(curr.id)) {
1183                      isVisible = false;
1184                      break;
1185                    }
1186                    curr = selectedCollection.sections.find((s) => s.id === (curr == null ? void 0 : curr.parentId));
1187                  }
1188                }
1189                if (!isVisible) return null;
1190                return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1191                  "div",
1192                  {
1193                    draggable: true,
1194                    onDragStart: (e) => handleSecDragStart(e, index),
1195                    onDragOver: handleSecDragOver,
1196                    onDrop: (e) => handleSecDrop(e, index),
1197                    style: { marginLeft: `${depth * 24}px` },
1198                    className: "flex items-center justify-between p-4 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl hover:border-zinc-300 dark:hover:border-zinc-700 transition-colors shadow-sm select-none",
1199                    children: [
1200                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
1201                        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "cursor-move text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200", title: "Drag to reorder", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Move, { size: 16 }) }),
1202                        hasChildren ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1203                          "button",
1204                          {
1205                            onClick: (e) => {
1206                              e.stopPropagation();
1207                              setExpandedSections((prev) => {
1208                                const next = new Set(prev);
1209                                if (next.has(sec.id)) next.delete(sec.id);
1210                                else next.add(sec.id);
1211                                return next;
1212                              });
1213                            },
1214                            className: "text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200 transition-colors",
1215                            children: expandedSections.has(sec.id) ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 16 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 16 })
1216                          }
1217                        ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4" }),
1218                        sec.bannerImage && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-10 rounded overflow-hidden flex-shrink-0 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700", children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: sec.bannerImage, className: "w-full h-full object-cover", onError: (e) => e.currentTarget.style.display = "none", alt: "banner" }) }),
1219                        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1220                          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1221                            /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-zinc-900 dark:text-white text-sm", children: sec.title }),
1222                            sec.composer && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] bg-zinc-100 dark:bg-zinc-800 text-zinc-650 dark:text-zinc-400 px-2 py-0.5 rounded-md font-medium", children: [
1223                              "by ",
1224                              sec.composer
1225                            ] }),
1226                            /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-[9px] px-1.5 py-0.2 rounded font-semibold ${sec.isPublished ? "bg-emerald-50 text-emerald-700 dark:bg-emerald-950/20 dark:text-emerald-400" : "bg-zinc-100 text-zinc-550 dark:bg-zinc-850 dark:text-zinc-500"}`, children: sec.isPublished ? "Published" : "Draft" })
1227                          ] }),
1228                          sec.subtitle && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-450 dark:text-zinc-500 mt-0.5", children: sec.subtitle }),
1229                          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-4 mt-2 text-[10px] text-zinc-400", children: [
1230                            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-zinc-50 dark:bg-zinc-950 px-2 py-0.5 border border-zinc-100 dark:border-zinc-850/60 rounded", children: [
1231                              "Tags: ",
1232                              ((_a2 = sec.tagSlugs) == null ? void 0 : _a2.length) || 0
1233                            ] }),
1234                            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-zinc-50 dark:bg-zinc-950 px-2 py-0.5 border border-zinc-100 dark:border-zinc-850/60 rounded", children: [
1235                              "Curated Tracks: ",
1236                              ((_b = sec.curatedTrackIds) == null ? void 0 : _b.length) || 0
1237                            ] }),
1238                            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-indigo-50/50 dark:bg-indigo-950/20 text-indigo-600 dark:text-indigo-400 px-2 py-0.5 rounded", children: [
1239                              "Total matched tracks: ",
1240                              sec.trackCount || 0
1241                            ] })
1242                          ] })
1243                        ] })
1244                      ] }),
1245                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1.5 items-center", children: [
1246                        !sec.bannerImage && /* @__PURE__ */ jsxRuntimeExports.jsx(
1247                          "button",
1248                          {
1249                            onClick: (e) => {
1250                              e.stopPropagation();
1251                              handleGenerateSectionBanner(sec);
1252                            },
1253                            disabled: generatingBannerId === String(sec.id),
1254                            className: "p-1.5 bg-indigo-50 hover:bg-indigo-100 dark:bg-indigo-900/20 dark:hover:bg-indigo-900/40 text-indigo-600 dark:text-indigo-400 rounded-lg transition-colors border border-indigo-100 dark:border-indigo-800 disabled:opacity-50",
1255                            title: "Generate Banner with AI",
1256                            children: generatingBannerId === String(sec.id) ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 13, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 13 })
1257                          }
1258                        ),
1259                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1260                          "button",
1261                          {
1262                            onClick: () => {
1263                              setEditingSection(sec);
1264                              const secSlug = sec.composer ? sec.composer.toLowerCase().replace(/[^a-z0-9]+/g, "-") : sec.id || "sec";
1265                              updateUrlParams({ section: secSlug });
1266                            },
1267                            className: "p-2 bg-zinc-50 hover:bg-zinc-100 dark:bg-zinc-850 dark:hover:bg-zinc-800 text-zinc-650 dark:text-zinc-300 rounded-lg transition-colors border border-zinc-150 dark:border-zinc-800/80",
1268                            title: "Edit Section Details",
1269                            children: /* @__PURE__ */ jsxRuntimeExports.jsx(Pen, { size: 13 })
1270                          }
1271                        ),
1272                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1273                          "button",
1274                          {
1275                            onClick: () => handleDeleteSection(sec.id, sec.title),
1276                            className: "p-2 bg-zinc-50 hover:bg-rose-50 dark:bg-zinc-850 dark:hover:bg-rose-950/20 text-zinc-400 hover:text-rose-500 rounded-lg transition-colors border border-zinc-150 dark:border-zinc-800/80",
1277                            title: "Delete Section",
1278                            children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 13 })
1279                          }
1280                        )
1281                      ] })
1282                    ]
1283                  },
1284                  sec.id
1285                );
1286              }) })
1287            ] })
1288          ) : (
1289            /* SECTION DETAILED EDITOR FORM */
1290            /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
1291              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 bg-zinc-100 dark:bg-zinc-900 border-b border-zinc-200 dark:border-zinc-850 flex justify-between items-center shrink-0", children: [
1292                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1293                  /* @__PURE__ */ jsxRuntimeExports.jsx(
1294                    "button",
1295                    {
1296                      onClick: () => {
1297                        setEditingSection(null);
1298                        updateUrlParams({ section: null });
1299                      },
1300                      className: "p-1.5 hover:bg-zinc-200 dark:hover:bg-zinc-800 rounded-lg text-zinc-600 transition-colors",
1301                      children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { size: 16 })
1302                    }
1303                  ),
1304                  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "font-bold text-zinc-900 dark:text-white text-sm", children: editingSection.id ? `Edit Section: ${editingSection.title}` : "Create Collection Section" })
1305                ] }),
1306                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1307                  /* @__PURE__ */ jsxRuntimeExports.jsx(
1308                    "button",
1309                    {
1310                      onClick: () => {
1311                        setEditingSection(null);
1312                        updateUrlParams({ section: null });
1313                      },
1314                      className: "px-3.5 py-1.5 border border-zinc-200 dark:border-zinc-800 text-xs font-semibold rounded-lg hover:bg-zinc-200 dark:hover:bg-zinc-800 transition-colors",
1315                      children: "Cancel"
1316                    }
1317                  ),
1318                  /* @__PURE__ */ jsxRuntimeExports.jsx(
1319                    "button",
1320                    {
1321                      onClick: handleSaveSection,
1322                      className: "px-3.5 py-1.5 bg-indigo-600 hover:bg-indigo-700 text-white text-xs font-semibold rounded-lg transition-colors shadow-sm",
1323                      children: "Save Section"
1324                    }
1325                  )
1326                ] })
1327              ] }),
1328              /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto p-5", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 xl:grid-cols-2 gap-6 mb-6", children: [
1329                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl p-5 shadow-sm h-fit", children: [
1330                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [
1331                    /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-xs text-zinc-450 uppercase tracking-wider", children: "General Configuration" }),
1332                    /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer text-xs font-medium", children: [
1333                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1334                        "input",
1335                        {
1336                          type: "checkbox",
1337                          className: "rounded border-zinc-300 dark:border-zinc-700 focus:ring-indigo-500 text-indigo-600",
1338                          checked: editingSection.isPublished !== void 0 ? editingSection.isPublished : true,
1339                          onChange: (e) => setEditingSection((prev) => prev ? { ...prev, isPublished: e.target.checked } : null)
1340                        }
1341                      ),
1342                      "Published"
1343                    ] })
1344                  ] }),
1345                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1346                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Section Title *" }),
1347                    /* @__PURE__ */ jsxRuntimeExports.jsx(
1348                      "input",
1349                      {
1350                        type: "text",
1351                        className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
1352                        placeholder: "e.g. Act I Favorites",
1353                        value: editingSection.title || "",
1354                        onChange: (e) => setEditingSection((prev) => prev ? { ...prev, title: e.target.value } : null)
1355                      }
1356                    )
1357                  ] }),
1358                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1359                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Parent Section (Optional)" }),
1360                    /* @__PURE__ */ jsxRuntimeExports.jsxs(
1361                      "select",
1362                      {
1363                        className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm text-zinc-800 dark:text-zinc-200 focus:ring-2 focus:ring-indigo-500 outline-none",
1364                        value: editingSection.parentId || "",
1365                        onChange: (e) => setEditingSection((prev) => prev ? { ...prev, parentId: e.target.value || null } : null),
1366                        children: [
1367                          /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-850", children: "None (Top Level)" }),
1368                          selectedCollection.sections.filter((s) => s.id !== editingSection.id).map((s) => /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: s.id, className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-850", children: [
1369                            s.title,
1370                            " ",
1371                            s.composer ? `(${s.composer})` : ""
1372                          ] }, s.id))
1373                        ]
1374                      }
1375                    )
1376                  ] }),
1377                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
1378                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1379                      /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Composer / Artist" }),
1380                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1381                        "input",
1382                        {
1383                          type: "text",
1384                          className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
1385                          placeholder: "e.g. Andrew Lloyd Webber",
1386                          value: editingSection.composer || "",
1387                          onChange: (e) => setEditingSection((prev) => prev ? { ...prev, composer: e.target.value } : null)
1388                        }
1389                      )
1390                    ] }),
1391                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1392                      /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Section Subtitle" }),
1393                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1394                        "input",
1395                        {
1396                          type: "text",
1397                          className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
1398                          placeholder: "e.g. Popular Showstoppers",
1399                          value: editingSection.subtitle || "",
1400                          onChange: (e) => setEditingSection((prev) => prev ? { ...prev, subtitle: e.target.value } : null)
1401                        }
1402                      )
1403                    ] })
1404                  ] }),
1405                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1406                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-1", children: [
1407                      /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase", children: "Banner Image URL / Path" }),
1408                      /* @__PURE__ */ jsxRuntimeExports.jsxs(
1409                        "button",
1410                        {
1411                          onClick: () => handleGenerateSectionBanner(editingSection),
1412                          disabled: generatingBannerId === String(editingSection.id || "new"),
1413                          className: "flex items-center gap-1 text-[10px] font-bold text-indigo-600 dark:text-indigo-400 bg-indigo-50 dark:bg-indigo-900/20 px-2 py-1 rounded border border-indigo-100 dark:border-indigo-800 hover:bg-indigo-100 dark:hover:bg-indigo-900/40 transition-colors disabled:opacity-50",
1414                          children: [
1415                            generatingBannerId === String(editingSection.id || "new") ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 12, className: "animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 12 }),
1416                            "Generate with AI"
1417                          ]
1418                        }
1419                      )
1420                    ] }),
1421                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1422                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-zinc-150 dark:bg-zinc-800 p-2 rounded-lg text-zinc-550 self-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Image, { size: 16 }) }),
1423                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1424                        "input",
1425                        {
1426                          type: "text",
1427                          className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none",
1428                          placeholder: "/images/broadway-banner.jpg",
1429                          value: editingSection.bannerImage || "",
1430                          onChange: (e) => setEditingSection((prev) => prev ? { ...prev, bannerImage: e.target.value } : null)
1431                        }
1432                      )
1433                    ] }),
1434                    editingSection.bannerImage && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 rounded-lg overflow-hidden border border-zinc-200 dark:border-zinc-800 bg-zinc-100 dark:bg-zinc-900 h-24 relative flex items-center justify-center", children: [
1435                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold text-zinc-400", children: "Image Preview" }),
1436                      /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: editingSection.bannerImage, alt: "Banner Preview", className: "absolute inset-0 w-full h-full object-cover", onError: (e) => e.currentTarget.style.display = "none", onLoad: (e) => e.currentTarget.style.display = "block" })
1437                    ] })
1438                  ] }),
1439                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1440                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-1", children: "Description (Markdown Supported)" }),
1441                    /* @__PURE__ */ jsxRuntimeExports.jsx(
1442                      "textarea",
1443                      {
1444                        rows: 4,
1445                        className: "w-full bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none resize-y",
1446                        placeholder: "Provide section descriptions...",
1447                        value: editingSection.description || "",
1448                        onChange: (e) => setEditingSection((prev) => prev ? { ...prev, description: e.target.value } : null)
1449                      }
1450                    )
1451                  ] }),
1452                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2 border-t border-zinc-100 dark:border-zinc-850/60", children: [
1453                    /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-xs font-bold text-zinc-450 uppercase mb-2", children: "Assigned Source Tags" }),
1454                    /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1.5 mb-2.5", children: (editingSection.tagSlugs || []).length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-450 italic", children: "No tags assigned. Dynamic track matching is disabled." }) : (editingSection.tagSlugs || []).map((slug) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 bg-indigo-50 text-indigo-700 dark:bg-indigo-950/40 dark:text-indigo-400 px-2.5 py-1 rounded-md text-xs font-medium border border-indigo-100 dark:border-indigo-900/30", children: [
1455                      "#",
1456                      slug,
1457                      /* @__P
1457URE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => handleRemoveTag(slug), className: "hover:text-rose-500 transition-colors p-0.5 rounded ml-0.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10 }) })
1458                    ] }, slug)) }),
1459                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
1460                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-1 text-sm focus-within:ring-2 focus-within:ring-indigo-500", children: [
1461                        /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 14, className: "text-zinc-400 mr-2" }),
1462                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1463                          "input",
1464                          {
1465                            ref: tagInputRef,
1466                            type: "text",
1467                            className: "w-full bg-transparent border-none outline-none py-1 text-sm",
1468                            placeholder: "Type to search and add source tag...",
1469                            value: tagSearch,
1470                            onChange: (e) => {
1471                              setTagSearch(e.target.value);
1472                              setShowTagSuggestions(true);
1473                            },
1474                            onFocus: () => setShowTagSuggestions(true),
1475                            onKeyDown: (e) => {
1476                              if (e.key === "Enter" && tagSearch.trim()) {
1477                                e.preventDefault();
1478                                const cleanSlug = tagSearch.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-");
1479                                if (cleanSlug) {
1480                                  handleAddTag(cleanSlug);
1481                                }
1482                              }
1483                            }
1484                          }
1485                        )
1486                      ] }),
1487                      showTagSuggestions && tagSuggestions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
1488                        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed inset-0 z-10", onClick: () => setShowTagSuggestions(false) }),
1489                        /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 right-0 top-full mt-1 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg shadow-lg z-20 max-h-48 overflow-y-auto", children: tagSuggestions.map((tag) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
1490                          "button",
1491                          {
1492                            type: "button",
1493                            onClick: () => handleAddTag(tag.slug),
1494                            className: "w-full text-left px-4 py-2 hover:bg-zinc-100 dark:hover:bg-zinc-800 text-xs font-semibold flex justify-between items-center",
1495                            children: [
1496                              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-800 dark:text-zinc-150", children: [
1497                                "#",
1498                                tag.slug
1499                              ] }),
1500                              /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-400 font-normal", children: [
1501                                "Score: ",
1502                                tag.score || 0
1503                              ] })
1504                            ]
1505                          },
1506                          tag.id
1507                        )) })
1508                      ] })
1509                    ] })
1510                  ] })
1511                ] }),
1512                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl p-5 shadow-sm flex flex-col h-[650px]", children: [
1513                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-zinc-850 pb-3 shrink-0", children: [
1514                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1515                      /* @__PURE__ */ jsxRuntimeExports.jsxs(
1516                        "button",
1517                        {
1518                          type: "button",
1519                          onClick: () => setRightPanelTab("works"),
1520                          className: `px-3 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-colors ${rightPanelTab === "works" ? "bg-indigo-600 text-white shadow-sm" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,
1521                          children: [
1522                            /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 14 }),
1523                            " Canonical Works (",
1524                            (editingSection.songs || []).length,
1525                            ")"
1526                          ]
1527                        }
1528                      ),
1529                      /* @__PURE__ */ jsxRuntimeExports.jsxs(
1530                        "button",
1531                        {
1532                          type: "button",
1533                          onClick: () => setRightPanelTab("pinned"),
1534                          className: `px-3 py-1.5 text-xs font-bold rounded-lg flex items-center gap-1.5 transition-colors ${rightPanelTab === "pinned" ? "bg-indigo-600 text-white shadow-sm" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-zinc-200 dark:hover:bg-zinc-700"}`,
1535                          children: [
1536                            /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14, className: rightPanelTab === "pinned" ? "text-amber-300" : "text-amber-500" }),
1537                            " Pinned Tracks (",
1538                            (editingSection.curatedTrackIds || []).length,
1539                            ")"
1540                          ]
1541                        }
1542                      )
1543                    ] }),
1544                    rightPanelTab === "works" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1545                      "button",
1546                      {
1547                        type: "button",
1548                        onClick: () => setEditingSong({ title: "", groupName: "", details: "", associatedMidiIds: [] }),
1549                        className: "bg-indigo-600/10 hover:bg-indigo-600/20 text-indigo-600 dark:text-indigo-400 px-2.5 py-1 text-xs font-semibold rounded-lg flex items-center gap-1 transition-colors border border-indigo-600/20",
1550                        children: [
1551                          /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { size: 13 }),
1552                          " Add Work"
1553                        ]
1554                      }
1555                    )
1556                  ] }),
1557                  rightPanelTab === "works" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex flex-col overflow-hidden", children: editingSong ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-800 rounded-lg p-4 shrink-0 overflow-y-auto", children: [
1558                    /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-bold mb-3", children: editingSong.id ? "Edit Work" : "Add New Work" }),
1559                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-3 mb-3", children: [
1560                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1561                        /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-bold text-zinc-450 uppercase mb-1", children: "Group Name" }),
1562                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1563                          "input",
1564                          {
1565                            type: "text",
1566                            className: "w-full bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-800 rounded px-3 py-1.5 text-xs focus:ring-1 focus:ring-indigo-500 outline-none",
1567                            placeholder: "e.g. Piano, Concertos",
1568                            value: editingSong.groupName || "",
1569                            onChange: (e) => setEditingSong((prev) => prev ? { ...prev, groupName: e.target.value } : null)
1570                          }
1571                        )
1572                      ] }),
1573                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
1574                        /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-bold text-zinc-450 uppercase mb-1", children: "Details" }),
1575                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1576                          "input",
1577                          {
1578                            type: "text",
1579                            className: "w-full bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-800 rounded px-3 py-1.5 text-xs focus:ring-1 focus:ring-indigo-500 outline-none",
1580                            placeholder: "e.g. op 10",
1581                            value: editingSong.details || "",
1582                            onChange: (e) => setEditingSong((prev) => prev ? { ...prev, details: e.target.value } : null)
1583                          }
1584                        )
1585                      ] })
1586                    ] }),
1587                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3", children: [
1588                      /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-bold text-zinc-450 uppercase mb-1", children: "Title *" }),
1589                      /* @__PURE__ */ jsxRuntimeExports.jsx(
1590                        "input",
1591                        {
1592                          type: "text",
1593                          className: "w-full bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-800 rounded px-3 py-1.5 text-sm font-semibold focus:ring-1 focus:ring-indigo-500 outline-none",
1594                          placeholder: "Work title...",
1595                          value: editingSong.title || "",
1596                          onChange: (e) => setEditingSong((prev) => prev ? { ...prev, title: e.target.value } : null)
1597                        }
1598                      )
1599                    ] }),
1600                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4", children: [
1601                      /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-bold text-zinc-450 uppercase mb-1", children: "Attached MIDIs (MIDI Track IDs)" }),
1602                      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-1.5 mb-2 flex-wrap min-h-[28px] p-2 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-md", children: (editingSong.associatedMidiIds || []).length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-zinc-400 italic", children: "No MIDIs attached yet. Add IDs below." }) : (editingSong.associatedMidiIds || []).map((midiId, i) => {
1603                        const cached = tracksCache[String(midiId)];
1604                        return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-1 bg-indigo-100 dark:bg-indigo-900/50 text-indigo-800 dark:text-indigo-200 text-xs font-semibold rounded-md border border-indigo-200 dark:border-indigo-800", children: [
1605                          /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 11 }),
1606                          " #",
1607                          midiId,
1608                          " ",
1609                          (cached == null ? void 0 : cached.title) ? `(${cached.title})` : "",
1610                          /* @__PURE__ */ jsxRuntimeExports.jsx(
1611                            "button",
1612                            {
1613                              type: "button",
1614                              onClick: () => {
1615                                const updated = (editingSong.associatedMidiIds || []).filter((_, idx) => idx !== i);
1616                                setEditingSong((prev) => prev ? { ...prev, associatedMidiIds: updated } : null);
1617                              },
1618                              className: "hover:text-rose-500 font-bold ml-1 text-sm leading-none",
1619                              children: "×"
1620                            }
1621                          )
1622                        ] }, i);
1623                      }) }),
1624                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
1625                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1626                          "input",
1627                          {
1628                            type: "text",
1629                            className: "flex-1 bg-white dark:bg-zinc-900 border border-zinc-300 dark:border-zinc-800 rounded px-3 py-1 text-xs outline-none focus:ring-1 focus:ring-indigo-500",
1630                            placeholder: "Enter MIDI ID or comma-separated IDs (e.g. 1042, 1043)...",
1631                            value: songMidiInput,
1632                            onChange: (e) => setSongMidiInput(e.target.value),
1633                            onKeyDown: (e) => {
1634                              if (e.key === "Enter") {
1635                                e.preventDefault();
1636                                if (songMidiInput.trim()) {
1637                                  const newIds = songMidiInput.split(",").map((s) => s.trim()).filter(Boolean);
1638                                  const current = (editingSong.associatedMidiIds || []).map(String);
1639                                  const combined = Array.from(/* @__PURE__ */ new Set([...current, ...newIds]));
1640                                  setEditingSong((prev) => prev ? { ...prev, associatedMidiIds: combined } : null);
1641                                  setSongMidiInput("");
1642                                }
1643                              }
1644                            }
1645                          }
1646                        ),
1647                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1648                          "button",
1649                          {
1650                            type: "button",
1651                            onClick: () => {
1652                              if (songMidiInput.trim()) {
1653                                const newIds = songMidiInput.split(",").map((s) => s.trim()).filter(Boolean);
1654                                const current = (editingSong.associatedMidiIds || []).map(String);
1655                                const combined = Array.from(/* @__PURE__ */ new Set([...current, ...newIds]));
1656                                setEditingSong((prev) => prev ? { ...prev, associatedMidiIds: combined } : null);
1657                                setSongMidiInput("");
1658                              }
1659                            },
1660                            className: "px-3 py-1 bg-zinc-200 dark:bg-zinc-800 text-xs font-semibold rounded hover:bg-zinc-300 dark:hover:bg-zinc-700",
1661                            children: "+ Add MIDI ID"
1662                          }
1663                        )
1664                      ] })
1665                    ] }),
1666                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 justify-end pt-2 border-t border-zinc-200 dark:border-zinc-850", children: [
1667                      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => setEditingSong(null), className: "px-3 py-1.5 text-xs border border-zinc-300 dark:border-zinc-700 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 font-semibold", children: "Cancel" }),
1668                      /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: handleSaveSong, className: "px-3 py-1.5 text-xs bg-indigo-600 text-white rounded-md hover:bg-indigo-700 font-semibold", children: "Save Work" })
1669                    ] })
1670                  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 border border-zinc-200 dark:border-zinc-800 rounded-lg overflow-y-auto bg-zinc-50/50 dark:bg-zinc-950/20", children: (editingSection.songs || []).length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-zinc-400", children: [
1671                    /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { size: 28, className: "mb-1 opacity-60" }),
1672                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "No canonical works defined in this section." })
1673                  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left text-xs", children: [
1674                    /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { className: "bg-zinc-100 dark:bg-zinc-850 text-zinc-500 sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { children: [
1675                      /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 font-semibold w-8" }),
1676                      /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 font-semibold w-24", children: "Group" }),
1677                      /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 font-semibold", children: "Title" }),
1678                      /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 font-semibold w-24", children: "Attached MIDIs" }),
1679                      /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 font-semibold w-20", children: "Details" }),
1680                      /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-3 py-2 font-semibold w-16 text-right", children: "Actions" })
1681                    ] }) }),
1682                    /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-200 dark:divide-zinc-800", children: (editingSection.songs || []).map((song, index) => {
1683                      const attachedIds = (song.associatedMidiIds || []).map(String);
1684                      const isExpanded = expandedSongId === song.id;
1685                      return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
1686                        /* @__PURE__ */ jsxRuntimeExports.jsxs(
1687                          "tr",
1688                          {
1689                            draggable: true,
1690                            onDragStart: (e) => handleSongDragStart(e, index),
1691                            onDragOver: handleSongDragOver,
1692                            onDrop: (e) => handleSongDrop(e, index),
1693                            className: "bg-white dark:bg-zinc-900 hover:bg-zinc-50 dark:hover:bg-zinc-850/50 transition-colors",
1694                            children: [
1695                              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-2 py-2 text-zinc-300 hover:text-zinc-500 cursor-move", title: "Drag to reorder", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Move, { size: 13 }) }),
1696                              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-zinc-500 dark:text-zinc-400 font-medium truncate max-w-[100px]", children: song.groupName || "-" }),
1697                              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 font-bold text-zinc-800 dark:text-zinc-200 truncate max-w-[180px]", children: song.title }),
1698                              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1699                                "button",
1700                                {
1701                                  type: "button",
1702                                  onClick: () => setExpandedSongId(isExpanded ? null : song.id),
1703                                  className: `inline-flex items-center gap-1 px-2 py-0.5 rounded text-[11px] font-semibold transition-colors ${attachedIds.length > 0 ? "bg-indigo-50 dark:bg-indigo-950/60 text-indigo-600 dark:text-indigo-400 hover:bg-indigo-100" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-400 hover:bg-zinc-200"}`,
1704                                  children: [
1705                                    /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 11 }),
1706                                    " ",
1707                                    attachedIds.length,
1708                                    " ",
1709                                    attachedIds.length === 1 ? "MIDI" : "MIDIs",
1710                                    isExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 11 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 11 })
1711                                  ]
1712                                }
1713                              ) }),
1714                              /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-3 py-2 text-zinc-500 truncate max-w-[80px]", children: song.details || "-" }),
1715                              /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "px-3 py-2 text-right space-x-1 shrink-0", children: [
1716                                /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => setEditingSong(song), className: "p-1 text-zinc-400 hover:text-indigo-500 rounded bg-zinc-50 hover:bg-indigo-50 dark:bg-zinc-800 dark:hover:bg-indigo-900/30 transition-colors", title: "Edit Work", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Pen, { size: 12 }) }),
1717                                /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: (e) => handleDeleteSong(song.id, song.title, e), className: "p-1 text-zinc-400 hover:text-rose-500 rounded bg-zinc-50 hover:bg-rose-50 dark:bg-zinc-800 dark:hover:bg-rose-900/30 transition-colors", title: "Delete Work", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 12 }) })
1718                              ] })
1719                            ]
1720                          }
1721                        ),
1722                        isExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx("tr", { className: "bg-indigo-50/20 dark:bg-indigo-950/20 border-b border-indigo-100 dark:border-indigo-900/30", children: /* @__PURE__ */ jsxRuntimeExports.jsx("td", { colSpan: 6, className: "px-4 py-2.5", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs", children: [
1723                          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-1.5", children: [
1724                            /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-bold text-zinc-700 dark:text-zinc-300", children: [
1725                              'Attached MIDI Files for "',
1726                              song.title,
1727                              '":'
1728                            ] }),
1729                            /* @__PURE__ */ jsxRuntimeExports.jsx(
1730                              "button",
1731                              {
1732                                type: "button",
1733                                onClick: () => setEditingSong(song),
1734                                className: "text-[11px] text-indigo-600 dark:text-indigo-400 hover:underline font-medium",
1735                                children: "+ Manage / Attach MIDIs"
1736                              }
1737                            )
1738                          ] }),
1739                          attachedIds.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-400 italic text-[11px]", children: "No MIDI files attached to this work. Click Edit to attach MIDI IDs." }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: attachedIds.map((mId) => {
1740                            const cached = tracksCache[mId] || { title: `MIDI Track #${mId}` };
1741                            return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-1.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded", children: [
1742                              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
1743                                /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 12, className: "text-indigo-500" }),
1744                                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-zinc-800 dark:text-zinc-200", children: cached.title }),
1745                                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-zinc-400", children: [
1746                                  "(ID: #",
1747                                  mId,
1748                                  ")"
1749                                ] })
1750                              ] }),
1751                              /* @__PURE__ */ jsxRuntimeExports.jsx(
1752                                "button",
1753                                {
1754                                  type: "button",
1755                                  onClick: async () => {
1756                                    if (!selectedCollection || !(editingSection == null ? void 0 : editingSection.id)) return;
1757                                    const newMidiIds = attachedIds.filter((id) => id !== mId);
1758                                    const updated = await api.updateSectionSong(selectedCollection.id, editingSection.id, song.id, { associatedMidiIds: newMidiIds });
1759                                    setEditingSection((prev) => {
1760                                      var _a2;
1761                                      return prev ? { ...prev, songs: (_a2 = prev.songs) == null ? void 0 : _a2.map((s) => s.id === updated.id ? updated : s) } : null;
1762                                    });
1763                                    triggerNotification("MIDI detached from work");
1764                                  },
1765                                  className: "text-[10px] text-rose-500 hover:text-rose-700 font-semibold px-2 py-0.5 rounded hover:bg-rose-50 dark:hover:bg-rose-950/40",
1766                                  children: "Detach"
1767                                }
1768                              )
1769                            ] }, mId);
1770                          }) })
1771                        ] }) }) })
1772                      ] }, song.id);
1773                    }) })
1774                  ] }) }) }),
1775                  rightPanelTab === "pinned" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col overflow-hidden", children: [
1776                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto border border-zinc-150 dark:border-zinc-800 rounded-lg p-3 bg-zinc-50/50 dark:bg-zinc-950/20 mb-3", children: [
1777                      /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-[10px] font-bold text-zinc-400 uppercase tracking-wider mb-2", children: "Pinned Tracks Order (Drag to Reorder)" }),
1778                      (editingSection.curatedTrackIds || []).length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-zinc-400", children: [
1779                        /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { size: 28, className: "mb-1 opacity-60" }),
1780                        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "No pinned tracks in this section." })
1781                      ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: (editingSection.curatedTrackIds || []).map((trackId, index) => {
1782                        const cached = tracksCache[String(trackId)] || { title: `Track ID #${trackId}`, artist: "" };
1783                        return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1784                          "div",
1785                          {
1786                            draggable: true,
1787                            onDragStart: (e) => handleTrackDragStart(e, index),
1788                            onDragOver: handleTrackDragOver,
1789                            onDrop: (e) => handleTrackDrop(e, index),
1790                            className: "flex items-center justify-between p-2.5 bg-white dark:bg-zinc-900 border border-zinc-150 dark:border-zinc-800/80 rounded-lg hover:border-zinc-300 dark:hover:border-zinc-700 transition-colors shadow-sm select-none",
1791                            children: [
1792                              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5 overflow-hidden", children: [
1793                                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "cursor-move text-zinc-300 hover:text-zinc-650", title: "Drag to reorder", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Move, { size: 14 }) }),
1794                                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "overflow-hidden", children: [
1795                                  /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-bold text-zinc-850 dark:text-zinc-200 truncate", children: cached.title }),
1796                                  /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] text-zinc-400 dark:text-zinc-500 truncate", children: [
1797                                    cached.artist,
1798                                    " (ID: #",
1799                                    trackId,
1800                                    ")"
1801                                  ] })
1802                                ] })
1803                              ] }),
1804                              /* @__PURE__ */ jsxRuntimeExports.jsx(
1805                                "button",
1806                                {
1807                                  type: "button",
1808                                  onClick: () => handleUnpinTrack(trackId),
1809                                  className: "p-1 hover:text-rose-500 text-zinc-400 rounded transition-colors",
1810                                  title: "Remove from curated list",
1811                                  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { size: 13 })
1812                                }
1813                              )
1814                            ]
1815                          },
1816                          trackId
1817                        );
1818                      }) })
1819                    ] }),
1820                    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "shrink-0 flex flex-col relative pt-2 border-t border-zinc-150 dark:border-zinc-800", children: [
1821                      /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[10px] font-bold text-zinc-450 uppercase mb-1.5", children: "Add Track to Curator" }),
1822                      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex items-center bg-zinc-50 dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-800 rounded-lg px-3 py-1.5", children: [
1823                        /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { size: 14, className: "text-zinc-400 mr-2" }),
1824                        /* @__PURE__ */ jsxRuntimeExports.jsx(
1825                          "input",
1826                          {
1827                            type: "text",
1828                            className: "w-full bg-transparent border-none outline-none py-0.5 text-xs text-zinc-900 dark:text-white",
1829                            placeholder: "Search MIDI title or artist name...",
1830                            value: trackSearch,
1831                            onChange: (e) => setTrackSearch(e.target.value)
1832                          }
1833                        ),
1834                        searchingTracks && /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { size: 12, className: "animate-spin text-indigo-500" })
1835                      ] }),
1836                      trackSearch.trim() && trackResults.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-full left-0 right-0 mb-1 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg shadow-xl z-20 max-h-56 overflow-y-auto p-1.5 space-y-1", children: trackResults.map((t) => {
1837                        const isPinned = (editingSection.curatedTrackIds || []).includes(String(t.id));
1838                        return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1839                          "button",
1840                          {
1841                            type: "button",
1842                            disabled: isPinned,
1843                            onClick: () => handlePinTrack(t),
1844                            className: `w-full text-left p-2 rounded-md text-xs flex justify-between items-center transition-colors ${isPinned ? "opacity-40 cursor-not-allowed bg-zinc-50 dark:bg-zinc-850" : "hover:bg-zinc-50 dark:hover:bg-zinc-850 text-zinc-800 dark:text-zinc-150"}`,
1845                            children: [
1846                              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "overflow-hidden", children: [
1847                                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold block truncate", children: t.title }),
1848                                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-400 block truncate", children: t.artist })
1849                              ] }),
1850                              isPinned ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-zinc-200 dark:bg-zinc-750 text-zinc-500 dark:text-zinc-400 px-2 py-0.5 rounded font-semibold shrink-0", children: "Pinned" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-indigo-50 dark:bg-indigo-900/30 text-indigo-600 dark:text-indigo-400 px-2 py-0.5 rounded font-semibold shrink-0 group-hover:bg-indigo-600 group-hover:text-white", children: "Pin +" })
1851                            ]
1852                          },
1853                          t.id
1854                        );
1855                      }) })
1856                    ] })
1857                  ] })
1858                ] })
1859              ] }) })
1860            ] })
1861          ) })
1862        ] })
1863      ] })
1864    )
1865  ] });
1866}
1867export {
1868  CollectionManager as default
1869};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.