PageSourceSearch

https://www.hamienet.com/assets/CollectionIndexPage-Bj-4XOh6.js

js hamienet.com collected 2026-10-02 05:00:10 UTC 39,455 bytes, 573 lines download raw bytes

1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js";
2import { a as api } from "./index-w5lXls15.js";
3import { g as getColorsFromHash, n as getOptimizedImageUrl, s as slugify } from "./utils-CEVb7nN1.js";
4import { g as getBilingualTitle, p as parseMultilingualData } from "./bilingual-CBk-npe4.js";
5import { u as useTranslation } from "./vendor-i18n-BC7ZMXWQ.js";
6import { ck as Layers, cM as RefreshCw, bi as Sparkles, bf as Search, aW as X, cA as SlidersHorizontal, cw as LayoutGrid, cx as List, bg as Music, bC as Gamepad2, dX as Film, de as Flame, cI as Disc, bn as Globe, e9 as Radio, cB as ChevronUp, cC as ChevronDown, bO as Library, bb as User, cn as ArrowRight, dO as Calendar } from "./vendor-D0P4XgNj.js";
7const CATEGORIES = [
8  { id: "all", labelKey: "collections.allCategories", defaultLabel: "All", icon: Sparkles },
9  { id: "classical", labelKey: "collections.catClassical", defaultLabel: "Classical & Masters", icon: Music },
10  { id: "games_anime", labelKey: "collections.catGamesAnime", defaultLabel: "Video Games & Anime", icon: Gamepad2 },
11  { id: "stage_cinema", labelKey: "collections.catStageScreen", defaultLabel: "Stage & Screen", icon: Film },
12  { id: "pop_rock", labelKey: "collections.catPopRock", defaultLabel: "Pop, Rock & Metal", icon: Flame },
13  { id: "jazz_blues", labelKey: "collections.catJazzBlues", defaultLabel: "Jazz, Blues & Soul", icon: Disc },
14  { id: "folk_world", labelKey: "collections.catFolkWorld", defaultLabel: "Folk & Traditional", icon: Globe },
15  { id: "electronic", labelKey: "collections.catElectronic", defaultLabel: "Electronic & Dance", icon: Radio }
16];
17function getCollectionCategory(c) {
18  const slug = (c.slug || "").toLowerCase();
19  const title = (c.title || "").toLowerCase();
20  const combined = `${slug} ${title}`;
21  if (/classical|baroque|chopin|bach|mozart|beethoven|cello|flute|woodwind|organ|violin|etude|exam-piece|encore|urtext|fugue|counterpoint/.test(combined)) {
22    return "classical";
23  }
24  if (/game|touhou|ghibli|anime|visual-novel|demoscene|tracker/.test(combined)) {
25    return "games_anime";
26  }
27  if (/broadway|musical|movie|soundtrack|disney|trailer|zimmer|morricone|tv-show|barbershop|circus|vaudeville/.test(combined)) {
28    return "stage_cinema";
29  }
30  if (/beatles|pop|rock|metal|disco|grunge|punk|ballad|k-pop|j-pop/.test(combined)) {
31    return "pop_rock";
32  }
33  if (/jazz|blues|swing|bebop|soul|motown|ragtime|bossa|r&b/.test(combined)) {
34    return "jazz_blues";
35  }
36  if (/electronic|dance|dubstep|synthwave|retrowave|trance|techno|house|lo-fi|edm|ambient|drone|chillout/.test(combined)) {
37    return "electronic";
38  }
39  return "folk_world";
40}
41const CollectionIndexPage = ({ onNavigate }) => {
42  const { t, i18n } = useTranslation();
43  const [collections, setCollections] = reactExports.useState([]);
44  const [isLoading, setIsLoading] = reactExports.useState(true);
45  const [error, setError] = reactExports.useState(null);
46  const [imageErrors, setImageErrors] = reactExports.useState({});
47  const [searchQuery, setSearchQuery] = reactExports.useState("");
48  const [debouncedQuery, setDebouncedQuery] = reactExports.useState("");
49  const [selectedCategory, setSelectedCategory] = reactExports.useState("all");
50  const [sortBy, setSortBy] = reactExports.useState("curated");
51  const [viewMode, setViewMode] = reactExports.useState(() => {
52    return typeof window !== "undefined" && localStorage.getItem("hamie_collection_index_view_mode") || "grid";
53  });
54  const [matchingSections, setMatchingSections] = reactExports.useState([]);
55  const [isSearchingSections, setIsSearchingSections] = reactExports.useState(false);
56  const [showAllSections, setShowAllSections] = reactExports.useState(false);
57  const searchInputRef = reactExports.useRef(null);
58  reactExports.useEffect(() => {
59    if (typeof window !== "undefined") {
60      localStorage.setItem("hamie_collection_index_view_mode", viewMode);
61    }
62  }, [viewMode]);
63  reactExports.useEffect(() => {
64    const handleKeyDown = (e) => {
65      var _a, _b;
66      if (e.key === "/" && document.activeElement !== searchInputRef.current && !(document.activeElement instanceof HTMLInputElement || document.activeElement instanceof HTMLTextAreaElement)) {
67        e.preventDefault();
68        (_a = searchInputRef.current) == null ? void 0 : _a.focus();
69      } else if (e.key === "Escape" && document.activeElement === searchInputRef.current) {
70        setSearchQuery("");
71        (_b = searchInputRef.current) == null ? void 0 : _b.blur();
72      }
73    };
74    window.addEventListener("keydown", handleKeyDown);
75    return () => window.removeEventListener("keydown", handleKeyDown);
76  }, []);
77  reactExports.useEffect(() => {
78    const timer = setTimeout(() => {
79      setDebouncedQuery(searchQuery.trim());
80    }, 250);
81    return () => clearTimeout(timer);
82  }, [searchQuery]);
83  reactExports.useEffect(() => {
84    if (debouncedQuery.length < 2) {
85      setMatchingSections([]);
86      setIsSearchingSections(false);
87      return;
88    }
89    let isMounted = true;
90    setIsSearchingSections(true);
91    api.searchCollectionSections(debouncedQuery, 24).then((res) => {
92      if (isMounted) {
93        setMatchingSections(res);
94        setIsSearchingSections(false);
95      }
96    }).catch(() => {
97      if (isMounted) {
98        setMatchingSections([]);
99        setIsSearchingSections(false);
100      }
101    });
102    return () => {
103      isMounted = false;
104    };
105  }, [debouncedQuery]);
106  reactExports.useEffect(() => {
107    document.title = t("collections.pageTitle", "Browse Curated MIDI Collections | HamieNET");
108    const metaDesc = document.querySelector('meta[name="description"]');
109    if (metaDesc) {
110      metaDesc.setAttribute(
111        "content",
112        "Explore hand-crafted collections of MIDI files organized by theme, composer, style, and musical eras. Discover lists like Broadway Musicals, Classical Masters, and more."
113      );
114    }
115    const fetchCollections = async () => {
116      try {
117        setIsLoading(true);
118        const data = await api.getCollections();
119        setCollections(data);
120        setError(null);
121      } catch (err) {
122        console.error("[CollectionIndexPage Load Error]", err);
123        const errMsg = err instanceof Error ? err.message : "Failed to load collections. Please try again later.";
124        setError(errMsg);
125      } finally {
126        setIsLoading(false);
127      }
128    };
129    fetchCollections();
130  }, [t]);
131  const categoryCounts = reactExports.useMemo(() => {
132    const counts = { all: collections.length };
133    for (const c of collections) {
134      const cat = getCollectionCategory(c);
135      counts[cat] = (counts[cat] || 0) + 1;
136    }
137    return counts;
138  }, [collections]);
139  const filteredCollections = reactExports.useMemo(() => {
140    let result = [...collections];
141    if (selectedCategory !== "all") {
142      result = result.filter((c) => getCollectionCategory(c) === selectedCategory);
143    }
144    const q = searchQuery.trim().toLowerCase();
145    if (q) {
146      result = result.filter((c) => {
147        const titleMatch = c.title.toLowerCase().includes(q);
148        const slugMatch = (c.slug || "").toLowerCase().includes(q);
149        const subMatch = c.subtitle && c.subtitle.toLowerCase().includes(q);
150        const descMatch = c.description && c.description.toLowerCase().includes(q);
151        return titleMatch || slugMatch || subMatch || descMatch;
152      });
153    }
154    if (sortBy === "name_asc") {
155      result.sort((a, b) => a.title.localeCompare(b.title));
156    } else if (sortBy === "name_desc") {
157      result.sort((a, b) => b.title.localeCompare(a.title));
158    } else if (sortBy === "sections_desc") {
159      result.sort((a, b) => (b.sectionCount || 0) - (a.sectionCount || 0));
160    } else if (sortBy === "newest") {
161      result.sort((a, b) => new Date(b.createdAt || 0).getTime() - new Date(a.createdAt || 0).getTime());
162    } else {
163      result.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
164    }
165    return result;
166  }, [collections, selectedCategory, searchQuery, sortBy]);
167  const hasActiveFilters = searchQuery.trim().length > 0 || selectedCategory !== "all";
168  const handleClearFilters = () => {
169    setSearchQuery("");
170    setSelectedCategory("all");
171    setSortBy("curated");
172  };
173  if (isLoading) {
174    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center justify-center min-h-[60vh] bg-zinc-50 dark:bg-[#0a0a0a]", children: [
175      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mb-4" }),
176      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm font-semibold animate-pulse", children: t("collections.loading", "Loading themed collections...") })
177    ] });
178  }
179  if (error) {
180    return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-md mx-auto my-20 p-8 rounded-2xl bg-red-50 dark:bg-red-950/20 border border-red-200 dark:border-red-900/30 text-center animate-in fade-in", children: [
181      /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { className: "mx-auto text-red-500 dark:text-red-400 mb-4", size: 40 }),
182      /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-red-800 dark:text-red-300 mb-2", children: t("collections.errorTitle", "Error Loading Collections") }),
183      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-red-600 dark:text-red-400 mb-6", children: error }),
184      /* @__PURE__ */ jsxRuntimeExports.jsxs(
185        "button",
186        {
187          onClick: () => window.location.reload(),
188          className: "inline-flex items-center gap-2 bg-red-600 hover:bg-red-700 text-white font-bold text-sm px-6 py-2.5 rounded-xl transition shadow-lg shadow-red-600/20",
189          children: [
190            /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 }),
191            t("collections.retry", "Retry")
192          ]
193        }
194      )
195    ] });
196  }
197  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4 sm:p-6 md:p-10 max-w-[2000px] mx-auto animate-in fade-in duration-700 bg-zinc-50 dark:bg-[#0a0a0a] min-h-full", children: [
198    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-4xl mb-8", children: [
199      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center gap-2 text-indigo-500 font-black text-xs uppercase tracking-widest mb-3 bg-indigo-50 dark:bg-indigo-950/30 px-3 py-1 rounded-full border border-indigo-100 dark:border-indigo-900/40", children: [
200        /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14 }),
201        t("collections.curatedSoundscapes", "Curated Soundscapes")
202      ] }),
203      /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-3xl sm:text-4xl md:text-5xl font-black tracking-tight text-zinc-900 dark:text-white mb-4", children: t("collections.browseCollections", "Browse Collections") }),
204      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-600 dark:text-zinc-400 text-base md:text-lg font-medium leading-relaxed", children: t(
205        "collections.browseDescription",
206        "Discover hand-crafted selections of MIDI tracks grouped by themes, stage shows, composers, and styles. Each collection is carefully arranged to guide you through different musical journeys."
207      ) })
208    ] }),
209    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6 space-y-4", children: [
210      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row gap-3 items-stretch md:items-center justify-between", children: [
211        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1 max-w-2xl", children: [
212          /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 text-zinc-400", size: 18 }),
213          /* @__PURE__ */ jsxRuntimeExports.jsx(
214            "input",
215            {
216              ref: searchInputRef,
217              type: "text",
218              value: searchQuery,
219              onChange: (e) => setSearchQuery(e.target.value),
220              placeholder: t("collections.searchPlaceholder", "Search collections, sections, themes, or composers..."),
221              className: "w-full pl-11 pr-24 py-3 rounded-2xl bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 text-sm font-semibold text-zinc-900 dark:text-white placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 shadow-sm transition"
222            }
223          ),
224          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1.5", children: searchQuery ? /* @__PURE__ */ jsxRuntimeExports.jsx(
225            "button",
226            {
227              onClick: () => setSearchQuery(""),
228              className: "p-1 rounded-lg text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-200 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition",
229              title: t("collections.clearSearch", "Clear Search"),
230              children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 16 })
231            }
232          ) : /* @__PURE__ */ jsxRuntimeExports.jsx("kbd", { className: "hidden sm:inline-flex items-center gap-0.5 text-[10px] font-bold text-zinc-400 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 px-1.5 py-0.5 rounded shadow-sm", children: "/" }) })
233        ] }),
234        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 self-end md:self-auto shrink-0", children: [
235          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl px-3 py-1.5 shadow-sm", children: [
236            /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersHorizontal, { size: 14, className: "text-zinc-400 shrink-0" }),
237            /* @__PURE__ */ jsxRuntimeExports.jsxs(
238              "select",
239              {
240                value: sortBy,
241                onChange: (e) => setSortBy(e.target.value),
242                "aria-label": t("collections.sortBy", "Sort By"),
243                className: "text-xs font-bold text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900 border-none focus:outline-none cursor-pointer py-1",
244                children: [
245                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "curated", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900", children: t("collections.sortCurated", "Curated Order") }),
246                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "name_asc", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900", children: t("collections.sortNameAsc", "Title (A–Z)") }),
247                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "name_desc", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900", children: t("collections.sortNameDesc", "Title (Z–A)") }),
248                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "sections_desc", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900", children: t("collections.sortSections", "Most Sections") }),
249                  /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "newest", className: "text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-900", children: t("collections.sortNewest", "Recently Added") })
250                ]
251              }
252            )
253          ] }),
254          /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl p-1 shadow-sm", children: [
255            /* @__PURE__ */ jsxRuntimeExports.jsx(
256              "button",
257              {
258                onClick: () => setViewMode("grid"),
259                className: `p-1.5 rounded-xl transition ${viewMode === "grid" ? "bg-indigo-600 text-white shadow-sm" : "text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200"}`,
260                title: "Grid View",
261                children: /* @__PURE__ */ jsxRuntimeExports.jsx(LayoutGrid, { size: 16 })
262              }
263            ),
264            /* @__PURE__ */ jsxRuntimeExports.jsx(
265              "button",
266              {
267                onClick: () => setViewMode("list"),
268                className: `p-1.5 rounded-xl transition ${viewMode === "list" ? "bg-indigo-600 text-white shadow-sm" : "text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200"}`,
269                title: "List View",
270                children: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 16 })
271              }
272            )
273          ] })
274        ] })
275      ] }),
276      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2 overflow-x-auto no-scrollbar py-1", children: CATEGORIES.map((cat) => {
277        const Icon = cat.icon;
278        const count = categoryCounts[cat.id] || 0;
279        const isSelected = selectedCategory === cat.id;
280        return /* @__PURE__ */ jsxRuntimeExports.jsxs(
281          "button",
282          {
283            onClick: () => setSelectedCategory(cat.id),
284            className: `inline-flex items-center gap-2 px-3.5 py-1.5 rounded-xl text-xs font-bold whitespace-nowrap transition-all duration-200 shrink-0 ${isSelected ? "bg-indigo-600 text-white shadow-md shadow-indigo-600/20" : "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 text-zinc-600 dark:text-zinc-400 hover:border-zinc-300 dark:hover:border-zinc-700 hover:text-zinc-900 dark:hover:text-white shadow-sm"}`,
285            children: [
286              /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { size: 14, className: isSelected ? "text-white" : "text-zinc-400" }),
287              /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t(cat.labelKey, cat.defaultLabel) }),
288              /* @__PURE__ */ jsxRuntimeExports.jsx(
289                "span",
290                {
291                  className: `text-[10px] font-black px-1.5 py-0.5 rounded-md ${isSelected ? "bg-indigo-700/60 text-white" : "bg-zinc-100 dark:bg-zinc-800 text-zinc-500"}`,
292                  children: count
293                }
294              )
295            ]
296          },
297          cat.id
298        );
299      }) })
300    ] }),
301    debouncedQuery.length >= 2 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-10 animate-in fade-in slide-in-from-top-4 duration-500", children: [
302      /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
303        /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [
304          /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-2 rounded-xl bg-indigo-50 dark:bg-indigo-950/40 text-indigo-600 dark:text-indigo-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 18 }) }),
305          /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-xl font-black text-zinc-900 dark:text-white", children: t("collections.matchingSections", "Matching Sections") }),
306          isSearchingSections ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin ml-1" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-black px-2.5 py-0.5 rounded-full bg-indigo-100 dark:bg-indigo-950/60 text-indigo-700 dark:text-indigo-300 border border-indigo-200 dark:border-indigo-800/50", children: matchingSections.length })
307        ] }),
308        matchingSections.length > 6 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
309          "button",
310          {
311            onClick: () => setShowAllSections(!showAllSections),
312            className: "text-xs font-bold text-indigo-600 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300 flex items-center gap-1 transition",
313            children: [
314              showAllSections ? t("collections.showLessSections", "Show fewer sections") : t("collections.viewAllSections", "View all {{count}} sections", { count: matchingSections.length }),
315              showAllSections ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 })
316            ]
317          }
318        )
319      ] }),
320      matchingSections.length === 0 && !isSearchingSections ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-6 rounded-2xl border border-zinc-200 dark:border-zinc-800/60 bg-white/50 dark:bg-zinc-900/30 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400", children: t("collections.noSectionsMatch", 'No individual sections directly matched "{{query}}".', { query: debouncedQuery }) }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-4", children: (showAllSections ? matchingSections : matchingSections.slice(0, 6)).map((sec) => {
321        const bilingual = getBilingualTitle(sec.title, sec.subtitle, i18n.language);
322        const colBilingual = getBilingualTitle(sec.collection.title, sec.collection.subtitle, i18n.language);
323        const colors = getColorsFromHash(sec.title + sec.id);
324        return /* @__PURE__ */ jsxRuntimeExports.jsxs(
325          "a",
326          {
327            href: sec.url,
328            onClick: (e) => {
329              if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) {
330                e.preventDefault();
331                const secSlug = sec.url.split("/").pop() || `${slugify(sec.title)}-${sec.id}`;
332                onNavigate("collection-section", `${sec.collection.slug}:${secSlug}`);
333              }
334            },
335            className: "group relative flex flex-col rounded-2xl overflow-hidden bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800/80 shadow-sm hover:shadow-xl hover:shadow-indigo-500/10 hover:border-indigo-500/40 transition-all duration-300 hover:-translate-y-1 p-3 no-underline text-inherit",
336            title: bilingual.hoverTooltip,
337            children: [
338              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-28 w-full rounded-xl overflow-hidden shrink-0 select-none mb-3", children: [
339                sec.bannerImage ? /* @__PURE__ */ jsxRuntimeExports.jsx(
340                  "img",
341                  {
342                    src: getOptimizedImageUrl(sec.bannerImage, "card"),
343                    alt: bilingual.altText,
344                    className: "w-full h-full object-cover transition-transform duration-500 group-hover:scale-105",
345                    loading: "lazy"
346                  }
347                ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
348                  "div",
349                  {
350                    className: "w-full h-full transition-transform duration-500 group-hover:scale-105",
351                    style: { background: colors.gradient }
352                  }
353                ),
354                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent" }),
355                /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "absolute top-2 left-2 max-w-[85%] truncate bg-black/60 backdrop-blur-md text-white font-extrabold text-[9px] uppercase tracking-wider px-2 py-0.5 rounded-md border border-white/10 flex items-center gap-1", children: [
356                  /* @__PURE__ */ jsxRuntimeExports.jsx(Library, { size: 10, className: "shrink-0" }),
357                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: colBilingual.mainTitle })
358                ] })
359              ] }),
360              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex flex-col justify-between", children: [
361                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
362                  /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-extrabold text-zinc-900 dark:text-white line-clamp-2 leading-tight group-hover:text-indigo-500 transition-colors", children: bilingual.mainTitle }),
363                  bilingual.subTitle && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] font-semibold text-zinc-400 dark:text-zinc-500 truncate mt-0.5", children: bilingual.subTitle }),
364                  sec.composer && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[10px] font-bold text-indigo-500 dark:text-indigo-400 mt-1 flex items-center gap-1 truncate", children: [
365                    /* @__PURE__ */ jsxRuntimeExports.jsx(User, { size: 10, className: "shrink-0" }),
366                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: sec.composer })
367                  ] })
368                ] }),
369                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pt-2.5 mt-2 border-t border-zinc-100 dark:border-zinc-800/80 flex items-center justify-between text-[11px] font-black text-indigo-500", children: [
370                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("collections.explore", "Explore") }),
371                  /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { size: 12, className: "transform transition-transform duration-200 group-hover:translate-x-1" })
372                ] })
373              ] })
374            ]
375          },
376          sec.id
377        );
378      }) })
379    ] }),
380    /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between mb-6", children: [
381      /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-xl font-black text-zinc-900 dark:text-white flex items-center gap-2", children: [
382        /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("collections.matchingCollections", "Collections") }),
383        /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold text-zinc-400", children: [
384          "(",
385          t("collections.showingResults", "Showing {{shown}} of {{total}}", {
386            shown: filteredCollections.length,
387            total: collections.length
388          }),
389          ")"
390        ] })
391      ] }),
392      hasActiveFilters && /* @__PURE__ */ jsxRuntimeExports.jsx(
393        "button",
394        {
395          onClick: handleClearFilters,
396          className: "text-xs font-bold text-indigo-600 dark:text-indigo-400 hover:underline",
397          children: t("collections.clearFilters", "Reset Filters")
398        }
399      )
400    ] }),
401    filteredCollections.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-12 text-center rounded-3xl border-2 border-dashed border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900/30 max-w-xl mx-auto my-12 animate-in fade-in", children: [
402      /* @__PURE__ */ jsxRuntimeExports.jsx(Library, { className: "mx-auto text-zinc-400 dark:text-zinc-600 mb-4", size: 48 }),
403      /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-zinc-700 dark:text-zinc-300 mb-1", children: t("collections.noResults", "No collections or sections found") }),
404      /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 dark:text-zinc-400 mb-6", children: t(
405        "collections.noResultsDesc",
406        "Try searching with a different title, composer, or keyword, or reset your filters."
407      ) }),
408      /* @__PURE__ */ jsxRuntimeExports.jsxs(
409        "button",
410        {
411          onClick: handleClearFilters,
412          className: "inline-flex items-center gap-2 bg-indigo-600 hover:bg-indigo-700 text-white font-bold text-sm px-6 py-2.5 rounded-xl transition shadow-lg shadow-indigo-600/20",
413          children: [
414            /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 14 }),
415            t("collections.clearFilters", "Reset Filters")
416          ]
417        }
418      )
419    ] }) : viewMode === "grid" ? (
420      /* Grid View */
421      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8", children: filteredCollections.map((collection, idx) => {
422        const colors = getColorsFromHash(collection.title + collection.slug);
423        const formattedDate = collection.createdAt ? new Date(collection.createdAt).toLocaleDateString(void 0, {
424          year: "numeric",
425          month: "short"
426        }) : null;
427        const bilingual = getBilingualTitle(collection.title, collection.subtitle, i18n.language);
428        const descDict = parseMultilingualData(collection.description);
429        const lang = (i18n.language || "en").toLowerCase();
430        const langPrefix = lang.split("-")[0];
431        const localizedDescription = descDict && (descDict[lang] || descDict[langPrefix] || descDict.en || Object.values(descDict)[0]) || (typeof collection.description === "string" && !collection.description.trim().startsWith("{") && !collection.description.trim().startsWith("[") ? collection.description : null);
432        return /* @__PURE__ */ jsxRuntimeExports.jsxs(
433          "a",
434          {
435            href: `/collections/${collection.slug}`,
436            onClick: (e) => {
437              if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) {
438                e.preventDefault();
439                onNavigate("collection", collection.slug);
440              }
441            },
442            className: "group relative rounded-3xl overflow-hidden bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800/80 shadow-md hover:shadow-2xl hover:shadow-indigo-500/10 transition-all duration-500 hover:-translate-y-2 cursor-pointer flex flex-col h-[400px] animate-in fade-in slide-in-from-bottom-8 no-underline text-inherit",
443            style: { animationDelay: `${Math.min(idx * 40, 400)}ms` },
444            title: bilingual.hoverTooltip,
445            children: [
446              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative h-48 w-full overflow-hidden shrink-0 select-none", children: [
447                collection.heroImage && !imageErrors[collection.id] ? /* @__PURE__ */ jsxRuntimeExports.jsx(
448                  "img",
449                  {
450                    src: getOptimizedImageUrl(collection.heroImage, "card"),
451                    alt: bilingual.altText,
452                    title: bilingual.hoverTooltip,
453                    className: "w-full h-full object-cover transition-transform duration-700 group-hover:scale-110",
454                    loading: "lazy",
455                    onError: () => setImageErrors((prev) => ({ ...prev, [collection.id]: true }))
456                  }
457                ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
458                  "div",
459                  {
460                    className: "w-full h-full transition-transform duration-700 group-hover:scale-110",
461                    style: { background: colors.gradient },
462                    title: bilingual.hoverTooltip
463                  }
464                ),
465                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-gradient-to-t from-black/80 via-black/30 to-transparent" }),
466                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-4 right-4 flex items-center gap-1.5", children: [
467                  bilingual.nativeBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-emerald-600/90 text-white font-black text-[10px] tracking-wider px-2 py-1 rounded-lg backdrop-blur-sm shadow border border-emerald-400/30", children: bilingual.nativeBadge }),
468                  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-indigo-600/90 text-white font-black text-[10px] uppercase tracking-wider px-3 py-1 rounded-xl backdrop-blur-sm shadow-lg border border-indigo-400/20", children: collection.sectionCount === 1 ? t("collections.sectionCount_one", "1 Section", { count: 1 }) : t("collections.sectionCount_other", "{{count}} Sections", {
469                    count: collection.sectionCount || 0
470                  }) })
471                ] })
472              ] }),
473              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 flex-1 flex flex-col justify-between", children: [
474                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
475                  /* @__PURE__ */ jsxRuntimeExports.jsx(
476                    "h3",
477                    {
478                      className: "text-xl font-extrabold text-zinc-900 dark:text-white line-clamp-2 group-hover:text-indigo-500 transition-colors leading-tight",
479                      title: bilingual.hoverTooltip,
480                      children: bilingual.mainTitle
481                    }
482                  ),
483                  bilingual.subTitle && /* @__PURE__ */ jsxRuntimeExports.jsx(
484                    "p",
485                    {
486                      className: "text-zinc-500 dark:text-zinc-400 text-xs font-semibold uppercase tracking-wider line-clamp-1",
487                      title: bilingual.hoverTooltip,
488                      children: bilingual.subTitle
489                    }
490                  ),
491                  localizedDescription && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400 text-sm line-clamp-2 leading-relaxed pt-1", children: localizedDescription })
492                ] }),
493                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between pt-4 border-t border-zinc-100 dark:border-zinc-800/80 mt-4 shrink-0", children: [
494                  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-1 text-[11px] text-zinc-400 dark:text-zinc-500 font-bold uppercase", children: formattedDate && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
495                    /* @__PURE__ */ jsxRuntimeExports.jsx(Calendar, { size: 12 }),
496                    /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formattedDate })
497                  ] }) }),
498                  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-[11px] font-black uppercase tracking-widest text-indigo-500 group-hover:text-indigo-400 transition-colors", children: [
499                    t("collections.explore", "Explore"),
500                    /* @__PURE__ */ jsxRuntimeExports.jsx(
501                      ArrowRight,
502                      {
503                        size: 12,
504                        className: "transform transition-transform duration-300 group-hover:translate-x-1"
505                      }
506                    )
507                  ] })
508                ] })
509              ] })
510            ]
511          },
512          collection.id
513        );
514      }) })
515    ) : (
516      /* Compact List View */
517      /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: filteredCollections.map((collection, idx) => {
518        const colors = getColorsFromHash(collection.title + collection.slug);
519        const bilingual = getBilingualTitle(collection.title, collection.subtitle, i18n.language);
520        const descDict = parseMultilingualData(collection.description);
521        const lang = (i18n.language || "en").toLowerCase();
522        const langPrefix = lang.split("-")[0];
523        const localizedDescription = descDict && (descDict[lang] || descDict[langPrefix] || descDict.en || Object.values(descDict)[0]) || (typeof collection.description === "string" && !collection.description.trim().startsWith("{") && !collection.description.trim().startsWith("[") ? collection.description : null);
524        return /* @__PURE__ */ jsxRuntimeExports.jsxs(
525          "a",
526          {
527            href: `/collections/${collection.slug}`,
528            onClick: (e) => {
529              if (!e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) {
530                e.preventDefault();
531                onNavigate("collection", collection.slug);
532              }
533            },
534            className: "group flex items-center justify-between p-4 rounded-2xl bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800/80 shadow-sm hover:shadow-lg hover:border-indigo-500/40 transition-all duration-300 no-underline text-inherit",
535            style: { animationDelay: `${Math.min(idx * 20, 300)}ms` },
536            children: [
537              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 min-w-0", children: [
538                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "relative w-16 h-16 sm:w-20 sm:h-20 rounded-xl overflow-hidden shrink-0 select-none", children: collection.heroImage && !imageErrors[collection.id] ? /* @__PURE__ */ jsxRuntimeExports.jsx(
539                  "img",
540                  {
541                    src: getOptimizedImageUrl(collection.heroImage, "card"),
542                    alt: bilingual.altText,
543                    className: "w-full h-full object-cover group-hover:scale-105 transition-transform duration-300",
544                    loading: "lazy",
545                    onError: () => setImageErrors((prev) => ({ ...prev, [collection.id]: true }))
546                  }
547                ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full h-full", style: { background: colors.gradient } }) }),
548                /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
549                  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap mb-1", children: [
550                    /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-base sm:text-lg font-extrabold text-zinc-900 dark:text-white truncate group-hover:text-indigo-500 transition-colors", children: bilingual.mainTitle }),
551                    bilingual.nativeBadge && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-emerald-600/90 text-white font-black text-[9px] px-1.5 py-0.5 rounded", children: bilingual.nativeBadge })
552                  ] }),
553                  bilingual.subTitle && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-bold text-zinc-400 truncate mb-1", children: bilingual.subTitle }),
554                  localizedDescription && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500 dark:text-zinc-400 line-clamp-1 hidden sm:block", children: localizedDescription })
555                ] })
556              ] }),
557              /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 shrink-0 pl-4", children: [
558                /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-bold px-3 py-1 rounded-xl bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-300", children: collection.sectionCount === 1 ? t("collections.sectionCount_one", "1 Section", { count: 1 }) : t("collections.sectionCount_other", "{{count}} Sections", {
559                  count: collection.sectionCount || 0
560                }) }),
561                /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-8 h-8 rounded-full bg-indigo-50 dark:bg-indigo-950/40 text-indigo-600 dark:text-indigo-400 flex items-center justify-center group-hover:bg-indigo-600 group-hover:text-white transition", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRight, { size: 14, className: "transform transition-transform duration-200 group-hover:translate-x-0.5" }) })
562              ] })
563            ]
564          },
565          collection.id
566        );
567      }) })
568    )
569  ] });
570};
571export {
572  CollectionIndexPage as default
573};

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.