@thanh01.pmt/presentation-kit 0.2.18 → 0.3.1
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.
- package/README.md +26 -0
- package/dist/ai/index.cjs +16 -15
- package/dist/ai/index.cjs.map +1 -1
- package/dist/ai/index.d.cts +3 -2
- package/dist/ai/index.d.ts +3 -2
- package/dist/ai/index.js +2 -1
- package/dist/ai/index.js.map +1 -1
- package/dist/assets/fonts/NOTICE.md +25 -0
- package/dist/assets/fonts/SOURCES.json +47 -0
- package/dist/assets/fonts/fira-code.woff2 +0 -0
- package/dist/assets/fonts/inter.woff2 +0 -0
- package/dist/assets/fonts/jetbrains-mono.woff2 +0 -0
- package/dist/assets/fonts/playfair-display.woff2 +0 -0
- package/dist/assets/fonts/plus-jakarta-sans.woff2 +0 -0
- package/dist/assets/fonts/space-grotesk.woff2 +0 -0
- package/dist/{chunk-DDOUFYLK.js → chunk-3DXMAPAY.js} +3 -3
- package/dist/{chunk-DDOUFYLK.js.map → chunk-3DXMAPAY.js.map} +1 -1
- package/dist/chunk-3Q4RMZVQ.cjs +867 -0
- package/dist/chunk-3Q4RMZVQ.cjs.map +1 -0
- package/dist/{chunk-JJQDQIRG.js → chunk-5HIDQQHE.js} +288 -855
- package/dist/chunk-5HIDQQHE.js.map +1 -0
- package/dist/{chunk-SFBEDDGU.cjs → chunk-5WEZOGQA.cjs} +292 -866
- package/dist/chunk-5WEZOGQA.cjs.map +1 -0
- package/dist/{chunk-DIHFQ5LF.cjs → chunk-CVEC72CT.cjs} +4 -4
- package/dist/{chunk-DIHFQ5LF.cjs.map → chunk-CVEC72CT.cjs.map} +1 -1
- package/dist/chunk-V6X7D44H.js +854 -0
- package/dist/chunk-V6X7D44H.js.map +1 -0
- package/dist/{click-css-Bcc1CkTN.d.cts → click-css-C8VciwZ6.d.cts} +1 -1
- package/dist/{click-css-B2s9B_uP.d.ts → click-css-COIiFo1I.d.ts} +1 -1
- package/dist/index.cjs +90 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -5
- package/dist/index.d.ts +46 -5
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +117 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +36 -0
- package/dist/node/index.d.ts +36 -0
- package/dist/node/index.js +105 -0
- package/dist/node/index.js.map +1 -0
- package/dist/{presets-D_hfPJX9.d.ts → presets-DPRGtpO-.d.ts} +1 -1
- package/dist/{presets-BYvgAAkJ.d.cts → presets-MIi91wZg.d.cts} +1 -1
- package/dist/react/index.cjs +17 -16
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +4 -3
- package/dist/react/index.d.ts +4 -3
- package/dist/react/index.js +4 -3
- package/dist/react/index.js.map +1 -1
- package/dist/{types-C3d04zzP.d.cts → types-CvxCxWus.d.cts} +1 -67
- package/dist/{types-C3d04zzP.d.ts → types-CvxCxWus.d.ts} +1 -67
- package/dist/types-k_eae8UV.d.cts +74 -0
- package/dist/types-k_eae8UV.d.ts +74 -0
- package/package.json +8 -2
- package/dist/chunk-JJQDQIRG.js.map +0 -1
- package/dist/chunk-SFBEDDGU.cjs.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
export { h as ClickCssOptions, f as ClickPostprocessResult, C as ClickPreprocessResult, j as countSlides, d as defineTheme, e as extractMaxClicks, g as generateClickCss, l as generateScopedCss, k as getThemeTokens, c as postprocessClicks, p as preprocessClicks, i as renderMarp, r as renderSlide, a as renderSlideAsync, b as resolveTheme } from './click-css-
|
|
2
|
-
import {
|
|
3
|
-
export { B as BuiltInTheme,
|
|
1
|
+
export { h as ClickCssOptions, f as ClickPostprocessResult, C as ClickPreprocessResult, j as countSlides, d as defineTheme, e as extractMaxClicks, g as generateClickCss, l as generateScopedCss, k as getThemeTokens, c as postprocessClicks, p as preprocessClicks, i as renderMarp, r as renderSlide, a as renderSlideAsync, b as resolveTheme } from './click-css-COIiFo1I.js';
|
|
2
|
+
import { e as SlideEngineConfig, f as SlideEngine, T as ThemeTokens, M as MermaidBlock, S as SlideLayoutData } from './types-CvxCxWus.js';
|
|
3
|
+
export { B as BuiltInTheme, L as LayoutBoxConfig, c as SlideRenderAsyncResult, a as SlideRenderOptions, b as SlideRenderResult, d as SlideTheme } from './types-CvxCxWus.js';
|
|
4
4
|
import { Highlighter } from 'shiki';
|
|
5
|
-
export { a as SLIDE_LAYOUT_PRESETS, S as SlideLayoutCategory, c as SlideLayoutPreset, g as getSlideLayoutPresetById, b as getSlideLayoutPresetsByCategory } from './presets-
|
|
5
|
+
export { a as SLIDE_LAYOUT_PRESETS, S as SlideLayoutCategory, c as SlideLayoutPreset, g as getSlideLayoutPresetById, b as getSlideLayoutPresetsByCategory } from './presets-DPRGtpO-.js';
|
|
6
|
+
import { c as HtmlThemeName, e as HtmlDeckTheme, b as HtmlLayoutPresetId, f as HtmlSlidePreset, a as HtmlDeckData, H as HtmlCompileOptions, C as CompiledHtmlDeckResult } from './types-k_eae8UV.js';
|
|
7
|
+
export { d as HtmlSlideSlotData, g as HtmlSlotDefinition } from './types-k_eae8UV.js';
|
|
6
8
|
export { FRONTEND_SLIDES_CORE_RULES, FrontendSlidesSkillOptions, STYLE_PRESETS, StylePresetItem, TEMPLATES_CATALOG, TemplateCatalogItem, VIEWPORT_BASE_CSS, frontendSlidesTools, getFrontendSlidesSkillPrompt, getSlideStylePresetSchema, getStylePreset, getStylePresetById, getTemplatesCatalog, querySlideTemplatesSchema, searchTemplates } from './skills/index.js';
|
|
7
9
|
import 'zod';
|
|
8
10
|
|
|
@@ -177,6 +179,13 @@ interface GenerateHtmlShellOptions {
|
|
|
177
179
|
slideCount: number;
|
|
178
180
|
customHeadTags?: string;
|
|
179
181
|
deckId?: string;
|
|
182
|
+
/**
|
|
183
|
+
* Self-contained @font-face CSS (base64 woff2). When provided the shell emits
|
|
184
|
+
* NO external font links, so the deck renders with its typography on a machine
|
|
185
|
+
* with no network. Built by the Node-only `presentation-kit/node` entry
|
|
186
|
+
* (`loadEmbeddedFontFaceCss`) — the main entry stays fs-free for browsers.
|
|
187
|
+
*/
|
|
188
|
+
fontFaceCss?: string;
|
|
180
189
|
}
|
|
181
190
|
declare function generateHtmlShell(options: GenerateHtmlShellOptions): string;
|
|
182
191
|
|
|
@@ -199,4 +208,36 @@ declare function compileHtmlDeckAsync(deckData: HtmlDeckData, options?: HtmlComp
|
|
|
199
208
|
|
|
200
209
|
declare function normalizeSlideSlots(slide: any): Record<string, any>;
|
|
201
210
|
|
|
202
|
-
|
|
211
|
+
interface SanitizedSlideHtml {
|
|
212
|
+
html: string;
|
|
213
|
+
/** Human-readable notes about what was stripped — surfaced to logs/repair prompts. */
|
|
214
|
+
removed: string[];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Strips executable and externally-fetching content from authored slide markup.
|
|
218
|
+
* Idempotent: sanitizing sanitized output changes nothing.
|
|
219
|
+
*/
|
|
220
|
+
declare function sanitizeSlideHtml(raw: string): SanitizedSlideHtml;
|
|
221
|
+
interface BespokeSlideValidation {
|
|
222
|
+
valid: boolean;
|
|
223
|
+
issues: string[];
|
|
224
|
+
}
|
|
225
|
+
interface BespokeSlideContract {
|
|
226
|
+
/** Hard cap on authored characters — bounds prompt/token waste per slide. */
|
|
227
|
+
maxChars?: number;
|
|
228
|
+
/** Minimum characters of real content, i.e. not an empty shell. */
|
|
229
|
+
minChars?: number;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Fail-closed contract for authored slide markup. A slide that fails here must
|
|
233
|
+
* be rejected (or sent back for repair) — never compiled into the deck.
|
|
234
|
+
*/
|
|
235
|
+
declare function validateBespokeSlideHtml(raw: string, contract?: BespokeSlideContract): BespokeSlideValidation;
|
|
236
|
+
/** Wraps sanitized authored markup into the stage contract the deck shell expects. */
|
|
237
|
+
declare function buildBespokeSlideMarkup(raw: string): string;
|
|
238
|
+
|
|
239
|
+
declare const SLIDE_UTILITY_CSS: string;
|
|
240
|
+
/** Every class defined by {@link SLIDE_UTILITY_CSS} — used by the coverage guard. */
|
|
241
|
+
declare function listUtilityClasses(): string[];
|
|
242
|
+
|
|
243
|
+
export { type BespokeSlideContract, type BespokeSlideValidation, CompiledHtmlDeckResult, type GenerateHtmlShellOptions, HTML_SLIDE_PRESETS, HTML_THEMES, HtmlCompileOptions, HtmlDeckData, HtmlDeckTheme, HtmlLayoutPresetId, HtmlSlidePreset, HtmlThemeName, MermaidBlock, type PptxExportOptions, SLIDE_UTILITY_CSS, type SanitizedSlideHtml, SlideEngine, SlideEngineConfig, SlideLayoutData, ThemeTokens, buildBespokeSlideMarkup, cleanHexColor, compileHtmlDeck, compileHtmlDeckAsync, convertMarkdownToPptx, convertTextArrows, createEngine, darkThemeTokens, detectMermaidBlocks, extractLayoutsAndNotes, extractMarpSource, filterPresenterNotes, generateHtmlShell, generateLayoutCss, generateScopedCss as generateScopedCssFromTokens, getHighlighter, highlightCodeBlocks, highlightDeckCodeBlocks, isLayoutComment, lightThemeTokens, listUtilityClasses, navyThemeTokens, normalizeSlideSlots, parseFontSizeToPt, parseLayoutFromComment, removeSlideElementFromMarkdown, renderMermaidDiagrams, resolveHtmlTheme, sanitizeMermaidCode, sanitizeSlideHtml, serializeLayoutToComment, splitMarkdownSlides, updateSlideLayoutInMarkdown, updateSlideTextInMarkdown, validateBespokeSlideHtml };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { resolveTheme, generateScopedCss2, renderSlideAsync, renderSlide } from './chunk-
|
|
2
|
-
export { convertTextArrows, countSlides, darkThemeTokens, defineTheme, detectMermaidBlocks, extractLayoutsAndNotes, extractMarpSource, extractMaxClicks, filterPresenterNotes, generateClickCss, generateLayoutCss, generateScopedCss2 as generateScopedCss, generateScopedCss as generateScopedCssFromTokens, getThemeTokens, isLayoutComment, lightThemeTokens, navyThemeTokens, parseLayoutFromComment, postprocessClicks, preprocessClicks, renderMarp, renderMermaidDiagrams, renderSlide, renderSlideAsync, resolveTheme, sanitizeMermaidCode } from './chunk-
|
|
3
|
-
import { splitMarkdownSlides } from './chunk-
|
|
4
|
-
export {
|
|
1
|
+
import { resolveTheme, generateScopedCss2, renderSlideAsync, renderSlide } from './chunk-3DXMAPAY.js';
|
|
2
|
+
export { convertTextArrows, countSlides, darkThemeTokens, defineTheme, detectMermaidBlocks, extractLayoutsAndNotes, extractMarpSource, extractMaxClicks, filterPresenterNotes, generateClickCss, generateLayoutCss, generateScopedCss2 as generateScopedCss, generateScopedCss as generateScopedCssFromTokens, getThemeTokens, isLayoutComment, lightThemeTokens, navyThemeTokens, parseLayoutFromComment, postprocessClicks, preprocessClicks, renderMarp, renderMermaidDiagrams, renderSlide, renderSlideAsync, resolveTheme, sanitizeMermaidCode } from './chunk-3DXMAPAY.js';
|
|
3
|
+
import { splitMarkdownSlides } from './chunk-V6X7D44H.js';
|
|
4
|
+
export { SLIDE_LAYOUT_PRESETS, getSlideLayoutPresetById, getSlideLayoutPresetsByCategory, removeSlideElementFromMarkdown, serializeLayoutToComment, splitMarkdownSlides, updateSlideLayoutInMarkdown, updateSlideTextInMarkdown } from './chunk-V6X7D44H.js';
|
|
5
|
+
export { HTML_SLIDE_PRESETS, HTML_THEMES, SLIDE_UTILITY_CSS, buildBespokeSlideMarkup, compileHtmlDeck, compileHtmlDeckAsync, generateHtmlShell, getHighlighter, highlightCodeBlocks, highlightDeckCodeBlocks, listUtilityClasses, normalizeSlideSlots, resolveHtmlTheme, sanitizeSlideHtml, validateBespokeSlideHtml } from './chunk-5HIDQQHE.js';
|
|
5
6
|
export { FRONTEND_SLIDES_CORE_RULES, STYLE_PRESETS, TEMPLATES_CATALOG, VIEWPORT_BASE_CSS, frontendSlidesTools, getFrontendSlidesSkillPrompt, getSlideStylePresetSchema, getStylePreset, getStylePresetById, getTemplatesCatalog, querySlideTemplatesSchema, searchTemplates } from './chunk-VRXEXUHR.js';
|
|
6
7
|
|
|
7
8
|
// src/engine.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine.ts","../src/core/export/pptx-converter.ts"],"names":["generateScopedCss"],"mappings":"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","file":"index.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════════\n// Engine Factory — createEngine() for advanced usage\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { SlideEngine, SlideEngineConfig, SlideRenderOptions, SlideRenderResult, SlideRenderAsyncResult, BuiltInTheme, SlideTheme } from './types.js';\nimport { renderSlide, renderSlideAsync } from './core/render.js';\nimport { resolveTheme, generateScopedCss } from './themes/index.js';\n\n/**\n * Create a slide engine instance with default configuration.\n *\n * @example\n * ```ts\n * const engine = createEngine({ theme: 'dark' });\n *\n * // Synchronous\n * const result = engine.render(markdown);\n *\n * // Async (with Shiki highlighting)\n * const fullResult = await engine.renderAsync(markdown);\n *\n * // Get theme CSS independently\n * const css = engine.getThemeCss('navy');\n * ```\n */\nexport function createEngine(config: SlideEngineConfig = {}): SlideEngine {\n const defaultTheme = config.theme ?? 'dark';\n const defaultEnableShiki = config.enableShiki ?? true;\n const defaultEnableMermaid = config.enableMermaid ?? true;\n\n function mergeOptions(options?: SlideRenderOptions): SlideRenderOptions {\n return {\n theme: options?.theme ?? defaultTheme,\n enableShiki: options?.enableShiki ?? defaultEnableShiki,\n enableMermaid: options?.enableMermaid ?? defaultEnableMermaid,\n containerId: options?.containerId,\n };\n }\n\n return {\n render: (markdown: string, options?: SlideRenderOptions): SlideRenderResult => {\n return renderSlide(markdown, mergeOptions(options));\n },\n\n renderAsync: async (markdown: string, options?: SlideRenderOptions): Promise<SlideRenderAsyncResult> => {\n return renderSlideAsync(markdown, mergeOptions(options));\n },\n\n getThemeCss: (theme: BuiltInTheme | SlideTheme, containerId?: string): string => {\n const resolved = resolveTheme(theme);\n return generateScopedCss(resolved, containerId);\n },\n };\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// pptx-converter.ts — Pure JS converter: Marp Markdown + Layouts → Native PPTX\n//\n// Generates native, fully editable PowerPoint textboxes and shapes\n// that open directly in PowerPoint, Keynote, and Google Slides.\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport { renderSlide } from '../render.js';\nimport { splitMarkdownSlides } from '../layout/serializer.js';\nimport type { LayoutBoxConfig } from '../../types.js';\n\nexport interface PptxExportOptions {\n title?: string;\n author?: string;\n theme?: 'dark' | 'light' | 'navy';\n}\n\n/**\n * Convert font size string ('2.5rem', '32px', '24pt') to pt number for PPTX\n */\nexport function parseFontSizeToPt(sizeStr?: string, defaultPt: number = 18): number {\n if (!sizeStr) return defaultPt;\n const trimmed = sizeStr.trim().toLowerCase();\n\n if (trimmed.endsWith('rem')) {\n const rem = parseFloat(trimmed);\n return Math.round(rem * 16 * 0.75) || defaultPt;\n }\n if (trimmed.endsWith('px')) {\n const px = parseFloat(trimmed);\n return Math.round(px * 0.75) || defaultPt;\n }\n if (trimmed.endsWith('pt')) {\n return parseInt(trimmed, 10) || defaultPt;\n }\n const num = parseFloat(trimmed);\n return !isNaN(num) ? Math.round(num) : defaultPt;\n}\n\n/**\n * Clean hex color string (removes leading #)\n */\nexport function cleanHexColor(colorStr?: string, defaultHex: string = '1F2937'): string {\n if (!colorStr) return defaultHex;\n const hex = colorStr.trim().replace(/^#/, '');\n if (/^[0-9A-Fa-f]{6}$/.test(hex)) return hex.toUpperCase();\n if (/^[0-9A-Fa-f]{3}$/.test(hex)) {\n return hex.split('').map(c => c + c).join('').toUpperCase();\n }\n return defaultHex;\n}\n\n/**\n * Extract slide-level background color directive from slide markdown\n */\nfunction extractSlideBgColor(slideMd: string, defaultColor: string): string {\n const match = slideMd.match(/<!--\\s*_backgroundColor:\\s*([^\\s>]+)\\s*-->/i);\n if (match && match[1]) {\n return cleanHexColor(match[1], defaultColor);\n }\n return defaultColor;\n}\n\n/**\n * Parse plain markdown lines into simple text items\n */\ninterface SimpleBlock {\n type: 'heading' | 'paragraph' | 'bullet' | 'code';\n level?: number;\n text: string;\n}\n\nfunction parseSimpleMarkdown(md: string): SimpleBlock[] {\n const lines = md.split('\\n');\n const blocks: SimpleBlock[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (trimmed.startsWith('<!--') && trimmed.endsWith('-->')) continue;\n\n // Heading\n const hMatch = trimmed.match(/^(#{1,6})\\s+(.*)$/);\n if (hMatch && hMatch[1] && hMatch[2]) {\n blocks.push({\n type: 'heading',\n level: hMatch[1].length,\n text: hMatch[2].replace(/\\*\\*(.*?)\\*\\*/g, '$1').replace(/\\*(.*?)\\*/g, '$1'),\n });\n continue;\n }\n\n // Bullet\n if (/^[-*+]\\s+/.test(trimmed)) {\n blocks.push({\n type: 'bullet',\n text: trimmed.replace(/^[-*+]\\s+/, '').replace(/\\*\\*(.*?)\\*\\*/g, '$1'),\n });\n continue;\n }\n\n // Paragraph\n blocks.push({\n type: 'paragraph',\n text: trimmed.replace(/\\*\\*(.*?)\\*\\*/g, '$1').replace(/\\*(.*?)\\*/g, '$1'),\n });\n }\n\n return blocks;\n}\n\n/**\n * Convert Marp Markdown presentation to an editable PPTX file using pptxgenjs.\n *\n * Each box in `layout.boxes` maps directly from 1280x720 canvas px to 13.33 x 7.5 inch PPTX coordinates.\n */\nexport async function convertMarkdownToPptx(\n markdown: string,\n options?: PptxExportOptions\n): Promise<Uint8Array> {\n const PptxGenModule = await import('pptxgenjs');\n const PptxGenClass = (PptxGenModule as any).default || PptxGenModule;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pptx = new (PptxGenClass as any)();\n\n pptx.layout = 'LAYOUT_WIDE'; // 16:9 aspect ratio (13.33 x 7.5 inches)\n if (options?.title) pptx.title = options.title;\n if (options?.author) pptx.author = options.author;\n\n const renderResult = renderSlide(markdown);\n const { slides } = splitMarkdownSlides(markdown);\n\n const isDark = options?.theme !== 'light';\n const defaultBg = isDark ? '14151B' : 'FFFFFF';\n const defaultText = isDark ? 'F1F5F9' : '1E293B';\n const defaultHeading = isDark ? '38BDF8' : '0284C7';\n\n // Coordinate scales from 1280x720 to 13.33x7.5 inches\n const scaleX = 13.33 / 1280;\n const scaleY = 7.5 / 720;\n\n slides.forEach((slideMd, slideIndex) => {\n const pptxSlide = pptx.addSlide();\n\n // 1. Set background\n const bgColor = extractSlideBgColor(slideMd, defaultBg);\n pptxSlide.background = { color: bgColor };\n\n // 2. Add Speaker / Teacher Notes\n const notes = renderResult.notes?.[slideIndex];\n if (notes && notes.length > 0) {\n pptxSlide.addNotes(notes.join('\\n\\n'));\n }\n\n // 3. Layout boxes\n const layout = renderResult.layouts?.[slideIndex];\n const blocks = parseSimpleMarkdown(slideMd);\n\n if (layout && layout.boxes && layout.boxes.length > 0) {\n // Freeform layout: Render boxes with exact pos\n for (const box of layout.boxes) {\n const [x, y, w, h] = box.pos;\n const xInch = Math.max(0, x * scaleX);\n const yInch = Math.max(0, y * scaleY);\n const wInch = Math.min(13.33 - xInch, w * scaleX);\n const hInch = Math.min(7.5 - yInch, h * scaleY);\n\n // Find corresponding text\n let contentText = '';\n if (box.target === 'h1') {\n contentText = blocks.find(b => b.type === 'heading' && b.level === 1)?.text || 'Heading';\n } else if (box.target === 'h2') {\n contentText = blocks.find(b => b.type === 'heading' && b.level === 2)?.text || 'Subheading';\n } else {\n contentText = blocks.find(b => b.type === 'paragraph' || b.type === 'bullet')?.text || 'Content block';\n }\n\n const fontSizePt = parseFontSizeToPt(box.fontSize, box.target === 'h1' ? 28 : 16);\n const colorHex = cleanHexColor(box.color, box.target === 'h1' ? defaultHeading : defaultText);\n\n pptxSlide.addText(contentText, {\n x: xInch,\n y: yInch,\n w: wInch,\n h: hInch,\n fontSize: fontSizePt,\n color: colorHex,\n align: box.textAlign ?? 'left',\n bold: box.target?.startsWith('h') ?? false,\n fontFace: 'Arial',\n });\n }\n } else {\n // Flow layout fallback: Stack headings and paragraphs nicely\n let currentY = 0.8;\n const marginX = 1.0;\n const contentWidth = 11.33;\n\n for (const block of blocks) {\n if (block.type === 'heading') {\n const fontSize = block.level === 1 ? 30 : block.level === 2 ? 22 : 18;\n const color = block.level === 1 ? defaultHeading : defaultText;\n\n pptxSlide.addText(block.text, {\n x: marginX,\n y: currentY,\n w: contentWidth,\n h: 0.8,\n fontSize,\n color,\n bold: true,\n fontFace: 'Arial',\n });\n currentY += 0.9;\n } else if (block.type === 'bullet') {\n pptxSlide.addText(block.text, {\n x: marginX + 0.3,\n y: currentY,\n w: contentWidth - 0.3,\n h: 0.45,\n fontSize: 15,\n color: defaultText,\n bullet: true,\n fontFace: 'Arial',\n });\n currentY += 0.5;\n } else {\n pptxSlide.addText(block.text, {\n x: marginX,\n y: currentY,\n w: contentWidth,\n h: 0.5,\n fontSize: 15,\n color: defaultText,\n fontFace: 'Arial',\n });\n currentY += 0.55;\n }\n\n if (currentY > 6.5) break; // Prevent vertical overflow off slide\n }\n }\n });\n\n const pptxArray = (await pptx.write({ outputType: 'arraybuffer' })) as ArrayBuffer;\n return new Uint8Array(pptxArray);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine.ts","../src/core/export/pptx-converter.ts"],"names":["generateScopedCss"],"mappings":"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","file":"index.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════════\n// Engine Factory — createEngine() for advanced usage\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { SlideEngine, SlideEngineConfig, SlideRenderOptions, SlideRenderResult, SlideRenderAsyncResult, BuiltInTheme, SlideTheme } from './types.js';\nimport { renderSlide, renderSlideAsync } from './core/render.js';\nimport { resolveTheme, generateScopedCss } from './themes/index.js';\n\n/**\n * Create a slide engine instance with default configuration.\n *\n * @example\n * ```ts\n * const engine = createEngine({ theme: 'dark' });\n *\n * // Synchronous\n * const result = engine.render(markdown);\n *\n * // Async (with Shiki highlighting)\n * const fullResult = await engine.renderAsync(markdown);\n *\n * // Get theme CSS independently\n * const css = engine.getThemeCss('navy');\n * ```\n */\nexport function createEngine(config: SlideEngineConfig = {}): SlideEngine {\n const defaultTheme = config.theme ?? 'dark';\n const defaultEnableShiki = config.enableShiki ?? true;\n const defaultEnableMermaid = config.enableMermaid ?? true;\n\n function mergeOptions(options?: SlideRenderOptions): SlideRenderOptions {\n return {\n theme: options?.theme ?? defaultTheme,\n enableShiki: options?.enableShiki ?? defaultEnableShiki,\n enableMermaid: options?.enableMermaid ?? defaultEnableMermaid,\n containerId: options?.containerId,\n };\n }\n\n return {\n render: (markdown: string, options?: SlideRenderOptions): SlideRenderResult => {\n return renderSlide(markdown, mergeOptions(options));\n },\n\n renderAsync: async (markdown: string, options?: SlideRenderOptions): Promise<SlideRenderAsyncResult> => {\n return renderSlideAsync(markdown, mergeOptions(options));\n },\n\n getThemeCss: (theme: BuiltInTheme | SlideTheme, containerId?: string): string => {\n const resolved = resolveTheme(theme);\n return generateScopedCss(resolved, containerId);\n },\n };\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// pptx-converter.ts — Pure JS converter: Marp Markdown + Layouts → Native PPTX\n//\n// Generates native, fully editable PowerPoint textboxes and shapes\n// that open directly in PowerPoint, Keynote, and Google Slides.\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport { renderSlide } from '../render.js';\nimport { splitMarkdownSlides } from '../layout/serializer.js';\nimport type { LayoutBoxConfig } from '../../types.js';\n\nexport interface PptxExportOptions {\n title?: string;\n author?: string;\n theme?: 'dark' | 'light' | 'navy';\n}\n\n/**\n * Convert font size string ('2.5rem', '32px', '24pt') to pt number for PPTX\n */\nexport function parseFontSizeToPt(sizeStr?: string, defaultPt: number = 18): number {\n if (!sizeStr) return defaultPt;\n const trimmed = sizeStr.trim().toLowerCase();\n\n if (trimmed.endsWith('rem')) {\n const rem = parseFloat(trimmed);\n return Math.round(rem * 16 * 0.75) || defaultPt;\n }\n if (trimmed.endsWith('px')) {\n const px = parseFloat(trimmed);\n return Math.round(px * 0.75) || defaultPt;\n }\n if (trimmed.endsWith('pt')) {\n return parseInt(trimmed, 10) || defaultPt;\n }\n const num = parseFloat(trimmed);\n return !isNaN(num) ? Math.round(num) : defaultPt;\n}\n\n/**\n * Clean hex color string (removes leading #)\n */\nexport function cleanHexColor(colorStr?: string, defaultHex: string = '1F2937'): string {\n if (!colorStr) return defaultHex;\n const hex = colorStr.trim().replace(/^#/, '');\n if (/^[0-9A-Fa-f]{6}$/.test(hex)) return hex.toUpperCase();\n if (/^[0-9A-Fa-f]{3}$/.test(hex)) {\n return hex.split('').map(c => c + c).join('').toUpperCase();\n }\n return defaultHex;\n}\n\n/**\n * Extract slide-level background color directive from slide markdown\n */\nfunction extractSlideBgColor(slideMd: string, defaultColor: string): string {\n const match = slideMd.match(/<!--\\s*_backgroundColor:\\s*([^\\s>]+)\\s*-->/i);\n if (match && match[1]) {\n return cleanHexColor(match[1], defaultColor);\n }\n return defaultColor;\n}\n\n/**\n * Parse plain markdown lines into simple text items\n */\ninterface SimpleBlock {\n type: 'heading' | 'paragraph' | 'bullet' | 'code';\n level?: number;\n text: string;\n}\n\nfunction parseSimpleMarkdown(md: string): SimpleBlock[] {\n const lines = md.split('\\n');\n const blocks: SimpleBlock[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (trimmed.startsWith('<!--') && trimmed.endsWith('-->')) continue;\n\n // Heading\n const hMatch = trimmed.match(/^(#{1,6})\\s+(.*)$/);\n if (hMatch && hMatch[1] && hMatch[2]) {\n blocks.push({\n type: 'heading',\n level: hMatch[1].length,\n text: hMatch[2].replace(/\\*\\*(.*?)\\*\\*/g, '$1').replace(/\\*(.*?)\\*/g, '$1'),\n });\n continue;\n }\n\n // Bullet\n if (/^[-*+]\\s+/.test(trimmed)) {\n blocks.push({\n type: 'bullet',\n text: trimmed.replace(/^[-*+]\\s+/, '').replace(/\\*\\*(.*?)\\*\\*/g, '$1'),\n });\n continue;\n }\n\n // Paragraph\n blocks.push({\n type: 'paragraph',\n text: trimmed.replace(/\\*\\*(.*?)\\*\\*/g, '$1').replace(/\\*(.*?)\\*/g, '$1'),\n });\n }\n\n return blocks;\n}\n\n/**\n * Convert Marp Markdown presentation to an editable PPTX file using pptxgenjs.\n *\n * Each box in `layout.boxes` maps directly from 1280x720 canvas px to 13.33 x 7.5 inch PPTX coordinates.\n */\nexport async function convertMarkdownToPptx(\n markdown: string,\n options?: PptxExportOptions\n): Promise<Uint8Array> {\n const PptxGenModule = await import('pptxgenjs');\n const PptxGenClass = (PptxGenModule as any).default || PptxGenModule;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pptx = new (PptxGenClass as any)();\n\n pptx.layout = 'LAYOUT_WIDE'; // 16:9 aspect ratio (13.33 x 7.5 inches)\n if (options?.title) pptx.title = options.title;\n if (options?.author) pptx.author = options.author;\n\n const renderResult = renderSlide(markdown);\n const { slides } = splitMarkdownSlides(markdown);\n\n const isDark = options?.theme !== 'light';\n const defaultBg = isDark ? '14151B' : 'FFFFFF';\n const defaultText = isDark ? 'F1F5F9' : '1E293B';\n const defaultHeading = isDark ? '38BDF8' : '0284C7';\n\n // Coordinate scales from 1280x720 to 13.33x7.5 inches\n const scaleX = 13.33 / 1280;\n const scaleY = 7.5 / 720;\n\n slides.forEach((slideMd, slideIndex) => {\n const pptxSlide = pptx.addSlide();\n\n // 1. Set background\n const bgColor = extractSlideBgColor(slideMd, defaultBg);\n pptxSlide.background = { color: bgColor };\n\n // 2. Add Speaker / Teacher Notes\n const notes = renderResult.notes?.[slideIndex];\n if (notes && notes.length > 0) {\n pptxSlide.addNotes(notes.join('\\n\\n'));\n }\n\n // 3. Layout boxes\n const layout = renderResult.layouts?.[slideIndex];\n const blocks = parseSimpleMarkdown(slideMd);\n\n if (layout && layout.boxes && layout.boxes.length > 0) {\n // Freeform layout: Render boxes with exact pos\n for (const box of layout.boxes) {\n const [x, y, w, h] = box.pos;\n const xInch = Math.max(0, x * scaleX);\n const yInch = Math.max(0, y * scaleY);\n const wInch = Math.min(13.33 - xInch, w * scaleX);\n const hInch = Math.min(7.5 - yInch, h * scaleY);\n\n // Find corresponding text\n let contentText = '';\n if (box.target === 'h1') {\n contentText = blocks.find(b => b.type === 'heading' && b.level === 1)?.text || 'Heading';\n } else if (box.target === 'h2') {\n contentText = blocks.find(b => b.type === 'heading' && b.level === 2)?.text || 'Subheading';\n } else {\n contentText = blocks.find(b => b.type === 'paragraph' || b.type === 'bullet')?.text || 'Content block';\n }\n\n const fontSizePt = parseFontSizeToPt(box.fontSize, box.target === 'h1' ? 28 : 16);\n const colorHex = cleanHexColor(box.color, box.target === 'h1' ? defaultHeading : defaultText);\n\n pptxSlide.addText(contentText, {\n x: xInch,\n y: yInch,\n w: wInch,\n h: hInch,\n fontSize: fontSizePt,\n color: colorHex,\n align: box.textAlign ?? 'left',\n bold: box.target?.startsWith('h') ?? false,\n fontFace: 'Arial',\n });\n }\n } else {\n // Flow layout fallback: Stack headings and paragraphs nicely\n let currentY = 0.8;\n const marginX = 1.0;\n const contentWidth = 11.33;\n\n for (const block of blocks) {\n if (block.type === 'heading') {\n const fontSize = block.level === 1 ? 30 : block.level === 2 ? 22 : 18;\n const color = block.level === 1 ? defaultHeading : defaultText;\n\n pptxSlide.addText(block.text, {\n x: marginX,\n y: currentY,\n w: contentWidth,\n h: 0.8,\n fontSize,\n color,\n bold: true,\n fontFace: 'Arial',\n });\n currentY += 0.9;\n } else if (block.type === 'bullet') {\n pptxSlide.addText(block.text, {\n x: marginX + 0.3,\n y: currentY,\n w: contentWidth - 0.3,\n h: 0.45,\n fontSize: 15,\n color: defaultText,\n bullet: true,\n fontFace: 'Arial',\n });\n currentY += 0.5;\n } else {\n pptxSlide.addText(block.text, {\n x: marginX,\n y: currentY,\n w: contentWidth,\n h: 0.5,\n fontSize: 15,\n color: defaultText,\n fontFace: 'Arial',\n });\n currentY += 0.55;\n }\n\n if (currentY > 6.5) break; // Prevent vertical overflow off slide\n }\n }\n });\n\n const pptxArray = (await pptx.write({ outputType: 'arraybuffer' })) as ArrayBuffer;\n return new Uint8Array(pptxArray);\n}\n"]}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk5WEZOGQA_cjs = require('../chunk-5WEZOGQA.cjs');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var url = require('url');
|
|
7
|
+
|
|
8
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
12
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
13
|
+
|
|
14
|
+
var fontManifest = null;
|
|
15
|
+
var fontFaceCache = /* @__PURE__ */ new Map();
|
|
16
|
+
function fontDirCandidates() {
|
|
17
|
+
const here = path__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
18
|
+
return [
|
|
19
|
+
process.env.PRESENTATION_KIT_FONT_DIR || "",
|
|
20
|
+
// dist/node/index.js → dist/assets/fonts
|
|
21
|
+
path__default.default.resolve(here, "../assets/fonts"),
|
|
22
|
+
path__default.default.resolve(here, "../..", "assets/fonts"),
|
|
23
|
+
// src/node/index.ts (tsx/vitest) → <pkg>/assets/fonts
|
|
24
|
+
path__default.default.resolve(here, "../../assets/fonts")
|
|
25
|
+
].filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
function resolveFontDir() {
|
|
28
|
+
for (const dir of fontDirCandidates()) {
|
|
29
|
+
if (fs__default.default.existsSync(path__default.default.join(dir, "SOURCES.json"))) return dir;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
function loadManifest() {
|
|
34
|
+
if (fontManifest) return fontManifest;
|
|
35
|
+
const dir = resolveFontDir();
|
|
36
|
+
if (!dir) {
|
|
37
|
+
console.warn(
|
|
38
|
+
"[presentation-kit/node] vendored fonts not found (assets/fonts/SOURCES.json) \u2014 run `node scripts/vendor-fonts.mjs`. Decks will fall back to external font links."
|
|
39
|
+
);
|
|
40
|
+
return fontManifest = [];
|
|
41
|
+
}
|
|
42
|
+
const parsed = JSON.parse(fs__default.default.readFileSync(path__default.default.join(dir, "SOURCES.json"), "utf-8"));
|
|
43
|
+
return fontManifest = parsed.fonts;
|
|
44
|
+
}
|
|
45
|
+
function base64(file, dir) {
|
|
46
|
+
return fs__default.default.readFileSync(path__default.default.join(dir, file)).toString("base64");
|
|
47
|
+
}
|
|
48
|
+
function loadEmbeddedFontFaceCss(families) {
|
|
49
|
+
const dir = resolveFontDir();
|
|
50
|
+
const manifest = loadManifest();
|
|
51
|
+
if (!dir || manifest.length === 0) return "";
|
|
52
|
+
const wanted = families && families.length > 0 ? families : manifest.map((f) => f.family);
|
|
53
|
+
const cacheKey = [...wanted].sort().join("|");
|
|
54
|
+
const cached = fontFaceCache.get(cacheKey);
|
|
55
|
+
if (cached !== void 0) return cached;
|
|
56
|
+
const blocks = [];
|
|
57
|
+
for (const font of manifest) {
|
|
58
|
+
if (!wanted.includes(font.family)) continue;
|
|
59
|
+
const data = base64(font.file, dir);
|
|
60
|
+
blocks.push(
|
|
61
|
+
`@font-face{font-family:'${font.family}';font-style:normal;font-weight:${font.weights};font-display:swap;src:url(data:font/woff2;base64,${data}) format('woff2');}`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const css = blocks.join("\n");
|
|
65
|
+
fontFaceCache.set(cacheKey, css);
|
|
66
|
+
return css;
|
|
67
|
+
}
|
|
68
|
+
function collectDeckFontFamilies(html, theme) {
|
|
69
|
+
const manifest = loadManifest();
|
|
70
|
+
const vendored = new Set(manifest.map((f) => f.family));
|
|
71
|
+
const found = /* @__PURE__ */ new Set();
|
|
72
|
+
for (const value of Object.values(theme.typography || {})) {
|
|
73
|
+
const primary = String(value).split(",")[0].replace(/["']/g, "").trim();
|
|
74
|
+
if (vendored.has(primary)) found.add(primary);
|
|
75
|
+
}
|
|
76
|
+
const authoredOnly = html.replace(/--font-(?:display|body|code)\s*:[^;]+;/g, "");
|
|
77
|
+
for (const family of vendored) {
|
|
78
|
+
if (new RegExp(`font-family[^;}]*${family.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i").test(authoredOnly)) {
|
|
79
|
+
found.add(family);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return Array.from(found);
|
|
83
|
+
}
|
|
84
|
+
function compileStandaloneHtmlDeck(deckData, options = {}) {
|
|
85
|
+
const base = chunk5WEZOGQA_cjs.compileHtmlDeck(deckData, options);
|
|
86
|
+
if (options.embedFonts === false) return { ...base, embeddedFonts: [] };
|
|
87
|
+
const families = collectDeckFontFamilies(base.html, base.theme);
|
|
88
|
+
const fontFaceCss = loadEmbeddedFontFaceCss(families);
|
|
89
|
+
if (!fontFaceCss) return { ...base, embeddedFonts: [] };
|
|
90
|
+
const html = base.html.replace(
|
|
91
|
+
/<!-- DECK_FONT_BLOCK_START -->[\s\S]*?<!-- DECK_FONT_BLOCK_END -->/,
|
|
92
|
+
`<!-- DECK_FONT_BLOCK_START -->
|
|
93
|
+
<style>
|
|
94
|
+
${fontFaceCss}
|
|
95
|
+
</style>
|
|
96
|
+
<!-- DECK_FONT_BLOCK_END -->`
|
|
97
|
+
);
|
|
98
|
+
return { ...base, html, embeddedFonts: families };
|
|
99
|
+
}
|
|
100
|
+
function findExternalReferences(html) {
|
|
101
|
+
const refs = [];
|
|
102
|
+
for (const match of html.matchAll(/(?:https?:)?\/\/[a-z0-9.-]+\.[a-z]{2,}[^\s"'<>)]*/gi)) {
|
|
103
|
+
const url = match[0];
|
|
104
|
+
if (/^\/\/(www\.)?w3\.org/i.test(url)) continue;
|
|
105
|
+
if (/xmlns/i.test(html.slice(Math.max(0, (match.index ?? 0) - 24), match.index ?? 0))) continue;
|
|
106
|
+
refs.push(url);
|
|
107
|
+
}
|
|
108
|
+
return Array.from(new Set(refs));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
exports.collectDeckFontFamilies = collectDeckFontFamilies;
|
|
112
|
+
exports.compileStandaloneHtmlDeck = compileStandaloneHtmlDeck;
|
|
113
|
+
exports.findExternalReferences = findExternalReferences;
|
|
114
|
+
exports.loadEmbeddedFontFaceCss = loadEmbeddedFontFaceCss;
|
|
115
|
+
exports.resolveFontDir = resolveFontDir;
|
|
116
|
+
//# sourceMappingURL=index.cjs.map
|
|
117
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/node/index.ts"],"names":["path","fileURLToPath","fs","compileHtmlDeck"],"mappings":"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","file":"index.cjs","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit/node — Self-contained deck assembly (Node only)\n//\n// Kept OUT of the main entry on purpose: the browser entry (`compileHtmlDeck`,\n// `renderSlide`) must never pull `node:fs`. Server-side callers (curriculum-kit's\n// slide workflow, the app's artifact pipeline) use this module to produce a deck\n// that renders with typography intact and WITHOUT a single outbound request.\n//\n// What \"self-contained\" has to cover (measured on the old artifacts):\n// 1. fonts — were `<link>` to fonts.googleapis.com (gone offline → system fonts)\n// 2. Tailwind — was `<script src=\"https://cdn.tailwindcss.com\">` (gone offline →\n// every slot-layout slide rendered unstyled)\n// 3. anything the AUTHORED slides bring in (handled by the sanitizer)\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { compileHtmlDeck } from '../html-engine/compiler.js';\nimport type { HtmlCompileOptions, HtmlDeckData } from '../html-engine/types.js';\n\ninterface FontSource {\n family: string;\n file: string;\n weights: string;\n license: string;\n source: string;\n}\n\nlet fontManifest: FontSource[] | null = null;\nlet fontFaceCache = new Map<string, string>();\n\n/** Candidate asset dirs: published `dist/assets/fonts`, then the repo's `assets/fonts`. */\nfunction fontDirCandidates(): string[] {\n const here = path.dirname(fileURLToPath(import.meta.url));\n return [\n process.env.PRESENTATION_KIT_FONT_DIR || '',\n // dist/node/index.js → dist/assets/fonts\n path.resolve(here, '../assets/fonts'),\n path.resolve(here, '../..', 'assets/fonts'),\n // src/node/index.ts (tsx/vitest) → <pkg>/assets/fonts\n path.resolve(here, '../../assets/fonts'),\n ].filter(Boolean) as string[];\n}\n\nexport function resolveFontDir(): string | null {\n for (const dir of fontDirCandidates()) {\n if (fs.existsSync(path.join(dir, 'SOURCES.json'))) return dir;\n }\n return null;\n}\n\nfunction loadManifest(): FontSource[] {\n if (fontManifest) return fontManifest;\n const dir = resolveFontDir();\n if (!dir) {\n console.warn(\n '[presentation-kit/node] vendored fonts not found (assets/fonts/SOURCES.json) — ' +\n 'run `node scripts/vendor-fonts.mjs`. Decks will fall back to external font links.'\n );\n return (fontManifest = []);\n }\n const parsed = JSON.parse(fs.readFileSync(path.join(dir, 'SOURCES.json'), 'utf-8'));\n return (fontManifest = parsed.fonts as FontSource[]);\n}\n\nfunction base64(file: string, dir: string): string {\n return fs.readFileSync(path.join(dir, file)).toString('base64');\n}\n\n/**\n * `@font-face` CSS with base64 woff2 for the requested families (all vendored\n * families when omitted). Returns '' when nothing is vendored, so the shell\n * keeps its (external) fallback instead of silently rendering with no fonts.\n */\nexport function loadEmbeddedFontFaceCss(families?: string[]): string {\n const dir = resolveFontDir();\n const manifest = loadManifest();\n if (!dir || manifest.length === 0) return '';\n\n const wanted = families && families.length > 0 ? families : manifest.map((f) => f.family);\n const cacheKey = [...wanted].sort().join('|');\n const cached = fontFaceCache.get(cacheKey);\n if (cached !== undefined) return cached;\n\n const blocks: string[] = [];\n for (const font of manifest) {\n if (!wanted.includes(font.family)) continue;\n const data = base64(font.file, dir);\n blocks.push(\n `@font-face{font-family:'${font.family}';font-style:normal;font-weight:${font.weights};` +\n `font-display:swap;src:url(data:font/woff2;base64,${data}) format('woff2');}`\n );\n }\n const css = blocks.join('\\n');\n fontFaceCache.set(cacheKey, css);\n return css;\n}\n\n/**\n * Which vendored families a compiled deck actually needs: the active theme's\n * display/body/code pairing, plus any vendored family named in the markup\n * (authored slides may reference one directly).\n */\nexport function collectDeckFontFamilies(html: string, theme: { typography?: Record<string, string> }): string[] {\n const manifest = loadManifest();\n const vendored = new Set(manifest.map((f) => f.family));\n const found = new Set<string>();\n\n // Only the PRIMARY family of each stack needs embedding: fallbacks exist for\n // when the primary is unavailable, and an embedded primary never is. (The old\n // code also pulled every fallback, e.g. Fira Code behind JetBrains Mono → +35KB\n // per deck for a font that can never be reached.)\n for (const value of Object.values(theme.typography || {})) {\n const primary = String(value).split(',')[0].replace(/[\"']/g, '').trim();\n if (vendored.has(primary)) found.add(primary);\n }\n // Authored slides may name a family directly (`font-family: 'Fira Code'`).\n // The theme's own variable declarations are excluded: they legitimately list\n // fallbacks, which must not drag a whole extra font into every deck.\n const authoredOnly = html.replace(/--font-(?:display|body|code)\\s*:[^;]+;/g, '');\n for (const family of vendored) {\n if (new RegExp(`font-family[^;}]*${family.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}`, 'i').test(authoredOnly)) {\n found.add(family);\n }\n }\n return Array.from(found);\n}\n\nexport interface StandaloneCompileOptions extends HtmlCompileOptions {\n /** Skip embedding fonts (returns the deck with external font links). */\n embedFonts?: boolean;\n}\n\n/**\n * Compiles a deck that needs no network at all: fonts embedded, utilities\n * inlined (the shell does that unconditionally now), markup sanitized.\n */\nexport function compileStandaloneHtmlDeck(\n deckData: HtmlDeckData,\n options: StandaloneCompileOptions = {}\n): { html: string; slideCount: number; theme: unknown; title: string; embeddedFonts: string[] } {\n const base = compileHtmlDeck(deckData, options);\n if (options.embedFonts === false) return { ...base, embeddedFonts: [] };\n\n const families = collectDeckFontFamilies(base.html, base.theme as any);\n const fontFaceCss = loadEmbeddedFontFaceCss(families);\n if (!fontFaceCss) return { ...base, embeddedFonts: [] };\n\n // The shell marks the block so this is an exact, single replacement — no\n // regex surgery on the rest of the document.\n const html = base.html.replace(\n /<!-- DECK_FONT_BLOCK_START -->[\\s\\S]*?<!-- DECK_FONT_BLOCK_END -->/,\n `<!-- DECK_FONT_BLOCK_START -->\\n <style>\\n${fontFaceCss}\\n </style>\\n <!-- DECK_FONT_BLOCK_END -->`\n );\n\n return { ...base, html, embeddedFonts: families };\n}\n\n/** Off-box references a compiled deck still carries — must be empty to ship. */\nexport function findExternalReferences(html: string): string[] {\n const refs: string[] = [];\n for (const match of html.matchAll(/(?:https?:)?\\/\\/[a-z0-9.-]+\\.[a-z]{2,}[^\\s\"'<>)]*/gi)) {\n const url = match[0];\n // Namespaced identifiers (xmlns, schema URLs) are values, not fetches.\n if (/^\\/\\/(www\\.)?w3\\.org/i.test(url)) continue;\n if (/xmlns/i.test(html.slice(Math.max(0, (match.index ?? 0) - 24), match.index ?? 0))) continue;\n refs.push(url);\n }\n return Array.from(new Set(refs));\n}\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { H as HtmlCompileOptions, a as HtmlDeckData } from '../types-k_eae8UV.cjs';
|
|
2
|
+
|
|
3
|
+
declare function resolveFontDir(): string | null;
|
|
4
|
+
/**
|
|
5
|
+
* `@font-face` CSS with base64 woff2 for the requested families (all vendored
|
|
6
|
+
* families when omitted). Returns '' when nothing is vendored, so the shell
|
|
7
|
+
* keeps its (external) fallback instead of silently rendering with no fonts.
|
|
8
|
+
*/
|
|
9
|
+
declare function loadEmbeddedFontFaceCss(families?: string[]): string;
|
|
10
|
+
/**
|
|
11
|
+
* Which vendored families a compiled deck actually needs: the active theme's
|
|
12
|
+
* display/body/code pairing, plus any vendored family named in the markup
|
|
13
|
+
* (authored slides may reference one directly).
|
|
14
|
+
*/
|
|
15
|
+
declare function collectDeckFontFamilies(html: string, theme: {
|
|
16
|
+
typography?: Record<string, string>;
|
|
17
|
+
}): string[];
|
|
18
|
+
interface StandaloneCompileOptions extends HtmlCompileOptions {
|
|
19
|
+
/** Skip embedding fonts (returns the deck with external font links). */
|
|
20
|
+
embedFonts?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compiles a deck that needs no network at all: fonts embedded, utilities
|
|
24
|
+
* inlined (the shell does that unconditionally now), markup sanitized.
|
|
25
|
+
*/
|
|
26
|
+
declare function compileStandaloneHtmlDeck(deckData: HtmlDeckData, options?: StandaloneCompileOptions): {
|
|
27
|
+
html: string;
|
|
28
|
+
slideCount: number;
|
|
29
|
+
theme: unknown;
|
|
30
|
+
title: string;
|
|
31
|
+
embeddedFonts: string[];
|
|
32
|
+
};
|
|
33
|
+
/** Off-box references a compiled deck still carries — must be empty to ship. */
|
|
34
|
+
declare function findExternalReferences(html: string): string[];
|
|
35
|
+
|
|
36
|
+
export { type StandaloneCompileOptions, collectDeckFontFamilies, compileStandaloneHtmlDeck, findExternalReferences, loadEmbeddedFontFaceCss, resolveFontDir };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { H as HtmlCompileOptions, a as HtmlDeckData } from '../types-k_eae8UV.js';
|
|
2
|
+
|
|
3
|
+
declare function resolveFontDir(): string | null;
|
|
4
|
+
/**
|
|
5
|
+
* `@font-face` CSS with base64 woff2 for the requested families (all vendored
|
|
6
|
+
* families when omitted). Returns '' when nothing is vendored, so the shell
|
|
7
|
+
* keeps its (external) fallback instead of silently rendering with no fonts.
|
|
8
|
+
*/
|
|
9
|
+
declare function loadEmbeddedFontFaceCss(families?: string[]): string;
|
|
10
|
+
/**
|
|
11
|
+
* Which vendored families a compiled deck actually needs: the active theme's
|
|
12
|
+
* display/body/code pairing, plus any vendored family named in the markup
|
|
13
|
+
* (authored slides may reference one directly).
|
|
14
|
+
*/
|
|
15
|
+
declare function collectDeckFontFamilies(html: string, theme: {
|
|
16
|
+
typography?: Record<string, string>;
|
|
17
|
+
}): string[];
|
|
18
|
+
interface StandaloneCompileOptions extends HtmlCompileOptions {
|
|
19
|
+
/** Skip embedding fonts (returns the deck with external font links). */
|
|
20
|
+
embedFonts?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compiles a deck that needs no network at all: fonts embedded, utilities
|
|
24
|
+
* inlined (the shell does that unconditionally now), markup sanitized.
|
|
25
|
+
*/
|
|
26
|
+
declare function compileStandaloneHtmlDeck(deckData: HtmlDeckData, options?: StandaloneCompileOptions): {
|
|
27
|
+
html: string;
|
|
28
|
+
slideCount: number;
|
|
29
|
+
theme: unknown;
|
|
30
|
+
title: string;
|
|
31
|
+
embeddedFonts: string[];
|
|
32
|
+
};
|
|
33
|
+
/** Off-box references a compiled deck still carries — must be empty to ship. */
|
|
34
|
+
declare function findExternalReferences(html: string): string[];
|
|
35
|
+
|
|
36
|
+
export { type StandaloneCompileOptions, collectDeckFontFamilies, compileStandaloneHtmlDeck, findExternalReferences, loadEmbeddedFontFaceCss, resolveFontDir };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { compileHtmlDeck } from '../chunk-5HIDQQHE.js';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
var fontManifest = null;
|
|
7
|
+
var fontFaceCache = /* @__PURE__ */ new Map();
|
|
8
|
+
function fontDirCandidates() {
|
|
9
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
return [
|
|
11
|
+
process.env.PRESENTATION_KIT_FONT_DIR || "",
|
|
12
|
+
// dist/node/index.js → dist/assets/fonts
|
|
13
|
+
path.resolve(here, "../assets/fonts"),
|
|
14
|
+
path.resolve(here, "../..", "assets/fonts"),
|
|
15
|
+
// src/node/index.ts (tsx/vitest) → <pkg>/assets/fonts
|
|
16
|
+
path.resolve(here, "../../assets/fonts")
|
|
17
|
+
].filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
function resolveFontDir() {
|
|
20
|
+
for (const dir of fontDirCandidates()) {
|
|
21
|
+
if (fs.existsSync(path.join(dir, "SOURCES.json"))) return dir;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function loadManifest() {
|
|
26
|
+
if (fontManifest) return fontManifest;
|
|
27
|
+
const dir = resolveFontDir();
|
|
28
|
+
if (!dir) {
|
|
29
|
+
console.warn(
|
|
30
|
+
"[presentation-kit/node] vendored fonts not found (assets/fonts/SOURCES.json) \u2014 run `node scripts/vendor-fonts.mjs`. Decks will fall back to external font links."
|
|
31
|
+
);
|
|
32
|
+
return fontManifest = [];
|
|
33
|
+
}
|
|
34
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(dir, "SOURCES.json"), "utf-8"));
|
|
35
|
+
return fontManifest = parsed.fonts;
|
|
36
|
+
}
|
|
37
|
+
function base64(file, dir) {
|
|
38
|
+
return fs.readFileSync(path.join(dir, file)).toString("base64");
|
|
39
|
+
}
|
|
40
|
+
function loadEmbeddedFontFaceCss(families) {
|
|
41
|
+
const dir = resolveFontDir();
|
|
42
|
+
const manifest = loadManifest();
|
|
43
|
+
if (!dir || manifest.length === 0) return "";
|
|
44
|
+
const wanted = families && families.length > 0 ? families : manifest.map((f) => f.family);
|
|
45
|
+
const cacheKey = [...wanted].sort().join("|");
|
|
46
|
+
const cached = fontFaceCache.get(cacheKey);
|
|
47
|
+
if (cached !== void 0) return cached;
|
|
48
|
+
const blocks = [];
|
|
49
|
+
for (const font of manifest) {
|
|
50
|
+
if (!wanted.includes(font.family)) continue;
|
|
51
|
+
const data = base64(font.file, dir);
|
|
52
|
+
blocks.push(
|
|
53
|
+
`@font-face{font-family:'${font.family}';font-style:normal;font-weight:${font.weights};font-display:swap;src:url(data:font/woff2;base64,${data}) format('woff2');}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const css = blocks.join("\n");
|
|
57
|
+
fontFaceCache.set(cacheKey, css);
|
|
58
|
+
return css;
|
|
59
|
+
}
|
|
60
|
+
function collectDeckFontFamilies(html, theme) {
|
|
61
|
+
const manifest = loadManifest();
|
|
62
|
+
const vendored = new Set(manifest.map((f) => f.family));
|
|
63
|
+
const found = /* @__PURE__ */ new Set();
|
|
64
|
+
for (const value of Object.values(theme.typography || {})) {
|
|
65
|
+
const primary = String(value).split(",")[0].replace(/["']/g, "").trim();
|
|
66
|
+
if (vendored.has(primary)) found.add(primary);
|
|
67
|
+
}
|
|
68
|
+
const authoredOnly = html.replace(/--font-(?:display|body|code)\s*:[^;]+;/g, "");
|
|
69
|
+
for (const family of vendored) {
|
|
70
|
+
if (new RegExp(`font-family[^;}]*${family.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i").test(authoredOnly)) {
|
|
71
|
+
found.add(family);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return Array.from(found);
|
|
75
|
+
}
|
|
76
|
+
function compileStandaloneHtmlDeck(deckData, options = {}) {
|
|
77
|
+
const base = compileHtmlDeck(deckData, options);
|
|
78
|
+
if (options.embedFonts === false) return { ...base, embeddedFonts: [] };
|
|
79
|
+
const families = collectDeckFontFamilies(base.html, base.theme);
|
|
80
|
+
const fontFaceCss = loadEmbeddedFontFaceCss(families);
|
|
81
|
+
if (!fontFaceCss) return { ...base, embeddedFonts: [] };
|
|
82
|
+
const html = base.html.replace(
|
|
83
|
+
/<!-- DECK_FONT_BLOCK_START -->[\s\S]*?<!-- DECK_FONT_BLOCK_END -->/,
|
|
84
|
+
`<!-- DECK_FONT_BLOCK_START -->
|
|
85
|
+
<style>
|
|
86
|
+
${fontFaceCss}
|
|
87
|
+
</style>
|
|
88
|
+
<!-- DECK_FONT_BLOCK_END -->`
|
|
89
|
+
);
|
|
90
|
+
return { ...base, html, embeddedFonts: families };
|
|
91
|
+
}
|
|
92
|
+
function findExternalReferences(html) {
|
|
93
|
+
const refs = [];
|
|
94
|
+
for (const match of html.matchAll(/(?:https?:)?\/\/[a-z0-9.-]+\.[a-z]{2,}[^\s"'<>)]*/gi)) {
|
|
95
|
+
const url = match[0];
|
|
96
|
+
if (/^\/\/(www\.)?w3\.org/i.test(url)) continue;
|
|
97
|
+
if (/xmlns/i.test(html.slice(Math.max(0, (match.index ?? 0) - 24), match.index ?? 0))) continue;
|
|
98
|
+
refs.push(url);
|
|
99
|
+
}
|
|
100
|
+
return Array.from(new Set(refs));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { collectDeckFontFamilies, compileStandaloneHtmlDeck, findExternalReferences, loadEmbeddedFontFaceCss, resolveFontDir };
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
105
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/node/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit/node — Self-contained deck assembly (Node only)\n//\n// Kept OUT of the main entry on purpose: the browser entry (`compileHtmlDeck`,\n// `renderSlide`) must never pull `node:fs`. Server-side callers (curriculum-kit's\n// slide workflow, the app's artifact pipeline) use this module to produce a deck\n// that renders with typography intact and WITHOUT a single outbound request.\n//\n// What \"self-contained\" has to cover (measured on the old artifacts):\n// 1. fonts — were `<link>` to fonts.googleapis.com (gone offline → system fonts)\n// 2. Tailwind — was `<script src=\"https://cdn.tailwindcss.com\">` (gone offline →\n// every slot-layout slide rendered unstyled)\n// 3. anything the AUTHORED slides bring in (handled by the sanitizer)\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { compileHtmlDeck } from '../html-engine/compiler.js';\nimport type { HtmlCompileOptions, HtmlDeckData } from '../html-engine/types.js';\n\ninterface FontSource {\n family: string;\n file: string;\n weights: string;\n license: string;\n source: string;\n}\n\nlet fontManifest: FontSource[] | null = null;\nlet fontFaceCache = new Map<string, string>();\n\n/** Candidate asset dirs: published `dist/assets/fonts`, then the repo's `assets/fonts`. */\nfunction fontDirCandidates(): string[] {\n const here = path.dirname(fileURLToPath(import.meta.url));\n return [\n process.env.PRESENTATION_KIT_FONT_DIR || '',\n // dist/node/index.js → dist/assets/fonts\n path.resolve(here, '../assets/fonts'),\n path.resolve(here, '../..', 'assets/fonts'),\n // src/node/index.ts (tsx/vitest) → <pkg>/assets/fonts\n path.resolve(here, '../../assets/fonts'),\n ].filter(Boolean) as string[];\n}\n\nexport function resolveFontDir(): string | null {\n for (const dir of fontDirCandidates()) {\n if (fs.existsSync(path.join(dir, 'SOURCES.json'))) return dir;\n }\n return null;\n}\n\nfunction loadManifest(): FontSource[] {\n if (fontManifest) return fontManifest;\n const dir = resolveFontDir();\n if (!dir) {\n console.warn(\n '[presentation-kit/node] vendored fonts not found (assets/fonts/SOURCES.json) — ' +\n 'run `node scripts/vendor-fonts.mjs`. Decks will fall back to external font links.'\n );\n return (fontManifest = []);\n }\n const parsed = JSON.parse(fs.readFileSync(path.join(dir, 'SOURCES.json'), 'utf-8'));\n return (fontManifest = parsed.fonts as FontSource[]);\n}\n\nfunction base64(file: string, dir: string): string {\n return fs.readFileSync(path.join(dir, file)).toString('base64');\n}\n\n/**\n * `@font-face` CSS with base64 woff2 for the requested families (all vendored\n * families when omitted). Returns '' when nothing is vendored, so the shell\n * keeps its (external) fallback instead of silently rendering with no fonts.\n */\nexport function loadEmbeddedFontFaceCss(families?: string[]): string {\n const dir = resolveFontDir();\n const manifest = loadManifest();\n if (!dir || manifest.length === 0) return '';\n\n const wanted = families && families.length > 0 ? families : manifest.map((f) => f.family);\n const cacheKey = [...wanted].sort().join('|');\n const cached = fontFaceCache.get(cacheKey);\n if (cached !== undefined) return cached;\n\n const blocks: string[] = [];\n for (const font of manifest) {\n if (!wanted.includes(font.family)) continue;\n const data = base64(font.file, dir);\n blocks.push(\n `@font-face{font-family:'${font.family}';font-style:normal;font-weight:${font.weights};` +\n `font-display:swap;src:url(data:font/woff2;base64,${data}) format('woff2');}`\n );\n }\n const css = blocks.join('\\n');\n fontFaceCache.set(cacheKey, css);\n return css;\n}\n\n/**\n * Which vendored families a compiled deck actually needs: the active theme's\n * display/body/code pairing, plus any vendored family named in the markup\n * (authored slides may reference one directly).\n */\nexport function collectDeckFontFamilies(html: string, theme: { typography?: Record<string, string> }): string[] {\n const manifest = loadManifest();\n const vendored = new Set(manifest.map((f) => f.family));\n const found = new Set<string>();\n\n // Only the PRIMARY family of each stack needs embedding: fallbacks exist for\n // when the primary is unavailable, and an embedded primary never is. (The old\n // code also pulled every fallback, e.g. Fira Code behind JetBrains Mono → +35KB\n // per deck for a font that can never be reached.)\n for (const value of Object.values(theme.typography || {})) {\n const primary = String(value).split(',')[0].replace(/[\"']/g, '').trim();\n if (vendored.has(primary)) found.add(primary);\n }\n // Authored slides may name a family directly (`font-family: 'Fira Code'`).\n // The theme's own variable declarations are excluded: they legitimately list\n // fallbacks, which must not drag a whole extra font into every deck.\n const authoredOnly = html.replace(/--font-(?:display|body|code)\\s*:[^;]+;/g, '');\n for (const family of vendored) {\n if (new RegExp(`font-family[^;}]*${family.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}`, 'i').test(authoredOnly)) {\n found.add(family);\n }\n }\n return Array.from(found);\n}\n\nexport interface StandaloneCompileOptions extends HtmlCompileOptions {\n /** Skip embedding fonts (returns the deck with external font links). */\n embedFonts?: boolean;\n}\n\n/**\n * Compiles a deck that needs no network at all: fonts embedded, utilities\n * inlined (the shell does that unconditionally now), markup sanitized.\n */\nexport function compileStandaloneHtmlDeck(\n deckData: HtmlDeckData,\n options: StandaloneCompileOptions = {}\n): { html: string; slideCount: number; theme: unknown; title: string; embeddedFonts: string[] } {\n const base = compileHtmlDeck(deckData, options);\n if (options.embedFonts === false) return { ...base, embeddedFonts: [] };\n\n const families = collectDeckFontFamilies(base.html, base.theme as any);\n const fontFaceCss = loadEmbeddedFontFaceCss(families);\n if (!fontFaceCss) return { ...base, embeddedFonts: [] };\n\n // The shell marks the block so this is an exact, single replacement — no\n // regex surgery on the rest of the document.\n const html = base.html.replace(\n /<!-- DECK_FONT_BLOCK_START -->[\\s\\S]*?<!-- DECK_FONT_BLOCK_END -->/,\n `<!-- DECK_FONT_BLOCK_START -->\\n <style>\\n${fontFaceCss}\\n </style>\\n <!-- DECK_FONT_BLOCK_END -->`\n );\n\n return { ...base, html, embeddedFonts: families };\n}\n\n/** Off-box references a compiled deck still carries — must be empty to ship. */\nexport function findExternalReferences(html: string): string[] {\n const refs: string[] = [];\n for (const match of html.matchAll(/(?:https?:)?\\/\\/[a-z0-9.-]+\\.[a-z]{2,}[^\\s\"'<>)]*/gi)) {\n const url = match[0];\n // Namespaced identifiers (xmlns, schema URLs) are values, not fetches.\n if (/^\\/\\/(www\\.)?w3\\.org/i.test(url)) continue;\n if (/xmlns/i.test(html.slice(Math.max(0, (match.index ?? 0) - 24), match.index ?? 0))) continue;\n refs.push(url);\n }\n return Array.from(new Set(refs));\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SlideLayoutData } from './types-
|
|
1
|
+
import { S as SlideLayoutData } from './types-CvxCxWus.js';
|
|
2
2
|
|
|
3
3
|
type SlideLayoutCategory = 'cover' | 'content' | 'split' | 'code' | 'metrics' | 'diagram' | 'quote' | 'assessment';
|
|
4
4
|
interface SlideLayoutPreset {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SlideLayoutData } from './types-
|
|
1
|
+
import { S as SlideLayoutData } from './types-CvxCxWus.cjs';
|
|
2
2
|
|
|
3
3
|
type SlideLayoutCategory = 'cover' | 'content' | 'split' | 'code' | 'metrics' | 'diagram' | 'quote' | 'assessment';
|
|
4
4
|
interface SlideLayoutPreset {
|