const SETTINGS_KEY = 'webToPixsoSettings'; const DEFAULT_SETTINGS = { useProxy: false, concurrency: '8', captureMode: 'mixed', captureWidth: null }; const MIN_CAPTURE_WIDTH = 320; const MAX_CAPTURE_WIDTH = 3840; const captureWidthInput = document.getElementById('captureWidth'); const exportBtn = document.getElementById('exportBtn'); const btnText = document.getElementById('btnText'); const status = document.getElementById('status'); const statusText = document.getElementById('statusText'); const progressFill = document.getElementById('progressFill'); function normalizeCaptureWidth(value, fallback = null) { const number = Number.parseInt(String(value || '').replace(/\D+/g, ''), 10); if (!Number.isFinite(number)) return fallback; return Math.max(MIN_CAPTURE_WIDTH, Math.min(MAX_CAPTURE_WIDTH, number)); } async function getCurrentViewportWidth() { try { const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); if (!tab?.id) return null; const [{ result }] = await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: () => Math.round(window.innerWidth || document.documentElement.clientWidth || 0) }); return normalizeCaptureWidth(result, null); } catch { return null; } } function setProgress(percent, text) { status.hidden = false; progressFill.style.width = `${Math.max(0, Math.min(100, percent))}%`; statusText.textContent = text; } function setBusy(isBusy) { exportBtn.disabled = isBusy; exportBtn.classList.toggle('loading', isBusy); exportBtn.classList.remove('success', 'error'); btnText.textContent = isBusy ? '处理中...' : '导出 PPTX'; } function setResult(kind, text) { exportBtn.classList.remove('loading', 'success', 'error'); exportBtn.classList.add(kind); btnText.textContent = text; } async function exportPptx() { setBusy(true); setProgress(5, '准备采集...'); const captureWidth = normalizeCaptureWidth(captureWidthInput.value, null); try { // 1. 采集 setProgress(15, '正在采集页面...'); const captureResult = await chrome.runtime.sendMessage({ type: 'WEB_TO_PPT_CAPTURE_START', options: { captureWidth } }); if (!captureResult || !captureResult.ok) throw new Error(captureResult?.error || '采集失败'); // 2. 获取数据 setProgress(40, '采集完成,获取数据...'); const dataResult = await chrome.runtime.sendMessage({ type: 'WEB_TO_PPT_GET_DATA' }); if (!dataResult || !dataResult.ok) throw new Error(dataResult?.error || '获取数据失败'); // 3. 转化 setProgress(50, '正在生成 PPTX...'); const schema = await WebToPPT.convertToPptx(dataResult.data, (msg) => setProgress(60, msg)); // 4. 生成 PPTX setProgress(85, '正在打包...'); const blob = await WebToPPT.schemaToPptxBlob(schema); // 5. 下载(data URL,popup 直接调用) setProgress(95, '正在下载...'); const buffer = await blob.arrayBuffer(); const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.length; i += 0x8000) { binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000)); } const dataUrl = 'data:application/vnd.openxmlformats-officedocument.presentationml.presentation;base64,' + btoa(binary); const title = dataResult.data.source?.title || 'webpage'; const safeTitle = title.replace(/[\\/:*?"<>|]+/g, '-').replace(/\s+/g, '-').slice(0, 64) || 'webpage'; const filename = `web-to-ppt/${safeTitle}-${Date.now()}.pptx`; await chrome.downloads.download({ url: dataUrl, filename }); setProgress(100, '导出完成'); setResult('success', '导出完成'); } catch (error) { setProgress(0, error.message || String(error)); setResult('error', '导出失败'); setTimeout(() => { setBusy(false); status.hidden = true; progressFill.style.width = '0'; }, 3000); } } document.addEventListener('DOMContentLoaded', async () => { const currentWidth = await getCurrentViewportWidth(); captureWidthInput.value = String(currentWidth || ''); }); exportBtn.addEventListener('click', exportPptx); captureWidthInput.addEventListener('input', () => { captureWidthInput.value = captureWidthInput.value.replace(/\D+/g, ''); });