// πŸ’‘ [μˆ˜μ •] ν”„λ‘œκ·Έλž¨ μ‹€ν–‰ μ‹œ 기본으둜 1단계가 μ‘΄μž¬ν•˜λ„λ‘ μ΄ˆκΈ°κ°’ μ„€μ • let currentScript = [{ credit: "좜처 μž…λ ₯\n@username", idiom: "βœ“ 핡심 ν‘œν˜„", meaning: "; λœ»μ„ μž…λ ₯ν•˜μ„Έμš”", en: "", ko: "", pron: "", prompt_top: "stand-up comedian, highly detailed", prompt_bottom: "audience reacting, cinematic lighting" }]; let assetStates = {}; let currentPreviewTurn = 0; // 🎨 숏폼 μžλ§‰ νƒ€μ΄ν¬κ·Έλž˜ν”Ό κΈ€λ‘œλ²Œ μ„€μ • (λ³„ν‘œ 효과 포함) let styleConfig = { english: { font: 'SUIT', size: 80, baseColor: '#FFFFFF', bgColor: '#000000', bgOpacity: 0.85, highlights: { 1: { color: '#FFF200', desc: '주제(λ…Έλž‘)' }, 2: { color: '#00E5FF', desc: 'νŒ¨ν„΄(μ‹œμ•ˆ)' }, 3: { color: '#FF3B30', desc: '주의(λΉ¨κ°•)' }, 4: { color: '#4CD964', desc: 'μ–΄νœ˜(초둝)' }, 5: { color: '#FF2D55', desc: 'λ‰˜μ•™μŠ€(핑크)' } } }, korean: { font: 'SeoulNamsanM', size: 55, color: '#FFFFFF', outlineColor: '#000000', outlineWidth: 3 }, pronunciation: { font: 'SchoolSafeDictation', size: 65, color: '#FF3B30', outlineColor: '#000000', outlineWidth: 2 } }; // ✨ [μ—”μ§„] λ³„ν‘œ 개수 기반 λ©€ν‹° μŠ€νƒ€μΌ μžλ§‰ λ³€ν™˜ ν•¨μˆ˜ function parseAsteriskToHTML(text) { if (!text) return ""; let html = text; html = html.replace(/\*{5}(.*?)\*{5}/g, `$1`); html = html.replace(/\*{4}(.*?)\*{4}/g, `$1`); html = html.replace(/\*{3}(.*?)\*{3}/g, `$1`); html = html.replace(/\*{2}(.*?)\*{2}/g, `$1`); html = html.replace(/\*(.*?)\*/g, `$1`); return html; } // [νŒ¨λ„ 1] AI λŒ€λ³Έ 생성 μ—”μ§„ async function generateScript() { const topic = document.getElementById('topicInput').value; if(!topic) { alert("주제λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”."); return; } const btn = event.currentTarget; btn.innerHTML = ' 생성 쀑...'; btn.disabled = true; try { const response = await fetch('/api/studio/generate-script', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ topic: topic }) }); if (!response.ok) throw new Error(`μ„œλ²„ 였λ₯˜: ${response.status}`); const data = await response.json(); currentScript = data.dialogue.map((turn) => ({ credit: turn.credit || "μ§€λ―Έ μΉ΄\n@jimmycarr", idiom: turn.idiom || "βœ“ 핡심 ν‘œν˜„", meaning: turn.meaning || "; λœ»μ„ μž…λ ₯ν•˜μ„Έμš”", en: turn.en || "", ko: turn.ko || "", pron: turn.ko_pron || turn.pron || "", prompt_top: turn.image_prompt_top || turn.image_prompt || "stand-up comedian, highly detailed", prompt_bottom: turn.image_prompt_bottom || "audience reacting, cinematic lighting" })); renderScriptPanel(); renderAssetPanel(); } catch (error) { console.error("λŒ€λ³Έ 생성 μ—λŸ¬:", error); // πŸ’‘ [μˆ˜μ •] AI 생성 μ‹€νŒ¨ μ‹œ(Mock 데이터) κΈ°λ³Έ 4단계 κΈ°νšμ— 맞좰 좜λ ₯ currentScript = [ { credit: "μ§€λ―Έ μΉ΄\n@jimmycarr", idiom: "βœ“ test the water(s)", meaning: "; λΆ„μœ„κΈ°λ₯Ό μ‚΄ν”Όλ‹€ / λ°˜μ‘μ„ 떠보닀", en: "I'll just, I'll *test the water* first.", ko: "κ·Έλƒ₯ λΆ„μœ„κΈ° ν•œ 번 볼게", pron: "μ•„μ„μ €μŠ€, μ•„μ„ν…ŒμŠ€λ” μ›ŒλŸ΄ νΌμŠ€λ”", prompt_top: "stand-up comedian holding a microphone", prompt_bottom: "crowd of people smiling" }, { credit: "μ§€λ―Έ μΉ΄\n@jimmycarr", idiom: "βœ“ make a move", meaning: "; 행동을 μ·¨ν•˜λ‹€ / λ‹€κ°€κ°€λ‹€", en: "Before I actually **make a move**, you know?", ko: "본격적으둜 ν–‰λ™ν•˜κΈ° 전에 말이야.", pron: "비포 아이 앑츄얼리 메이컀 무브, 유 λ…Έ?", prompt_top: "man looking thoughtful, cinematic lighting", prompt_bottom: "close up of a person's eyes" }, { credit: "μ§€λ―Έ μΉ΄\n@jimmycarr", idiom: "βœ“ play it safe", meaning: "; μ•ˆμ „ν•˜κ²Œ κ°€λ‹€ / λ¬΄λ¦¬ν•˜μ§€ μ•Šλ‹€", en: "It's always better to ***play it safe***.", ko: "μ–Έμ œλ‚˜ μ•ˆμ „ν•˜κ²Œ κ°€λŠ” 게 λ‚«μ§€.", pron: "이츠 μ˜¬μ›¨μ΄μ¦ˆ 베러 투 ν”Œλ ˆμ΄ μž‡ 세이프.", prompt_top: "a cautious person, dark background", prompt_bottom: "audience nodding in agreement" }, { credit: "μ§€λ―Έ μΉ΄\n@jimmycarr", idiom: "βœ“ go all in", meaning: "; μ˜¬μΈν•˜λ‹€ / λ‹€ κ±Έλ‹€", en: "Then I'll just ****go all in****.", ko: "그러고 λ‚˜μ„œ μ‹Ή λ‹€ 걸어버릴 κ±°μ•Ό.", pron: "덴 μ•„μ„μ €μŠ€ κ³  올 인.", prompt_top: "casino chips, aggressive lighting", prompt_bottom: "people cheering loudly" } ]; renderScriptPanel(); renderAssetPanel(); } finally { btn.innerHTML = ' AI 생성'; btn.disabled = false; } } // [νŒ¨λ„ 1] λŒ€λ³Έ 에디터 UI λ Œλ”λ§ function renderScriptPanel() { const container = document.getElementById('scriptContainer'); container.innerHTML = ""; currentScript.forEach((turn, idx) => { // πŸ’‘ [μˆ˜μ •] μΉ΄λ“œ 우츑 상단에 X(μ‚­μ œ) λ²„νŠΌ μΆ”κ°€ const html = `
${idx+1}
`; container.insertAdjacentHTML('beforeend', html); }); } // πŸ—‘οΈ [μ‹ κ·œ μΆ”κ°€] μˆ˜λ™μœΌλ‘œ νŠΉμ • 단계(Turn) μ‚­μ œ ν•¨μˆ˜ function deleteScriptTurn(idx) { if (currentScript.length <= 1) { alert("μ΅œμ†Œ 1개의 λ‹¨κ³„λŠ” μœ μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€."); return; } if (confirm(`μ •λ§λ‘œ ${idx + 1}단계λ₯Ό μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?`)) { // ν•΄λ‹Ή 인덱슀의 λŒ€λ³Έ 및 에셋 데이터 μ‚­μ œ currentScript.splice(idx, 1); // 에셋 데이터도 λ™κΈ°ν™”ν•˜μ—¬ μ‚­μ œν•˜κ³  인덱슀 μž¬μ •λ ¬ const newAssetStates = {}; currentScript.forEach((_, newIdx) => { newAssetStates[newIdx] = assetStates[newIdx >= idx ? newIdx + 1 : newIdx]; }); assetStates = newAssetStates; // 프리뷰 μΈλ±μŠ€κ°€ λ²”μœ„λ₯Ό λ²—μ–΄λ‚˜λ©΄ μ‘°μ • if (currentPreviewTurn >= currentScript.length) { currentPreviewTurn = Math.max(0, currentScript.length - 1); } localStorage.setItem('studio_current_script', JSON.stringify(currentScript)); renderScriptPanel(); renderAssetPanel(); updatePreviewScreen(); } } function updateScriptData(idx, field, value) { currentScript[idx][field] = value; localStorage.setItem('studio_current_script', JSON.stringify(currentScript)); updatePreviewScreen(); } // πŸ’Š [νŒ¨λ„ 1] μˆ˜λ™μœΌλ‘œ μƒˆλ‘œμš΄ λŒ€λ³Έ 단계(Turn) μΆ”κ°€ ν•¨μˆ˜ function addScriptTurn() { // 1. 빈 데이터 배열에 λ°€μ–΄λ„£κΈ° currentScript.push({ credit: "좜처 μž…λ ₯\n@username", idiom: "βœ“ 핡심 ν‘œν˜„", meaning: "; λœ»μ„ μž…λ ₯ν•˜μ„Έμš”", en: "", ko: "", pron: "", prompt_top: "stand-up comedian, highly detailed", prompt_bottom: "audience reacting, cinematic lighting" }); // 2. νŒ¨λ„ λ‹€μ‹œ 그리기 (1번, 2번 νŒ¨λ„ λ™μ‹œ 동기화) renderScriptPanel(); renderAssetPanel(); // 3. μ‚¬μš©μžκ°€ 방금 μΆ”κ°€ν•œ 맨 μ•„λž˜μͺ½μœΌλ‘œ 슀크둀 λΆ€λ“œλŸ½κ²Œ 이동 setTimeout(() => { const container = document.getElementById('scriptContainer'); if (container) { container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); } }, 50); } // 🎨 ν…Œλ§ˆ 프리셋 적용 ν•¨μˆ˜ function applyThemePreset(theme) { if (theme === 'variety') { // 예λŠ₯ν˜•: κ°•λ ¬ν•œ 흰색 + λ‘κΊΌμš΄ 검은 ν…Œλ‘λ¦¬, λ°œμŒμ€ 빨간색 styleConfig.korean.color = '#FFFFFF'; styleConfig.korean.outlineColor = '#000000'; styleConfig.pronunciation.color = '#FF3B30'; styleConfig.pronunciation.outlineColor = '#000000'; } else if (theme === 'docu') { // λ‹€νν˜•: μ°¨λΆ„ν•œ μ—°νšŒμƒ‰ + ν…Œλ‘λ¦¬ μ—†μŒ, λ°œμŒμ€ μ–΄λ‘μš΄ νšŒμƒ‰ styleConfig.korean.color = '#E5E5E5'; styleConfig.korean.outlineColor = 'transparent'; styleConfig.pronunciation.color = '#A3A3A3'; styleConfig.pronunciation.outlineColor = 'transparent'; } else if (theme === 'trendy') { // νŠΈλ Œλ””νŒ: λ„€μ˜¨ λŠλ‚Œμ˜ μ‹œμ•ˆμƒ‰ + μ–΄λ‘μš΄ 보라색 ν…Œλ‘λ¦¬ styleConfig.korean.color = '#00E5FF'; styleConfig.korean.outlineColor = '#1A0B2E'; styleConfig.pronunciation.color = '#FF2D55'; styleConfig.pronunciation.outlineColor = '#1A0B2E'; } // λ³€κ²½λœ 섀정을 미리보기 화면에 μ¦‰μ‹œ λ Œλ”λ§ updatePreviewScreen(); }