// pptx-workflow.js — PPTX 마법사 사이드바 워크플로(이미지 캡처→아웃라인→생성). // extracted from app.js (v4.3.19-3 split). let _pptxImages = []; // {url, thumb, label} let _pptxOutline = null; // parsed pptx-outline JSON let _pptxOutlineEdits = {}; // {index: {title, bullets}} function setPptxSbTab(tab) { ['setup','images','outline'].forEach(t => { document.getElementById('psb-tab-' + t).classList.toggle('active', t === tab); const el = document.getElementById('psb-' + t); if (el) el.style.display = t === tab ? (t === 'outline' ? 'flex' : (t === 'images' ? 'block' : 'flex')) : 'none'; }); } function pptxSetStatus(msg) { const el = document.getElementById('psb-status'); if (el) el.textContent = msg; } function pptxStartWorkflow() { const topic = (document.getElementById('psb-topic').value || '').trim(); const pmcidsRaw = (document.getElementById('psb-pmcids').value || '').trim(); const template = document.getElementById('psb-template').value || 'dark'; const slideCount = parseInt(document.getElementById('psb-slide-count').value) || 0; if (!topic) { pptxSetStatus('주제를 입력하세요.'); return; } const pmcids = pmcidsRaw .split(/[\n,]+/) .map(s => s.trim().replace(/^PMC/i, 'PMC')) .filter(Boolean); _pptxImages = []; _pptxOutline = null; _pptxOutlineEdits = {}; pptxRenderImageGrid(); const cardsEl = document.getElementById('psb-outline-cards'); if (cardsEl) cardsEl.innerHTML = '
아웃라인 생성 중...
'; const genBtn = document.getElementById('psb-gen-btn'); if (genBtn) { genBtn.disabled = true; genBtn.style.opacity = '.5'; } // Build workflow message const pmcLine = pmcids.length ? `\nPMCIDs: ${pmcids.join(', ')}` : ''; const slideLine = slideCount ? `\n슬라이드 수: ${slideCount}장` : ''; const msg = `[PPTX 워크플로 시작]\n주제: ${topic}${pmcLine}\n템플릿: ${template}${slideLine}\n\n아래 순서로 진행해주세요:\n1. 작업 폴더 생성 (주제 기반)\n2. PDF 다운로드 (pubmed_fulltext, 위 PMCIDs)\n3. 이미지 추출 (pdf_extract_images, 각 PDF)\n4. 소형 이미지 필터링 (python_eval)\n5. 슬라이드 구성을 \`\`\`pptx-outline 블록으로 출력 (JSON 형식)\n6. 확인 대기 (create_presentation은 아직 호출하지 마세요)`; pptxSetStatus('워크플로 시작...'); setSidebarTab('pptx'); setPptxSbTab('setup'); // Send via main chat const input = document.getElementById('chat-input'); if (input) { input.value = msg; sendChat(); } } function pptxCaptureImages(toolResultText) { // Parse image links from pdf_extract_images output: [label](/api/files/...) const re = /\[([^\]]*)\]\((\/api\/files\/[^)]+\.(?:png|jpg|jpeg|gif|webp))\)/gi; let m; while ((m = re.exec(toolResultText)) !== null) { const label = m[1]; const url = m[2]; if (!_pptxImages.some(i => i.url === url)) { _pptxImages.push({ url, label }); } } if (_pptxImages.length) { pptxRenderImageGrid(); const countEl = document.getElementById('psb-img-count'); if (countEl) countEl.textContent = `(${_pptxImages.length})`; // Auto-switch to images tab if PPTX tab is active if (sidebarTab === 'pptx') setPptxSbTab('images'); pptxSetStatus(`이미지 ${_pptxImages.length}개 추출됨`); } } function pptxRenderImageGrid() { const grid = document.getElementById('psb-image-grid'); if (!grid) return; if (!_pptxImages.length) { grid.innerHTML = '
이미지 추출 후 표시됩니다
'; return; } grid.innerHTML = _pptxImages.map((img, idx) => `
${escHtml(img.label)}
`).join(''); } function pptxImageClick(idx) { const img = _pptxImages[idx]; if (!img) return; window.open(img.url, '_blank', 'noopener'); } function pptxSetOutline(data) { _pptxOutline = data; _pptxOutlineEdits = {}; pptxRenderOutlineCards(); const genBtn = document.getElementById('psb-gen-btn'); if (genBtn) { genBtn.disabled = false; genBtn.style.opacity = '1'; } if (sidebarTab === 'pptx') setPptxSbTab('outline'); pptxSetStatus(`아웃라인 준비 (${(data.slides || []).length}장)`); } const SLIDE_TYPE_ICON = { title:'🏷', section:'📂', content:'📝', image:'🖼', table:'📊', chart:'📈', timeline:'⏳', compare:'⚖' }; function pptxRenderOutlineCards() { const container = document.getElementById('psb-outline-cards'); if (!container || !_pptxOutline) return; const slides = _pptxOutline.slides || []; if (!slides.length) { container.innerHTML = '
슬라이드가 없습니다
'; return; } container.innerHTML = slides.map((s, i) => { const icon = SLIDE_TYPE_ICON[s.type] || '📄'; const imgThumb = s.image_url ? `` : `
🖼
`; const bulletsPreview = Array.isArray(s.bullets) && s.bullets.length ? `
${s.bullets.slice(0,2).map(b => `• ${b}`).join('
')}
` : ''; return `
${i+1} ${imgThumb}
${icon} ${escHtml(s.title || '(제목 없음)')}
${s.type}
${bulletsPreview}
`; }).join(''); } function pptxToggleEdit(idx) { const el = document.getElementById('psb-edit-' + idx); if (el) el.style.display = el.style.display === 'none' ? 'block' : 'none'; } function pptxEditTitle(idx, val) { if (!_pptxOutlineEdits[idx]) _pptxOutlineEdits[idx] = {}; _pptxOutlineEdits[idx].title = val; } function pptxEditBullets(idx, val) { if (!_pptxOutlineEdits[idx]) _pptxOutlineEdits[idx] = {}; _pptxOutlineEdits[idx].bullets = val.split(/\n\n+/).map(g => g.replace(/^\n+|\n+$/g, '')).filter(g => g.trim() !== ''); } function pptxGenerate() { if (!_pptxOutline) return; // Apply edits to outline const slides = (_pptxOutline.slides || []).map((s, i) => { const ed = _pptxOutlineEdits[i]; if (!ed) return s; return Object.assign({}, s, ed.title !== undefined ? { title: ed.title } : {}, ed.bullets !== undefined ? { bullets: ed.bullets } : {}); }); const edited = Object.assign({}, _pptxOutline, { slides }); const hasEdits = Object.keys(_pptxOutlineEdits).length > 0; const msg = hasEdits ? `아웃라인을 수정했습니다. 수정된 아웃라인으로 create_presentation을 호출해주세요:\n\`\`\`json\n${JSON.stringify(edited, null, 2)}\n\`\`\`` : `아웃라인 확인했습니다. create_presentation으로 슬라이드를 생성해주세요.`; const genBtn = document.getElementById('psb-gen-btn'); if (genBtn) { genBtn.disabled = true; genBtn.style.opacity = '.5'; } pptxSetStatus('생성 중...'); const input = document.getElementById('chat-input'); if (input) { input.value = msg; sendChat(); } } function pptxResetWorkflow() { _pptxImages = []; _pptxOutline = null; _pptxOutlineEdits = {}; pptxRenderImageGrid(); const cardsEl = document.getElementById('psb-outline-cards'); if (cardsEl) cardsEl.innerHTML = '
아웃라인 생성 후 표시됩니다
'; const genBtn = document.getElementById('psb-gen-btn'); if (genBtn) { genBtn.disabled = true; genBtn.style.opacity = '.5'; } const countEl = document.getElementById('psb-img-count'); if (countEl) countEl.textContent = ''; pptxSetStatus(''); setPptxSbTab('setup'); }