// v2 FINAL stage — one-pager preview + revision chat with model picker const V2_FINAL_MODELS = [ {id:'nemotron',name:'Nemotron 3 Nano Omni', tag:'NVIDIA · multimodal', dot:'#76B900'}, {id:'claude', name:'Claude Opus 4.7', tag:'Anthropic', dot:'#006747'}, {id:'gemini', name:'Gemini 3.1 Pro', tag:'Google', dot:'#4285F4'}, {id:'gpt', name:'GPT-5', tag:'OpenAI', dot:'#10A37F'}, {id:'omni', name:'Palette Omni', tag:'Hybrid', dot:'#1A1A1A'}, ]; const V2FinalStage = ({palette, projectId, pinnedIds, onExportDeck, onExportMarkdown}) => { const map = {}; (palette.blocks || []).forEach(b => (b.keywords || []).forEach(k => map[k.id] = k)); const pinned = pinnedIds.map(id => map[id]).filter(Boolean); const headlineKw = pinned.find(k => k.type === 'METAPHOR') || pinned[0]; const [exporting, setExporting] = React.useState(null); // 'md' | 'deck' | null const exportMd = async () => { setExporting('md'); try { await onExportMarkdown?.(); } finally { setExporting(null); } }; const exportDeck = async () => { setExporting('deck'); try { await onExportDeck?.(); } finally { setExporting(null); } }; return (

최종 산출물

{palette.project?.name} · v3 draft
COPY · ONE-PAGER PREVIEW
{headlineKw ? ( <>

{headlineKw.ko}

{headlineKw.en}

) : (

아직 핀된 키워드가 없습니다

)}

{palette.project?.brief || '프로젝트 브리프가 없습니다. Data 단계에서 작성해주세요.'}

BASED ON {pinned.slice(0, 6).map(k => ( · {k.ko || k.en} ))}
); }; const V2FinalChat = ({palette, projectId, pinnedIds}) => { const [model, setModel] = React.useState(V2_FINAL_MODELS[0]); const [pickerOpen, setPickerOpen] = React.useState(false); const [messages, setMessages] = React.useState([ {role:'ai', text:'Final 덱의 카피·구조·톤을 자유롭게 다듬어 드릴 수 있어요. 무엇을 바꿔볼까요?', hint:true, model: V2_FINAL_MODELS[0].id}, ]); const [draft, setDraft] = React.useState(''); const [thinking, setThinking] = React.useState(false); const send = async () => { const v = draft.trim(); if (!v || thinking) return; const userMsg = {role:'user', text: v}; const next = [...messages.filter(m => !m.hint), userMsg]; setMessages([{role:'ai', text:'Final 덱의 카피·구조·톤을 자유롭게 다듬어 드릴 수 있어요. 무엇을 바꿔볼까요?', hint:true, model: model.id}, ...next]); setDraft(''); setThinking(true); try { const res = await window.API.brainstorm(projectId, model.id, next.map(m => ({me: m.role==='user', text: m.text}))); setMessages(prev => [...prev, {role:'ai', text: res.reply, items: extractList(res.reply), kws: res.kws || [], model: model.id}]); } catch(e) { setMessages(prev => [...prev, {role:'ai', text:'에러: '+e.message, model: model.id}]); } finally { setThinking(false); } }; const presets = ['더 짧게','시제 바꿔','감정 톤↑','영문 버전','한 줄 요약','전체 흐름 점검']; return (
{pickerOpen && (
{V2_FINAL_MODELS.map(m => ( ))}
)}
FINAL · CHAT
{messages.map((m, i) => { const mdl = V2_FINAL_MODELS.find(x => x.id === m.model) || model; return (
{m.role==='user' ? 'YOU' : <>{mdl.name.toUpperCase()}}
{m.text}
{(m.items || []).length > 0 && (
    {m.items.map((it,j) => (
  1. {it}
  2. ))}
)}
); })} {thinking && (
)}
{presets.map(p => ( ))}