@lemonppt/renderer 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2107,8 +2107,7 @@
2107
2107
  if (!stage || !scaler) return;
2108
2108
  if (editorRoot && editorRoot.classList.contains('lp-editor-presentation-mode')) {
2109
2109
  const scale = Math.min(window.innerWidth / SLIDE_WIDTH, window.innerHeight / SLIDE_HEIGHT);
2110
- scaler.style.setProperty('--lp-presentation-scale', String(Math.max(scale, 0.35)));
2111
- scaler.style.transform = '';
2110
+ scaler.style.transform = 'scale(' + Math.max(scale, 0.35) + ')';
2112
2111
  return;
2113
2112
  }
2114
2113
  const scale = userZoom == null ? fitScale() : userZoom;
@@ -1,2 +1,2 @@
1
- export declare const editorScript = "\n(function () {\n // \u7528 jQuery \u4E3A\u6240\u6709\u5E26 data-scrollbar \u7684\u5BB9\u5668\u6DFB\u52A0\u81EA\u5B9A\u4E49\u6EDA\u52A8\u6761\u6837\u5F0F\u7C7B\n if (typeof jQuery !== 'undefined') {\n jQuery(function ($) {\n $('[data-scrollbar]').addClass('lp-custom-scrollbar');\n });\n }\n\n const MIGRATION_KEY = 'lemonppt:editor:migration:showInsight:v1';\n const MAX_HISTORY = 50;\n\n let goal = window.__lemonPPT_goal;\n if (!goal) return;\n\n function getStorageKey() {\n return 'lemonppt:editor:v2:' + (goal.theme || 'theme01') + ':' + (goal.randomSeed || goal.title || 'default');\n }\n\n // \u4F18\u5148\u4ECE localStorage \u6062\u590D\n let needsDomRebuild = false;\n try {\n const saved = localStorage.getItem(getStorageKey());\n if (saved) {\n const parsed = JSON.parse(saved);\n if (parsed && parsed.slides) {\n // \u4E00\u6B21\u6027\u8FC1\u79FB\uFF1A\u5C06\u56FE\u8868\u9875\u7684\u91CD\u70B9\u5F3A\u8C03\u5F00\u5173\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u5F00\u542F\n if (!localStorage.getItem(MIGRATION_KEY)) {\n parsed.slides.forEach((slide) => {\n if (slide.role === 'chart') {\n slide.props = slide.props || {};\n slide.props.showInsight = true;\n }\n });\n localStorage.setItem(MIGRATION_KEY, '1');\n }\n // \u82E5\u7ED3\u6784\u6570\u91CF\u6216\u5E7B\u706F\u7247\u987A\u5E8F\u53D1\u751F\u53D8\u5316\uFF0C\u9700\u8981\u91CD\u5EFA DOM\uFF1B\u4EC5\u5185\u5BB9\u53D8\u5316\u5219\u540C\u6B65\u6587\u672C\u5373\u53EF\n const orderChanged = parsed.slides.length !== goal.slides.length ||\n parsed.slides.some((slide, i) => slide.layout !== goal.slides[i].layout);\n goal = parsed;\n window.__lemonPPT_goal = goal;\n if (orderChanged) {\n needsDomRebuild = true;\n } else {\n syncDomFromGoal();\n }\n }\n }\n } catch (err) {\n console.warn('\u81EA\u52A8\u6062\u590D\u5931\u8D25', err);\n }\n\n // \u521D\u59CB\u5316\u5916\u89C2\u6A21\u5F0F\uFF08\u540C\u4E00\u4E3B\u9898\u4E0B\u7684\u6D45\u8272/\u6DF1\u8272\u5207\u6362\uFF0C\u901A\u8FC7 data-theme / data-appearance \u5207\u6362 CSS \u53D8\u91CF\uFF09\n syncAppearanceFromGoal();\n\n const TRANSITIONS = [\n { key: 'none', label: '\u65E0\u52A8\u753B' },\n { key: 'slide', label: '\u6A2A\u6ED1' },\n { key: 'fade', label: '\u6DE1\u5165\u6DE1\u51FA' },\n { key: 'scale', label: '\u7F29\u653E' },\n { key: 'cut-in', label: '\u5207\u5165' },\n { key: 'cube', label: '\u7ACB\u65B9\u4F53' },\n { key: 'flip', label: '\u7FFB\u9875' },\n { key: 'cover', label: '\u8986\u76D6' },\n { key: 'wipe', label: '\u64E6\u9664' },\n ];\n\n const history = [deepClone(goal)];\n let historyIndex = 0;\n\n function deepClone(obj) {\n return JSON.parse(JSON.stringify(obj));\n }\n\n function recordHistory() {\n // \u622A\u65AD redo \u5206\u652F\n if (historyIndex < history.length - 1) {\n history.splice(historyIndex + 1);\n }\n history.push(deepClone(goal));\n if (history.length > MAX_HISTORY) {\n history.shift();\n } else {\n historyIndex++;\n }\n updateUndoRedoButtons();\n }\n\n function undo() {\n if (historyIndex <= 0) return;\n historyIndex--;\n restoreGoal(history[historyIndex]);\n }\n\n function redo() {\n if (historyIndex >= history.length - 1) return;\n historyIndex++;\n restoreGoal(history[historyIndex]);\n }\n\n function restoreGoal(newGoal) {\n const oldSlides = goal && goal.slides ? goal.slides : [];\n goal = newGoal;\n window.__lemonPPT_goal = goal;\n Object.assign(window.__lemonPPT_goal, newGoal);\n const orderChanged = oldSlides.length !== goal.slides.length ||\n oldSlides.some((slide, i) => slide.layout !== goal.slides[i].layout);\n if (orderChanged) {\n current = 0;\n selectedSlideIdx = 0;\n rebuildSlidesAndThumbnails();\n if (typeof selectSlide === 'function') selectSlide(current);\n } else {\n syncDomFromGoal();\n }\n syncAppearanceFromGoal();\n autoSave();\n updateUndoRedoButtons();\n }\n\n let autoSaveTimer = null;\n function autoSave() {\n if (autoSaveTimer) {\n clearTimeout(autoSaveTimer);\n }\n autoSaveTimer = setTimeout(() => {\n autoSaveTimer = null;\n try {\n localStorage.setItem(getStorageKey(), JSON.stringify(goal));\n } catch (err) {\n console.warn('\u81EA\u52A8\u4FDD\u5B58\u5931\u8D25', err);\n }\n }, 400);\n }\n\n // \u83B7\u53D6\u53EF\u7F16\u8F91\u5143\u7D20\uFF1B\u517C\u5BB9 Text \u8282\u70B9\u3001Shadow DOM \u7B49\u573A\u666F\n function getEditingElement(target) {\n if (!target) return null;\n if (target.nodeType === Node.TEXT_NODE) {\n target = target.parentElement;\n }\n if (!target || typeof target.closest !== 'function') return null;\n return target.closest('[data-lp-editable=\"true\"], [contenteditable=\"true\"], input, textarea, select');\n }\n\n function isEditingTarget(target) {\n return !!getEditingElement(target);\n }\n\n function propMatchesChangedPath(prop, changedPath) {\n if (!changedPath) return true;\n return prop === changedPath || prop.startsWith(changedPath + '.') || changedPath.startsWith(prop + '.');\n }\n\n function syncDomFromGoal(changedPath) {\n // \u6587\u672C\uFF1A\u5982\u679C\u6307\u5B9A\u4E86 changedPath\uFF0C\u53EA\u540C\u6B65\u53D7\u5F71\u54CD\u7684\u8DEF\u5F84\uFF0C\u907F\u514D\u8986\u76D6 React \u6E32\u67D3\u7684\u7279\u6B8A\u7ED3\u6784\uFF08\u5982\u6807\u9898\u9AD8\u4EAE\uFF09\n document.querySelectorAll('[data-lp-editable=\"true\"]').forEach((el) => {\n const slideIdx = Number(el.getAttribute('data-lp-slide-idx'));\n const prop = el.getAttribute('data-lp-prop');\n if (Number.isNaN(slideIdx) || !prop) return;\n if (!propMatchesChangedPath(prop, changedPath)) return;\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n const value = getProp(slide.props, prop);\n el.textContent = value == null ? '' : String(value);\n });\n\n // \u56FE\u7247\n document.querySelectorAll('[data-lp-editable-image=\"true\"]').forEach((img) => {\n const slideIdx = Number(img.getAttribute('data-lp-slide-idx'));\n const prop = img.getAttribute('data-lp-prop') || 'image';\n if (Number.isNaN(slideIdx)) return;\n if (!propMatchesChangedPath(prop, changedPath)) return;\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n const value = getProp(slide.props, prop);\n if (value) {\n if (img.tagName === 'IMG') {\n img.setAttribute('src', String(value));\n } else {\n // \u5360\u4F4D\u5BB9\u5668\u9700\u8981\u66FF\u6362\u4E3A\u771F\u5B9E\u56FE\u7247\n const newImg = document.createElement('img');\n newImg.className = img.className.replace(/\blp-editable-image-placeholder\b/g, '').trim();\n newImg.src = String(value);\n newImg.alt = img.getAttribute('alt') || '';\n newImg.setAttribute('data-lp-editable-image', 'true');\n newImg.setAttribute('data-lp-slide-idx', String(slideIdx));\n newImg.setAttribute('data-lp-prop', prop);\n img.parentNode.replaceChild(newImg, img);\n }\n }\n // \u65E0\u56FE\u7247\u65F6\u4FDD\u7559\u5360\u4F4D\u5BB9\u5668\uFF0C\u786E\u4FDD\u4ECD\u53EF\u70B9\u51FB\u4E0A\u4F20\n });\n }\n\n function getProp(obj, path) {\n const keys = path.split('.');\n let target = obj;\n for (const k of keys) {\n if (target == null || typeof target !== 'object') return undefined;\n target = target[k];\n }\n return target;\n }\n\n function setProp(obj, path, value) {\n const keys = path.split('.');\n let target = obj;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!(k in target)) target[k] = [];\n target = target[k];\n }\n target[keys[keys.length - 1]] = value;\n }\n\n // \u70B9\u51FB\u56FE\u7247\u56FA\u5B9A\u533A\u57DF\u76F4\u63A5\u4E0A\u4F20\u672C\u5730\u56FE\u7247\n function handleImageUpload(target, slideIdx, prop) {\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = 'image/*';\n input.style.display = 'none';\n document.body.appendChild(input);\n\n input.addEventListener('change', () => {\n const file = input.files?.[0];\n if (!file) {\n input.remove();\n return;\n }\n const reader = new FileReader();\n reader.onload = (e) => {\n const dataUrl = e.target?.result;\n if (typeof dataUrl !== 'string') {\n input.remove();\n return;\n }\n recordHistory();\n setProp(slide.props, prop, dataUrl);\n autoSave();\n renderCurrentSlideToRoot();\n input.remove();\n };\n reader.readAsDataURL(file);\n });\n\n input.addEventListener('cancel', () => input.remove());\n input.click();\n }\n\n function escapeHtml(str) {\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n }\n\n // \u521D\u59CB\u5316 contenteditable \u5C5E\u6027\uFF1B\u4F7F\u7528 MutationObserver \u76D1\u542C React \u91CD\u65B0\u6E32\u67D3\u540E\u65B0\u589E\u7684\u7F16\u8F91\u5143\u7D20\n function initEditableElements(container) {\n const scope = container || document;\n scope.querySelectorAll('[data-lp-editable=\"true\"]').forEach((el) => {\n if (el.getAttribute('contenteditable') !== 'true') {\n el.setAttribute('contenteditable', 'true');\n }\n });\n }\n initEditableElements();\n\n const editableObserver = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n initEditableElements(node);\n }\n });\n });\n });\n editableObserver.observe(document.body, { childList: true, subtree: true });\n\n // \u4F7F\u7528\u4E8B\u4EF6\u59D4\u6258\u5904\u7406\u6587\u672C\u7F16\u8F91\uFF0C\u652F\u6301 React \u5C40\u90E8\u6E32\u67D3\u540E\u81EA\u52A8\u751F\u6548\n document.addEventListener('focus', (e) => {\n const el = e.target.closest('[data-lp-editable=\"true\"]');\n if (!el) return;\n recordHistory();\n }, true);\n\n document.addEventListener('blur', (e) => {\n const el = e.target.closest('[data-lp-editable=\"true\"]');\n if (!el) return;\n const slideIdx = Number(el.getAttribute('data-lp-slide-idx'));\n const prop = el.getAttribute('data-lp-prop');\n if (Number.isNaN(slideIdx) || !prop) return;\n\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n\n const rawValue = el.textContent || '';\n const fieldType = el.getAttribute('data-lp-editable-type');\n const value = fieldType === 'number' ? Number(rawValue) : rawValue;\n setProp(slide.props, prop, value);\n autoSave();\n // \u56FE\u8868\u6570\u636E\u5B57\u6BB5\u5728\u753B\u5E03\u4E0A\u76F4\u63A5\u7F16\u8F91\u540E\uFF0C\u9700\u8981\u91CD\u65B0\u6E32\u67D3\u5E7B\u706F\u7247\u4EE5\u66F4\u65B0\u56FE\u8868\n if (el.getAttribute('data-lp-chart-data') === 'true' && typeof window.__lemonPPT_initECharts === 'function') {\n refreshCurrentSlide();\n }\n }, true);\n\n document.addEventListener('keydown', (e) => {\n const el = getEditingElement(e.target) || getEditingElement(document.activeElement);\n if (!el) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n el.blur();\n }\n });\n\n // \u56FE\u7247\u6362\u56FE\uFF1A\u70B9\u51FB\u56FA\u5B9A\u533A\u57DF\u76F4\u63A5\u5524\u8D77\u6587\u4EF6\u9009\u62E9\u5668\u4E0A\u4F20\n document.addEventListener('click', (e) => {\n const target = e.target.closest('[data-lp-editable-image=\"true\"]');\n if (!target) return;\n const slideIdx = Number(target.getAttribute('data-lp-slide-idx'));\n const prop = target.getAttribute('data-lp-prop') || 'image';\n if (Number.isNaN(slideIdx)) return;\n handleImageUpload(target, slideIdx, prop);\n });\n\n const exportToggle = document.getElementById('lp-export-toggle');\n const exportMenu = document.getElementById('lp-export-menu');\n const exportWrapper = document.getElementById('lp-editor-export');\n if (exportToggle && exportMenu) {\n exportToggle.addEventListener('click', (e) => {\n e.stopPropagation();\n const hidden = exportMenu.hasAttribute('hidden');\n if (hidden) {\n exportMenu.removeAttribute('hidden');\n exportToggle.setAttribute('aria-expanded', 'true');\n } else {\n exportMenu.setAttribute('hidden', '');\n exportToggle.setAttribute('aria-expanded', 'false');\n }\n });\n document.addEventListener('click', (e) => {\n if (!exportWrapper || exportWrapper.contains(e.target)) return;\n exportMenu.setAttribute('hidden', '');\n exportToggle.setAttribute('aria-expanded', 'false');\n });\n }\n\n const exportHtmlBtn = document.getElementById('lp-export-html');\n if (exportHtmlBtn) {\n exportHtmlBtn.addEventListener('click', () => {\n const html = '<!DOCTYPE html>\\n' + document.documentElement.outerHTML;\n const blob = new Blob([html], { type: 'text/html' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'presentation.html';\n a.click();\n URL.revokeObjectURL(url);\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n });\n }\n\n const exportPptxBtn = document.getElementById('lp-export-pptx');\n if (exportPptxBtn) {\n const pptxLabel = exportPptxBtn.querySelector('.lp-editor-export-label');\n exportPptxBtn.addEventListener('click', async () => {\n if (window.location.protocol === 'file:') {\n alert('\u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\u65E0\u6CD5\u76F4\u63A5\u5BFC\u51FA PPTX\u3002\\n\\n\u8BF7\u901A\u8FC7\u672C\u5730\u670D\u52A1\u5668\u8BBF\u95EE\u540E\u5BFC\u51FA\uFF1A\\n pnpm dev\\n\u6216\u8FD0\u884C\uFF1A\\n node scripts/export-pptx.mjs <goal.json> out.pptx');\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n return;\n }\n if (pptxLabel) pptxLabel.textContent = '\u5BFC\u51FA\u4E2D...';\n exportPptxBtn.disabled = true;\n try {\n const res = await fetch('/api/export/pptx', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(goal),\n });\n if (!res.ok) throw new Error('\u5BFC\u51FA\u5931\u8D25: ' + res.status);\n const blob = await res.blob();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'presentation.pptx';\n a.click();\n URL.revokeObjectURL(url);\n } catch (err) {\n alert(err instanceof Error ? err.message : String(err));\n } finally {\n if (pptxLabel) pptxLabel.textContent = 'PPTX';\n exportPptxBtn.disabled = false;\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n }\n });\n }\n\n const exportPdfBtn = document.getElementById('lp-export-pdf');\n if (exportPdfBtn) {\n const pdfLabel = exportPdfBtn.querySelector('.lp-editor-export-label');\n exportPdfBtn.addEventListener('click', async () => {\n if (window.location.protocol === 'file:') {\n alert('\u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\u65E0\u6CD5\u76F4\u63A5\u5BFC\u51FA PDF\u3002\\n\\n\u8BF7\u901A\u8FC7\u672C\u5730\u670D\u52A1\u5668\u8BBF\u95EE\u540E\u5BFC\u51FA\uFF1A\\n pnpm dev\\n\u6216\u8FD0\u884C\uFF1A\\n node scripts/export-pdf.mjs <goal.json> out.pdf');\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n return;\n }\n if (pdfLabel) pdfLabel.textContent = '\u5BFC\u51FA\u4E2D...';\n exportPdfBtn.disabled = true;\n try {\n const res = await fetch('/api/export/pdf', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(goal),\n });\n if (!res.ok) throw new Error('\u5BFC\u51FA\u5931\u8D25: ' + res.status);\n const blob = await res.blob();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'presentation.pdf';\n a.click();\n URL.revokeObjectURL(url);\n } catch (err) {\n alert(err instanceof Error ? err.message : String(err));\n } finally {\n if (pdfLabel) pdfLabel.textContent = 'PDF';\n exportPdfBtn.disabled = false;\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n }\n });\n }\n\n // \u64A4\u9500 / \u91CD\u505A\n const undoBtn = document.getElementById('lp-undo');\n const redoBtn = document.getElementById('lp-redo');\n\n function updateUndoRedoButtons() {\n if (undoBtn) undoBtn.disabled = historyIndex <= 0;\n if (redoBtn) redoBtn.disabled = historyIndex >= history.length - 1;\n }\n\n if (undoBtn) undoBtn.addEventListener('click', undo);\n if (redoBtn) redoBtn.addEventListener('click', redo);\n\n const saveDeckBtn = document.getElementById('lp-save-deck');\n if (saveDeckBtn) {\n saveDeckBtn.addEventListener('click', () => {\n try {\n localStorage.setItem(getStorageKey(), JSON.stringify(goal));\n const original = saveDeckBtn.textContent;\n saveDeckBtn.textContent = '\u5DF2\u4FDD\u5B58';\n saveDeckBtn.disabled = true;\n setTimeout(() => {\n saveDeckBtn.textContent = original;\n saveDeckBtn.disabled = false;\n }, 1200);\n } catch (err) {\n console.warn('\u624B\u52A8\u4FDD\u5B58\u5931\u8D25', err);\n alert('\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6D4F\u89C8\u5668\u5B58\u50A8\u6743\u9650\u3002');\n }\n });\n }\n\n function syncAppearanceFromGoal() {\n const t = goal.theme || 'theme01';\n if (t === 'theme04' || t === 'theme05' || t === 'theme06' || t === 'theme07' || t === 'theme08' || t === 'theme09' || t === 'theme10') {\n const scheme = goal.colorScheme || (t === 'theme05' ? 'coral' : t === 'theme06' ? 'volt' : t === 'theme07' ? 'cold-white' : t === 'theme08' ? 'obsidian-gold' : t === 'theme09' ? 'ink-editorial' : t === 'theme10' ? 'gold-index' : 'green');\n const rawAppearance = goal.appearance || (t === 'theme07' ? 'light' : t === 'theme08' || t === 'theme09' || t === 'theme10' ? 'primary' : 'dark');\n /* theme08 / theme09 \u5F52\u4E00\u5316\uFF1Alight\u2192muted / dark\u2192primary */\n const appearance = (t === 'theme08' || t === 'theme09') ? ((rawAppearance === 'light' || rawAppearance === 'muted') ? 'muted' : 'primary') : rawAppearance;\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.setAttribute('data-appearance', appearance);\n } else if (t === 'theme03') {\n const scheme = goal.colorScheme || 'scheme-a';\n const appearance = goal.appearance || 'dark';\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.setAttribute('data-appearance', appearance);\n } else if (t === 'theme11') {\n // theme11 \u89C6\u89C9\u7531\u6BCF\u9875 mood class \u9A71\u52A8\uFF0C\u6839\u5C5E\u6027\u4EC5\u4F5C\u5360\u4F4D\u907F\u514D\u843D\u5165 else \u88AB\u8BEF\u5220\n const scheme = goal.colorScheme || 'theme11';\n const appearance = goal.appearance || 'light';\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.setAttribute('data-appearance', appearance);\n } else {\n const scheme = goal.colorScheme || (t === 'theme02' ? 'scheme-a' : 'light');\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.removeAttribute('data-appearance');\n }\n\n document.querySelectorAll('.lp-appearance-btn').forEach((btn) => {\n const appearanceValue = btn.getAttribute('data-appearance');\n const themeValue = btn.getAttribute('data-theme');\n if (!appearanceValue && !themeValue) return;\n\n let active = false;\n if (t === 'theme04' || t === 'theme05' || t === 'theme06' || t === 'theme07' || t === 'theme08' || t === 'theme09' || t === 'theme10') {\n if (themeValue) {\n active = themeValue === (goal.colorScheme || (t === 'theme05' ? 'coral' : t === 'theme06' ? 'volt' : t === 'theme07' ? 'cold-white' : t === 'theme08' ? 'obsidian-gold' : t === 'theme09' ? 'ink-editorial' : t === 'theme10' ? 'gold-index' : 'green'));\n } else if (appearanceValue) {\n const rawGoal = goal.appearance || (t === 'theme07' ? 'light' : t === 'theme08' || t === 'theme09' || t === 'theme10' ? 'primary' : 'dark');\n const normGoal = (t === 'theme08' || t === 'theme09') ? ((rawGoal === 'light' || rawGoal === 'muted') ? 'muted' : 'primary') : rawGoal;\n const normBtn = (t === 'theme08' || t === 'theme09') ? ((appearanceValue === 'light' || appearanceValue === 'muted') ? 'muted' : 'primary') : appearanceValue;\n active = normBtn === normGoal;\n }\n } else if (t === 'theme03') {\n active = appearanceValue === (goal.appearance || 'dark');\n } else if (t === 'theme11') {\n active = appearanceValue === (goal.appearance || 'light');\n } else {\n active = appearanceValue === (goal.colorScheme || (t === 'theme02' ? 'scheme-a' : 'light'));\n }\n btn.classList.toggle('lp-appearance-active', active);\n btn.setAttribute('aria-pressed', String(active));\n });\n }\n\n function applyAppearanceClientSide(newMode) {\n const t = goal.theme || 'theme01';\n\n if (t === 'theme04' || t === 'theme05' || t === 'theme06' || t === 'theme07' || t === 'theme08' || t === 'theme09' || t === 'theme10') {\n /* theme08 / theme09 \u5F52\u4E00\u5316\uFF1Alight\u2192muted / dark\u2192primary */\n const normMode = (t === 'theme08' || t === 'theme09') ? ((newMode === 'light' || newMode === 'muted') ? 'muted' : 'primary') : newMode;\n const curNorm = (t === 'theme08' || t === 'theme09') ? ((goal.appearance === 'light' || goal.appearance === 'muted') ? 'muted' : 'primary') : goal.appearance;\n if (normMode === curNorm) return;\n recordHistory();\n goal.appearance = newMode;\n autoSave();\n document.documentElement.setAttribute('data-appearance', newMode);\n } else if (t === 'theme03') {\n if (newMode === goal.appearance) return;\n recordHistory();\n goal.appearance = newMode;\n autoSave();\n document.documentElement.setAttribute('data-appearance', newMode);\n } else if (t === 'theme11') {\n // theme11 \u7531\u6BCF\u9875 mood class \u9A71\u52A8\uFF0C\u9876\u90E8\u5916\u89C2\u6309\u94AE\u4EC5\u540C\u6B65\u6839\u5C5E\u6027\u4E0E goal.appearance\n if (newMode === goal.appearance) return;\n recordHistory();\n goal.appearance = newMode;\n autoSave();\n document.documentElement.setAttribute('data-appearance', newMode);\n } else {\n if (newMode === goal.colorScheme) return;\n recordHistory();\n goal.colorScheme = newMode;\n autoSave();\n document.documentElement.setAttribute('data-theme', newMode);\n }\n\n syncAppearanceFromGoal();\n\n // \u5207\u6362\u5916\u89C2\u540E CSS \u53D8\u91CF\u503C\u53D8\u5316\uFF0C\u91CA\u653E\u5168\u90E8\u5B9E\u4F8B\u5E76\u91CD\u65B0\u521D\u59CB\u5316\u5F53\u524D active slide \u7684 ECharts\u3002\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n initActiveSlideECharts();\n }\n\n function applyToneClientSide(newTone) {\n const t = goal.theme || 'theme01';\n if (t !== 'theme04' && t !== 'theme05' && t !== 'theme06') return;\n if (newTone === goal.colorScheme) return;\n recordHistory();\n goal.colorScheme = newTone;\n autoSave();\n document.documentElement.setAttribute('data-theme', newTone);\n syncAppearanceFromGoal();\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n initActiveSlideECharts();\n }\n\n // \u5916\u89C2\u6A21\u5F0F\u5207\u6362\uFF08\u6D45\u8272 / \u6DF1\u8272 / \u4E3B\u9898 04 \u8272\u8C03\uFF09\n document.querySelectorAll('.lp-appearance-btn').forEach((btn) => {\n btn.addEventListener('click', () => {\n const appearance = btn.getAttribute('data-appearance');\n const tone = btn.getAttribute('data-theme');\n if (tone) applyToneClientSide(tone);\n if (appearance) applyAppearanceClientSide(appearance);\n });\n });\n\n // \u6DFB\u52A0\u5E7B\u706F\u7247\n function createDefaultSlide(layoutId, role) {\n const slide = { layout: layoutId, role, props: { title: '\u65B0\u5E7B\u706F\u7247' } };\n switch (role) {\n case 'cover': {\n if (layoutId === 'theme02_cover_v1') {\n slide.props = {\n kicker: '\u9713\u8679\u4E3B\u9898\u6F14\u793A',\n title: 'Neon Pitch',\n subtitle: '\u6DF1\u8272\u80CC\u666F + \u53CC\u914D\u8272\u65B9\u6848 + \u79D1\u6280\u611F\u7248\u5F0F',\n date: '2026.07'\n };\n } else {\n slide.props = { title: '\u5C01\u9762\u6807\u9898', subtitle: '\u526F\u6807\u9898' };\n }\n break;\n }\n case 'closing': {\n if (layoutId === 'theme01_conclusion_v1') {\n slide.props = {\n title: '\u6838\u5FC3\u7ED3\u8BBA',\n subtitle: 'CONCLUSION',\n points: [\n { item: 'AI \u6F14\u793A\u751F\u6210\u5DF2\u8FC8\u5165\u5B9E\u7528\u9636\u6BB5' },\n { item: '\u4E3B\u9898\u7CFB\u7EDF\u4E0E\u5E03\u5C40\u5F15\u64CE\u662F\u62A4\u57CE\u6CB3' },\n { item: '\u5F00\u653E\u751F\u6001\u5C06\u52A0\u901F\u884C\u4E1A\u843D\u5730' }\n ]\n };\n } else if (layoutId === 'theme02_closing_v1') {\n slide.props = {\n kicker: '\u611F\u8C22\u89C2\u770B',\n title: \"Let's Build Neon\",\n subtitle: '\u7528\u6DF1\u8272\u9713\u8679\u98CE\u683C\u70B9\u4EAE\u4E0B\u4E00\u573A\u6F14\u793A',\n cta: '\u5F00\u59CB\u4F7F\u7528',\n contact: '\u67E0\u6AAC\u56E2\u961F',\n email: 'hello@lemonppt.dev',\n link: 'lemonppt.dev'\n };\n } else {\n slide.props = {\n kicker: '\u611F\u8C22\u89C2\u770B',\n title: '\u671F\u5F85\u4E0E\u60A8\u5408\u4F5C',\n subtitle: '\u6709\u4EFB\u4F55\u95EE\u9898\uFF0C\u6B22\u8FCE\u968F\u65F6\u4EA4\u6D41',\n cta: '\u8054\u7CFB\u6211\u4EEC',\n contact: '\u67E0\u6AAC\u56E2\u961F',\n email: 'hello@lemonppt.dev',\n link: 'lemonppt.dev'\n };\n }\n break;\n }\n case 'quote': {\n if (layoutId === 'theme02_quote_v1') {\n slide.props = {\n quote: '\u8BBE\u8BA1\u7684\u672C\u8D28\u662F\u89E3\u51B3\u95EE\u9898\uFF0C\u800C\u597D\u7684\u8BBE\u8BA1\u662F\u8BA9\u590D\u6742\u53D8\u5F97\u7B80\u5355\u3002',\n author: '\u67E0\u6AAC\u56E2\u961F',\n role: '\u4EA7\u54C1\u7406\u5FF5'\n };\n } else {\n slide.props = {\n quote: '\u7B80\u6D01\u662F\u7EC8\u6781\u7684\u590D\u6742\u3002',\n author: '\u83B1\u6602\u7EB3\u591A\u00B7\u8FBE\u00B7\u82AC\u5947'\n };\n }\n break;\n }\n case 'content': {\n if (layoutId === 'theme01_risk_v1') {\n slide.props = {\n kicker: '\u98CE\u9669\u7814\u5224',\n title: '\u98CE\u9669\u6807\u9898',\n items: [\n { risk: '\u98CE\u9669 1', impact: '\u9AD8', response: '\u5E94\u5BF9\u7B56\u7565' },\n { risk: '\u98CE\u9669 2', impact: '\u4E2D', response: '\u5E94\u5BF9\u7B56\u7565' }\n ]\n };\n } else if (layoutId === 'theme01_outlook_v1') {\n slide.props = {\n kicker: '\u6295\u8D44\u5C55\u671B',\n title: '\u5C55\u671B\u6807\u9898',\n items: [\n { title: '\u8D8B\u52BF\u4E00', trend: '\u8D8B\u52BF\u5224\u65AD', action: '\u884C\u52A8\u5EFA\u8BAE' },\n { title: '\u8D8B\u52BF\u4E8C', trend: '\u8D8B\u52BF\u5224\u65AD', action: '\u884C\u52A8\u5EFA\u8BAE' }\n ]\n };\n } else if (layoutId === 'theme01_region_v1') {\n slide.props = {\n kicker: '\u5730\u533A\u5206\u5E03',\n title: '\u5E02\u573A\u5206\u5E03',\n regions: [\n { name: '\u5730\u533A\u4E00', value: '35%', change: '+2%', note: '\u8BF4\u660E\u6587\u5B57' },\n { name: '\u5730\u533A\u4E8C', value: '25%', change: '+1%', note: '\u8BF4\u660E\u6587\u5B57' }\n ]\n };\n } else if (layoutId === 'theme01_case_study') {\n slide.props = {\n kicker: '\u5178\u578B\u6848\u4F8B',\n title: '\u67E0\u6AAC\u79D1\u6280',\n subtitle: 'FROM CHALLENGER TO FRONTRUNNER',\n intro: '\u67E0\u6AAC\u79D1\u6280\u6210\u7ACB\u4E8E 2022 \u5E74\uFF0C\u4E13\u6CE8 AI \u6F14\u793A\u6587\u7A3F\u751F\u6210\u3002\u51ED\u501F\u81EA\u7814\u5E03\u5C40\u5F15\u64CE\u4E0E\u4E3B\u9898\u7CFB\u7EDF\uFF0C\u5728 18 \u4E2A\u6708\u5185\u5B8C\u6210\u4ECE 0 \u5230 1 \u7684\u8DE8\u8D8A\u3002',\n rounds: [\n { date: '2022\u00B706', round: '\u5929\u4F7F\u8F6E', valuation: '\u4F30\u503C \u00A51 \u4EBF', amount: '\u00A51,000\u4E07' },\n { date: '2023\u00B703', round: 'Pre-A', valuation: '\u4F30\u503C \u00A55 \u4EBF', amount: '\u00A55,000\u4E07' },\n { date: '2024\u00B701', round: 'A \u8F6E', valuation: '\u4F30\u503C \u00A520 \u4EBF', amount: '\u00A51.5\u4EBF' }\n ],\n quote: 'AI \u4E0D\u662F\u8981\u66FF\u4EE3\u4EBA\u505A PPT\uFF0C\u800C\u662F\u628A\u521B\u610F\u4ECE\u683C\u5F0F\u4E2D\u89E3\u653E\u51FA\u6765\u3002',\n quoteAuthor: '\u674E\u96F7\uFF0C\u8054\u5408\u521B\u59CB\u4EBA\u517C CEO',\n footnote: '\u6570\u636E\u6765\u6E90\uFF1A\u516C\u53F8\u516C\u5F00\u62AB\u9732\u4E0E\u5A92\u4F53\u6574\u7406\u3002'\n };\n } else if (layoutId === 'theme01_spotlight_grid') {\n slide.props = {\n kicker: '\u4E3B\u9898\u805A\u7126',\n title: '\u94B1\uFF0C\u6700\u7EC8\u7838\u5411\u4E86\u8FD9\u4E09\u4EF6\u4E8B',\n subtitle: '\u8D44\u91D1\u96C6\u4E2D\u6D41\u5411\u6A21\u578B\u80FD\u529B\u3001\u5E94\u7528\u573A\u666F\u4E0E\u57FA\u7840\u8BBE\u65BD',\n columns: [\n { tag: '\u6A21\u578B\u80FD\u529B', title: '\u901A\u7528\u5927\u6A21\u578B', description: '\u5934\u90E8\u5382\u5546\u6301\u7EED\u52A0\u7801\u57FA\u5EA7\u6A21\u578B\uFF0C\u89C4\u6A21\u6548\u5E94\u663E\u73B0\u3002' },\n { tag: '\u5E94\u7528\u573A\u666F', title: '\u5782\u76F4\u5E94\u7528', description: '\u529E\u516C\u3001\u8BBE\u8BA1\u3001\u7F16\u7A0B\u7B49\u573A\u666F\u7387\u5148\u8DD1\u51FA\u5546\u4E1A\u5316\u8DEF\u5F84\u3002' },\n { tag: '\u57FA\u7840\u8BBE\u65BD', title: 'AI \u57FA\u7840\u8BBE\u65BD', description: '\u7B97\u529B\u3001\u6570\u636E\u4E0E\u5DE5\u5177\u94FE\u6210\u4E3A\u652F\u6491\u4E0A\u5C42\u521B\u65B0\u7684\u5173\u952E\u5E95\u5EA7\u3002' }\n ]\n };\n } else if (layoutId === 'theme02_chapter_v1') {\n slide.props = {\n kicker: '\u7AE0\u8282',\n number: '01',\n title: '\u6838\u5FC3\u53D1\u73B0',\n subtitle: '\u4ECE\u6570\u636E\u5230\u6D1E\u5BDF\u7684\u5173\u952E\u8F6C\u6298'\n };\n } else if (layoutId === 'theme02_content_v1') {\n slide.props = {\n kicker: '\u8981\u70B9\u603B\u7ED3',\n title: '\u5185\u5BB9\u6807\u9898',\n subtitle: '\u7528\u9713\u8679 bullet \u5217\u8868\u7A81\u51FA\u5173\u952E\u4FE1\u606F',\n bullets: [\n '\u5168\u6808 AI \u6F14\u793A\u751F\u6210\uFF0C\u4ECE\u5927\u7EB2\u5230\u6210\u7A3F\u4E00\u952E\u5B8C\u6210',\n '\u53CC\u914D\u8272\u65B9\u6848\u81EA\u7531\u5207\u6362\uFF0C\u9002\u914D\u4E0D\u540C\u54C1\u724C\u8C03\u6027',\n 'SVG \u56FE\u8868 + \u6D1E\u5BDF\u9762\u677F\uFF0C\u6570\u636E\u8868\u8FBE\u66F4\u805A\u7126'\n ]\n };\n } else {\n slide.props = { title: '\u5185\u5BB9\u6807\u9898', points: ['\u65B0\u8981\u70B9'] };\n }\n break;\n }\n case 'chart': {\n if (layoutId === 'theme01_chart_treemap') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u77E9\u5F62\u6811\u56FE',\n unit: '',\n data: [\n { name: '\u5206\u7C7B A', value: 400, children: [\n { name: 'A-1', value: 200 },\n { name: 'A-2', value: 150 },\n { name: 'A-3', value: 50 }\n ]},\n { name: '\u5206\u7C7B B', value: 300, children: [\n { name: 'B-1', value: 180 },\n { name: 'B-2', value: 120 }\n ]}\n ]\n };\n } else if (layoutId === 'theme01_chart_sankey') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u6851\u57FA\u56FE',\n data: [\n { source: '\u8BBF\u95EE', target: '\u70B9\u51FB', value: 5000 },\n { source: '\u70B9\u51FB', target: '\u54A8\u8BE2', value: 3500 },\n { source: '\u54A8\u8BE2', target: '\u8BA2\u5355', value: 2200 },\n { source: '\u8BBF\u95EE', target: '\u8DF3\u51FA', value: 1500 },\n { source: '\u70B9\u51FB', target: '\u79BB\u5F00', value: 1000 },\n { source: '\u54A8\u8BE2', target: '\u6D41\u5931', value: 1300 }\n ]\n };\n } else if (layoutId === 'theme01_chart_sunburst') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u65ED\u65E5\u56FE',\n data: [\n { name: '\u7EBF\u4E0A', value: 600, children: [\n { name: '\u641C\u7D22', value: 300 },\n { name: '\u793E\u4EA4', value: 200 },\n { name: '\u76F4\u8BBF', value: 100 }\n ]},\n { name: '\u7EBF\u4E0B', value: 400, children: [\n { name: '\u95E8\u5E97', value: 250 },\n { name: '\u4EE3\u7406', value: 150 }\n ]}\n ]\n };\n } else if (layoutId === 'theme01_chart_gauge') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u4EEA\u8868\u76D8',\n value: 78,\n min: 0,\n max: 100,\n unit: '%'\n };\n } else if (layoutId === 'theme01_chart_heatmap') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u70ED\u529B\u56FE',\n xAxis: ['A', 'B', 'C', 'D'],\n yAxis: ['W1', 'W2', 'W3'],\n data: [\n ['A', 'W1', 12], ['B', 'W1', 45], ['C', 'W1', 78], ['D', 'W1', 23],\n ['A', 'W2', 67], ['B', 'W2', 89], ['C', 'W2', 34], ['D', 'W2', 56],\n ['A', 'W3', 91], ['B', 'W3', 15], ['C', 'W3', 66], ['D', 'W3', 40]\n ]\n };\n } else if (layoutId === 'theme01_chart_funnel') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u6F0F\u6597\u56FE',\n data: [\n { name: '\u8BBF\u95EE', value: 10000 },\n { name: '\u610F\u5411', value: 6500 },\n { name: '\u8BE2\u4EF7', value: 4200 },\n { name: '\u6210\u4EA4', value: 2800 }\n ]\n };\n } else if (layoutId === 'theme01_chart_radar') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u96F7\u8FBE\u56FE',\n indicators: [\n { name: '\u6027\u80FD', max: 100 },\n { name: '\u7A33\u5B9A\u6027', max: 100 },\n { name: '\u6613\u7528\u6027', max: 100 },\n { name: '\u6269\u5C55\u6027', max: 100 },\n { name: '\u5B89\u5168\u6027', max: 100 }\n ],\n data: [\n { name: '\u5F53\u524D', value: [85, 90, 78, 88, 82] },\n { name: '\u76EE\u6807', value: [95, 95, 90, 92, 95] }\n ]\n };\n } else if (layoutId === 'theme01_chart_graph') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u5173\u7CFB\u56FE',\n categories: [\n { name: '\u6838\u5FC3' },\n { name: '\u4EA7\u54C1' },\n { name: '\u6280\u672F' }\n ],\n nodes: [\n { name: '\u5E73\u53F0', value: 80, category: 0 },\n { name: '\u7528\u6237\u7AEF', value: 50, category: 1 },\n { name: '\u7BA1\u7406\u7AEF', value: 45, category: 1 },\n { name: '\u6570\u636E\u4E2D\u53F0', value: 60, category: 2 },\n { name: 'AI \u5F15\u64CE', value: 70, category: 2 },\n { name: '\u6E32\u67D3\u5668', value: 55, category: 2 },\n { name: '\u6A21\u677F\u5E93', value: 40, category: 1 }\n ],\n links: [\n { source: '\u5E73\u53F0', target: '\u7528\u6237\u7AEF', value: 5 },\n { source: '\u5E73\u53F0', target: '\u7BA1\u7406\u7AEF', value: 5 },\n { source: '\u5E73\u53F0', target: '\u6570\u636E\u4E2D\u53F0', value: 8 },\n { source: '\u6570\u636E\u4E2D\u53F0', target: 'AI \u5F15\u64CE', value: 6 },\n { source: 'AI \u5F15\u64CE', target: '\u6E32\u67D3\u5668', value: 4 },\n { source: '\u7BA1\u7406\u7AEF', target: '\u6A21\u677F\u5E93', value: 3 },\n { source: '\u7528\u6237\u7AEF', target: '\u6E32\u67D3\u5668', value: 4 }\n ]\n };\n } else if (layoutId === 'theme01_chart_bar3d') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '3D \u67F1\u72B6\u56FE',\n labels: ['Q1', 'Q2', 'Q3', 'Q4'],\n data: [120, 200, 150, 80]\n };\n } else if (layoutId === 'theme01_chart_wordcloud') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u8BCD\u4E91',\n words: [\n { name: 'AI', value: 100 },\n { name: 'PPT', value: 90 },\n { name: '\u6570\u636E\u53EF\u89C6\u5316', value: 85 },\n { name: '\u81EA\u52A8\u5316', value: 80 },\n { name: '\u5927\u5C4F', value: 75 },\n { name: '\u4E3B\u9898', value: 70 },\n { name: '\u6A21\u677F', value: 65 },\n { name: '\u667A\u80FD\u751F\u6210', value: 60 }\n ]\n };\n } else if (layoutId === 'theme01_chart_donut') {\n slide.props = {\n title: '\u8D5B\u9053\u5206\u5E03',\n kicker: '\u73AF\u5F62\u56FE',\n subtitle: '\u878D\u8D44\u989D\u5360\u6BD4',\n total: { value: '970 \u4EBF\u7F8E\u5143', label: '\u5168\u5E74\u5408\u8BA1' },\n segments: [\n { label: '\u901A\u7528\u5927\u6A21\u578B', labelEn: 'General LLM', value: '420', percent: '43.3%', color: 'var(--lp-blue)' },\n { label: '\u5782\u76F4\u5E94\u7528', labelEn: 'Vertical Apps', value: '245', percent: '25.3%', color: 'var(--lp-green)' },\n { label: 'AI \u57FA\u7840\u8BBE\u65BD', labelEn: 'AI Infra', value: '198', percent: '20.4%', color: 'var(--lp-amber)' },\n { label: 'AI \u82AF\u7247', labelEn: 'AI Chips', value: '97', percent: '10.0%', color: 'var(--lp-red)' },\n { label: '\u5176\u4ED6', labelEn: 'Others', value: '10', percent: '1.0%', color: 'var(--lp-violet)' }\n ]\n };\n } else if (layoutId === 'theme02_chart_funnel') {\n slide.props = {\n title: '\u8D44\u91D1\u96C6\u4E2D\u5EA6',\n kicker: '\u9713\u8679\u6F0F\u6597',\n subtitle: '\u8D44\u672C\u5982\u4F55\u5411\u5934\u90E8\u6536\u655B',\n data: [\n { name: '\u5168\u5E02\u573A', value: 970 },\n { name: '\u5934\u90E8 10 \u5BB6', value: 232 },\n { name: 'Top 3', value: 181 },\n { name: '\u5355\u7B14\u6700\u5927', value: 66 }\n ],\n showInsight: true,\n insight: {\n value: '24%',\n label: '\u5934\u90E8 10 \u5BB6 = \u5168\u5E02\u573A\u8FD1 1/4',\n description: '\u5C11\u6570\u72EC\u89D2\u517D\u53CD\u590D\u83B7\u5F97\u5DE8\u989D\u8FFD\u52A0\u6295\u8D44\uFF0C\u5934\u90E8\u683C\u5C40\u5DF2\u7136\u786E\u7ACB\u3002'\n },\n footnote: '\u6570\u636E\u6765\u6E90\uFF1AlemonPPT \u5185\u90E8\u7EDF\u8BA1 \u00B7 2026'\n };\n } else if (layoutId === 'theme02_chart_donut') {\n slide.props = {\n title: '\u8D5B\u9053\u5206\u5E03',\n kicker: '\u9713\u8679\u73AF\u5F62',\n subtitle: '\u878D\u8D44\u989D\u5360\u6BD4',\n total: { value: '970 \u4EBF\u7F8E\u5143', label: '\u5168\u5E74\u5408\u8BA1' },\n segments: [\n { label: '\u901A\u7528\u5927\u6A21\u578B', labelEn: 'General LLM', value: '420', percent: '43.3%', color: 'var(--lp-accent)' },\n { label: '\u5782\u76F4\u5E94\u7528', labelEn: 'Vertical Apps', value: '245', percent: '25.3%', color: 'var(--lp-accent-cool)' },\n { label: 'AI \u57FA\u7840\u8BBE\u65BD', labelEn: 'AI Infra', value: '198', percent: '20.4%', color: 'var(--lp-accent-2)' },\n { label: 'AI \u82AF\u7247', labelEn: 'AI Chips', value: '97', percent: '10.0%', color: 'var(--lp-violet)' },\n { label: '\u5176\u4ED6', labelEn: 'Others', value: '10', percent: '1.0%', color: 'var(--lp-cyan)' }\n ],\n footnote: '\u6570\u636E\u6765\u6E90\uFF1AlemonPPT \u5185\u90E8\u7EDF\u8BA1 \u00B7 2026',\n showInsight: true,\n insight: {\n value: '43.3%',\n label: '\u901A\u7528\u5927\u6A21\u578B\u5360\u6BD4\u6700\u9AD8',\n description: '\u901A\u7528\u5927\u6A21\u578B\u4E0E\u5782\u76F4\u5E94\u7528\u5408\u8BA1\u8D21\u732E\u8FD1\u4E03\u6210\u878D\u8D44\u989D\uFF0C\u8D5B\u9053\u5934\u90E8\u6548\u5E94\u660E\u663E\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_heatmap') {\n slide.props = {\n title: '\u7528\u6237\u6D3B\u8DC3\u5EA6\u70ED\u529B',\n kicker: '\u9713\u8679\u70ED\u529B',\n xAxis: ['\u5468\u4E00', '\u5468\u4E8C', '\u5468\u4E09', '\u5468\u56DB', '\u5468\u4E94'],\n yAxis: ['00:00', '06:00', '12:00', '18:00'],\n data: [\n ['\u5468\u4E00', '00:00', 120], ['\u5468\u4E00', '06:00', 80], ['\u5468\u4E00', '12:00', 320], ['\u5468\u4E00', '18:00', 280],\n ['\u5468\u4E8C', '00:00', 90], ['\u5468\u4E8C', '06:00', 60], ['\u5468\u4E8C', '12:00', 350], ['\u5468\u4E8C', '18:00', 300],\n ['\u5468\u4E09', '00:00', 100], ['\u5468\u4E09', '06:00', 70], ['\u5468\u4E09', '12:00', 380], ['\u5468\u4E09', '18:00', 340],\n ['\u5468\u56DB', '00:00', 110], ['\u5468\u56DB', '06:00', 75], ['\u5468\u56DB', '12:00', 360], ['\u5468\u56DB', '18:00', 310],\n ['\u5468\u4E94', '00:00', 130], ['\u5468\u4E94', '06:00', 85], ['\u5468\u4E94', '12:00', 400], ['\u5468\u4E94', '18:00', 360]\n ],\n showInsight: true,\n insight: {\n value: '400',\n label: '\u5468\u4E94 12:00 \u6D3B\u8DC3\u5EA6\u5CF0\u503C',\n description: '\u5DE5\u4F5C\u65E5\u5348\u95F4\u662F\u7528\u6237\u6D3B\u8DC3\u9AD8\u5CF0\uFF0C\u9002\u5408\u63A8\u9001\u5173\u952E\u5185\u5BB9\u4E0E\u8FD0\u8425\u6D3B\u52A8\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_radar') {\n slide.props = {\n title: '\u80FD\u529B\u96F7\u8FBE\u5BF9\u6BD4',\n kicker: '\u9713\u8679\u96F7\u8FBE',\n indicators: [\n { name: '\u6027\u80FD', max: 100 },\n { name: '\u7A33\u5B9A\u6027', max: 100 },\n { name: '\u6613\u7528\u6027', max: 100 },\n { name: '\u6269\u5C55\u6027', max: 100 },\n { name: '\u5B89\u5168\u6027', max: 100 }\n ],\n data: [\n { name: '\u5F53\u524D', value: [85, 90, 78, 88, 82] },\n { name: '\u76EE\u6807', value: [95, 95, 90, 92, 95] }\n ],\n showInsight: true,\n insight: {\n value: '84.6',\n label: '\u5F53\u524D\u7EFC\u5408\u80FD\u529B\u5747\u5206',\n description: '\u6027\u80FD\u4E0E\u7A33\u5B9A\u6027\u8868\u73B0\u7A81\u51FA\uFF0C\u6613\u7528\u6027\u4ECD\u6709\u63D0\u5347\u7A7A\u95F4\uFF0C\u5EFA\u8BAE\u4F18\u5148\u4F18\u5316\u4EA4\u4E92\u6D41\u7A0B\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_gauge') {\n slide.props = {\n title: '\u5E74\u5EA6\u76EE\u6807\u8FBE\u6210\u7387',\n kicker: '\u9713\u8679\u4EEA\u8868',\n value: 78,\n min: 0,\n max: 100,\n unit: '%',\n showInsight: true,\n insight: {\n value: '+22%',\n label: '\u8F83\u4E0A\u6708\u63D0\u5347',\n description: '\u5E74\u5EA6\u76EE\u6807\u5B8C\u6210\u7387\u5DF2\u63A5\u8FD1 80%\uFF0C\u6309\u5F53\u524D\u589E\u901F\u9884\u8BA1\u4E0B\u5B63\u5EA6\u53EF\u8D85\u989D\u8FBE\u6210\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_v1') {\n slide.props = {\n title: '\u5B63\u5EA6\u8425\u6536\u589E\u957F',\n kicker: '\u9713\u8679\u56FE\u8868',\n subtitle: '\u5168\u5E74\u56DB\u4E2A\u5B63\u5EA6\u6301\u7EED\u4E0A\u626C',\n type: 'bar',\n labels: ['Q1', 'Q2', 'Q3', 'Q4'],\n data: [1800, 2450, 3200, 4150],\n unit: '\u4E07\u5143',\n showInsight: true,\n insight: {\n headline: '11,600',\n subheadline: 'FULL-YEAR TOTAL',\n items: [\n { label: 'Q4 \u73AF\u6BD4', value: '+30%' },\n { label: '\u5E73\u5747\u5B63\u5EA6', value: '2,900' }\n ],\n badge: { text: 'Q4 \u589E\u957F\u6700\u5FEB', tone: 'accent' }\n }\n };\n } else {\n slide.props = { title: '\u56FE\u8868\u6807\u9898', labels: ['Q1', 'Q2', 'Q3', 'Q4'], data: [20, 45, 70, 95] };\n }\n break;\n }\n case 'bento': {\n if (layoutId === 'theme02_bento_v1') {\n slide.props = {\n kicker: 'Bento',\n title: '\u6A21\u5757\u5316\u6570\u636E\u5C55\u793A',\n subtitle: '\u7528\u4E0D\u89C4\u5219\u7F51\u683C\u7A81\u51FA\u5173\u952E\u6570\u5B57',\n items: [\n { label: '\u5E74\u5EA6\u8425\u6536', value: '1.2', unit: '\u4EBF\u5143', size: 'large' },\n { label: '\u7528\u6237\u589E\u957F', value: '+320', unit: '%', size: 'medium' },\n { label: 'NPS', value: '72', unit: '\u5206', size: 'small' },\n { label: '\u4ED8\u8D39\u5BA2\u6237', value: '1,200', unit: '+', size: 'small' },\n { label: '\u6A21\u677F\u6570', value: '150', unit: '+', size: 'small' }\n ]\n };\n } else {\n slide.props = {\n kicker: 'Bento',\n title: '\u6A21\u5757\u5316\u5C55\u793A',\n items: [\n { title: '\u6838\u5FC3\u6307\u6807', description: '\u5173\u952E\u6570\u636E\u4E00\u89C8', span: 'large' },\n { title: '\u7528\u6237\u589E\u957F', description: '\u6708\u6D3B\u8DC3\u7528\u6237\u6301\u7EED\u63D0\u5347', span: 'medium' },\n { title: '\u4EA7\u54C1\u7279\u6027', description: '\u6838\u5FC3\u80FD\u529B', span: 'small' },\n { title: '\u6280\u672F\u67B6\u6784', description: '\u7A33\u5B9A\u53EF\u9760', span: 'small' }\n ]\n };\n }\n break;\n }\n case 'gallery': {\n if (layoutId === 'theme01_filmstrip_v1') {\n slide.props = {\n kicker: '\u5F71\u50CF\u957F\u5377',\n title: '\u6545\u4E8B\u7EBF',\n images: [\n { url: '', caption: '\u9636\u6BB5\u4E00' },\n { url: '', caption: '\u9636\u6BB5\u4E8C' },\n { url: '', caption: '\u9636\u6BB5\u4E09' },\n { url: '', caption: '\u9636\u6BB5\u56DB' }\n ]\n };\n } else if (layoutId === 'theme02_gallery_v1') {\n slide.props = {\n kicker: '\u56FE\u96C6',\n title: '\u7CBE\u9009\u56FE\u7247',\n subtitle: '\u7528\u9713\u8679\u8FB9\u6846\u7F51\u683C\u5C55\u793A\u4EA7\u54C1\u573A\u666F',\n images: [\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 1' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 2' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 3' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 4' }\n ]\n };\n } else {\n slide.props = {\n kicker: '\u56FE\u96C6',\n title: '\u7CBE\u9009\u56FE\u7247',\n images: [\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 1' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 2' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 3' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 4' }\n ]\n };\n }\n break;\n }\n case 'table': {\n if (layoutId === 'theme01_table_data') {\n slide.props = {\n kicker: '\u6570\u636E\u8868',\n title: '\u5BF9\u6BD4\u4E00\u89C8',\n columns: [\n { key: 'rank', label: '\u6392\u540D', align: 'left' },\n { key: 'company', label: '\u516C\u53F8', align: 'left' },\n { key: 'track', label: '\u8D5B\u9053', align: 'left' },\n { key: 'amount', label: '\u878D\u8D44\u989D', align: 'right' }\n ],\n rows: [\n { rank: '1', company: '\u67E0\u6AAC\u79D1\u6280', track: 'SaaS', amount: '\u00A55,000\u4E07' },\n { rank: '2', company: '\u9752\u4E91\u6570\u636E', track: '\u4E91\u539F\u751F', amount: '\u00A53,200\u4E07' },\n { rank: '3', company: '\u7EA2\u661F\u667A\u80FD', track: 'AI \u786C\u4EF6', amount: '\u00A52,800\u4E07' },\n { rank: '4', company: '\u84DD\u6D77\u8D44\u672C', track: '\u91D1\u878D\u79D1\u6280', amount: '\u00A51,500\u4E07' }\n ],\n highlightRow: 0\n };\n } else if (layoutId === 'theme02_table_v1') {\n slide.props = {\n kicker: '\u6570\u636E\u8868',\n title: '\u65B9\u6848\u5BF9\u6BD4\u4E00\u89C8',\n subtitle: '\u591A\u7EF4\u5EA6\u8BC4\u4F30\uFF0C\u8F85\u52A9\u51B3\u7B56',\n headers: ['\u7EF4\u5EA6', '\u4F20\u7EDF\u65B9\u5F0F', '\u67E0\u6AAC PPT'],\n rows: [\n ['\u5236\u4F5C\u65F6\u95F4', '2-3 \u5929', '10 \u5206\u949F'],\n ['\u98CE\u683C\u4E00\u81F4\u6027', '\u4F9D\u8D56\u8BBE\u8BA1\u5E08', '\u4E3B\u9898\u7CFB\u7EDF\u4FDD\u8BC1'],\n ['\u6570\u636E\u66F4\u65B0', '\u624B\u52A8\u4FEE\u6539', '\u8054\u52A8\u5237\u65B0'],\n ['\u534F\u4F5C\u6548\u7387', '\u7248\u672C\u6DF7\u4E71', '\u5B9E\u65F6\u534F\u4F5C'],\n ['\u5BFC\u51FA\u683C\u5F0F', '\u5355\u4E00', 'PPTX / PDF / \u56FE\u7247']\n ],\n highlightFirstColumn: true\n };\n } else {\n slide.props = {\n kicker: '\u6570\u636E\u8868',\n title: '\u5BF9\u6BD4\u4E00\u89C8',\n headers: ['\u7EF4\u5EA6', '\u65B9\u6848 A', '\u65B9\u6848 B', '\u65B9\u6848 C'],\n rows: [\n ['\u6210\u672C', '\u4F4E', '\u4E2D', '\u9AD8'],\n ['\u6027\u80FD', '\u4E2D', '\u9AD8', '\u9AD8'],\n ['\u6613\u7528\u6027', '\u9AD8', '\u4E2D', '\u4F4E'],\n ['\u6269\u5C55\u6027', '\u4E2D', '\u9AD8', '\u9AD8']\n ],\n highlightFirstColumn: true\n };\n }\n break;\n }\n case 'tags': {\n if (layoutId === 'theme02_tags_v1') {\n slide.props = {\n kicker: '\u6807\u7B7E\u5899',\n title: '\u5173\u952E\u8BCD\u4E91',\n subtitle: '\u4E00\u773C\u770B\u61C2\u7528\u6237\u6700\u5173\u5FC3\u7684\u80FD\u529B',\n tags: [\n { label: 'AI', value: 98, tone: 'accent' },\n { label: '\u81EA\u52A8\u5316', value: 85, tone: 'positive' },\n { label: '\u53EF\u89C6\u5316', value: 72 },\n { label: '\u534F\u4F5C', value: 60 },\n { label: '\u4E91\u7AEF', value: 55 },\n { label: '\u6548\u7387', value: 80, tone: 'positive' },\n { label: '\u8BBE\u8BA1', value: 45 },\n { label: '\u54C1\u724C', value: 38 },\n { label: 'PPTX', value: 65, tone: 'accent' },\n { label: '\u667A\u80FD\u751F\u6210', value: 70 }\n ]\n };\n } else {\n slide.props = {\n kicker: '\u6807\u7B7E\u5899',\n title: '\u5173\u952E\u8BCD\u4E91',\n tags: [\n { label: 'AI', value: 98, tone: 'accent' },\n { label: '\u81EA\u52A8\u5316', value: 85, tone: 'positive' },\n { label: '\u53EF\u89C6\u5316', value: 72 },\n { label: '\u534F\u4F5C', value: 60 },\n { label: '\u4E91\u7AEF', value: 55 },\n { label: '\u6548\u7387', value: 80, tone: 'positive' },\n { label: '\u8BBE\u8BA1', value: 45 },\n { label: '\u54C1\u724C', value: 38 },\n { label: 'PPTX', value: 65, tone: 'accent' },\n { label: '\u667A\u80FD\u751F\u6210', value: 70 }\n ]\n };\n }\n break;\n }\n case 'process': {\n if (layoutId === 'theme02_process_v1') {\n slide.props = {\n kicker: '\u5B9E\u65BD\u8DEF\u5F84',\n title: '\u56DB\u6B65\u4E0A\u7EBF',\n subtitle: '\u4ECE\u9700\u6C42\u5230\u4EA4\u4ED8\u7684\u6807\u51C6\u5316\u6D41\u7A0B',\n steps: [\n { title: '\u9700\u6C42\u68B3\u7406', description: '\u660E\u786E\u53D7\u4F17\u3001\u76EE\u6807\u4E0E\u6838\u5FC3\u4FE1\u606F\u3002' },\n { title: 'AI \u751F\u6210', description: '\u8F93\u5165\u4E3B\u9898\uFF0C\u81EA\u52A8\u751F\u6210\u5927\u7EB2\u4E0E\u7248\u5F0F\u3002' },\n { title: '\u89C6\u89C9\u8C03\u4F18', description: '\u5207\u6362\u914D\u8272\u3001\u66FF\u6362\u56FE\u7247\u3001\u5FAE\u8C03\u6587\u6848\u3002' },\n { title: '\u5BFC\u51FA\u4EA4\u4ED8', description: '\u4E00\u952E\u5BFC\u51FA PPTX / PDF / \u56FE\u7247\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u6D41\u7A0B\u6807\u9898', steps: ['\u65B0\u6B65\u9AA4'] };\n }\n break;\n }\n case 'feature': {\n if (layoutId === 'theme02_feature_v1') {\n slide.props = {\n kicker: '\u4EA7\u54C1\u7279\u6027',\n title: '\u4E3A\u4EC0\u4E48\u9009\u62E9\u67E0\u6AAC PPT',\n subtitle: '\u4ECE\u751F\u6210\u5230\u4EA4\u4ED8\uFF0C\u6BCF\u4E2A\u73AF\u8282\u90FD\u66F4\u9AD8\u6548',\n features: [\n { title: 'AI \u4E00\u952E\u751F\u6210', description: '\u57FA\u4E8E\u5927\u6A21\u578B\u81EA\u52A8\u7406\u89E3\u9700\u6C42\uFF0C\u751F\u6210\u5B8C\u6574\u5927\u7EB2\u4E0E\u9875\u9762\u3002' },\n { title: '\u4E3B\u9898\u7CFB\u7EDF', description: '\u6DF1\u8272\u9713\u8679\u3001\u6D45\u8272\u73BB\u7483\u7B49\u591A\u79CD\u98CE\u683C\uFF0C\u5168\u5C40\u4E00\u952E\u5207\u6362\u3002' },\n { title: '\u6570\u636E\u53EF\u89C6\u5316', description: '\u5185\u7F6E SVG \u56FE\u8868\u4E0E\u6D1E\u5BDF\u9762\u677F\uFF0C\u6570\u636E\u8868\u8FBE\u66F4\u805A\u7126\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u7279\u6027\u6807\u9898', features: [{ title: '\u65B0\u7279\u6027', description: '' }] };\n }\n break;\n }\n case 'stats': {\n if (layoutId === 'theme02_metrics_v1') {\n slide.props = {\n kicker: '\u6570\u636E\u5899',\n title: '\u6838\u5FC3\u6307\u6807\u4E00\u89C8',\n subtitle: '\u7528\u9713\u8679\u5361\u7247\u5448\u73B0\u5173\u952E\u4E1A\u52A1\u6570\u636E',\n stats: [\n { label: '\u5E74\u5EA6\u8425\u6536', value: '1.2', unit: '\u4EBF\u5143', tone: 'accent' },\n { label: '\u7528\u6237\u589E\u957F', value: '+320', unit: '%', tone: 'cool' },\n { label: '\u5BA2\u6237\u6EE1\u610F\u5EA6', value: '98', unit: '%', tone: 'accent2' },\n { label: '\u56E2\u961F\u89C4\u6A21', value: '86', unit: '\u4EBA', tone: 'default' }\n ]\n };\n } else if (layoutId === 'theme02_stats_v1') {\n slide.props = {\n kicker: '\u6838\u5FC3\u6307\u6807',\n title: '\u589E\u957F\u6570\u636E\u4E00\u89C8',\n subtitle: '\u7528\u9713\u8679\u6570\u5B57\u5361\u7247\u7A81\u51FA\u5173\u952E\u6210\u679C',\n stats: [\n { label: '\u6CE8\u518C\u7528\u6237', value: '120K', unit: '+' },\n { label: '\u6708\u6D3B\u8DC3\u7528\u6237', value: '45K', unit: '+' },\n { label: '\u4ED8\u8D39\u8F6C\u5316\u7387', value: '8.5', unit: '%' },\n { label: '\u5BA2\u6237\u7EED\u8D39\u7387', value: '92', unit: '%' },\n { label: '\u6A21\u677F\u4F7F\u7528\u91CF', value: '2.4M', unit: '+' },\n { label: 'NPS \u8BC4\u5206', value: '72', unit: '' }\n ]\n };\n } else {\n slide.props = { title: '\u6570\u636E\u6807\u9898', stats: [{ label: '\u6307\u6807', value: 0, unit: '', change: '' }] };\n }\n break;\n }\n case 'metric': {\n if (layoutId === 'theme01_metric_big') {\n slide.props = {\n kicker: '\u6838\u5FC3\u6307\u6807',\n title: '\u5E74\u5EA6\u878D\u8D44\u603B\u989D',\n subtitle: '\u5168\u5E74\u5927\u989D\u878D\u8D44\u4E8B\u4EF6\u6C47\u603B',\n value: '970',\n unit: '\u4EBF\u7F8E\u5143',\n context: '\u521B\u5386\u53F2\u65B0\u9AD8\uFF0C\u5360\u5168\u7F8E\u98CE\u9669\u6295\u8D44\u8FD1\u4E09\u5206\u4E4B\u4E00',\n metrics: [\n { value: '97', label: '\u4E8B\u4EF6\u7B14\u6570', accent: false },\n { value: '\u224810 \u4EBF', label: '\u5E73\u5747\u5355\u7B14', accent: false },\n { value: '+41%', label: 'Q4 \u73AF\u6BD4', accent: true },\n { value: 'Q2-Q3', label: '\u9AD8\u5CF0\u533A\u95F4', accent: false }\n ]\n };\n } else if (layoutId === 'theme01_metric_triptych') {\n slide.props = {\n kicker: '\u6307\u6807\u603B\u89C8',\n title: '\u5173\u952E\u6570\u636E\u4E09\u89C6\u56FE',\n subtitle: '\u89C4\u6A21\u3001\u589E\u901F\u4E0E\u8D28\u91CF\u7684\u7EFC\u5408\u89C6\u89D2',\n panels: [\n { index: '01', title: '\u878D\u8D44\u603B\u989D', value: '970 \u4EBF', subtitle: '\u5168\u5E74\u7D2F\u8BA1\u89C4\u6A21\u521B\u65B0\u9AD8', chartType: 'bar', chartData: [120, 180, 320, 350] },\n { index: '02', title: '\u4E8B\u4EF6\u7B14\u6570', value: '97 \u7B14', subtitle: '\u5927\u989D\u4EA4\u6613\u6D3B\u8DC3\u5EA6\u63D0\u5347', chartType: 'line', chartData: [18, 22, 28, 29] },\n { index: '03', title: '\u5E73\u5747\u5355\u7B14', value: '\u224810 \u4EBF', subtitle: '\u5355\u7B14\u91D1\u989D\u6301\u7EED\u8D70\u9AD8', chartType: 'area', chartData: [6.7, 8.2, 11.4, 12.1] }\n ]\n };\n } else if (layoutId === 'theme02_metric_big') {\n slide.props = {\n kicker: '\u6838\u5FC3\u6307\u6807',\n title: '\u5E74\u5EA6\u878D\u8D44\u603B\u989D',\n subtitle: '\u5168\u5E74\u5927\u989D\u878D\u8D44\u4E8B\u4EF6\u6C47\u603B',\n value: '970',\n unit: '\u4EBF\u7F8E\u5143',\n context: '\u521B\u5386\u53F2\u65B0\u9AD8\uFF0C\u5360\u5168\u7F8E\u98CE\u9669\u6295\u8D44\u8FD1\u4E09\u5206\u4E4B\u4E00',\n metrics: [\n { value: '97', label: '\u4E8B\u4EF6\u7B14\u6570', accent: false },\n { value: '\u224810 \u4EBF', label: '\u5E73\u5747\u5355\u7B14', accent: false },\n { value: '+41%', label: 'Q4 \u73AF\u6BD4', accent: true },\n { value: 'Q2-Q3', label: '\u9AD8\u5CF0\u533A\u95F4', accent: false }\n ]\n };\n } else if (layoutId === 'theme02_progress_v1') {\n slide.props = {\n kicker: '\u8FBE\u6210\u5EA6',\n title: '\u5E74\u5EA6\u76EE\u6807\u5B8C\u6210\u8FDB\u5EA6',\n subtitle: '\u5173\u952E OKR \u5F53\u524D\u5B8C\u6210\u5EA6',\n items: [\n { label: 'ARR \u8425\u6536\u76EE\u6807', value: 8400, max: 12000, unit: '\u4E07\u5143' },\n { label: '\u4F01\u4E1A\u5BA2\u6237\u7B7E\u7EA6', value: 320, max: 500, unit: '\u5BB6' },\n { label: '\u4EA7\u54C1 NPS', value: 72, max: 100, unit: '\u5206' },\n { label: '\u56E2\u961F\u89C4\u6A21', value: 86, max: 120, unit: '\u4EBA' }\n ]\n };\n } else if (layoutId === 'theme02_delta_v1') {\n slide.props = {\n kicker: '\u4ECA\u6614\u5BF9\u7167',\n title: '\u5173\u952E\u6307\u6807\u53D8\u5316',\n subtitle: '\u53BB\u5E74\u540C\u671F vs \u5F53\u524D\u8868\u73B0',\n items: [\n { label: 'ARR \u8425\u6536', previous: 4200, current: 8400, unit: '\u4E07\u5143' },\n { label: '\u4F01\u4E1A\u5BA2\u6237', previous: 120, current: 320, unit: '\u5BB6' },\n { label: '\u4EA7\u54C1 NPS', previous: 58, current: 72, unit: '\u5206' },\n { label: '\u56E2\u961F\u89C4\u6A21', previous: 45, current: 86, unit: '\u4EBA' }\n ],\n footnote: '\u6570\u636E\u6765\u6E90\uFF1A\u516C\u53F8\u8D22\u52A1\u4E0E\u8FD0\u8425\u7CFB\u7EDF \u00B7 2026'\n };\n } else {\n slide.props = { title: '\u6307\u6807\u6807\u9898', metrics: [{ label: '\u6307\u6807', value: '0', unit: '', change: '' }] };\n }\n break;\n }\n case 'comparison': {\n if (layoutId === 'theme01_diptych_contrast') {\n slide.props = {\n kicker: '\u53CC\u8054\u5BF9\u6BD4',\n title: '\u6A21\u578B\u80FD\u529B vs \u5E94\u7528\u843D\u5730',\n left: { label: '\u6A21\u578B\u80FD\u529B', labelEn: 'FOUNDATION MODEL', imageUrl: '' },\n right: { label: '\u5E94\u7528\u843D\u5730', labelEn: 'APPLICATION', imageUrl: '' },\n centerCard: {\n title: '\u6838\u5FC3\u5DEE\u5F02',\n comparisons: [\n { leftValue: '66%', leftLabel: '\u8D44\u672C\u5360\u6BD4', rightValue: '25%', rightLabel: '\u8D44\u672C\u5360\u6BD4' },\n { leftValue: '420 \u4EBF', leftLabel: '\u878D\u8D44\u989D', rightValue: '245 \u4EBF', rightLabel: '\u878D\u8D44\u989D' },\n { leftValue: '\u5934\u90E8\u96C6\u4E2D', leftLabel: '\u7ADE\u4E89\u683C\u5C40', rightValue: '\u957F\u5C3E\u5206\u6563', rightLabel: '\u7ADE\u4E89\u683C\u5C40' }\n ],\n conclusion: '\u5E95\u5C42\u6A21\u578B\u4ECD\u662F\u8D44\u91D1\u4E3B\u6218\u573A\uFF0C\u4F46\u5782\u76F4\u5E94\u7528\u7684\u53D8\u73B0\u8DEF\u5F84\u66F4\u6E05\u6670\u3002'\n }\n };\n } else if (layoutId === 'theme02_comparison_v1') {\n slide.props = {\n kicker: '\u65B9\u6848\u5BF9\u6BD4',\n title: '\u4F20\u7EDF\u65B9\u5F0F vs \u67E0\u6AAC PPT',\n subtitle: '\u4ECE\u5C0F\u65F6\u7EA7\u5230\u5206\u949F\u7EA7\u7684\u6548\u7387\u8DC3\u8FC1',\n leftTitle: '\u4F20\u7EDF PPT \u5236\u4F5C',\n rightTitle: '\u67E0\u6AAC PPT \u751F\u6210',\n leftItems: ['\u624B\u52A8\u6392\u7248\u8017\u65F6\u4E45', '\u98CE\u683C\u96BE\u4EE5\u7EDF\u4E00', '\u534F\u4F5C\u7248\u672C\u6DF7\u4E71', '\u6570\u636E\u56FE\u8868\u66F4\u65B0\u6162'],\n rightItems: ['AI \u4E00\u952E\u751F\u6210\u7248\u5F0F', '\u4E3B\u9898\u7CFB\u7EDF\u5168\u5C40\u4E00\u81F4', '\u4E91\u7AEF\u5B9E\u65F6\u534F\u4F5C', '\u6570\u636E\u8054\u52A8\u81EA\u52A8\u5237\u65B0']\n };\n } else {\n slide.props = { title: '\u5BF9\u6BD4\u6807\u9898', left: '\u5DE6\u4FA7', right: '\u53F3\u4FA7' };\n }\n break;\n }\n case 'team': {\n if (layoutId === 'theme02_team_v1') {\n slide.props = {\n kicker: '\u6838\u5FC3\u56E2\u961F',\n title: '\u6211\u4EEC\u662F\u8C01',\n subtitle: '\u4E00\u652F\u76F8\u4FE1\u6280\u672F\u4E0E\u8BBE\u8BA1\u80FD\u6539\u53D8\u6F14\u793A\u7684\u56E2\u961F',\n members: [\n { name: '\u674E\u96F7', role: 'CEO', bio: '\u8FDE\u7EED\u521B\u4E1A\u8005\uFF0C\u524D\u5934\u90E8 SaaS \u4EA7\u54C1\u8D1F\u8D23\u4EBA\u3002' },\n { name: '\u97E9\u6885\u6885', role: 'CTO', bio: '\u5168\u6808\u5DE5\u7A0B\u5E08\uFF0C\u4E13\u6CE8 AI \u4E0E\u6E32\u67D3\u5F15\u64CE\u3002' },\n { name: '\u6797\u6D9B', role: '\u8BBE\u8BA1\u603B\u76D1', bio: '\u5341\u5E74\u54C1\u724C\u8BBE\u8BA1\u7ECF\u9A8C\uFF0C\u6DF1\u8015\u89C6\u89C9\u7CFB\u7EDF\u3002' },\n { name: '\u5409\u59C6', role: '\u589E\u957F\u8D1F\u8D23\u4EBA', bio: '\u6570\u636E\u9A71\u52A8\u589E\u957F\uFF0C\u64C5\u957F B2B \u89C4\u6A21\u5316\u83B7\u5BA2\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u56E2\u961F', members: [{ name: '\u65B0\u6210\u5458', role: '', bio: '', imageUrl: '' }] };\n }\n break;\n }\n case 'timeline': {\n if (layoutId === 'theme02_timeline_v1') {\n slide.props = {\n kicker: '\u91CC\u7A0B\u7891',\n title: '\u53D1\u5C55\u5386\u7A0B',\n subtitle: '\u4ECE\u60F3\u6CD5\u5230\u4EA7\u54C1\u7684\u5173\u952E\u8282\u70B9',\n milestones: [\n { date: '2022.06', title: '\u9879\u76EE\u542F\u52A8', description: '\u67E0\u6AAC PPT \u7ACB\u9879\uFF0C\u805A\u7126 AI \u6F14\u793A\u751F\u6210\u3002' },\n { date: '2023.03', title: '\u4EA7\u54C1\u4E0A\u7EBF', description: '\u9996\u4E2A\u7248\u672C\u53D1\u5E03\uFF0C\u652F\u6301\u4E00\u952E\u751F\u6210\u5B8C\u6574\u5927\u7EB2\u3002' },\n { date: '2024.01', title: '\u89C4\u6A21\u589E\u957F', description: '\u7D2F\u8BA1\u7528\u6237\u7A81\u7834 10 \u4E07\uFF0C\u4F01\u4E1A\u5BA2\u6237\u7B7E\u7EA6\u3002' },\n { date: '2025.09', title: '\u751F\u6001\u5F00\u653E', description: '\u5F00\u653E\u4E3B\u9898\u4E0E\u6A21\u677F\u5E02\u573A\uFF0C\u5171\u5EFA\u521B\u4F5C\u8005\u751F\u6001\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u65F6\u95F4\u7EBF', events: [{ date: '', title: '\u65B0\u91CC\u7A0B\u7891', description: '' }] };\n }\n break;\n }\n case 'faq': {\n if (layoutId === 'theme02_faq_v1') {\n slide.props = {\n kicker: '\u5E38\u89C1\u95EE\u9898',\n title: '\u4F60\u53EF\u80FD\u60F3\u77E5\u9053\u7684',\n subtitle: '\u5FEB\u901F\u4E86\u89E3\u67E0\u6AAC PPT \u7684\u6838\u5FC3\u80FD\u529B\u4E0E\u4F7F\u7528\u65B9\u5F0F',\n items: [\n { q: '\u67E0\u6AAC PPT \u9002\u5408\u4EC0\u4E48\u573A\u666F\uFF1F', a: '\u878D\u8D44\u8DEF\u6F14\u3001\u4EA7\u54C1\u53D1\u5E03\u3001\u5B63\u5EA6\u6C47\u62A5\u3001\u57F9\u8BAD\u8BFE\u4EF6\u7B49\u9700\u8981\u9AD8\u8D28\u91CF\u6F14\u793A\u7684\u573A\u5408\u3002' },\n { q: '\u751F\u6210\u540E\u80FD\u5426\u7EE7\u7EED\u7F16\u8F91\uFF1F', a: '\u53EF\u4EE5\u3002\u7F16\u8F91\u5668\u652F\u6301\u76F4\u63A5\u4FEE\u6539\u6587\u5B57\u3001\u66FF\u6362\u56FE\u7247\u3001\u8C03\u6574\u914D\u8272\u4E0E\u7248\u5F0F\u3002' },\n { q: '\u5BFC\u51FA\u683C\u5F0F\u6709\u54EA\u4E9B\uFF1F', a: '\u652F\u6301 PPTX\u3001PDF \u548C\u9AD8\u6E05\u56FE\u7247\u5BFC\u51FA\uFF0C\u6EE1\u8DB3\u4E0D\u540C\u5206\u53D1\u9700\u6C42\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u5E38\u89C1\u95EE\u9898', items: [{ q: '\u95EE\u9898', a: '\u56DE\u7B54' }] };\n }\n break;\n }\n case 'partners': {\n if (layoutId === 'theme02_partners_v1') {\n slide.props = {\n kicker: '\u5408\u4F5C\u4F19\u4F34',\n title: '\u4ED6\u4EEC\u90FD\u5728\u4F7F\u7528',\n subtitle: '\u4E0E\u9886\u5148\u4F01\u4E1A\u5171\u540C\u63A8\u52A8\u6F14\u793A\u65B9\u5F0F\u5347\u7EA7',\n partners: [\n { name: '\u4E91\u667A\u79D1\u6280', logoUrl: '' },\n { name: '\u672A\u6765\u8D44\u672C', logoUrl: '' },\n { name: '\u661F\u8FB0\u6570\u636E', logoUrl: '' },\n { name: '\u84DD\u6D77\u4F20\u5A92', logoUrl: '' },\n { name: '\u7EA2\u77F3\u54A8\u8BE2', logoUrl: '' },\n { name: '\u6781\u5149\u8BBE\u8BA1', logoUrl: '' },\n { name: '\u4E07\u8C61\u96C6\u56E2', logoUrl: '' },\n { name: '\u94F6\u6CB3\u521B\u6295', logoUrl: '' }\n ]\n };\n } else {\n slide.props = { title: '\u5408\u4F5C\u4F19\u4F34', partners: [{ name: '', logoUrl: '' }] };\n }\n break;\n }\n case 'roadmap': {\n if (layoutId === 'theme02_roadmap_v1') {\n slide.props = {\n kicker: '\u4EA7\u54C1\u8DEF\u7EBF',\n title: '\u672A\u6765 12 \u4E2A\u6708\u89C4\u5212',\n subtitle: '\u4ECE\u57FA\u7840\u80FD\u529B\u5230\u751F\u6001\u5F00\u653E\u7684\u6E05\u6670\u8282\u594F',\n phases: [\n { phase: 'Q1 \u57FA\u7840\u80FD\u529B', items: ['\u7F16\u8F91\u5668\u5185\u6838\u91CD\u6784', '\u4E3B\u9898\u7CFB\u7EDF 2.0', 'PPTX \u5BFC\u51FA\u4F18\u5316'] },\n { phase: 'Q2 \u667A\u80FD\u5347\u7EA7', items: ['AI \u591A\u8F6E\u5BF9\u8BDD\u751F\u6210', '\u81EA\u52A8\u914D\u56FE\u4E0E\u6570\u636E\u8054\u52A8', '\u534F\u4F5C\u8BC4\u8BBA'] },\n { phase: 'Q3 \u4F01\u4E1A\u573A\u666F', items: ['\u54C1\u724C\u8D44\u4EA7\u4E2D\u5FC3', '\u6743\u9650\u4E0E\u7248\u672C\u7BA1\u7406', '\u79C1\u6709\u90E8\u7F72'] },\n { phase: 'Q4 \u751F\u6001\u5F00\u653E', items: ['\u6A21\u677F\u5E02\u573A', '\u5F00\u53D1\u8005 SDK', '\u5F00\u653E\u4E3B\u9898\u534F\u8BAE'] }\n ]\n };\n } else {\n slide.props = { title: '\u8DEF\u7EBF\u56FE', phases: [{ phase: '\u9636\u6BB5', items: ['\u91CC\u7A0B\u7891'] }] };\n }\n break;\n }\n case 'pricing': {\n if (layoutId === 'theme02_pricing_v1') {\n slide.props = {\n kicker: '\u5B9A\u4EF7\u65B9\u6848',\n title: '\u9009\u62E9\u9002\u5408\u4F60\u7684\u65B9\u6848',\n subtitle: '\u4ECE\u4E2A\u4EBA\u521B\u4F5C\u8005\u5230\u4F01\u4E1A\u56E2\u961F\uFF0C\u7075\u6D3B\u6269\u5C55',\n tiers: [\n {\n name: '\u514D\u8D39\u7248',\n price: '\u00A50',\n period: '/ \u6708',\n features: ['\u6BCF\u6708 10 \u6B21\u751F\u6210', '3 \u79CD\u57FA\u7840\u4E3B\u9898', 'PNG \u5BFC\u51FA'],\n cta: '\u5F00\u59CB\u4F7F\u7528',\n highlight: false\n },\n {\n name: '\u4E13\u4E1A\u7248',\n price: '\u00A599',\n period: '/ \u6708',\n features: ['\u65E0\u9650\u6B21\u751F\u6210', '\u5168\u90E8\u4E3B\u9898\u4E0E\u7248\u5F0F', 'PPTX / PDF \u5BFC\u51FA', '\u4F18\u5148\u5BA2\u670D'],\n cta: '\u7ACB\u5373\u5347\u7EA7',\n highlight: true\n },\n {\n name: '\u56E2\u961F\u7248',\n price: '\u00A5399',\n period: '/ \u6708',\n features: ['5 \u4EBA\u534F\u4F5C', '\u54C1\u724C\u8D44\u4EA7\u4E2D\u5FC3', 'API \u63A5\u5165', '\u4E13\u5C5E\u5BA2\u6237\u6210\u529F'],\n cta: '\u8054\u7CFB\u9500\u552E',\n highlight: false\n }\n ]\n };\n } else {\n slide.props = { title: '\u5B9A\u4EF7', tiers: [{ name: '\u65B9\u6848', price: '', period: '', features: [''], cta: '' }] };\n }\n break;\n }\n case 'image': {\n if (layoutId === 'theme02_image_v1') {\n slide.props = {\n kicker: '\u89C6\u89C9\u5448\u73B0',\n title: '\u4E00\u5F20\u56FE\u8BB2\u6E05\u4EA7\u54C1\u4EF7\u503C',\n subtitle: '\u4E0A\u4F20\u9AD8\u6E05\u5927\u56FE\uFF0C\u7528\u9713\u8679\u6E10\u53D8\u53E0\u52A0\u6253\u9020\u79D1\u6280\u611F\u5C01\u9762\u3002',\n image: '',\n caption: '\u56FE\u6CE8\uFF1A\u4EA7\u54C1\u754C\u9762\u793A\u610F\u56FE'\n };\n } else {\n slide.props = { title: '\u56FE\u7247\u6807\u9898', subtitle: '\u526F\u6807\u9898' };\n }\n break;\n }\n case 'swot': {\n if (layoutId === 'theme02_swot_v1') {\n slide.props = {\n kicker: '\u6218\u7565\u5206\u6790',\n title: 'SWOT \u5206\u6790',\n subtitle: '\u770B\u6E05\u5185\u5916\u90E8\u73AF\u5883\uFF0C\u5236\u5B9A\u4E0B\u4E00\u6B65\u7B56\u7565',\n strength: 'AI \u751F\u6210\u80FD\u529B\u9886\u5148\uFF0C\u4E3B\u9898\u7CFB\u7EDF\u53EF\u6269\u5C55\uFF0C\u793E\u533A\u6D3B\u8DC3\u5EA6\u9AD8\u3002',\n weakness: '\u54C1\u724C\u77E5\u540D\u5EA6\u4ECD\u5728\u5EFA\u7ACB\uFF0C\u90E8\u5206\u9AD8\u7EA7\u7248\u5F0F\u4F9D\u8D56\u4EBA\u5DE5\u8BBE\u8BA1\u3002',\n opportunity: '\u4F01\u4E1A\u6570\u5B57\u5316\u6C47\u62A5\u9700\u6C42\u589E\u957F\uFF0CAI \u5DE5\u5177\u63A5\u53D7\u5EA6\u63D0\u5347\u3002',\n threat: '\u5927\u5382\u540C\u7C7B\u4EA7\u54C1\u5E03\u5C40\u52A0\u901F\uFF0C\u7528\u6237\u5BA1\u7F8E\u8981\u6C42\u6301\u7EED\u63D0\u9AD8\u3002'\n };\n } else {\n slide.props = { title: 'SWOT \u5206\u6790', strength: '\u4F18\u52BF', weakness: '\u52A3\u52BF', opportunity: '\u673A\u4F1A', threat: '\u5A01\u80C1' };\n }\n break;\n }\n case 'pest': {\n if (layoutId === 'theme02_pest_v1') {\n slide.props = {\n kicker: '\u5B8F\u89C2\u5206\u6790',\n title: 'PEST \u5206\u6790',\n subtitle: '\u4ECE\u653F\u7B56\u3001\u7ECF\u6D4E\u3001\u793E\u4F1A\u3001\u6280\u672F\u56DB\u7EF4\u5EA6\u6D1E\u5BDF\u73AF\u5883',\n political: 'AI \u5185\u5BB9\u751F\u6210\u76D1\u7BA1\u6846\u67B6\u9010\u6B65\u5B8C\u5584\uFF0C\u5408\u89C4\u5316\u6210\u4E3A\u4EA7\u54C1\u51C6\u5165\u95E8\u69DB\u3002',\n economic: '\u4F01\u4E1A\u964D\u672C\u589E\u6548\u9700\u6C42\u5F3A\u70C8\uFF0C\u6570\u5B57\u5316\u5DE5\u5177\u9884\u7B97\u5360\u6BD4\u6301\u7EED\u63D0\u5347\u3002',\n social: '\u8FDC\u7A0B\u534F\u4F5C\u5E38\u6001\u5316\uFF0C\u5BF9\u8F7B\u91CF\u5316\u3001\u53EF\u5206\u4EAB\u6F14\u793A\u5185\u5BB9\u7684\u9700\u6C42\u6FC0\u589E\u3002',\n technological: '\u5927\u6A21\u578B\u591A\u6A21\u6001\u80FD\u529B\u5FEB\u901F\u8FDB\u6B65\uFF0C\u6587\u672C\u5230\u5E7B\u706F\u7247\u7684\u751F\u6210\u8D28\u91CF\u663E\u8457\u63D0\u9AD8\u3002'\n };\n } else {\n slide.props = { title: 'PEST \u5206\u6790', political: '\u653F\u6CBB\u73AF\u5883', economic: '\u7ECF\u6D4E\u73AF\u5883', social: '\u793E\u4F1A\u73AF\u5883', technological: '\u6280\u672F\u73AF\u5883' };\n }\n break;\n }\n case 'testimonial': {\n if (layoutId === 'theme02_testimonial_v1') {\n slide.props = {\n kicker: '\u5BA2\u6237\u8BC4\u4EF7',\n quote: '\u67E0\u6AAC PPT \u5E2E\u52A9\u6211\u4EEC\u628A\u6C47\u62A5\u5236\u4F5C\u65F6\u95F4\u4ECE 2 \u5929\u7F29\u77ED\u5230 2 \u5C0F\u65F6\uFF0C\u56E2\u961F\u53EF\u4EE5\u628A\u66F4\u591A\u7CBE\u529B\u653E\u5728\u4E1A\u52A1\u601D\u8003\u4E0A\u3002',\n author: '\u9648\u6653\u660E',\n role: '\u4EA7\u54C1\u603B\u76D1',\n company: '\u672A\u6765\u79D1\u6280\u6709\u9650\u516C\u53F8',\n avatarUrl: ''\n };\n } else {\n slide.props = { kicker: '\u5BA2\u6237\u8BC4\u4EF7', quote: '\u4EA7\u54C1\u5E2E\u52A9\u6211\u4EEC\u628A\u6C47\u62A5\u6548\u7387\u63D0\u5347\u4E86 3 \u500D\u3002', author: '\u5F20\u4E09', role: '\u4EA7\u54C1\u7ECF\u7406', company: '\u793A\u4F8B\u79D1\u6280' };\n }\n break;\n }\n case 'tableOfContents': {\n if (layoutId === 'theme02_table_of_contents_v1') {\n slide.props = {\n title: '\u76EE\u5F55',\n subtitle: 'CONTENTS',\n items: [\n { title: '\u5E02\u573A\u80CC\u666F\u4E0E\u673A\u4F1A', page: '02' },\n { title: '\u4EA7\u54C1\u65B9\u6848\u4E0E\u4F18\u52BF', page: '04' },\n { title: '\u5546\u4E1A\u6A21\u5F0F\u4E0E\u589E\u957F', page: '06' },\n { title: '\u56E2\u961F\u4E0E\u878D\u8D44\u8BA1\u5212', page: '08' }\n ]\n };\n } else {\n slide.props = { title: '\u76EE\u5F55', items: ['\u76EE\u5F55\u9879'] };\n }\n break;\n }\n }\n return slide;\n }\n\n const addSlideBtn = document.getElementById('lp-add-slide');\n const addSlideModal = document.getElementById('lp-add-slide-modal');\n const addSlideConfirm = document.getElementById('lp-add-slide-confirm');\n let selectedLayoutId = null;\n let selectedRole = null;\n\n function closeAddSlideModal() {\n if (addSlideModal) addSlideModal.setAttribute('hidden', '');\n addSlideModal?.querySelectorAll('.lp-add-slide-option').forEach((el) => el.classList.remove('selected'));\n selectedLayoutId = null;\n selectedRole = null;\n if (addSlideConfirm) addSlideConfirm.disabled = true;\n }\n\n function openAddSlideModal() {\n if (addSlideModal) addSlideModal.removeAttribute('hidden');\n }\n\n function confirmAddSlide() {\n if (!selectedLayoutId) return;\n const newSlide = createDefaultSlide(selectedLayoutId, selectedRole || selectedLayoutId.split('_')[0]);\n recordHistory();\n goal.slides.push(newSlide);\n selectedSlideIdx = goal.slides.length - 1;\n // current \u4F1A\u5728 rebuildSlidesAndThumbnails \u4E2D\u540C\u6B65\uFF0C\u907F\u514D\u5728 DOM \u91CD\u5EFA\u524D\u8BBF\u95EE\u8D8A\u754C\n autoSave();\n closeAddSlideModal();\n reloadEditor();\n }\n\n if (addSlideBtn) {\n addSlideBtn.addEventListener('click', openAddSlideModal);\n }\n\n if (addSlideModal) {\n addSlideModal.addEventListener('click', (e) => {\n const target = e.target;\n if (target.closest('[data-close-modal]')) {\n closeAddSlideModal();\n return;\n }\n const option = target.closest('.lp-add-slide-option');\n if (option) {\n addSlideModal.querySelectorAll('.lp-add-slide-option').forEach((el) => el.classList.remove('selected'));\n option.classList.add('selected');\n selectedLayoutId = option.getAttribute('data-layout');\n selectedRole = option.getAttribute('data-role');\n if (addSlideConfirm) addSlideConfirm.disabled = false;\n }\n });\n }\n\n if (addSlideConfirm) {\n addSlideConfirm.addEventListener('click', confirmAddSlide);\n }\n\n updateUndoRedoButtons();\n\n // \u7FFB\u9875\u811A\u672C\n let slides = Array.from(document.querySelectorAll('.lp-slide-wrapper'));\n let thumbnails = Array.from(document.querySelectorAll('.lp-thumbnail'));\n const prevBtn = document.getElementById('lp-prev');\n const nextBtn = document.getElementById('lp-next');\n const currentLabel = document.getElementById('lp-current');\n const deck = document.querySelector('.lp-deck');\n let current = 0;\n let isTransitioning = false;\n let cleanupTimer = null;\n let dragSrcIndex = null;\n let dragDropIndicator = null;\n\n function refreshSlidesAndThumbnails() {\n slides = Array.from(document.querySelectorAll('.lp-slide-wrapper'));\n thumbnails = Array.from(document.querySelectorAll('.lp-thumbnail'));\n }\n\n function updateEditorUI() {\n thumbnails.forEach((thumb, index) => {\n thumb.classList.toggle('active', index === current);\n });\n const activeThumb = thumbnails[current];\n if (activeThumb) activeThumb.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n const labelEl = document.getElementById('lp-current');\n if (labelEl) labelEl.textContent = String(current + 1);\n if (prevBtn) prevBtn.disabled = current === 0 || isTransitioning;\n if (nextBtn) nextBtn.disabled = current === slides.length - 1 || isTransitioning;\n }\n\n function resetTransitionState() {\n slides.forEach((slide) => slide.classList.remove('enter', 'leave'));\n if (deck) {\n deck.setAttribute('data-lp-transition', 'none');\n deck.removeAttribute('data-lp-direction');\n }\n isTransitioning = false;\n updateClasses();\n }\n\n function updateClasses() {\n updateEditorUI();\n }\n\n function goTo(index) {\n if (index === current || index < 0 || index >= slides.length || isTransitioning) {\n return;\n }\n const leaving = slides[current];\n const entering = slides[index];\n const transition = entering.dataset.lpTransition || 'none';\n const direction = index > current ? 'next' : 'prev';\n\n if (transition === 'none') {\n current = index;\n if (deck) {\n deck.setAttribute('data-lp-transition', 'none');\n deck.removeAttribute('data-lp-direction');\n }\n leaving.classList.remove('active');\n entering.classList.add('active');\n updateClasses();\n if (typeof selectSlide === 'function') selectSlide(current);\n // \u5207\u9875\u540E\u91CA\u653E\u79BB\u5F00 slide \u7684\u56FE\u8868\u5E76\u4EC5\u521D\u59CB\u5316\u65B0 active slide \u7684 ECharts\u3002\n requestAnimationFrame(() => {\n disposeEChartsInWrapper(leaving);\n initActiveSlideECharts();\n });\n return;\n }\n\n isTransitioning = true;\n current = index;\n updateEditorUI();\n\n if (deck) {\n deck.setAttribute('data-lp-transition', transition);\n deck.setAttribute('data-lp-direction', direction);\n }\n\n leaving.classList.add('leave');\n entering.classList.add('enter');\n // \u5148\u7981\u7528 transition\uFF0C\u8BA9 leave/enter \u7684\u521D\u59CB\u72B6\u6001\u76F4\u63A5\u843D\u4F4D\uFF0C\n // \u907F\u514D\u4ECE\u4E0A\u4E00\u72B6\u6001\u7684 translateX \u63D2\u503C\uFF1B\u968F\u540E\u6062\u590D transition \u518D\u5207\u6362 active\u3002\n leaving.style.transition = 'none';\n entering.style.transition = 'none';\n void entering.offsetWidth;\n leaving.style.transition = '';\n entering.style.transition = '';\n leaving.classList.remove('active');\n entering.classList.add('active');\n\n function finish() {\n entering.removeEventListener('transitionend', finish);\n clearTimeout(cleanupTimer);\n resetTransitionState();\n // \u52A8\u753B\u7ED3\u675F\u540E\u91CA\u653E\u79BB\u5F00 slide \u7684\u56FE\u8868\uFF0C\u5E76\u4EC5\u5BF9\u5F53\u524D active slide \u7684 ECharts \u8FDB\u884C resize/\u521D\u59CB\u5316\n requestAnimationFrame(() => {\n disposeEChartsInWrapper(leaving);\n initActiveSlideECharts();\n });\n }\n entering.addEventListener('transitionend', finish);\n cleanupTimer = setTimeout(finish, 600);\n\n if (typeof selectSlide === 'function') selectSlide(current);\n }\n\n if (prevBtn) prevBtn.addEventListener('click', () => goTo(current - 1));\n if (nextBtn) nextBtn.addEventListener('click', () => goTo(current + 1));\n const deleteConfirmToast = document.getElementById('lp-delete-confirm-toast');\n const deleteConfirmText = document.getElementById('lp-delete-confirm-text');\n const deleteConfirmCancel = document.getElementById('lp-delete-confirm-cancel');\n const deleteConfirmConfirm = document.getElementById('lp-delete-confirm-confirm');\n let pendingDeleteIndex = null;\n\n function showDeleteConfirm(index, triggerEl) {\n if (goal.slides.length <= 1) {\n alert('\u81F3\u5C11\u4FDD\u7559\u4E00\u9875\u5E7B\u706F\u7247\u3002');\n return;\n }\n pendingDeleteIndex = index;\n if (deleteConfirmText) {\n deleteConfirmText.textContent = '\u786E\u5B9A\u5220\u9664\u7B2C ' + (index + 1) + ' \u9875\u5E7B\u706F\u7247\uFF1F';\n }\n if (deleteConfirmToast) {\n deleteConfirmToast.removeAttribute('hidden');\n positionDeleteConfirmToast(triggerEl);\n }\n }\n\n function positionDeleteConfirmToast(triggerEl) {\n if (!deleteConfirmToast || !triggerEl) return;\n const rect = triggerEl.getBoundingClientRect();\n const toastRect = deleteConfirmToast.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const gap = 8;\n let top = rect.bottom + gap;\n let left = rect.left + rect.width / 2 - toastRect.width / 2;\n if (left < gap) left = gap;\n if (left + toastRect.width > viewportWidth - gap) {\n left = viewportWidth - toastRect.width - gap;\n }\n if (top + toastRect.height > viewportHeight - gap) {\n top = rect.top - toastRect.height - gap;\n }\n deleteConfirmToast.style.top = top + 'px';\n deleteConfirmToast.style.left = left + 'px';\n }\n\n function hideDeleteConfirm() {\n pendingDeleteIndex = null;\n if (deleteConfirmToast) {\n deleteConfirmToast.setAttribute('hidden', '');\n deleteConfirmToast.style.top = '';\n deleteConfirmToast.style.left = '';\n }\n }\n\n function confirmDelete() {\n if (pendingDeleteIndex == null) return;\n const index = pendingDeleteIndex;\n hideDeleteConfirm();\n recordHistory();\n goal.slides.splice(index, 1);\n selectedSlideIdx = Math.min(index, goal.slides.length - 1);\n // current \u4F1A\u5728 rebuildSlidesAndThumbnails \u4E2D\u540C\u6B65\n autoSave();\n reloadEditor();\n }\n\n if (deleteConfirmCancel) {\n deleteConfirmCancel.addEventListener('click', hideDeleteConfirm);\n }\n if (deleteConfirmConfirm) {\n deleteConfirmConfirm.addEventListener('click', confirmDelete);\n }\n if (deleteConfirmToast) {\n deleteConfirmToast.addEventListener('click', (e) => {\n if (e.target === deleteConfirmToast) {\n hideDeleteConfirm();\n }\n });\n }\n\n function attachThumbnailListeners() {\n thumbnails.forEach((thumb) => {\n thumb.addEventListener('click', (e) => {\n const deleteBtn = e.target.closest && e.target.closest('.lp-thumbnail-delete');\n if (deleteBtn) {\n e.stopPropagation();\n const index = Number(deleteBtn.getAttribute('data-index'));\n showDeleteConfirm(index, deleteBtn);\n return;\n }\n goTo(Number(thumb.dataset.index));\n });\n\n // \u7F29\u7565\u56FE\u6539\u4E3A div \u540E\uFF0C\u8865\u5145\u952E\u76D8\u9009\u4E2D\u652F\u6301\n thumb.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n goTo(Number(thumb.dataset.index));\n }\n });\n });\n }\n attachThumbnailListeners();\n\n // \u7F29\u7565\u56FE\u865A\u62DF\u6EDA\u52A8\uFF1A\u7528 IntersectionObserver \u5378\u8F7D\u89C6\u53E3\u5916 slide \u6E32\u67D3 DOM\uFF0C\u4EC5\u4FDD\u7559\u5360\u4F4D\u6846\u67B6\u4E0E\u6587\u5B57\u4FE1\u606F\u3002\n const thumbnailRenderCache = {};\n let thumbnailObserver = null;\n\n function getThumbnailCacheKey(thumb) {\n return 'thumb-' + thumb.dataset.index;\n }\n\n function mountThumbnailRender(thumb) {\n const render = thumb.querySelector('.lp-thumbnail-render');\n if (!render) return;\n if (thumb.dataset.lpThumbMounted === 'true') return;\n const key = getThumbnailCacheKey(thumb);\n const html = thumbnailRenderCache[key];\n if (html != null) {\n render.innerHTML = html;\n }\n thumb.dataset.lpThumbMounted = 'true';\n }\n\n function unmountThumbnailRender(thumb) {\n const render = thumb.querySelector('.lp-thumbnail-render');\n if (!render) return;\n if (thumb.dataset.lpThumbMounted === 'false') return;\n const key = getThumbnailCacheKey(thumb);\n thumbnailRenderCache[key] = render.innerHTML;\n render.innerHTML = '';\n thumb.dataset.lpThumbMounted = 'false';\n }\n\n function initThumbnailVirtualScroll() {\n const container = document.querySelector('.lp-editor-left-panel');\n if (!container || !('IntersectionObserver' in window)) return;\n\n if (thumbnailObserver) {\n thumbnailObserver.disconnect();\n thumbnailObserver = null;\n }\n\n // \u9ED8\u8BA4\u6240\u6709\u7F29\u7565\u56FE\u5904\u4E8E\u6302\u8F7D\u72B6\u6001\n thumbnails.forEach((thumb) => {\n thumb.dataset.lpThumbMounted = 'true';\n });\n\n thumbnailObserver = new IntersectionObserver((entries) => {\n entries.forEach((entry) => {\n const thumb = entry.target;\n const index = Number(thumb.dataset.index);\n // \u5F53\u524D\u6FC0\u6D3B\u9875\u59CB\u7EC8\u4FDD\u7559\u6E32\u67D3\uFF0C\u907F\u514D\u5207\u6362\u65F6\u767D\u5C4F\n if (index === current || entry.isIntersecting) {\n mountThumbnailRender(thumb);\n } else {\n unmountThumbnailRender(thumb);\n }\n });\n }, {\n root: container,\n rootMargin: '200px 0px 200px 0px',\n threshold: 0,\n });\n\n thumbnails.forEach((thumb) => thumbnailObserver.observe(thumb));\n }\n\n function disconnectThumbnailVirtualScroll() {\n if (thumbnailObserver) {\n thumbnailObserver.disconnect();\n thumbnailObserver = null;\n }\n }\n initThumbnailVirtualScroll();\n\n function attachDragAndDropListeners() {\n const container = document.querySelector('.lp-editor-thumbnails');\n if (!container) return;\n\n function getOrCreateDropIndicator() {\n if (dragDropIndicator && dragDropIndicator.parentElement) return dragDropIndicator;\n dragDropIndicator = document.createElement('div');\n dragDropIndicator.className = 'lp-thumbnail-drop-indicator';\n container.appendChild(dragDropIndicator);\n return dragDropIndicator;\n }\n\n function clearDragState() {\n dragSrcIndex = null;\n thumbnails.forEach((t) => t.classList.remove('dragging', 'drag-over'));\n if (dragDropIndicator) dragDropIndicator.classList.remove('visible');\n }\n\n function positionDropIndicator(target, before) {\n const indicator = getOrCreateDropIndicator();\n if (!indicator || !target) return;\n const rect = target.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const top = before ? rect.top - containerRect.top - 1 : rect.bottom - containerRect.top - 1;\n indicator.style.top = top + 'px';\n indicator.classList.add('visible');\n }\n\n container.addEventListener('dragstart', (e) => {\n const thumb = e.target && e.target.closest && e.target.closest('.lp-thumbnail');\n if (!thumb) return;\n // \u70B9\u51FB\u5220\u9664\u6309\u94AE\u65F6\u4E0D\u5E94\u89E6\u53D1\u62D6\u62FD\n const deleteBtn = e.target.closest && e.target.closest('.lp-thumbnail-delete');\n if (deleteBtn) {\n e.preventDefault();\n return;\n }\n if (isTransitioning || goal.slides.length <= 1) {\n e.preventDefault();\n return;\n }\n const src = Number(thumb.dataset.index);\n if (Number.isNaN(src)) return;\n dragSrcIndex = src;\n hideDeleteConfirm();\n thumb.classList.add('dragging');\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', String(src));\n try {\n e.dataTransfer.setDragImage(thumb, 16, 16);\n } catch (_) {}\n }\n });\n\n container.addEventListener('dragover', (e) => {\n if (dragSrcIndex == null) return;\n e.preventDefault();\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';\n const targetThumb = e.target && e.target.closest && e.target.closest('.lp-thumbnail');\n const indicator = getOrCreateDropIndicator();\n if (!targetThumb) {\n const lastThumb = container.querySelector('.lp-thumbnail:last-child');\n if (lastThumb && dragSrcIndex !== goal.slides.length - 1) {\n positionDropIndicator(lastThumb, false);\n } else {\n indicator.classList.remove('visible');\n }\n thumbnails.forEach((t) => t.classList.remove('drag-over'));\n return;\n }\n const targetIndex = Number(targetThumb.dataset.index);\n if (Number.isNaN(targetIndex)) return;\n const rect = targetThumb.getBoundingClientRect();\n const before = e.clientY < rect.top + rect.height / 2;\n if (targetIndex === dragSrcIndex) {\n indicator.classList.remove('visible');\n targetThumb.classList.remove('drag-over');\n return;\n }\n thumbnails.forEach((t) => t.classList.remove('drag-over'));\n targetThumb.classList.add('drag-over');\n positionDropIndicator(targetThumb, before);\n });\n\n container.addEventListener('dragleave', (e) => {\n if (dragSrcIndex == null) return;\n if (!container.contains(e.relatedTarget)) {\n if (dragDropIndicator) dragDropIndicator.classList.remove('visible');\n thumbnails.forEach((t) => t.classList.remove('drag-over'));\n }\n });\n\n container.addEventListener('drop', (e) => {\n if (dragSrcIndex == null) return;\n e.preventDefault();\n const src = dragSrcIndex;\n const targetThumb = e.target && e.target.closest && e.target.closest('.lp-thumbnail');\n let targetIndex = 0;\n let insertBefore = true;\n if (targetThumb) {\n targetIndex = Number(targetThumb.dataset.index);\n const rect = targetThumb.getBoundingClientRect();\n insertBefore = e.clientY < rect.top + rect.height / 2;\n } else {\n targetIndex = goal.slides.length - 1;\n insertBefore = false;\n }\n if (Number.isNaN(targetIndex) || src === targetIndex) {\n clearDragState();\n return;\n }\n let insertAt = targetIndex;\n if (insertBefore) {\n if (src < targetIndex) insertAt = targetIndex - 1;\n } else {\n if (src > targetIndex) insertAt = targetIndex + 1;\n }\n const activeSlide = goal.slides[current];\n const movingSlide = goal.slides[src];\n const newSlides = goal.slides.filter((_, i) => i !== src);\n insertAt = Math.max(0, Math.min(insertAt, newSlides.length));\n newSlides.splice(insertAt, 0, movingSlide);\n goal.slides = newSlides;\n recordHistory();\n selectedSlideIdx = goal.slides.indexOf(activeSlide);\n current = selectedSlideIdx;\n autoSave();\n rebuildSlidesAndThumbnails();\n if (typeof selectSlide === 'function') selectSlide(current);\n clearDragState();\n });\n\n container.addEventListener('dragend', () => {\n clearDragState();\n });\n }\n attachDragAndDropListeners();\n\n function deleteSlide(index) {\n if (goal.slides.length <= 1) {\n alert('\u81F3\u5C11\u4FDD\u7559\u4E00\u9875\u5E7B\u706F\u7247\u3002');\n return;\n }\n const deleteBtn = document.querySelector('.lp-thumbnail-delete[data-index=\"' + index + '\"]');\n showDeleteConfirm(index, deleteBtn);\n }\n\n document.addEventListener('keydown', (e) => {\n if (isEditingTarget(e.target) || isEditingTarget(document.activeElement)) return;\n const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;\n const mod = isMac ? e.metaKey : e.ctrlKey;\n\n // \u5173\u95ED\u5220\u9664\u786E\u8BA4\u6C14\u6CE1\n if (e.key === 'Escape' && pendingDeleteIndex != null) {\n e.preventDefault();\n hideDeleteConfirm();\n return;\n }\n\n // \u64A4\u9500 / \u91CD\u505A\n if (mod && e.key.toLowerCase() === 'z') {\n e.preventDefault();\n if (e.shiftKey) redo();\n else undo();\n return;\n }\n if (mod && e.key.toLowerCase() === 'y') {\n e.preventDefault();\n redo();\n return;\n }\n\n // \u7FFB\u9875\u5BFC\u822A\n if (isTransitioning || slides.length === 0) return;\n if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') {\n e.preventDefault();\n goTo(current + 1);\n }\n if (e.key === 'ArrowLeft' || e.key === 'PageUp') {\n e.preventDefault();\n goTo(current - 1);\n }\n if (e.key === 'Home') {\n e.preventDefault();\n goTo(0);\n }\n if (e.key === 'End') {\n e.preventDefault();\n goTo(slides.length - 1);\n }\n });\n\n updateClasses();\n\n // \u7F29\u653E\u63A7\u5236\n const stage = document.querySelector('.lp-editor-stage');\n const scaler = document.querySelector('.lp-editor-stage-scaler');\n const editorRoot = document.querySelector('.lp-editor-root');\n const zoomSlider = document.getElementById('lp-zoom-slider');\n const zoomValue = document.getElementById('lp-zoom-value');\n const zoomOutBtn = document.getElementById('lp-zoom-out');\n const zoomInBtn = document.getElementById('lp-zoom-in');\n const zoomFitBtn = document.getElementById('lp-zoom-fit');\n let userZoom = null;\n\n const SLIDE_WIDTH = 1280;\n const SLIDE_HEIGHT = 720;\n\n function fitScale() {\n if (!stage || !scaler) return 1;\n return Math.min(stage.clientWidth / SLIDE_WIDTH, stage.clientHeight / SLIDE_HEIGHT) * 0.92;\n }\n\n function updateScale() {\n if (!stage || !scaler) return;\n if (editorRoot && editorRoot.classList.contains('lp-editor-presentation-mode')) {\n const scale = Math.min(window.innerWidth / SLIDE_WIDTH, window.innerHeight / SLIDE_HEIGHT);\n scaler.style.setProperty('--lp-presentation-scale', String(Math.max(scale, 0.35)));\n scaler.style.transform = '';\n return;\n }\n const scale = userZoom == null ? fitScale() : userZoom;\n scaler.style.transform = 'scale(' + Math.max(scale, 0.35) + ')';\n scaler.style.removeProperty('--lp-presentation-scale');\n if (zoomValue) zoomValue.textContent = Math.round(scale * 100) + '%';\n if (zoomSlider && userZoom != null) zoomSlider.value = String(Math.round(scale * 100));\n }\n\n if (zoomSlider) {\n zoomSlider.addEventListener('input', () => {\n userZoom = Number(zoomSlider.value) / 100;\n updateScale();\n });\n }\n if (zoomOutBtn) {\n zoomOutBtn.addEventListener('click', () => {\n userZoom = (userZoom == null ? fitScale() : userZoom) - 0.1;\n if (userZoom < 0.35) userZoom = 0.35;\n updateScale();\n });\n }\n if (zoomInBtn) {\n zoomInBtn.addEventListener('click', () => {\n userZoom = (userZoom == null ? fitScale() : userZoom) + 0.1;\n if (userZoom > 1.5) userZoom = 1.5;\n updateScale();\n });\n }\n if (zoomFitBtn) {\n zoomFitBtn.addEventListener('click', () => {\n userZoom = null;\n updateScale();\n });\n }\n window.addEventListener('resize', updateScale);\n updateScale();\n\n // \u64AD\u653E\uFF1A\u8FDB\u5165\u5168\u5C4F\u6F14\u793A\u6A21\u5F0F\uFF0C\u4F7F\u7528\u5F53\u524D\u7F16\u8F91\u72B6\u6001\u76F4\u63A5\u64AD\u653E\n const playBtn = document.getElementById('lp-play');\n let presentationExitBtn = null;\n let presentationKeyHandler = null;\n\n let isExitingPresentation = false;\n\n function exitPresentationMode() {\n if (!editorRoot || isExitingPresentation) return;\n isExitingPresentation = true;\n editorRoot.classList.remove('lp-editor-presentation-mode');\n if (presentationExitBtn && presentationExitBtn.parentNode) {\n presentationExitBtn.parentNode.removeChild(presentationExitBtn);\n presentationExitBtn = null;\n }\n if (presentationKeyHandler) {\n document.removeEventListener('keydown', presentationKeyHandler);\n presentationKeyHandler = null;\n }\n const done = () => {\n isExitingPresentation = false;\n updateScale();\n };\n if (document.fullscreenElement && document.exitFullscreen) {\n document.exitFullscreen().catch(() => {}).finally(done);\n } else {\n done();\n }\n }\n\n async function enterPresentationMode() {\n if (!editorRoot) return;\n editorRoot.classList.add('lp-editor-presentation-mode');\n updateScale();\n try {\n const target = editorRoot.requestFullscreen ? editorRoot : document.documentElement;\n if (target.requestFullscreen) {\n await target.requestFullscreen();\n }\n } catch (err) {\n console.warn('\u8FDB\u5165\u5168\u5C4F\u5931\u8D25\uFF0C\u5C06\u4EE5\u7A97\u53E3\u6F14\u793A\u6A21\u5F0F\u64AD\u653E', err);\n }\n if (!presentationExitBtn) {\n presentationExitBtn = document.createElement('button');\n presentationExitBtn.className = 'lp-editor-presentation-exit';\n presentationExitBtn.textContent = '\u9000\u51FA\u6F14\u793A (Esc)';\n presentationExitBtn.type = 'button';\n presentationExitBtn.addEventListener('click', exitPresentationMode);\n document.body.appendChild(presentationExitBtn);\n }\n if (!presentationKeyHandler) {\n presentationKeyHandler = (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n exitPresentationMode();\n }\n };\n document.addEventListener('keydown', presentationKeyHandler);\n }\n }\n\n document.addEventListener('fullscreenchange', () => {\n if (!document.fullscreenElement && editorRoot && editorRoot.classList.contains('lp-editor-presentation-mode')) {\n exitPresentationMode();\n }\n });\n\n if (playBtn) {\n playBtn.addEventListener('click', enterPresentationMode);\n }\n\n // \u5185\u5BB9\u7ED3\u6784\u53D8\u66F4\u540E\u91CD\u65B0\u6E32\u67D3\u5F53\u524D\u7F16\u8F91\u5668\uFF08\u4EC5\u7528\u4E8E\u6570\u7EC4\u589E\u5220\u7B49\u6A21\u677F\u5185\u5BB9\u8C03\u6574\uFF09\n function saveCurrentForReload() {\n try {\n localStorage.setItem('lemonppt:editor:currentSlide', String(selectedSlideIdx));\n } catch (e) {}\n }\n\n function getSlideLabel(slide) {\n const props = slide.props || {};\n const candidates = [\n props.title,\n props.name,\n props.kicker,\n props.quote,\n props.heading,\n props.items && Array.isArray(props.items) ? props.items[0] : undefined,\n props.metric,\n props.value,\n ];\n for (const c of candidates) {\n if (c !== undefined && c !== null && String(c).trim()) return String(c).trim();\n }\n return slide.layout;\n }\n\n // \u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\uFF0C\u589E\u5220\u5E7B\u706F\u7247\u540E\u4E0D\u518D\u4F7F\u7528 document.write \u91CD\u5199\u6574\u9875\uFF08\u4F1A\u4E2D\u65AD CSS/\u8D44\u6E90\u52A0\u8F7D\uFF09\uFF0C\n // \u800C\u662F\u76F4\u63A5\u5728\u73B0\u6709 DOM \u4E2D\u91CD\u5EFA slide \u5BB9\u5668\u548C\u7F29\u7565\u56FE\uFF0C\u5E76\u590D\u7528 React root \u91CD\u65B0\u6E32\u67D3\u5185\u5BB9\u3002\n function rebuildSlidesAndThumbnails() {\n if (!deck) return;\n if (typeof window.__lemonPPT_renderSlideHtml !== 'function') return;\n\n // DOM \u91CD\u5EFA\u524D\u5C06 current \u540C\u6B65\u5230\u5408\u6CD5\u8303\u56F4\uFF0C\u907F\u514D\u540E\u7EED goTo/updateClasses \u8D8A\u754C\n current = Math.max(0, Math.min(selectedSlideIdx, goal.slides.length - 1));\n\n const firstWrapper = slides[0];\n const width = firstWrapper ? parseInt(firstWrapper.style.width, 10) || 1280 : 1280;\n const height = firstWrapper ? parseInt(firstWrapper.style.height, 10) || 720 : 720;\n const theme = goal.theme || 'theme01';\n\n // \u91CD\u5EFA deck \u4E2D\u7684 slide \u5BB9\u5668\n const wrappersHtml = goal.slides.map((slide, index) => {\n const slideHtml = window.__lemonPPT_renderSlideHtml(slide, { slideIdx: index, editable: true, theme });\n const stateClass = index === current ? 'active' : (index < current ? 'prev' : '');\n const transition = String((slide.props && slide.props.transition) || 'none');\n return '<div class=\"lp-slide-wrapper ' + stateClass + '\" data-slide-index=\"' + index + '\" data-layout=\"' + slide.layout + '\" data-lp-transition=\"' + transition + '\" style=\"width:' + width + 'px;height:' + height + 'px;box-sizing:border-box;\">' + slideHtml + '</div>';\n }).join('');\n deck.innerHTML = wrappersHtml;\n\n // \u91CD\u5EFA\u5DE6\u4FA7\u7F29\u7565\u56FE\n const thumbnailsContainer = document.querySelector('.lp-editor-thumbnails');\n if (thumbnailsContainer) {\n const thumbInnerWidth = 156;\n const scale = thumbInnerWidth / width;\n const showDragHandle = goal.slides.length > 1;\n const dragHandleHtml = showDragHandle\n ? '<span class=\"lp-thumbnail-drag-handle\" data-lp-action=\"drag-handle\" aria-hidden=\"true\">' +\n '<svg width=\"12\" height=\"16\" viewBox=\"0 0 12 16\" fill=\"currentColor\" aria-hidden=\"true\">' +\n '<circle cx=\"2.5\" cy=\"2.5\" r=\"1.5\"/>' +\n '<circle cx=\"9.5\" cy=\"2.5\" r=\"1.5\"/>' +\n '<circle cx=\"2.5\" cy=\"8\" r=\"1.5\"/>' +\n '<circle cx=\"9.5\" cy=\"8\" r=\"1.5\"/>' +\n '<circle cx=\"2.5\" cy=\"13.5\" r=\"1.5\"/>' +\n '<circle cx=\"9.5\" cy=\"13.5\" r=\"1.5\"/>' +\n '</svg>' +\n '</span>'\n : '';\n const buttonsHtml = goal.slides.map((slide, index) => {\n const label = getSlideLabel(slide);\n const activeClass = index === current ? 'active' : '';\n const slideHtml = window.__lemonPPT_renderSlideHtml(slide, { slideIdx: index, editable: true, theme });\n const draggableAttr = goal.slides.length > 1 ? 'draggable=\"true\"' : '';\n return '<div class=\"lp-thumbnail ' + activeClass + '\" data-index=\"' + index + '\" role=\"button\" tabindex=\"0\" ' + draggableAttr + ' aria-label=\"\u5E7B\u706F\u7247 ' + (index + 1) + '\uFF0C\u62D6\u52A8\u53EF\u8C03\u6574\u987A\u5E8F\">' +\n dragHandleHtml +\n '<div class=\"lp-thumbnail-render\">' +\n '<div class=\"lp-thumbnail-scaler\" style=\"width:' + width + 'px;height:' + height + 'px;transform:scale(' + scale + ');transform-origin:top left;\">' + slideHtml + '</div>' +\n '</div>' +\n '<div class=\"lp-thumbnail-scrim\"></div>' +\n '<div class=\"lp-thumbnail-content\">' +\n '<div class=\"lp-thumbnail-index\">' + (index + 1) + ' / ' + goal.slides.length + '</div>' +\n '<div class=\"lp-thumbnail-title\">' + escapeHtml(label) + '</div>' +\n '<div class=\"lp-thumbnail-layout\">' + escapeHtml(slide.layout) + '</div>' +\n '</div>' +\n '<span class=\"lp-thumbnail-delete\" data-lp-action=\"delete-slide\" data-index=\"' + index + '\" title=\"\u5220\u9664\u5E7B\u706F\u7247\" aria-label=\"\u5220\u9664\u5E7B\u706F\u7247\">\u00D7</span>' +\n '</div>';\n }).join('');\n thumbnailsContainer.innerHTML = buttonsHtml;\n // \u7F29\u7565\u56FE DOM \u91CD\u5EFA\u540E\u6E05\u7A7A\u865A\u62DF\u6EDA\u52A8\u7F13\u5B58\uFF0C\u907F\u514D\u65E7 HTML \u4E0E\u65B0\u7D22\u5F15\u9519\u4F4D\n Object.keys(thumbnailRenderCache).forEach((key) => {\n delete thumbnailRenderCache[key];\n });\n }\n\n // \u540C\u6B65\u9875\u7801\u8BA1\u6570\u5668\n const pageCounter = document.querySelector('.lp-editor-page-counter');\n if (pageCounter) {\n pageCounter.innerHTML = '<span id=\"lp-current\">' + (current + 1) + '</span> / ' + goal.slides.length;\n }\n\n refreshSlidesAndThumbnails();\n attachThumbnailListeners();\n updateClasses();\n renderAllSlidesToRoot();\n initThumbnailVirtualScroll();\n }\n\n function refreshCurrentSlide() {\n if (typeof window.__lemonPPT_renderSlideToRoot === 'function') {\n renderCurrentSlideToRoot();\n } else {\n // \u515C\u5E95\uFF1A\u65E7\u7248\u9759\u6001 HTML \u66FF\u6362\n const wrapper = document.querySelector('.lp-slide-wrapper.active');\n if (!wrapper) return;\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n const html = window.__lemonPPT_renderSlideHtml(slide, { slideIdx: selectedSlideIdx, editable: true, theme: goal.theme });\n wrapper.innerHTML = html;\n initActiveSlideECharts();\n }\n }\n\n async function reloadEditor() {\n saveCurrentForReload();\n\n // \u4F18\u5148\u5C1D\u8BD5\u670D\u52A1\u5668\u7AEF\u91CD\u65B0\u6E32\u67D3\uFF08\u5F00\u53D1\u670D\u52A1\u5668\u6A21\u5F0F\uFF09\n try {\n const res = await fetch('/api/render-editor', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(goal),\n });\n if (!res.ok) throw new Error('\u91CD\u65B0\u6E32\u67D3\u5931\u8D25: ' + res.status);\n window.location.reload();\n return;\n } catch (err) {\n // \u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\uFF0C\u76F4\u63A5\u5728\u5F53\u524D DOM \u4E2D\u91CD\u5EFA slide \u5BB9\u5668\u548C\u7F29\u7565\u56FE\uFF0C\n // \u907F\u514D document.write \u4E2D\u65AD CSS/\u8D44\u6E90\u52A0\u8F7D\u5BFC\u81F4\u5E7B\u706F\u7247\u5185\u5BB9\u65E0\u6CD5\u6E32\u67D3\u3002\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n rebuildSlidesAndThumbnails();\n initActiveSlideECharts();\n }\n }\n\n // \u5143\u7D20\u9009\u4E2D\u4E0E\u53F3\u4FA7\u5C5E\u6027\u9762\u677F\n let selectedEl = null;\n let selectedSlideIdx = 0;\n const propertyContent = document.getElementById('lp-property-content');\n const layoutSchemas = window.__lemonPPT_layoutSchemas || {};\n\n const FIELD_LABELS = {\n kicker: '\u6807\u7B7E',\n title: '\u6807\u9898',\n subtitle: '\u526F\u6807\u9898',\n date: '\u65E5\u671F',\n quote: '\u5F15\u7528',\n author: '\u4F5C\u8005',\n role: '\u804C\u4F4D',\n company: '\u516C\u53F8',\n value: '\u6570\u503C',\n unit: '\u5355\u4F4D',\n label: '\u6307\u6807\u540D',\n description: '\u8BF4\u660E',\n change: '\u53D8\u5316',\n imageUrl: '\u56FE\u7247',\n url: '\u56FE\u7247',\n logoUrl: 'Logo',\n avatarUrl: '\u5934\u50CF',\n caption: '\u8BF4\u660E',\n q: '\u95EE\u9898',\n a: '\u56DE\u7B54',\n question: '\u95EE\u9898',\n answer: '\u56DE\u7B54',\n name: '\u540D\u79F0',\n price: '\u4EF7\u683C',\n period: '\u5468\u671F',\n cta: '\u6309\u94AE\u6587\u6848',\n type: '\u56FE\u8868\u7C7B\u578B',\n status: '\u72B6\u6001',\n bio: '\u7B80\u4ECB',\n items: '\u76EE\u5F55\u9879',\n points: '\u8981\u70B9',\n leftPoints: '\u5DE6\u4FA7\u8981\u70B9',\n rightPoints: '\u53F3\u4FA7\u8981\u70B9',\n features: '\u7279\u6027',\n steps: '\u6B65\u9AA4',\n stats: '\u7EDF\u8BA1\u6570\u636E',\n members: '\u6210\u5458',\n milestones: '\u91CC\u7A0B\u7891',\n phases: '\u9636\u6BB5',\n plans: '\u65B9\u6848',\n tiers: '\u5957\u9910',\n images: '\u56FE\u7247',\n partners: '\u5408\u4F5C\u4F19\u4F34',\n datasets: '\u6570\u636E\u96C6',\n metrics: '\u6307\u6807',\n cards: '\u5361\u7247',\n highlighted: '\u9AD8\u4EAE',\n columns: '\u4E3B\u9898\u5217',\n panels: '\u6307\u6807\u9762\u677F',\n left: '\u5DE6\u4FA7\u5185\u5BB9',\n right: '\u53F3\u4FA7\u5185\u5BB9',\n centerCard: '\u4E2D\u592E\u7ED3\u8BBA\u5361',\n comparisons: '\u5BF9\u6BD4\u9879',\n conclusion: '\u7ED3\u8BBA',\n chartType: '\u56FE\u8868\u7C7B\u578B',\n chartData: '\u56FE\u8868\u6570\u636E',\n footnote: '\u811A\u6CE8',\n source: '\u6765\u6E90',\n number: '\u5E8F\u53F7',\n context: '\u4E0A\u4E0B\u6587',\n showInsight: '\u91CD\u70B9\u5F3A\u8C03',\n insight: '\u6D1E\u5BDF\u9762\u677F',\n previous: '\u539F\u6570\u503C',\n current: '\u73B0\u6570\u503C',\n max: '\u6700\u5927\u503C',\n tone: '\u8272\u8C03',\n percent: '\u5360\u6BD4',\n color: '\u989C\u8272',\n rank: '\u6392\u540D',\n company: '\u516C\u53F8',\n track: '\u8D5B\u9053',\n amount: '\u91D1\u989D',\n highlightRow: '\u9AD8\u4EAE\u884C',\n highlightFirstColumn: '\u9AD8\u4EAE\u9996\u5217',\n leftTitle: '\u5DE6\u4FA7\u6807\u9898',\n rightTitle: '\u53F3\u4FA7\u6807\u9898',\n leftValue: '\u5DE6\u4FA7\u6570\u503C',\n rightValue: '\u53F3\u4FA7\u6570\u503C',\n leftLabel: '\u5DE6\u4FA7\u6807\u7B7E',\n rightLabel: '\u53F3\u4FA7\u6807\u7B7E',\n size: '\u5C3A\u5BF8',\n icon: '\u56FE\u6807',\n page: '\u9875\u7801',\n image: '\u56FE\u7247',\n avatar: '\u5934\u50CF',\n logo: 'Logo',\n };\n\n const LAYOUT_LABELS = {\n cover_v1: '\u5C01\u9762',\n cover_v2: '\u5C01\u9762\uFF08\u5C45\u4E2D\uFF09',\n cover_v3: '\u5C01\u9762\uFF08\u5168\u56FE\uFF09',\n table_of_contents_v1: '\u76EE\u5F55',\n table_of_contents_v2: '\u76EE\u5F55\uFF08\u5361\u7247\uFF09',\n content_v1: '\u5185\u5BB9\u5217\u8868',\n content_v2: '\u5185\u5BB9\u5BF9\u6BD4',\n content_v3: '\u5185\u5BB9\u6BB5\u843D',\n content_v4: '\u5185\u5BB9\u5361\u7247',\n chart_v1: '\u56FE\u8868',\n chart_v2: '\u591A\u7CFB\u5217\u56FE\u8868',\n process_v1: '\u6D41\u7A0B\u6B65\u9AA4',\n process_v2: '\u6D41\u7A0B\u65F6\u95F4\u7EBF',\n process_v3: '\u6D41\u7A0B\u5361\u7247',\n stats_v1: '\u6570\u636E\u6307\u6807',\n stats_v2: '\u6570\u636E\u6307\u6807\uFF08\u5361\u7247\uFF09',\n metric_v1: '\u6838\u5FC3\u6307\u6807',\n metric_v2: '\u6838\u5FC3\u6307\u6807\uFF08\u5927\u6570\u5B57\uFF09',\n metric_v3: '\u53CC\u6307\u6807\u5BF9\u6BD4',\n feature_v1: '\u7279\u6027\u5217\u8868',\n feature_v2: '\u7279\u6027\u7F51\u683C',\n feature_v3: '\u7279\u6027\u5361\u7247',\n team_v1: '\u56E2\u961F\u4ECB\u7ECD',\n team_v2: '\u56E2\u961F\u5361\u7247',\n timeline_v1: '\u65F6\u95F4\u7EBF',\n timeline_v2: '\u65F6\u95F4\u7EBF\uFF08\u6A2A\u5411\uFF09',\n timeline_v3: '\u65F6\u95F4\u7EBF\uFF08\u91CC\u7A0B\u7891\uFF09',\n roadmap_v1: '\u8DEF\u7EBF\u56FE',\n faq_v1: '\u95EE\u7B54',\n partners_v1: '\u5408\u4F5C\u4F19\u4F34',\n gallery_v1: '\u56FE\u7247\u96C6',\n gallery_v2: '\u56FE\u7247\u96C6\uFF08\u7F51\u683C\uFF09',\n pricing_v1: '\u5B9A\u4EF7',\n pricing_v2: '\u5B9A\u4EF7\u65B9\u6848',\n closing_v2: '\u7ED3\u675F\u9875',\n quote_v1: '\u5F15\u7528\u9875',\n testimonial_v1: '\u63A8\u8350\u8BED',\n metric_big: '\u5927\u6570\u5B57\u9875',\n metric_triptych: '\u4E09\u6307\u6807\u603B\u89C8',\n chart_donut: '\u73AF\u5F62\u56FE\u62C6\u89E3',\n chart_funnel: '\u6F0F\u6597\u56FE',\n table_data: '\u6570\u636E\u8868\u683C',\n case_study: '\u6848\u4F8B\u8BE6\u60C5',\n spotlight_grid: '\u4E3B\u9898\u805A\u7126\u7F51\u683C',\n diptych_contrast: '\u53CC\u8054\u5BF9\u6BD4',\n conclusion_v1: '\u7ED3\u8BBA\u9875',\n table_v1: '\u8868\u683C',\n ranking_v1: '\u6392\u884C\u699C',\n region_v1: '\u5730\u533A\u5206\u5E03',\n risk_v1: '\u98CE\u9669\u7814\u5224',\n outlook_v1: '\u6295\u8D44\u5C55\u671B',\n bento_v1: 'Bento \u7F51\u683C',\n gallery_v1: '\u56FE\u7247\u96C6',\n gantt_v1: '\u7518\u7279\u56FE',\n quadrant_v1: '\u56DB\u8C61\u9650',\n scorecard_v1: '\u8BC4\u5206\u5361',\n tags_v1: '\u6807\u7B7E\u4E91',\n filmstrip_v1: '\u80F6\u7247\u6761',\n swot_v1: 'SWOT \u5206\u6790',\n pest_v1: 'PEST \u5206\u6790',\n appendix_v1: '\u9644\u5F55',\n image_v1: '\u56FE\u7247\u9875',\n process_v1: '\u6D41\u7A0B\u6B65\u9AA4',\n faq_v1: '\u95EE\u7B54',\n partners_v1: '\u5408\u4F5C\u4F19\u4F34',\n pricing_v1: '\u5B9A\u4EF7',\n team_v1: '\u56E2\u961F\u4ECB\u7ECD',\n team_v2: '\u56E2\u961F\u5361\u7247',\n timeline_v1: '\u65F6\u95F4\u7EBF',\n roadmap_v1: '\u8DEF\u7EBF\u56FE',\n chapter_v1: '\u7AE0\u8282\u9875',\n chapter_v2: '\u7AE0\u8282\u9875\uFF08\u5168\u56FE\uFF09',\n chapter_v3: '\u7AE0\u8282\u9875\uFF08\u5361\u7247\uFF09',\n cover_v3: '\u5C01\u9762\uFF08\u5168\u56FE\uFF09',\n cover_v4: '\u5C01\u9762\uFF08\u6742\u5FD7\uFF09',\n chart_bar3d: '3D \u67F1\u72B6\u56FE',\n chart_gauge: '\u4EEA\u8868\u76D8',\n chart_graph: '\u5173\u7CFB\u56FE',\n chart_heatmap: '\u70ED\u529B\u56FE',\n chart_radar: '\u96F7\u8FBE\u56FE',\n chart_sankey: '\u6851\u57FA\u56FE',\n chart_sunburst: '\u65ED\u65E5\u56FE',\n chart_treemap: '\u77E9\u5F62\u6811\u56FE',\n chart_wordcloud: '\u8BCD\u4E91',\n content_v4: '\u5185\u5BB9\u5361\u7247',\n metrics_v1: '\u6307\u6807\u5899',\n delta_v1: '\u4ECA\u6614\u5BF9\u7167',\n comparison_v1: '\u5BF9\u6BD4\u5206\u6790',\n progress_v1: '\u8FDB\u5EA6\u6761',\n number_showcase_v1: '\u6570\u5B57\u79C0',\n testimonial_v1: '\u63A8\u8350\u8BED',\n closing_v1: '\u7ED3\u675F\u9875',\n };\n\n function getLayoutLabel(layoutId) {\n if (LAYOUT_LABELS[layoutId]) return LAYOUT_LABELS[layoutId];\n const shortId = String(layoutId).replace(/^themed+_/, '');\n return LAYOUT_LABELS[shortId] || layoutId;\n }\n\n function resolveLayoutSchema(slide) {\n if (layoutSchemas[slide.layout]) return layoutSchemas[slide.layout];\n const theme = goal.theme || 'theme01';\n const shortId = String(slide.layout).replace(/^themed+_/, '');\n return layoutSchemas[theme + '_' + shortId] || null;\n }\n\n function getFieldLabel(path) {\n const key = String(path).split('.').pop() || '';\n return FIELD_LABELS[key] || key;\n }\n\n function inferFieldType(path, value) {\n const key = String(path).split('.').pop() || '';\n if (/image|url|logo|avatar/i.test(key) && typeof value === 'string') return 'image';\n if (typeof value === 'boolean') return 'boolean';\n if (typeof value === 'number') return 'number';\n if (key === 'type' && ['bar', 'line', 'pie'].includes(String(value))) return 'select';\n if (key === 'status') return 'select';\n if (['description', 'bio', 'quote', 'answer', 'a', 'subtitle'].includes(key)) return 'textarea';\n return 'text';\n }\n\n function getSelectOptions(path) {\n const key = String(path).split('.').pop() || '';\n if (key === 'type') return [{ value: 'bar', label: '\u67F1\u72B6' }, { value: 'line', label: '\u6298\u7EBF' }, { value: 'pie', label: '\u997C\u56FE' }];\n if (key === 'status') return [{ value: '\u5DF2\u5B8C\u6210', label: '\u5DF2\u5B8C\u6210' }, { value: '\u8FDB\u884C\u4E2D', label: '\u8FDB\u884C\u4E2D' }, { value: '\u89C4\u5212\u4E2D', label: '\u89C4\u5212\u4E2D' }];\n return [];\n }\n\n function clearSelection() {\n if (selectedEl) {\n selectedEl.classList.remove('lp-selected');\n selectedEl = null;\n }\n selectedSlideIdx = current;\n renderSlidePanel();\n }\n\n function setField(path, value) {\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n recordHistory();\n setProp(slide.props, path, value);\n // \u6253\u5F00\u4F9D\u8D56\u5F00\u5173\u65F6\uFF0C\u5982\u679C\u88AB\u63A7\u5B57\u6BB5\u4E3A\u7A7A\u4E14\u914D\u6709\u9ED8\u8BA4\u503C\uFF0C\u5219\u81EA\u52A8\u586B\u5145\u6D4B\u8BD5\u6570\u636E\n if (value === true) {\n const schema = resolveLayoutSchema(slide);\n if (schema && schema.fields) {\n schema.fields.forEach((field) => {\n if (field.visibleWhen && field.visibleWhen.key === path && field.visibleWhen.value === true && field.defaultValue !== undefined) {\n const current = getProp(slide.props, field.key);\n const isEmpty = current == null || (typeof current === 'object' && Object.keys(current).length === 0);\n if (isEmpty) {\n setProp(slide.props, field.key, deepClone(field.defaultValue));\n }\n }\n });\n }\n }\n syncDomFromGoal(path);\n autoSave();\n // \u5982\u679C\u8BE5\u5B57\u6BB5\u5728\u753B\u5E03\u4E0A\u6CA1\u6709\u5BF9\u5E94\u7684\u76F4\u63A5\u7F16\u8F91\u5143\u7D20\uFF08\u5982 SVG \u56FE\u8868\u6570\u636E\uFF09\uFF0C\u5237\u65B0\u6574\u9875\u5E7B\u706F\u7247\u3002\n // \u5BF9\u4E8E\u6807\u8BB0\u4E3A data-lp-chart-data \u7684\u56FE\u8868\u6570\u636E\u5B57\u6BB5\uFF0C\u5373\u4F7F\u753B\u5E03\u4E0A\u53EF\u76F4\u63A5\u7F16\u8F91\uFF0C\u4E5F\u9700\u8981\n // \u91CD\u65B0\u6E32\u67D3\u56FE\u8868\u4EE5\u540C\u6B65\u6570\u636E\u53D8\u5316\u3002\n if (!hasEditableElementForPath(path) || hasChartDataElementForPath(path)) {\n refreshCurrentSlide();\n }\n }\n\n function hasEditableElementForPath(path) {\n const selector = '[data-lp-editable=\"true\"][data-lp-slide-idx=\"' + selectedSlideIdx + '\"][data-lp-prop=\"' + path + '\"]';\n return document.querySelector(selector) !== null;\n }\n\n function hasChartDataElementForPath(path) {\n const selector = '[data-lp-editable=\"true\"][data-lp-chart-data=\"true\"][data-lp-slide-idx=\"' + selectedSlideIdx + '\"][data-lp-prop=\"' + path + '\"]';\n return document.querySelector(selector) !== null;\n }\n\n function createEl(tag, className, parent) {\n const el = document.createElement(tag);\n if (className) el.className = className;\n if (parent) parent.appendChild(el);\n return el;\n }\n\n function createTextField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const input = createEl('input', 'lp-property-input', wrap);\n input.type = 'text';\n input.value = value == null ? '' : String(value);\n input.addEventListener('input', () => onChange(input.value));\n return wrap;\n }\n\n function createTextareaField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const textarea = createEl('textarea', 'lp-property-textarea', wrap);\n textarea.value = value == null ? '' : String(value);\n textarea.addEventListener('input', () => onChange(textarea.value));\n return wrap;\n }\n\n function createNumberField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const input = createEl('input', 'lp-property-input', wrap);\n input.type = 'number';\n input.value = value == null ? '' : String(value);\n input.addEventListener('input', () => onChange(Number(input.value)));\n return wrap;\n }\n\n function renderSliderTicks(ruler, min, max) {\n const scale = createEl('div', 'lp-property-slider-scale', ruler);\n const span = max - min;\n if (span <= 0) return scale;\n // \u523B\u5EA6\u6B65\u957F\uFF1A\u8303\u56F4\u5C0F\u65F6\u6BCF\u4E2A\u6574\u6570\u4E00\u4E2A\u523B\u5EA6\uFF0C\u8303\u56F4\u5927\u65F6\u5747\u5300\u53D6 6~10 \u4E2A\n const desiredTicks = 8;\n const step = span <= desiredTicks ? 1 : Math.max(1, Math.round(span / desiredTicks));\n for (let i = min; i <= max; i += step) {\n const tick = createEl('div', 'lp-property-slider-tick', scale);\n tick.style.left = ((i - min) / span) * 100 + '%';\n const label = createEl('div', 'lp-property-slider-tick-label', tick);\n label.textContent = String(i);\n tick.addEventListener('click', () => {\n const input = ruler.querySelector('input[type=\"range\"]');\n if (input) {\n input.value = String(i);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n });\n }\n return scale;\n }\n\n function createSliderField(field, value, onChange) {\n const wrap = createEl('div', 'lp-property-slider-field');\n const header = createEl('div', 'lp-property-section-header', wrap);\n createEl('div', 'lp-property-section-title', header).textContent = field.label;\n const countBadge = createEl('span', 'lp-property-slider-value', header);\n const initialValue = value == null ? (field.min || 0) : value;\n countBadge.textContent = String(initialValue);\n\n const min = typeof field.min === 'number' ? field.min : 0;\n const max = typeof field.max === 'number' ? field.max : 100;\n\n const sliderWrap = createEl('div', 'lp-property-slider-wrap', wrap);\n const ruler = createEl('div', 'lp-property-slider-ruler', sliderWrap);\n const range = document.createElement('input');\n range.type = 'range';\n range.className = 'lp-property-slider';\n range.min = String(min);\n range.max = String(max);\n range.value = String(initialValue);\n ruler.appendChild(range);\n renderSliderTicks(ruler, min, max);\n\n range.addEventListener('input', () => {\n const num = Number(range.value);\n countBadge.textContent = String(num);\n onChange(num);\n });\n\n return wrap;\n }\n\n function createToggleField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n const labelEl = createEl('label', 'lp-property-toggle', wrap);\n const text = createEl('span', '', labelEl);\n text.textContent = label;\n const input = createEl('input', '', labelEl);\n input.type = 'checkbox';\n input.checked = !!value;\n const track = createEl('div', 'lp-property-toggle-track', labelEl);\n createEl('div', 'lp-property-toggle-thumb', track);\n input.addEventListener('change', () => onChange(input.checked));\n return wrap;\n }\n\n function createSelectField(label, value, options, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const segmented = createEl('div', 'lp-property-segmented', wrap);\n options.forEach((opt) => {\n const btn = createEl('button', '', segmented);\n btn.textContent = opt.label;\n btn.type = 'button';\n if (opt.value === value) btn.classList.add('active');\n btn.addEventListener('click', () => {\n Array.from(segmented.children).forEach((b) => b.classList.remove('active'));\n btn.classList.add('active');\n onChange(opt.value);\n });\n });\n return wrap;\n }\n\n function createImageHintField(label) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const hint = createEl('div', 'lp-property-help', wrap);\n hint.textContent = '\u8BF7\u70B9\u51FB\u753B\u5E03\u4E2D\u7684\u56FE\u7247\u533A\u57DF\u76F4\u63A5\u4E0A\u4F20';\n return wrap;\n }\n\n function createColorField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const row = createEl('div', '', wrap);\n row.style.display = 'flex';\n row.style.alignItems = 'center';\n row.style.gap = '8px';\n const input = createEl('input', 'lp-property-input', row);\n input.type = 'text';\n input.value = value == null ? '' : String(value);\n input.style.flex = '1';\n input.addEventListener('input', () => onChange(input.value));\n const picker = createEl('input', '', row);\n picker.type = 'color';\n picker.value = value || '#000000';\n picker.style.width = '32px';\n picker.style.height = '32px';\n picker.style.border = 'none';\n picker.style.background = 'none';\n picker.style.cursor = 'pointer';\n picker.addEventListener('input', () => {\n input.value = picker.value;\n onChange(picker.value);\n });\n return wrap;\n }\n\n function createFieldControl(path, value) {\n const type = inferFieldType(path, value);\n const label = getFieldLabel(path);\n if (type === 'image') return createImageHintField(label);\n if (type === 'textarea') return createTextareaField(label, value, (v) => setField(path, v || undefined));\n if (type === 'number') return createNumberField(label, value, (v) => setField(path, v));\n if (type === 'boolean') return createToggleField(label, value, (v) => setField(path, v));\n if (type === 'select') return createSelectField(label, value, getSelectOptions(path), (v) => setField(path, v));\n return createTextField(label, value, (v) => setField(path, v || undefined));\n }\n\n function createSchemaFieldControl(field, value, path, skipCanvasEditable) {\n const label = field.label || field.key;\n if (skipCanvasEditable && (field.type === 'text' || field.type === 'textarea' || field.type === 'image')) {\n return null;\n }\n if (field.type === 'array') {\n const wrap = createEl('div', 'lp-property-nested-array');\n createSchemaArraySection(field, Array.isArray(value) ? value : [], wrap, path, skipCanvasEditable);\n return wrap;\n }\n if (field.type === 'image') return createImageHintField(label);\n if (field.type === 'textarea') return createTextareaField(label, value, (v) => setField(path, v || undefined));\n if (field.type === 'number') return createNumberField(label, value, (v) => setField(path, Number(v)));\n if (field.type === 'slider') {\n const effectiveValue = value === undefined && field.defaultValue !== undefined ? field.defaultValue : value;\n let sliderField = field;\n if (field.key === 'highlightRow') {\n const slideProps = goal.slides[selectedSlideIdx]?.props;\n const rows = Array.isArray(slideProps?.rows) ? slideProps.rows : [];\n sliderField = { ...field, max: Math.min(field.max ?? 11, Math.max(0, rows.length - 1)) };\n }\n return createSliderField(sliderField, effectiveValue, (v) => setField(path, v));\n }\n if (field.type === 'boolean') {\n const effectiveValue = value === undefined && field.defaultValue !== undefined ? field.defaultValue : value;\n return createToggleField(label, effectiveValue, (v) => {\n setField(path, v);\n renderSlidePanel();\n });\n }\n if (field.type === 'select') return createSelectField(label, value, field.options || [], (v) => setField(path, v));\n if (field.type === 'color') return createColorField(label, value, (v) => setField(path, v));\n return createTextField(label, value, (v) => setField(path, v || undefined));\n }\n\n function getSchemaEmptyItem(itemSchema) {\n if (!itemSchema || !itemSchema.length) return '\u793A\u4F8B\u9879';\n // itemSchema \u53EA\u6709\u5355\u4E00\u5360\u4F4D\u5B57\u6BB5\u65F6\uFF0C\u8868\u793A\u6570\u7EC4\u5143\u7D20\u662F\u539F\u59CB\u503C\uFF08\u5B57\u7B26\u4E32/\u6570\u5B57\u7B49\uFF09\n if (itemSchema.length === 1 && itemSchema[0].key === 'item') {\n const field = itemSchema[0];\n if (field.defaultValue !== undefined) return JSON.parse(JSON.stringify(field.defaultValue));\n if (field.type === 'boolean') return false;\n if (field.type === 'number') return 100;\n if (field.type === 'textarea') return '\u793A\u4F8B\u5185\u5BB9';\n return '\u793A\u4F8B\u6587\u5B57';\n }\n const item = {};\n itemSchema.forEach((field) => {\n if (field.defaultValue !== undefined) {\n item[field.key] = JSON.parse(JSON.stringify(field.defaultValue));\n } else if (field.type === 'boolean') {\n item[field.key] = false;\n } else if (field.type === 'number') {\n item[field.key] = 100;\n } else if (field.type === 'textarea') {\n item[field.key] = '\u793A\u4F8B\u5185\u5BB9';\n } else if (field.type === 'array') {\n const count = typeof field.minItems === 'number' ? Math.max(1, field.minItems) : 1;\n const empty = [];\n for (let i = 0; i < count; i++) {\n empty.push(getSchemaEmptyItem(field.itemSchema));\n }\n item[field.key] = empty;\n } else if (field.type === 'image') {\n item[field.key] = '';\n } else if (field.type === 'text') {\n item[field.key] = '\u793A\u4F8B\u6587\u5B57';\n } else if (field.type === 'select') {\n const firstOption = Array.isArray(field.options) && field.options.length > 0 ? field.options[0] : undefined;\n item[field.key] = firstOption && typeof firstOption === 'object' ? firstOption.value : '';\n } else {\n item[field.key] = '\u793A\u4F8B\u6587\u5B57';\n }\n });\n return item;\n }\n\n function createSchemaArraySection(field, array, parent, basePath, skipCanvasEditable) {\n const path = basePath || field.key;\n const section = createEl('div', 'lp-property-section', parent);\n\n // \u6761\u76EE\u6570\u7531\u7EC4\u4EF6\u6837\u5F0F\u51B3\u5B9A\uFF1AminItems / maxItems \u6765\u81EA\u7248\u5F0F Schema\n const minItems = typeof field.minItems === 'number' ? field.minItems : 1;\n // \u5F53 Schema \u672A\u58F0\u660E maxItems \u65F6\uFF0C\u4F7F\u7528\u4E00\u4E2A\u9ED8\u8BA4\u4E0A\u9650\uFF08\u800C\u4E0D\u662F\u5F53\u524D\u6570\u7EC4\u957F\u5EA6\uFF09\uFF0C\n // \u907F\u514D\u7528\u6237\u51CF\u5C11\u6761\u76EE\u540E slider \u7684 max \u540C\u6B65\u7F29\u5C0F\uFF0C\u5BFC\u81F4\u65E0\u6CD5\u6062\u590D\u6216\u7EE7\u7EED\u589E\u52A0\u3002\n const maxItems = typeof field.maxItems === 'number' ? field.maxItems : Math.max(array.length, minItems, 6);\n const currentCount = Math.max(minItems, Math.min(maxItems, array.length));\n\n // \u6807\u9898\u4E0E\u6570\u503C\u653E\u5728\u540C\u4E00\u884C\n const header = createEl('div', 'lp-property-section-header', section);\n createEl('div', 'lp-property-section-title', header).textContent = field.label;\n const countBadge = createEl('span', 'lp-property-slider-value', header);\n countBadge.textContent = String(currentCount);\n\n // \u6570\u91CF\u6ED1\u5757\uFF08\u6807\u5C3A\u6837\u5F0F\uFF09\n const sliderWrap = createEl('div', 'lp-property-slider-wrap', section);\n const ruler = createEl('div', 'lp-property-slider-ruler', sliderWrap);\n const range = document.createElement('input');\n range.type = 'range';\n range.className = 'lp-property-slider';\n range.min = String(minItems);\n range.max = String(maxItems);\n range.value = String(currentCount);\n ruler.appendChild(range);\n renderSliderTicks(ruler, minItems, maxItems);\n\n function setArrayLength(newLength) {\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n let arr = getProp(slide.props, path);\n if (!Array.isArray(arr)) {\n arr = [];\n setProp(slide.props, path, arr);\n }\n if (newLength < arr.length) {\n arr.length = newLength;\n } else if (newLength > arr.length) {\n while (arr.length < newLength) {\n arr.push(getSchemaEmptyItem(field.itemSchema));\n }\n }\n }\n\n function updateSliderFill() {\n const min = Number(range.min);\n const max = Number(range.max);\n const pct = max === min ? 0 : ((Number(range.value) - min) / (max - min)) * 100;\n range.style.background = 'linear-gradient(90deg, rgba(91,155,213,1) 0%, rgba(139,92,246,1) ' + pct + '%, rgba(255,255,255,0.14) ' + pct + '%, rgba(255,255,255,0.14) 100%)';\n }\n updateSliderFill();\n\n range.addEventListener('input', () => {\n const newLength = Number(range.value);\n countBadge.textContent = String(newLength);\n setArrayLength(newLength);\n updateSliderFill();\n refreshCurrentSlide();\n });\n\n range.addEventListener('change', () => {\n recordHistory();\n autoSave();\n renderSlidePanel();\n });\n\n // \u70B9\u51FB\u6807\u5C3A\u5FEB\u901F\u8DF3\u8F6C\n ruler.addEventListener('click', (e) => {\n if (e.target === range || range.contains(e.target)) return;\n const rect = ruler.getBoundingClientRect();\n const padding = 9;\n const availableWidth = rect.width - padding * 2;\n const x = e.clientX - rect.left - padding;\n const ratio = Math.max(0, Math.min(1, x / availableWidth));\n const newLength = Math.round(minItems + ratio * (maxItems - minItems));\n if (newLength !== Number(range.value)) {\n range.value = String(newLength);\n countBadge.textContent = String(newLength);\n setArrayLength(newLength);\n updateSliderFill();\n refreshCurrentSlide();\n recordHistory();\n autoSave();\n renderSlidePanel();\n }\n });\n\n // \u663E\u793A\u5F53\u524D\u6761\u76EE\u7684\u53EF\u7F16\u8F91\u5B57\u6BB5\uFF08\u6570\u7EC4\u9879\u59CB\u7EC8\u5C55\u793A\uFF0C\u907F\u514D\u65B0\u589E\u7A7A\u767D\u6761\u76EE\u65E0\u6CD5\u7F16\u8F91\uFF09\n const isPrimitiveItem = field.itemSchema && field.itemSchema.length === 1 && field.itemSchema[0].key === 'item';\n const allSubFieldsAreCanvasEditable = false;\n\n if (!allSubFieldsAreCanvasEditable) {\n const list = createEl('div', 'lp-property-array', section);\n array.forEach((item, index) => {\n const itemWrap = createEl('div', 'lp-property-array-item', list);\n\n const itemHeader = createEl('div', 'lp-property-array-item-header', itemWrap);\n const itemIndexLabel = createEl('span', '', itemHeader);\n itemIndexLabel.textContent = '#' + (index + 1);\n const deleteBtn = createEl('button', 'lp-property-btn-danger', itemHeader);\n deleteBtn.type = 'button';\n deleteBtn.title = '\u5220\u9664\u8BE5\u9879';\n deleteBtn.textContent = '\u00D7';\n deleteBtn.disabled = array.length <= minItems;\n deleteBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n e.preventDefault();\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n const arr = getProp(slide.props, path);\n if (!Array.isArray(arr)) return;\n arr.splice(index, 1);\n refreshCurrentSlide();\n recordHistory();\n autoSave();\n renderSlidePanel();\n });\n\n if (isPrimitiveItem) {\n const subField = field.itemSchema[0];\n const itemPath = path + '.' + index;\n const itemValue = item;\n const control = createSchemaFieldControl(subField, itemValue, itemPath, skipCanvasEditable);\n if (control) itemWrap.appendChild(control);\n } else if (field.itemSchema) {\n field.itemSchema.forEach((subField) => {\n if (skipCanvasEditable && (subField.type === 'text' || subField.type === 'textarea' || subField.type === 'image')) return;\n const itemPath = path + '.' + index + '.' + subField.key;\n const itemValue = typeof item === 'object' && item !== null ? item[subField.key] : undefined;\n const control = createSchemaFieldControl(subField, itemValue, itemPath, skipCanvasEditable);\n if (control) itemWrap.appendChild(control);\n });\n } else if (typeof item === 'string') {\n if (skipCanvasEditable) return;\n const textarea = createEl('textarea', 'lp-property-textarea', itemWrap);\n textarea.value = item;\n textarea.addEventListener('input', () => {\n setField(path + '.' + index, textarea.value);\n });\n }\n });\n }\n }\n\n function findSchemaField(schema, path) {\n if (!schema || !schema.fields || !path) return null;\n const parts = path.split('.');\n let fields = schema.fields;\n let field = null;\n for (let i = 0; i < parts.length; i++) {\n const key = parts[i];\n if (!fields) return null;\n if (/^d+$/.test(key)) {\n // \u6570\u7EC4\u7D22\u5F15\uFF0C\u8FDB\u5165\u4E0B\u4E00\u5C42 itemSchema\n if (field && field.itemSchema) {\n fields = field.itemSchema;\n }\n continue;\n }\n field = fields.find((f) => f.key === key) || null;\n if (!field) return null;\n if (i < parts.length - 1) {\n fields = field.type === 'array' ? field.itemSchema : (field.type === 'object' ? field.itemSchema : null);\n }\n }\n return field;\n }\n\n function renderSchemaFields(schema, props, parent, basePath, skipCanvasEditable) {\n if (!schema || !schema.fields) return;\n schema.fields.forEach((field) => {\n // \u7B80\u5355\u6587\u5B57\u5B57\u6BB5\u76F4\u63A5\u5728\u753B\u5E03\u4E0A\u7F16\u8F91\uFF0C\u4E0D\u5728\u53F3\u4FA7\u8FB9\u680F\u91CD\u590D\u6E32\u67D3\n if (field.inlineEditable) return;\n // \u975E\u56FE\u8868\u7EC4\u4EF6\u4E2D\uFF0C\u53EF\u5728\u753B\u5E03\u76F4\u63A5\u7F16\u8F91\u7684\u6587\u5B57\u3001\u56FE\u7247\u5B57\u6BB5\u4E0D\u518D\u5728\u53F3\u4FA7\u8FB9\u680F\u91CD\u590D\u63D0\u4F9B\n if (skipCanvasEditable && (field.type === 'text' || field.type === 'textarea' || field.type === 'image')) return;\n // \u4F9D\u8D56\u5F00\u5173\u7684\u5B57\u6BB5\uFF0C\u5F00\u5173\u5173\u95ED\u65F6\u6536\u8D77\uFF1B\u672A\u8BBE\u7F6E\u65F6\u6309\u5F00\u5173\u9ED8\u8BA4\u503C\u5224\u65AD\n if (field.visibleWhen) {\n const controller = findSchemaField(schema, field.visibleWhen.key);\n const rawValue = getProp(props, field.visibleWhen.key);\n const effectiveValue = rawValue === undefined && controller?.defaultValue !== undefined ? controller.defaultValue : rawValue;\n if (effectiveValue !== field.visibleWhen.value) return;\n }\n const path = basePath ? basePath + '.' + field.key : field.key;\n const value = getProp(props, path);\n if (field.type === 'array') {\n createSchemaArraySection(field, Array.isArray(value) ? value : [], parent, path, skipCanvasEditable);\n } else if (field.type === 'object') {\n const section = createEl('div', 'lp-property-section', parent);\n createEl('div', 'lp-property-section-title', section).textContent = field.label;\n if (field.itemSchema) {\n renderSchemaFields({ fields: field.itemSchema }, props, section, path, skipCanvasEditable);\n }\n } else if (field.type === 'slider') {\n const section = createEl('div', 'lp-property-section', parent);\n const effectiveValue = value === undefined && field.defaultValue !== undefined ? field.defaultValue : value;\n let sliderField = field;\n if (field.key === 'highlightRow') {\n const rows = Array.isArray(props?.rows) ? props.rows : [];\n sliderField = { ...field, max: Math.min(field.max ?? 11, Math.max(0, rows.length - 1)) };\n }\n section.appendChild(createSliderField(sliderField, effectiveValue, (v) => setField(path, v)));\n } else {\n const control = createSchemaFieldControl(field, value, path, skipCanvasEditable);\n if (control) parent.appendChild(control);\n }\n });\n }\n\n function renderSlideFields(props, parent, skipCanvasEditable) {\n Object.keys(props).forEach((key) => {\n if (key === '_style') return;\n const value = props[key];\n if (Array.isArray(value)) {\n // \u65E0 Schema \u65F6\u4EC5\u63D0\u4F9B\u7B80\u5355\u6587\u672C\u7F16\u8F91\uFF0C\u4E0D\u663E\u793A\u5E8F\u53F7\u523B\u5EA6\n const section = createEl('div', 'lp-property-section', parent);\n createEl('div', 'lp-property-section-title', section).textContent = getFieldLabel(key) + ' (' + value.length + ')';\n const list = createEl('div', 'lp-property-array', section);\n value.forEach((item, index) => {\n const itemWrap = createEl('div', 'lp-property-array-item', list);\n if (typeof item === 'string') {\n if (skipCanvasEditable) return;\n const textarea = createEl('textarea', 'lp-property-textarea', itemWrap);\n textarea.value = item;\n textarea.addEventListener('input', () => {\n setField(key + '.' + index, textarea.value);\n });\n } else if (typeof item === 'object' && item !== null) {\n Object.keys(item).forEach((subKey) => {\n const type = inferFieldType(key + '.' + index + '.' + subKey, item[subKey]);\n if (skipCanvasEditable && (type === 'text' || type === 'textarea' || type === 'image')) return;\n itemWrap.appendChild(createFieldControl(key + '.' + index + '.' + subKey, item[subKey]));\n });\n }\n if (!itemWrap.hasChildNodes()) {\n itemWrap.remove();\n }\n });\n if (!list.hasChildNodes()) {\n list.remove();\n }\n } else if (value !== null && typeof value === 'object') {\n // \u5D4C\u5957\u5BF9\u8C61\u76F4\u63A5\u5C55\u5F00\uFF08\u76EE\u524D\u8F83\u5C11\uFF09\n const section = createEl('div', 'lp-property-section', parent);\n createEl('div', 'lp-property-section-title', section).textContent = getFieldLabel(key);\n Object.keys(value).forEach((subKey) => {\n const type = inferFieldType(key + '.' + subKey, value[subKey]);\n if (skipCanvasEditable && (type === 'text' || type === 'textarea' || type === 'image')) return;\n section.appendChild(createFieldControl(key + '.' + subKey, value[subKey]));\n });\n } else {\n const type = inferFieldType(key, value);\n if (skipCanvasEditable && (type === 'text' || type === 'textarea' || type === 'image')) return;\n parent.appendChild(createFieldControl(key, value));\n }\n });\n }\n\n function renderSlidePanel() {\n if (!propertyContent) return;\n propertyContent.innerHTML = '';\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n\n const info = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', info).textContent = '\u5E7B\u706F\u7247 ' + (selectedSlideIdx + 1);\n const layoutLabel = createEl('div', 'lp-property-help', info);\n layoutLabel.textContent = '\u7248\u5F0F\uFF1A' + getLayoutLabel(slide.layout);\n\n const transitionSection = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', transitionSection).textContent = '\u5207\u6362\u52A8\u753B';\n const transitionSelect = document.createElement('select');\n transitionSelect.className = 'lp-editor-select';\n transitionSelect.style.width = '100%';\n const currentTransition = slide.props.transition || 'none';\n TRANSITIONS.forEach((t) => {\n const opt = document.createElement('option');\n opt.value = t.key;\n opt.textContent = t.label;\n if (t.key === currentTransition) opt.selected = true;\n transitionSelect.appendChild(opt);\n });\n transitionSelect.addEventListener('change', () => {\n slide.props.transition = transitionSelect.value;\n const wrapper = document.querySelector('.lp-slide-wrapper[data-slide-index=\"' + selectedSlideIdx + '\"]');\n if (wrapper) wrapper.setAttribute('data-lp-transition', slide.props.transition);\n recordHistory();\n saveState();\n });\n transitionSection.appendChild(transitionSelect);\n\n if (selectedEl && slide.role === 'chart') {\n const prop = selectedEl.getAttribute('data-lp-prop');\n const schema = resolveLayoutSchema(slide);\n const schemaField = schema ? findSchemaField(schema, prop) : null;\n // \u7B80\u5355\u6587\u5B57\u5B57\u6BB5\u5DF2\u5728\u753B\u5E03\u4E0A\u76F4\u63A5\u7F16\u8F91\uFF0C\u4E0D\u5728\u53F3\u4FA7\u8FB9\u680F\u91CD\u590D\u663E\u793A\n if (!schemaField || !schemaField.inlineEditable) {\n const quickSection = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', quickSection).textContent = '\u5F53\u524D\u9009\u4E2D';\n const fieldValue = getProp(slide.props, prop);\n quickSection.appendChild(createFieldControl(prop, fieldValue));\n }\n }\n\n const fieldsSection = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', fieldsSection).textContent = '\u5185\u5BB9\u5C5E\u6027';\n const schema = resolveLayoutSchema(slide);\n const skipCanvasEditable = slide.role !== 'chart';\n if (schema && schema.fields) {\n renderSchemaFields(schema, slide.props, fieldsSection, undefined, skipCanvasEditable);\n } else {\n renderSlideFields(slide.props, fieldsSection, skipCanvasEditable);\n }\n }\n\n function selectEl(el) {\n if (selectedEl === el) return;\n if (selectedEl) selectedEl.classList.remove('lp-selected');\n selectedEl = el;\n selectedEl.classList.add('lp-selected');\n selectedSlideIdx = Number(el.getAttribute('data-lp-slide-idx')) || current;\n renderSlidePanel();\n // \u6EDA\u52A8\u5230\u5F53\u524D\u9009\u4E2D\u5B57\u6BB5\n const path = el.getAttribute('data-lp-prop');\n if (path && propertyContent) {\n const label = propertyContent.querySelector('.lp-property-label');\n // \u7B80\u5355\u9AD8\u4EAE\uFF1A\u6682\u65F6\u4E0D\u505A\u81EA\u52A8\u6EDA\u52A8\uFF0C\u907F\u514D\u590D\u6742\u5EA6\n }\n }\n\n function renderSlideToRootByIndex(index, wrapper) {\n if (typeof window.__lemonPPT_renderSlideToRoot !== 'function') return;\n const slide = goal.slides[index];\n if (!slide || !wrapper) {\n return;\n }\n window.__lemonPPT_renderSlideToRoot(wrapper, slide, { slideIdx: index, editable: true, theme: goal.theme });\n initEditableElements(wrapper);\n }\n\n function initActiveSlideECharts() {\n const activeWrapper = document.querySelector('.lp-slide-wrapper.active');\n if (activeWrapper && typeof window.__lemonPPT_initECharts === 'function') {\n window.__lemonPPT_initECharts(goal.theme || 'theme01', activeWrapper);\n }\n }\n\n function disposeEChartsInWrapper(wrapper) {\n if (!wrapper) return;\n wrapper.querySelectorAll('[data-lp-echart-type]').forEach((container) => {\n const inst = container.__lpEChartInstance;\n if (inst && !inst.isDisposed()) {\n inst.dispose();\n }\n container.__lpEChartInstance = undefined;\n });\n }\n\n function renderCurrentSlideToRoot() {\n const wrapper = document.querySelector('.lp-slide-wrapper.active');\n if (!wrapper) return;\n renderSlideToRootByIndex(selectedSlideIdx, wrapper);\n // React 18 createRoot().render() \u662F\u5F02\u6B65\u7684\uFF0C\u5EF6\u8FDF\u5230\u4E0B\u4E00\u5E27\u518D\u521D\u59CB\u5316 ECharts\uFF0C\n // \u786E\u4FDD\u5360\u4F4D\u5BB9\u5668\u5DF2\u7ECF\u6302\u8F7D\u5230 DOM \u5E76\u6709\u53EF\u6D4B\u91CF\u7684\u5C3A\u5BF8\u3002\n requestAnimationFrame(() => {\n initActiveSlideECharts();\n });\n }\n\n // \u9884\u521B\u5EFA\u6240\u6709 slide \u7684 React root \u5E76\u6E32\u67D3\uFF0C\u4F7F\u5207\u9875\u548C\u975E active slide \u66F4\u65B0\u90FD\u65E0\u9700\u91CD\u65B0\u6784\u5EFA DOM\n function renderAllSlidesToRoot() {\n if (typeof window.__lemonPPT_renderSlideToRoot !== 'function') {\n return;\n }\n const wrappers = document.querySelectorAll('.lp-slide-wrapper');\n wrappers.forEach((wrapper, index) => {\n renderSlideToRootByIndex(index, wrapper);\n });\n // \u7F16\u8F91\u5668\u573A\u666F\u4E0B\u53EA\u521D\u59CB\u5316\u5F53\u524D active slide \u7684 ECharts\uFF0C\u907F\u514D 75+ \u9875\u540C\u65F6\u6E32\u67D3\u5BFC\u81F4\u5361\u987F\u3002\n requestAnimationFrame(() => {\n initActiveSlideECharts();\n });\n }\n\n function selectSlide(index) {\n if (selectedEl) {\n selectedEl.classList.remove('lp-selected');\n selectedEl = null;\n }\n selectedSlideIdx = index;\n renderSlidePanel();\n }\n\n document.addEventListener('focusin', (e) => {\n const el = e.target.closest && e.target.closest('[data-lp-editable=\"true\"], [data-lp-editable-image=\"true\"]');\n if (el) selectEl(el);\n });\n\n document.addEventListener('click', (e) => {\n const editableEl = e.target.closest && e.target.closest('[data-lp-editable=\"true\"], [data-lp-editable-image=\"true\"]');\n if (editableEl) {\n selectEl(editableEl);\n return;\n }\n const wrapper = e.target.closest && e.target.closest('.lp-slide-wrapper');\n if (wrapper) {\n selectSlide(Number(wrapper.getAttribute('data-slide-index')));\n return;\n }\n if (e.target.closest && !e.target.closest('.lp-editor-right-panel')) {\n clearSelection();\n }\n });\n\n // \u6062\u590D\u4E0A\u6B21\u505C\u7559\u7684\u5E7B\u706F\u7247\uFF08\u7ED3\u6784\u53D8\u66F4\u540E\u91CD\u8F7D\u7528\uFF09\n const savedCurrent = localStorage.getItem('lemonppt:editor:currentSlide');\n if (savedCurrent) {\n const savedIndex = Number(savedCurrent);\n if (!Number.isNaN(savedIndex) && savedIndex >= 0 && savedIndex < slides.length) {\n goTo(savedIndex);\n }\n localStorage.removeItem('lemonppt:editor:currentSlide');\n }\n\n // \u9ED8\u8BA4\u9009\u4E2D\u5F53\u524D\u5E7B\u706F\u7247\n selectSlide(current);\n\n // \u82E5 localStorage \u4E2D\u5E7B\u706F\u7247\u6570\u91CF\u4E0E\u5F53\u524D DOM \u4E0D\u5339\u914D\uFF0C\u91CD\u5EFA DOM \u540E\u518D\u6E32\u67D3\n if (needsDomRebuild) {\n rebuildSlidesAndThumbnails();\n } else {\n // \u9884\u521B\u5EFA\u5E76\u6E32\u67D3\u6240\u6709 slide \u7684 React root\uFF0C\u4F7F\u5207\u9875\u4E0E\u540E\u7EED\u66F4\u65B0\u90FD\u8D70 React reconcile\n renderAllSlidesToRoot();\n }\n\n // \u66B4\u9732\u7ED9 editor.js\uFF1A\u65E0\u5237\u65B0\u5207\u6362\u4E3B\u9898\u65F6\u91CD\u7F6E\u5185\u90E8\u72B6\u6001\n window.__lemonPPT_applyTheme = function(newGoal, newSlidesMarkup) {\n try {\n const oldKey = 'lemonppt:editor:v2:' + (goal.theme || 'theme01') + ':' + (goal.randomSeed || goal.title || 'default');\n localStorage.setItem(oldKey, JSON.stringify(goal));\n } catch (err) {\n console.warn('\u5207\u6362\u4E3B\u9898\u524D\u4FDD\u5B58\u5931\u8D25', err);\n }\n goal = newGoal;\n window.__lemonPPT_goal = goal;\n\n // \u5C1D\u8BD5\u6062\u590D\u5F53\u524D\u4E3B\u9898\u7684\u672C\u5730\u7F16\u8F91\u72B6\u6001\n try {\n const newKey = 'lemonppt:editor:v2:' + (goal.theme || 'theme01') + ':' + (goal.randomSeed || goal.title || 'default');\n const saved = localStorage.getItem(newKey);\n if (saved) {\n const parsed = JSON.parse(saved);\n if (parsed && Array.isArray(parsed.slides)) {\n goal = parsed;\n window.__lemonPPT_goal = goal;\n }\n }\n } catch (err) {\n console.warn('\u5207\u6362\u4E3B\u9898\u540E\u6062\u590D\u5931\u8D25', err);\n }\n\n const slidesContainer = document.getElementById('lp-slides');\n if (slidesContainer) {\n slidesContainer.innerHTML = newSlidesMarkup;\n }\n current = 0;\n selectedSlideIdx = 0;\n rebuildSlidesAndThumbnails();\n selectSlide(current);\n syncAppearanceFromGoal();\n autoSave();\n updateUndoRedoButtons();\n };\n})();\n";
1
+ export declare const editorScript = "\n(function () {\n // \u7528 jQuery \u4E3A\u6240\u6709\u5E26 data-scrollbar \u7684\u5BB9\u5668\u6DFB\u52A0\u81EA\u5B9A\u4E49\u6EDA\u52A8\u6761\u6837\u5F0F\u7C7B\n if (typeof jQuery !== 'undefined') {\n jQuery(function ($) {\n $('[data-scrollbar]').addClass('lp-custom-scrollbar');\n });\n }\n\n const MIGRATION_KEY = 'lemonppt:editor:migration:showInsight:v1';\n const MAX_HISTORY = 50;\n\n let goal = window.__lemonPPT_goal;\n if (!goal) return;\n\n function getStorageKey() {\n return 'lemonppt:editor:v2:' + (goal.theme || 'theme01') + ':' + (goal.randomSeed || goal.title || 'default');\n }\n\n // \u4F18\u5148\u4ECE localStorage \u6062\u590D\n let needsDomRebuild = false;\n try {\n const saved = localStorage.getItem(getStorageKey());\n if (saved) {\n const parsed = JSON.parse(saved);\n if (parsed && parsed.slides) {\n // \u4E00\u6B21\u6027\u8FC1\u79FB\uFF1A\u5C06\u56FE\u8868\u9875\u7684\u91CD\u70B9\u5F3A\u8C03\u5F00\u5173\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u5F00\u542F\n if (!localStorage.getItem(MIGRATION_KEY)) {\n parsed.slides.forEach((slide) => {\n if (slide.role === 'chart') {\n slide.props = slide.props || {};\n slide.props.showInsight = true;\n }\n });\n localStorage.setItem(MIGRATION_KEY, '1');\n }\n // \u82E5\u7ED3\u6784\u6570\u91CF\u6216\u5E7B\u706F\u7247\u987A\u5E8F\u53D1\u751F\u53D8\u5316\uFF0C\u9700\u8981\u91CD\u5EFA DOM\uFF1B\u4EC5\u5185\u5BB9\u53D8\u5316\u5219\u540C\u6B65\u6587\u672C\u5373\u53EF\n const orderChanged = parsed.slides.length !== goal.slides.length ||\n parsed.slides.some((slide, i) => slide.layout !== goal.slides[i].layout);\n goal = parsed;\n window.__lemonPPT_goal = goal;\n if (orderChanged) {\n needsDomRebuild = true;\n } else {\n syncDomFromGoal();\n }\n }\n }\n } catch (err) {\n console.warn('\u81EA\u52A8\u6062\u590D\u5931\u8D25', err);\n }\n\n // \u521D\u59CB\u5316\u5916\u89C2\u6A21\u5F0F\uFF08\u540C\u4E00\u4E3B\u9898\u4E0B\u7684\u6D45\u8272/\u6DF1\u8272\u5207\u6362\uFF0C\u901A\u8FC7 data-theme / data-appearance \u5207\u6362 CSS \u53D8\u91CF\uFF09\n syncAppearanceFromGoal();\n\n const TRANSITIONS = [\n { key: 'none', label: '\u65E0\u52A8\u753B' },\n { key: 'slide', label: '\u6A2A\u6ED1' },\n { key: 'fade', label: '\u6DE1\u5165\u6DE1\u51FA' },\n { key: 'scale', label: '\u7F29\u653E' },\n { key: 'cut-in', label: '\u5207\u5165' },\n { key: 'cube', label: '\u7ACB\u65B9\u4F53' },\n { key: 'flip', label: '\u7FFB\u9875' },\n { key: 'cover', label: '\u8986\u76D6' },\n { key: 'wipe', label: '\u64E6\u9664' },\n ];\n\n const history = [deepClone(goal)];\n let historyIndex = 0;\n\n function deepClone(obj) {\n return JSON.parse(JSON.stringify(obj));\n }\n\n function recordHistory() {\n // \u622A\u65AD redo \u5206\u652F\n if (historyIndex < history.length - 1) {\n history.splice(historyIndex + 1);\n }\n history.push(deepClone(goal));\n if (history.length > MAX_HISTORY) {\n history.shift();\n } else {\n historyIndex++;\n }\n updateUndoRedoButtons();\n }\n\n function undo() {\n if (historyIndex <= 0) return;\n historyIndex--;\n restoreGoal(history[historyIndex]);\n }\n\n function redo() {\n if (historyIndex >= history.length - 1) return;\n historyIndex++;\n restoreGoal(history[historyIndex]);\n }\n\n function restoreGoal(newGoal) {\n const oldSlides = goal && goal.slides ? goal.slides : [];\n goal = newGoal;\n window.__lemonPPT_goal = goal;\n Object.assign(window.__lemonPPT_goal, newGoal);\n const orderChanged = oldSlides.length !== goal.slides.length ||\n oldSlides.some((slide, i) => slide.layout !== goal.slides[i].layout);\n if (orderChanged) {\n current = 0;\n selectedSlideIdx = 0;\n rebuildSlidesAndThumbnails();\n if (typeof selectSlide === 'function') selectSlide(current);\n } else {\n syncDomFromGoal();\n }\n syncAppearanceFromGoal();\n autoSave();\n updateUndoRedoButtons();\n }\n\n let autoSaveTimer = null;\n function autoSave() {\n if (autoSaveTimer) {\n clearTimeout(autoSaveTimer);\n }\n autoSaveTimer = setTimeout(() => {\n autoSaveTimer = null;\n try {\n localStorage.setItem(getStorageKey(), JSON.stringify(goal));\n } catch (err) {\n console.warn('\u81EA\u52A8\u4FDD\u5B58\u5931\u8D25', err);\n }\n }, 400);\n }\n\n // \u83B7\u53D6\u53EF\u7F16\u8F91\u5143\u7D20\uFF1B\u517C\u5BB9 Text \u8282\u70B9\u3001Shadow DOM \u7B49\u573A\u666F\n function getEditingElement(target) {\n if (!target) return null;\n if (target.nodeType === Node.TEXT_NODE) {\n target = target.parentElement;\n }\n if (!target || typeof target.closest !== 'function') return null;\n return target.closest('[data-lp-editable=\"true\"], [contenteditable=\"true\"], input, textarea, select');\n }\n\n function isEditingTarget(target) {\n return !!getEditingElement(target);\n }\n\n function propMatchesChangedPath(prop, changedPath) {\n if (!changedPath) return true;\n return prop === changedPath || prop.startsWith(changedPath + '.') || changedPath.startsWith(prop + '.');\n }\n\n function syncDomFromGoal(changedPath) {\n // \u6587\u672C\uFF1A\u5982\u679C\u6307\u5B9A\u4E86 changedPath\uFF0C\u53EA\u540C\u6B65\u53D7\u5F71\u54CD\u7684\u8DEF\u5F84\uFF0C\u907F\u514D\u8986\u76D6 React \u6E32\u67D3\u7684\u7279\u6B8A\u7ED3\u6784\uFF08\u5982\u6807\u9898\u9AD8\u4EAE\uFF09\n document.querySelectorAll('[data-lp-editable=\"true\"]').forEach((el) => {\n const slideIdx = Number(el.getAttribute('data-lp-slide-idx'));\n const prop = el.getAttribute('data-lp-prop');\n if (Number.isNaN(slideIdx) || !prop) return;\n if (!propMatchesChangedPath(prop, changedPath)) return;\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n const value = getProp(slide.props, prop);\n el.textContent = value == null ? '' : String(value);\n });\n\n // \u56FE\u7247\n document.querySelectorAll('[data-lp-editable-image=\"true\"]').forEach((img) => {\n const slideIdx = Number(img.getAttribute('data-lp-slide-idx'));\n const prop = img.getAttribute('data-lp-prop') || 'image';\n if (Number.isNaN(slideIdx)) return;\n if (!propMatchesChangedPath(prop, changedPath)) return;\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n const value = getProp(slide.props, prop);\n if (value) {\n if (img.tagName === 'IMG') {\n img.setAttribute('src', String(value));\n } else {\n // \u5360\u4F4D\u5BB9\u5668\u9700\u8981\u66FF\u6362\u4E3A\u771F\u5B9E\u56FE\u7247\n const newImg = document.createElement('img');\n newImg.className = img.className.replace(/\blp-editable-image-placeholder\b/g, '').trim();\n newImg.src = String(value);\n newImg.alt = img.getAttribute('alt') || '';\n newImg.setAttribute('data-lp-editable-image', 'true');\n newImg.setAttribute('data-lp-slide-idx', String(slideIdx));\n newImg.setAttribute('data-lp-prop', prop);\n img.parentNode.replaceChild(newImg, img);\n }\n }\n // \u65E0\u56FE\u7247\u65F6\u4FDD\u7559\u5360\u4F4D\u5BB9\u5668\uFF0C\u786E\u4FDD\u4ECD\u53EF\u70B9\u51FB\u4E0A\u4F20\n });\n }\n\n function getProp(obj, path) {\n const keys = path.split('.');\n let target = obj;\n for (const k of keys) {\n if (target == null || typeof target !== 'object') return undefined;\n target = target[k];\n }\n return target;\n }\n\n function setProp(obj, path, value) {\n const keys = path.split('.');\n let target = obj;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!(k in target)) target[k] = [];\n target = target[k];\n }\n target[keys[keys.length - 1]] = value;\n }\n\n // \u70B9\u51FB\u56FE\u7247\u56FA\u5B9A\u533A\u57DF\u76F4\u63A5\u4E0A\u4F20\u672C\u5730\u56FE\u7247\n function handleImageUpload(target, slideIdx, prop) {\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = 'image/*';\n input.style.display = 'none';\n document.body.appendChild(input);\n\n input.addEventListener('change', () => {\n const file = input.files?.[0];\n if (!file) {\n input.remove();\n return;\n }\n const reader = new FileReader();\n reader.onload = (e) => {\n const dataUrl = e.target?.result;\n if (typeof dataUrl !== 'string') {\n input.remove();\n return;\n }\n recordHistory();\n setProp(slide.props, prop, dataUrl);\n autoSave();\n renderCurrentSlideToRoot();\n input.remove();\n };\n reader.readAsDataURL(file);\n });\n\n input.addEventListener('cancel', () => input.remove());\n input.click();\n }\n\n function escapeHtml(str) {\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n }\n\n // \u521D\u59CB\u5316 contenteditable \u5C5E\u6027\uFF1B\u4F7F\u7528 MutationObserver \u76D1\u542C React \u91CD\u65B0\u6E32\u67D3\u540E\u65B0\u589E\u7684\u7F16\u8F91\u5143\u7D20\n function initEditableElements(container) {\n const scope = container || document;\n scope.querySelectorAll('[data-lp-editable=\"true\"]').forEach((el) => {\n if (el.getAttribute('contenteditable') !== 'true') {\n el.setAttribute('contenteditable', 'true');\n }\n });\n }\n initEditableElements();\n\n const editableObserver = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n initEditableElements(node);\n }\n });\n });\n });\n editableObserver.observe(document.body, { childList: true, subtree: true });\n\n // \u4F7F\u7528\u4E8B\u4EF6\u59D4\u6258\u5904\u7406\u6587\u672C\u7F16\u8F91\uFF0C\u652F\u6301 React \u5C40\u90E8\u6E32\u67D3\u540E\u81EA\u52A8\u751F\u6548\n document.addEventListener('focus', (e) => {\n const el = e.target.closest('[data-lp-editable=\"true\"]');\n if (!el) return;\n recordHistory();\n }, true);\n\n document.addEventListener('blur', (e) => {\n const el = e.target.closest('[data-lp-editable=\"true\"]');\n if (!el) return;\n const slideIdx = Number(el.getAttribute('data-lp-slide-idx'));\n const prop = el.getAttribute('data-lp-prop');\n if (Number.isNaN(slideIdx) || !prop) return;\n\n const slide = goal.slides[slideIdx];\n if (!slide) return;\n\n const rawValue = el.textContent || '';\n const fieldType = el.getAttribute('data-lp-editable-type');\n const value = fieldType === 'number' ? Number(rawValue) : rawValue;\n setProp(slide.props, prop, value);\n autoSave();\n // \u56FE\u8868\u6570\u636E\u5B57\u6BB5\u5728\u753B\u5E03\u4E0A\u76F4\u63A5\u7F16\u8F91\u540E\uFF0C\u9700\u8981\u91CD\u65B0\u6E32\u67D3\u5E7B\u706F\u7247\u4EE5\u66F4\u65B0\u56FE\u8868\n if (el.getAttribute('data-lp-chart-data') === 'true' && typeof window.__lemonPPT_initECharts === 'function') {\n refreshCurrentSlide();\n }\n }, true);\n\n document.addEventListener('keydown', (e) => {\n const el = getEditingElement(e.target) || getEditingElement(document.activeElement);\n if (!el) return;\n if (e.key === 'Enter') {\n e.preventDefault();\n el.blur();\n }\n });\n\n // \u56FE\u7247\u6362\u56FE\uFF1A\u70B9\u51FB\u56FA\u5B9A\u533A\u57DF\u76F4\u63A5\u5524\u8D77\u6587\u4EF6\u9009\u62E9\u5668\u4E0A\u4F20\n document.addEventListener('click', (e) => {\n const target = e.target.closest('[data-lp-editable-image=\"true\"]');\n if (!target) return;\n const slideIdx = Number(target.getAttribute('data-lp-slide-idx'));\n const prop = target.getAttribute('data-lp-prop') || 'image';\n if (Number.isNaN(slideIdx)) return;\n handleImageUpload(target, slideIdx, prop);\n });\n\n const exportToggle = document.getElementById('lp-export-toggle');\n const exportMenu = document.getElementById('lp-export-menu');\n const exportWrapper = document.getElementById('lp-editor-export');\n if (exportToggle && exportMenu) {\n exportToggle.addEventListener('click', (e) => {\n e.stopPropagation();\n const hidden = exportMenu.hasAttribute('hidden');\n if (hidden) {\n exportMenu.removeAttribute('hidden');\n exportToggle.setAttribute('aria-expanded', 'true');\n } else {\n exportMenu.setAttribute('hidden', '');\n exportToggle.setAttribute('aria-expanded', 'false');\n }\n });\n document.addEventListener('click', (e) => {\n if (!exportWrapper || exportWrapper.contains(e.target)) return;\n exportMenu.setAttribute('hidden', '');\n exportToggle.setAttribute('aria-expanded', 'false');\n });\n }\n\n const exportHtmlBtn = document.getElementById('lp-export-html');\n if (exportHtmlBtn) {\n exportHtmlBtn.addEventListener('click', () => {\n const html = '<!DOCTYPE html>\\n' + document.documentElement.outerHTML;\n const blob = new Blob([html], { type: 'text/html' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'presentation.html';\n a.click();\n URL.revokeObjectURL(url);\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n });\n }\n\n const exportPptxBtn = document.getElementById('lp-export-pptx');\n if (exportPptxBtn) {\n const pptxLabel = exportPptxBtn.querySelector('.lp-editor-export-label');\n exportPptxBtn.addEventListener('click', async () => {\n if (window.location.protocol === 'file:') {\n alert('\u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\u65E0\u6CD5\u76F4\u63A5\u5BFC\u51FA PPTX\u3002\\n\\n\u8BF7\u901A\u8FC7\u672C\u5730\u670D\u52A1\u5668\u8BBF\u95EE\u540E\u5BFC\u51FA\uFF1A\\n pnpm dev\\n\u6216\u8FD0\u884C\uFF1A\\n node scripts/export-pptx.mjs <goal.json> out.pptx');\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n return;\n }\n if (pptxLabel) pptxLabel.textContent = '\u5BFC\u51FA\u4E2D...';\n exportPptxBtn.disabled = true;\n try {\n const res = await fetch('/api/export/pptx', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(goal),\n });\n if (!res.ok) throw new Error('\u5BFC\u51FA\u5931\u8D25: ' + res.status);\n const blob = await res.blob();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'presentation.pptx';\n a.click();\n URL.revokeObjectURL(url);\n } catch (err) {\n alert(err instanceof Error ? err.message : String(err));\n } finally {\n if (pptxLabel) pptxLabel.textContent = 'PPTX';\n exportPptxBtn.disabled = false;\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n }\n });\n }\n\n const exportPdfBtn = document.getElementById('lp-export-pdf');\n if (exportPdfBtn) {\n const pdfLabel = exportPdfBtn.querySelector('.lp-editor-export-label');\n exportPdfBtn.addEventListener('click', async () => {\n if (window.location.protocol === 'file:') {\n alert('\u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\u65E0\u6CD5\u76F4\u63A5\u5BFC\u51FA PDF\u3002\\n\\n\u8BF7\u901A\u8FC7\u672C\u5730\u670D\u52A1\u5668\u8BBF\u95EE\u540E\u5BFC\u51FA\uFF1A\\n pnpm dev\\n\u6216\u8FD0\u884C\uFF1A\\n node scripts/export-pdf.mjs <goal.json> out.pdf');\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n return;\n }\n if (pdfLabel) pdfLabel.textContent = '\u5BFC\u51FA\u4E2D...';\n exportPdfBtn.disabled = true;\n try {\n const res = await fetch('/api/export/pdf', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(goal),\n });\n if (!res.ok) throw new Error('\u5BFC\u51FA\u5931\u8D25: ' + res.status);\n const blob = await res.blob();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = 'presentation.pdf';\n a.click();\n URL.revokeObjectURL(url);\n } catch (err) {\n alert(err instanceof Error ? err.message : String(err));\n } finally {\n if (pdfLabel) pdfLabel.textContent = 'PDF';\n exportPdfBtn.disabled = false;\n if (exportMenu) exportMenu.setAttribute('hidden', '');\n }\n });\n }\n\n // \u64A4\u9500 / \u91CD\u505A\n const undoBtn = document.getElementById('lp-undo');\n const redoBtn = document.getElementById('lp-redo');\n\n function updateUndoRedoButtons() {\n if (undoBtn) undoBtn.disabled = historyIndex <= 0;\n if (redoBtn) redoBtn.disabled = historyIndex >= history.length - 1;\n }\n\n if (undoBtn) undoBtn.addEventListener('click', undo);\n if (redoBtn) redoBtn.addEventListener('click', redo);\n\n const saveDeckBtn = document.getElementById('lp-save-deck');\n if (saveDeckBtn) {\n saveDeckBtn.addEventListener('click', () => {\n try {\n localStorage.setItem(getStorageKey(), JSON.stringify(goal));\n const original = saveDeckBtn.textContent;\n saveDeckBtn.textContent = '\u5DF2\u4FDD\u5B58';\n saveDeckBtn.disabled = true;\n setTimeout(() => {\n saveDeckBtn.textContent = original;\n saveDeckBtn.disabled = false;\n }, 1200);\n } catch (err) {\n console.warn('\u624B\u52A8\u4FDD\u5B58\u5931\u8D25', err);\n alert('\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6D4F\u89C8\u5668\u5B58\u50A8\u6743\u9650\u3002');\n }\n });\n }\n\n function syncAppearanceFromGoal() {\n const t = goal.theme || 'theme01';\n if (t === 'theme04' || t === 'theme05' || t === 'theme06' || t === 'theme07' || t === 'theme08' || t === 'theme09' || t === 'theme10') {\n const scheme = goal.colorScheme || (t === 'theme05' ? 'coral' : t === 'theme06' ? 'volt' : t === 'theme07' ? 'cold-white' : t === 'theme08' ? 'obsidian-gold' : t === 'theme09' ? 'ink-editorial' : t === 'theme10' ? 'gold-index' : 'green');\n const rawAppearance = goal.appearance || (t === 'theme07' ? 'light' : t === 'theme08' || t === 'theme09' || t === 'theme10' ? 'primary' : 'dark');\n /* theme08 / theme09 \u5F52\u4E00\u5316\uFF1Alight\u2192muted / dark\u2192primary */\n const appearance = (t === 'theme08' || t === 'theme09') ? ((rawAppearance === 'light' || rawAppearance === 'muted') ? 'muted' : 'primary') : rawAppearance;\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.setAttribute('data-appearance', appearance);\n } else if (t === 'theme03') {\n const scheme = goal.colorScheme || 'scheme-a';\n const appearance = goal.appearance || 'dark';\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.setAttribute('data-appearance', appearance);\n } else if (t === 'theme11') {\n // theme11 \u89C6\u89C9\u7531\u6BCF\u9875 mood class \u9A71\u52A8\uFF0C\u6839\u5C5E\u6027\u4EC5\u4F5C\u5360\u4F4D\u907F\u514D\u843D\u5165 else \u88AB\u8BEF\u5220\n const scheme = goal.colorScheme || 'theme11';\n const appearance = goal.appearance || 'light';\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.setAttribute('data-appearance', appearance);\n } else {\n const scheme = goal.colorScheme || (t === 'theme02' ? 'scheme-a' : 'light');\n document.documentElement.setAttribute('data-theme', scheme);\n document.documentElement.removeAttribute('data-appearance');\n }\n\n document.querySelectorAll('.lp-appearance-btn').forEach((btn) => {\n const appearanceValue = btn.getAttribute('data-appearance');\n const themeValue = btn.getAttribute('data-theme');\n if (!appearanceValue && !themeValue) return;\n\n let active = false;\n if (t === 'theme04' || t === 'theme05' || t === 'theme06' || t === 'theme07' || t === 'theme08' || t === 'theme09' || t === 'theme10') {\n if (themeValue) {\n active = themeValue === (goal.colorScheme || (t === 'theme05' ? 'coral' : t === 'theme06' ? 'volt' : t === 'theme07' ? 'cold-white' : t === 'theme08' ? 'obsidian-gold' : t === 'theme09' ? 'ink-editorial' : t === 'theme10' ? 'gold-index' : 'green'));\n } else if (appearanceValue) {\n const rawGoal = goal.appearance || (t === 'theme07' ? 'light' : t === 'theme08' || t === 'theme09' || t === 'theme10' ? 'primary' : 'dark');\n const normGoal = (t === 'theme08' || t === 'theme09') ? ((rawGoal === 'light' || rawGoal === 'muted') ? 'muted' : 'primary') : rawGoal;\n const normBtn = (t === 'theme08' || t === 'theme09') ? ((appearanceValue === 'light' || appearanceValue === 'muted') ? 'muted' : 'primary') : appearanceValue;\n active = normBtn === normGoal;\n }\n } else if (t === 'theme03') {\n active = appearanceValue === (goal.appearance || 'dark');\n } else if (t === 'theme11') {\n active = appearanceValue === (goal.appearance || 'light');\n } else {\n active = appearanceValue === (goal.colorScheme || (t === 'theme02' ? 'scheme-a' : 'light'));\n }\n btn.classList.toggle('lp-appearance-active', active);\n btn.setAttribute('aria-pressed', String(active));\n });\n }\n\n function applyAppearanceClientSide(newMode) {\n const t = goal.theme || 'theme01';\n\n if (t === 'theme04' || t === 'theme05' || t === 'theme06' || t === 'theme07' || t === 'theme08' || t === 'theme09' || t === 'theme10') {\n /* theme08 / theme09 \u5F52\u4E00\u5316\uFF1Alight\u2192muted / dark\u2192primary */\n const normMode = (t === 'theme08' || t === 'theme09') ? ((newMode === 'light' || newMode === 'muted') ? 'muted' : 'primary') : newMode;\n const curNorm = (t === 'theme08' || t === 'theme09') ? ((goal.appearance === 'light' || goal.appearance === 'muted') ? 'muted' : 'primary') : goal.appearance;\n if (normMode === curNorm) return;\n recordHistory();\n goal.appearance = newMode;\n autoSave();\n document.documentElement.setAttribute('data-appearance', newMode);\n } else if (t === 'theme03') {\n if (newMode === goal.appearance) return;\n recordHistory();\n goal.appearance = newMode;\n autoSave();\n document.documentElement.setAttribute('data-appearance', newMode);\n } else if (t === 'theme11') {\n // theme11 \u7531\u6BCF\u9875 mood class \u9A71\u52A8\uFF0C\u9876\u90E8\u5916\u89C2\u6309\u94AE\u4EC5\u540C\u6B65\u6839\u5C5E\u6027\u4E0E goal.appearance\n if (newMode === goal.appearance) return;\n recordHistory();\n goal.appearance = newMode;\n autoSave();\n document.documentElement.setAttribute('data-appearance', newMode);\n } else {\n if (newMode === goal.colorScheme) return;\n recordHistory();\n goal.colorScheme = newMode;\n autoSave();\n document.documentElement.setAttribute('data-theme', newMode);\n }\n\n syncAppearanceFromGoal();\n\n // \u5207\u6362\u5916\u89C2\u540E CSS \u53D8\u91CF\u503C\u53D8\u5316\uFF0C\u91CA\u653E\u5168\u90E8\u5B9E\u4F8B\u5E76\u91CD\u65B0\u521D\u59CB\u5316\u5F53\u524D active slide \u7684 ECharts\u3002\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n initActiveSlideECharts();\n }\n\n function applyToneClientSide(newTone) {\n const t = goal.theme || 'theme01';\n if (t !== 'theme04' && t !== 'theme05' && t !== 'theme06') return;\n if (newTone === goal.colorScheme) return;\n recordHistory();\n goal.colorScheme = newTone;\n autoSave();\n document.documentElement.setAttribute('data-theme', newTone);\n syncAppearanceFromGoal();\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n initActiveSlideECharts();\n }\n\n // \u5916\u89C2\u6A21\u5F0F\u5207\u6362\uFF08\u6D45\u8272 / \u6DF1\u8272 / \u4E3B\u9898 04 \u8272\u8C03\uFF09\n document.querySelectorAll('.lp-appearance-btn').forEach((btn) => {\n btn.addEventListener('click', () => {\n const appearance = btn.getAttribute('data-appearance');\n const tone = btn.getAttribute('data-theme');\n if (tone) applyToneClientSide(tone);\n if (appearance) applyAppearanceClientSide(appearance);\n });\n });\n\n // \u6DFB\u52A0\u5E7B\u706F\u7247\n function createDefaultSlide(layoutId, role) {\n const slide = { layout: layoutId, role, props: { title: '\u65B0\u5E7B\u706F\u7247' } };\n switch (role) {\n case 'cover': {\n if (layoutId === 'theme02_cover_v1') {\n slide.props = {\n kicker: '\u9713\u8679\u4E3B\u9898\u6F14\u793A',\n title: 'Neon Pitch',\n subtitle: '\u6DF1\u8272\u80CC\u666F + \u53CC\u914D\u8272\u65B9\u6848 + \u79D1\u6280\u611F\u7248\u5F0F',\n date: '2026.07'\n };\n } else {\n slide.props = { title: '\u5C01\u9762\u6807\u9898', subtitle: '\u526F\u6807\u9898' };\n }\n break;\n }\n case 'closing': {\n if (layoutId === 'theme01_conclusion_v1') {\n slide.props = {\n title: '\u6838\u5FC3\u7ED3\u8BBA',\n subtitle: 'CONCLUSION',\n points: [\n { item: 'AI \u6F14\u793A\u751F\u6210\u5DF2\u8FC8\u5165\u5B9E\u7528\u9636\u6BB5' },\n { item: '\u4E3B\u9898\u7CFB\u7EDF\u4E0E\u5E03\u5C40\u5F15\u64CE\u662F\u62A4\u57CE\u6CB3' },\n { item: '\u5F00\u653E\u751F\u6001\u5C06\u52A0\u901F\u884C\u4E1A\u843D\u5730' }\n ]\n };\n } else if (layoutId === 'theme02_closing_v1') {\n slide.props = {\n kicker: '\u611F\u8C22\u89C2\u770B',\n title: \"Let's Build Neon\",\n subtitle: '\u7528\u6DF1\u8272\u9713\u8679\u98CE\u683C\u70B9\u4EAE\u4E0B\u4E00\u573A\u6F14\u793A',\n cta: '\u5F00\u59CB\u4F7F\u7528',\n contact: '\u67E0\u6AAC\u56E2\u961F',\n email: 'hello@lemonppt.dev',\n link: 'lemonppt.dev'\n };\n } else {\n slide.props = {\n kicker: '\u611F\u8C22\u89C2\u770B',\n title: '\u671F\u5F85\u4E0E\u60A8\u5408\u4F5C',\n subtitle: '\u6709\u4EFB\u4F55\u95EE\u9898\uFF0C\u6B22\u8FCE\u968F\u65F6\u4EA4\u6D41',\n cta: '\u8054\u7CFB\u6211\u4EEC',\n contact: '\u67E0\u6AAC\u56E2\u961F',\n email: 'hello@lemonppt.dev',\n link: 'lemonppt.dev'\n };\n }\n break;\n }\n case 'quote': {\n if (layoutId === 'theme02_quote_v1') {\n slide.props = {\n quote: '\u8BBE\u8BA1\u7684\u672C\u8D28\u662F\u89E3\u51B3\u95EE\u9898\uFF0C\u800C\u597D\u7684\u8BBE\u8BA1\u662F\u8BA9\u590D\u6742\u53D8\u5F97\u7B80\u5355\u3002',\n author: '\u67E0\u6AAC\u56E2\u961F',\n role: '\u4EA7\u54C1\u7406\u5FF5'\n };\n } else {\n slide.props = {\n quote: '\u7B80\u6D01\u662F\u7EC8\u6781\u7684\u590D\u6742\u3002',\n author: '\u83B1\u6602\u7EB3\u591A\u00B7\u8FBE\u00B7\u82AC\u5947'\n };\n }\n break;\n }\n case 'content': {\n if (layoutId === 'theme01_risk_v1') {\n slide.props = {\n kicker: '\u98CE\u9669\u7814\u5224',\n title: '\u98CE\u9669\u6807\u9898',\n items: [\n { risk: '\u98CE\u9669 1', impact: '\u9AD8', response: '\u5E94\u5BF9\u7B56\u7565' },\n { risk: '\u98CE\u9669 2', impact: '\u4E2D', response: '\u5E94\u5BF9\u7B56\u7565' }\n ]\n };\n } else if (layoutId === 'theme01_outlook_v1') {\n slide.props = {\n kicker: '\u6295\u8D44\u5C55\u671B',\n title: '\u5C55\u671B\u6807\u9898',\n items: [\n { title: '\u8D8B\u52BF\u4E00', trend: '\u8D8B\u52BF\u5224\u65AD', action: '\u884C\u52A8\u5EFA\u8BAE' },\n { title: '\u8D8B\u52BF\u4E8C', trend: '\u8D8B\u52BF\u5224\u65AD', action: '\u884C\u52A8\u5EFA\u8BAE' }\n ]\n };\n } else if (layoutId === 'theme01_region_v1') {\n slide.props = {\n kicker: '\u5730\u533A\u5206\u5E03',\n title: '\u5E02\u573A\u5206\u5E03',\n regions: [\n { name: '\u5730\u533A\u4E00', value: '35%', change: '+2%', note: '\u8BF4\u660E\u6587\u5B57' },\n { name: '\u5730\u533A\u4E8C', value: '25%', change: '+1%', note: '\u8BF4\u660E\u6587\u5B57' }\n ]\n };\n } else if (layoutId === 'theme01_case_study') {\n slide.props = {\n kicker: '\u5178\u578B\u6848\u4F8B',\n title: '\u67E0\u6AAC\u79D1\u6280',\n subtitle: 'FROM CHALLENGER TO FRONTRUNNER',\n intro: '\u67E0\u6AAC\u79D1\u6280\u6210\u7ACB\u4E8E 2022 \u5E74\uFF0C\u4E13\u6CE8 AI \u6F14\u793A\u6587\u7A3F\u751F\u6210\u3002\u51ED\u501F\u81EA\u7814\u5E03\u5C40\u5F15\u64CE\u4E0E\u4E3B\u9898\u7CFB\u7EDF\uFF0C\u5728 18 \u4E2A\u6708\u5185\u5B8C\u6210\u4ECE 0 \u5230 1 \u7684\u8DE8\u8D8A\u3002',\n rounds: [\n { date: '2022\u00B706', round: '\u5929\u4F7F\u8F6E', valuation: '\u4F30\u503C \u00A51 \u4EBF', amount: '\u00A51,000\u4E07' },\n { date: '2023\u00B703', round: 'Pre-A', valuation: '\u4F30\u503C \u00A55 \u4EBF', amount: '\u00A55,000\u4E07' },\n { date: '2024\u00B701', round: 'A \u8F6E', valuation: '\u4F30\u503C \u00A520 \u4EBF', amount: '\u00A51.5\u4EBF' }\n ],\n quote: 'AI \u4E0D\u662F\u8981\u66FF\u4EE3\u4EBA\u505A PPT\uFF0C\u800C\u662F\u628A\u521B\u610F\u4ECE\u683C\u5F0F\u4E2D\u89E3\u653E\u51FA\u6765\u3002',\n quoteAuthor: '\u674E\u96F7\uFF0C\u8054\u5408\u521B\u59CB\u4EBA\u517C CEO',\n footnote: '\u6570\u636E\u6765\u6E90\uFF1A\u516C\u53F8\u516C\u5F00\u62AB\u9732\u4E0E\u5A92\u4F53\u6574\u7406\u3002'\n };\n } else if (layoutId === 'theme01_spotlight_grid') {\n slide.props = {\n kicker: '\u4E3B\u9898\u805A\u7126',\n title: '\u94B1\uFF0C\u6700\u7EC8\u7838\u5411\u4E86\u8FD9\u4E09\u4EF6\u4E8B',\n subtitle: '\u8D44\u91D1\u96C6\u4E2D\u6D41\u5411\u6A21\u578B\u80FD\u529B\u3001\u5E94\u7528\u573A\u666F\u4E0E\u57FA\u7840\u8BBE\u65BD',\n columns: [\n { tag: '\u6A21\u578B\u80FD\u529B', title: '\u901A\u7528\u5927\u6A21\u578B', description: '\u5934\u90E8\u5382\u5546\u6301\u7EED\u52A0\u7801\u57FA\u5EA7\u6A21\u578B\uFF0C\u89C4\u6A21\u6548\u5E94\u663E\u73B0\u3002' },\n { tag: '\u5E94\u7528\u573A\u666F', title: '\u5782\u76F4\u5E94\u7528', description: '\u529E\u516C\u3001\u8BBE\u8BA1\u3001\u7F16\u7A0B\u7B49\u573A\u666F\u7387\u5148\u8DD1\u51FA\u5546\u4E1A\u5316\u8DEF\u5F84\u3002' },\n { tag: '\u57FA\u7840\u8BBE\u65BD', title: 'AI \u57FA\u7840\u8BBE\u65BD', description: '\u7B97\u529B\u3001\u6570\u636E\u4E0E\u5DE5\u5177\u94FE\u6210\u4E3A\u652F\u6491\u4E0A\u5C42\u521B\u65B0\u7684\u5173\u952E\u5E95\u5EA7\u3002' }\n ]\n };\n } else if (layoutId === 'theme02_chapter_v1') {\n slide.props = {\n kicker: '\u7AE0\u8282',\n number: '01',\n title: '\u6838\u5FC3\u53D1\u73B0',\n subtitle: '\u4ECE\u6570\u636E\u5230\u6D1E\u5BDF\u7684\u5173\u952E\u8F6C\u6298'\n };\n } else if (layoutId === 'theme02_content_v1') {\n slide.props = {\n kicker: '\u8981\u70B9\u603B\u7ED3',\n title: '\u5185\u5BB9\u6807\u9898',\n subtitle: '\u7528\u9713\u8679 bullet \u5217\u8868\u7A81\u51FA\u5173\u952E\u4FE1\u606F',\n bullets: [\n '\u5168\u6808 AI \u6F14\u793A\u751F\u6210\uFF0C\u4ECE\u5927\u7EB2\u5230\u6210\u7A3F\u4E00\u952E\u5B8C\u6210',\n '\u53CC\u914D\u8272\u65B9\u6848\u81EA\u7531\u5207\u6362\uFF0C\u9002\u914D\u4E0D\u540C\u54C1\u724C\u8C03\u6027',\n 'SVG \u56FE\u8868 + \u6D1E\u5BDF\u9762\u677F\uFF0C\u6570\u636E\u8868\u8FBE\u66F4\u805A\u7126'\n ]\n };\n } else {\n slide.props = { title: '\u5185\u5BB9\u6807\u9898', points: ['\u65B0\u8981\u70B9'] };\n }\n break;\n }\n case 'chart': {\n if (layoutId === 'theme01_chart_treemap') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u77E9\u5F62\u6811\u56FE',\n unit: '',\n data: [\n { name: '\u5206\u7C7B A', value: 400, children: [\n { name: 'A-1', value: 200 },\n { name: 'A-2', value: 150 },\n { name: 'A-3', value: 50 }\n ]},\n { name: '\u5206\u7C7B B', value: 300, children: [\n { name: 'B-1', value: 180 },\n { name: 'B-2', value: 120 }\n ]}\n ]\n };\n } else if (layoutId === 'theme01_chart_sankey') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u6851\u57FA\u56FE',\n data: [\n { source: '\u8BBF\u95EE', target: '\u70B9\u51FB', value: 5000 },\n { source: '\u70B9\u51FB', target: '\u54A8\u8BE2', value: 3500 },\n { source: '\u54A8\u8BE2', target: '\u8BA2\u5355', value: 2200 },\n { source: '\u8BBF\u95EE', target: '\u8DF3\u51FA', value: 1500 },\n { source: '\u70B9\u51FB', target: '\u79BB\u5F00', value: 1000 },\n { source: '\u54A8\u8BE2', target: '\u6D41\u5931', value: 1300 }\n ]\n };\n } else if (layoutId === 'theme01_chart_sunburst') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u65ED\u65E5\u56FE',\n data: [\n { name: '\u7EBF\u4E0A', value: 600, children: [\n { name: '\u641C\u7D22', value: 300 },\n { name: '\u793E\u4EA4', value: 200 },\n { name: '\u76F4\u8BBF', value: 100 }\n ]},\n { name: '\u7EBF\u4E0B', value: 400, children: [\n { name: '\u95E8\u5E97', value: 250 },\n { name: '\u4EE3\u7406', value: 150 }\n ]}\n ]\n };\n } else if (layoutId === 'theme01_chart_gauge') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u4EEA\u8868\u76D8',\n value: 78,\n min: 0,\n max: 100,\n unit: '%'\n };\n } else if (layoutId === 'theme01_chart_heatmap') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u70ED\u529B\u56FE',\n xAxis: ['A', 'B', 'C', 'D'],\n yAxis: ['W1', 'W2', 'W3'],\n data: [\n ['A', 'W1', 12], ['B', 'W1', 45], ['C', 'W1', 78], ['D', 'W1', 23],\n ['A', 'W2', 67], ['B', 'W2', 89], ['C', 'W2', 34], ['D', 'W2', 56],\n ['A', 'W3', 91], ['B', 'W3', 15], ['C', 'W3', 66], ['D', 'W3', 40]\n ]\n };\n } else if (layoutId === 'theme01_chart_funnel') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u6F0F\u6597\u56FE',\n data: [\n { name: '\u8BBF\u95EE', value: 10000 },\n { name: '\u610F\u5411', value: 6500 },\n { name: '\u8BE2\u4EF7', value: 4200 },\n { name: '\u6210\u4EA4', value: 2800 }\n ]\n };\n } else if (layoutId === 'theme01_chart_radar') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u96F7\u8FBE\u56FE',\n indicators: [\n { name: '\u6027\u80FD', max: 100 },\n { name: '\u7A33\u5B9A\u6027', max: 100 },\n { name: '\u6613\u7528\u6027', max: 100 },\n { name: '\u6269\u5C55\u6027', max: 100 },\n { name: '\u5B89\u5168\u6027', max: 100 }\n ],\n data: [\n { name: '\u5F53\u524D', value: [85, 90, 78, 88, 82] },\n { name: '\u76EE\u6807', value: [95, 95, 90, 92, 95] }\n ]\n };\n } else if (layoutId === 'theme01_chart_graph') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u5173\u7CFB\u56FE',\n categories: [\n { name: '\u6838\u5FC3' },\n { name: '\u4EA7\u54C1' },\n { name: '\u6280\u672F' }\n ],\n nodes: [\n { name: '\u5E73\u53F0', value: 80, category: 0 },\n { name: '\u7528\u6237\u7AEF', value: 50, category: 1 },\n { name: '\u7BA1\u7406\u7AEF', value: 45, category: 1 },\n { name: '\u6570\u636E\u4E2D\u53F0', value: 60, category: 2 },\n { name: 'AI \u5F15\u64CE', value: 70, category: 2 },\n { name: '\u6E32\u67D3\u5668', value: 55, category: 2 },\n { name: '\u6A21\u677F\u5E93', value: 40, category: 1 }\n ],\n links: [\n { source: '\u5E73\u53F0', target: '\u7528\u6237\u7AEF', value: 5 },\n { source: '\u5E73\u53F0', target: '\u7BA1\u7406\u7AEF', value: 5 },\n { source: '\u5E73\u53F0', target: '\u6570\u636E\u4E2D\u53F0', value: 8 },\n { source: '\u6570\u636E\u4E2D\u53F0', target: 'AI \u5F15\u64CE', value: 6 },\n { source: 'AI \u5F15\u64CE', target: '\u6E32\u67D3\u5668', value: 4 },\n { source: '\u7BA1\u7406\u7AEF', target: '\u6A21\u677F\u5E93', value: 3 },\n { source: '\u7528\u6237\u7AEF', target: '\u6E32\u67D3\u5668', value: 4 }\n ]\n };\n } else if (layoutId === 'theme01_chart_bar3d') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '3D \u67F1\u72B6\u56FE',\n labels: ['Q1', 'Q2', 'Q3', 'Q4'],\n data: [120, 200, 150, 80]\n };\n } else if (layoutId === 'theme01_chart_wordcloud') {\n slide.props = {\n title: '\u56FE\u8868\u6807\u9898',\n kicker: '\u8BCD\u4E91',\n words: [\n { name: 'AI', value: 100 },\n { name: 'PPT', value: 90 },\n { name: '\u6570\u636E\u53EF\u89C6\u5316', value: 85 },\n { name: '\u81EA\u52A8\u5316', value: 80 },\n { name: '\u5927\u5C4F', value: 75 },\n { name: '\u4E3B\u9898', value: 70 },\n { name: '\u6A21\u677F', value: 65 },\n { name: '\u667A\u80FD\u751F\u6210', value: 60 }\n ]\n };\n } else if (layoutId === 'theme01_chart_donut') {\n slide.props = {\n title: '\u8D5B\u9053\u5206\u5E03',\n kicker: '\u73AF\u5F62\u56FE',\n subtitle: '\u878D\u8D44\u989D\u5360\u6BD4',\n total: { value: '970 \u4EBF\u7F8E\u5143', label: '\u5168\u5E74\u5408\u8BA1' },\n segments: [\n { label: '\u901A\u7528\u5927\u6A21\u578B', labelEn: 'General LLM', value: '420', percent: '43.3%', color: 'var(--lp-blue)' },\n { label: '\u5782\u76F4\u5E94\u7528', labelEn: 'Vertical Apps', value: '245', percent: '25.3%', color: 'var(--lp-green)' },\n { label: 'AI \u57FA\u7840\u8BBE\u65BD', labelEn: 'AI Infra', value: '198', percent: '20.4%', color: 'var(--lp-amber)' },\n { label: 'AI \u82AF\u7247', labelEn: 'AI Chips', value: '97', percent: '10.0%', color: 'var(--lp-red)' },\n { label: '\u5176\u4ED6', labelEn: 'Others', value: '10', percent: '1.0%', color: 'var(--lp-violet)' }\n ]\n };\n } else if (layoutId === 'theme02_chart_funnel') {\n slide.props = {\n title: '\u8D44\u91D1\u96C6\u4E2D\u5EA6',\n kicker: '\u9713\u8679\u6F0F\u6597',\n subtitle: '\u8D44\u672C\u5982\u4F55\u5411\u5934\u90E8\u6536\u655B',\n data: [\n { name: '\u5168\u5E02\u573A', value: 970 },\n { name: '\u5934\u90E8 10 \u5BB6', value: 232 },\n { name: 'Top 3', value: 181 },\n { name: '\u5355\u7B14\u6700\u5927', value: 66 }\n ],\n showInsight: true,\n insight: {\n value: '24%',\n label: '\u5934\u90E8 10 \u5BB6 = \u5168\u5E02\u573A\u8FD1 1/4',\n description: '\u5C11\u6570\u72EC\u89D2\u517D\u53CD\u590D\u83B7\u5F97\u5DE8\u989D\u8FFD\u52A0\u6295\u8D44\uFF0C\u5934\u90E8\u683C\u5C40\u5DF2\u7136\u786E\u7ACB\u3002'\n },\n footnote: '\u6570\u636E\u6765\u6E90\uFF1AlemonPPT \u5185\u90E8\u7EDF\u8BA1 \u00B7 2026'\n };\n } else if (layoutId === 'theme02_chart_donut') {\n slide.props = {\n title: '\u8D5B\u9053\u5206\u5E03',\n kicker: '\u9713\u8679\u73AF\u5F62',\n subtitle: '\u878D\u8D44\u989D\u5360\u6BD4',\n total: { value: '970 \u4EBF\u7F8E\u5143', label: '\u5168\u5E74\u5408\u8BA1' },\n segments: [\n { label: '\u901A\u7528\u5927\u6A21\u578B', labelEn: 'General LLM', value: '420', percent: '43.3%', color: 'var(--lp-accent)' },\n { label: '\u5782\u76F4\u5E94\u7528', labelEn: 'Vertical Apps', value: '245', percent: '25.3%', color: 'var(--lp-accent-cool)' },\n { label: 'AI \u57FA\u7840\u8BBE\u65BD', labelEn: 'AI Infra', value: '198', percent: '20.4%', color: 'var(--lp-accent-2)' },\n { label: 'AI \u82AF\u7247', labelEn: 'AI Chips', value: '97', percent: '10.0%', color: 'var(--lp-violet)' },\n { label: '\u5176\u4ED6', labelEn: 'Others', value: '10', percent: '1.0%', color: 'var(--lp-cyan)' }\n ],\n footnote: '\u6570\u636E\u6765\u6E90\uFF1AlemonPPT \u5185\u90E8\u7EDF\u8BA1 \u00B7 2026',\n showInsight: true,\n insight: {\n value: '43.3%',\n label: '\u901A\u7528\u5927\u6A21\u578B\u5360\u6BD4\u6700\u9AD8',\n description: '\u901A\u7528\u5927\u6A21\u578B\u4E0E\u5782\u76F4\u5E94\u7528\u5408\u8BA1\u8D21\u732E\u8FD1\u4E03\u6210\u878D\u8D44\u989D\uFF0C\u8D5B\u9053\u5934\u90E8\u6548\u5E94\u660E\u663E\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_heatmap') {\n slide.props = {\n title: '\u7528\u6237\u6D3B\u8DC3\u5EA6\u70ED\u529B',\n kicker: '\u9713\u8679\u70ED\u529B',\n xAxis: ['\u5468\u4E00', '\u5468\u4E8C', '\u5468\u4E09', '\u5468\u56DB', '\u5468\u4E94'],\n yAxis: ['00:00', '06:00', '12:00', '18:00'],\n data: [\n ['\u5468\u4E00', '00:00', 120], ['\u5468\u4E00', '06:00', 80], ['\u5468\u4E00', '12:00', 320], ['\u5468\u4E00', '18:00', 280],\n ['\u5468\u4E8C', '00:00', 90], ['\u5468\u4E8C', '06:00', 60], ['\u5468\u4E8C', '12:00', 350], ['\u5468\u4E8C', '18:00', 300],\n ['\u5468\u4E09', '00:00', 100], ['\u5468\u4E09', '06:00', 70], ['\u5468\u4E09', '12:00', 380], ['\u5468\u4E09', '18:00', 340],\n ['\u5468\u56DB', '00:00', 110], ['\u5468\u56DB', '06:00', 75], ['\u5468\u56DB', '12:00', 360], ['\u5468\u56DB', '18:00', 310],\n ['\u5468\u4E94', '00:00', 130], ['\u5468\u4E94', '06:00', 85], ['\u5468\u4E94', '12:00', 400], ['\u5468\u4E94', '18:00', 360]\n ],\n showInsight: true,\n insight: {\n value: '400',\n label: '\u5468\u4E94 12:00 \u6D3B\u8DC3\u5EA6\u5CF0\u503C',\n description: '\u5DE5\u4F5C\u65E5\u5348\u95F4\u662F\u7528\u6237\u6D3B\u8DC3\u9AD8\u5CF0\uFF0C\u9002\u5408\u63A8\u9001\u5173\u952E\u5185\u5BB9\u4E0E\u8FD0\u8425\u6D3B\u52A8\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_radar') {\n slide.props = {\n title: '\u80FD\u529B\u96F7\u8FBE\u5BF9\u6BD4',\n kicker: '\u9713\u8679\u96F7\u8FBE',\n indicators: [\n { name: '\u6027\u80FD', max: 100 },\n { name: '\u7A33\u5B9A\u6027', max: 100 },\n { name: '\u6613\u7528\u6027', max: 100 },\n { name: '\u6269\u5C55\u6027', max: 100 },\n { name: '\u5B89\u5168\u6027', max: 100 }\n ],\n data: [\n { name: '\u5F53\u524D', value: [85, 90, 78, 88, 82] },\n { name: '\u76EE\u6807', value: [95, 95, 90, 92, 95] }\n ],\n showInsight: true,\n insight: {\n value: '84.6',\n label: '\u5F53\u524D\u7EFC\u5408\u80FD\u529B\u5747\u5206',\n description: '\u6027\u80FD\u4E0E\u7A33\u5B9A\u6027\u8868\u73B0\u7A81\u51FA\uFF0C\u6613\u7528\u6027\u4ECD\u6709\u63D0\u5347\u7A7A\u95F4\uFF0C\u5EFA\u8BAE\u4F18\u5148\u4F18\u5316\u4EA4\u4E92\u6D41\u7A0B\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_gauge') {\n slide.props = {\n title: '\u5E74\u5EA6\u76EE\u6807\u8FBE\u6210\u7387',\n kicker: '\u9713\u8679\u4EEA\u8868',\n value: 78,\n min: 0,\n max: 100,\n unit: '%',\n showInsight: true,\n insight: {\n value: '+22%',\n label: '\u8F83\u4E0A\u6708\u63D0\u5347',\n description: '\u5E74\u5EA6\u76EE\u6807\u5B8C\u6210\u7387\u5DF2\u63A5\u8FD1 80%\uFF0C\u6309\u5F53\u524D\u589E\u901F\u9884\u8BA1\u4E0B\u5B63\u5EA6\u53EF\u8D85\u989D\u8FBE\u6210\u3002'\n }\n };\n } else if (layoutId === 'theme02_chart_v1') {\n slide.props = {\n title: '\u5B63\u5EA6\u8425\u6536\u589E\u957F',\n kicker: '\u9713\u8679\u56FE\u8868',\n subtitle: '\u5168\u5E74\u56DB\u4E2A\u5B63\u5EA6\u6301\u7EED\u4E0A\u626C',\n type: 'bar',\n labels: ['Q1', 'Q2', 'Q3', 'Q4'],\n data: [1800, 2450, 3200, 4150],\n unit: '\u4E07\u5143',\n showInsight: true,\n insight: {\n headline: '11,600',\n subheadline: 'FULL-YEAR TOTAL',\n items: [\n { label: 'Q4 \u73AF\u6BD4', value: '+30%' },\n { label: '\u5E73\u5747\u5B63\u5EA6', value: '2,900' }\n ],\n badge: { text: 'Q4 \u589E\u957F\u6700\u5FEB', tone: 'accent' }\n }\n };\n } else {\n slide.props = { title: '\u56FE\u8868\u6807\u9898', labels: ['Q1', 'Q2', 'Q3', 'Q4'], data: [20, 45, 70, 95] };\n }\n break;\n }\n case 'bento': {\n if (layoutId === 'theme02_bento_v1') {\n slide.props = {\n kicker: 'Bento',\n title: '\u6A21\u5757\u5316\u6570\u636E\u5C55\u793A',\n subtitle: '\u7528\u4E0D\u89C4\u5219\u7F51\u683C\u7A81\u51FA\u5173\u952E\u6570\u5B57',\n items: [\n { label: '\u5E74\u5EA6\u8425\u6536', value: '1.2', unit: '\u4EBF\u5143', size: 'large' },\n { label: '\u7528\u6237\u589E\u957F', value: '+320', unit: '%', size: 'medium' },\n { label: 'NPS', value: '72', unit: '\u5206', size: 'small' },\n { label: '\u4ED8\u8D39\u5BA2\u6237', value: '1,200', unit: '+', size: 'small' },\n { label: '\u6A21\u677F\u6570', value: '150', unit: '+', size: 'small' }\n ]\n };\n } else {\n slide.props = {\n kicker: 'Bento',\n title: '\u6A21\u5757\u5316\u5C55\u793A',\n items: [\n { title: '\u6838\u5FC3\u6307\u6807', description: '\u5173\u952E\u6570\u636E\u4E00\u89C8', span: 'large' },\n { title: '\u7528\u6237\u589E\u957F', description: '\u6708\u6D3B\u8DC3\u7528\u6237\u6301\u7EED\u63D0\u5347', span: 'medium' },\n { title: '\u4EA7\u54C1\u7279\u6027', description: '\u6838\u5FC3\u80FD\u529B', span: 'small' },\n { title: '\u6280\u672F\u67B6\u6784', description: '\u7A33\u5B9A\u53EF\u9760', span: 'small' }\n ]\n };\n }\n break;\n }\n case 'gallery': {\n if (layoutId === 'theme01_filmstrip_v1') {\n slide.props = {\n kicker: '\u5F71\u50CF\u957F\u5377',\n title: '\u6545\u4E8B\u7EBF',\n images: [\n { url: '', caption: '\u9636\u6BB5\u4E00' },\n { url: '', caption: '\u9636\u6BB5\u4E8C' },\n { url: '', caption: '\u9636\u6BB5\u4E09' },\n { url: '', caption: '\u9636\u6BB5\u56DB' }\n ]\n };\n } else if (layoutId === 'theme02_gallery_v1') {\n slide.props = {\n kicker: '\u56FE\u96C6',\n title: '\u7CBE\u9009\u56FE\u7247',\n subtitle: '\u7528\u9713\u8679\u8FB9\u6846\u7F51\u683C\u5C55\u793A\u4EA7\u54C1\u573A\u666F',\n images: [\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 1' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 2' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 3' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 4' }\n ]\n };\n } else {\n slide.props = {\n kicker: '\u56FE\u96C6',\n title: '\u7CBE\u9009\u56FE\u7247',\n images: [\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 1' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 2' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 3' },\n { url: '', caption: '\u56FE\u7247\u8BF4\u660E 4' }\n ]\n };\n }\n break;\n }\n case 'table': {\n if (layoutId === 'theme01_table_data') {\n slide.props = {\n kicker: '\u6570\u636E\u8868',\n title: '\u5BF9\u6BD4\u4E00\u89C8',\n columns: [\n { key: 'rank', label: '\u6392\u540D', align: 'left' },\n { key: 'company', label: '\u516C\u53F8', align: 'left' },\n { key: 'track', label: '\u8D5B\u9053', align: 'left' },\n { key: 'amount', label: '\u878D\u8D44\u989D', align: 'right' }\n ],\n rows: [\n { rank: '1', company: '\u67E0\u6AAC\u79D1\u6280', track: 'SaaS', amount: '\u00A55,000\u4E07' },\n { rank: '2', company: '\u9752\u4E91\u6570\u636E', track: '\u4E91\u539F\u751F', amount: '\u00A53,200\u4E07' },\n { rank: '3', company: '\u7EA2\u661F\u667A\u80FD', track: 'AI \u786C\u4EF6', amount: '\u00A52,800\u4E07' },\n { rank: '4', company: '\u84DD\u6D77\u8D44\u672C', track: '\u91D1\u878D\u79D1\u6280', amount: '\u00A51,500\u4E07' }\n ],\n highlightRow: 0\n };\n } else if (layoutId === 'theme02_table_v1') {\n slide.props = {\n kicker: '\u6570\u636E\u8868',\n title: '\u65B9\u6848\u5BF9\u6BD4\u4E00\u89C8',\n subtitle: '\u591A\u7EF4\u5EA6\u8BC4\u4F30\uFF0C\u8F85\u52A9\u51B3\u7B56',\n headers: ['\u7EF4\u5EA6', '\u4F20\u7EDF\u65B9\u5F0F', '\u67E0\u6AAC PPT'],\n rows: [\n ['\u5236\u4F5C\u65F6\u95F4', '2-3 \u5929', '10 \u5206\u949F'],\n ['\u98CE\u683C\u4E00\u81F4\u6027', '\u4F9D\u8D56\u8BBE\u8BA1\u5E08', '\u4E3B\u9898\u7CFB\u7EDF\u4FDD\u8BC1'],\n ['\u6570\u636E\u66F4\u65B0', '\u624B\u52A8\u4FEE\u6539', '\u8054\u52A8\u5237\u65B0'],\n ['\u534F\u4F5C\u6548\u7387', '\u7248\u672C\u6DF7\u4E71', '\u5B9E\u65F6\u534F\u4F5C'],\n ['\u5BFC\u51FA\u683C\u5F0F', '\u5355\u4E00', 'PPTX / PDF / \u56FE\u7247']\n ],\n highlightFirstColumn: true\n };\n } else {\n slide.props = {\n kicker: '\u6570\u636E\u8868',\n title: '\u5BF9\u6BD4\u4E00\u89C8',\n headers: ['\u7EF4\u5EA6', '\u65B9\u6848 A', '\u65B9\u6848 B', '\u65B9\u6848 C'],\n rows: [\n ['\u6210\u672C', '\u4F4E', '\u4E2D', '\u9AD8'],\n ['\u6027\u80FD', '\u4E2D', '\u9AD8', '\u9AD8'],\n ['\u6613\u7528\u6027', '\u9AD8', '\u4E2D', '\u4F4E'],\n ['\u6269\u5C55\u6027', '\u4E2D', '\u9AD8', '\u9AD8']\n ],\n highlightFirstColumn: true\n };\n }\n break;\n }\n case 'tags': {\n if (layoutId === 'theme02_tags_v1') {\n slide.props = {\n kicker: '\u6807\u7B7E\u5899',\n title: '\u5173\u952E\u8BCD\u4E91',\n subtitle: '\u4E00\u773C\u770B\u61C2\u7528\u6237\u6700\u5173\u5FC3\u7684\u80FD\u529B',\n tags: [\n { label: 'AI', value: 98, tone: 'accent' },\n { label: '\u81EA\u52A8\u5316', value: 85, tone: 'positive' },\n { label: '\u53EF\u89C6\u5316', value: 72 },\n { label: '\u534F\u4F5C', value: 60 },\n { label: '\u4E91\u7AEF', value: 55 },\n { label: '\u6548\u7387', value: 80, tone: 'positive' },\n { label: '\u8BBE\u8BA1', value: 45 },\n { label: '\u54C1\u724C', value: 38 },\n { label: 'PPTX', value: 65, tone: 'accent' },\n { label: '\u667A\u80FD\u751F\u6210', value: 70 }\n ]\n };\n } else {\n slide.props = {\n kicker: '\u6807\u7B7E\u5899',\n title: '\u5173\u952E\u8BCD\u4E91',\n tags: [\n { label: 'AI', value: 98, tone: 'accent' },\n { label: '\u81EA\u52A8\u5316', value: 85, tone: 'positive' },\n { label: '\u53EF\u89C6\u5316', value: 72 },\n { label: '\u534F\u4F5C', value: 60 },\n { label: '\u4E91\u7AEF', value: 55 },\n { label: '\u6548\u7387', value: 80, tone: 'positive' },\n { label: '\u8BBE\u8BA1', value: 45 },\n { label: '\u54C1\u724C', value: 38 },\n { label: 'PPTX', value: 65, tone: 'accent' },\n { label: '\u667A\u80FD\u751F\u6210', value: 70 }\n ]\n };\n }\n break;\n }\n case 'process': {\n if (layoutId === 'theme02_process_v1') {\n slide.props = {\n kicker: '\u5B9E\u65BD\u8DEF\u5F84',\n title: '\u56DB\u6B65\u4E0A\u7EBF',\n subtitle: '\u4ECE\u9700\u6C42\u5230\u4EA4\u4ED8\u7684\u6807\u51C6\u5316\u6D41\u7A0B',\n steps: [\n { title: '\u9700\u6C42\u68B3\u7406', description: '\u660E\u786E\u53D7\u4F17\u3001\u76EE\u6807\u4E0E\u6838\u5FC3\u4FE1\u606F\u3002' },\n { title: 'AI \u751F\u6210', description: '\u8F93\u5165\u4E3B\u9898\uFF0C\u81EA\u52A8\u751F\u6210\u5927\u7EB2\u4E0E\u7248\u5F0F\u3002' },\n { title: '\u89C6\u89C9\u8C03\u4F18', description: '\u5207\u6362\u914D\u8272\u3001\u66FF\u6362\u56FE\u7247\u3001\u5FAE\u8C03\u6587\u6848\u3002' },\n { title: '\u5BFC\u51FA\u4EA4\u4ED8', description: '\u4E00\u952E\u5BFC\u51FA PPTX / PDF / \u56FE\u7247\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u6D41\u7A0B\u6807\u9898', steps: ['\u65B0\u6B65\u9AA4'] };\n }\n break;\n }\n case 'feature': {\n if (layoutId === 'theme02_feature_v1') {\n slide.props = {\n kicker: '\u4EA7\u54C1\u7279\u6027',\n title: '\u4E3A\u4EC0\u4E48\u9009\u62E9\u67E0\u6AAC PPT',\n subtitle: '\u4ECE\u751F\u6210\u5230\u4EA4\u4ED8\uFF0C\u6BCF\u4E2A\u73AF\u8282\u90FD\u66F4\u9AD8\u6548',\n features: [\n { title: 'AI \u4E00\u952E\u751F\u6210', description: '\u57FA\u4E8E\u5927\u6A21\u578B\u81EA\u52A8\u7406\u89E3\u9700\u6C42\uFF0C\u751F\u6210\u5B8C\u6574\u5927\u7EB2\u4E0E\u9875\u9762\u3002' },\n { title: '\u4E3B\u9898\u7CFB\u7EDF', description: '\u6DF1\u8272\u9713\u8679\u3001\u6D45\u8272\u73BB\u7483\u7B49\u591A\u79CD\u98CE\u683C\uFF0C\u5168\u5C40\u4E00\u952E\u5207\u6362\u3002' },\n { title: '\u6570\u636E\u53EF\u89C6\u5316', description: '\u5185\u7F6E SVG \u56FE\u8868\u4E0E\u6D1E\u5BDF\u9762\u677F\uFF0C\u6570\u636E\u8868\u8FBE\u66F4\u805A\u7126\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u7279\u6027\u6807\u9898', features: [{ title: '\u65B0\u7279\u6027', description: '' }] };\n }\n break;\n }\n case 'stats': {\n if (layoutId === 'theme02_metrics_v1') {\n slide.props = {\n kicker: '\u6570\u636E\u5899',\n title: '\u6838\u5FC3\u6307\u6807\u4E00\u89C8',\n subtitle: '\u7528\u9713\u8679\u5361\u7247\u5448\u73B0\u5173\u952E\u4E1A\u52A1\u6570\u636E',\n stats: [\n { label: '\u5E74\u5EA6\u8425\u6536', value: '1.2', unit: '\u4EBF\u5143', tone: 'accent' },\n { label: '\u7528\u6237\u589E\u957F', value: '+320', unit: '%', tone: 'cool' },\n { label: '\u5BA2\u6237\u6EE1\u610F\u5EA6', value: '98', unit: '%', tone: 'accent2' },\n { label: '\u56E2\u961F\u89C4\u6A21', value: '86', unit: '\u4EBA', tone: 'default' }\n ]\n };\n } else if (layoutId === 'theme02_stats_v1') {\n slide.props = {\n kicker: '\u6838\u5FC3\u6307\u6807',\n title: '\u589E\u957F\u6570\u636E\u4E00\u89C8',\n subtitle: '\u7528\u9713\u8679\u6570\u5B57\u5361\u7247\u7A81\u51FA\u5173\u952E\u6210\u679C',\n stats: [\n { label: '\u6CE8\u518C\u7528\u6237', value: '120K', unit: '+' },\n { label: '\u6708\u6D3B\u8DC3\u7528\u6237', value: '45K', unit: '+' },\n { label: '\u4ED8\u8D39\u8F6C\u5316\u7387', value: '8.5', unit: '%' },\n { label: '\u5BA2\u6237\u7EED\u8D39\u7387', value: '92', unit: '%' },\n { label: '\u6A21\u677F\u4F7F\u7528\u91CF', value: '2.4M', unit: '+' },\n { label: 'NPS \u8BC4\u5206', value: '72', unit: '' }\n ]\n };\n } else {\n slide.props = { title: '\u6570\u636E\u6807\u9898', stats: [{ label: '\u6307\u6807', value: 0, unit: '', change: '' }] };\n }\n break;\n }\n case 'metric': {\n if (layoutId === 'theme01_metric_big') {\n slide.props = {\n kicker: '\u6838\u5FC3\u6307\u6807',\n title: '\u5E74\u5EA6\u878D\u8D44\u603B\u989D',\n subtitle: '\u5168\u5E74\u5927\u989D\u878D\u8D44\u4E8B\u4EF6\u6C47\u603B',\n value: '970',\n unit: '\u4EBF\u7F8E\u5143',\n context: '\u521B\u5386\u53F2\u65B0\u9AD8\uFF0C\u5360\u5168\u7F8E\u98CE\u9669\u6295\u8D44\u8FD1\u4E09\u5206\u4E4B\u4E00',\n metrics: [\n { value: '97', label: '\u4E8B\u4EF6\u7B14\u6570', accent: false },\n { value: '\u224810 \u4EBF', label: '\u5E73\u5747\u5355\u7B14', accent: false },\n { value: '+41%', label: 'Q4 \u73AF\u6BD4', accent: true },\n { value: 'Q2-Q3', label: '\u9AD8\u5CF0\u533A\u95F4', accent: false }\n ]\n };\n } else if (layoutId === 'theme01_metric_triptych') {\n slide.props = {\n kicker: '\u6307\u6807\u603B\u89C8',\n title: '\u5173\u952E\u6570\u636E\u4E09\u89C6\u56FE',\n subtitle: '\u89C4\u6A21\u3001\u589E\u901F\u4E0E\u8D28\u91CF\u7684\u7EFC\u5408\u89C6\u89D2',\n panels: [\n { index: '01', title: '\u878D\u8D44\u603B\u989D', value: '970 \u4EBF', subtitle: '\u5168\u5E74\u7D2F\u8BA1\u89C4\u6A21\u521B\u65B0\u9AD8', chartType: 'bar', chartData: [120, 180, 320, 350] },\n { index: '02', title: '\u4E8B\u4EF6\u7B14\u6570', value: '97 \u7B14', subtitle: '\u5927\u989D\u4EA4\u6613\u6D3B\u8DC3\u5EA6\u63D0\u5347', chartType: 'line', chartData: [18, 22, 28, 29] },\n { index: '03', title: '\u5E73\u5747\u5355\u7B14', value: '\u224810 \u4EBF', subtitle: '\u5355\u7B14\u91D1\u989D\u6301\u7EED\u8D70\u9AD8', chartType: 'area', chartData: [6.7, 8.2, 11.4, 12.1] }\n ]\n };\n } else if (layoutId === 'theme02_metric_big') {\n slide.props = {\n kicker: '\u6838\u5FC3\u6307\u6807',\n title: '\u5E74\u5EA6\u878D\u8D44\u603B\u989D',\n subtitle: '\u5168\u5E74\u5927\u989D\u878D\u8D44\u4E8B\u4EF6\u6C47\u603B',\n value: '970',\n unit: '\u4EBF\u7F8E\u5143',\n context: '\u521B\u5386\u53F2\u65B0\u9AD8\uFF0C\u5360\u5168\u7F8E\u98CE\u9669\u6295\u8D44\u8FD1\u4E09\u5206\u4E4B\u4E00',\n metrics: [\n { value: '97', label: '\u4E8B\u4EF6\u7B14\u6570', accent: false },\n { value: '\u224810 \u4EBF', label: '\u5E73\u5747\u5355\u7B14', accent: false },\n { value: '+41%', label: 'Q4 \u73AF\u6BD4', accent: true },\n { value: 'Q2-Q3', label: '\u9AD8\u5CF0\u533A\u95F4', accent: false }\n ]\n };\n } else if (layoutId === 'theme02_progress_v1') {\n slide.props = {\n kicker: '\u8FBE\u6210\u5EA6',\n title: '\u5E74\u5EA6\u76EE\u6807\u5B8C\u6210\u8FDB\u5EA6',\n subtitle: '\u5173\u952E OKR \u5F53\u524D\u5B8C\u6210\u5EA6',\n items: [\n { label: 'ARR \u8425\u6536\u76EE\u6807', value: 8400, max: 12000, unit: '\u4E07\u5143' },\n { label: '\u4F01\u4E1A\u5BA2\u6237\u7B7E\u7EA6', value: 320, max: 500, unit: '\u5BB6' },\n { label: '\u4EA7\u54C1 NPS', value: 72, max: 100, unit: '\u5206' },\n { label: '\u56E2\u961F\u89C4\u6A21', value: 86, max: 120, unit: '\u4EBA' }\n ]\n };\n } else if (layoutId === 'theme02_delta_v1') {\n slide.props = {\n kicker: '\u4ECA\u6614\u5BF9\u7167',\n title: '\u5173\u952E\u6307\u6807\u53D8\u5316',\n subtitle: '\u53BB\u5E74\u540C\u671F vs \u5F53\u524D\u8868\u73B0',\n items: [\n { label: 'ARR \u8425\u6536', previous: 4200, current: 8400, unit: '\u4E07\u5143' },\n { label: '\u4F01\u4E1A\u5BA2\u6237', previous: 120, current: 320, unit: '\u5BB6' },\n { label: '\u4EA7\u54C1 NPS', previous: 58, current: 72, unit: '\u5206' },\n { label: '\u56E2\u961F\u89C4\u6A21', previous: 45, current: 86, unit: '\u4EBA' }\n ],\n footnote: '\u6570\u636E\u6765\u6E90\uFF1A\u516C\u53F8\u8D22\u52A1\u4E0E\u8FD0\u8425\u7CFB\u7EDF \u00B7 2026'\n };\n } else {\n slide.props = { title: '\u6307\u6807\u6807\u9898', metrics: [{ label: '\u6307\u6807', value: '0', unit: '', change: '' }] };\n }\n break;\n }\n case 'comparison': {\n if (layoutId === 'theme01_diptych_contrast') {\n slide.props = {\n kicker: '\u53CC\u8054\u5BF9\u6BD4',\n title: '\u6A21\u578B\u80FD\u529B vs \u5E94\u7528\u843D\u5730',\n left: { label: '\u6A21\u578B\u80FD\u529B', labelEn: 'FOUNDATION MODEL', imageUrl: '' },\n right: { label: '\u5E94\u7528\u843D\u5730', labelEn: 'APPLICATION', imageUrl: '' },\n centerCard: {\n title: '\u6838\u5FC3\u5DEE\u5F02',\n comparisons: [\n { leftValue: '66%', leftLabel: '\u8D44\u672C\u5360\u6BD4', rightValue: '25%', rightLabel: '\u8D44\u672C\u5360\u6BD4' },\n { leftValue: '420 \u4EBF', leftLabel: '\u878D\u8D44\u989D', rightValue: '245 \u4EBF', rightLabel: '\u878D\u8D44\u989D' },\n { leftValue: '\u5934\u90E8\u96C6\u4E2D', leftLabel: '\u7ADE\u4E89\u683C\u5C40', rightValue: '\u957F\u5C3E\u5206\u6563', rightLabel: '\u7ADE\u4E89\u683C\u5C40' }\n ],\n conclusion: '\u5E95\u5C42\u6A21\u578B\u4ECD\u662F\u8D44\u91D1\u4E3B\u6218\u573A\uFF0C\u4F46\u5782\u76F4\u5E94\u7528\u7684\u53D8\u73B0\u8DEF\u5F84\u66F4\u6E05\u6670\u3002'\n }\n };\n } else if (layoutId === 'theme02_comparison_v1') {\n slide.props = {\n kicker: '\u65B9\u6848\u5BF9\u6BD4',\n title: '\u4F20\u7EDF\u65B9\u5F0F vs \u67E0\u6AAC PPT',\n subtitle: '\u4ECE\u5C0F\u65F6\u7EA7\u5230\u5206\u949F\u7EA7\u7684\u6548\u7387\u8DC3\u8FC1',\n leftTitle: '\u4F20\u7EDF PPT \u5236\u4F5C',\n rightTitle: '\u67E0\u6AAC PPT \u751F\u6210',\n leftItems: ['\u624B\u52A8\u6392\u7248\u8017\u65F6\u4E45', '\u98CE\u683C\u96BE\u4EE5\u7EDF\u4E00', '\u534F\u4F5C\u7248\u672C\u6DF7\u4E71', '\u6570\u636E\u56FE\u8868\u66F4\u65B0\u6162'],\n rightItems: ['AI \u4E00\u952E\u751F\u6210\u7248\u5F0F', '\u4E3B\u9898\u7CFB\u7EDF\u5168\u5C40\u4E00\u81F4', '\u4E91\u7AEF\u5B9E\u65F6\u534F\u4F5C', '\u6570\u636E\u8054\u52A8\u81EA\u52A8\u5237\u65B0']\n };\n } else {\n slide.props = { title: '\u5BF9\u6BD4\u6807\u9898', left: '\u5DE6\u4FA7', right: '\u53F3\u4FA7' };\n }\n break;\n }\n case 'team': {\n if (layoutId === 'theme02_team_v1') {\n slide.props = {\n kicker: '\u6838\u5FC3\u56E2\u961F',\n title: '\u6211\u4EEC\u662F\u8C01',\n subtitle: '\u4E00\u652F\u76F8\u4FE1\u6280\u672F\u4E0E\u8BBE\u8BA1\u80FD\u6539\u53D8\u6F14\u793A\u7684\u56E2\u961F',\n members: [\n { name: '\u674E\u96F7', role: 'CEO', bio: '\u8FDE\u7EED\u521B\u4E1A\u8005\uFF0C\u524D\u5934\u90E8 SaaS \u4EA7\u54C1\u8D1F\u8D23\u4EBA\u3002' },\n { name: '\u97E9\u6885\u6885', role: 'CTO', bio: '\u5168\u6808\u5DE5\u7A0B\u5E08\uFF0C\u4E13\u6CE8 AI \u4E0E\u6E32\u67D3\u5F15\u64CE\u3002' },\n { name: '\u6797\u6D9B', role: '\u8BBE\u8BA1\u603B\u76D1', bio: '\u5341\u5E74\u54C1\u724C\u8BBE\u8BA1\u7ECF\u9A8C\uFF0C\u6DF1\u8015\u89C6\u89C9\u7CFB\u7EDF\u3002' },\n { name: '\u5409\u59C6', role: '\u589E\u957F\u8D1F\u8D23\u4EBA', bio: '\u6570\u636E\u9A71\u52A8\u589E\u957F\uFF0C\u64C5\u957F B2B \u89C4\u6A21\u5316\u83B7\u5BA2\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u56E2\u961F', members: [{ name: '\u65B0\u6210\u5458', role: '', bio: '', imageUrl: '' }] };\n }\n break;\n }\n case 'timeline': {\n if (layoutId === 'theme02_timeline_v1') {\n slide.props = {\n kicker: '\u91CC\u7A0B\u7891',\n title: '\u53D1\u5C55\u5386\u7A0B',\n subtitle: '\u4ECE\u60F3\u6CD5\u5230\u4EA7\u54C1\u7684\u5173\u952E\u8282\u70B9',\n milestones: [\n { date: '2022.06', title: '\u9879\u76EE\u542F\u52A8', description: '\u67E0\u6AAC PPT \u7ACB\u9879\uFF0C\u805A\u7126 AI \u6F14\u793A\u751F\u6210\u3002' },\n { date: '2023.03', title: '\u4EA7\u54C1\u4E0A\u7EBF', description: '\u9996\u4E2A\u7248\u672C\u53D1\u5E03\uFF0C\u652F\u6301\u4E00\u952E\u751F\u6210\u5B8C\u6574\u5927\u7EB2\u3002' },\n { date: '2024.01', title: '\u89C4\u6A21\u589E\u957F', description: '\u7D2F\u8BA1\u7528\u6237\u7A81\u7834 10 \u4E07\uFF0C\u4F01\u4E1A\u5BA2\u6237\u7B7E\u7EA6\u3002' },\n { date: '2025.09', title: '\u751F\u6001\u5F00\u653E', description: '\u5F00\u653E\u4E3B\u9898\u4E0E\u6A21\u677F\u5E02\u573A\uFF0C\u5171\u5EFA\u521B\u4F5C\u8005\u751F\u6001\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u65F6\u95F4\u7EBF', events: [{ date: '', title: '\u65B0\u91CC\u7A0B\u7891', description: '' }] };\n }\n break;\n }\n case 'faq': {\n if (layoutId === 'theme02_faq_v1') {\n slide.props = {\n kicker: '\u5E38\u89C1\u95EE\u9898',\n title: '\u4F60\u53EF\u80FD\u60F3\u77E5\u9053\u7684',\n subtitle: '\u5FEB\u901F\u4E86\u89E3\u67E0\u6AAC PPT \u7684\u6838\u5FC3\u80FD\u529B\u4E0E\u4F7F\u7528\u65B9\u5F0F',\n items: [\n { q: '\u67E0\u6AAC PPT \u9002\u5408\u4EC0\u4E48\u573A\u666F\uFF1F', a: '\u878D\u8D44\u8DEF\u6F14\u3001\u4EA7\u54C1\u53D1\u5E03\u3001\u5B63\u5EA6\u6C47\u62A5\u3001\u57F9\u8BAD\u8BFE\u4EF6\u7B49\u9700\u8981\u9AD8\u8D28\u91CF\u6F14\u793A\u7684\u573A\u5408\u3002' },\n { q: '\u751F\u6210\u540E\u80FD\u5426\u7EE7\u7EED\u7F16\u8F91\uFF1F', a: '\u53EF\u4EE5\u3002\u7F16\u8F91\u5668\u652F\u6301\u76F4\u63A5\u4FEE\u6539\u6587\u5B57\u3001\u66FF\u6362\u56FE\u7247\u3001\u8C03\u6574\u914D\u8272\u4E0E\u7248\u5F0F\u3002' },\n { q: '\u5BFC\u51FA\u683C\u5F0F\u6709\u54EA\u4E9B\uFF1F', a: '\u652F\u6301 PPTX\u3001PDF \u548C\u9AD8\u6E05\u56FE\u7247\u5BFC\u51FA\uFF0C\u6EE1\u8DB3\u4E0D\u540C\u5206\u53D1\u9700\u6C42\u3002' }\n ]\n };\n } else {\n slide.props = { title: '\u5E38\u89C1\u95EE\u9898', items: [{ q: '\u95EE\u9898', a: '\u56DE\u7B54' }] };\n }\n break;\n }\n case 'partners': {\n if (layoutId === 'theme02_partners_v1') {\n slide.props = {\n kicker: '\u5408\u4F5C\u4F19\u4F34',\n title: '\u4ED6\u4EEC\u90FD\u5728\u4F7F\u7528',\n subtitle: '\u4E0E\u9886\u5148\u4F01\u4E1A\u5171\u540C\u63A8\u52A8\u6F14\u793A\u65B9\u5F0F\u5347\u7EA7',\n partners: [\n { name: '\u4E91\u667A\u79D1\u6280', logoUrl: '' },\n { name: '\u672A\u6765\u8D44\u672C', logoUrl: '' },\n { name: '\u661F\u8FB0\u6570\u636E', logoUrl: '' },\n { name: '\u84DD\u6D77\u4F20\u5A92', logoUrl: '' },\n { name: '\u7EA2\u77F3\u54A8\u8BE2', logoUrl: '' },\n { name: '\u6781\u5149\u8BBE\u8BA1', logoUrl: '' },\n { name: '\u4E07\u8C61\u96C6\u56E2', logoUrl: '' },\n { name: '\u94F6\u6CB3\u521B\u6295', logoUrl: '' }\n ]\n };\n } else {\n slide.props = { title: '\u5408\u4F5C\u4F19\u4F34', partners: [{ name: '', logoUrl: '' }] };\n }\n break;\n }\n case 'roadmap': {\n if (layoutId === 'theme02_roadmap_v1') {\n slide.props = {\n kicker: '\u4EA7\u54C1\u8DEF\u7EBF',\n title: '\u672A\u6765 12 \u4E2A\u6708\u89C4\u5212',\n subtitle: '\u4ECE\u57FA\u7840\u80FD\u529B\u5230\u751F\u6001\u5F00\u653E\u7684\u6E05\u6670\u8282\u594F',\n phases: [\n { phase: 'Q1 \u57FA\u7840\u80FD\u529B', items: ['\u7F16\u8F91\u5668\u5185\u6838\u91CD\u6784', '\u4E3B\u9898\u7CFB\u7EDF 2.0', 'PPTX \u5BFC\u51FA\u4F18\u5316'] },\n { phase: 'Q2 \u667A\u80FD\u5347\u7EA7', items: ['AI \u591A\u8F6E\u5BF9\u8BDD\u751F\u6210', '\u81EA\u52A8\u914D\u56FE\u4E0E\u6570\u636E\u8054\u52A8', '\u534F\u4F5C\u8BC4\u8BBA'] },\n { phase: 'Q3 \u4F01\u4E1A\u573A\u666F', items: ['\u54C1\u724C\u8D44\u4EA7\u4E2D\u5FC3', '\u6743\u9650\u4E0E\u7248\u672C\u7BA1\u7406', '\u79C1\u6709\u90E8\u7F72'] },\n { phase: 'Q4 \u751F\u6001\u5F00\u653E', items: ['\u6A21\u677F\u5E02\u573A', '\u5F00\u53D1\u8005 SDK', '\u5F00\u653E\u4E3B\u9898\u534F\u8BAE'] }\n ]\n };\n } else {\n slide.props = { title: '\u8DEF\u7EBF\u56FE', phases: [{ phase: '\u9636\u6BB5', items: ['\u91CC\u7A0B\u7891'] }] };\n }\n break;\n }\n case 'pricing': {\n if (layoutId === 'theme02_pricing_v1') {\n slide.props = {\n kicker: '\u5B9A\u4EF7\u65B9\u6848',\n title: '\u9009\u62E9\u9002\u5408\u4F60\u7684\u65B9\u6848',\n subtitle: '\u4ECE\u4E2A\u4EBA\u521B\u4F5C\u8005\u5230\u4F01\u4E1A\u56E2\u961F\uFF0C\u7075\u6D3B\u6269\u5C55',\n tiers: [\n {\n name: '\u514D\u8D39\u7248',\n price: '\u00A50',\n period: '/ \u6708',\n features: ['\u6BCF\u6708 10 \u6B21\u751F\u6210', '3 \u79CD\u57FA\u7840\u4E3B\u9898', 'PNG \u5BFC\u51FA'],\n cta: '\u5F00\u59CB\u4F7F\u7528',\n highlight: false\n },\n {\n name: '\u4E13\u4E1A\u7248',\n price: '\u00A599',\n period: '/ \u6708',\n features: ['\u65E0\u9650\u6B21\u751F\u6210', '\u5168\u90E8\u4E3B\u9898\u4E0E\u7248\u5F0F', 'PPTX / PDF \u5BFC\u51FA', '\u4F18\u5148\u5BA2\u670D'],\n cta: '\u7ACB\u5373\u5347\u7EA7',\n highlight: true\n },\n {\n name: '\u56E2\u961F\u7248',\n price: '\u00A5399',\n period: '/ \u6708',\n features: ['5 \u4EBA\u534F\u4F5C', '\u54C1\u724C\u8D44\u4EA7\u4E2D\u5FC3', 'API \u63A5\u5165', '\u4E13\u5C5E\u5BA2\u6237\u6210\u529F'],\n cta: '\u8054\u7CFB\u9500\u552E',\n highlight: false\n }\n ]\n };\n } else {\n slide.props = { title: '\u5B9A\u4EF7', tiers: [{ name: '\u65B9\u6848', price: '', period: '', features: [''], cta: '' }] };\n }\n break;\n }\n case 'image': {\n if (layoutId === 'theme02_image_v1') {\n slide.props = {\n kicker: '\u89C6\u89C9\u5448\u73B0',\n title: '\u4E00\u5F20\u56FE\u8BB2\u6E05\u4EA7\u54C1\u4EF7\u503C',\n subtitle: '\u4E0A\u4F20\u9AD8\u6E05\u5927\u56FE\uFF0C\u7528\u9713\u8679\u6E10\u53D8\u53E0\u52A0\u6253\u9020\u79D1\u6280\u611F\u5C01\u9762\u3002',\n image: '',\n caption: '\u56FE\u6CE8\uFF1A\u4EA7\u54C1\u754C\u9762\u793A\u610F\u56FE'\n };\n } else {\n slide.props = { title: '\u56FE\u7247\u6807\u9898', subtitle: '\u526F\u6807\u9898' };\n }\n break;\n }\n case 'swot': {\n if (layoutId === 'theme02_swot_v1') {\n slide.props = {\n kicker: '\u6218\u7565\u5206\u6790',\n title: 'SWOT \u5206\u6790',\n subtitle: '\u770B\u6E05\u5185\u5916\u90E8\u73AF\u5883\uFF0C\u5236\u5B9A\u4E0B\u4E00\u6B65\u7B56\u7565',\n strength: 'AI \u751F\u6210\u80FD\u529B\u9886\u5148\uFF0C\u4E3B\u9898\u7CFB\u7EDF\u53EF\u6269\u5C55\uFF0C\u793E\u533A\u6D3B\u8DC3\u5EA6\u9AD8\u3002',\n weakness: '\u54C1\u724C\u77E5\u540D\u5EA6\u4ECD\u5728\u5EFA\u7ACB\uFF0C\u90E8\u5206\u9AD8\u7EA7\u7248\u5F0F\u4F9D\u8D56\u4EBA\u5DE5\u8BBE\u8BA1\u3002',\n opportunity: '\u4F01\u4E1A\u6570\u5B57\u5316\u6C47\u62A5\u9700\u6C42\u589E\u957F\uFF0CAI \u5DE5\u5177\u63A5\u53D7\u5EA6\u63D0\u5347\u3002',\n threat: '\u5927\u5382\u540C\u7C7B\u4EA7\u54C1\u5E03\u5C40\u52A0\u901F\uFF0C\u7528\u6237\u5BA1\u7F8E\u8981\u6C42\u6301\u7EED\u63D0\u9AD8\u3002'\n };\n } else {\n slide.props = { title: 'SWOT \u5206\u6790', strength: '\u4F18\u52BF', weakness: '\u52A3\u52BF', opportunity: '\u673A\u4F1A', threat: '\u5A01\u80C1' };\n }\n break;\n }\n case 'pest': {\n if (layoutId === 'theme02_pest_v1') {\n slide.props = {\n kicker: '\u5B8F\u89C2\u5206\u6790',\n title: 'PEST \u5206\u6790',\n subtitle: '\u4ECE\u653F\u7B56\u3001\u7ECF\u6D4E\u3001\u793E\u4F1A\u3001\u6280\u672F\u56DB\u7EF4\u5EA6\u6D1E\u5BDF\u73AF\u5883',\n political: 'AI \u5185\u5BB9\u751F\u6210\u76D1\u7BA1\u6846\u67B6\u9010\u6B65\u5B8C\u5584\uFF0C\u5408\u89C4\u5316\u6210\u4E3A\u4EA7\u54C1\u51C6\u5165\u95E8\u69DB\u3002',\n economic: '\u4F01\u4E1A\u964D\u672C\u589E\u6548\u9700\u6C42\u5F3A\u70C8\uFF0C\u6570\u5B57\u5316\u5DE5\u5177\u9884\u7B97\u5360\u6BD4\u6301\u7EED\u63D0\u5347\u3002',\n social: '\u8FDC\u7A0B\u534F\u4F5C\u5E38\u6001\u5316\uFF0C\u5BF9\u8F7B\u91CF\u5316\u3001\u53EF\u5206\u4EAB\u6F14\u793A\u5185\u5BB9\u7684\u9700\u6C42\u6FC0\u589E\u3002',\n technological: '\u5927\u6A21\u578B\u591A\u6A21\u6001\u80FD\u529B\u5FEB\u901F\u8FDB\u6B65\uFF0C\u6587\u672C\u5230\u5E7B\u706F\u7247\u7684\u751F\u6210\u8D28\u91CF\u663E\u8457\u63D0\u9AD8\u3002'\n };\n } else {\n slide.props = { title: 'PEST \u5206\u6790', political: '\u653F\u6CBB\u73AF\u5883', economic: '\u7ECF\u6D4E\u73AF\u5883', social: '\u793E\u4F1A\u73AF\u5883', technological: '\u6280\u672F\u73AF\u5883' };\n }\n break;\n }\n case 'testimonial': {\n if (layoutId === 'theme02_testimonial_v1') {\n slide.props = {\n kicker: '\u5BA2\u6237\u8BC4\u4EF7',\n quote: '\u67E0\u6AAC PPT \u5E2E\u52A9\u6211\u4EEC\u628A\u6C47\u62A5\u5236\u4F5C\u65F6\u95F4\u4ECE 2 \u5929\u7F29\u77ED\u5230 2 \u5C0F\u65F6\uFF0C\u56E2\u961F\u53EF\u4EE5\u628A\u66F4\u591A\u7CBE\u529B\u653E\u5728\u4E1A\u52A1\u601D\u8003\u4E0A\u3002',\n author: '\u9648\u6653\u660E',\n role: '\u4EA7\u54C1\u603B\u76D1',\n company: '\u672A\u6765\u79D1\u6280\u6709\u9650\u516C\u53F8',\n avatarUrl: ''\n };\n } else {\n slide.props = { kicker: '\u5BA2\u6237\u8BC4\u4EF7', quote: '\u4EA7\u54C1\u5E2E\u52A9\u6211\u4EEC\u628A\u6C47\u62A5\u6548\u7387\u63D0\u5347\u4E86 3 \u500D\u3002', author: '\u5F20\u4E09', role: '\u4EA7\u54C1\u7ECF\u7406', company: '\u793A\u4F8B\u79D1\u6280' };\n }\n break;\n }\n case 'tableOfContents': {\n if (layoutId === 'theme02_table_of_contents_v1') {\n slide.props = {\n title: '\u76EE\u5F55',\n subtitle: 'CONTENTS',\n items: [\n { title: '\u5E02\u573A\u80CC\u666F\u4E0E\u673A\u4F1A', page: '02' },\n { title: '\u4EA7\u54C1\u65B9\u6848\u4E0E\u4F18\u52BF', page: '04' },\n { title: '\u5546\u4E1A\u6A21\u5F0F\u4E0E\u589E\u957F', page: '06' },\n { title: '\u56E2\u961F\u4E0E\u878D\u8D44\u8BA1\u5212', page: '08' }\n ]\n };\n } else {\n slide.props = { title: '\u76EE\u5F55', items: ['\u76EE\u5F55\u9879'] };\n }\n break;\n }\n }\n return slide;\n }\n\n const addSlideBtn = document.getElementById('lp-add-slide');\n const addSlideModal = document.getElementById('lp-add-slide-modal');\n const addSlideConfirm = document.getElementById('lp-add-slide-confirm');\n let selectedLayoutId = null;\n let selectedRole = null;\n\n function closeAddSlideModal() {\n if (addSlideModal) addSlideModal.setAttribute('hidden', '');\n addSlideModal?.querySelectorAll('.lp-add-slide-option').forEach((el) => el.classList.remove('selected'));\n selectedLayoutId = null;\n selectedRole = null;\n if (addSlideConfirm) addSlideConfirm.disabled = true;\n }\n\n function openAddSlideModal() {\n if (addSlideModal) addSlideModal.removeAttribute('hidden');\n }\n\n function confirmAddSlide() {\n if (!selectedLayoutId) return;\n const newSlide = createDefaultSlide(selectedLayoutId, selectedRole || selectedLayoutId.split('_')[0]);\n recordHistory();\n goal.slides.push(newSlide);\n selectedSlideIdx = goal.slides.length - 1;\n // current \u4F1A\u5728 rebuildSlidesAndThumbnails \u4E2D\u540C\u6B65\uFF0C\u907F\u514D\u5728 DOM \u91CD\u5EFA\u524D\u8BBF\u95EE\u8D8A\u754C\n autoSave();\n closeAddSlideModal();\n reloadEditor();\n }\n\n if (addSlideBtn) {\n addSlideBtn.addEventListener('click', openAddSlideModal);\n }\n\n if (addSlideModal) {\n addSlideModal.addEventListener('click', (e) => {\n const target = e.target;\n if (target.closest('[data-close-modal]')) {\n closeAddSlideModal();\n return;\n }\n const option = target.closest('.lp-add-slide-option');\n if (option) {\n addSlideModal.querySelectorAll('.lp-add-slide-option').forEach((el) => el.classList.remove('selected'));\n option.classList.add('selected');\n selectedLayoutId = option.getAttribute('data-layout');\n selectedRole = option.getAttribute('data-role');\n if (addSlideConfirm) addSlideConfirm.disabled = false;\n }\n });\n }\n\n if (addSlideConfirm) {\n addSlideConfirm.addEventListener('click', confirmAddSlide);\n }\n\n updateUndoRedoButtons();\n\n // \u7FFB\u9875\u811A\u672C\n let slides = Array.from(document.querySelectorAll('.lp-slide-wrapper'));\n let thumbnails = Array.from(document.querySelectorAll('.lp-thumbnail'));\n const prevBtn = document.getElementById('lp-prev');\n const nextBtn = document.getElementById('lp-next');\n const currentLabel = document.getElementById('lp-current');\n const deck = document.querySelector('.lp-deck');\n let current = 0;\n let isTransitioning = false;\n let cleanupTimer = null;\n let dragSrcIndex = null;\n let dragDropIndicator = null;\n\n function refreshSlidesAndThumbnails() {\n slides = Array.from(document.querySelectorAll('.lp-slide-wrapper'));\n thumbnails = Array.from(document.querySelectorAll('.lp-thumbnail'));\n }\n\n function updateEditorUI() {\n thumbnails.forEach((thumb, index) => {\n thumb.classList.toggle('active', index === current);\n });\n const activeThumb = thumbnails[current];\n if (activeThumb) activeThumb.scrollIntoView({ block: 'nearest', behavior: 'smooth' });\n const labelEl = document.getElementById('lp-current');\n if (labelEl) labelEl.textContent = String(current + 1);\n if (prevBtn) prevBtn.disabled = current === 0 || isTransitioning;\n if (nextBtn) nextBtn.disabled = current === slides.length - 1 || isTransitioning;\n }\n\n function resetTransitionState() {\n slides.forEach((slide) => slide.classList.remove('enter', 'leave'));\n if (deck) {\n deck.setAttribute('data-lp-transition', 'none');\n deck.removeAttribute('data-lp-direction');\n }\n isTransitioning = false;\n updateClasses();\n }\n\n function updateClasses() {\n updateEditorUI();\n }\n\n function goTo(index) {\n if (index === current || index < 0 || index >= slides.length || isTransitioning) {\n return;\n }\n const leaving = slides[current];\n const entering = slides[index];\n const transition = entering.dataset.lpTransition || 'none';\n const direction = index > current ? 'next' : 'prev';\n\n if (transition === 'none') {\n current = index;\n if (deck) {\n deck.setAttribute('data-lp-transition', 'none');\n deck.removeAttribute('data-lp-direction');\n }\n leaving.classList.remove('active');\n entering.classList.add('active');\n updateClasses();\n if (typeof selectSlide === 'function') selectSlide(current);\n // \u5207\u9875\u540E\u91CA\u653E\u79BB\u5F00 slide \u7684\u56FE\u8868\u5E76\u4EC5\u521D\u59CB\u5316\u65B0 active slide \u7684 ECharts\u3002\n requestAnimationFrame(() => {\n disposeEChartsInWrapper(leaving);\n initActiveSlideECharts();\n });\n return;\n }\n\n isTransitioning = true;\n current = index;\n updateEditorUI();\n\n if (deck) {\n deck.setAttribute('data-lp-transition', transition);\n deck.setAttribute('data-lp-direction', direction);\n }\n\n leaving.classList.add('leave');\n entering.classList.add('enter');\n // \u5148\u7981\u7528 transition\uFF0C\u8BA9 leave/enter \u7684\u521D\u59CB\u72B6\u6001\u76F4\u63A5\u843D\u4F4D\uFF0C\n // \u907F\u514D\u4ECE\u4E0A\u4E00\u72B6\u6001\u7684 translateX \u63D2\u503C\uFF1B\u968F\u540E\u6062\u590D transition \u518D\u5207\u6362 active\u3002\n leaving.style.transition = 'none';\n entering.style.transition = 'none';\n void entering.offsetWidth;\n leaving.style.transition = '';\n entering.style.transition = '';\n leaving.classList.remove('active');\n entering.classList.add('active');\n\n function finish() {\n entering.removeEventListener('transitionend', finish);\n clearTimeout(cleanupTimer);\n resetTransitionState();\n // \u52A8\u753B\u7ED3\u675F\u540E\u91CA\u653E\u79BB\u5F00 slide \u7684\u56FE\u8868\uFF0C\u5E76\u4EC5\u5BF9\u5F53\u524D active slide \u7684 ECharts \u8FDB\u884C resize/\u521D\u59CB\u5316\n requestAnimationFrame(() => {\n disposeEChartsInWrapper(leaving);\n initActiveSlideECharts();\n });\n }\n entering.addEventListener('transitionend', finish);\n cleanupTimer = setTimeout(finish, 600);\n\n if (typeof selectSlide === 'function') selectSlide(current);\n }\n\n if (prevBtn) prevBtn.addEventListener('click', () => goTo(current - 1));\n if (nextBtn) nextBtn.addEventListener('click', () => goTo(current + 1));\n const deleteConfirmToast = document.getElementById('lp-delete-confirm-toast');\n const deleteConfirmText = document.getElementById('lp-delete-confirm-text');\n const deleteConfirmCancel = document.getElementById('lp-delete-confirm-cancel');\n const deleteConfirmConfirm = document.getElementById('lp-delete-confirm-confirm');\n let pendingDeleteIndex = null;\n\n function showDeleteConfirm(index, triggerEl) {\n if (goal.slides.length <= 1) {\n alert('\u81F3\u5C11\u4FDD\u7559\u4E00\u9875\u5E7B\u706F\u7247\u3002');\n return;\n }\n pendingDeleteIndex = index;\n if (deleteConfirmText) {\n deleteConfirmText.textContent = '\u786E\u5B9A\u5220\u9664\u7B2C ' + (index + 1) + ' \u9875\u5E7B\u706F\u7247\uFF1F';\n }\n if (deleteConfirmToast) {\n deleteConfirmToast.removeAttribute('hidden');\n positionDeleteConfirmToast(triggerEl);\n }\n }\n\n function positionDeleteConfirmToast(triggerEl) {\n if (!deleteConfirmToast || !triggerEl) return;\n const rect = triggerEl.getBoundingClientRect();\n const toastRect = deleteConfirmToast.getBoundingClientRect();\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const gap = 8;\n let top = rect.bottom + gap;\n let left = rect.left + rect.width / 2 - toastRect.width / 2;\n if (left < gap) left = gap;\n if (left + toastRect.width > viewportWidth - gap) {\n left = viewportWidth - toastRect.width - gap;\n }\n if (top + toastRect.height > viewportHeight - gap) {\n top = rect.top - toastRect.height - gap;\n }\n deleteConfirmToast.style.top = top + 'px';\n deleteConfirmToast.style.left = left + 'px';\n }\n\n function hideDeleteConfirm() {\n pendingDeleteIndex = null;\n if (deleteConfirmToast) {\n deleteConfirmToast.setAttribute('hidden', '');\n deleteConfirmToast.style.top = '';\n deleteConfirmToast.style.left = '';\n }\n }\n\n function confirmDelete() {\n if (pendingDeleteIndex == null) return;\n const index = pendingDeleteIndex;\n hideDeleteConfirm();\n recordHistory();\n goal.slides.splice(index, 1);\n selectedSlideIdx = Math.min(index, goal.slides.length - 1);\n // current \u4F1A\u5728 rebuildSlidesAndThumbnails \u4E2D\u540C\u6B65\n autoSave();\n reloadEditor();\n }\n\n if (deleteConfirmCancel) {\n deleteConfirmCancel.addEventListener('click', hideDeleteConfirm);\n }\n if (deleteConfirmConfirm) {\n deleteConfirmConfirm.addEventListener('click', confirmDelete);\n }\n if (deleteConfirmToast) {\n deleteConfirmToast.addEventListener('click', (e) => {\n if (e.target === deleteConfirmToast) {\n hideDeleteConfirm();\n }\n });\n }\n\n function attachThumbnailListeners() {\n thumbnails.forEach((thumb) => {\n thumb.addEventListener('click', (e) => {\n const deleteBtn = e.target.closest && e.target.closest('.lp-thumbnail-delete');\n if (deleteBtn) {\n e.stopPropagation();\n const index = Number(deleteBtn.getAttribute('data-index'));\n showDeleteConfirm(index, deleteBtn);\n return;\n }\n goTo(Number(thumb.dataset.index));\n });\n\n // \u7F29\u7565\u56FE\u6539\u4E3A div \u540E\uFF0C\u8865\u5145\u952E\u76D8\u9009\u4E2D\u652F\u6301\n thumb.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n goTo(Number(thumb.dataset.index));\n }\n });\n });\n }\n attachThumbnailListeners();\n\n // \u7F29\u7565\u56FE\u865A\u62DF\u6EDA\u52A8\uFF1A\u7528 IntersectionObserver \u5378\u8F7D\u89C6\u53E3\u5916 slide \u6E32\u67D3 DOM\uFF0C\u4EC5\u4FDD\u7559\u5360\u4F4D\u6846\u67B6\u4E0E\u6587\u5B57\u4FE1\u606F\u3002\n const thumbnailRenderCache = {};\n let thumbnailObserver = null;\n\n function getThumbnailCacheKey(thumb) {\n return 'thumb-' + thumb.dataset.index;\n }\n\n function mountThumbnailRender(thumb) {\n const render = thumb.querySelector('.lp-thumbnail-render');\n if (!render) return;\n if (thumb.dataset.lpThumbMounted === 'true') return;\n const key = getThumbnailCacheKey(thumb);\n const html = thumbnailRenderCache[key];\n if (html != null) {\n render.innerHTML = html;\n }\n thumb.dataset.lpThumbMounted = 'true';\n }\n\n function unmountThumbnailRender(thumb) {\n const render = thumb.querySelector('.lp-thumbnail-render');\n if (!render) return;\n if (thumb.dataset.lpThumbMounted === 'false') return;\n const key = getThumbnailCacheKey(thumb);\n thumbnailRenderCache[key] = render.innerHTML;\n render.innerHTML = '';\n thumb.dataset.lpThumbMounted = 'false';\n }\n\n function initThumbnailVirtualScroll() {\n const container = document.querySelector('.lp-editor-left-panel');\n if (!container || !('IntersectionObserver' in window)) return;\n\n if (thumbnailObserver) {\n thumbnailObserver.disconnect();\n thumbnailObserver = null;\n }\n\n // \u9ED8\u8BA4\u6240\u6709\u7F29\u7565\u56FE\u5904\u4E8E\u6302\u8F7D\u72B6\u6001\n thumbnails.forEach((thumb) => {\n thumb.dataset.lpThumbMounted = 'true';\n });\n\n thumbnailObserver = new IntersectionObserver((entries) => {\n entries.forEach((entry) => {\n const thumb = entry.target;\n const index = Number(thumb.dataset.index);\n // \u5F53\u524D\u6FC0\u6D3B\u9875\u59CB\u7EC8\u4FDD\u7559\u6E32\u67D3\uFF0C\u907F\u514D\u5207\u6362\u65F6\u767D\u5C4F\n if (index === current || entry.isIntersecting) {\n mountThumbnailRender(thumb);\n } else {\n unmountThumbnailRender(thumb);\n }\n });\n }, {\n root: container,\n rootMargin: '200px 0px 200px 0px',\n threshold: 0,\n });\n\n thumbnails.forEach((thumb) => thumbnailObserver.observe(thumb));\n }\n\n function disconnectThumbnailVirtualScroll() {\n if (thumbnailObserver) {\n thumbnailObserver.disconnect();\n thumbnailObserver = null;\n }\n }\n initThumbnailVirtualScroll();\n\n function attachDragAndDropListeners() {\n const container = document.querySelector('.lp-editor-thumbnails');\n if (!container) return;\n\n function getOrCreateDropIndicator() {\n if (dragDropIndicator && dragDropIndicator.parentElement) return dragDropIndicator;\n dragDropIndicator = document.createElement('div');\n dragDropIndicator.className = 'lp-thumbnail-drop-indicator';\n container.appendChild(dragDropIndicator);\n return dragDropIndicator;\n }\n\n function clearDragState() {\n dragSrcIndex = null;\n thumbnails.forEach((t) => t.classList.remove('dragging', 'drag-over'));\n if (dragDropIndicator) dragDropIndicator.classList.remove('visible');\n }\n\n function positionDropIndicator(target, before) {\n const indicator = getOrCreateDropIndicator();\n if (!indicator || !target) return;\n const rect = target.getBoundingClientRect();\n const containerRect = container.getBoundingClientRect();\n const top = before ? rect.top - containerRect.top - 1 : rect.bottom - containerRect.top - 1;\n indicator.style.top = top + 'px';\n indicator.classList.add('visible');\n }\n\n container.addEventListener('dragstart', (e) => {\n const thumb = e.target && e.target.closest && e.target.closest('.lp-thumbnail');\n if (!thumb) return;\n // \u70B9\u51FB\u5220\u9664\u6309\u94AE\u65F6\u4E0D\u5E94\u89E6\u53D1\u62D6\u62FD\n const deleteBtn = e.target.closest && e.target.closest('.lp-thumbnail-delete');\n if (deleteBtn) {\n e.preventDefault();\n return;\n }\n if (isTransitioning || goal.slides.length <= 1) {\n e.preventDefault();\n return;\n }\n const src = Number(thumb.dataset.index);\n if (Number.isNaN(src)) return;\n dragSrcIndex = src;\n hideDeleteConfirm();\n thumb.classList.add('dragging');\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', String(src));\n try {\n e.dataTransfer.setDragImage(thumb, 16, 16);\n } catch (_) {}\n }\n });\n\n container.addEventListener('dragover', (e) => {\n if (dragSrcIndex == null) return;\n e.preventDefault();\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'move';\n const targetThumb = e.target && e.target.closest && e.target.closest('.lp-thumbnail');\n const indicator = getOrCreateDropIndicator();\n if (!targetThumb) {\n const lastThumb = container.querySelector('.lp-thumbnail:last-child');\n if (lastThumb && dragSrcIndex !== goal.slides.length - 1) {\n positionDropIndicator(lastThumb, false);\n } else {\n indicator.classList.remove('visible');\n }\n thumbnails.forEach((t) => t.classList.remove('drag-over'));\n return;\n }\n const targetIndex = Number(targetThumb.dataset.index);\n if (Number.isNaN(targetIndex)) return;\n const rect = targetThumb.getBoundingClientRect();\n const before = e.clientY < rect.top + rect.height / 2;\n if (targetIndex === dragSrcIndex) {\n indicator.classList.remove('visible');\n targetThumb.classList.remove('drag-over');\n return;\n }\n thumbnails.forEach((t) => t.classList.remove('drag-over'));\n targetThumb.classList.add('drag-over');\n positionDropIndicator(targetThumb, before);\n });\n\n container.addEventListener('dragleave', (e) => {\n if (dragSrcIndex == null) return;\n if (!container.contains(e.relatedTarget)) {\n if (dragDropIndicator) dragDropIndicator.classList.remove('visible');\n thumbnails.forEach((t) => t.classList.remove('drag-over'));\n }\n });\n\n container.addEventListener('drop', (e) => {\n if (dragSrcIndex == null) return;\n e.preventDefault();\n const src = dragSrcIndex;\n const targetThumb = e.target && e.target.closest && e.target.closest('.lp-thumbnail');\n let targetIndex = 0;\n let insertBefore = true;\n if (targetThumb) {\n targetIndex = Number(targetThumb.dataset.index);\n const rect = targetThumb.getBoundingClientRect();\n insertBefore = e.clientY < rect.top + rect.height / 2;\n } else {\n targetIndex = goal.slides.length - 1;\n insertBefore = false;\n }\n if (Number.isNaN(targetIndex) || src === targetIndex) {\n clearDragState();\n return;\n }\n let insertAt = targetIndex;\n if (insertBefore) {\n if (src < targetIndex) insertAt = targetIndex - 1;\n } else {\n if (src > targetIndex) insertAt = targetIndex + 1;\n }\n const activeSlide = goal.slides[current];\n const movingSlide = goal.slides[src];\n const newSlides = goal.slides.filter((_, i) => i !== src);\n insertAt = Math.max(0, Math.min(insertAt, newSlides.length));\n newSlides.splice(insertAt, 0, movingSlide);\n goal.slides = newSlides;\n recordHistory();\n selectedSlideIdx = goal.slides.indexOf(activeSlide);\n current = selectedSlideIdx;\n autoSave();\n rebuildSlidesAndThumbnails();\n if (typeof selectSlide === 'function') selectSlide(current);\n clearDragState();\n });\n\n container.addEventListener('dragend', () => {\n clearDragState();\n });\n }\n attachDragAndDropListeners();\n\n function deleteSlide(index) {\n if (goal.slides.length <= 1) {\n alert('\u81F3\u5C11\u4FDD\u7559\u4E00\u9875\u5E7B\u706F\u7247\u3002');\n return;\n }\n const deleteBtn = document.querySelector('.lp-thumbnail-delete[data-index=\"' + index + '\"]');\n showDeleteConfirm(index, deleteBtn);\n }\n\n document.addEventListener('keydown', (e) => {\n if (isEditingTarget(e.target) || isEditingTarget(document.activeElement)) return;\n const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;\n const mod = isMac ? e.metaKey : e.ctrlKey;\n\n // \u5173\u95ED\u5220\u9664\u786E\u8BA4\u6C14\u6CE1\n if (e.key === 'Escape' && pendingDeleteIndex != null) {\n e.preventDefault();\n hideDeleteConfirm();\n return;\n }\n\n // \u64A4\u9500 / \u91CD\u505A\n if (mod && e.key.toLowerCase() === 'z') {\n e.preventDefault();\n if (e.shiftKey) redo();\n else undo();\n return;\n }\n if (mod && e.key.toLowerCase() === 'y') {\n e.preventDefault();\n redo();\n return;\n }\n\n // \u7FFB\u9875\u5BFC\u822A\n if (isTransitioning || slides.length === 0) return;\n if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') {\n e.preventDefault();\n goTo(current + 1);\n }\n if (e.key === 'ArrowLeft' || e.key === 'PageUp') {\n e.preventDefault();\n goTo(current - 1);\n }\n if (e.key === 'Home') {\n e.preventDefault();\n goTo(0);\n }\n if (e.key === 'End') {\n e.preventDefault();\n goTo(slides.length - 1);\n }\n });\n\n updateClasses();\n\n // \u7F29\u653E\u63A7\u5236\n const stage = document.querySelector('.lp-editor-stage');\n const scaler = document.querySelector('.lp-editor-stage-scaler');\n const editorRoot = document.querySelector('.lp-editor-root');\n const zoomSlider = document.getElementById('lp-zoom-slider');\n const zoomValue = document.getElementById('lp-zoom-value');\n const zoomOutBtn = document.getElementById('lp-zoom-out');\n const zoomInBtn = document.getElementById('lp-zoom-in');\n const zoomFitBtn = document.getElementById('lp-zoom-fit');\n let userZoom = null;\n\n const SLIDE_WIDTH = 1280;\n const SLIDE_HEIGHT = 720;\n\n function fitScale() {\n if (!stage || !scaler) return 1;\n return Math.min(stage.clientWidth / SLIDE_WIDTH, stage.clientHeight / SLIDE_HEIGHT) * 0.92;\n }\n\n function updateScale() {\n if (!stage || !scaler) return;\n if (editorRoot && editorRoot.classList.contains('lp-editor-presentation-mode')) {\n const scale = Math.min(window.innerWidth / SLIDE_WIDTH, window.innerHeight / SLIDE_HEIGHT);\n scaler.style.transform = 'scale(' + Math.max(scale, 0.35) + ')';\n return;\n }\n const scale = userZoom == null ? fitScale() : userZoom;\n scaler.style.transform = 'scale(' + Math.max(scale, 0.35) + ')';\n scaler.style.removeProperty('--lp-presentation-scale');\n if (zoomValue) zoomValue.textContent = Math.round(scale * 100) + '%';\n if (zoomSlider && userZoom != null) zoomSlider.value = String(Math.round(scale * 100));\n }\n\n if (zoomSlider) {\n zoomSlider.addEventListener('input', () => {\n userZoom = Number(zoomSlider.value) / 100;\n updateScale();\n });\n }\n if (zoomOutBtn) {\n zoomOutBtn.addEventListener('click', () => {\n userZoom = (userZoom == null ? fitScale() : userZoom) - 0.1;\n if (userZoom < 0.35) userZoom = 0.35;\n updateScale();\n });\n }\n if (zoomInBtn) {\n zoomInBtn.addEventListener('click', () => {\n userZoom = (userZoom == null ? fitScale() : userZoom) + 0.1;\n if (userZoom > 1.5) userZoom = 1.5;\n updateScale();\n });\n }\n if (zoomFitBtn) {\n zoomFitBtn.addEventListener('click', () => {\n userZoom = null;\n updateScale();\n });\n }\n window.addEventListener('resize', updateScale);\n updateScale();\n\n // \u64AD\u653E\uFF1A\u8FDB\u5165\u5168\u5C4F\u6F14\u793A\u6A21\u5F0F\uFF0C\u4F7F\u7528\u5F53\u524D\u7F16\u8F91\u72B6\u6001\u76F4\u63A5\u64AD\u653E\n const playBtn = document.getElementById('lp-play');\n let presentationExitBtn = null;\n let presentationKeyHandler = null;\n\n let isExitingPresentation = false;\n\n function exitPresentationMode() {\n if (!editorRoot || isExitingPresentation) return;\n isExitingPresentation = true;\n editorRoot.classList.remove('lp-editor-presentation-mode');\n if (presentationExitBtn && presentationExitBtn.parentNode) {\n presentationExitBtn.parentNode.removeChild(presentationExitBtn);\n presentationExitBtn = null;\n }\n if (presentationKeyHandler) {\n document.removeEventListener('keydown', presentationKeyHandler);\n presentationKeyHandler = null;\n }\n const done = () => {\n isExitingPresentation = false;\n updateScale();\n };\n if (document.fullscreenElement && document.exitFullscreen) {\n document.exitFullscreen().catch(() => {}).finally(done);\n } else {\n done();\n }\n }\n\n async function enterPresentationMode() {\n if (!editorRoot) return;\n editorRoot.classList.add('lp-editor-presentation-mode');\n updateScale();\n try {\n const target = editorRoot.requestFullscreen ? editorRoot : document.documentElement;\n if (target.requestFullscreen) {\n await target.requestFullscreen();\n }\n } catch (err) {\n console.warn('\u8FDB\u5165\u5168\u5C4F\u5931\u8D25\uFF0C\u5C06\u4EE5\u7A97\u53E3\u6F14\u793A\u6A21\u5F0F\u64AD\u653E', err);\n }\n if (!presentationExitBtn) {\n presentationExitBtn = document.createElement('button');\n presentationExitBtn.className = 'lp-editor-presentation-exit';\n presentationExitBtn.textContent = '\u9000\u51FA\u6F14\u793A (Esc)';\n presentationExitBtn.type = 'button';\n presentationExitBtn.addEventListener('click', exitPresentationMode);\n document.body.appendChild(presentationExitBtn);\n }\n if (!presentationKeyHandler) {\n presentationKeyHandler = (e) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n exitPresentationMode();\n }\n };\n document.addEventListener('keydown', presentationKeyHandler);\n }\n }\n\n document.addEventListener('fullscreenchange', () => {\n if (!document.fullscreenElement && editorRoot && editorRoot.classList.contains('lp-editor-presentation-mode')) {\n exitPresentationMode();\n }\n });\n\n if (playBtn) {\n playBtn.addEventListener('click', enterPresentationMode);\n }\n\n // \u5185\u5BB9\u7ED3\u6784\u53D8\u66F4\u540E\u91CD\u65B0\u6E32\u67D3\u5F53\u524D\u7F16\u8F91\u5668\uFF08\u4EC5\u7528\u4E8E\u6570\u7EC4\u589E\u5220\u7B49\u6A21\u677F\u5185\u5BB9\u8C03\u6574\uFF09\n function saveCurrentForReload() {\n try {\n localStorage.setItem('lemonppt:editor:currentSlide', String(selectedSlideIdx));\n } catch (e) {}\n }\n\n function getSlideLabel(slide) {\n const props = slide.props || {};\n const candidates = [\n props.title,\n props.name,\n props.kicker,\n props.quote,\n props.heading,\n props.items && Array.isArray(props.items) ? props.items[0] : undefined,\n props.metric,\n props.value,\n ];\n for (const c of candidates) {\n if (c !== undefined && c !== null && String(c).trim()) return String(c).trim();\n }\n return slide.layout;\n }\n\n // \u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\uFF0C\u589E\u5220\u5E7B\u706F\u7247\u540E\u4E0D\u518D\u4F7F\u7528 document.write \u91CD\u5199\u6574\u9875\uFF08\u4F1A\u4E2D\u65AD CSS/\u8D44\u6E90\u52A0\u8F7D\uFF09\uFF0C\n // \u800C\u662F\u76F4\u63A5\u5728\u73B0\u6709 DOM \u4E2D\u91CD\u5EFA slide \u5BB9\u5668\u548C\u7F29\u7565\u56FE\uFF0C\u5E76\u590D\u7528 React root \u91CD\u65B0\u6E32\u67D3\u5185\u5BB9\u3002\n function rebuildSlidesAndThumbnails() {\n if (!deck) return;\n if (typeof window.__lemonPPT_renderSlideHtml !== 'function') return;\n\n // DOM \u91CD\u5EFA\u524D\u5C06 current \u540C\u6B65\u5230\u5408\u6CD5\u8303\u56F4\uFF0C\u907F\u514D\u540E\u7EED goTo/updateClasses \u8D8A\u754C\n current = Math.max(0, Math.min(selectedSlideIdx, goal.slides.length - 1));\n\n const firstWrapper = slides[0];\n const width = firstWrapper ? parseInt(firstWrapper.style.width, 10) || 1280 : 1280;\n const height = firstWrapper ? parseInt(firstWrapper.style.height, 10) || 720 : 720;\n const theme = goal.theme || 'theme01';\n\n // \u91CD\u5EFA deck \u4E2D\u7684 slide \u5BB9\u5668\n const wrappersHtml = goal.slides.map((slide, index) => {\n const slideHtml = window.__lemonPPT_renderSlideHtml(slide, { slideIdx: index, editable: true, theme });\n const stateClass = index === current ? 'active' : (index < current ? 'prev' : '');\n const transition = String((slide.props && slide.props.transition) || 'none');\n return '<div class=\"lp-slide-wrapper ' + stateClass + '\" data-slide-index=\"' + index + '\" data-layout=\"' + slide.layout + '\" data-lp-transition=\"' + transition + '\" style=\"width:' + width + 'px;height:' + height + 'px;box-sizing:border-box;\">' + slideHtml + '</div>';\n }).join('');\n deck.innerHTML = wrappersHtml;\n\n // \u91CD\u5EFA\u5DE6\u4FA7\u7F29\u7565\u56FE\n const thumbnailsContainer = document.querySelector('.lp-editor-thumbnails');\n if (thumbnailsContainer) {\n const thumbInnerWidth = 156;\n const scale = thumbInnerWidth / width;\n const showDragHandle = goal.slides.length > 1;\n const dragHandleHtml = showDragHandle\n ? '<span class=\"lp-thumbnail-drag-handle\" data-lp-action=\"drag-handle\" aria-hidden=\"true\">' +\n '<svg width=\"12\" height=\"16\" viewBox=\"0 0 12 16\" fill=\"currentColor\" aria-hidden=\"true\">' +\n '<circle cx=\"2.5\" cy=\"2.5\" r=\"1.5\"/>' +\n '<circle cx=\"9.5\" cy=\"2.5\" r=\"1.5\"/>' +\n '<circle cx=\"2.5\" cy=\"8\" r=\"1.5\"/>' +\n '<circle cx=\"9.5\" cy=\"8\" r=\"1.5\"/>' +\n '<circle cx=\"2.5\" cy=\"13.5\" r=\"1.5\"/>' +\n '<circle cx=\"9.5\" cy=\"13.5\" r=\"1.5\"/>' +\n '</svg>' +\n '</span>'\n : '';\n const buttonsHtml = goal.slides.map((slide, index) => {\n const label = getSlideLabel(slide);\n const activeClass = index === current ? 'active' : '';\n const slideHtml = window.__lemonPPT_renderSlideHtml(slide, { slideIdx: index, editable: true, theme });\n const draggableAttr = goal.slides.length > 1 ? 'draggable=\"true\"' : '';\n return '<div class=\"lp-thumbnail ' + activeClass + '\" data-index=\"' + index + '\" role=\"button\" tabindex=\"0\" ' + draggableAttr + ' aria-label=\"\u5E7B\u706F\u7247 ' + (index + 1) + '\uFF0C\u62D6\u52A8\u53EF\u8C03\u6574\u987A\u5E8F\">' +\n dragHandleHtml +\n '<div class=\"lp-thumbnail-render\">' +\n '<div class=\"lp-thumbnail-scaler\" style=\"width:' + width + 'px;height:' + height + 'px;transform:scale(' + scale + ');transform-origin:top left;\">' + slideHtml + '</div>' +\n '</div>' +\n '<div class=\"lp-thumbnail-scrim\"></div>' +\n '<div class=\"lp-thumbnail-content\">' +\n '<div class=\"lp-thumbnail-index\">' + (index + 1) + ' / ' + goal.slides.length + '</div>' +\n '<div class=\"lp-thumbnail-title\">' + escapeHtml(label) + '</div>' +\n '<div class=\"lp-thumbnail-layout\">' + escapeHtml(slide.layout) + '</div>' +\n '</div>' +\n '<span class=\"lp-thumbnail-delete\" data-lp-action=\"delete-slide\" data-index=\"' + index + '\" title=\"\u5220\u9664\u5E7B\u706F\u7247\" aria-label=\"\u5220\u9664\u5E7B\u706F\u7247\">\u00D7</span>' +\n '</div>';\n }).join('');\n thumbnailsContainer.innerHTML = buttonsHtml;\n // \u7F29\u7565\u56FE DOM \u91CD\u5EFA\u540E\u6E05\u7A7A\u865A\u62DF\u6EDA\u52A8\u7F13\u5B58\uFF0C\u907F\u514D\u65E7 HTML \u4E0E\u65B0\u7D22\u5F15\u9519\u4F4D\n Object.keys(thumbnailRenderCache).forEach((key) => {\n delete thumbnailRenderCache[key];\n });\n }\n\n // \u540C\u6B65\u9875\u7801\u8BA1\u6570\u5668\n const pageCounter = document.querySelector('.lp-editor-page-counter');\n if (pageCounter) {\n pageCounter.innerHTML = '<span id=\"lp-current\">' + (current + 1) + '</span> / ' + goal.slides.length;\n }\n\n refreshSlidesAndThumbnails();\n attachThumbnailListeners();\n updateClasses();\n renderAllSlidesToRoot();\n initThumbnailVirtualScroll();\n }\n\n function refreshCurrentSlide() {\n if (typeof window.__lemonPPT_renderSlideToRoot === 'function') {\n renderCurrentSlideToRoot();\n } else {\n // \u515C\u5E95\uFF1A\u65E7\u7248\u9759\u6001 HTML \u66FF\u6362\n const wrapper = document.querySelector('.lp-slide-wrapper.active');\n if (!wrapper) return;\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n const html = window.__lemonPPT_renderSlideHtml(slide, { slideIdx: selectedSlideIdx, editable: true, theme: goal.theme });\n wrapper.innerHTML = html;\n initActiveSlideECharts();\n }\n }\n\n async function reloadEditor() {\n saveCurrentForReload();\n\n // \u4F18\u5148\u5C1D\u8BD5\u670D\u52A1\u5668\u7AEF\u91CD\u65B0\u6E32\u67D3\uFF08\u5F00\u53D1\u670D\u52A1\u5668\u6A21\u5F0F\uFF09\n try {\n const res = await fetch('/api/render-editor', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(goal),\n });\n if (!res.ok) throw new Error('\u91CD\u65B0\u6E32\u67D3\u5931\u8D25: ' + res.status);\n window.location.reload();\n return;\n } catch (err) {\n // \u9759\u6001\u6587\u4EF6\u6A21\u5F0F\u4E0B\uFF0C\u76F4\u63A5\u5728\u5F53\u524D DOM \u4E2D\u91CD\u5EFA slide \u5BB9\u5668\u548C\u7F29\u7565\u56FE\uFF0C\n // \u907F\u514D document.write \u4E2D\u65AD CSS/\u8D44\u6E90\u52A0\u8F7D\u5BFC\u81F4\u5E7B\u706F\u7247\u5185\u5BB9\u65E0\u6CD5\u6E32\u67D3\u3002\n if (typeof window.__lemonPPT_disposeECharts === 'function') {\n window.__lemonPPT_disposeECharts();\n }\n rebuildSlidesAndThumbnails();\n initActiveSlideECharts();\n }\n }\n\n // \u5143\u7D20\u9009\u4E2D\u4E0E\u53F3\u4FA7\u5C5E\u6027\u9762\u677F\n let selectedEl = null;\n let selectedSlideIdx = 0;\n const propertyContent = document.getElementById('lp-property-content');\n const layoutSchemas = window.__lemonPPT_layoutSchemas || {};\n\n const FIELD_LABELS = {\n kicker: '\u6807\u7B7E',\n title: '\u6807\u9898',\n subtitle: '\u526F\u6807\u9898',\n date: '\u65E5\u671F',\n quote: '\u5F15\u7528',\n author: '\u4F5C\u8005',\n role: '\u804C\u4F4D',\n company: '\u516C\u53F8',\n value: '\u6570\u503C',\n unit: '\u5355\u4F4D',\n label: '\u6307\u6807\u540D',\n description: '\u8BF4\u660E',\n change: '\u53D8\u5316',\n imageUrl: '\u56FE\u7247',\n url: '\u56FE\u7247',\n logoUrl: 'Logo',\n avatarUrl: '\u5934\u50CF',\n caption: '\u8BF4\u660E',\n q: '\u95EE\u9898',\n a: '\u56DE\u7B54',\n question: '\u95EE\u9898',\n answer: '\u56DE\u7B54',\n name: '\u540D\u79F0',\n price: '\u4EF7\u683C',\n period: '\u5468\u671F',\n cta: '\u6309\u94AE\u6587\u6848',\n type: '\u56FE\u8868\u7C7B\u578B',\n status: '\u72B6\u6001',\n bio: '\u7B80\u4ECB',\n items: '\u76EE\u5F55\u9879',\n points: '\u8981\u70B9',\n leftPoints: '\u5DE6\u4FA7\u8981\u70B9',\n rightPoints: '\u53F3\u4FA7\u8981\u70B9',\n features: '\u7279\u6027',\n steps: '\u6B65\u9AA4',\n stats: '\u7EDF\u8BA1\u6570\u636E',\n members: '\u6210\u5458',\n milestones: '\u91CC\u7A0B\u7891',\n phases: '\u9636\u6BB5',\n plans: '\u65B9\u6848',\n tiers: '\u5957\u9910',\n images: '\u56FE\u7247',\n partners: '\u5408\u4F5C\u4F19\u4F34',\n datasets: '\u6570\u636E\u96C6',\n metrics: '\u6307\u6807',\n cards: '\u5361\u7247',\n highlighted: '\u9AD8\u4EAE',\n columns: '\u4E3B\u9898\u5217',\n panels: '\u6307\u6807\u9762\u677F',\n left: '\u5DE6\u4FA7\u5185\u5BB9',\n right: '\u53F3\u4FA7\u5185\u5BB9',\n centerCard: '\u4E2D\u592E\u7ED3\u8BBA\u5361',\n comparisons: '\u5BF9\u6BD4\u9879',\n conclusion: '\u7ED3\u8BBA',\n chartType: '\u56FE\u8868\u7C7B\u578B',\n chartData: '\u56FE\u8868\u6570\u636E',\n footnote: '\u811A\u6CE8',\n source: '\u6765\u6E90',\n number: '\u5E8F\u53F7',\n context: '\u4E0A\u4E0B\u6587',\n showInsight: '\u91CD\u70B9\u5F3A\u8C03',\n insight: '\u6D1E\u5BDF\u9762\u677F',\n previous: '\u539F\u6570\u503C',\n current: '\u73B0\u6570\u503C',\n max: '\u6700\u5927\u503C',\n tone: '\u8272\u8C03',\n percent: '\u5360\u6BD4',\n color: '\u989C\u8272',\n rank: '\u6392\u540D',\n company: '\u516C\u53F8',\n track: '\u8D5B\u9053',\n amount: '\u91D1\u989D',\n highlightRow: '\u9AD8\u4EAE\u884C',\n highlightFirstColumn: '\u9AD8\u4EAE\u9996\u5217',\n leftTitle: '\u5DE6\u4FA7\u6807\u9898',\n rightTitle: '\u53F3\u4FA7\u6807\u9898',\n leftValue: '\u5DE6\u4FA7\u6570\u503C',\n rightValue: '\u53F3\u4FA7\u6570\u503C',\n leftLabel: '\u5DE6\u4FA7\u6807\u7B7E',\n rightLabel: '\u53F3\u4FA7\u6807\u7B7E',\n size: '\u5C3A\u5BF8',\n icon: '\u56FE\u6807',\n page: '\u9875\u7801',\n image: '\u56FE\u7247',\n avatar: '\u5934\u50CF',\n logo: 'Logo',\n };\n\n const LAYOUT_LABELS = {\n cover_v1: '\u5C01\u9762',\n cover_v2: '\u5C01\u9762\uFF08\u5C45\u4E2D\uFF09',\n cover_v3: '\u5C01\u9762\uFF08\u5168\u56FE\uFF09',\n table_of_contents_v1: '\u76EE\u5F55',\n table_of_contents_v2: '\u76EE\u5F55\uFF08\u5361\u7247\uFF09',\n content_v1: '\u5185\u5BB9\u5217\u8868',\n content_v2: '\u5185\u5BB9\u5BF9\u6BD4',\n content_v3: '\u5185\u5BB9\u6BB5\u843D',\n content_v4: '\u5185\u5BB9\u5361\u7247',\n chart_v1: '\u56FE\u8868',\n chart_v2: '\u591A\u7CFB\u5217\u56FE\u8868',\n process_v1: '\u6D41\u7A0B\u6B65\u9AA4',\n process_v2: '\u6D41\u7A0B\u65F6\u95F4\u7EBF',\n process_v3: '\u6D41\u7A0B\u5361\u7247',\n stats_v1: '\u6570\u636E\u6307\u6807',\n stats_v2: '\u6570\u636E\u6307\u6807\uFF08\u5361\u7247\uFF09',\n metric_v1: '\u6838\u5FC3\u6307\u6807',\n metric_v2: '\u6838\u5FC3\u6307\u6807\uFF08\u5927\u6570\u5B57\uFF09',\n metric_v3: '\u53CC\u6307\u6807\u5BF9\u6BD4',\n feature_v1: '\u7279\u6027\u5217\u8868',\n feature_v2: '\u7279\u6027\u7F51\u683C',\n feature_v3: '\u7279\u6027\u5361\u7247',\n team_v1: '\u56E2\u961F\u4ECB\u7ECD',\n team_v2: '\u56E2\u961F\u5361\u7247',\n timeline_v1: '\u65F6\u95F4\u7EBF',\n timeline_v2: '\u65F6\u95F4\u7EBF\uFF08\u6A2A\u5411\uFF09',\n timeline_v3: '\u65F6\u95F4\u7EBF\uFF08\u91CC\u7A0B\u7891\uFF09',\n roadmap_v1: '\u8DEF\u7EBF\u56FE',\n faq_v1: '\u95EE\u7B54',\n partners_v1: '\u5408\u4F5C\u4F19\u4F34',\n gallery_v1: '\u56FE\u7247\u96C6',\n gallery_v2: '\u56FE\u7247\u96C6\uFF08\u7F51\u683C\uFF09',\n pricing_v1: '\u5B9A\u4EF7',\n pricing_v2: '\u5B9A\u4EF7\u65B9\u6848',\n closing_v2: '\u7ED3\u675F\u9875',\n quote_v1: '\u5F15\u7528\u9875',\n testimonial_v1: '\u63A8\u8350\u8BED',\n metric_big: '\u5927\u6570\u5B57\u9875',\n metric_triptych: '\u4E09\u6307\u6807\u603B\u89C8',\n chart_donut: '\u73AF\u5F62\u56FE\u62C6\u89E3',\n chart_funnel: '\u6F0F\u6597\u56FE',\n table_data: '\u6570\u636E\u8868\u683C',\n case_study: '\u6848\u4F8B\u8BE6\u60C5',\n spotlight_grid: '\u4E3B\u9898\u805A\u7126\u7F51\u683C',\n diptych_contrast: '\u53CC\u8054\u5BF9\u6BD4',\n conclusion_v1: '\u7ED3\u8BBA\u9875',\n table_v1: '\u8868\u683C',\n ranking_v1: '\u6392\u884C\u699C',\n region_v1: '\u5730\u533A\u5206\u5E03',\n risk_v1: '\u98CE\u9669\u7814\u5224',\n outlook_v1: '\u6295\u8D44\u5C55\u671B',\n bento_v1: 'Bento \u7F51\u683C',\n gallery_v1: '\u56FE\u7247\u96C6',\n gantt_v1: '\u7518\u7279\u56FE',\n quadrant_v1: '\u56DB\u8C61\u9650',\n scorecard_v1: '\u8BC4\u5206\u5361',\n tags_v1: '\u6807\u7B7E\u4E91',\n filmstrip_v1: '\u80F6\u7247\u6761',\n swot_v1: 'SWOT \u5206\u6790',\n pest_v1: 'PEST \u5206\u6790',\n appendix_v1: '\u9644\u5F55',\n image_v1: '\u56FE\u7247\u9875',\n process_v1: '\u6D41\u7A0B\u6B65\u9AA4',\n faq_v1: '\u95EE\u7B54',\n partners_v1: '\u5408\u4F5C\u4F19\u4F34',\n pricing_v1: '\u5B9A\u4EF7',\n team_v1: '\u56E2\u961F\u4ECB\u7ECD',\n team_v2: '\u56E2\u961F\u5361\u7247',\n timeline_v1: '\u65F6\u95F4\u7EBF',\n roadmap_v1: '\u8DEF\u7EBF\u56FE',\n chapter_v1: '\u7AE0\u8282\u9875',\n chapter_v2: '\u7AE0\u8282\u9875\uFF08\u5168\u56FE\uFF09',\n chapter_v3: '\u7AE0\u8282\u9875\uFF08\u5361\u7247\uFF09',\n cover_v3: '\u5C01\u9762\uFF08\u5168\u56FE\uFF09',\n cover_v4: '\u5C01\u9762\uFF08\u6742\u5FD7\uFF09',\n chart_bar3d: '3D \u67F1\u72B6\u56FE',\n chart_gauge: '\u4EEA\u8868\u76D8',\n chart_graph: '\u5173\u7CFB\u56FE',\n chart_heatmap: '\u70ED\u529B\u56FE',\n chart_radar: '\u96F7\u8FBE\u56FE',\n chart_sankey: '\u6851\u57FA\u56FE',\n chart_sunburst: '\u65ED\u65E5\u56FE',\n chart_treemap: '\u77E9\u5F62\u6811\u56FE',\n chart_wordcloud: '\u8BCD\u4E91',\n content_v4: '\u5185\u5BB9\u5361\u7247',\n metrics_v1: '\u6307\u6807\u5899',\n delta_v1: '\u4ECA\u6614\u5BF9\u7167',\n comparison_v1: '\u5BF9\u6BD4\u5206\u6790',\n progress_v1: '\u8FDB\u5EA6\u6761',\n number_showcase_v1: '\u6570\u5B57\u79C0',\n testimonial_v1: '\u63A8\u8350\u8BED',\n closing_v1: '\u7ED3\u675F\u9875',\n };\n\n function getLayoutLabel(layoutId) {\n if (LAYOUT_LABELS[layoutId]) return LAYOUT_LABELS[layoutId];\n const shortId = String(layoutId).replace(/^themed+_/, '');\n return LAYOUT_LABELS[shortId] || layoutId;\n }\n\n function resolveLayoutSchema(slide) {\n if (layoutSchemas[slide.layout]) return layoutSchemas[slide.layout];\n const theme = goal.theme || 'theme01';\n const shortId = String(slide.layout).replace(/^themed+_/, '');\n return layoutSchemas[theme + '_' + shortId] || null;\n }\n\n function getFieldLabel(path) {\n const key = String(path).split('.').pop() || '';\n return FIELD_LABELS[key] || key;\n }\n\n function inferFieldType(path, value) {\n const key = String(path).split('.').pop() || '';\n if (/image|url|logo|avatar/i.test(key) && typeof value === 'string') return 'image';\n if (typeof value === 'boolean') return 'boolean';\n if (typeof value === 'number') return 'number';\n if (key === 'type' && ['bar', 'line', 'pie'].includes(String(value))) return 'select';\n if (key === 'status') return 'select';\n if (['description', 'bio', 'quote', 'answer', 'a', 'subtitle'].includes(key)) return 'textarea';\n return 'text';\n }\n\n function getSelectOptions(path) {\n const key = String(path).split('.').pop() || '';\n if (key === 'type') return [{ value: 'bar', label: '\u67F1\u72B6' }, { value: 'line', label: '\u6298\u7EBF' }, { value: 'pie', label: '\u997C\u56FE' }];\n if (key === 'status') return [{ value: '\u5DF2\u5B8C\u6210', label: '\u5DF2\u5B8C\u6210' }, { value: '\u8FDB\u884C\u4E2D', label: '\u8FDB\u884C\u4E2D' }, { value: '\u89C4\u5212\u4E2D', label: '\u89C4\u5212\u4E2D' }];\n return [];\n }\n\n function clearSelection() {\n if (selectedEl) {\n selectedEl.classList.remove('lp-selected');\n selectedEl = null;\n }\n selectedSlideIdx = current;\n renderSlidePanel();\n }\n\n function setField(path, value) {\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n recordHistory();\n setProp(slide.props, path, value);\n // \u6253\u5F00\u4F9D\u8D56\u5F00\u5173\u65F6\uFF0C\u5982\u679C\u88AB\u63A7\u5B57\u6BB5\u4E3A\u7A7A\u4E14\u914D\u6709\u9ED8\u8BA4\u503C\uFF0C\u5219\u81EA\u52A8\u586B\u5145\u6D4B\u8BD5\u6570\u636E\n if (value === true) {\n const schema = resolveLayoutSchema(slide);\n if (schema && schema.fields) {\n schema.fields.forEach((field) => {\n if (field.visibleWhen && field.visibleWhen.key === path && field.visibleWhen.value === true && field.defaultValue !== undefined) {\n const current = getProp(slide.props, field.key);\n const isEmpty = current == null || (typeof current === 'object' && Object.keys(current).length === 0);\n if (isEmpty) {\n setProp(slide.props, field.key, deepClone(field.defaultValue));\n }\n }\n });\n }\n }\n syncDomFromGoal(path);\n autoSave();\n // \u5982\u679C\u8BE5\u5B57\u6BB5\u5728\u753B\u5E03\u4E0A\u6CA1\u6709\u5BF9\u5E94\u7684\u76F4\u63A5\u7F16\u8F91\u5143\u7D20\uFF08\u5982 SVG \u56FE\u8868\u6570\u636E\uFF09\uFF0C\u5237\u65B0\u6574\u9875\u5E7B\u706F\u7247\u3002\n // \u5BF9\u4E8E\u6807\u8BB0\u4E3A data-lp-chart-data \u7684\u56FE\u8868\u6570\u636E\u5B57\u6BB5\uFF0C\u5373\u4F7F\u753B\u5E03\u4E0A\u53EF\u76F4\u63A5\u7F16\u8F91\uFF0C\u4E5F\u9700\u8981\n // \u91CD\u65B0\u6E32\u67D3\u56FE\u8868\u4EE5\u540C\u6B65\u6570\u636E\u53D8\u5316\u3002\n if (!hasEditableElementForPath(path) || hasChartDataElementForPath(path)) {\n refreshCurrentSlide();\n }\n }\n\n function hasEditableElementForPath(path) {\n const selector = '[data-lp-editable=\"true\"][data-lp-slide-idx=\"' + selectedSlideIdx + '\"][data-lp-prop=\"' + path + '\"]';\n return document.querySelector(selector) !== null;\n }\n\n function hasChartDataElementForPath(path) {\n const selector = '[data-lp-editable=\"true\"][data-lp-chart-data=\"true\"][data-lp-slide-idx=\"' + selectedSlideIdx + '\"][data-lp-prop=\"' + path + '\"]';\n return document.querySelector(selector) !== null;\n }\n\n function createEl(tag, className, parent) {\n const el = document.createElement(tag);\n if (className) el.className = className;\n if (parent) parent.appendChild(el);\n return el;\n }\n\n function createTextField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const input = createEl('input', 'lp-property-input', wrap);\n input.type = 'text';\n input.value = value == null ? '' : String(value);\n input.addEventListener('input', () => onChange(input.value));\n return wrap;\n }\n\n function createTextareaField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const textarea = createEl('textarea', 'lp-property-textarea', wrap);\n textarea.value = value == null ? '' : String(value);\n textarea.addEventListener('input', () => onChange(textarea.value));\n return wrap;\n }\n\n function createNumberField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const input = createEl('input', 'lp-property-input', wrap);\n input.type = 'number';\n input.value = value == null ? '' : String(value);\n input.addEventListener('input', () => onChange(Number(input.value)));\n return wrap;\n }\n\n function renderSliderTicks(ruler, min, max) {\n const scale = createEl('div', 'lp-property-slider-scale', ruler);\n const span = max - min;\n if (span <= 0) return scale;\n // \u523B\u5EA6\u6B65\u957F\uFF1A\u8303\u56F4\u5C0F\u65F6\u6BCF\u4E2A\u6574\u6570\u4E00\u4E2A\u523B\u5EA6\uFF0C\u8303\u56F4\u5927\u65F6\u5747\u5300\u53D6 6~10 \u4E2A\n const desiredTicks = 8;\n const step = span <= desiredTicks ? 1 : Math.max(1, Math.round(span / desiredTicks));\n for (let i = min; i <= max; i += step) {\n const tick = createEl('div', 'lp-property-slider-tick', scale);\n tick.style.left = ((i - min) / span) * 100 + '%';\n const label = createEl('div', 'lp-property-slider-tick-label', tick);\n label.textContent = String(i);\n tick.addEventListener('click', () => {\n const input = ruler.querySelector('input[type=\"range\"]');\n if (input) {\n input.value = String(i);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }\n });\n }\n return scale;\n }\n\n function createSliderField(field, value, onChange) {\n const wrap = createEl('div', 'lp-property-slider-field');\n const header = createEl('div', 'lp-property-section-header', wrap);\n createEl('div', 'lp-property-section-title', header).textContent = field.label;\n const countBadge = createEl('span', 'lp-property-slider-value', header);\n const initialValue = value == null ? (field.min || 0) : value;\n countBadge.textContent = String(initialValue);\n\n const min = typeof field.min === 'number' ? field.min : 0;\n const max = typeof field.max === 'number' ? field.max : 100;\n\n const sliderWrap = createEl('div', 'lp-property-slider-wrap', wrap);\n const ruler = createEl('div', 'lp-property-slider-ruler', sliderWrap);\n const range = document.createElement('input');\n range.type = 'range';\n range.className = 'lp-property-slider';\n range.min = String(min);\n range.max = String(max);\n range.value = String(initialValue);\n ruler.appendChild(range);\n renderSliderTicks(ruler, min, max);\n\n range.addEventListener('input', () => {\n const num = Number(range.value);\n countBadge.textContent = String(num);\n onChange(num);\n });\n\n return wrap;\n }\n\n function createToggleField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n const labelEl = createEl('label', 'lp-property-toggle', wrap);\n const text = createEl('span', '', labelEl);\n text.textContent = label;\n const input = createEl('input', '', labelEl);\n input.type = 'checkbox';\n input.checked = !!value;\n const track = createEl('div', 'lp-property-toggle-track', labelEl);\n createEl('div', 'lp-property-toggle-thumb', track);\n input.addEventListener('change', () => onChange(input.checked));\n return wrap;\n }\n\n function createSelectField(label, value, options, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const segmented = createEl('div', 'lp-property-segmented', wrap);\n options.forEach((opt) => {\n const btn = createEl('button', '', segmented);\n btn.textContent = opt.label;\n btn.type = 'button';\n if (opt.value === value) btn.classList.add('active');\n btn.addEventListener('click', () => {\n Array.from(segmented.children).forEach((b) => b.classList.remove('active'));\n btn.classList.add('active');\n onChange(opt.value);\n });\n });\n return wrap;\n }\n\n function createImageHintField(label) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const hint = createEl('div', 'lp-property-help', wrap);\n hint.textContent = '\u8BF7\u70B9\u51FB\u753B\u5E03\u4E2D\u7684\u56FE\u7247\u533A\u57DF\u76F4\u63A5\u4E0A\u4F20';\n return wrap;\n }\n\n function createColorField(label, value, onChange) {\n const wrap = createEl('div', 'lp-property-field');\n createEl('label', 'lp-property-label', wrap).textContent = label;\n const row = createEl('div', '', wrap);\n row.style.display = 'flex';\n row.style.alignItems = 'center';\n row.style.gap = '8px';\n const input = createEl('input', 'lp-property-input', row);\n input.type = 'text';\n input.value = value == null ? '' : String(value);\n input.style.flex = '1';\n input.addEventListener('input', () => onChange(input.value));\n const picker = createEl('input', '', row);\n picker.type = 'color';\n picker.value = value || '#000000';\n picker.style.width = '32px';\n picker.style.height = '32px';\n picker.style.border = 'none';\n picker.style.background = 'none';\n picker.style.cursor = 'pointer';\n picker.addEventListener('input', () => {\n input.value = picker.value;\n onChange(picker.value);\n });\n return wrap;\n }\n\n function createFieldControl(path, value) {\n const type = inferFieldType(path, value);\n const label = getFieldLabel(path);\n if (type === 'image') return createImageHintField(label);\n if (type === 'textarea') return createTextareaField(label, value, (v) => setField(path, v || undefined));\n if (type === 'number') return createNumberField(label, value, (v) => setField(path, v));\n if (type === 'boolean') return createToggleField(label, value, (v) => setField(path, v));\n if (type === 'select') return createSelectField(label, value, getSelectOptions(path), (v) => setField(path, v));\n return createTextField(label, value, (v) => setField(path, v || undefined));\n }\n\n function createSchemaFieldControl(field, value, path, skipCanvasEditable) {\n const label = field.label || field.key;\n if (skipCanvasEditable && (field.type === 'text' || field.type === 'textarea' || field.type === 'image')) {\n return null;\n }\n if (field.type === 'array') {\n const wrap = createEl('div', 'lp-property-nested-array');\n createSchemaArraySection(field, Array.isArray(value) ? value : [], wrap, path, skipCanvasEditable);\n return wrap;\n }\n if (field.type === 'image') return createImageHintField(label);\n if (field.type === 'textarea') return createTextareaField(label, value, (v) => setField(path, v || undefined));\n if (field.type === 'number') return createNumberField(label, value, (v) => setField(path, Number(v)));\n if (field.type === 'slider') {\n const effectiveValue = value === undefined && field.defaultValue !== undefined ? field.defaultValue : value;\n let sliderField = field;\n if (field.key === 'highlightRow') {\n const slideProps = goal.slides[selectedSlideIdx]?.props;\n const rows = Array.isArray(slideProps?.rows) ? slideProps.rows : [];\n sliderField = { ...field, max: Math.min(field.max ?? 11, Math.max(0, rows.length - 1)) };\n }\n return createSliderField(sliderField, effectiveValue, (v) => setField(path, v));\n }\n if (field.type === 'boolean') {\n const effectiveValue = value === undefined && field.defaultValue !== undefined ? field.defaultValue : value;\n return createToggleField(label, effectiveValue, (v) => {\n setField(path, v);\n renderSlidePanel();\n });\n }\n if (field.type === 'select') return createSelectField(label, value, field.options || [], (v) => setField(path, v));\n if (field.type === 'color') return createColorField(label, value, (v) => setField(path, v));\n return createTextField(label, value, (v) => setField(path, v || undefined));\n }\n\n function getSchemaEmptyItem(itemSchema) {\n if (!itemSchema || !itemSchema.length) return '\u793A\u4F8B\u9879';\n // itemSchema \u53EA\u6709\u5355\u4E00\u5360\u4F4D\u5B57\u6BB5\u65F6\uFF0C\u8868\u793A\u6570\u7EC4\u5143\u7D20\u662F\u539F\u59CB\u503C\uFF08\u5B57\u7B26\u4E32/\u6570\u5B57\u7B49\uFF09\n if (itemSchema.length === 1 && itemSchema[0].key === 'item') {\n const field = itemSchema[0];\n if (field.defaultValue !== undefined) return JSON.parse(JSON.stringify(field.defaultValue));\n if (field.type === 'boolean') return false;\n if (field.type === 'number') return 100;\n if (field.type === 'textarea') return '\u793A\u4F8B\u5185\u5BB9';\n return '\u793A\u4F8B\u6587\u5B57';\n }\n const item = {};\n itemSchema.forEach((field) => {\n if (field.defaultValue !== undefined) {\n item[field.key] = JSON.parse(JSON.stringify(field.defaultValue));\n } else if (field.type === 'boolean') {\n item[field.key] = false;\n } else if (field.type === 'number') {\n item[field.key] = 100;\n } else if (field.type === 'textarea') {\n item[field.key] = '\u793A\u4F8B\u5185\u5BB9';\n } else if (field.type === 'array') {\n const count = typeof field.minItems === 'number' ? Math.max(1, field.minItems) : 1;\n const empty = [];\n for (let i = 0; i < count; i++) {\n empty.push(getSchemaEmptyItem(field.itemSchema));\n }\n item[field.key] = empty;\n } else if (field.type === 'image') {\n item[field.key] = '';\n } else if (field.type === 'text') {\n item[field.key] = '\u793A\u4F8B\u6587\u5B57';\n } else if (field.type === 'select') {\n const firstOption = Array.isArray(field.options) && field.options.length > 0 ? field.options[0] : undefined;\n item[field.key] = firstOption && typeof firstOption === 'object' ? firstOption.value : '';\n } else {\n item[field.key] = '\u793A\u4F8B\u6587\u5B57';\n }\n });\n return item;\n }\n\n function createSchemaArraySection(field, array, parent, basePath, skipCanvasEditable) {\n const path = basePath || field.key;\n const section = createEl('div', 'lp-property-section', parent);\n\n // \u6761\u76EE\u6570\u7531\u7EC4\u4EF6\u6837\u5F0F\u51B3\u5B9A\uFF1AminItems / maxItems \u6765\u81EA\u7248\u5F0F Schema\n const minItems = typeof field.minItems === 'number' ? field.minItems : 1;\n // \u5F53 Schema \u672A\u58F0\u660E maxItems \u65F6\uFF0C\u4F7F\u7528\u4E00\u4E2A\u9ED8\u8BA4\u4E0A\u9650\uFF08\u800C\u4E0D\u662F\u5F53\u524D\u6570\u7EC4\u957F\u5EA6\uFF09\uFF0C\n // \u907F\u514D\u7528\u6237\u51CF\u5C11\u6761\u76EE\u540E slider \u7684 max \u540C\u6B65\u7F29\u5C0F\uFF0C\u5BFC\u81F4\u65E0\u6CD5\u6062\u590D\u6216\u7EE7\u7EED\u589E\u52A0\u3002\n const maxItems = typeof field.maxItems === 'number' ? field.maxItems : Math.max(array.length, minItems, 6);\n const currentCount = Math.max(minItems, Math.min(maxItems, array.length));\n\n // \u6807\u9898\u4E0E\u6570\u503C\u653E\u5728\u540C\u4E00\u884C\n const header = createEl('div', 'lp-property-section-header', section);\n createEl('div', 'lp-property-section-title', header).textContent = field.label;\n const countBadge = createEl('span', 'lp-property-slider-value', header);\n countBadge.textContent = String(currentCount);\n\n // \u6570\u91CF\u6ED1\u5757\uFF08\u6807\u5C3A\u6837\u5F0F\uFF09\n const sliderWrap = createEl('div', 'lp-property-slider-wrap', section);\n const ruler = createEl('div', 'lp-property-slider-ruler', sliderWrap);\n const range = document.createElement('input');\n range.type = 'range';\n range.className = 'lp-property-slider';\n range.min = String(minItems);\n range.max = String(maxItems);\n range.value = String(currentCount);\n ruler.appendChild(range);\n renderSliderTicks(ruler, minItems, maxItems);\n\n function setArrayLength(newLength) {\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n let arr = getProp(slide.props, path);\n if (!Array.isArray(arr)) {\n arr = [];\n setProp(slide.props, path, arr);\n }\n if (newLength < arr.length) {\n arr.length = newLength;\n } else if (newLength > arr.length) {\n while (arr.length < newLength) {\n arr.push(getSchemaEmptyItem(field.itemSchema));\n }\n }\n }\n\n function updateSliderFill() {\n const min = Number(range.min);\n const max = Number(range.max);\n const pct = max === min ? 0 : ((Number(range.value) - min) / (max - min)) * 100;\n range.style.background = 'linear-gradient(90deg, rgba(91,155,213,1) 0%, rgba(139,92,246,1) ' + pct + '%, rgba(255,255,255,0.14) ' + pct + '%, rgba(255,255,255,0.14) 100%)';\n }\n updateSliderFill();\n\n range.addEventListener('input', () => {\n const newLength = Number(range.value);\n countBadge.textContent = String(newLength);\n setArrayLength(newLength);\n updateSliderFill();\n refreshCurrentSlide();\n });\n\n range.addEventListener('change', () => {\n recordHistory();\n autoSave();\n renderSlidePanel();\n });\n\n // \u70B9\u51FB\u6807\u5C3A\u5FEB\u901F\u8DF3\u8F6C\n ruler.addEventListener('click', (e) => {\n if (e.target === range || range.contains(e.target)) return;\n const rect = ruler.getBoundingClientRect();\n const padding = 9;\n const availableWidth = rect.width - padding * 2;\n const x = e.clientX - rect.left - padding;\n const ratio = Math.max(0, Math.min(1, x / availableWidth));\n const newLength = Math.round(minItems + ratio * (maxItems - minItems));\n if (newLength !== Number(range.value)) {\n range.value = String(newLength);\n countBadge.textContent = String(newLength);\n setArrayLength(newLength);\n updateSliderFill();\n refreshCurrentSlide();\n recordHistory();\n autoSave();\n renderSlidePanel();\n }\n });\n\n // \u663E\u793A\u5F53\u524D\u6761\u76EE\u7684\u53EF\u7F16\u8F91\u5B57\u6BB5\uFF08\u6570\u7EC4\u9879\u59CB\u7EC8\u5C55\u793A\uFF0C\u907F\u514D\u65B0\u589E\u7A7A\u767D\u6761\u76EE\u65E0\u6CD5\u7F16\u8F91\uFF09\n const isPrimitiveItem = field.itemSchema && field.itemSchema.length === 1 && field.itemSchema[0].key === 'item';\n const allSubFieldsAreCanvasEditable = false;\n\n if (!allSubFieldsAreCanvasEditable) {\n const list = createEl('div', 'lp-property-array', section);\n array.forEach((item, index) => {\n const itemWrap = createEl('div', 'lp-property-array-item', list);\n\n const itemHeader = createEl('div', 'lp-property-array-item-header', itemWrap);\n const itemIndexLabel = createEl('span', '', itemHeader);\n itemIndexLabel.textContent = '#' + (index + 1);\n const deleteBtn = createEl('button', 'lp-property-btn-danger', itemHeader);\n deleteBtn.type = 'button';\n deleteBtn.title = '\u5220\u9664\u8BE5\u9879';\n deleteBtn.textContent = '\u00D7';\n deleteBtn.disabled = array.length <= minItems;\n deleteBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n e.preventDefault();\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n const arr = getProp(slide.props, path);\n if (!Array.isArray(arr)) return;\n arr.splice(index, 1);\n refreshCurrentSlide();\n recordHistory();\n autoSave();\n renderSlidePanel();\n });\n\n if (isPrimitiveItem) {\n const subField = field.itemSchema[0];\n const itemPath = path + '.' + index;\n const itemValue = item;\n const control = createSchemaFieldControl(subField, itemValue, itemPath, skipCanvasEditable);\n if (control) itemWrap.appendChild(control);\n } else if (field.itemSchema) {\n field.itemSchema.forEach((subField) => {\n if (skipCanvasEditable && (subField.type === 'text' || subField.type === 'textarea' || subField.type === 'image')) return;\n const itemPath = path + '.' + index + '.' + subField.key;\n const itemValue = typeof item === 'object' && item !== null ? item[subField.key] : undefined;\n const control = createSchemaFieldControl(subField, itemValue, itemPath, skipCanvasEditable);\n if (control) itemWrap.appendChild(control);\n });\n } else if (typeof item === 'string') {\n if (skipCanvasEditable) return;\n const textarea = createEl('textarea', 'lp-property-textarea', itemWrap);\n textarea.value = item;\n textarea.addEventListener('input', () => {\n setField(path + '.' + index, textarea.value);\n });\n }\n });\n }\n }\n\n function findSchemaField(schema, path) {\n if (!schema || !schema.fields || !path) return null;\n const parts = path.split('.');\n let fields = schema.fields;\n let field = null;\n for (let i = 0; i < parts.length; i++) {\n const key = parts[i];\n if (!fields) return null;\n if (/^d+$/.test(key)) {\n // \u6570\u7EC4\u7D22\u5F15\uFF0C\u8FDB\u5165\u4E0B\u4E00\u5C42 itemSchema\n if (field && field.itemSchema) {\n fields = field.itemSchema;\n }\n continue;\n }\n field = fields.find((f) => f.key === key) || null;\n if (!field) return null;\n if (i < parts.length - 1) {\n fields = field.type === 'array' ? field.itemSchema : (field.type === 'object' ? field.itemSchema : null);\n }\n }\n return field;\n }\n\n function renderSchemaFields(schema, props, parent, basePath, skipCanvasEditable) {\n if (!schema || !schema.fields) return;\n schema.fields.forEach((field) => {\n // \u7B80\u5355\u6587\u5B57\u5B57\u6BB5\u76F4\u63A5\u5728\u753B\u5E03\u4E0A\u7F16\u8F91\uFF0C\u4E0D\u5728\u53F3\u4FA7\u8FB9\u680F\u91CD\u590D\u6E32\u67D3\n if (field.inlineEditable) return;\n // \u975E\u56FE\u8868\u7EC4\u4EF6\u4E2D\uFF0C\u53EF\u5728\u753B\u5E03\u76F4\u63A5\u7F16\u8F91\u7684\u6587\u5B57\u3001\u56FE\u7247\u5B57\u6BB5\u4E0D\u518D\u5728\u53F3\u4FA7\u8FB9\u680F\u91CD\u590D\u63D0\u4F9B\n if (skipCanvasEditable && (field.type === 'text' || field.type === 'textarea' || field.type === 'image')) return;\n // \u4F9D\u8D56\u5F00\u5173\u7684\u5B57\u6BB5\uFF0C\u5F00\u5173\u5173\u95ED\u65F6\u6536\u8D77\uFF1B\u672A\u8BBE\u7F6E\u65F6\u6309\u5F00\u5173\u9ED8\u8BA4\u503C\u5224\u65AD\n if (field.visibleWhen) {\n const controller = findSchemaField(schema, field.visibleWhen.key);\n const rawValue = getProp(props, field.visibleWhen.key);\n const effectiveValue = rawValue === undefined && controller?.defaultValue !== undefined ? controller.defaultValue : rawValue;\n if (effectiveValue !== field.visibleWhen.value) return;\n }\n const path = basePath ? basePath + '.' + field.key : field.key;\n const value = getProp(props, path);\n if (field.type === 'array') {\n createSchemaArraySection(field, Array.isArray(value) ? value : [], parent, path, skipCanvasEditable);\n } else if (field.type === 'object') {\n const section = createEl('div', 'lp-property-section', parent);\n createEl('div', 'lp-property-section-title', section).textContent = field.label;\n if (field.itemSchema) {\n renderSchemaFields({ fields: field.itemSchema }, props, section, path, skipCanvasEditable);\n }\n } else if (field.type === 'slider') {\n const section = createEl('div', 'lp-property-section', parent);\n const effectiveValue = value === undefined && field.defaultValue !== undefined ? field.defaultValue : value;\n let sliderField = field;\n if (field.key === 'highlightRow') {\n const rows = Array.isArray(props?.rows) ? props.rows : [];\n sliderField = { ...field, max: Math.min(field.max ?? 11, Math.max(0, rows.length - 1)) };\n }\n section.appendChild(createSliderField(sliderField, effectiveValue, (v) => setField(path, v)));\n } else {\n const control = createSchemaFieldControl(field, value, path, skipCanvasEditable);\n if (control) parent.appendChild(control);\n }\n });\n }\n\n function renderSlideFields(props, parent, skipCanvasEditable) {\n Object.keys(props).forEach((key) => {\n if (key === '_style') return;\n const value = props[key];\n if (Array.isArray(value)) {\n // \u65E0 Schema \u65F6\u4EC5\u63D0\u4F9B\u7B80\u5355\u6587\u672C\u7F16\u8F91\uFF0C\u4E0D\u663E\u793A\u5E8F\u53F7\u523B\u5EA6\n const section = createEl('div', 'lp-property-section', parent);\n createEl('div', 'lp-property-section-title', section).textContent = getFieldLabel(key) + ' (' + value.length + ')';\n const list = createEl('div', 'lp-property-array', section);\n value.forEach((item, index) => {\n const itemWrap = createEl('div', 'lp-property-array-item', list);\n if (typeof item === 'string') {\n if (skipCanvasEditable) return;\n const textarea = createEl('textarea', 'lp-property-textarea', itemWrap);\n textarea.value = item;\n textarea.addEventListener('input', () => {\n setField(key + '.' + index, textarea.value);\n });\n } else if (typeof item === 'object' && item !== null) {\n Object.keys(item).forEach((subKey) => {\n const type = inferFieldType(key + '.' + index + '.' + subKey, item[subKey]);\n if (skipCanvasEditable && (type === 'text' || type === 'textarea' || type === 'image')) return;\n itemWrap.appendChild(createFieldControl(key + '.' + index + '.' + subKey, item[subKey]));\n });\n }\n if (!itemWrap.hasChildNodes()) {\n itemWrap.remove();\n }\n });\n if (!list.hasChildNodes()) {\n list.remove();\n }\n } else if (value !== null && typeof value === 'object') {\n // \u5D4C\u5957\u5BF9\u8C61\u76F4\u63A5\u5C55\u5F00\uFF08\u76EE\u524D\u8F83\u5C11\uFF09\n const section = createEl('div', 'lp-property-section', parent);\n createEl('div', 'lp-property-section-title', section).textContent = getFieldLabel(key);\n Object.keys(value).forEach((subKey) => {\n const type = inferFieldType(key + '.' + subKey, value[subKey]);\n if (skipCanvasEditable && (type === 'text' || type === 'textarea' || type === 'image')) return;\n section.appendChild(createFieldControl(key + '.' + subKey, value[subKey]));\n });\n } else {\n const type = inferFieldType(key, value);\n if (skipCanvasEditable && (type === 'text' || type === 'textarea' || type === 'image')) return;\n parent.appendChild(createFieldControl(key, value));\n }\n });\n }\n\n function renderSlidePanel() {\n if (!propertyContent) return;\n propertyContent.innerHTML = '';\n const slide = goal.slides[selectedSlideIdx];\n if (!slide) return;\n\n const info = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', info).textContent = '\u5E7B\u706F\u7247 ' + (selectedSlideIdx + 1);\n const layoutLabel = createEl('div', 'lp-property-help', info);\n layoutLabel.textContent = '\u7248\u5F0F\uFF1A' + getLayoutLabel(slide.layout);\n\n const transitionSection = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', transitionSection).textContent = '\u5207\u6362\u52A8\u753B';\n const transitionSelect = document.createElement('select');\n transitionSelect.className = 'lp-editor-select';\n transitionSelect.style.width = '100%';\n const currentTransition = slide.props.transition || 'none';\n TRANSITIONS.forEach((t) => {\n const opt = document.createElement('option');\n opt.value = t.key;\n opt.textContent = t.label;\n if (t.key === currentTransition) opt.selected = true;\n transitionSelect.appendChild(opt);\n });\n transitionSelect.addEventListener('change', () => {\n slide.props.transition = transitionSelect.value;\n const wrapper = document.querySelector('.lp-slide-wrapper[data-slide-index=\"' + selectedSlideIdx + '\"]');\n if (wrapper) wrapper.setAttribute('data-lp-transition', slide.props.transition);\n recordHistory();\n saveState();\n });\n transitionSection.appendChild(transitionSelect);\n\n if (selectedEl && slide.role === 'chart') {\n const prop = selectedEl.getAttribute('data-lp-prop');\n const schema = resolveLayoutSchema(slide);\n const schemaField = schema ? findSchemaField(schema, prop) : null;\n // \u7B80\u5355\u6587\u5B57\u5B57\u6BB5\u5DF2\u5728\u753B\u5E03\u4E0A\u76F4\u63A5\u7F16\u8F91\uFF0C\u4E0D\u5728\u53F3\u4FA7\u8FB9\u680F\u91CD\u590D\u663E\u793A\n if (!schemaField || !schemaField.inlineEditable) {\n const quickSection = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', quickSection).textContent = '\u5F53\u524D\u9009\u4E2D';\n const fieldValue = getProp(slide.props, prop);\n quickSection.appendChild(createFieldControl(prop, fieldValue));\n }\n }\n\n const fieldsSection = createEl('div', 'lp-property-section', propertyContent);\n createEl('div', 'lp-property-section-title', fieldsSection).textContent = '\u5185\u5BB9\u5C5E\u6027';\n const schema = resolveLayoutSchema(slide);\n const skipCanvasEditable = slide.role !== 'chart';\n if (schema && schema.fields) {\n renderSchemaFields(schema, slide.props, fieldsSection, undefined, skipCanvasEditable);\n } else {\n renderSlideFields(slide.props, fieldsSection, skipCanvasEditable);\n }\n }\n\n function selectEl(el) {\n if (selectedEl === el) return;\n if (selectedEl) selectedEl.classList.remove('lp-selected');\n selectedEl = el;\n selectedEl.classList.add('lp-selected');\n selectedSlideIdx = Number(el.getAttribute('data-lp-slide-idx')) || current;\n renderSlidePanel();\n // \u6EDA\u52A8\u5230\u5F53\u524D\u9009\u4E2D\u5B57\u6BB5\n const path = el.getAttribute('data-lp-prop');\n if (path && propertyContent) {\n const label = propertyContent.querySelector('.lp-property-label');\n // \u7B80\u5355\u9AD8\u4EAE\uFF1A\u6682\u65F6\u4E0D\u505A\u81EA\u52A8\u6EDA\u52A8\uFF0C\u907F\u514D\u590D\u6742\u5EA6\n }\n }\n\n function renderSlideToRootByIndex(index, wrapper) {\n if (typeof window.__lemonPPT_renderSlideToRoot !== 'function') return;\n const slide = goal.slides[index];\n if (!slide || !wrapper) {\n return;\n }\n window.__lemonPPT_renderSlideToRoot(wrapper, slide, { slideIdx: index, editable: true, theme: goal.theme });\n initEditableElements(wrapper);\n }\n\n function initActiveSlideECharts() {\n const activeWrapper = document.querySelector('.lp-slide-wrapper.active');\n if (activeWrapper && typeof window.__lemonPPT_initECharts === 'function') {\n window.__lemonPPT_initECharts(goal.theme || 'theme01', activeWrapper);\n }\n }\n\n function disposeEChartsInWrapper(wrapper) {\n if (!wrapper) return;\n wrapper.querySelectorAll('[data-lp-echart-type]').forEach((container) => {\n const inst = container.__lpEChartInstance;\n if (inst && !inst.isDisposed()) {\n inst.dispose();\n }\n container.__lpEChartInstance = undefined;\n });\n }\n\n function renderCurrentSlideToRoot() {\n const wrapper = document.querySelector('.lp-slide-wrapper.active');\n if (!wrapper) return;\n renderSlideToRootByIndex(selectedSlideIdx, wrapper);\n // React 18 createRoot().render() \u662F\u5F02\u6B65\u7684\uFF0C\u5EF6\u8FDF\u5230\u4E0B\u4E00\u5E27\u518D\u521D\u59CB\u5316 ECharts\uFF0C\n // \u786E\u4FDD\u5360\u4F4D\u5BB9\u5668\u5DF2\u7ECF\u6302\u8F7D\u5230 DOM \u5E76\u6709\u53EF\u6D4B\u91CF\u7684\u5C3A\u5BF8\u3002\n requestAnimationFrame(() => {\n initActiveSlideECharts();\n });\n }\n\n // \u9884\u521B\u5EFA\u6240\u6709 slide \u7684 React root \u5E76\u6E32\u67D3\uFF0C\u4F7F\u5207\u9875\u548C\u975E active slide \u66F4\u65B0\u90FD\u65E0\u9700\u91CD\u65B0\u6784\u5EFA DOM\n function renderAllSlidesToRoot() {\n if (typeof window.__lemonPPT_renderSlideToRoot !== 'function') {\n return;\n }\n const wrappers = document.querySelectorAll('.lp-slide-wrapper');\n wrappers.forEach((wrapper, index) => {\n renderSlideToRootByIndex(index, wrapper);\n });\n // \u7F16\u8F91\u5668\u573A\u666F\u4E0B\u53EA\u521D\u59CB\u5316\u5F53\u524D active slide \u7684 ECharts\uFF0C\u907F\u514D 75+ \u9875\u540C\u65F6\u6E32\u67D3\u5BFC\u81F4\u5361\u987F\u3002\n requestAnimationFrame(() => {\n initActiveSlideECharts();\n });\n }\n\n function selectSlide(index) {\n if (selectedEl) {\n selectedEl.classList.remove('lp-selected');\n selectedEl = null;\n }\n selectedSlideIdx = index;\n renderSlidePanel();\n }\n\n document.addEventListener('focusin', (e) => {\n const el = e.target.closest && e.target.closest('[data-lp-editable=\"true\"], [data-lp-editable-image=\"true\"]');\n if (el) selectEl(el);\n });\n\n document.addEventListener('click', (e) => {\n const editableEl = e.target.closest && e.target.closest('[data-lp-editable=\"true\"], [data-lp-editable-image=\"true\"]');\n if (editableEl) {\n selectEl(editableEl);\n return;\n }\n const wrapper = e.target.closest && e.target.closest('.lp-slide-wrapper');\n if (wrapper) {\n selectSlide(Number(wrapper.getAttribute('data-slide-index')));\n return;\n }\n if (e.target.closest && !e.target.closest('.lp-editor-right-panel')) {\n clearSelection();\n }\n });\n\n // \u6062\u590D\u4E0A\u6B21\u505C\u7559\u7684\u5E7B\u706F\u7247\uFF08\u7ED3\u6784\u53D8\u66F4\u540E\u91CD\u8F7D\u7528\uFF09\n const savedCurrent = localStorage.getItem('lemonppt:editor:currentSlide');\n if (savedCurrent) {\n const savedIndex = Number(savedCurrent);\n if (!Number.isNaN(savedIndex) && savedIndex >= 0 && savedIndex < slides.length) {\n goTo(savedIndex);\n }\n localStorage.removeItem('lemonppt:editor:currentSlide');\n }\n\n // \u9ED8\u8BA4\u9009\u4E2D\u5F53\u524D\u5E7B\u706F\u7247\n selectSlide(current);\n\n // \u82E5 localStorage \u4E2D\u5E7B\u706F\u7247\u6570\u91CF\u4E0E\u5F53\u524D DOM \u4E0D\u5339\u914D\uFF0C\u91CD\u5EFA DOM \u540E\u518D\u6E32\u67D3\n if (needsDomRebuild) {\n rebuildSlidesAndThumbnails();\n } else {\n // \u9884\u521B\u5EFA\u5E76\u6E32\u67D3\u6240\u6709 slide \u7684 React root\uFF0C\u4F7F\u5207\u9875\u4E0E\u540E\u7EED\u66F4\u65B0\u90FD\u8D70 React reconcile\n renderAllSlidesToRoot();\n }\n\n // \u66B4\u9732\u7ED9 editor.js\uFF1A\u65E0\u5237\u65B0\u5207\u6362\u4E3B\u9898\u65F6\u91CD\u7F6E\u5185\u90E8\u72B6\u6001\n window.__lemonPPT_applyTheme = function(newGoal, newSlidesMarkup) {\n try {\n const oldKey = 'lemonppt:editor:v2:' + (goal.theme || 'theme01') + ':' + (goal.randomSeed || goal.title || 'default');\n localStorage.setItem(oldKey, JSON.stringify(goal));\n } catch (err) {\n console.warn('\u5207\u6362\u4E3B\u9898\u524D\u4FDD\u5B58\u5931\u8D25', err);\n }\n goal = newGoal;\n window.__lemonPPT_goal = goal;\n\n // \u5C1D\u8BD5\u6062\u590D\u5F53\u524D\u4E3B\u9898\u7684\u672C\u5730\u7F16\u8F91\u72B6\u6001\n try {\n const newKey = 'lemonppt:editor:v2:' + (goal.theme || 'theme01') + ':' + (goal.randomSeed || goal.title || 'default');\n const saved = localStorage.getItem(newKey);\n if (saved) {\n const parsed = JSON.parse(saved);\n if (parsed && Array.isArray(parsed.slides)) {\n goal = parsed;\n window.__lemonPPT_goal = goal;\n }\n }\n } catch (err) {\n console.warn('\u5207\u6362\u4E3B\u9898\u540E\u6062\u590D\u5931\u8D25', err);\n }\n\n const slidesContainer = document.getElementById('lp-slides');\n if (slidesContainer) {\n slidesContainer.innerHTML = newSlidesMarkup;\n }\n current = 0;\n selectedSlideIdx = 0;\n rebuildSlidesAndThumbnails();\n selectSlide(current);\n syncAppearanceFromGoal();\n autoSave();\n updateUndoRedoButtons();\n };\n})();\n";
2
2
  //# sourceMappingURL=editor-script.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor-script.d.ts","sourceRoot":"","sources":["../src/editor-script.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,q3vJAoyGxB,CAAC"}
1
+ {"version":3,"file":"editor-script.d.ts","sourceRoot":"","sources":["../src/editor-script.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,8zvJAmyGxB,CAAC"}
@@ -2111,8 +2111,7 @@ export const editorScript = `
2111
2111
  if (!stage || !scaler) return;
2112
2112
  if (editorRoot && editorRoot.classList.contains('lp-editor-presentation-mode')) {
2113
2113
  const scale = Math.min(window.innerWidth / SLIDE_WIDTH, window.innerHeight / SLIDE_HEIGHT);
2114
- scaler.style.setProperty('--lp-presentation-scale', String(Math.max(scale, 0.35)));
2115
- scaler.style.transform = '';
2114
+ scaler.style.transform = 'scale(' + Math.max(scale, 0.35) + ')';
2116
2115
  return;
2117
2116
  }
2118
2117
  const scale = userZoom == null ? fitScale() : userZoom;
@@ -1 +1 @@
1
- {"version":3,"file":"editor-script.js","sourceRoot":"","sources":["../src/editor-script.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,gCAAgC;AAChC,6CAA6C;AAE7C,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoyG3B,CAAC"}
1
+ {"version":3,"file":"editor-script.js","sourceRoot":"","sources":["../src/editor-script.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,gCAAgC;AAChC,6CAA6C;AAE7C,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmyG3B,CAAC"}
@@ -54,7 +54,7 @@ export async function exportDeckToPdf(goal, options) {
54
54
  width: `${width}px`,
55
55
  height: `${height}px`,
56
56
  printBackground: true,
57
- preferCSSPageSize: false,
57
+ preferCSSPageSize: true,
58
58
  });
59
59
  }
60
60
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"export-pdf.js","sourceRoot":"","sources":["../src/export-pdf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"export-pdf.js","sourceRoot":"","sources":["../src/export-pdf.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"render-editor-data.d.ts","sourceRoot":"","sources":["../src/render-editor-data.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAkBzE,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6DD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAE,mBAAwB,GAAG,UAAU,CA0D9F;AAsID;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,KAAK,SAAO,EACZ,MAAM,SAAM,GACX,UAAU,CASZ;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,mBAAwB,GAAG,YAAY,CAq2B1G"}
1
+ {"version":3,"file":"render-editor-data.d.ts","sourceRoot":"","sources":["../src/render-editor-data.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAkBzE,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6DD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAE,mBAAwB,GAAG,UAAU,CA0D9F;AAuID;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,KAAK,SAAO,EACZ,MAAM,SAAM,GACX,UAAU,CASZ;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,GAAE,mBAAwB,GAAG,YAAY,CA+3B1G"}
@@ -122,6 +122,7 @@ function buildEditorBarMarkup(goal) {
122
122
  </select>
123
123
  <button id="lp-add-slide" class="lp-editor-btn" type="button"><span>+</span> 添加页面</button>
124
124
  <button id="lp-save-deck" class="lp-editor-btn lp-editor-btn-primary" type="button">保存</button>
125
+ <button id="lp-play" class="lp-editor-btn lp-editor-btn-primary" type="button" title="播放演示"><span>▶</span> 播放</button>
125
126
  <div class="lp-editor-export">
126
127
  <button id="lp-export-toggle" class="lp-editor-btn lp-editor-export-toggle" type="button"><span>⬇</span> 导出 <span class="lp-editor-caret">▼</span></button>
127
128
  <div id="lp-export-menu" class="lp-editor-export-menu" hidden>
@@ -498,6 +499,32 @@ ${themeCssVars}
498
499
  transition: transform 0.2s ease;
499
500
  position: relative;
500
501
  }
502
+ .lp-editor-root.lp-editor-presentation-mode {
503
+ position: fixed;
504
+ inset: 0;
505
+ width: 100vw;
506
+ height: 100vh;
507
+ z-index: 9999;
508
+ background: #000;
509
+ }
510
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-bar,
511
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-left-panel,
512
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-right-panel,
513
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-zoom-bar,
514
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-page-counter,
515
+ .lp-editor-root.lp-editor-presentation-mode .lp-add-slide-modal-overlay {
516
+ display: none !important;
517
+ }
518
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-workspace {
519
+ width: 100vw;
520
+ height: 100vh;
521
+ min-height: 100vh;
522
+ }
523
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-stage {
524
+ width: 100vw;
525
+ height: 100vh;
526
+ background: #000;
527
+ }
501
528
  .lp-editor-page-counter {
502
529
  position: absolute;
503
530
  bottom: -32px;
@@ -1 +1 @@
1
- {"version":3,"file":"render-editor-data.js","sourceRoot":"","sources":["../src/render-editor-data.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render-editor-data.js","sourceRoot":"","sources":["../src/render-editor-data.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAa7D,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,CAu5CpF"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAa7D,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,CAi6CpF"}
package/dist/render.js CHANGED
@@ -1071,10 +1071,17 @@ ${themeCssVars}
1071
1071
  outline-offset: 2px;
1072
1072
  border-radius: 2px;
1073
1073
  }
1074
+ @page {
1075
+ size: ${width}px ${height}px;
1076
+ margin: 0;
1077
+ }
1074
1078
  @media print {
1075
1079
  html, body {
1076
1080
  display: block;
1077
1081
  background: #fff;
1082
+ width: auto !important;
1083
+ height: auto !important;
1084
+ overflow: visible !important;
1078
1085
  }
1079
1086
  .lp-editor-bar,
1080
1087
  .lp-nav,
@@ -1098,6 +1105,7 @@ ${themeCssVars}
1098
1105
  transform: none !important;
1099
1106
  }
1100
1107
  .lp-deck {
1108
+ display: block !important;
1101
1109
  box-shadow: none;
1102
1110
  border-radius: 0;
1103
1111
  width: 100% !important;
@@ -1105,7 +1113,7 @@ ${themeCssVars}
1105
1113
  overflow: visible;
1106
1114
  }
1107
1115
  .lp-slide-wrapper {
1108
- position: relative;
1116
+ position: relative !important;
1109
1117
  transform: none !important;
1110
1118
  opacity: 1 !important;
1111
1119
  width: 100% !important;
@@ -1113,6 +1121,8 @@ ${themeCssVars}
1113
1121
  page-break-after: always;
1114
1122
  break-after: page;
1115
1123
  z-index: auto !important;
1124
+ float: none !important;
1125
+ clear: both !important;
1116
1126
  }
1117
1127
  .lp-slide-wrapper:last-child {
1118
1128
  page-break-after: auto;
@@ -1 +1 @@
1
- {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EACL,WAAW,EACX,iCAAiC,EACjC,sCAAsC,EACtC,sCAAsC,EACtC,iDAAiD,EACjD,mDAAmD,EACnD,mDAAmD,GACpD,MAAM,qBAAqB,CAAC;AAC7B,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAS9C,MAAM,UAAU,UAAU,CAAC,IAAc,EAAE,UAAyB,EAAE;IACpE,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC;IAE/C,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAEtC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACrD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC5F,MAAM,UAAU,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,OAAO,CACL,cAEE,SAAS,EAAE,oBAAoB,UAAU,EAAE,sBACzB,KAAK,iBACV,KAAK,CAAC,MAAM,wBACL,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,IAAI,MAAM,CAAC,EAC5D,KAAK,EAAE;gBACL,KAAK,EAAE,GAAG,KAAK,IAAI;gBACnB,MAAM,EAAE,GAAG,MAAM,IAAI;gBACrB,SAAS,EAAE,YAAY;aACxB,YAEA,OAAO,IAAI,CACV,eAAK,SAAS,EAAC,UAAU,iDACf,KAAK,CAAC,MAAM,IAChB,CACP,IAfI,KAAK,CAgBN,CACP,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,cAAc,CAAC,oBAAoB,CACtD,cACE,SAAS,EAAC,SAAS,gBACP,IAAI,CAAC,KAAK,wBACH,MAAM,EACzB,KAAK,EAAE;YACL,KAAK,EAAE,GAAG,KAAK,IAAI;YACnB,MAAM,EAAE,GAAG,MAAM,IAAI;SACtB,YAEA,aAAa,GACV,CACP,CAAC;IAEF,MAAM,SAAS,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,iBAAiB,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,GAAG,YAAY,GAAG,SAAS,GAAG,YAAY,EAAE,CAAC;IAEhE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC;IACtC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3M,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACtJ,MAAM,cAAc,GAAG,UAAU,CAAC,CAAC,CAAC,qBAAqB,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,YAAY,GAAG,KAAK,KAAK,SAAS;QACtC,CAAC,CAAC,iCAAiC,EAAE;QACrC,CAAC,CAAC,KAAK,KAAK,SAAS;YACnB,CAAC,CAAC,sCAAsC,EAAE;YAC1C,CAAC,CAAC,KAAK,KAAK,SAAS;gBACnB,CAAC,CAAC,sCAAsC,EAAE;gBAC1C,CAAC,CAAC,KAAK,KAAK,SAAS;oBACnB,CAAC,CAAC,iDAAiD,EAAE;oBACrD,CAAC,CAAC,KAAK,KAAK,SAAS;wBACnB,CAAC,CAAC,mDAAmD,EAAE;wBACvD,CAAC,CAAC,KAAK,KAAK,SAAS;4BACnB,CAAC,CAAC,mDAAmD,EAAE;4BACvD,CAAC,CAAC,EAAE,CAAC;IAEjB,MAAM,IAAI,GAAG;cACD,IAAI,CAAC,QAAQ,IAAI,IAAI,iBAAiB,WAAW,IAAI,cAAc;;;yCAGxC,KAAK;WACnC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;;0CAES,KAAK;;;;EAI7C,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmiCI,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAkCT,KAAK;gBACJ,MAAM;;;;;;;;eAQP,KAAK;gBACJ,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8OpB,UAAU;;QAEJ,CAAC;IAEP,OAAO;QACL,IAAI;QACJ,MAAM,EAAE;YACN,0BAA0B;YAC1B,YAAY,KAAK,MAAM;YACvB,wBAAwB;SACzB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,UAAkB;IACxC,IAAI,UAAU,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACvD,2BAA2B,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,iBAAiB,CAAC,WAAW,CAChF,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEX,OAAO;;6BAEoB,IAAI;;;gEAG+B,UAAU;kCACxC,CAAC;AACnC,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAiHC,CAAC;AACX,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lemonppt/renderer",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -32,9 +32,9 @@
32
32
  "pptxgenjs": "3.12.0",
33
33
  "react": "^18.3.1",
34
34
  "react-dom": "^18.3.1",
35
- "@lemonppt/core": "1.0.6",
36
- "@lemonppt/templates": "1.0.6",
37
- "@lemonppt/themes": "1.0.6"
35
+ "@lemonppt/templates": "1.0.7",
36
+ "@lemonppt/core": "1.0.7",
37
+ "@lemonppt/themes": "1.0.7"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^20.0.0",
@@ -256,6 +256,32 @@
256
256
  transition: transform 0.2s ease;
257
257
  position: relative;
258
258
  }
259
+ .lp-editor-root.lp-editor-presentation-mode {
260
+ position: fixed;
261
+ inset: 0;
262
+ width: 100vw;
263
+ height: 100vh;
264
+ z-index: 9999;
265
+ background: #000;
266
+ }
267
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-bar,
268
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-left-panel,
269
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-right-panel,
270
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-zoom-bar,
271
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-page-counter,
272
+ .lp-editor-root.lp-editor-presentation-mode .lp-add-slide-modal-overlay {
273
+ display: none !important;
274
+ }
275
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-workspace {
276
+ width: 100vw;
277
+ height: 100vh;
278
+ min-height: 100vh;
279
+ }
280
+ .lp-editor-root.lp-editor-presentation-mode .lp-editor-stage {
281
+ width: 100vw;
282
+ height: 100vh;
283
+ background: #000;
284
+ }
259
285
  .lp-editor-page-counter {
260
286
  position: absolute;
261
287
  bottom: -32px;
@@ -56,6 +56,7 @@
56
56
  </div>
57
57
  <button id="lp-add-slide" class="lp-editor-btn" type="button"><span>+</span> 添加页面</button>
58
58
  <button id="lp-save-deck" class="lp-editor-btn lp-editor-btn-primary" type="button">保存</button>
59
+ <button id="lp-play" class="lp-editor-btn lp-editor-btn-primary" type="button" title="播放演示"><span>▶</span> 播放</button>
59
60
  <div id="lp-editor-export" class="lp-editor-export">
60
61
  <button id="lp-export-toggle" class="lp-editor-btn lp-editor-export-toggle" type="button" aria-expanded="false"><span>⬇</span> 导出 <span class="lp-editor-caret">▼</span></button>
61
62
  <div id="lp-export-menu" class="lp-editor-export-menu" hidden>