PDF 样式 IDE
DITA 内容 · PDF 样式模板
AI
样式助手 描述效果,自动生成 CSS / XSL-FO
▼
你好,我是 PDF 样式助手。试试描述:
「生成深色封面」·「表格斑马纹」·「中文字体优化」·「页脚页码样式」
预览 CSS Paged Media 渲染
100%
`; } function updatePreview() { const css = editor.getValue(); previewFrame.srcdoc = buildPreviewHTML(css); } previewFrame.addEventListener('load', () => { try { const doc = previewFrame.contentDocument; if (doc && doc.documentElement) { const h = Math.max( doc.documentElement.scrollHeight, doc.body ? doc.body.scrollHeight : 0, 1122 ); previewHeightPx = h; previewFrame.style.height = h + 'px'; applyZoom(); } } catch (e) { /* 静默处理 */ } }); /* ───────────────────────────────────────────── 7. 缩放控制 ───────────────────────────────────────────── */ let zoomLevel = 100; const ZOOM_STEP = 10; const ZOOM_MIN = 30; const ZOOM_MAX = 200; const zoomLevelEl = document.getElementById('zoom-level'); function applyZoom() { const scale = zoomLevel / 100; const baseWidth = 210 * 3.7795275591; // 210mm → px previewFrame.style.transform = `scale(${scale})`; previewWrap.style.width = (baseWidth * scale) + 'px'; previewWrap.style.height = (previewHeightPx * scale) + 'px'; zoomLevelEl.textContent = zoomLevel + '%'; } document.getElementById('zoom-in').addEventListener('click', () => { zoomLevel = Math.min(ZOOM_MAX, zoomLevel + ZOOM_STEP); applyZoom(); }); document.getElementById('zoom-out').addEventListener('click', () => { zoomLevel = Math.max(ZOOM_MIN, zoomLevel - ZOOM_STEP); applyZoom(); }); document.getElementById('zoom-reset').addEventListener('click', () => { zoomLevel = 100; applyZoom(); }); /* ───────────────────────────────────────────── 8. 预览格式指示器 ───────────────────────────────────────────── */ const formatIndicator = document.getElementById('preview-format-indicator'); const formatText = document.getElementById('preview-format-text'); function updateFormatIndicator(tabId) { if (tabId === 'xslfo') { formatIndicator.classList.add('xsl-mode'); formatText.textContent = 'XSL-FO 渲染预览'; } else if (tabId === 'dita') { formatIndicator.classList.remove('xsl-mode'); formatText.textContent = 'DITA 内容结构'; } else { formatIndicator.classList.remove('xsl-mode'); formatText.textContent = 'CSS Paged Media 渲染'; } } /* ───────────────────────────────────────────── 9. 编辑器事件 ───────────────────────────────────────────── */ editor.on('change', () => { tabContents[currentTab] = editor.getValue(); if (currentTab === 'css') { updatePreview(); } }); /* ───────────────────────────────────────────── 10. 顶部按钮 ───────────────────────────────────────────── */ document.getElementById('btn-load').addEventListener('click', () => { if (!confirm('加载默认模板将覆盖当前编辑内容,是否继续?')) return; const tab = TABS.find(t => t.id === currentTab); editor.setValue(tab.content); if (currentTab === 'css') updatePreview(); }); document.getElementById('btn-copy').addEventListener('click', async () => { const text = editor.getValue(); const btn = document.getElementById('btn-copy'); try { await navigator.clipboard.writeText(text); const original = btn.innerHTML; btn.innerHTML = '✅ 已复制'; setTimeout(() => { btn.innerHTML = original; }, 1500); } catch (e) { const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); } }); document.getElementById('btn-export').addEventListener('click', () => { const tab = TABS.find(t => t.id === currentTab); const text = editor.getValue(); const extensions = { css: 'css', xslfo: 'xsl', dita: 'ditamap' }; const mimes = { css: 'text/css', xslfo: 'application/xml', dita: 'application/xml' }; const ext = extensions[currentTab] || 'txt'; const blob = new Blob([text], { type: mimes[currentTab] || 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = tab.name.replace(/\.(css|xsl|ditamap)$/, '') + '.' + ext; a.click(); URL.revokeObjectURL(url); }); document.getElementById('btn-ai').addEventListener('click', () => { document.getElementById('ai-input').focus(); }); /* ───────────────────────────────────────────── 11. AI 助手(模拟) ───────────────────────────────────────────── */ const aiLog = document.getElementById('ai-chat-log'); const aiInput = document.getElementById('ai-input'); function addAIMessage(html, type) { const div = document.createElement('div'); div.className = `ai-msg ${type}`; div.innerHTML = html; aiLog.appendChild(div); aiLog.scrollTop = aiLog.scrollHeight; } function handleAISend() { const text = aiInput.value.trim(); if (!text) return; addAIMessage(text, 'user'); aiInput.value = ''; setTimeout(() => { let response = ''; let snippet = ''; const lower = text.toLowerCase(); if (lower.includes('封面') || lower.includes('cover')) { response = '已生成封面页样式,包含渐变背景、标题与元数据布局。'; snippet = SNIPPETS.css.cover.code; } else if (lower.includes('表格') || lower.includes('table')) { response = '已生成表格样式,包含深色表头、斑马纹与边框设置。'; snippet = SNIPPETS.css.table.code; } else if (lower.includes('页脚') || lower.includes('footer')) { response = '已生成页脚样式,页码格式为「第 X 页」。'; snippet = SNIPPETS.css.page.code; } else if (lower.includes('中文') || lower.includes('字体')) { response = '已生成中文字体支持样式,包含 CJK 字体族和行高优化。'; snippet = SNIPPETS.css.chinese.code; } else if (lower.includes('目录') || lower.includes('toc')) { response = '已生成目录样式,包含标题下划线与条目引导线。'; snippet = SNIPPETS.css.toc.code; } else if (lower.includes('变量') || lower.includes('主题')) { response = '已生成 CSS 主题变量,便于统一管理配色。'; snippet = SNIPPETS.css.variables.code; } else if (lower.includes('代码') || lower.includes('code')) { response = '已生成代码块样式,深色背景 + 左侧橙色强调线。'; snippet = SNIPPETS.css.code.code; } else if (lower.includes('注释') || lower.includes('警告') || lower.includes('note')) { response = '已生成注释样式,包含提示、警告、重要三种变体。'; snippet = SNIPPETS.css.note.code; } else { response = '已生成通用页面设置样式,可作为基础模板使用。'; snippet = SNIPPETS.css.page.code; } addAIMessage(response + ' 样式已插入编辑器末尾。', 'ai'); if (snippet) { const lastLine = editor.lastLine(); editor.replaceRange('\n' + snippet, { line: lastLine, ch: 0 }); editor.focus(); } }, 550); } document.getElementById('ai-send-btn').addEventListener('click', handleAISend); aiInput.addEventListener('keydown', e => { if (e.key === 'Enter') handleAISend(); }); document.getElementById('ai-panel-header').addEventListener('click', function () { this.classList.toggle('collapsed'); document.getElementById('ai-panel-body').classList.toggle('collapsed'); }); /* ───────────────────────────────────────────── 12. 拖拽分隔条 ───────────────────────────────────────────── */ const splitHandle = document.getElementById('split-handle'); const editorPanel = document.getElementById('editor-panel'); const previewPanel = document.getElementById('preview-panel'); let isResizing = false; splitHandle.addEventListener('mousedown', e => { isResizing = true; splitHandle.classList.add('active'); document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; e.preventDefault(); }); document.addEventListener('mousemove', e => { if (!isResizing) return; const rect = document.getElementById('main-layout').getBoundingClientRect(); const isVertical = window.getComputedStyle(document.getElementById('main-layout')).flexDirection === 'column'; if (isVertical) { const offsetY = e.clientY - rect.top; const ratio = offsetY / rect.height; if (ratio > 0.15 && ratio < 0.85) { editorPanel.style.flex = `0 0 ${ratio * 100}%`; previewPanel.style.flex = `0 0 ${(1 - ratio) * 100}%`; } } else { const offsetX = e.clientX - rect.left; const ratio = offsetX / rect.width; if (ratio > 0.15 && ratio < 0.85) { editorPanel.style.flex = `0 0 ${ratio * 100}%`; previewPanel.style.flex = `0 0 ${(1 - ratio) * 100}%`; } } }); document.addEventListener('mouseup', () => { if (isResizing) { isResizing = false; splitHandle.classList.remove('active'); document.body.style.cursor = ''; document.body.style.userSelect = ''; } }); /* ───────────────────────────────────────────── 13. 格式切换 ───────────────────────────────────────────── */ document.querySelectorAll('.format-option').forEach(opt => { opt.addEventListener('click', () => { switchTab(opt.dataset.format); }); }); /* ───────────────────────────────────────────── 14. 初始化 ───────────────────────────────────────────── */ renderTabs(); renderSnippets('css'); updateFormatIndicator('css'); editor.setValue(DEFAULT_CSS); // 等首屏稳定后再渲染预览,避免尺寸抖动 setTimeout(() => { updatePreview(); applyZoom(); }, 60); window.addEventListener('resize', () => { if (currentTab === 'css') applyZoom(); }); // 初始聚焦 setTimeout(() => editor.focus(), 150); console.log( '%c PDF 样式 IDE %c DITA · XSL-FO · CSS Paged Media ', 'background:#FF9500;color:#fff;padding:4px 10px;border-radius:4px 0 0 4px;font-weight:600;', 'background:#232946;color:#A3ABCB;padding:4px 10px;border-radius:0 4px 4px 0;' ); })();