1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { T as TrackRow } from "./TrackRow-CdYYo5vh.js"; 3import { a as api } from "./index-w5lXls15.js"; 4import { A as AdSlot } from "./AdSlot-BEBlpA_I.js"; 5import { u as useTranslation } from "./vendor-i18n-BC7ZMXWQ.js"; 6import { bO as Library, de as Flame, cH as Tag, bi as Sparkles } from "./vendor-D0P4XgNj.js"; 7import "./main-aQ5jZgYp.js"; 8import "./vendor-react-three-BCww3P1x.js"; 9import "./utils-CEVb7nN1.js"; 10import "./synth-Fr-K52Ox.js"; 11import "./wasm-synth-adapter-DpNGIIyM.js"; 12import "./vendor-spessasynth-DH8n6o_V.js"; 13import "./vendor-audio-D-wTggbx.js"; 14import "./Waveform-BgnMyg5E.js"; 15import "./vendor-framer-B-4FZk1Z.js"; 16import "./labTools-HX5HYAPB.js"; 17import "./UserAvatar-BQN4jlY-.js"; 18const CategoryCard = ({ icon: Icon, label, color, gradient, href }) => /* @__PURE__ */ jsxRuntimeExports.jsxs( 19 "a", 20 { 21 href, 22 className: "group relative flex flex-col items-center justify-center p-4 bg-white dark:bg-zinc-900 rounded-2xl transition-all h-24 flex-1 min-w-[140px] border border-zinc-200 dark:border-zinc-800 shadow-sm hover:shadow-2xl hover:shadow-indigo-500/10 hover:-translate-y-1 active:scale-95 overflow-hidden flex", 23 children: [
24 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `absolute top-0 right-0 w-16 h-16 opacity-10 group-hover:opacity-20 transition-opacity bg-gradient-to-br ${gradient} rounded-full -mr-6 -mt-6 blur-xl group-hover:scale-150 duration-700` }), 25 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `p-2.5 rounded-xl mb-2 bg-zinc-50 dark:bg-zinc-800 shadow-inner group-hover:scale-110 group-hover:rotate-3 transition-all duration-300`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Icon, { size: 20, className: `${color}` }) }), 26 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-black text-[10px] uppercase tracking-wider text-zinc-500 group-hover:text-zinc-900 dark:group-hover:text-white transition-colors text-center line-clamp-1", children: label }) 27 ] 28 } 29); 30const TAG_GRADIENTS = [ 31 "from-pink-400 to-rose-600", 32 "from-purple-400 to-indigo-600", 33 "from-blue-400 to-indigo-700", 34 "from-emerald-400 to-teal-600", 35 "from-amber-400 to-amber-700", 36 "from-red-400 to-orange-600", 37 "from-indigo-400 to-purple-600", 38 "from-teal-400 to-cyan-600", 39 "from-fuchsia-400 to-purple-600", 40 "from-violet-400 to-indigo-600" 41]; 42const TAG_COLORS = [ 43 "text-pink-500", 44 "text-purple-500", 45 "text-blue-500", 46 "text-emerald-500", 47 "text-amber-500", 48 "text-red-500", 49 "text-indigo-500", 50 "text-teal-500", 51 "text-fuchsia-500", 52 "text-violet-500" 53]; 54const LazyTrackRow = ({ 55 cat, 56 currentLang, 57 onSelectTrack, 58 onToggleWatchLater, 59 watchLaterSet, 60 onAddToQueue, 61 onAddToPlaylist, 62 onCategorySelect, 63 t 64}) => { 65 var _a; 66 const [tracks, setTracks] = reactExports.useState([]); 67 const [loading, setLoading] = reactExports.useState(false); 68 const [hasLoaded, setHasLoaded] = reactExports.useState(false); 69 const containerRef = reactExports.useRef(null); 70 reactExports.useEffect(() => { 71 const observer = new IntersectionObserver( 72 (entries) => { 73 if (entries[0].isIntersecting && !hasLoaded && !loading) { 74 setLoading(true); 75 const sort = cat.key === "trending" ? "popular" : "recent"; 76 api.getTracks("", cat.key, sort, 20).then((data) => { 77 setTracks(data); 78 setHasLoaded(true); 79 }).catch((e) => { 80 console.error(`Failed to fetch ${cat.key}`, e); 81 }).finally(() => { 82 setLoading(false); 83 }); 84 } 85 }, 86 { rootMargin: "200px 0px" } 87 ); 88 if (containerRef.current) { 89 observer.observe(containerRef.current); 90 } 91 return () => { 92 observer.disconnect(); 93 }; 94 }, [cat.key, hasLoaded, loading]); 95 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: containerRef, className: "min-h-[100px]", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 96 TrackRow, 97 { 98 title: ((_a = cat.translations) == null ? void 0 : _a[currentLang]) || t(`categories.${cat.key}`, cat.label), 99 tracks, 100 loading, 101 onSelectTrack, 102 onToggleWatchLater, 103 watchLaterSet, 104 onAddToQueue, 105 onAddToPlaylist, 106 onSeeAll: () => onCategorySelect(cat.key), 107 seeAllHref: `/${cat.key}` 108 } 109 ) }); 110}; 111const ExplorePage = ({ tracks, onSelectTrack, onToggleWatchLater, watchLaterSet, onCategorySelect, onAddToQueue, onAddToPlaylist, onTagSelect }) => { 112 const { t, i18n } = useTranslation(); 113 const [categories, setCategories] = reactExports.useState([]); 114 const [randomTags, setRandomTags] = reactExports.useState([]); 115 reactExports.useEffect(() => { 116 const init = async () => { 117 try { 118 const cats = await api.getCategories(0, 100); 119 const filtered = cats.filter((c) => c.key !== "all"); 120 if (!filtered.find((c) => c.key === "trending")) { 121 filtered.unshift({ key: "trending", label: "Trending" }); 122 } 123 setCategories(filtered); 124 try { 125 const allTags = await api.getTags(); 126 const top50 = allTags.slice(0, 50); 127 const shuffled = [...top50].sort(() => 0.5 - Math.random()); 128 const selectedTags = shuffled.slice(0, 10); 129 setRandomTags(selectedTags); 130 } catch (tagErr) { 131 console.error("Failed to load tags for ExplorePage", tagErr); 132 } 133 } catch (e) { 134 console.error("Failed to load categories", e); 135 } 136 }; 137 init(); 138 }, []); 139 const currentLang = i18n.language; 140 const topRowItems = reactExports.useMemo(() => { 141 var _a; 142 const items = []; 143 items.push({ 144 key: "collections", 145 label: t("categories.collections", "Collections"), 146 icon: Library, 147 color: "text-indigo-500", 148 gradient: "from-indigo-500 to-purple-600", 149 href: "/collections" 150 }); 151 const trendingCat = categories.find((c) => c.key === "trending"); 152 items.push({ 153 key: "trending", 154 label: ((_a = trendingCat == null ? void 0 : trendingCat.translations) == null ? void 0 : _a[currentLang]) || t("categories.trending", (trendingCat == null ? void 0 : trendingCat.label) || "Trending"), 155 icon: Flame, 156 color: "text-orange-500", 157 gradient: "from-orange-500 to-red-600", 158 href: "/trending" 159 });
160 randomTags.forEach((tag, idx) => { 161 var _a2; 162 const gradient = TAG_GRADIENTS[idx % TAG_GRADIENTS.length]; 163 const color = TAG_COLORS[idx % TAG_COLORS.length]; 164 items.push({ 165 key: `tag-${tag.id}`, 166 label: ((_a2 = tag.translations) == null ? void 0 : _a2[currentLang]) || tag.name, 167 icon: Tag, 168 color, 169 gradient, 170 href: `/tag?name=${encodeURIComponent(tag.name)}` 171 }); 172 }); 173 return items; 174 }, [categories, randomTags, currentLang, t]); 175 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "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: [ 176 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col md:flex-row md:items-end justify-between gap-6 mb-12", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 177 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-indigo-500 font-black text-xs uppercase tracking-tighter mb-2", children: [
178 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14 }), 179 "Discover New Sound" 180 ] }), 181 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-4xl md:text-5xl font-black tracking-tight text-zinc-900 dark:text-white flex items-center gap-4", children: t("nav.explore", "Explore") }) 182 ] }) }), 183 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-4 overflow-x-auto pb-4 scrollbar-hide -mx-4 px-4 sm:mx-0 sm:px-0 sm:grid sm:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-11 mb-10", children: topRowItems.map((item, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 w-40 sm:w-auto animate-in fade-in slide-in-from-bottom-4", style: { animationDelay: `${i * 50}ms` }, children: /* @__PURE__ */ jsxRuntimeExports.jsx( 184 CategoryCard, 185 { 186 icon: item.icon, 187 label: item.label, 188 color: item.color, 189 gradient: item.gradient, 190 href: item.href 191 } 192 ) }, item.key)) }), 193 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-12 relative overflow-hidden rounded-3xl bg-gradient-to-r from-indigo-950 via-purple-950 to-zinc-950 border border-indigo-500/20 shadow-xl p-8 flex flex-col md:flex-row md:items-center justify-between gap-6 animate-in fade-in slide-in-from-bottom-6", children: [
194 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative z-10", children: [ 195 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-indigo-400 font-bold text-xs uppercase tracking-wider mb-2", children: [ 196 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 14, className: "animate-pulse text-indigo-400" }), 197 "Curated Showcases" 198 ] }), 199 /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-2xl md:text-3xl font-black text-white mb-2 tracking-tight", children: "Themed MIDI Collections" }), 200 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-400 text-sm max-w-xl leading-relaxed", children: "Explore dedicated showcases curated for music lovers. Dive into Broadway musicals, movie soundtracks, and classical masterpieces." }) 201 ] }), 202 /* @__PURE__ */ jsxRuntimeExports.jsx( 203 "a", 204 { 205 href: "/collections", 206 className: "relative z-10 shrink-0 self-start md:self-auto px-6 py-3 bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-sm rounded-xl transition-all shadow-lg hover:shadow-xl active:scale-95 border border-indigo-400/20", 207 children: "Browse Collections" 208 } 209 ),
210 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 right-0 w-96 h-96 opacity-20 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-full -mr-20 -mt-20 blur-3xl pointer-events-none" }) 211 ] }), 212 /* @__PURE__ */ jsxRuntimeExports.jsx(AdSlot, { type: "exploreList" }), 213 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-4", children: categories.map((cat, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "animate-in fade-in slide-in-from-bottom-8", style: { animationDelay: `${i * 100}ms` }, children: /* @__PURE__ */ jsxRuntimeExports.jsx( 214 LazyTrackRow, 215 { 216 cat, 217 currentLang, 218 onSelectTrack, 219 onToggleWatchLater, 220 watchLaterSet, 221 onAddToQueue, 222 onAddToPlaylist, 223 onCategorySelect, 224 t 225 } 226 ) }, `row-${cat.key}`)) }), 227 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-20 py-10 border-t border-zinc-200 dark:border-zinc-800 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 text-sm font-medium", children: t("explore.moreComing", "More genres and curated collections are added daily.") }) }) 228 ] }); 229}; 230export { 231 ExplorePage as default 232};
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.