1import { r as reactExports, j as jsxRuntimeExports, R as React } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-D0Bmh-UX.js"; 3import { V as VideoCard } from "./main-CznOH3wZ.js"; 4import { u as useShouldShowAd, A as AdSlot } from "./AdSlot-CPW4Sb2o.js"; 5import { u as useTranslation } from "./vendor-i18n-R-bMoYp_.js"; 6import { cz as SlidersHorizontal, c3 as RotateCcw, bS as Mic, cA as ChevronUp, cB as ChevronDown, bg as Music, cj as Layers, cC as Drum, bi as Sparkles, bT as Volume2, cn as FileMusic, bJ as PanelsTopLeft, cD as Headphones, cE as Key, bn as Globe, bF as CircleQuestionMark, bf as Search, c8 as ArrowUpDown, cv as LayoutGrid, cw as List, bA as Eye, aW as X } from "./vendor-dVNaLJx_.js"; 7import "./vendor-react-three-MDLA5jog.js"; 8import "./utils-BYCVoSTN.js"; 9import "./synth-BE1TSTD4.js"; 10import "./wasm-synth-adapter-BrqgBX1y.js"; 11import "./vendor-spessasynth-DH8n6o_V.js"; 12import "./vendor-audio-DH3w0BzW.js"; 13import "./Waveform-MxtdejK7.js"; 14import "./vendor-framer-C8eEgkdL.js"; 15import "./labTools-BATZiaPB.js"; 16import "./UserAvatar-BQN4jlY-.js"; 17const NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; 18const getNoteName = (midiNumber) => { 19 const octave = Math.floor(midiNumber / 12) - 1; 20 const noteIndex = midiNumber % 12; 21 return `${NOTE_NAMES[noteIndex]}${octave}`; 22}; 23const COMMON_INSTRUMENTS = [ 24 { name: "Piano", program: 0 }, 25 { name: "Bright Piano", program: 1 }, 26 { name: "Electric Piano", program: 4 }, 27 { name: "Harpsichord", program: 6 }, 28 { name: "Celesta", program: 8 }, 29 { name: "Organ", program: 16 }, 30 { name: "Accordion", program: 21 }, 31 { name: "Nylon Guitar", program: 24 }, 32 { name: "Steel Guitar", program: 25 }, 33 { name: "Electric Guitar", program: 27 }, 34 { name: "Acoustic Bass", program: 32 }, 35 { name: "Electric Bass", program: 33 }, 36 { name: "Violin", program: 40 }, 37 { name: "Cello", program: 42 }, 38 { name: "Harp", program: 46 }, 39 { name: "Timpani", program: 47 }, 40 { name: "String Ensemble", program: 48 }, 41 { name: "Trumpet", program: 56 }, 42 { name: "Trombone", program: 57 }, 43 { name: "Tuba", program: 58 }, 44 { name: "Soprano Sax", program: 64 }, 45 { name: "Alto Sax", program: 65 }, 46 { name: "Tenor Sax", program: 66 }, 47 { name: "Oboe", program: 68 }, 48 { name: "Bassoon", program: 70 }, 49 { name: "Clarinet", program: 71 }, 50 { name: "Flute", program: 73 }, 51 { name: "Recorder", program: 74 }, 52 { name: "Synth Lead", program: 80 }, 53 { name: "Synth Pad", program: 88 }, 54 { name: "Sitar", program: 104 }, 55 { name: "Banjo", program: 105 }, 56 { name: "Steel Drums", program: 114 } 57]; 58const GENRES = ["Classical", "Jazz", "Pop", "Rock", "Electronic", "Soundtrack", "Game Music", "Folk", "Hip Hop", "R&B", "Anime"]; 59const MOODS = ["Energetic", "Calm", "Melancholy", "Epic", "Happy", "Dark", "Romantic", "Chill", "Dreamy", "Mysterious"]; 60const KEYS = [ 61 "C major", 62 "C minor", 63 "G major", 64 "G minor", 65 "D major", 66 "D minor", 67 "A major", 68 "A minor", 69 "E major", 70 "E minor", 71 "B major", 72 "B minor", 73 "F major", 74 "F minor", 75 "B- major", 76 "B- minor", 77 "E- major", 78 "E- minor", 79 "A- major", 80 "D- major", 81 "F# minor", 82 "C# minor" 83]; 84const TIME_SIGNATURES = ["4/4", "3/4", "2/4", "6/8", "12/8", "5/4"]; 85const DIFFICULTIES = ["Beginner", "Intermediate", "Advanced", "Virtuoso"]; 86const AdvancedSearchPage = ({ 87 searchQuery, 88 onSelectTrack, 89 onToggleWatchLater, 90 watchLaterSet, 91 onAddToQueue, 92 onAddToPlaylist, 93 theme = "dark" 94}) => { 95 const { t } = useTranslation(); 96 const shouldShowExploreAd = useShouldShowAd("exploreList"); 97 const [viewMode, setViewMode] = reactExports.useState(() => { 98 return localStorage.getItem("hamie_view_mode") || "grid"; 99 }); 100 const [expanded, setExpanded] = reactExports.useState({ 101 music: true, 102 instruments: true, 103 complexity: true, 104 classification: true, 105 community: false, 106 origin: false, 107 melody: false 108 }); 109 const toggleSection = (section) => { 110 setExpanded((prev) => ({ ...prev, [section]: !prev[section] })); 111 }; 112 const getInitialFilters = () => { 113 const params = new URLSearchParams(window.location.search); 114 return { 115 q: params.get("q") || "", 116 tempo_min: params.get("tempo_min") || "", 117 tempo_max: params.get("tempo_max") || "", 118 key: params.getAll("key"), 119 time_sig: params.getAll("time_sig"), 120 difficulty: params.getAll("difficulty"), 121 instruments: params.getAll("instruments"), 122 has_drums: params.get("has_drums") === "true", 123 has_lyrics: params.get("has_lyrics") === "true", 124 has_sheets: params.get("has_sheets") === "true", 125 has_stems: params.get("has_stems") === "true", 126 has_analysis: params.get("has_analysis") === "true", 127 duration_min: params.get("duration_min") || "", 128 duration_max: params.get("duration_max") || "", 129 note_count_min: params.get("note_count_min") || "", 130 note_count_max: params.get("note_count_max") || "", 131 track_count_min: params.get("track_count_min") || "", 132 track_count_max: params.get("track_count_max") || "", 133 genre: params.getAll("genre"), 134 mood: params.getAll("mood"), 135 min_views: params.get("min_views") || "", 136 min_likes: params.get("min_likes") || "", 137 country: params.get("country") || "", 138 sort: params.get("sort") || "recent", 139 melody_notes: params.get("melody_notes") || "", 140 melody_mode: params.get("melody_mode") || "easy" 141 }; 142 }; 143 const [filters, setFilters] = reactExports.useState(getInitialFilters); 144 const [localQ, setLocalQ] = reactExports.useState(filters.q); 145 const [localTempoMin, setLocalTempoMin] = reactExports.useState(filters.tempo_min); 146 const [localTempoMax, setLocalTempoMax] = reactExports.useState(filters.tempo_max); 147 const [localDurationMin, setLocalDurationMin] = reactExports.useState(filters.duration_min); 148 const [localDurationMax, setLocalDurationMax] = reactExports.useState(filters.duration_max); 149 const [localNoteMin, setLocalNoteMin] = reactExports.useState(filters.note_count_min);
150 const [localNoteMax, setLocalNoteMax] = reactExports.useState(filters.note_count_max); 151 const [localTrackMin, setLocalTrackMin] = reactExports.useState(filters.track_count_min); 152 const [localTrackMax, setLocalTrackMax] = reactExports.useState(filters.track_count_max); 153 const [hits, setHits] = reactExports.useState([]); 154 const [facetDistribution, setFacetDistribution] = reactExports.useState({}); 155 const [nbHits, setNbHits] = reactExports.useState(0); 156 const [loading, setLoading] = reactExports.useState(false); 157 const [isSidebarOpen, setIsSidebarOpen] = reactExports.useState(true); 158 const [melodyNotes, setMelodyNotes] = reactExports.useState(() => { 159 const raw = new URLSearchParams(window.location.search).get("melody_notes"); 160 return raw ? raw.split(",").map(Number).filter((n) => !isNaN(n)) : []; 161 }); 162 const [melodyMode, setMelodyMode] = reactExports.useState(filters.melody_mode); 163 const [searchTarget, setSearchTarget] = reactExports.useState( 164 new URLSearchParams(window.location.search).get("target") || "everything" 165 ); 166 const fetchResults = reactExports.useCallback(async () => { 167 var _a, _b; 168 setLoading(true); 169 try { 170 let mergedHits = []; 171 let nextFacet = {}; 172 let totalNb = 0; 173 const promises = []; 174 if (searchTarget === "everything" || searchTarget === "midi") { 175 promises.push( 176 api.advancedSearch({ 177 ...filters, 178 melody_notes: melodyNotes.join(","), 179 melody_mode: melodyMode 180 }).then((res) => ({ type: "midi", res })).catch((err) => { 181 console.error("[Advanced Search Midi Error]", err); 182 return { type: "midi", res: null }; 183 }) 184 ); 185 } 186 if (searchTarget === "everything" || searchTarget === "lyrics") { 187 if (filters.q.trim()) { 188 promises.push( 189 api.searchLyrics(filters.q).then((res) => ({ type: "lyrics", res })).catch((err) => { 190 console.error("[Advanced Search Lyrics Error]", err); 191 return { type: "lyrics", res: null }; 192 }) 193 ); 194 } else if (searchTarget === "lyrics") { 195 setHits([]); 196 setFacetDistribution({}); 197 setNbHits(0); 198 setLoading(false); 199 return; 200 } 201 } 202 const results = await Promise.all(promises); 203 const midiData = (_a = results.find((r) => r.type === "midi")) == null ? void 0 : _a.res; 204 const lyricData = (_b = results.find((r) => r.type === "lyrics")) == null ? void 0 : _b.res; 205 if (searchTarget === "midi") { 206 mergedHits = (midiData == null ? void 0 : midiData.hits) || []; 207 nextFacet = (midiData == null ? void 0 : midiData.facetDistribution) || {}; 208 totalNb = (midiData == null ? void 0 : midiData.nbHits) || 0; 209 } else if (searchTarget === "lyrics") { 210 mergedHits = ((lyricData == null ? void 0 : lyricData.results) || []).map((l) => ({ ...l, _isLyric: true })); 211 totalNb = (lyricData == null ? void 0 : lyricData.total) || 0; 212 } else { 213 const midis = (midiData == null ? void 0 : midiData.hits) || []; 214 const lyrics = ((lyricData == null ? void 0 : lyricData.results) || []).map((l) => ({ ...l, _isLyric: true })); 215 nextFacet = (midiData == null ? void 0 : midiData.facetDistribution) || {}; 216 totalNb = ((midiData == null ? void 0 : midiData.nbHits) || 0) + ((lyricData == null ? void 0 : lyricData.total) || 0); 217 const interleaved = []; 218 const maxLen = Math.max(midis.length, lyrics.length); 219 for (let i = 0; i < maxLen; i++) { 220 if (midis[i]) interleaved.push(midis[i]); 221 if (lyrics[i]) interleaved.push(lyrics[i]); 222 } 223 mergedHits = interleaved; 224 } 225 setHits(mergedHits); 226 setFacetDistribution(nextFacet); 227 setNbHits(totalNb); 228 } catch (e) { 229 console.error("[Advanced Search Fetch Error]", e); 230 } finally { 231 setLoading(false); 232 } 233 }, [filters, melodyNotes, melodyMode, searchTarget]); 234 reactExports.useEffect(() => { 235 const nextFilters = { 236 ...filters, 237 melody_notes: melodyNotes.join(","), 238 melody_mode: melodyMode, 239 target: searchTarget 240 }; 241 const params = new URLSearchParams(); 242 for (const [key, val] of Object.entries(nextFilters)) { 243 if (val === void 0 || val === null || val === "" || val === false) continue; 244 if (Array.isArray(val)) { 245 val.forEach((v) => params.append(key, v)); 246 } else if (typeof val === "boolean") { 247 if (val) params.set(key, "true"); 248 } else { 249 params.set(key, String(val)); 250 } 251 } 252 const searchStr = params.toString(); 253 const nextUrl = window.location.pathname + (searchStr ? `?${searchStr}` : ""); 254 if (window.location.search !== `?${searchStr}` && window.location.search !== searchStr) { 255 window.history.replaceState(null, "", nextUrl); 256 } 257 }, [filters, melodyNotes, melodyMode, searchTarget]); 258 reactExports.useEffect(() => { 259 const timer = setTimeout(() => { 260 fetchResults(); 261 }, 300); 262 return () => clearTimeout(timer); 263 }, [filters, melodyNotes, melodyMode, searchTarget, fetchResults]); 264 reactExports.useEffect(() => { 265 const onPopState = () => { 266 const init = getInitialFilters(); 267 setFilters(init); 268 setLocalQ(init.q); 269 setLocalTempoMin(init.tempo_min); 270 setLocalTempoMax(init.tempo_max); 271 setLocalDurationMin(init.duration_min); 272 setLocalDurationMax(init.duration_max); 273 setLocalNoteMin(init.note_count_min); 274 setLocalNoteMax(init.note_count_max); 275 setLocalTrackMin(init.track_count_min); 276 setLocalTrackMax(init.track_count_max); 277 const raw = new URLSearchParams(window.location.search).get("melody_notes"); 278 setMelodyNotes(raw ? raw.split(",").map(Number).filter((n) => !isNaN(n)) : []); 279 setMelodyMode(new URLSearchParams(window.location.search).get("melody_mode") || "easy"); 280 setSearchTarget(new URLSearchParams(window.location.search).get("target") || "everything"); 281 }; 282 window.addEventListener("popstate", onPopState); 283 return () => window.removeEventListener("popstate", onPopState); 284 }, []); 285 reactExports.useEffect(() => { 286 if (searchQuery !== void 0) { 287 setLocalQ(searchQuery); 288 setFilters((prev) => { 289 if (prev.q !== searchQuery) { 290 setLocalTempoMin(""); 291 setLocalTempoMax(""); 292 setLocalDurationMin(""); 293 setLocalDurationMax(""); 294 setLocalNoteMin(""); 295 setLocalNoteMax(""); 296 setLocalTrackMin(""); 297 setLocalTrackMax(""); 298 setMelodyNotes([]); 299 setMelodyMode("easy"); 300 return { 301 q: searchQuery, 302 tempo_min: "", 303 tempo_max: "", 304 key: [], 305 time_sig: [], 306 difficulty: [], 307 instruments: [], 308 has_drums: false,
309 has_lyrics: false, 310 has_sheets: false, 311 has_stems: false, 312 has_analysis: false, 313 duration_min: "", 314 duration_max: "", 315 note_count_min: "", 316 note_count_max: "", 317 track_count_min: "", 318 track_count_max: "", 319 genre: [], 320 mood: [], 321 min_views: "", 322 min_likes: "", 323 country: "", 324 sort: "recent", 325 melody_notes: "", 326 melody_mode: "easy" 327 }; 328 } 329 return prev; 330 }); 331 } 332 }, [searchQuery]); 333 const handleTextChange = (e) => { 334 const val = e.target.value; 335 setLocalQ(val); 336 setFilters((prev) => ({ ...prev, q: val })); 337 }; 338 const handleFilterToggle = (key, val) => { 339 setFilters((prev) => { 340 const arr = [...prev[key] || []]; 341 const idx = arr.indexOf(val); 342 if (idx > -1) { 343 arr.splice(idx, 1); 344 } else { 345 arr.push(val); 346 } 347 return { ...prev, [key]: arr }; 348 }); 349 }; 350 const handleBooleanToggle = (key) => { 351 setFilters((prev) => ({ ...prev, [key]: !prev[key] })); 352 }; 353 const handleRangeChange = (key, val) => { 354 setFilters((prev) => ({ ...prev, [key]: val })); 355 }; 356 const handleReset = () => { 357 setLocalQ(""); 358 setLocalTempoMin(""); 359 setLocalTempoMax(""); 360 setLocalDurationMin(""); 361 setLocalDurationMax(""); 362 setLocalNoteMin(""); 363 setLocalNoteMax(""); 364 setLocalTrackMin(""); 365 setLocalTrackMax(""); 366 setMelodyNotes([]); 367 setFilters({ 368 q: "", 369 tempo_min: "", 370 tempo_max: "", 371 key: [], 372 time_sig: [], 373 difficulty: [], 374 instruments: [], 375 has_drums: false, 376 has_lyrics: false, 377 has_sheets: false, 378 has_stems: false, 379 has_analysis: false, 380 duration_min: "", 381 duration_max: "", 382 note_count_min: "", 383 note_count_max: "", 384 track_count_min: "", 385 track_count_max: "", 386 genre: [], 387 mood: [], 388 min_views: "", 389 min_likes: "", 390 country: "", 391 sort: "recent", 392 melody_notes: "", 393 melody_mode: "easy" 394 }); 395 }; 396 const playMidiNote = (note) => { 397 try { 398 const ctx = new (window.AudioContext || window.webkitAudioContext)(); 399 const osc = ctx.createOscillator(); 400 const gain = ctx.createGain(); 401 const freq = 440 * Math.pow(2, (note - 69) / 12); 402 osc.frequency.setValueAtTime(freq, ctx.currentTime); 403 osc.type = "sine"; 404 gain.gain.setValueAtTime(0.15, ctx.currentTime); 405 gain.gain.exponentialRampToValueAtTime(1e-3, ctx.currentTime + 0.4); 406 osc.connect(gain); 407 gain.connect(ctx.destination); 408 osc.start(); 409 osc.stop(ctx.currentTime + 0.4); 410 } catch (e) { 411 console.warn("AudioContext playback blocked:", e); 412 } 413 }; 414 const handlePianoKeyPress = (note) => { 415 playMidiNote(note); 416 setMelodyNotes((prev) => [...prev, note]); 417 }; 418 const removeLastNote = () => { 419 setMelodyNotes((prev) => prev.slice(0, -1)); 420 }; 421 const getFacetCount = (facetKey, val) => { 422 var _a; 423 return ((_a = facetDistribution == null ? void 0 : facetDistribution[facetKey]) == null ? void 0 : _a[val]) || 0; 424 }; 425 const getActiveFilterCount = () => {
426 let count = 0; 427 if (filters.q) count++; 428 if (filters.tempo_min || filters.tempo_max) count++; 429 if (filters.duration_min || filters.duration_max) count++; 430 if (filters.note_count_min || filters.note_count_max) count++; 431 if (filters.track_count_min || filters.track_count_max) count++; 432 if (filters.country) count++; 433 if (filters.min_views) count++; 434 if (filters.min_likes) count++; 435 if (filters.has_drums) count++; 436 if (filters.has_lyrics) count++; 437 if (filters.has_sheets) count++; 438 if (filters.has_stems) count++; 439 if (filters.has_analysis) count++; 440 count += filters.key.length; 441 count += filters.time_sig.length; 442 count += filters.difficulty.length; 443 count += filters.instruments.length; 444 count += filters.genre.length; 445 count += filters.mood.length; 446 if (melodyNotes.length > 0) count++; 447 return count; 448 }; 449 return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `advanced-search-page`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-container", children: [ 450 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "lg:hidden w-full flex justify-between mb-4", children: [
451 /* @__PURE__ */ jsxRuntimeExports.jsxs( 452 "button", 453 { 454 onClick: () => setIsSidebarOpen(!isSidebarOpen), 455 className: "flex items-center gap-2 px-4 py-2.5 bg-zinc-900 rounded-xl font-bold border border-zinc-800 text-xs text-white", 456 children: [ 457 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersHorizontal, { size: 14 }), 458 isSidebarOpen ? "Hide Filters" : "Show Filters" 459 ] 460 } 461 ), 462 /* @__PURE__ */ jsxRuntimeExports.jsxs( 463 "button", 464 { 465 onClick: handleReset, 466 className: "flex items-center gap-2 px-4 py-2.5 bg-zinc-900 rounded-xl font-bold border border-zinc-800 text-xs text-white", 467 children: [ 468 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 14 }), 469 "Reset" 470 ] 471 } 472 ) 473 ] }), 474 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `as-sidebar ${!isSidebarOpen ? "collapsed" : ""} ${searchTarget === "lyrics" ? "opacity-30 pointer-events-none grayscale" : ""}`, children: [
475 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center pb-2 border-b border-zinc-200 dark:border-zinc-800", children: [ 476 /* @__PURE__ */ jsxRuntimeExports.jsxs("h2", { className: "text-sm font-black uppercase tracking-wider flex items-center gap-2 text-zinc-900 dark:text-white", children: [ 477 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersHorizontal, { size: 14, className: "text-indigo-500" }), 478 t("search.advancedFilters", "Advanced Filters") 479 ] }), 480 /* @__PURE__ */ jsxRuntimeExports.jsxs( 481 "button", 482 { 483 onClick: handleReset, 484 className: "text-[10px] font-black text-indigo-500 hover:text-indigo-600 dark:hover:text-indigo-400 uppercase tracking-widest flex items-center gap-1 bg-zinc-100 dark:bg-zinc-800 px-2 py-1 rounded", 485 children: [ 486 /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { size: 10 }), 487 t("search.resetAll", "Reset All") 488 ] 489 } 490 ) 491 ] }),
492 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-accordion-section", children: [ 493 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-header", onClick: () => toggleSection("melody"), children: [ 494 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 495 /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { size: 12, className: "text-pink-500" }), 496 t("search.melodyKeyboardSearch", "Melody Keyboard Search") 497 ] }), 498 expanded.melody ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) 499 ] }), 500 expanded.melody && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-content", children: [ 501 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[10px] text-zinc-500 font-semibold mb-2", children: "Click keys below to input melody intervals. Restricts results to tracks matching the melody pattern." }),
502 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2", children: [ 503 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex bg-zinc-950 p-2 rounded-xl border border-zinc-800 justify-center gap-1 overflow-x-auto", children: [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76].map((note) => { 504 const isBlack = [1, 3, 6, 8, 10].includes(note % 12); 505 return /* @__PURE__ */ jsxRuntimeExports.jsx( 506 "button", 507 { 508 onClick: () => handlePianoKeyPress(note), 509 className: `h-16 w-6 rounded-b transition-all active:scale-95 shrink-0 ${isBlack ? "bg-zinc-850 border border-zinc-950 text-white z-10 -mx-1.5 h-11 w-4.5" : "bg-white border border-zinc-300 text-zinc-900"}`, 510 style: { 511 boxShadow: isBlack ? "inset 0 -3px 0 rgba(0,0,0,0.5)" : "inset 0 -3px 0 rgba(0,0,0,0.1)" 512 }, 513 title: getNoteName(note) 514 }, 515 note 516 ); 517 }) }), 518 melodyNotes.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 p-2 bg-zinc-100 dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-850", children: [
519 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center", children: [ 520 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-bold uppercase tracking-wider text-zinc-500", children: [ 521 "Notes Sequence (", 522 melodyNotes.length, 523 ")" 524 ] }), 525 /* @__PURE__ */ jsxRuntimeExports.jsx( 526 "button", 527 { 528 onClick: removeLastNote, 529 className: "text-[9px] font-bold text-red-500 hover:underline uppercase", 530 children: "Delete Last" 531 } 532 ) 533 ] }), 534 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1 max-h-16 overflow-y-auto custom-scrollbar", children: melodyNotes.map((note, i) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-bold px-1.5 py-0.5 bg-indigo-500/10 text-indigo-500 border border-indigo-500/20 rounded", children: getNoteName(note) }, i)) }),
535 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 mt-1", children: [ 536 /* @__PURE__ */ jsxRuntimeExports.jsx( 537 "button", 538 { 539 onClick: () => setMelodyMode("easy"), 540 className: `flex-1 text-[9px] font-black py-1 rounded border uppercase tracking-wider ${melodyMode === "easy" ? "bg-indigo-500 text-white border-indigo-500" : "bg-transparent text-zinc-400 border-zinc-700"}`, 541 children: "Easy (Parsons)" 542 } 543 ), 544 /* @__PURE__ */ jsxRuntimeExports.jsx( 545 "button", 546 { 547 onClick: () => setMelodyMode("advanced"), 548 className: `flex-1 text-[9px] font-black py-1 rounded border uppercase tracking-wider ${melodyMode === "advanced" ? "bg-indigo-500 text-white border-indigo-500" : "bg-transparent text-zinc-400 border-zinc-700"}`, 549 children: "Exact Intervals" 550 } 551 ) 552 ] }) 553 ] }) 554 ] }) 555 ] }) 556 ] }),
557 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-accordion-section", children: [ 558 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-header", onClick: () => toggleSection("music"), children: [ 559 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 560 /* @__PURE__ */ jsxRuntimeExports.jsx(Music, { size: 12, className: "text-indigo-500" }), 561 t("search.musicalProperties", "Musical Properties") 562 ] }), 563 expanded.music ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) 564 ] }), 565 expanded.music && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-content", children: [ 566 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 567 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.keySignature", "Key Signature") }),
568 /* @__PURE__ */ jsxRuntimeExports.jsxs( 569 "select", 570 { 571 value: filters.key[0] || "", 572 onChange: (e) => setFilters((prev) => ({ ...prev, key: e.target.value ? [e.target.value] : [] })), 573 className: "as-select", 574 children: [ 575 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: t("search.anyKey", "Any Key") }), 576 KEYS.map((k) => /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: k, children: [ 577 k, 578 " ", 579 getFacetCount("keySignature", k) > 0 ? `(${getFacetCount("keySignature", k)})` : "" 580 ] }, k)) 581 ] 582 } 583 ) 584 ] }), 585 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 586 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-range-labels", children: [ 587 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("search.tempoBpm", "Tempo / BPM") }),
588 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 589 filters.tempo_min || "40", 590 " - ", 591 filters.tempo_max || "240" 592 ] }) 593 ] }), 594 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-inputs", children: [ 595 /* @__PURE__ */ jsxRuntimeExports.jsx( 596 "input", 597 { 598 type: "number", 599 placeholder: "Min", 600 value: localTempoMin, 601 onChange: (e) => { 602 setLocalTempoMin(e.target.value); 603 handleRangeChange("tempo_min", e.target.value); 604 }, 605 className: "as-range-input" 606 } 607 ), 608 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500", children: "-" }), 609 /* @__PURE__ */ jsxRuntimeExports.jsx( 610 "input", 611 { 612 type: "number", 613 placeholder: "Max", 614 value: localTempoMax, 615 onChange: (e) => { 616 setLocalTempoMax(e.target.value); 617 handleRangeChange("tempo_max", e.target.value); 618 }, 619 className: "as-range-input" 620 } 621 ) 622 ] }) 623 ] }),
624 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 625 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.timeSignature", "Time Signature") }), 626 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "as-chips-grid", children: TIME_SIGNATURES.map((sig) => { 627 const isActive = filters.time_sig.includes(sig); 628 const count = getFacetCount("timeSignature", sig); 629 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 630 "button", 631 { 632 onClick: () => handleFilterToggle("time_sig", sig), 633 className: `as-chip ${isActive ? "active" : ""}`, 634 children: [ 635 sig, 636 count > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip-count", children: [ 637 "(", 638 count, 639 ")" 640 ] }) 641 ] 642 }, 643 sig 644 ); 645 }) }) 646 ] }) 647 ] }) 648 ] }),
649 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-accordion-section", children: [ 650 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-header", onClick: () => toggleSection("instruments"), children: [ 651 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 652 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 12, className: "text-emerald-500" }), 653 t("search.instrumentation", "Instrumentation") 654 ] }), 655 expanded.instruments ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) 656 ] }), 657 expanded.instruments && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-content", children: [ 658 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 659 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-range-labels", children: [
660 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("search.numberOfTracks", "Number of Tracks") }), 661 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 662 filters.track_count_min || "1", 663 " - ", 664 filters.track_count_max || "32+" 665 ] }) 666 ] }), 667 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-inputs", children: [ 668 /* @__PURE__ */ jsxRuntimeExports.jsx( 669 "input", 670 { 671 type: "number", 672 placeholder: "Min", 673 value: localTrackMin, 674 onChange: (e) => { 675 setLocalTrackMin(e.target.value); 676 handleRangeChange("track_count_min", e.target.value); 677 }, 678 className: "as-range-input" 679 } 680 ), 681 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500", children: "-" }),
682 /* @__PURE__ */ jsxRuntimeExports.jsx( 683 "input", 684 { 685 type: "number", 686 placeholder: "Max", 687 value: localTrackMax, 688 onChange: (e) => { 689 setLocalTrackMax(e.target.value); 690 handleRangeChange("track_count_max", e.target.value); 691 }, 692 className: "as-range-input" 693 } 694 ) 695 ] }) 696 ] }), 697 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-toggle-wrapper", children: [ 698 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 699 /* @__PURE__ */ jsxRuntimeExports.jsx(Drum, { size: 12, className: "text-zinc-400" }), 700 t("search.hasDrums", "Has Drums/Percussion") 701 ] }), 702 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "as-switch", children: [ 703 /* @__PURE__ */ jsxRuntimeExports.jsx( 704 "input", 705 { 706 type: "checkbox", 707 checked: filters.has_drums, 708 onChange: () => handleBooleanToggle("has_drums") 709 } 710 ),
711 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-slider" }) 712 ] }) 713 ] }), 714 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 715 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.instrumentsUsed", "Instruments Used") }), 716 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "as-chips-grid max-h-48 overflow-y-auto custom-scrollbar p-1 border border-zinc-200 dark:border-zinc-800 rounded-lg", children: COMMON_INSTRUMENTS.map((inst) => { 717 const isActive = filters.instruments.includes(String(inst.program)); 718 const count = getFacetCount("instrumentNames", inst.name); 719 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 720 "button", 721 { 722 onClick: () => handleFilterToggle("instruments", String(inst.program)), 723 className: `as-chip ${isActive ? "active" : ""}`, 724 children: [ 725 t(`instruments.${inst.name}`, inst.name), 726 count > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip-count", children: [ 727 "(", 728 count, 729 ")" 730 ] }) 731 ] 732 }, 733 inst.program 734 ); 735 }) }) 736 ] }) 737 ] }) 738 ] }),
739 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-accordion-section", children: [ 740 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-header", onClick: () => toggleSection("complexity"), children: [ 741 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 742 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersHorizontal, { size: 12, className: "text-amber-500" }), 743 t("search.difficultyComplexity", "Difficulty & Complexity") 744 ] }), 745 expanded.complexity ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) 746 ] }), 747 expanded.complexity && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-content", children: [ 748 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 749 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.difficulty", "Difficulty") }),
750 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "as-chips-grid", children: DIFFICULTIES.map((diff) => { 751 const isActive = filters.difficulty.includes(diff); 752 const count = getFacetCount("difficulty", diff); 753 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 754 "button", 755 { 756 onClick: () => handleFilterToggle("difficulty", diff), 757 className: `as-chip ${isActive ? "active" : ""}`, 758 children: [ 759 t(`difficulties.${diff}`, diff), 760 count > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip-count", children: [ 761 "(", 762 count, 763 ")" 764 ] }) 765 ] 766 }, 767 diff 768 ); 769 }) }) 770 ] }), 771 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [
772 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.durationSeconds", "Duration (seconds)") }), 773 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-inputs", children: [ 774 /* @__PURE__ */ jsxRuntimeExports.jsx( 775 "input", 776 { 777 type: "number", 778 placeholder: "Min", 779 value: localDurationMin, 780 onChange: (e) => { 781 setLocalDurationMin(e.target.value); 782 handleRangeChange("duration_min", e.target.value); 783 }, 784 className: "as-range-input" 785 } 786 ), 787 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500", children: "-" }), 788 /* @__PURE__ */ jsxRuntimeExports.jsx( 789 "input", 790 { 791 type: "number", 792 placeholder: "Max", 793 value: localDurationMax, 794 onChange: (e) => { 795 setLocalDurationMax(e.target.value); 796 handleRangeChange("duration_max", e.target.value); 797 }, 798 className: "as-range-input" 799 } 800 ) 801 ] }) 802 ] }),
803 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 804 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.noteCountNotes", "Note Count (Notes)") }), 805 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-inputs", children: [ 806 /* @__PURE__ */ jsxRuntimeExports.jsx( 807 "input", 808 { 809 type: "number", 810 placeholder: "Min", 811 value: localNoteMin, 812 onChange: (e) => { 813 setLocalNoteMin(e.target.value); 814 handleRangeChange("note_count_min", e.target.value); 815 }, 816 className: "as-range-input" 817 } 818 ), 819 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500", children: "-" }), 820 /* @__PURE__ */ jsxRuntimeExports.jsx( 821 "input", 822 { 823 type: "number", 824 placeholder: "Max", 825 value: localNoteMax, 826 onChange: (e) => { 827 setLocalNoteMax(e.target.value); 828 handleRangeChange("note_count_max", e.target.value); 829 }, 830 className: "as-range-input" 831 } 832 ) 833 ] }) 834 ] }) 835 ] }) 836 ] }),
837 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-accordion-section", children: [ 838 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-header", onClick: () => toggleSection("classification"), children: [ 839 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 840 /* @__PURE__ */ jsxRuntimeExports.jsx(Sparkles, { size: 12, className: "text-purple-500" }), 841 t("search.genreMood", "Genre & Mood") 842 ] }), 843 expanded.classification ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) 844 ] }), 845 expanded.classification && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-content", children: [ 846 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 847 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.genres", "Genres") }),
848 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "as-chips-grid max-h-36 overflow-y-auto custom-scrollbar p-1", children: GENRES.map((g) => { 849 const isActive = filters.genre.includes(g); 850 const count = getFacetCount("genreTags", g); 851 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 852 "button", 853 { 854 onClick: () => handleFilterToggle("genre", g), 855 className: `as-chip ${isActive ? "active" : ""}`, 856 children: [ 857 t(`genres.${g}`, g), 858 count > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip-count", children: [ 859 "(", 860 count, 861 ")" 862 ] }) 863 ] 864 }, 865 g 866 ); 867 }) }) 868 ] }), 869 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [
870 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.moods", "Moods") }), 871 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "as-chips-grid max-h-36 overflow-y-auto custom-scrollbar p-1", children: MOODS.map((m) => { 872 const isActive = filters.mood.includes(m); 873 const count = getFacetCount("moodTags", m); 874 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 875 "button", 876 { 877 onClick: () => handleFilterToggle("mood", m), 878 className: `as-chip ${isActive ? "active" : ""}`, 879 children: [ 880 t(`moods.${m}`, m), 881 count > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip-count", children: [ 882 "(", 883 count, 884 ")" 885 ] }) 886 ] 887 }, 888 m 889 ); 890 }) }) 891 ] }) 892 ] }) 893 ] }),
894 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-accordion-section", children: [ 895 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-header", onClick: () => toggleSection("origin"), children: [ 896 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 897 /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 12, className: "text-pink-400" }), 898 t("search.availabilityOrigin", "Availability & Origin") 899 ] }), 900 expanded.origin ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) 901 ] }), 902 expanded.origin && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-content", children: [ 903 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-toggle-wrapper", children: [ 904 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [
905 /* @__PURE__ */ jsxRuntimeExports.jsx(FileMusic, { size: 12, className: "text-zinc-400" }), 906 t("search.hasSheetMusic", "Has Sheet Music") 907 ] }), 908 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "as-switch", children: [ 909 /* @__PURE__ */ jsxRuntimeExports.jsx( 910 "input", 911 { 912 type: "checkbox", 913 checked: filters.has_sheets, 914 onChange: () => handleBooleanToggle("has_sheets") 915 } 916 ), 917 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-slider" }) 918 ] }) 919 ] }), 920 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-toggle-wrapper", children: [ 921 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 922 /* @__PURE__ */ jsxRuntimeExports.jsx(PanelsTopLeft, { size: 12, className: "text-zinc-400" }), 923 t("search.hasSeparatedStems", "Has Separated Stems") 924 ] }),
925 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "as-switch", children: [ 926 /* @__PURE__ */ jsxRuntimeExports.jsx( 927 "input", 928 { 929 type: "checkbox", 930 checked: filters.has_stems, 931 onChange: () => handleBooleanToggle("has_stems") 932 } 933 ), 934 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-slider" }) 935 ] }) 936 ] }), 937 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-toggle-wrapper", children: [ 938 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 939 /* @__PURE__ */ jsxRuntimeExports.jsx(Headphones, { size: 12, className: "text-zinc-400" }), 940 t("search.hasLyrics", "Has Lyrics") 941 ] }), 942 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "as-switch", children: [ 943 /* @__PURE__ */ jsxRuntimeExports.jsx( 944 "input", 945 { 946 type: "checkbox", 947 checked: filters.has_lyrics, 948 onChange: () => handleBooleanToggle("has_lyrics") 949 } 950 ),
951 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-slider" }) 952 ] }) 953 ] }), 954 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-toggle-wrapper", children: [ 955 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 956 /* @__PURE__ */ jsxRuntimeExports.jsx(Key, { size: 12, className: "text-zinc-400" }), 957 t("search.hasHarmonicAnalysis", "Has Harmonic Analysis") 958 ] }), 959 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "as-switch", children: [ 960 /* @__PURE__ */ jsxRuntimeExports.jsx( 961 "input", 962 { 963 type: "checkbox", 964 checked: filters.has_analysis, 965 onChange: () => handleBooleanToggle("has_analysis") 966 } 967 ), 968 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-slider" }) 969 ] }) 970 ] }),
971 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper mt-2", children: [ 972 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-range-labels", children: [ 973 /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { size: 11, className: "text-zinc-400 inline mr-1" }), 974 t("search.geotaggedCountry", "Geotagged Origin Country") 975 ] }), 976 /* @__PURE__ */ jsxRuntimeExports.jsx( 977 "input", 978 { 979 type: "text", 980 placeholder: "e.g. US, JP, DE", 981 value: filters.country, 982 onChange: (e) => handleRangeChange("country", e.target.value.toUpperCase().slice(0, 2)), 983 className: "as-range-input" 984 } 985 ) 986 ] }) 987 ] }) 988 ] }), 989 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-accordion-section", children: [ 990 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-header", onClick: () => toggleSection("community"), children: [
991 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-2", children: [ 992 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { size: 12, className: "text-zinc-400" }), 993 t("search.communityMetrics", "Community Metrics") 994 ] }), 995 expanded.community ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 14 }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 14 }) 996 ] }), 997 expanded.community && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-section-content", children: [ 998 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 999 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.minViews", "Minimum Views") }), 1000 /* @__PURE__ */ jsxRuntimeExports.jsx( 1001 "input", 1002 { 1003 type: "number", 1004 placeholder: "e.g. 50", 1005 value: filters.min_views, 1006 onChange: (e) => handleRangeChange("min_views", e.target.value), 1007 className: "as-range-input" 1008 } 1009 ) 1010 ] }),
1011 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-range-wrapper", children: [ 1012 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "as-range-labels", children: t("search.minLikes", "Minimum Likes") }), 1013 /* @__PURE__ */ jsxRuntimeExports.jsx( 1014 "input", 1015 { 1016 type: "number", 1017 placeholder: "e.g. 5", 1018 value: filters.min_likes, 1019 onChange: (e) => handleRangeChange("min_likes", e.target.value), 1020 className: "as-range-input" 1021 } 1022 ) 1023 ] }) 1024 ] }) 1025 ] }) 1026 ] }), 1027 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-main", children: [ 1028 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-header-card", children: [ 1029 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex bg-zinc-200 dark:bg-zinc-800/80 p-1 rounded-xl mb-4 w-full self-start shadow-inner text-sm font-bold border border-zinc-300 dark:border-zinc-700", children: [
1030 /* @__PURE__ */ jsxRuntimeExports.jsx( 1031 "button", 1032 { 1033 onClick: () => setSearchTarget("everything"), 1034 className: `flex-1 px-6 py-2 rounded-lg transition-all ${searchTarget === "everything" ? "bg-white dark:bg-zinc-700 text-indigo-600 shadow-sm" : "text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200"}`, 1035 children: t("search.targetEverything", "Everything") 1036 } 1037 ), 1038 /* @__PURE__ */ jsxRuntimeExports.jsx( 1039 "button", 1040 { 1041 onClick: () => setSearchTarget("midi"), 1042 className: `flex-1 px-6 py-2 rounded-lg transition-all ${searchTarget === "midi" ? "bg-white dark:bg-zinc-700 text-indigo-600 shadow-sm" : "text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200"}`, 1043 children: t("search.targetMidi", "MIDI Tracks") 1044 } 1045 ), 1046 /* @__PURE__ */ jsxRuntimeExports.jsx( 1047 "button", 1048 { 1049 onClick: () => setSearchTarget("lyrics"), 1050 className: `flex-1 px-6 py-2 rounded-lg transition-all ${searchTarget === "lyrics" ? "bg-white dark:bg-zinc-700 text-indigo-600 shadow-sm" : "text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200"}`, 1051 children: t("search.targetLyrics", "Lyrics") 1052 } 1053 ) 1054 ] }),
1055 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-search-input-wrapper", children: [ 1056 /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "as-search-icon", size: 16 }), 1057 /* @__PURE__ */ jsxRuntimeExports.jsx( 1058 "input", 1059 { 1060 type: "text", 1061 placeholder: t("search.searchPlaceholder", "Search MIDI title, artist, or tags..."), 1062 value: localQ, 1063 onChange: handleTextChange, 1064 className: "as-search-input-field" 1065 } 1066 ) 1067 ] }), 1068 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-info-controls", children: [ 1069 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-results-count", children: [ 1070 nbHits, 1071 " ", 1072 nbHits === 1 ? "result" : "results", 1073 " found" 1074 ] }), 1075 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 1076 /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowUpDown, { size: 14, className: "text-zinc-500" }),
1077 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1078 "select", 1079 { 1080 value: filters.sort, 1081 onChange: (e) => handleRangeChange("sort", e.target.value), 1082 className: "as-select py-1.5 px-3 text-xs w-36", 1083 children: [ 1084 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "recent", children: t("search.sortRecent", "Recent") }), 1085 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "popular", children: t("search.sortPopular", "Popularity") }), 1086 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "top-rated", children: t("search.sortTopRated", "Top Rated") }), 1087 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "plays", children: t("search.sortPlays", "Most Played") }), 1088 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "tempo-asc", children: t("search.sortTempoAsc", "Tempo (Low to High)") }), 1089 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "tempo-desc", children: t("search.sortTempoDesc", "Tempo (High to Low)") }),
1090 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "duration-asc", children: t("search.sortDurationAsc", "Duration (Short to Long)") }), 1091 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "duration-desc", children: t("search.sortDurationDesc", "Duration (Long to Short)") }) 1092 ] 1093 } 1094 ) 1095 ] }), 1096 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center bg-zinc-100 dark:bg-zinc-800 p-1 rounded-xl shadow-inner shrink-0", children: [ 1097 /* @__PURE__ */ jsxRuntimeExports.jsx( 1098 "button", 1099 { 1100 onClick: () => { 1101 setViewMode("grid"); 1102 localStorage.setItem("hamie_view_mode", "grid"); 1103 }, 1104 className: `p-1.5 rounded-lg transition-all ${viewMode === "grid" ? "bg-white dark:bg-zinc-700 text-indigo-600 shadow-sm" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`, 1105 title: "Grid View", 1106 children: /* @__PURE__ */ jsxRuntimeExports.jsx(LayoutGrid, { size: 16 }) 1107 } 1108 ),
1109 /* @__PURE__ */ jsxRuntimeExports.jsx( 1110 "button", 1111 { 1112 onClick: () => { 1113 setViewMode("list"); 1114 localStorage.setItem("hamie_view_mode", "list"); 1115 }, 1116 className: `p-1.5 rounded-lg transition-all ${viewMode === "list" ? "bg-white dark:bg-zinc-700 text-indigo-600 shadow-sm" : "text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"}`, 1117 title: "List View", 1118 children: /* @__PURE__ */ jsxRuntimeExports.jsx(List, { size: 16 }) 1119 } 1120 ) 1121 ] }) 1122 ] }) 1123 ] }), 1124 getActiveFilterCount() > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-active-filters bg-zinc-100 dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl p-2.5 flex items-center justify-between", children: [ 1125 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap gap-1.5 items-center", children: [ 1126 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-black uppercase text-zinc-500 mr-2 tracking-wider", children: t("search.activeFilters", "Active Filters:") }), 1127 filters.q && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1128 'Text: "', 1129 filters.q, 1130 '"',
1131 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => { 1132 setLocalQ(""); 1133 handleRangeChange("q", ""); 1134 } }) 1135 ] }), 1136 melodyNotes.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px] bg-pink-600 border-pink-600", children: [ 1137 t("search.filterMelody", "Melody Sequence"), 1138 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => setMelodyNotes([]) }) 1139 ] }), 1140 filters.key.map((k) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1141 "Key: ", 1142 k, 1143 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleFilterToggle("key", k) }) 1144 ] }, k)), 1145 filters.time_sig.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1146 "Sig: ", 1147 s, 1148 /* @__P
1148URE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleFilterToggle("time_sig", s) }) 1149 ] }, s)), 1150 filters.difficulty.map((d) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1151 "Diff: ", 1152 d, 1153 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleFilterToggle("difficulty", d) }) 1154 ] }, d)), 1155 filters.genre.map((g) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px] bg-purple-600 border-purple-600", children: [ 1156 g, 1157 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleFilterToggle("genre", g) }) 1158 ] }, g)), 1159 filters.mood.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px] bg-purple-600 border-purple-600", children: [ 1160 m, 1161 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleFilterToggle("mood", m) }) 1162 ] }, m)), 1163 filters.instruments.map((prog) => { 1164 const inst = COMMON_INSTRUMENTS.find((i) => String(i.program) === prog); 1165 return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px] bg-emerald-600 border-emerald-600", children: [ 1166 "Inst: ", 1167 (inst == null ? void 0 : inst.name) || prog,
1168 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleFilterToggle("instruments", prog) }) 1169 ] }, prog); 1170 }), 1171 filters.has_drums && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1172 t("search.filterDrums", "Drums"), 1173 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleRangeChange("has_drums", "") }) 1174 ] }), 1175 filters.has_lyrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1176 t("search.targetLyrics", "Lyrics"), 1177 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleRangeChange("has_lyrics", "") }) 1178 ] }), 1179 filters.has_sheets && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1180 t("search.filterSheets", "Sheet Music"), 1181 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleRangeChange("has_sheets", "") }) 1182 ] }), 1183 filters.has_stems && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1184 t("search.filterStems", "Stems"),
1185 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleRangeChange("has_stems", "") }) 1186 ] }), 1187 filters.has_analysis && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1188 t("search.filterAnalyzed", "Analyzed"), 1189 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => handleRangeChange("has_analysis", "") }) 1190 ] }), 1191 (filters.tempo_min || filters.tempo_max) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "as-chip active py-1 text-[10px]", children: [ 1192 "Tempo: ", 1193 filters.tempo_min || "40", 1194 "-", 1195 filters.tempo_max || "240", 1196 " BPM", 1197 /* @__PURE__ */ jsxRuntimeExports.jsx(X, { size: 10, onClick: () => { 1198 setLocalTempoMin(""); 1199 setLocalTempoMax(""); 1200 setFilters((prev) => ({ ...prev, tempo_min: "", tempo_max: "" })); 1201 } }) 1202 ] }) 1203 ] }),
1204 /* @__PURE__ */ jsxRuntimeExports.jsx( 1205 "button", 1206 { 1207 onClick: handleReset, 1208 className: "as-clear-all-btn text-[10px] whitespace-nowrap", 1209 children: t("search.clearAll", "Clear All") 1210 } 1211 ) 1212 ] }), 1213 loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-96 items-center justify-center min-h-[50vh]", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin" }) }) : hits.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: viewMode === "grid" ? "as-tracks-grid" : "flex flex-col gap-4 w-full", children: hits.map((track, idx) => { 1214 const showAd = (idx + 1) % 12 === 0; 1215 if (track._isLyric) { 1216 return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [ 1217 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1218 "div", 1219 { 1220 onClick: () => window.location.href = `/lyrics/${track.id}`, 1221 className: `p-4 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 hover:border-indigo-500 rounded-xl cursor-pointer shadow-sm hover:shadow-md transition flex flex-col justify-between ${viewMode === "grid" ? "aspect-video" : "w-full flex-row items-center h-20"}`, 1222 children: [
1223 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 1224 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "font-bold text-zinc-900 dark:text-white group-hover:text-indigo-400 transition mb-1 text-base line-clamp-1", children: track.title }), 1225 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-zinc-500 line-clamp-1", children: [ 1226 track.artist && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-indigo-400 font-medium", children: track.artist }), 1227 track.album && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: track.album }) 1228 ] }) 1229 ] }), 1230 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex items-center gap-3 text-xs text-zinc-500 ${viewMode === "grid" ? "mt-4" : "ml-auto"}`, children: [ 1231 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 font-bold text-indigo-500 bg-indigo-500/10 px-2 py-1 rounded", children: [
1232 /* @__PURE__ */ jsxRuntimeExports.jsx(Mic, { size: 12 }), 1233 t("search.targetLyrics", "Lyrics") 1234 ] }), 1235 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [ 1236 /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { size: 12 }), 1237 track.views || 0 1238 ] }) 1239 ] }) 1240 ] 1241 }, 1242 `lyric-${track.id}` 1243 ), 1244 showAd && shouldShowExploreAd && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: viewMode === "grid" ? "col-span-1" : "w-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx(AdSlot, { type: "exploreList", className: "!my-0 h-full min-h-[200px]" }) }) 1245 ] }, `lyric-frag-${track.id}-${idx}`); 1246 } 1247 return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [ 1248 /* @__PURE__ */ jsxRuntimeExports.jsx( 1249 VideoCard, 1250 { 1251 track, 1252 onSelect: onSelectTrack, 1253 onToggleWatchLater, 1254 isWatchLater: watchLaterSet.has(String(track.id)), 1255 onAddToQueue, 1256 onAddToPlaylist, 1257 variant: viewMode 1258 }, 1259 `midi-${track.id}` 1260 ),
1261 showAd && shouldShowExploreAd && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: viewMode === "grid" ? "col-span-1" : "w-full", children: /* @__PURE__ */ jsxRuntimeExports.jsx(AdSlot, { type: "exploreList", className: "!my-0 h-full min-h-[200px]" }) }) 1262 ] }, `midi-frag-${track.id}-${idx}`); 1263 }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "as-empty-state", children: [ 1264 /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { size: 48, className: "text-zinc-600 animate-pulse" }), 1265 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "as-empty-title", children: t("search.noMatchingTracks", "No matching MIDI tracks") }), 1266 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "as-empty-desc", children: t("search.noMatchingDesc", "We couldn't find any results matching your filter combination. Try resetting some filters or typing a different query.") }) 1267 ] }) 1268 ] }) 1269 ] }) }); 1270}; 1271export { 1272 AdvancedSearchPage, 1273 AdvancedSearchPage as default 1274};
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.