1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/MidiEngine-2SYF-_aI.js","assets/vendor-audio-D-wTggbx.js","assets/vendor-react-Bay9Y7Lb.js","assets/vendor-D0P4XgNj.js","assets/vendor-i18n-BC7ZMXWQ.js","assets/vendor-DqKRdbjz.css","assets/vendor-spessasynth-DH8n6o_V.js"])))=>i.map(i=>d[i]); 2import { _ as __vitePreload } from "./vendor-react-three-BCww3P1x.js"; 3import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 4import { u as useSettings, a as api } from "./index-w5lXls15.js"; 5import { g as getActionCost } from "./credit-rules-8PgauDOD.js"; 6import { W as Waveform } from "./Waveform-BgnMyg5E.js"; 7import { A as AdSlot } from "./AdSlot-BEBlpA_I.js"; 8import { C as CreditsExhaustedModal } from "./CreditsExhaustedModal-DRhMaBTs.js"; 9import { u as useTranslation } from "./vendor-i18n-BC7ZMXWQ.js"; 10import { b3 as FileHeadphone, b2 as Upload, db as Music2, b_ as Pause, bV as Play, cq as Settings2, ck as Layers, bU as Volume2, cg as Activity, dh as SlidersVertical, bq as Coins, cM as RefreshCw, a_ as CircleCheck, d1 as Download, b1 as CircleAlert } from "./vendor-D0P4XgNj.js"; 11import "./vendor-framer-B-4FZk1Z.js"; 12const ConverterPage = ({ currentUser, onAuthRequested, onPublish }) => { 13 const { t } = useTranslation(); 14 const { formatNumber } = useSettings(); 15 const [file, setFile] = reactExports.useState(null); 16 const [jobId, setJobId] = reactExports.useState(null); 17 const [status, setStatus] = reactExports.useState("idle"); 18 const [errorMsg, setErrorMsg] = reactExports.useState(""); 19 const [isCreditsExhaustedModalOpen, setIsCreditsExhaustedModalOpen] = reactExports.useState(false); 20 const [result, setResult] = reactExports.useState(null); 21 const [isPlaying, setIsPlaying] = reactExports.useState(false); 22 const [currentTime, setCurrentTime] = reactExports.useState(0); 23 const [duration, setDuration] = reactExports.useState(0); 24 const fileInputRef = reactExports.useRef(null); 25 const audioRef = reactExports.useRef(null); 26 const pollingInterval = reactExports.useRef(null); 27 const shouldClearFileOnPopRef = reactExports.useRef(false); 28 const [logs, setLogs] = reactExports.useState(""); 29 const logContainerRef = reactExports.useRef(null); 30 const [creditCost, setCreditCost] = reactExports.useState(5); 31 const [extraSfCost, setExtraSfCost] = reactExports.useState(2); 32 const [wavSurchargeCost, setWavSurchargeCost] = reactExports.useState(10); 33 const [extraFormatCost, setExtraFormatCost] = reactExports.useState(2); 34 const [progressMsg, setProgressMsg] = reactExports.useState(""); 35 const [percent, setPercent] = reactExports.useState(null); 36 const [isBuyingWav, setIsBuyingWav] = reactExports.useState(false); 37 const [soundfonts, setSoundfonts] = reactExports.useState([]); 38 const [tempLifetimeHours, setTempLifetimeHours] = reactExports.useState(12); 39 const [guestUsage, setGuestUsage] = reactExports.useState(null); 40 const [freeCredits, setFreeCredits] = reactExports.useState(25); 41 const [activeOutputIdx, setActiveOutputIdx] = reactExports.useState(0); 42 const [mixingSettings, setMixingSettings] = reactExports.useState({ 43 soundfont: "", 44 soundfonts: [], 45 reverb: 0.2, 46 chorus: 0, 47 // Aligned with site standard processing 48 gain: 1, 49 format: "mp3", 50 formats: ["mp3"], 51 exportStems: false, 52 mastering: true 53 }); 54 const [midiBuffer, setMidiBuffer] = reactExports.useState(null); 55 const [isMidiPlaying, setIsMidiPlaying] = reactExports.useState(false); 56 const [midiCurrentTime, setMidiCurrentTime] = reactExports.useState(0); 57 const [midiDuration, setMidiDuration] = reactExports.useState(0); 58 const [isMidiLoading, setIsMidiLoading] = reactExports.useState(false); 59 const midiEngineRef = reactExports.useRef(null); 60 const audioCtxRef = reactExports.useRef(null); 61 const midiTimerRef = reactExports.useRef(null); 62 const N = Math.max(1, mixingSettings.soundfonts.length); 63 const soundfontsCost = extraSfCost * (N - 1); 64 const hasWav = mixingSettings.formats.some((f) => f.toLowerCase() === "wav"); 65 const wavSurchargeTotal = hasWav ? wavSurchargeCost * N : 0; 66 let extraFormatsCost = 0; 67 const extraFormats = mixingSettings.formats.slice(1); 68 for (const fmt of extraFormats) { 69 if (fmt.toLowerCase() !== "wav") { 70 extraFormatsCost += extraFormatCost; 71 } 72 } 73 const currentCost = creditCost + soundfontsCost + wavSurchargeTotal + extraFormatsCost * N; 74 const stopMidiPlayback = () => { 75 if (midiTimerRef.current) { 76 clearInterval(midiTimerRef.current); 77 midiTimerRef.current = null; 78 } 79 if (midiEngineRef.current) {
80 try { 81 midiEngineRef.current.stop(); 82 midiEngineRef.current.destroy(); 83 } catch (e) { 84 console.warn(e); 85 } 86 midiEngineRef.current = null; 87 } 88 setIsMidiPlaying(false); 89 setMidiCurrentTime(0); 90 }; 91 const toggleMidiPlayback = async () => { 92 if (!midiBuffer) return; 93 if (isMidiPlaying) { 94 if (midiEngineRef.current) { 95 midiEngineRef.current.pause(); 96 } 97 setIsMidiPlaying(false); 98 if (midiTimerRef.current) { 99 clearInterval(midiTimerRef.current); 100 midiTimerRef.current = null; 101 } 102 } else { 103 setIsPlaying(false); 104 setIsMidiLoading(true); 105 try { 106 if (!audioCtxRef.current) { 107 audioCtxRef.current = new (window.AudioContext || window.webkitAudioContext)(); 108 } 109 if (audioCtxRef.current.state === "suspended") { 110 await audioCtxRef.current.resume(); 111 } 112 if (!midiEngineRef.current) { 113 const sfPath = mixingSettings.soundfonts[0] || mixingSettings.soundfont; 114 const sfUrl = `/storage/soundfonts/${sfPath}`; 115 const { MidiEngine } = await __vitePreload(async () => { 116 const { MidiEngine: MidiEngine2 } = await import("./MidiEngine-2SYF-_aI.js"); 117 return { MidiEngine: MidiEngine2 }; 118 }, true ? __vite__mapDeps([0,1,2,3,4,5,6]) : void 0); 119 midiEngineRef.current = new MidiEngine(audioCtxRef.current, audioCtxRef.current.destination, sfUrl); 120 await midiEngineRef.current.initialize(); 121 midiEngineRef.current.loadMidi(midiBuffer); 122 } 123 await midiEngineRef.current.play(); 124 setIsMidiPlaying(true); 125 setMidiDuration(midiEngineRef.current.getDuration() || 0); 126 midiTimerRef.current = setInterval(() => { 127 if (midiEngineRef.current) { 128 const cur = midiEngineRef.current.getCurrentTime(); 129 setMidiCurrentTime(cur); 130 if (cur >= midiEngineRef.current.getDuration() - 0.2) { 131 stopMidiPlayback(); 132 } 133 } 134 }, 200); 135 } catch (err) { 136 console.error("Midi playback failed:", err); 137 alert("Failed to initialize client synth: " + err.message); 138 stopMidiPlayback(); 139 } finally { 140 setIsMidiLoading(false); 141 } 142 } 143 }; 144 const handleMidiSeek = (time) => { 145 if (midiEngineRef.current) { 146 midiEngineRef.current.seek(time); 147 setMidiCurrentTime(time); 148 } 149 }; 150 const handleSoundfontsChange = (newSfs) => { 151 stopMidiPlayback(); 152 setMixingSettings((prev) => ({ 153 ...prev, 154 soundfonts: newSfs, 155 soundfont: newSfs[0] || "" 156 })); 157 }; 158 reactExports.useEffect(() => { 159 if (file) { 160 const reader = new FileReader(); 161 reader.onload = (e) => { 162 var _a; 163 if ((_a = e.target) == null ? void 0 : _a.result) { 164 setMidiBuffer(e.target.result); 165 } 166 }; 167 reader.readAsArrayBuffer(file); 168 } else { 169 setMidiBuffer(null); 170 stopMidiPlayback(); 171 } 172 return () => { 173 stopMidiPlayback(); 174 }; 175 }, [file]); 176 reactExports.useEffect(() => { 177 const pageTitle = t("converter.title", "MIDI Converter"); 178 document.title = `${pageTitle} | HamieNet`; 179 api.getSoundfonts().then((res) => { 180 const meta = res.metadata || {}; 181 const filteredSf = (res.files || []).filter((sf) => { 182 const isSf2 = sf.path.toLowerCase().endsWith(".sf2"); 183 const baseName = sf.path.replace(/\.h?sf[23]$/i, ""); 184 const folderMeta = meta[baseName] || {}; 185 const formatMeta = meta[sf.path] || {}; 186 const combinedMeta = { ...folderMeta, ...formatMeta }; 187 return isSf2 && combinedMeta.visibleConverter !== false; 188 }).map((sf) => { 189 const baseName = sf.path.replace(/\.h?sf[23]$/i, ""); 190 const folderMeta = meta[baseName] || {}; 191 const formatMeta = meta[sf.path] || {}; 192 const combinedMeta = { ...folderMeta, ...formatMeta }; 193 return { 194 ...sf, 195 displayName: folderMeta.name || combinedMeta.name || baseName.replace(/_/g, " ") 196 }; 197 }).sort((a, b) => (a.displayName || a.path).localeCompare(b.displayName || b.path, void 0, { sensitivity: "base", numeric: true })); 198 setSoundfonts(filteredSf); 199 const defaultSf = res.defaultConverterSf || res.active; 200 if (defaultSf && defaultSf.toLowerCase().endsWith(".sf2")) { 201 setMixingSettings((prev) => ({ 202 ...prev, 203 soundfont: prev.soundfont || defaultSf, 204 soundfonts: prev.soundfonts.length > 0 ? prev.soundfonts : [defaultSf] 205 })); 206 } else if (filteredSf.length > 0) { 207 setMixingSettings((prev) => ({ 208 ...prev, 209 soundfont: prev.soundfont || filteredSf[0].path, 210 soundfonts: prev.soundfonts.length > 0 ? prev.soundfonts : [filteredSf[0].path] 211 })); 212 } 213 }).catch((e) => console.warn("Failed to fetch soundfonts", e)); 214 if (!currentUser) { 215 fetch("/api/convert/guest-usage").then((r) => r.json()).then((data) => { 216 if (data && data.success) { 217 setGuestUsage({ 218 limit: data.limit, 219 spent: data.spent, 220 cost: data.cost 221 }); 222 setCreditCost(data.cost); 223 } 224 }).catch((e) => console.warn("Failed to fetch guest usage", e)); 225 } 226 api.getSystemSetting("credit_config").then((res) => { 227 var _a; 228 const config = res == null ? void 0 : res.value; 229 if (config) { 230 setCreditCost(getActionCost("conversion", config)); 231 setExtraSfCost(getActionCost("conversion_extra_sf", config)); 232 setWavSurchargeCost(getActionCost("conversion_wav_surcharge", config)); 233 setExtraFormatCost(getActionCost("conversion_extra_format", config)); 234 const freeTier = (_a = config.allotments) == null ? void 0 : _a.find((a) => a.id === "free"); 235 if (freeTier == null ? void 0 : freeTier.base) { 236 setFreeCredits(freeTier.base); 237 } 238 } 239 }).catch((e) => console.warn("Failed to fetch credit config", e)); 240 api.getSystemSetting("converter_config").then((res) => { 241 if ((res == null ? void 0 : res.value) && typeof res.value === "object") { 242 const val = res.value.tempFilesLifetimeHours; 243 if (val !== void 0) { 244 setTempLifetimeHours(Number(val)); 245 } 246 } 247 }).catch((e) => console.warn("Failed to fetch converter config", e)); 248 return () => { 249 document.title = "HamieNet"; 250 stopMidiPlayback(); 251 if (audioCtxRef.current) { 252 audioCtxRef.current.close().catch(() => { 253 }); 254 } 255 }; 256 }, [currentUser]); 257 const resetConverter = (clearFile = true) => { 258 var _a; 259 setIsPlaying(false); 260 stopMidiPlayback(); 261 if ((_a = window.history.state) == null ? void 0 : _a.converterActive) { 262 shouldClearFileOnPopRef.current = clearFile; 263 window.history.back(); 264 } else { 265 setStatus("idle"); 266 if (clearFile) { 267 setFile(null); 268 } 269 setJobId(null); 270 setResult(null); 271 setErrorMsg(""); 272 } 273 }; 274 reactExports.useEffect(() => { 275 const handlePopState = (e) => { 276 if (status !== "idle" && (!e.state || !e.state.converterActive)) { 277 setStatus("idle"); 278 if (shouldClearFileOnPopRef.current) { 279 setFile(null); 280 } 281 shouldClearFileOnPopRef.current = false;
282 setJobId(null); 283 setResult(null); 284 setErrorMsg(""); 285 setIsPlaying(false); 286 stopMidiPlayback(); 287 } 288 }; 289 window.addEventListener("popstate", handlePopState); 290 return () => window.removeEventListener("popstate", handlePopState); 291 }, [status]); 292 reactExports.useEffect(() => { 293 if (logContainerRef.current) { 294 logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight; 295 } 296 }, [logs]); 297 const formatTime = (secs) => { 298 if (isNaN(secs) || !isFinite(secs)) return "0:00"; 299 const m = Math.floor(secs / 60); 300 const s = Math.floor(secs % 60); 301 return `${m}:${s < 10 ? "0" : ""}${s}`; 302 }; 303 const handleFileSelect = (e) => { 304 if (e.target.files && e.target.files[0]) { 305 setFile(e.target.files[0]); 306 setStatus("idle"); 307 setResult(null); 308 setJobId(null); 309 } 310 }; 311 const handleDrop = (e) => { 312 e.preventDefault(); 313 if (e.dataTransfer.files && e.dataTransfer.files[0]) { 314 setFile(e.dataTransfer.files[0]); 315 setStatus("idle"); 316 setResult(null); 317 setJobId(null); 318 } 319 }; 320 const handleBuyWav = async () => { 321 var _a; 322 if (!result || !jobId) return; 323 const isGuest = !currentUser; 324 const confirmMsg = isGuest ? t("converter.buyWavGuestConfirm", `Downloading the WAV file will cost ${wavSurchargeCost} credits. Continue?`) : t("converter.buyWavConfirm", `Downloading the WAV file will cost ${wavSurchargeCost} credits. Your current balance is ${formatNumber(currentUser.credits || 0)}. Continue?`); 325 if (!confirm(confirmMsg)) { 326 return; 327 } 328 setIsBuyingWav(true); 329 try { 330 const activeOutput = ((_a = result.outputs) == null ? void 0 : _a[activeOutputIdx]) || { 331 soundfont: mixingSettings.soundfont 332 }; 333 const sf = activeOutput.soundfont || mixingSettings.soundfont; 334 const res = await fetch("/api/convert/buy-wav", { 335 method: "POST", 336 headers: { 337 "Content-Type": "application/json" 338 }, 339 body: JSON.stringify({ 340 jobId, 341 soundfont: sf 342 }) 343 }); 344 const data = await res.json(); 345 if (res.ok && data && data.success) { 346 if (currentUser && typeof data.newCredits === "number") { 347 currentUser.credits = data.newCredits; 348 } 349 const newOutput = { 350 format: "wav", 351 path: data.path, 352 soundfont: sf, 353 waveform: [] 354 }; 355 setResult((prev) => { 356 if (!prev) return null; 357 const nextOutputs = [...prev.outputs || []]; 358 if (!nextOutputs.some((out) => out.format.toLowerCase() === "wav" && out.soundfont === sf)) { 359 nextOutputs.push(newOutput); 360 } 361 return { 362 ...prev, 363 outputs: nextOutputs 364 }; 365 }); 366 const link = document.createElement("a"); 367 link.href = data.path; 368 link.download = `${result.title || "audio"}.wav`; 369 document.body.appendChild(link); 370 link.click(); 371 document.body.removeChild(link); 372 } else { 373 alert(data.error || "Failed to purchase WAV file."); 374 } 375 } catch (e) { 376 alert(e.message || "An error occurred during purchase."); 377 } finally { 378 setIsBuyingWav(false); 379 } 380 }; 381 const handleUpload = async () => { 382 if (!file) return; 383 const isGuest = !currentUser; 384 if (isGuest) { 385 const limit = (guestUsage == null ? void 0 : guestUsage.limit) || 15; 386 const spent = (guestUsage == null ? void 0 : guestUsage.spent) || 0; 387 const available = Math.max(0, limit - spent); 388 if (available < currentCost) { 389 setIsCreditsExhaustedModalOpen(true); 390 return; 391 } 392 } 393 const confirmMsg = isGuest ? t("converter.guestConfirm", `As a guest, this conversion costs ${currentCost} credits. Continue?`) : t("converter.creditConfirm", `This conversion will cost ${currentCost} credits. Your current balance is ${formatNumber(currentUser.credits || 0)}. Continue?`); 394 if (!confirm(confirmMsg)) { 395 return; 396 } 397 stopMidiPlayback(); 398 setIsPlaying(false); 399 setStatus("uploading"); 400 try { 401 window.history.pushState({ ...window.history.state, converterActive: true }, ""); 402 } catch (e) { 403 console.warn("History pushState in ConverterPage blocked:", e); 404 } 405 setErrorMsg(""); 406 setPercent(0);
407 setLogs(""); 408 const formData = new FormData(); 409 formData.append("midiFile", file); 410 formData.append("title", file.name.replace(".mid", "")); 411 formData.append("mixingSettings", JSON.stringify(mixingSettings)); 412 try { 413 const res = await fetch("/api/convert", { 414 method: "POST", 415 body: formData 416 }); 417 const data = await res.json(); 418 if (res.ok && data && data.track) { 419 if (currentUser && typeof data.newCredits === "number") { 420 currentUser.credits = data.newCredits; 421 } 422 setStatus((prev) => { 423 if (prev === "uploading") { 424 setJobId(data.track.id); 425 return "processing"; 426 } 427 return prev; 428 }); 429 } else { 430 setStatus((prev) => { 431 if (prev === "uploading") { 432 if (res.status === 403 && isGuest) { 433 setErrorMsg(t("converter.guestLimitError", "Daily guest conversion limit reached (5/day). Please sign in for higher limits.")); 434 } else { 435 setErrorMsg(data.error || "Upload failed to initialize."); 436 } 437 return "error"; 438 } 439 return prev; 440 }); 441 } 442 } catch (e) { 443 setStatus((prev) => { 444 if (prev === "uploading") { 445 setErrorMsg(e.message || "An unexpected error occurred."); 446 return "error"; 447 } 448 return prev; 449 }); 450 } 451 }; 452 reactExports.useEffect(() => { 453 if (status === "processing" && jobId) { 454 pollingInterval.current = setInterval(async () => { 455 try { 456 if (jobId.toString().startsWith("temp_")) { 457 const res = await fetch(`/api/convert/status/${jobId}`); 458 if (!res.ok) { 459 throw new Error("Failed to fetch status"); 460 } 461 const track = await res.json(); 462 if (track) { 463 if (track.logs !== void 0 && track.logs !== null) { 464 setLogs(track.logs); 465 } 466 if (track.status === "completed") { 467 setStatus((prev) => { 468 if (prev === "processing") { 469 setResult(track); 470 setPercent(100); 471 setIsPlaying(false); 472 return "completed"; 473 } 474 return prev; 475 }); 476 clearInterval(pollingInterval.current); 477 } else if (track.status === "error" || track.status === "rejected" || track.status === "suspended" || track.status === "paused") { 478 setStatus((prev) => { 479 if (prev === "processing") { 480 setErrorMsg("File processing failed on the server."); 481 return "error"; 482 } 483 return prev; 484 }); 485 clearInterval(pollingInterval.current); 486 } else { 487 setStatus((prev) => { 488 if (prev === "processing") { 489 if (track.status !== "pending" && track.status !== "processing") { 490 setProgressMsg(track.status); 491 } 492 if (typeof track.percent === "number") { 493 setPercent(track.percent); 494 } 495 } 496 return prev; 497 }); 498 } 499 } 500 } else { 501 try { 502 const track = await api.getTrack(jobId); 503 if (track) { 504 if (track.status === "completed") { 505 setStatus((prev) => { 506 if (prev === "processing") { 507 setResult(track); 508 setPercent(100); 509 setIsPlaying(false); 510 return "completed"; 511 } 512 return prev; 513 }); 514 clearInterval(pollingInterval.current); 515 } else if (track.status === "rejected" || track.status === "suspended" || track.status === "paused") { 516 setStatus((prev) => { 517 if (prev === "processing") { 518 setErrorMsg("File processing failed on the server."); 519 return "error"; 520 } 521 return prev; 522 }); 523 clearInterval(pollingInterval.current); 524 } else { 525 setStatus((prev) => { 526 if (prev === "processing") { 527 if (track.status !== "pending" && track.status !== "processing") { 528 setProgressMsg(track.status); 529 } 530 if (typeof track.percent === "number") { 531 setPercent(track.percent); 532 } 533 } 534 return prev; 535 }); 536 } 537 } 538 } catch (err) { 539 if (err.message.includes("404") || err.message.includes("Not Found") || err.message.includes("available")) { 540 setStatus((prev) => { 541 if (prev === "processing") { 542 setErrorMsg(err.message); 543 return "error"; 544 } 545 return prev; 546 }); 547 clearInterval(pollingInterval.current); 548 } 549 } 550 } 551 } catch (e) { 552 } 553 }, 2e3); 554 } 555 return () => clearInterval(pollingInterval.current); 556 }, [status, jobId]); 557 reactExports.useEffect(() => { 558 var _a; 559 const activeOutput = ((_a = result == null ? void 0 : result.outputs) == null ? void 0 : _a[activeOutputIdx]) || { 560 path: result == null ? void 0 : result.mp3Path, 561 waveform: result == null ? void 0 : result.waveform, 562 soundfont: mixingSettings.soundfont, 563 format: mixingSettings.format 564 }; 565 const activePath = activeOutput.path; 566 if (!audioRef.current || !activePath) return; 567 if (audioRef.current.src !== activePath) { 568 audioRef.current.src = activePath; 569 audioRef.current.load(); 570 } 571 if (isPlaying) { 572 audioRef.current.play().catch((e) => { 573 console.error("Playback failed:", e); 574 setIsPlaying(false); 575 }); 576 } else { 577 audioRef.current.pause(); 578 } 579 }, [isPlaying, result == null ? void 0 : result.outputs, activeOutputIdx, result == null ? void 0 : result.mp3Path, mixingSettings.soundfont, mixingSettings.format]); 580 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 md:p-12 max-w-4xl mx-auto flex flex-col items-center justify-center min-h-[80vh] animate-in fade-in", children: [
581 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center mb-10", children: [ 582 /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-4xl font-bold text-zinc-900 dark:text-white mb-2 flex items-center justify-center gap-3 flex-wrap", children: [ 583 /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { className: "text-indigo-500", size: 40 }), 584 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("converter.title", "MIDI Converter") }), 585 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-amber-500/10 text-amber-500 border border-amber-500/30", children: "beta" }) 586 ] }), 587 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 dark:text-zinc-400", children: t("converter.desc", "High-quality audio rendering using FluidSynth. Upload your .mid file and select soundfonts to generate premium-quality MP3, Opus, or WAV files in seconds.") }), 588 tempLifetimeHours && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 text-zinc-400 dark:text-zinc-500 text-xs font-semibold animate-in fade-in duration-700", children: t("converter.retentionNotice", "Note: Converted temporary files are automatically deleted after {{hours}} hours.", { hours: tempLifetimeHours }) }) 589 ] }), 590 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-xl bg-white dark:bg-zinc-900 rounded-2xl shadow-xl border border-zinc-200 dark:border-zinc-800 overflow-hidden relative", children: [ 591 status === "idle" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
592 /* @__PURE__ */ jsxRuntimeExports.jsxs( 593 "div", 594 { 595 className: "p-10 flex flex-col items-center justify-center border-2 border-dashed border-zinc-300 dark:border-zinc-700 m-4 mb-2 rounded-xl cursor-pointer hover:bg-zinc-50 dark:hover:bg-zinc-800/50 transition-colors", 596 onDragOver: (e) => e.preventDefault(), 597 onDrop: handleDrop, 598 onClick: () => { 599 var _a; 600 return (_a = fileInputRef.current) == null ? void 0 : _a.click(); 601 }, 602 children: [ 603 /* @__PURE__ */ jsxRuntimeExports.jsx("input", { type: "file", ref: fileInputRef, className: "hidden", accept: ".mid,.midi", onChange: handleFileSelect }), 604 file ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2 animate-in zoom-in-95", children: [ 605 /* @__PURE__ */ jsxRuntimeExports.jsx(FileHeadphone, { size: 40, className: "text-indigo-500" }), 606 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-lg text-zinc-900 dark:text-white", children: file.name }),
607 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-zinc-500", children: [ 608 (file.size / 1024).toFixed(1), 609 " KB ⢠", 610 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: (e) => { 611 e.stopPropagation(); 612 setFile(null); 613 }, className: "text-indigo-500 hover:underline", children: "Change File" }) 614 ] }) 615 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 616 /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { size: 48, className: "text-zinc-300 dark:text-zinc-600 mb-4" }), 617 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-lg font-medium text-zinc-700 dark:text-zinc-300", children: t("converter.dragDrop", "Drag & Drop MIDI file") }), 618 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 mt-2", children: t("converter.browse", "or click to browse") }) 619 ] }) 620 ] 621 } 622 ), 623 file && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-6 pb-6 pt-2 animate-in slide-in-from-top-4 duration-500 text-left", children: [
624 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-800/50 rounded-xl border border-zinc-200 dark:border-zinc-800 p-5 mb-5 text-left", children: [ 625 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white mb-4 flex items-center gap-2", children: [ 626 /* @__PURE__ */ jsxRuntimeExports.jsx(Music2, { size: 16, className: "text-indigo-500" }), 627 " ", 628 t("converter.previewOriginal", "In-Browser Preview (Original MIDI)") 629 ] }), 630 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [ 631 /* @__PURE__ */ jsxRuntimeExports.jsx( 632 "button", 633 { 634 onClick: toggleMidiPlayback, 635 disabled: isMidiLoading, 636 className: "w-12 h-12 rounded-full bg-indigo-600 hover:bg-indigo-700 text-white flex items-center justify-center shrink-0 transition-colors disabled:opacity-55",
637 children: isMidiLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin" }) : isMidiPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 20, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 20, fill: "currentColor", className: "ml-1" }) 638 } 639 ), 640 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex items-center gap-3", children: [ 641 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-mono text-zinc-500 dark:text-zinc-400 select-none w-10 text-right", children: formatTime(midiCurrentTime) }), 642 /* @__PURE__ */ jsxRuntimeExports.jsx( 643 "input", 644 { 645 type: "range", 646 min: 0, 647 max: midiDuration || 100, 648 step: 0.1, 649 value: midiCurrentTime, 650 onChange: (e) => handleMidiSeek(parseFloat(e.target.value)), 651 className: "flex-1 h-1.5 bg-zinc-300 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-indigo-600 focus:outline-none" 652 } 653 ),
654 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-mono text-zinc-500 dark:text-zinc-400 select-none w-10", children: formatTime(midiDuration) }) 655 ] }) 656 ] }) 657 ] }), 658 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-50 dark:bg-zinc-800/50 rounded-xl border border-zinc-200 dark:border-zinc-800 p-5", children: [ 659 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white mb-4 flex items-center gap-2", children: [ 660 /* @__PURE__ */ jsxRuntimeExports.jsx(Settings2, { size: 16, className: "text-indigo-500" }), 661 " ", 662 t("converter.mixingDesk", "Mixing Desk") 663 ] }), 664 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [ 665 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 text-left", children: [ 666 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
667 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[10px] uppercase font-bold text-zinc-400 mb-2.5 block flex items-center justify-between", children: [ 668 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("converter.soundfonts", "SoundFonts (select one or more)") }), 669 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] font-black text-indigo-500 lowercase bg-indigo-50 dark:bg-indigo-950/40 px-1.5 py-0.5 rounded", children: [ 670 "+", 671 t("converter.sfExtraCost", "{{cost}} credits / additional SF", { cost: extraSfCost }) 672 ] }) 673 ] }), 674 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border border-zinc-200 dark:border-zinc-700 rounded-xl bg-white dark:bg-zinc-900 max-h-40 overflow-y-auto p-3.5 space-y-2.5 custom-scrollbar shadow-inner text-left animate-in fade-in duration-300", children: soundfonts.map((sf) => { 675 const isChecked = mixingSettings.soundfonts.includes(sf.path); 676 return /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "flex items-center gap-3 cursor-pointer group select-none", children: [
677 /* @__PURE__ */ jsxRuntimeExports.jsx( 678 "input", 679 { 680 type: "checkbox", 681 checked: isChecked, 682 onChange: () => { 683 let nextSfs = [...mixingSettings.soundfonts]; 684 if (isChecked) { 685 if (nextSfs.length > 1) { 686 nextSfs = nextSfs.filter((p) => p !== sf.path); 687 } 688 } else { 689 nextSfs.push(sf.path); 690 } 691 handleSoundfontsChange(nextSfs); 692 }, 693 className: "w-4 h-4 rounded text-indigo-600 border-zinc-300 focus:ring-indigo-500" 694 } 695 ), 696 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-xs font-semibold transition-colors ${isChecked ? "text-indigo-600 dark:text-indigo-400" : "text-zinc-600 dark:text-zinc-400 group-hover:text-zinc-900 dark:group-hover:text-zinc-200"}`, children: sf.displayName }) 697 ] }, sf.path); 698 }) }) 699 ] }),
700 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 701 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[10px] uppercase font-bold text-zinc-400 mb-2.5 block flex items-center justify-between", children: [ 702 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t("converter.formats", "Formats (select one or more)") }), 703 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[9px] font-black text-indigo-500 lowercase bg-indigo-50 dark:bg-indigo-950/40 px-1.5 py-0.5 rounded", children: [ 704 "+", 705 t("converter.fmtExtraCost", "{{cost}} credit / extra format, WAV = {{wavCost}}", { cost: extraFormatCost, wavCost: wavSurchargeCost }) 706 ] }) 707 ] }), 708 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-3 gap-2 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded-xl p-2.5 shadow-inner", children: ["mp3", "opus", "wav", "flac", "ogg", "aac"].map((f) => { 709 const isChecked = mixingSettings.formats.includes(f); 710 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 711 "button", 712 { 713 type: "button", 714 onClick: () => { 715 let nextFmts = [...mixingSettings.formats]; 716 if (isChecked) { 717 if (nextFmts.length > 1) { 718 nextFmts = nextFmts.filter((fmt) => fmt !== f); 719 } 720 } else { 721 nextFmts.push(f); 722 } 723 setMixingSettings((prev) => ({ 724 ...prev, 725 formats: nextFmts, 726 format: nextFmts[0] || "mp3" 727 })); 728 }, 729 className: `py-2 text-[10px] font-bold rounded-lg uppercase transition-all flex items-center justify-center gap-1.5 ${isChecked ? "bg-indigo-500 text-white shadow" : "text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800"}`, 730 children: [
731 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `w-1.5 h-1.5 rounded-full ${isChecked ? "bg-white" : "bg-transparent"}` }), 732 f 733 ] 734 }, 735 f 736 ); 737 }) }) 738 ] }), 739 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 bg-indigo-500/5 dark:bg-indigo-500/10 p-3 rounded-lg border border-indigo-500/20", children: [ 740 /* @__PURE__ */ jsxRuntimeExports.jsx( 741 "input", 742 { 743 type: "checkbox", 744 id: "exportStems", 745 checked: mixingSettings.exportStems, 746 onChange: (e) => setMixingSettings({ ...mixingSettings, exportStems: e.target.checked }), 747 className: "w-4 h-4 rounded text-indigo-500 focus:ring-indigo-500" 748 } 749 ), 750 /* @__P
750URE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "exportStems", className: "flex flex-col cursor-pointer", children: [ 751 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-bold text-indigo-600 dark:text-indigo-400 flex items-center gap-1.5", children: [ 752 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 12 }), 753 " ", 754 t("converter.exportStems", "Export Stems") 755 ] }), 756 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-zinc-500", children: t("converter.exportStemsDesc", "Render each MIDI track as separate file") }) 757 ] }) 758 ] }) 759 ] }), 760 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [ 761 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 762 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-1.5", children: [
763 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[10px] uppercase font-bold text-zinc-400 flex items-center gap-1", children: [ 764 /* @__PURE__ */ jsxRuntimeExports.jsx(Volume2, { size: 10 }), 765 " ", 766 t("converter.gain", "Gain") 767 ] }), 768 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-mono text-zinc-500", children: [ 769 mixingSettings.gain.toFixed(1), 770 "x" 771 ] }) 772 ] }), 773 /* @__PURE__ */ jsxRuntimeExports.jsx( 774 "input", 775 { 776 type: "range", 777 min: "0", 778 max: "2", 779 step: "0.1", 780 value: mixingSettings.gain, 781 onChange: (e) => setMixingSettings({ ...mixingSettings, gain: parseFloat(e.target.value) }), 782 className: "w-full h-1.5 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-indigo-500" 783 } 784 ) 785 ] }),
786 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 787 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-1.5", children: [ 788 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[10px] uppercase font-bold text-zinc-400 flex items-center gap-1", children: [ 789 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 10 }), 790 " ", 791 t("converter.reverb", "Reverb") 792 ] }), 793 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-mono text-zinc-500", children: [ 794 Math.round(mixingSettings.reverb * 100), 795 "%" 796 ] }) 797 ] }), 798 /* @__PURE__ */ jsxRuntimeExports.jsx( 799 "input", 800 { 801 type: "range", 802 min: "0", 803 max: "1", 804 step: "0.05", 805 value: mixingSettings.reverb, 806 onChange: (e) => setMixingSettings({ ...mixingSettings, reverb: parseFloat(e.target.value) }), 807 className: "w-full h-1.5 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-indigo-500" 808 } 809 ) 810 ] }),
811 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 812 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-1.5", children: [ 813 /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-[10px] uppercase font-bold text-zinc-400 flex items-center gap-1", children: [ 814 /* @__PURE__ */ jsxRuntimeExports.jsx(SlidersVertical, { size: 10 }), 815 " ", 816 t("converter.chorus", "Chorus") 817 ] }), 818 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-mono text-zinc-500", children: [ 819 Math.round(mixingSettings.chorus * 100), 820 "%" 821 ] }) 822 ] }), 823 /* @__PURE__ */ jsxRuntimeExports.jsx( 824 "input", 825 { 826 type: "range", 827 min: "0", 828 max: "1", 829 step: "0.05", 830 value: mixingSettings.chorus, 831 onChange: (e) => setMixingSettings({ ...mixingSettings, chorus: parseFloat(e.target.value) }), 832 className: "w-full h-1.5 bg-zinc-200 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-indigo-500" 833 } 834 ) 835 ] }) 836 ] }) 837 ] }), 838 (() => { 839 const cost = currentCost; 840 if (currentUser) { 841 const credits = currentUser.credits || 0; 842 const hasEnoughCredits = credits >= cost; 843 const remaining = Math.max(0, credits - cost); 844 const maxScale = Math.max(credits, cost * 2.5, 20); 845 const remainingPercent = remaining / maxScale * 100; 846 const costPercent = Math.min(credits, cost) / maxScale * 100; 847 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-8 bg-white dark:bg-zinc-950/30 border border-zinc-200/80 dark:border-zinc-800 rounded-2xl p-5 shadow-lg animate-in fade-in slide-in-from-top-4 duration-500", children: [
848 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-2", children: [ 849 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] font-black uppercase tracking-wider text-indigo-500 dark:text-indigo-400 flex items-center gap-1.5", children: [ 850 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 12, className: "animate-pulse" }), 851 t("converter.energyFuel", "Your Credit Fuel") 852 ] }), 853 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-black font-mono text-zinc-800 dark:text-zinc-100 bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 rounded-full border border-zinc-200/50 dark:border-zinc-700/50", children: [ 854 formatNumber(credits), 855 " ", 856 t("converter.creditsUnit", "Credits") 857 ] }) 858 ] }), 859 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full bg-zinc-200 dark:bg-zinc-800/80 h-4 rounded-full overflow-hidden flex relative border border-zinc-300/40 dark:border-zinc-700/40 shadow-inner", children: [ 860 remainingPercent > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx( 861 "div", 862 { 863 className: "h-full bg-gradient-to-r from-emerald-500 via-teal-500 to-cyan-500 transition-all duration-1000 relative shadow-[0_0_12px_rgba(16,185,129,0.3)]", 864 style: { width: `${remainingPercent}%` }, 865 children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full animate-shimmer", style: { animationDuration: "3s", animationIterationCount: "infinite" } }) 866 } 867 ), 868 costPercent > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx( 869 "div", 870 { 871 className: "h-full bg-amber-500 transition-all duration-1000 relative overflow-hidden border-l border-white/20", 872 style: { 873 width: `${costPercent}%`, 874 backgroundImage: "linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent)", 875 backgroundSize: "12px 12px", 876 animation: "shimmer 1.5s infinite linear, pulse 2s infinite" 877 }, 878 title: `-${cost} Credits for this conversion`, 879 children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute inset-0 bg-amber-400 opacity-20 animate-pulse" }) 880 } 881 ) 882 ] }),
883 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mt-2.5 text-[10px] font-bold text-zinc-400 dark:text-zinc-500", children: [ 884 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [ 885 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-emerald-500 inline-block" }), 886 t("converter.remainingFuel", "Post-Conversion:"), 887 " ", 888 formatNumber(remaining) 889 ] }), 890 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [ 891 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-amber-500 animate-pulse inline-block" }), 892 t("converter.deductionAmount", "Cost:"), 893 " -", 894 cost 895 ] }) 896 ] }),
897 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-center mt-3 font-semibold px-3 py-1.5 rounded-xl border bg-zinc-100/50 dark:bg-zinc-800/30", children: hasEnoughCredits ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-600 dark:text-emerald-400", children: [ 898 "ð ", 899 t("converter.fuelReady", "Energy cells fully charged. Ready!") 900 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-red-500 dark:text-red-400", children: [ 901 "â ï¸ ", 902 t("converter.fuelLow", "Low fuel. Please acquire credits.") 903 ] }) }) 904 ] }); 905 } else { 906 const limit = (guestUsage == null ? void 0 : guestUsage.limit) || 15; 907 const spent = (guestUsage == null ? void 0 : guestUsage.spent) || 0; 908 const available = Math.max(0, limit - spent); 909 const remaining = Math.max(0, available - cost); 910 const remainingPercent = remaining / limit * 100; 911 const costPercent = Math.min(available, cost) / limit * 100; 912 const spentPercent = spent / limit * 100; 913 const maxConversions = Math.floor(limit / cost); 914 const leftConversions = Math.floor(available / cost); 915 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-8 space-y-4", children: [
916 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-950/30 border border-zinc-200/80 dark:border-zinc-800 rounded-2xl p-5 shadow-lg animate-in fade-in slide-in-from-top-4 duration-500", children: [ 917 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-2", children: [ 918 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] font-black uppercase tracking-wider text-indigo-500 dark:text-indigo-400 flex items-center gap-1.5", children: [ 919 /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { size: 12, className: "animate-pulse" }), 920 t("converter.guestAllowance", "Guest Daily Credit Allowance") 921 ] }), 922 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-black font-mono text-zinc-800 dark:text-zinc-100 bg-zinc-100 dark:bg-zinc-800 px-2.5 py-0.5 rounded-full border border-zinc-200/50 dark:border-zinc-700/50", children: t("converter.availableCredits", "Available: {{available}} Credits", { available }) }) 923 ] }),
924 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full bg-zinc-200 dark:bg-zinc-800/80 h-4 rounded-full overflow-hidden flex relative border border-zinc-300/40 dark:border-zinc-700/40 shadow-inner", children: [ 925 remainingPercent > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx( 926 "div", 927 { 928 className: "h-full bg-gradient-to-r from-emerald-500 to-teal-500 transition-all duration-1000 relative shadow-[0_0_12px_rgba(16,185,129,0.3)]", 929 style: { width: `${remainingPercent}%` }, 930 children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full animate-shimmer", style: { animationDuration: "3s", animationIterationCount: "infinite" } }) 931 } 932 ), 933 costPercent > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx( 934 "div", 935 { 936 className: "h-full bg-amber-500 transition-all duration-1000 relative overflow-hidden border-l border-white/20", 937 style: { 938 width: `${costPercent}%`, 939 backgroundImage: "linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent)", 940 backgroundSize: "12px 12px", 941 animation: "shimmer 1.5s infinite linear, pulse 2s infinite" 942 }, 943 title: `-${cost} Credits for this conversion`, 944 children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute inset-0 bg-amber-400 opacity-20 animate-pulse" }) 945 } 946 ), 947 spentPercent > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx( 948 "div", 949 { 950 className: "h-full bg-zinc-400 dark:bg-zinc-600 transition-all duration-1000 border-l border-white/10", 951 style: { width: `${spentPercent}%` }, 952 title: `Spent ${spent} credits today` 953 } 954 ) 955 ] }),
956 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col md:flex-row justify-between items-start md:items-center mt-2.5 gap-1 md:gap-0 text-[10px] font-bold text-zinc-400 dark:text-zinc-500", children: [ 957 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [ 958 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [ 959 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-emerald-500 inline-block" }), 960 t("converter.postConversionRemaining", "Post-Conversion:"), 961 " ", 962 remaining 963 ] }), 964 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [ 965 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-amber-500 animate-pulse inline-block" }), 966 t("converter.deductionAmount", "Cost:"), 967 " -", 968 cost 969 ] }), 970 spent > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
971 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2 h-2 rounded-full bg-zinc-400 inline-block" }), 972 t("converter.spentToday", "Spent Today:"), 973 " ", 974 spent 975 ] }) 976 ] }), 977 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-indigo-500 dark:text-indigo-400 font-extrabold uppercase tracking-wider bg-indigo-50 dark:bg-indigo-950/40 px-1.5 py-0.5 rounded text-[8px]", children: [ 978 leftConversions, 979 " / ", 980 maxConversions, 981 " ", 982 t("converter.conversionsRemaining", "left") 983 ] }) 984 ] }) 985 ] }), 986 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-gradient-to-r from-indigo-500/10 via-purple-500/10 to-indigo-500/5 border border-indigo-200/50 dark:border-indigo-800/30 rounded-2xl p-5 shadow-lg text-center relative overflow-hidden group", children: [
987 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-0 right-0 w-32 h-32 bg-indigo-500/10 rounded-full blur-2xl -mr-10 -mt-10 group-hover:bg-indigo-500/20 transition-all duration-700" }), 988 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] font-black uppercase tracking-wider text-indigo-500 dark:text-indigo-400 block mb-1.5", children: [ 989 "⨠", 990 t("converter.premiumPromoTitle", "Unlock Unlimited Access"), 991 " â¨" 992 ] }), 993 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-zinc-600 dark:text-zinc-300 mb-3.5 leading-relaxed font-medium", children: t("converter.promoDesc", "Guests are limited to {{maxConversions}} free daily conversions. Sign up now to receive {{signupCredits}} FREE Credits instantly, unlock access to HQ Soundfonts, and render WAV or Opus stems!", { maxConversions, signupCredits: freeCredits }) }),
994 /* @__PURE__ */ jsxRuntimeExports.jsxs( 995 "button", 996 { 997 onClick: onAuthRequested, 998 className: "py-2 px-5 bg-indigo-600 hover:bg-indigo-700 hover:scale-[1.02] active:scale-[0.98] text-white rounded-xl text-[11px] font-extrabold transition-all shadow-md shadow-indigo-600/20 flex items-center gap-1.5 mx-auto animate-in zoom-in-95", 999 children: [ 1000 /* @__PURE__ */ jsxRuntimeExports.jsx(Coins, { size: 12 }), 1001 " ", 1002 t("converter.promoButton", "Create Free Account & Claim Credits") 1003 ] 1004 } 1005 ) 1006 ] }) 1007 ] }); 1008 } 1009 })(), 1010 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1011 "button", 1012 { 1013 onClick: handleUpload, 1014 className: "mt-8 w-full py-3.5 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl font-bold shadow-lg shadow-indigo-500/30 transition-all flex items-center justify-center gap-3 group", 1015 children: [
1016 /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { size: 20, className: "group-hover:rotate-180 transition-transform duration-500" }), 1017 t("converter.start", "Initialize Processing Engine") 1018 ] 1019 } 1020 ) 1021 ] }) 1022 ] }) 1023 ] }), 1024 (status === "uploading" || status === "processing") && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-8 flex flex-col items-center justify-center text-center w-full", children: [ 1025 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative w-16 h-16 mb-4", children: [ 1026 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 border-4 border-indigo-500/20 rounded-full" }), 1027 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin" }), 1028 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Activity, { className: "text-indigo-500 animate-pulse", size: 24 }) }) 1029 ] }),
1030 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xl font-bold text-zinc-900 dark:text-white mb-1.5", children: status === "uploading" ? t("converter.uploading", "Uploading...") : progressMsg || t("converter.rendering", "Rendering Audio...") }), 1031 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 text-xs max-w-xs mx-auto mb-4", children: status === "uploading" ? t("converter.status_uploading_desc", "Please wait while we receive your file.") : t("converter.status_rendering_desc", "Cloud workers are synthesizing your MIDI using professional grade SoundFonts.") }), 1032 status === "processing" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full max-w-md flex flex-col gap-4 mt-2", children: [ 1033 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full flex flex-col items-center gap-1.5", children: [ 1034 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-full bg-zinc-100 dark:bg-zinc-800 h-2.5 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1035 "div", 1036 { 1037 className: `h-full bg-indigo-500 transition-all duration-500 ${percent === null ? "animate-progress-indeterminate" : ""}`, 1038 style: percent !== null ? { width: `${percent}%` } : void 0 1039 } 1040 ) }), 1041 percent !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-black text-indigo-500 dark:text-indigo-400", children: [ 1042 percent, 1043 "%" 1044 ] }) 1045 ] }),
1046 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full bg-zinc-950 border border-zinc-800 rounded-xl p-4 text-left shadow-inner animate-in fade-in slide-in-from-bottom-4 duration-500", children: [ 1047 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 border-b border-zinc-900 pb-2 mb-3", children: [ 1048 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-red-500" }), 1049 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-yellow-500" }), 1050 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-2.5 h-2.5 rounded-full bg-green-500" }), 1051 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-mono text-zinc-500 font-bold uppercase tracking-wider ml-1", children: "Conversion Engine Log" }) 1052 ] }), 1053 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1054 "div", 1055 { 1056 className: "font-mono text-[11px] text-zinc-300 h-40 overflow-y-auto space-y-1 scrollbar-thin scrollbar-thumb-zinc-800 scrollbar-track-transparent pr-1", 1057 ref: logContainerRef, 1058 children: [ 1059 logs.split("\n").filter(Boolean).map((line, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "leading-relaxed whitespace-pre-wrap", children: [
1060 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-500 select-none mr-2", children: ">" }), 1061 line 1062 ] }, idx)), 1063 !logs && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-zinc-500 italic animate-pulse", children: "Initializing pipeline workers..." }) 1064 ] 1065 } 1066 ) 1067 ] }), 1068 /* @__PURE__ */ jsxRuntimeExports.jsx(AdSlot, { type: "converterBottom", className: "my-2 p-2 border-dashed bg-indigo-500/5 border-indigo-500/10" }) 1069 ] }) 1070 ] }), 1071 status === "completed" && result && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-8 animate-in zoom-in-95", children: [ 1072 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center mb-6", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-emerald-100 dark:bg-emerald-900/30 rounded-full flex items-center justify-center text-emerald-600 dark:text-emerald-400", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { size: 32 }) }) }),
1073 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-2xl font-bold text-center text-zinc-900 dark:text-white mb-1", children: t("converter.complete", "Conversion Complete!") }), 1074 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-center text-zinc-500 mb-8", children: t("converter.ready", { title: result.title, defaultValue: `${result.title}.opus is ready.` }) }), 1075 (() => { 1076 var _a; 1077 const activeOutput = ((_a = result.outputs) == null ? void 0 : _a[activeOutputIdx]) || { 1078 path: result.mp3Path, 1079 waveform: result.waveform, 1080 soundfont: mixingSettings.soundfont, 1081 format: mixingSettings.format 1082 }; 1083 return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 1084 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-zinc-100 dark:bg-zinc-800 rounded-xl p-4 mb-6 flex flex-col gap-4", children: [ 1085 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4", children: [
1086 /* @__PURE__ */ jsxRuntimeExports.jsx( 1087 "button", 1088 { 1089 onClick: () => setIsPlaying(!isPlaying), 1090 className: "w-12 h-12 rounded-full bg-indigo-600 hover:bg-indigo-700 text-white flex items-center justify-center shrink-0 transition-colors", 1091 children: isPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 20, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 20, fill: "currentColor", className: "ml-1" }) 1092 } 1093 ), 1094 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 flex items-center gap-3", children: [ 1095 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-mono text-zinc-500 dark:text-zinc-400 select-none w-10 text-right", children: formatTime(currentTime) }), 1096 /* @__PURE__ */ jsxRuntimeExports.jsx( 1097 "input", 1098 { 1099 type: "range", 1100 min: 0, 1101 max: duration || 100, 1102 step: 0.1, 1103 value: currentTime, 1104 onChange: (e) => { 1105 const newTime = parseFloat(e.target.value); 1106 setCurrentTime(newTime); 1107 if (audioRef.current) { 1108 audioRef.current.currentTime = newTime; 1109 } 1110 }, 1111 className: "flex-1 h-1.5 bg-zinc-300 dark:bg-zinc-700 rounded-lg appearance-none cursor-pointer accent-indigo-600 focus:outline-none" 1112 } 1113 ),
1114 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-mono text-zinc-500 dark:text-zinc-400 select-none w-10", children: formatTime(duration) }) 1115 ] }) 1116 ] }), 1117 activeOutput.waveform && activeOutput.waveform.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-12 w-full opacity-70 hover:opacity-100 transition-opacity cursor-pointer px-14", onClick: (e) => { 1118 if (audioRef.current && duration) { 1119 const rect = e.currentTarget.getBoundingClientRect(); 1120 const clickX = e.clientX - rect.left; 1121 const newTime = clickX / rect.width * duration; 1122 audioRef.current.currentTime = newTime; 1123 setCurrentTime(newTime); 1124 } 1125 }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Waveform, { data: activeOutput.waveform, active: true, progress: duration ? currentTime / duration : 0, className: "h-full pointer-events-none" }) }),
1126 activeOutput.path && /* @__PURE__ */ jsxRuntimeExports.jsx( 1127 "audio", 1128 { 1129 ref: audioRef, 1130 src: activeOutput.path, 1131 onEnded: () => setIsPlaying(false), 1132 onTimeUpdate: () => { 1133 if (audioRef.current) { 1134 setCurrentTime(audioRef.current.currentTime); 1135 setDuration(audioRef.current.duration); 1136 } 1137 }, 1138 onLoadedMetadata: () => { 1139 if (audioRef.current) { 1140 setDuration(audioRef.current.duration); 1141 } 1142 }, 1143 className: "hidden" 1144 } 1145 ) 1146 ] }), 1147 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-3", children: [ 1148 /* @__PURE__ */ jsxRuntimeExports.jsx( 1149 "button", 1150 { 1151 onClick: resetConverter, 1152 className: "flex-1 py-3 rounded-lg font-bold text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors", 1153 children: t("converter.convertAnother", "Convert Another") 1154 } 1155 ),
1156 /* @__PURE__ */ jsxRuntimeExports.jsxs( 1157 "a", 1158 { 1159 href: activeOutput.path || "#", 1160 download: true, 1161 className: "flex-[2] py-3 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-bold flex items-center justify-center gap-2 shadow-lg shadow-indigo-500/20 transition-all", 1162 children: [ 1163 /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { size: 20 }), 1164 " ", 1165 t("converter.download", `Download ${activeOutput.format.toUpperCase()}`) 1166 ] 1167 } 1168 ) 1169 ] }), 1170 (!result.outputs || !result.outputs.some((out) => out.format.toLowerCase() === "wav")) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-4 w-full", children: /* @__PURE__ */ jsxRuntimeExports.jsxs( 1171 "button", 1172 { 1173 onClick: handleBuyWav, 1174 disabled: isBuyingWav, 1175 className: "w-full py-2.5 bg-amber-500 hover:bg-amber-600 disabled:opacity-50 text-white rounded-lg font-bold flex items-center justify-center gap-2 shadow transition-all text-sm", 1176 children: [ 1177 isBuyingWav ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-4.5 h-4.5 border-2 border-white border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Coins, { size: 16 }), 1178 t("converter.buyWav", "Download Lossless WAV (2 Credits)") 1179 ] 1180 } 1181 ) }), 1182 result.outputs && result.outputs.length > 0 && (() => { 1183 const uniqueSoundfonts = Array.from(new Set(result.outputs.map((out) => out.soundfont))); 1184 const uniqueFormats = Array.from(new Set(result.outputs.map((out) => out.format.toLowerCase()))); 1185 const isMultiOutput = uniqueSoundfonts.length > 1 || uniqueFormats.length > 1; 1186 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-8 pt-8 border-t border-zinc-100 dark:border-zinc-800 text-left", children: [
1187 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white mb-4 flex items-center gap-2 uppercase tracking-wider", children: [ 1188 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 16, className: "text-indigo-500" }), 1189 " ", 1190 t("converter.convertedOutputs", "Converted Outputs") 1191 ] }), 1192 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "overflow-x-auto rounded-xl border border-zinc-200 dark:border-zinc-800", children: isMultiOutput ? /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse", children: [ 1193 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "bg-zinc-50 dark:bg-zinc-900/80 border-b border-zinc-200 dark:border-zinc-800 text-zinc-400 dark:text-zinc-500 text-[10px] font-black uppercase tracking-wider", children: [ 1194 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 text-left", children: "SoundFont" }), 1195 uniqueFormats.map((fmt) => /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 text-center", children: fmt.toUpperCase() }, fmt)) 1196 ] }) }),
1197 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-100 dark:divide-zinc-800/60 bg-white dark:bg-zinc-900", children: uniqueSoundfonts.map((sf) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "hover:bg-zinc-50/50 dark:hover:bg-zinc-800/30 transition-colors", children: [ 1198 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 font-bold text-zinc-800 dark:text-zinc-200 text-sm", children: sf.replace(".sf2", "").replace(/_/g, " ") }), 1199 uniqueFormats.map((fmt) => { 1200 const idx = result.outputs.findIndex((out2) => out2.soundfont === sf && out2.format.toLowerCase() === fmt); 1201 if (idx === -1) { 1202 return /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-center text-zinc-400 dark:text-zinc-600", children: "-" }, fmt); 1203 } 1204 const out = result.outputs[idx]; 1205 const isCurrent = activeOutputIdx === idx; 1206 const isCurrentPlaying = isCurrent && isPlaying; 1207 return /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-center gap-2", children: [
1208 /* @__PURE__ */ jsxRuntimeExports.jsx( 1209 "button", 1210 { 1211 onClick: () => { 1212 if (isCurrent) { 1213 setIsPlaying(!isPlaying); 1214 } else { 1215 setActiveOutputIdx(idx); 1216 setIsPlaying(true); 1217 } 1218 }, 1219 className: `w-8 h-8 rounded-full flex items-center justify-center transition-all ${isCurrentPlaying ? "bg-indigo-600 text-white animate-pulse" : "bg-indigo-50 dark:bg-indigo-950/50 text-indigo-500 hover:bg-indigo-100 dark:hover:bg-indigo-950"}`, 1220 title: isCurrentPlaying ? "Pause" : "Play", 1221 children: isCurrentPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12, fill: "currentColor", className: "ml-0.5" }) 1222 } 1223 ),
1224 /* @__PURE__ */ jsxRuntimeExports.jsx( 1225 "a", 1226 { 1227 href: out.path, 1228 download: true, 1229 className: "inline-flex items-center justify-center w-8 h-8 bg-zinc-100 dark:bg-zinc-800 hover:bg-indigo-600 dark:hover:bg-indigo-600 hover:text-white dark:hover:text-white rounded-full text-zinc-600 dark:text-zinc-400 transition-all", 1230 title: "Download", 1231 children: /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { size: 12 }) 1232 } 1233 ) 1234 ] }) }, fmt); 1235 }) 1236 ] }, sf)) }) 1237 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-left border-collapse", children: [ 1238 /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "bg-zinc-50 dark:bg-zinc-900/80 border-b border-zinc-200 dark:border-zinc-800 text-zinc-400 dark:text-zinc-500 text-[10px] font-black uppercase tracking-wider", children: [
1239 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-12 text-center", children: "Play" }), 1240 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3", children: "SoundFont" }), 1241 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-24", children: "Format" }), 1242 /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "p-3 w-28 text-right", children: "Action" }) 1243 ] }) }), 1244 /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { className: "divide-y divide-zinc-100 dark:divide-zinc-800/60 bg-white dark:bg-zinc-900", children: result.outputs.map((out, idx) => { 1245 const isCurrent = activeOutputIdx === idx; 1246 const isCurrentPlaying = isCurrent && isPlaying; 1247 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 1248 "tr", 1249 { 1250 className: `transition-colors ${isCurrent ? "bg-indigo-500/5 dark:bg-indigo-950/20" : "hover:bg-zinc-50/50 dark:hover:bg-zinc-800/30"}`, 1251 children: [
1252 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1253 "button", 1254 { 1255 onClick: () => { 1256 if (isCurrent) { 1257 setIsPlaying(!isPlaying); 1258 } else { 1259 setActiveOutputIdx(idx); 1260 setIsPlaying(true); 1261 } 1262 }, 1263 className: `w-8 h-8 rounded-full flex items-center justify-center transition-all ${isCurrentPlaying ? "bg-indigo-600 text-white animate-pulse" : "bg-indigo-50 dark:bg-indigo-950/50 text-indigo-500 hover:bg-indigo-100 dark:hover:bg-indigo-950"}`, 1264 children: isCurrentPlaying ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 12, fill: "currentColor" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 12, fill: "currentColor", className: "ml-0.5" }) 1265 } 1266 ) }),
1267 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-zinc-800 dark:text-zinc-200", children: out.soundfont.replace(".sf2", "").replace(/_/g, " ") }) }), 1268 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[10px] font-black uppercase tracking-wider ${out.format.toLowerCase() === "wav" ? "bg-amber-100 dark:bg-amber-950/40 text-amber-600 dark:text-amber-400" : out.format.toLowerCase() === "mp3" ? "bg-emerald-100 dark:bg-emerald-950/40 text-emerald-600 dark:text-amber-400" : "bg-indigo-100 dark:bg-indigo-950/40 text-indigo-600 dark:text-indigo-400"}`, children: out.format }) }), 1269 /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "p-3 text-right", children: /* @__PURE__ */ jsxRuntimeExports.jsxs( 1270 "a", 1271 { 1272 href: out.path, 1273 download: true, 1274 className: "inline-flex items-center gap-1 px-3 py-1.5 bg-zinc-100 dark:bg-zinc-800 hover:bg-indigo-600 dark:hover:bg-indigo-600 hover:text-white dark:hover:text-white rounded-lg text-zinc-600 dark:text-zinc-400 font-bold transition-all text-xs", 1275 children: [
1276 /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { size: 12 }), 1277 " Download" 1278 ] 1279 } 1280 ) }) 1281 ] 1282 }, 1283 idx 1284 ); 1285 }) }) 1286 ] }) }) 1287 ] }); 1288 })() 1289 ] }); 1290 })(), 1291 !(jobId == null ? void 0 : jobId.toString().startsWith("temp_")) && status === "completed" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-4 text-center text-sm text-green-600 dark:text-green-400 font-medium", children: "Successfully Uploaded!" }), 1292 (jobId == null ? void 0 : jobId.toString().startsWith("temp_")) && status === "completed" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx( 1293 "button", 1294 { 1295 onClick: () => onPublish(file), 1296 className: "w-full py-3 bg-zinc-800 hover:bg-zinc-900 dark:bg-zinc-700 dark:hover:bg-zinc-600 text-white rounded-lg font-bold flex items-center justify-center gap-2 transition-all", 1297 children: "Uploadt MIDI to Site" 1298 } 1299 ) }), 1300 result.instruments && result.instruments.some((i) => i.stemPath) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-10 pt-8 border-t border-zinc-100 dark:border-zinc-800", children: [
1301 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-sm font-bold text-zinc-900 dark:text-white mb-4 flex items-center gap-2 uppercase tracking-wider", children: [ 1302 /* @__PURE__ */ jsxRuntimeExports.jsx(Layers, { size: 16, className: "text-indigo-500" }), 1303 " Generated Stems" 1304 ] }), 1305 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid grid-cols-1 gap-2 max-h-48 overflow-y-auto pr-2 custom-scrollbar", children: result.instruments.filter((i) => i.stemPath).map((inst, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between bg-zinc-50 dark:bg-zinc-900/50 p-3 rounded-lg border border-zinc-200 dark:border-zinc-800", children: [ 1306 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 overflow-hidden", children: [ 1307 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-8 h-8 rounded bg-zinc-200 dark:bg-zinc-800 flex items-center justify-center text-[10px] font-bold text-zinc-500 shrink-0", children: [ 1308 "#", 1309 inst.trackId 1310 ] }),
1311 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 truncate", children: inst.name }) 1312 ] }), 1313 /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: inst.stemPath, download: true, className: "text-indigo-500 hover:text-indigo-600 p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { size: 16 }) }) 1314 ] }, idx)) }) 1315 ] }) 1316 ] }), 1317 status === "error" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-10 flex flex-col items-center justify-center text-center", children: [ 1318 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-16 h-16 bg-red-100 dark:bg-red-900/30 rounded-full flex items-center justify-center text-red-500 mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { size: 32 }) }), 1319 /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-xl font-bold text-zinc-900 dark:text-white mb-2", children: t("converter.error", "Conversion Failed") }),
1320 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-zinc-500 text-sm mb-6", children: errorMsg || t("converter.error_desc", "Something went wrong during the conversion process. Please try a different file.") }), 1321 /* @__PURE__ */ jsxRuntimeExports.jsx( 1322 "button", 1323 { 1324 onClick: () => resetConverter(false), 1325 className: "px-6 py-2 bg-zinc-200 dark:bg-zinc-800 hover:bg-zinc-300 dark:hover:bg-zinc-700 rounded-lg font-bold text-zinc-900 dark:text-white transition-colors", 1326 children: t("converter.try_again", "Try Again") 1327 } 1328 ) 1329 ] }) 1330 ] }), 1331 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-12 grid grid-cols-1 md:grid-cols-3 gap-6 text-center max-w-4xl", children: [ 1332 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4", children: [ 1333 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold text-zinc-900 dark:text-white mb-1", children: t("converter.features.soundfontsTitle", "High Quality SoundFonts") }),
1334 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500", children: t("converter.features.soundfontsDesc", "We use professional-grade FluidR3 GM soundfonts for realistic instrument reproduction.") }) 1335 ] }), 1336 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4", children: [ 1337 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold text-zinc-900 dark:text-white mb-1", children: t("converter.features.fastProcessingTitle", "Fast Processing") }), 1338 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500", children: t("converter.features.fastProcessingDesc", "Cloud-based workers convert your MIDI files in seconds, regardless of complexity.") }) 1339 ] }), 1340 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4", children: [ 1341 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold text-zinc-900 dark:text-white mb-1", children: t("converter.features.securePrivateTitle", "Secure & Private") }),
1342 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-500", children: t("converter.features.securePrivateDesc", "Files are processed securely and can be deleted immediately after download.") }) 1343 ] }) 1344 ] }), 1345 /* @__PURE__ */ jsxRuntimeExports.jsx(AdSlot, { type: "converterBottom" }), 1346 /* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: ` 1347 @keyframes shimmer { 1348 100% { 1349 transform: translateX(100%); 1350 } 1351 } 1352 .animate-shimmer { 1353 animation: shimmer 2.5s infinite linear; 1354 } 1355 ` }), 1356 /* @__PURE__ */ jsxRuntimeExports.jsx( 1357 CreditsExhaustedModal, 1358 { 1359 isOpen: isCreditsExhaustedModalOpen, 1360 onClose: () => setIsCreditsExhaustedModalOpen(false), 1361 onSignUp: onAuthRequested 1362 } 1363 ) 1364 ] }); 1365}; 1366export { 1367 ConverterPage as default 1368};
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.