@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.
Files changed (56) hide show
  1. package/README.md +26 -0
  2. package/dist/ai/index.cjs +16 -15
  3. package/dist/ai/index.cjs.map +1 -1
  4. package/dist/ai/index.d.cts +3 -2
  5. package/dist/ai/index.d.ts +3 -2
  6. package/dist/ai/index.js +2 -1
  7. package/dist/ai/index.js.map +1 -1
  8. package/dist/assets/fonts/NOTICE.md +25 -0
  9. package/dist/assets/fonts/SOURCES.json +47 -0
  10. package/dist/assets/fonts/fira-code.woff2 +0 -0
  11. package/dist/assets/fonts/inter.woff2 +0 -0
  12. package/dist/assets/fonts/jetbrains-mono.woff2 +0 -0
  13. package/dist/assets/fonts/playfair-display.woff2 +0 -0
  14. package/dist/assets/fonts/plus-jakarta-sans.woff2 +0 -0
  15. package/dist/assets/fonts/space-grotesk.woff2 +0 -0
  16. package/dist/{chunk-DDOUFYLK.js → chunk-3DXMAPAY.js} +3 -3
  17. package/dist/{chunk-DDOUFYLK.js.map → chunk-3DXMAPAY.js.map} +1 -1
  18. package/dist/chunk-3Q4RMZVQ.cjs +867 -0
  19. package/dist/chunk-3Q4RMZVQ.cjs.map +1 -0
  20. package/dist/{chunk-JJQDQIRG.js → chunk-5HIDQQHE.js} +288 -855
  21. package/dist/chunk-5HIDQQHE.js.map +1 -0
  22. package/dist/{chunk-SFBEDDGU.cjs → chunk-5WEZOGQA.cjs} +292 -866
  23. package/dist/chunk-5WEZOGQA.cjs.map +1 -0
  24. package/dist/{chunk-DIHFQ5LF.cjs → chunk-CVEC72CT.cjs} +4 -4
  25. package/dist/{chunk-DIHFQ5LF.cjs.map → chunk-CVEC72CT.cjs.map} +1 -1
  26. package/dist/chunk-V6X7D44H.js +854 -0
  27. package/dist/chunk-V6X7D44H.js.map +1 -0
  28. package/dist/{click-css-Bcc1CkTN.d.cts → click-css-C8VciwZ6.d.cts} +1 -1
  29. package/dist/{click-css-B2s9B_uP.d.ts → click-css-COIiFo1I.d.ts} +1 -1
  30. package/dist/index.cjs +90 -69
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +46 -5
  33. package/dist/index.d.ts +46 -5
  34. package/dist/index.js +5 -4
  35. package/dist/index.js.map +1 -1
  36. package/dist/node/index.cjs +117 -0
  37. package/dist/node/index.cjs.map +1 -0
  38. package/dist/node/index.d.cts +36 -0
  39. package/dist/node/index.d.ts +36 -0
  40. package/dist/node/index.js +105 -0
  41. package/dist/node/index.js.map +1 -0
  42. package/dist/{presets-D_hfPJX9.d.ts → presets-DPRGtpO-.d.ts} +1 -1
  43. package/dist/{presets-BYvgAAkJ.d.cts → presets-MIi91wZg.d.cts} +1 -1
  44. package/dist/react/index.cjs +17 -16
  45. package/dist/react/index.cjs.map +1 -1
  46. package/dist/react/index.d.cts +4 -3
  47. package/dist/react/index.d.ts +4 -3
  48. package/dist/react/index.js +4 -3
  49. package/dist/react/index.js.map +1 -1
  50. package/dist/{types-C3d04zzP.d.cts → types-CvxCxWus.d.cts} +1 -67
  51. package/dist/{types-C3d04zzP.d.ts → types-CvxCxWus.d.ts} +1 -67
  52. package/dist/types-k_eae8UV.d.cts +74 -0
  53. package/dist/types-k_eae8UV.d.ts +74 -0
  54. package/package.json +8 -2
  55. package/dist/chunk-JJQDQIRG.js.map +0 -1
  56. package/dist/chunk-SFBEDDGU.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugins/shiki.ts","../src/html-engine/themes.ts","../src/html-engine/presets.ts","../src/html-engine/utilities.ts","../src/html-engine/shell.ts","../src/html-engine/normalizer.ts","../src/html-engine/sanitize.ts","../src/html-engine/compiler.ts"],"names":["decodeHtmlEntities","notesAttr","customClass"],"mappings":"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A;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;A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A;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","file":"chunk-5HIDQQHE.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════════\n// Shiki Plugin — Lazy singleton syntax highlighting for code blocks\n// Pattern: Ported from Slidev composables/useShiki.ts\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { Highlighter } from 'shiki';\n\n/** Singleton highlighter promise — created once, reused everywhere */\nlet highlighterPromise: Promise<Highlighter> | null = null;\n/** Track if singleton init failed so we can retry */\nlet highlighterFailed = false;\n\n/** Supported languages for syntax highlighting */\nconst SUPPORTED_LANGS = [\n 'javascript',\n 'typescript',\n 'python',\n 'cpp',\n 'c',\n 'java',\n 'html',\n 'css',\n 'json',\n 'yaml',\n 'bash',\n 'shell',\n 'sql',\n 'go',\n 'rust',\n 'swift',\n 'kotlin',\n 'ruby',\n 'php',\n 'xml',\n 'markdown',\n 'dockerfile',\n 'toml',\n 'ini',\n 'graphql',\n 'prisma',\n 'solidity',\n] as const;\n\n/**\n * Get or create the Shiki highlighter singleton.\n * First call lazy-loads the WASM module (~3MB); subsequent calls return cached instance.\n */\nexport async function getHighlighter(): Promise<Highlighter> {\n if (highlighterFailed) {\n // Reset so we can retry after a failure\n highlighterPromise = null;\n highlighterFailed = false;\n }\n\n if (!highlighterPromise) {\n highlighterPromise = import('shiki')\n .then((mod) =>\n mod.createHighlighter({\n themes: ['vitesse-dark', 'vitesse-light'],\n langs: [...SUPPORTED_LANGS],\n }),\n )\n .catch((err) => {\n // Reset singleton on failure so next call retries\n highlighterPromise = null;\n highlighterFailed = true;\n throw new Error(`[presentation-kit] Failed to load Shiki highlighter: ${err}`);\n });\n }\n return highlighterPromise;\n}\n\n/**\n * Decode HTML entities in code content (e.g. &lt; → <, &amp; → &)\n */\nfunction decodeHtmlEntities(html: string): string {\n return html\n .replace(/&amp;/g, '&') // MUST be first to avoid double-decode (e.g. &amp;lt; → &lt; → <)\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&#x27;/g, \"'\")\n .replace(/&nbsp;/g, ' ')\n .replace(/&#(\\d+);/g, (_, code: string) => String.fromCharCode(Number(code)))\n .replace(/&#x([0-9a-fA-F]+);/g, (_, hex: string) => String.fromCharCode(parseInt(hex, 16)));\n}\n\n/**\n * Map slide theme name to Shiki theme.\n */\nfunction getShikiTheme(theme: string): string {\n if (theme === 'light') return 'vitesse-light';\n return 'vitesse-dark';\n}\n\n/**\n * Post-process rendered HTML: replace <pre><code class=\"language-xxx\"> blocks\n * with Shiki-highlighted HTML.\n *\n * This is async because Shiki WASM must be loaded on first call.\n */\nexport async function highlightCodeBlocks(\n html: string,\n theme: string = 'dark',\n): Promise<string> {\n // Quick check — if no code blocks, return early\n if (!html.includes('<pre><code')) return html;\n\n const highlighter = await getHighlighter();\n const shikiTheme = getShikiTheme(theme);\n\n // Replace <pre><code class=\"language-xxx\">...</code></pre>\n return html.replace(\n /<pre><code class=\"language-(\\w+)\">([\\s\\S]*?)<\\/code><\\/pre>/g,\n (_, lang: string, code: string) => {\n const decoded = decodeHtmlEntities(code);\n try {\n return highlighter.codeToHtml(decoded, { lang, theme: shikiTheme });\n } catch {\n // If language not supported, return original\n return `<pre><code class=\"language-${lang}\">${code}</code></pre>`;\n }\n },\n );\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit — Curated HTML Themes\n// Inspired by beautiful-html-templates (\"Anti-AI-slop\" aesthetic standards)\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { HtmlDeckTheme, HtmlThemeName } from './types.js';\n\nexport const HTML_THEMES: Record<HtmlThemeName, HtmlDeckTheme> = {\n 'blue-professional': {\n id: 'blue-professional',\n name: 'Blue Professional',\n scheme: 'light',\n palette: {\n bg: '#FDFAE7',\n bgSecondary: '#F4EFCF',\n primary: '#1E2BFA',\n accent: '#0D18B9',\n text: '#111111',\n textMuted: '#6B6B6B',\n border: '#E2DCB9',\n },\n typography: {\n display: \"'Space Grotesk', system-ui, -apple-system, sans-serif\",\n body: \"'Inter', system-ui, -apple-system, sans-serif\",\n code: \"'JetBrains Mono', 'Fira Code', monospace\",\n },\n },\n\n 'editorial-forest': {\n id: 'editorial-forest',\n name: 'Editorial Forest',\n scheme: 'dark',\n palette: {\n bg: '#0F1F17',\n bgSecondary: '#183024',\n primary: '#D9A74A',\n accent: '#8EA696',\n text: '#F4F1EA',\n textMuted: '#A0B4A8',\n border: 'rgba(244, 241, 234, 0.12)',\n },\n typography: {\n display: \"'Playfair Display', Georgia, serif\",\n body: \"'Inter', system-ui, -apple-system, sans-serif\",\n code: \"'JetBrains Mono', monospace\",\n },\n },\n\n 'cobalt-grid': {\n id: 'cobalt-grid',\n name: 'Cobalt Grid (Tech Modern)',\n scheme: 'dark',\n palette: {\n bg: '#090D1E',\n bgSecondary: '#111836',\n primary: '#00E5FF',\n accent: '#3B82F6',\n text: '#F8FAFC',\n textMuted: '#94A3B8',\n border: 'rgba(255, 255, 255, 0.1)',\n },\n typography: {\n display: \"'Plus Jakarta Sans', system-ui, sans-serif\",\n body: \"'Inter', system-ui, sans-serif\",\n code: \"'Fira Code', 'JetBrains Mono', monospace\",\n },\n customCss: `\n .slide-canvas {\n background-image: radial-gradient(rgba(0, 229, 255, 0.05) 1px, transparent 0);\n background-size: 32px 32px;\n }\n `,\n },\n\n studio: {\n id: 'studio',\n name: 'Studio Minimalist',\n scheme: 'light',\n palette: {\n bg: '#F8F9FA',\n bgSecondary: '#EDEFF2',\n primary: '#0F172A',\n accent: '#2563EB',\n text: '#0F172A',\n textMuted: '#64748B',\n border: '#E2E8F0',\n },\n typography: {\n display: \"'Inter', system-ui, -apple-system, sans-serif\",\n body: \"'Inter', system-ui, -apple-system, sans-serif\",\n code: \"'JetBrains Mono', monospace\",\n },\n },\n\n monochrome: {\n id: 'monochrome',\n name: 'Monochrome Dark',\n scheme: 'dark',\n palette: {\n bg: '#09090B',\n bgSecondary: '#141417',\n primary: '#FAFAFA',\n accent: '#38BDF8',\n text: '#FAFAFA',\n textMuted: '#A1A1AA',\n border: 'rgba(255, 255, 255, 0.12)',\n },\n typography: {\n display: \"'Inter', system-ui, sans-serif\",\n body: \"'Inter', system-ui, sans-serif\",\n code: \"'JetBrains Mono', monospace\",\n },\n },\n};\n\nexport function resolveHtmlTheme(themeName?: HtmlThemeName): HtmlDeckTheme {\n if (themeName && HTML_THEMES[themeName]) {\n return HTML_THEMES[themeName];\n }\n return HTML_THEMES['blue-professional'];\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit — 8 Standard Pedagogical Slide Layout Presets\n// Calibrated for 1920x1080 Stage Canvas (Frontend Slides & Beautiful HTML specs)\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { HtmlDeckTheme, HtmlSlidePreset, HtmlLayoutPresetId } from './types.js';\n\nfunction escapeHtml(str: any): string {\n if (str === undefined || str === null) return '';\n return String(str)\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n}\n\nexport const HTML_SLIDE_PRESETS: Record<HtmlLayoutPresetId, HtmlSlidePreset> = {\n 'hero-cover': {\n id: 'hero-cover',\n name: 'Hero Cover',\n category: 'cover',\n description: 'Title slide with topic badge, large display heading, subtitle, and author/date info',\n slots: [\n { name: 'tag', label: 'Badge / Tag', type: 'badge', description: 'e.g. Unit 1 · Lesson 3' },\n { name: 'title', label: 'Main Title', type: 'text', description: 'Main title of the lesson or deck', required: true },\n { name: 'subtitle', label: 'Subtitle', type: 'multiline', description: 'Brief description or learning goal' },\n { name: 'author', label: 'Instructor / Author', type: 'text', description: 'Instructor name or school' },\n { name: 'date', label: 'Date / Period', type: 'text', description: 'Date or semester' },\n ],\n template: (slots, theme) => `\n <div class=\"h-full w-full flex flex-col justify-between p-20 relative overflow-hidden\">\n <div class=\"space-y-6 max-w-4xl z-10 my-auto\">\n ${slots.tag ? `<div class=\"inline-flex items-center px-4 py-1.5 rounded-full text-sm font-bold tracking-widest uppercase border border-current opacity-90\" style=\"color: var(--primary);\">${escapeHtml(slots.tag)}</div>` : ''}\n <h1 class=\"text-6xl font-extrabold leading-tight tracking-tight\" style=\"font-family: var(--font-display); color: var(--text);\">\n ${escapeHtml(slots.title || 'Slide Title')}\n </h1>\n ${slots.subtitle ? `<p class=\"text-2xl leading-relaxed opacity-85 max-w-3xl\" style=\"color: var(--text-muted);\">${escapeHtml(slots.subtitle)}</p>` : ''}\n </div>\n <div class=\"flex items-center justify-between border-t pt-6 text-base opacity-75 z-10\" style=\"border-color: var(--border); color: var(--text-muted);\">\n <span class=\"font-medium\">${escapeHtml(slots.author || '')}</span>\n <span class=\"font-mono\">${escapeHtml(slots.date || '')}</span>\n </div>\n </div>\n `,\n },\n\n 'split-concept-code': {\n id: 'split-concept-code',\n name: 'Split Concept & Code',\n category: 'split',\n description: 'Left column for theoretical concept & bullet points, right column for code walkthrough',\n slots: [\n { name: 'tag', label: 'Section Tag', type: 'badge', description: 'e.g. Syntax & Example' },\n { name: 'title', label: 'Slide Title', type: 'text', description: 'Heading for this topic', required: true },\n { name: 'points', label: 'Key Points', type: 'list', description: 'Array of key takeaways or bullet points' },\n { name: 'code', label: 'Code Snippet', type: 'code', description: 'Source code', required: true },\n { name: 'language', label: 'Programming Language', type: 'text', description: 'e.g. python, typescript, swift' },\n { name: 'codeNote', label: 'Code Footnote / Explanation', type: 'text', description: 'Small tip under the code box' },\n ],\n template: (slots, theme) => {\n const points = Array.isArray(slots.points) ? slots.points : [];\n return `\n <div class=\"h-full w-full flex flex-col p-16 gap-6 justify-between\">\n <div>\n ${slots.tag ? `<span class=\"text-xs uppercase tracking-widest font-bold opacity-80\" style=\"color: var(--primary);\">${escapeHtml(slots.tag)}</span>` : ''}\n <h2 class=\"text-4xl font-bold mt-1.5\" style=\"font-family: var(--font-display); color: var(--text);\">${escapeHtml(slots.title || '')}</h2>\n </div>\n <div class=\"flex-1 grid grid-cols-12 gap-8 items-center min-h-0\">\n <div class=\"col-span-5 flex flex-col justify-center space-y-4\">\n <ul class=\"space-y-4\">\n ${points.map((pt: string) => `\n <li class=\"flex items-start gap-3 text-lg leading-relaxed\" style=\"color: var(--text);\">\n <span class=\"inline-block mt-2 w-2 h-2 rounded-full flex-shrink-0\" style=\"background-color: var(--primary);\"></span>\n <span>${escapeHtml(pt)}</span>\n </li>\n `).join('')}\n </ul>\n </div>\n <div class=\"col-span-7 flex flex-col rounded-xl overflow-hidden border shadow-lg\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\">\n <div class=\"flex items-center justify-between px-5 py-2.5 border-b text-xs font-mono opacity-80\" style=\"border-color: var(--border); color: var(--text-muted);\">\n <div class=\"flex items-center gap-1.5\">\n <span class=\"w-3 h-3 rounded-full bg-red-500/70 inline-block\"></span>\n <span class=\"w-3 h-3 rounded-full bg-yellow-500/70 inline-block\"></span>\n <span class=\"w-3 h-3 rounded-full bg-green-500/70 inline-block\"></span>\n </div>\n <span class=\"uppercase tracking-wider font-semibold\">${escapeHtml(slots.language || 'code')}</span>\n </div>\n <pre class=\"flex-1 p-6 text-sm font-mono overflow-hidden leading-relaxed\" style=\"color: var(--text);\"><code>${escapeHtml(slots.code || '')}</code></pre>\n ${slots.codeNote ? `<div class=\"px-5 py-2.5 border-t text-xs font-mono opacity-80\" style=\"border-color: var(--border); color: var(--text-muted);\">${escapeHtml(slots.codeNote)}</div>` : ''}\n </div>\n </div>\n </div>\n `;\n },\n },\n\n 'two-columns-compare': {\n id: 'two-columns-compare',\n name: 'Two Columns Compare',\n category: 'split',\n description: 'Side-by-side comparison for Pros/Cons, Approaches, or Before/After',\n slots: [\n { name: 'tag', label: 'Section Tag', type: 'badge', description: 'e.g. Comparison' },\n { name: 'title', label: 'Slide Title', type: 'text', description: 'Comparison headline', required: true },\n { name: 'col1Title', label: 'Left Column Title', type: 'text', description: 'e.g. Approach A / Pros' },\n { name: 'col1Items', label: 'Left Column Items', type: 'list', description: 'Array of points' },\n { name: 'col2Title', label: 'Right Column Title', type: 'text', description: 'e.g. Approach B / Cons' },\n { name: 'col2Items', label: 'Right Column Items', type: 'list', description: 'Array of points' },\n ],\n template: (slots, theme) => {\n const col1 = Array.isArray(slots.col1Items) ? slots.col1Items : [];\n const col2 = Array.isArray(slots.col2Items) ? slots.col2Items : [];\n return `\n <div class=\"h-full w-full flex flex-col p-16 gap-6 justify-between\">\n <div>\n ${slots.tag ? `<span class=\"text-xs uppercase tracking-widest font-bold opacity-80\" style=\"color: var(--primary);\">${escapeHtml(slots.tag)}</span>` : ''}\n <h2 class=\"text-4xl font-bold mt-1.5\" style=\"font-family: var(--font-display); color: var(--text);\">${escapeHtml(slots.title || '')}</h2>\n </div>\n <div class=\"flex-1 grid grid-cols-2 gap-8 items-stretch min-h-0 my-auto\">\n <div class=\"p-8 rounded-xl border flex flex-col space-y-4 shadow-sm justify-center\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\">\n <h3 class=\"text-2xl font-bold\" style=\"color: var(--primary);\">${escapeHtml(slots.col1Title || 'Column 1')}</h3>\n <ul class=\"space-y-3\">\n ${col1.map((item: string) => `\n <li class=\"flex items-start gap-3 text-lg leading-relaxed\" style=\"color: var(--text);\">\n <span class=\"opacity-60 mt-0.5 font-bold\">•</span>\n <span>${escapeHtml(item)}</span>\n </li>\n `).join('')}\n </ul>\n </div>\n <div class=\"p-8 rounded-xl border flex flex-col space-y-4 shadow-sm justify-center\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\">\n <h3 class=\"text-2xl font-bold\" style=\"color: var(--text);\">${escapeHtml(slots.col2Title || 'Column 2')}</h3>\n <ul class=\"space-y-3\">\n ${col2.map((item: string) => `\n <li class=\"flex items-start gap-3 text-lg leading-relaxed\" style=\"color: var(--text);\">\n <span class=\"opacity-60 mt-0.5 font-bold\">•</span>\n <span>${escapeHtml(item)}</span>\n </li>\n `).join('')}\n </ul>\n </div>\n </div>\n </div>\n `;\n },\n },\n\n 'three-cards-grid': {\n id: 'three-cards-grid',\n name: 'Three Cards Grid',\n category: 'grid',\n description: '3 cards showcasing 3 key pillars, concepts, or architectural layers',\n slots: [\n { name: 'tag', label: 'Section Tag', type: 'badge', description: 'e.g. Core Pillars' },\n { name: 'title', label: 'Slide Title', type: 'text', description: 'Overview title', required: true },\n { name: 'cards', label: 'Cards List', type: 'options', description: 'Array of 3 cards: {badge, title, desc}' },\n ],\n template: (slots, theme) => {\n const cards = Array.isArray(slots.cards) ? slots.cards : [];\n return `\n <div class=\"h-full w-full flex flex-col p-16 gap-6 justify-between\">\n <div>\n ${slots.tag ? `<span class=\"text-xs uppercase tracking-widest font-bold opacity-80\" style=\"color: var(--primary);\">${escapeHtml(slots.tag)}</span>` : ''}\n <h2 class=\"text-4xl font-bold mt-1.5\" style=\"font-family: var(--font-display); color: var(--text);\">${escapeHtml(slots.title || '')}</h2>\n </div>\n <div class=\"flex-1 grid grid-cols-3 gap-6 items-stretch min-h-0 my-auto\">\n ${cards.map((c: any, idx: number) => `\n <div class=\"p-8 rounded-xl border flex flex-col justify-between space-y-4 shadow-sm\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\">\n <div class=\"space-y-3\">\n <span class=\"text-xs font-mono font-bold uppercase tracking-wider px-3 py-1 rounded border inline-block\" style=\"border-color: var(--border); color: var(--primary);\">\n ${escapeHtml(c.badge || `0${idx + 1}`)}\n </span>\n <h3 class=\"text-2xl font-bold mt-2\" style=\"color: var(--text);\">${escapeHtml(c.title || '')}</h3>\n <p class=\"text-base leading-relaxed opacity-85\" style=\"color: var(--text-muted);\">${escapeHtml(c.desc || '')}</p>\n </div>\n ${c.footer ? `<div class=\"text-xs font-mono opacity-65 border-t pt-3\" style=\"border-color: var(--border); color: var(--text-muted);\">${escapeHtml(c.footer)}</div>` : ''}\n </div>\n `).join('')}\n </div>\n </div>\n `;\n },\n },\n\n 'timeline-steps': {\n id: 'timeline-steps',\n name: 'Timeline / Workflow Steps',\n category: 'timeline',\n description: 'Step-by-step workflow, pipeline stages, or algorithm execution order',\n slots: [\n { name: 'tag', label: 'Section Tag', type: 'badge', description: 'e.g. Execution Flow' },\n { name: 'title', label: 'Slide Title', type: 'text', description: 'Workflow title', required: true },\n { name: 'steps', label: 'Steps List', type: 'options', description: 'Array of {stepNum, title, desc}' },\n ],\n template: (slots, theme) => {\n const steps = Array.isArray(slots.steps) ? slots.steps : [];\n return `\n <div class=\"h-full w-full flex flex-col p-16 gap-6 justify-between\">\n <div>\n ${slots.tag ? `<span class=\"text-xs uppercase tracking-widest font-bold opacity-80\" style=\"color: var(--primary);\">${escapeHtml(slots.tag)}</span>` : ''}\n <h2 class=\"text-4xl font-bold mt-1.5\" style=\"font-family: var(--font-display); color: var(--text);\">${escapeHtml(slots.title || '')}</h2>\n </div>\n <div class=\"flex-1 flex flex-col justify-center my-auto\">\n <div class=\"grid grid-cols-${steps.length || 4} gap-5 relative\">\n ${steps.map((st: any, idx: number) => `\n <div class=\"flex flex-col space-y-3 p-6 rounded-xl border relative shadow-sm\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\">\n <div class=\"w-10 h-10 rounded-xl flex items-center justify-center font-bold text-lg shadow\" style=\"background-color: var(--primary); color: #FFFFFF;\">\n ${escapeHtml(st.stepNum || idx + 1)}\n </div>\n <h4 class=\"text-xl font-bold\" style=\"color: var(--text);\">${escapeHtml(st.title || '')}</h4>\n <p class=\"text-sm opacity-85 leading-relaxed\" style=\"color: var(--text-muted);\">${escapeHtml(st.desc || '')}</p>\n </div>\n `).join('')}\n </div>\n </div>\n </div>\n `;\n },\n },\n\n 'metric-callout': {\n id: 'metric-callout',\n name: 'Metric / Key Callout',\n category: 'callout',\n description: 'High-impact slide emphasizing a critical benchmark, theorem, or takeaway statistic',\n slots: [\n { name: 'tag', label: 'Category Tag', type: 'badge', description: 'e.g. Performance Benchmark' },\n { name: 'metric', label: 'Big Stat / Number', type: 'metric', description: 'e.g. 10x, 99.9%, O(1)' },\n { name: 'metricLabel', label: 'Metric Label', type: 'text', description: 'Short metric description' },\n { name: 'title', label: 'Callout Headline', type: 'text', description: 'Main point of this stat', required: true },\n { name: 'desc', label: 'Context Explanation', type: 'multiline', description: 'Deep context or impact' },\n ],\n template: (slots, theme) => `\n <div class=\"h-full w-full flex flex-col justify-center items-center p-16 text-center space-y-6 my-auto\">\n ${slots.tag ? `<div class=\"px-5 py-1.5 rounded-full text-xs font-bold uppercase tracking-widest border\" style=\"border-color: var(--border); color: var(--primary);\">${escapeHtml(slots.tag)}</div>` : ''}\n <div class=\"text-8xl font-black tracking-tighter\" style=\"font-family: var(--font-display); color: var(--primary);\">\n ${escapeHtml(slots.metric || '100%')}\n </div>\n ${slots.metricLabel ? `<div class=\"text-lg font-bold tracking-widest uppercase opacity-75\" style=\"color: var(--text-muted);\">${escapeHtml(slots.metricLabel)}</div>` : ''}\n <h2 class=\"text-4xl font-extrabold max-w-3xl leading-tight\" style=\"font-family: var(--font-display); color: var(--text);\">${escapeHtml(slots.title || '')}</h2>\n ${slots.desc ? `<p class=\"text-lg max-w-2xl opacity-85 leading-relaxed\" style=\"color: var(--text-muted);\">${escapeHtml(slots.desc)}</p>` : ''}\n </div>\n `,\n },\n\n 'checkpoint-quiz': {\n id: 'checkpoint-quiz',\n name: 'Interactive Checkpoint Quiz',\n category: 'assessment',\n description: 'Interactive mid-lesson comprehension check with option cards and revealable answer',\n slots: [\n { name: 'tag', label: 'Badge', type: 'badge', description: 'e.g. Knowledge Check' },\n { name: 'question', label: 'Question Text', type: 'multiline', description: 'The question to answer', required: true },\n { name: 'options', label: 'Options List', type: 'options', description: 'Array of {label, text, isCorrect}' },\n { name: 'explanation', label: 'Explanation', type: 'multiline', description: 'Explanation revealed on answer' },\n ],\n template: (slots, theme) => {\n const opts = Array.isArray(slots.options) ? slots.options : [];\n return `\n <div class=\"h-full w-full flex flex-col p-16 gap-6 justify-between\">\n <div>\n ${slots.tag ? `<span class=\"text-xs uppercase tracking-widest font-bold opacity-80\" style=\"color: var(--primary);\">${escapeHtml(slots.tag)}</span>` : ''}\n <h2 class=\"text-3xl font-extrabold mt-1.5 leading-snug max-w-4xl\" style=\"font-family: var(--font-display); color: var(--text);\">${escapeHtml(slots.question || 'Quick Check')}</h2>\n </div>\n <div class=\"w-full grid grid-cols-2 gap-5 items-stretch min-h-0 my-auto\">\n ${opts.map((opt: any, idx: number) => {\n const letter = opt.label || String.fromCharCode(65 + idx);\n return `\n <div class=\"quiz-option p-6 rounded-xl border flex items-center gap-4 shadow-sm\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\">\n <span class=\"opt-badge w-12 h-12 rounded-xl flex items-center justify-center font-bold text-lg border shrink-0\" style=\"border-color: var(--border); color: var(--primary);\">\n ${escapeHtml(letter)}\n </span>\n <span class=\"text-lg font-medium leading-relaxed\" style=\"color: var(--text);\">${escapeHtml(opt.text || '')}</span>\n </div>\n `;\n }).join('')}\n </div>\n ${slots.explanation ? `\n <div class=\"quiz-explanation p-4 rounded-xl border text-sm flex items-center gap-3 opacity-90 shadow-sm\" style=\"background-color: var(--bg-secondary); border-color: var(--border); color: var(--text);\">\n <span class=\"text-xl\">💡</span>\n <div><strong class=\"text-primary font-bold\">Hint / Explanation:</strong> ${escapeHtml(slots.explanation)}</div>\n </div>\n ` : ''}\n </div>\n `;\n },\n },\n\n 'summary-takeaways': {\n id: 'summary-takeaways',\n name: 'Summary & Takeaways',\n category: 'content',\n description: 'End-of-lesson wrap-up slide with bulleted takeaways and next lesson teaser',\n slots: [\n { name: 'tag', label: 'Tag', type: 'badge', description: 'e.g. Recap' },\n { name: 'title', label: 'Slide Title', type: 'text', description: 'Wrap up headline', required: true },\n { name: 'takeaways', label: 'Takeaways List', type: 'list', description: 'Key points students should remember' },\n { name: 'nextStep', label: 'Next Lesson / Challenge', type: 'text', description: 'Preview of what comes next' },\n ],\n template: (slots, theme) => {\n const takeaways = Array.isArray(slots.takeaways) ? slots.takeaways : [];\n return `\n <div class=\"h-full w-full flex flex-col p-16 gap-6 justify-between\">\n <div>\n ${slots.tag ? `<span class=\"text-xs uppercase tracking-widest font-bold opacity-80\" style=\"color: var(--primary);\">${escapeHtml(slots.tag)}</span>` : ''}\n <h2 class=\"text-4xl font-bold mt-1.5\" style=\"font-family: var(--font-display); color: var(--text);\">${escapeHtml(slots.title || 'Summary')}</h2>\n </div>\n <div class=\"flex-1 flex flex-col justify-center space-y-6 min-h-0 my-auto\">\n <div class=\"space-y-4\">\n ${takeaways.map((t: string) => `\n <div class=\"p-5 rounded-xl border flex items-start gap-4 shadow-sm\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\">\n <span class=\"text-xs font-bold w-7 h-7 rounded-full flex items-center justify-center flex-shrink-0 mt-0.5 shadow-sm\" style=\"background-color: var(--primary); color: #FFF;\">✓</span>\n <span class=\"text-lg font-medium leading-relaxed\" style=\"color: var(--text);\">${escapeHtml(t)}</span>\n </div>\n `).join('')}\n </div>\n ${slots.nextStep ? `\n <div class=\"p-6 rounded-xl border flex items-center justify-between shadow-sm\" style=\"border-color: var(--primary); background-color: var(--bg-secondary);\">\n <div>\n <div class=\"text-xs uppercase font-bold tracking-widest opacity-80\" style=\"color: var(--primary);\">Next Session</div>\n <div class=\"text-lg font-bold mt-1\" style=\"color: var(--text);\">${escapeHtml(slots.nextStep)}</div>\n </div>\n <span class=\"text-2xl\" style=\"color: var(--primary);\">➔</span>\n </div>\n ` : ''}\n </div>\n </div>\n `;\n },\n },\n};\n","// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit — Inlined Slide Layout Utilities\n//\n// WHY: the shell used to load `https://cdn.tailwindcss.com` at view time. Every\n// slot-layout slide is authored in Tailwind utility classes, so on a machine\n// with no network (or under a CSP that blocks the CDN) the entire deck rendered\n// UNSTYLED — one of the real causes behind \"slides look terrible\". A deck also\n// must not phone home: it is opened by learners, embedded in iframes, and\n// exported as an artifact.\n//\n// This is the CDN's job done deterministically: only the utilities the slide\n// presets actually use, with the SAME variable-driven scale semantics the old\n// tailwind.config declared (font sizes via --font-size-*, spacing via\n// --scale-spacing-eff, colors via the theme's CSS variables) so the in-deck\n// density/scale editor keeps working.\n//\n// `test/utilities-coverage.test.mjs` fails if a preset uses a class that is not\n// defined here — the guard against silently dropping a rule.\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/** font-size utility → CSS var + fallback (mirrors the old tailwind.config). */\nconst FONT_SIZES: Array<[string, string, string, string]> = [\n // class, var, fallback px, line-height\n ['xs', '--font-size-xs', '14px', '1.4'],\n ['sm', '--font-size-sm', '16px', '1.45'],\n ['base', '--font-size-base', '18px', '1.5'],\n ['lg', '--font-size-lg', '20px', '1.5'],\n ['xl', '--font-size-xl', '22px', '1.5'],\n ['2xl', '--font-size-2xl', '26px', '1.4'],\n ['3xl', '--font-size-3xl', '32px', '1.3'],\n ['4xl', '--font-size-4xl', '38px', '1.25'],\n ['6xl', '--font-size-6xl', '58px', '1.15'],\n ['8xl', '--font-size-8xl', '96px', '1.05'],\n];\n\n/** spacing utility → rem amount (scaled by --scale-spacing-eff, as before). */\nconst SPACING: Record<string, string> = {\n '0.5': '0.125rem',\n '1': '0.25rem',\n '1.5': '0.375rem',\n '2': '0.5rem',\n '2.5': '0.625rem',\n '3': '0.75rem',\n '4': '1rem',\n '5': '1.25rem',\n '6': '1.5rem',\n '7': '1.75rem',\n '8': '2rem',\n '10': '2.5rem',\n '12': '3rem',\n '16': '4rem',\n '20': '5rem',\n '24': '6rem',\n};\n\nfunction sp(value: string): string {\n return `calc(${SPACING[value]} * var(--scale-spacing-eff, 1))`;\n}\n\nfunction fontSizeRules(): string {\n return FONT_SIZES.map(\n ([cls, cssVar, fallback, lh]) =>\n `.text-${cls}{font-size:var(${cssVar}, ${fallback});line-height:${lh}}`\n ).join('\\n');\n}\n\nfunction spacingRules(): string {\n const out: string[] = [];\n for (const value of Object.keys(SPACING)) {\n out.push(`.p-${value}{padding:${sp(value)}}`);\n out.push(`.px-${value}{padding-left:${sp(value)};padding-right:${sp(value)}}`);\n out.push(`.py-${value}{padding-top:${sp(value)};padding-bottom:${sp(value)}}`);\n out.push(`.pt-${value}{padding-top:${sp(value)}}`);\n out.push(`.pb-${value}{padding-bottom:${sp(value)}}`);\n out.push(`.mt-${value}{margin-top:${sp(value)}}`);\n out.push(`.mb-${value}{margin-bottom:${sp(value)}}`);\n out.push(`.my-${value}{margin-top:${sp(value)};margin-bottom:${sp(value)}`);\n out.push(`.gap-${value}{gap:${sp(value)}}`);\n }\n return out.join('\\n');\n}\n\nfunction sizeRules(): string {\n const out: string[] = [];\n for (const value of Object.keys(SPACING)) {\n out.push(`.w-${value}{width:${sp(value)}}`);\n out.push(`.h-${value}{height:${sp(value)}}`);\n }\n return out.join('\\n');\n}\n\nexport const SLIDE_UTILITY_CSS = `\n/* preflight — the presets were authored against Tailwind's reset (no default\n margins, no list markers, border-box everywhere), so it has to come along. */\n.slide-page *,.slide-page *::before,.slide-page *::after{box-sizing:border-box;border-width:0;border-style:solid}\n.slide-page h1,.slide-page h2,.slide-page h3,.slide-page h4,.slide-page h5,.slide-page h6,\n.slide-page p,.slide-page ul,.slide-page ol,.slide-page li,.slide-page figure,.slide-page blockquote,\n.slide-page pre,.slide-page dl,.slide-page dd{margin:0;padding:0}\n.slide-page ul,.slide-page ol{list-style:none}\n.slide-page img,.slide-page svg,.slide-page video{display:block;max-width:100%}\n.slide-page button{font:inherit;background:none;border:0;cursor:pointer;color:inherit}\n.slide-page table{border-collapse:collapse}\n.slide-page a{color:inherit;text-decoration:inherit}\n/* layout */\n.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.inline-block{display:inline-block}\n.flex-col{flex-direction:column}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.shrink-0{flex-shrink:0}\n.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}\n.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}\n.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}\n.col-span-5{grid-column:span 5/span 5}.col-span-7{grid-column:span 7/span 7}\n.items-start{align-items:flex-start}.items-center{align-items:center}.items-stretch{align-items:stretch}\n.justify-center{justify-content:center}.justify-between{justify-content:space-between}\n.relative{position:relative}.overflow-hidden{overflow:hidden}.z-10{z-index:10}\n.h-full{height:100%}.w-full{width:100%}.min-h-0{min-height:0}.my-auto{margin-top:auto;margin-bottom:auto}\n.text-center{text-align:center}\n/* typography */\n.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}\n.font-extrabold{font-weight:800}.font-black{font-weight:900}.font-mono{font-family:var(--font-code)}\n.tracking-tighter{letter-spacing:-0.05em}.tracking-tight{letter-spacing:-0.025em}\n.tracking-wider{letter-spacing:0.05em}.tracking-widest{letter-spacing:0.1em}\n.uppercase{text-transform:uppercase}\n.leading-tight{line-height:1.25}.leading-snug{line-height:1.375}.leading-relaxed{line-height:1.625}\n.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}\n${fontSizeRules()}\n/* spacing (scaled by the density editor) */\n${spacingRules()}\n${sizeRules()}\n.space-y-3 > * + *{margin-top:${sp('3')}}\n.space-y-4 > * + *{margin-top:${sp('4')}}\n.space-y-6 > * + *{margin-top:${sp('6')}}\n/* surfaces & color (theme-variable driven) */\n.rounded{border-radius:0.25rem}.rounded-xl{border-radius:0.75rem}.rounded-full{border-radius:9999px}\n.border{border-width:1px;border-style:solid;border-color:var(--border)}\n.border-t{border-top-width:1px;border-top-style:solid;border-top-color:var(--border)}\n.border-b{border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--border)}\n.border-current{border-color:currentColor}\n.shadow{box-shadow:0 1px 3px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.08)}\n.shadow-sm{box-shadow:0 1px 2px rgba(0,0,0,.08)}\n.shadow-lg{box-shadow:0 10px 15px -3px rgba(0,0,0,.15),0 4px 6px -4px rgba(0,0,0,.1)}\n.opacity-60{opacity:.6}.opacity-65{opacity:.65}.opacity-75{opacity:.75}\n.opacity-80{opacity:.8}.opacity-85{opacity:.85}.opacity-90{opacity:.9}\n.bg-red-500\\\\/70{background-color:rgba(239,68,68,.7)}\n.bg-yellow-500\\\\/70{background-color:rgba(234,179,8,.7)}\n.bg-green-500\\\\/70{background-color:rgba(34,197,94,.7)}\n/* theme color utilities (previously provided by tailwind.config extend) */\n.text-primary{color:var(--primary)}.text-muted{color:var(--text-muted)}.text-themeText{color:var(--text)}\n.bg-themeBg{background-color:var(--bg)}.bg-themeBgSecondary{background-color:var(--bg-secondary)}\n.bg-themePrimary{background-color:var(--primary)}.border-themeBorder{border-color:var(--border)}\n`.trim();\n\n/** Every class defined by {@link SLIDE_UTILITY_CSS} — used by the coverage guard. */\nexport function listUtilityClasses(): string[] {\n return Array.from(SLIDE_UTILITY_CSS.matchAll(/\\.([a-zA-Z0-9\\\\/_.-]+)(?=[{,: >])/g)).map((m) =>\n m[1].replace(/\\\\\\//g, '/')\n );\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit — Self-Contained HTML Slide Shell Generator\n// 1920x1080 Fixed Canvas, Auto-Scaling, Keyboard Navigation, postMessage Bridge\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { HtmlDeckTheme } from './types.js';\nimport { SLIDE_UTILITY_CSS } from './utilities.js';\n\nexport interface GenerateHtmlShellOptions {\n title: string;\n theme: HtmlDeckTheme;\n slidesHtml: string;\n slideCount: number;\n customHeadTags?: string;\n deckId?: string;\n /**\n * Self-contained @font-face CSS (base64 woff2). When provided the shell emits\n * NO external font links, so the deck renders with its typography on a machine\n * with no network. Built by the Node-only `presentation-kit/node` entry\n * (`loadEmbeddedFontFaceCss`) — the main entry stays fs-free for browsers.\n */\n fontFaceCss?: string;\n}\n\nexport function generateHtmlShell(options: GenerateHtmlShellOptions): string {\n const { title, theme, slidesHtml, slideCount, customHeadTags = '', deckId = '' } = options;\n\n return `<!DOCTYPE html>\n<html lang=\"en\" class=\"${theme.scheme}\"${deckId ? ` data-deck-id=\"${deckId}\"` : ''}>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n <title>${title}</title>\n \n <!-- Fonts -->\n <!-- DECK_FONT_BLOCK_START -->${options?.fontFaceCss\n ? `\\n <style>\\n${options.fontFaceCss}\\n </style>`\n : `\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&family=Plus+Jakarta+Sans:wght@500;700;800&family=Space+Grotesk:wght@500;700&display=swap\" rel=\"stylesheet\">`}<!-- DECK_FONT_BLOCK_END -->\n\n <!-- Core Presentation Styling & 1920x1080 Scaled Layout Utilities (inlined) -->\n <style>${SLIDE_UTILITY_CSS}</style>\n <script>\n window.tailwind = window.tailwind || {};\n tailwind.config = {\n darkMode: 'class',\n theme: {\n extend: {\n fontSize: {\n 'xs': ['var(--font-size-xs, 14px)', { lineHeight: '1.4' }],\n 'sm': ['var(--font-size-sm, 16px)', { lineHeight: '1.45' }],\n 'base': ['var(--font-size-base, 18px)', { lineHeight: '1.5' }],\n 'lg': ['var(--font-size-lg, 20px)', { lineHeight: '1.5' }],\n 'xl': ['var(--font-size-xl, 22px)', { lineHeight: '1.5' }],\n '2xl': ['var(--font-size-2xl, 26px)', { lineHeight: '1.4' }],\n '3xl': ['var(--font-size-3xl, 32px)', { lineHeight: '1.3' }],\n '4xl': ['var(--font-size-4xl, 38px)', { lineHeight: '1.25' }],\n '5xl': ['var(--font-size-5xl, 46px)', { lineHeight: '1.2' }],\n '6xl': ['var(--font-size-6xl, 58px)', { lineHeight: '1.15' }],\n '7xl': ['var(--font-size-7xl, 72px)', { lineHeight: '1.1' }],\n '8xl': ['var(--font-size-8xl, 96px)', { lineHeight: '1.05' }],\n },\n spacing: {\n '2': 'calc(0.5rem * var(--scale-spacing-eff, 1))',\n '3': 'calc(0.75rem * var(--scale-spacing-eff, 1))',\n '4': 'calc(1rem * var(--scale-spacing-eff, 1))',\n '5': 'calc(1.25rem * var(--scale-spacing-eff, 1))',\n '6': 'calc(1.5rem * var(--scale-spacing-eff, 1))',\n '8': 'calc(2rem * var(--scale-spacing-eff, 1))',\n '10': 'calc(2.5rem * var(--scale-spacing-eff, 1))',\n '12': 'calc(3rem * var(--scale-spacing-eff, 1))',\n '16': 'calc(4rem * var(--scale-spacing-eff, 1))',\n '20': 'calc(5rem * var(--scale-spacing-eff, 1))',\n '24': 'calc(6rem * var(--scale-spacing-eff, 1))',\n },\n colors: {\n themeBg: 'var(--bg)',\n themeBgSecondary: 'var(--bg-secondary)',\n themePrimary: 'var(--primary)',\n themeText: 'var(--text)',\n themeMuted: 'var(--text-muted)',\n themeBorder: 'var(--border)',\n }\n }\n }\n }\n </script>\n\n <style>\n :root {\n /* Scaling Multiplier Architecture: Unified vs Independent */\n --scale-mode: unified;\n --scale-factor: 1.0;\n --scale-headings: 1.0;\n --scale-body: 1.0;\n --scale-code: 1.0;\n --scale-spacing: 1.0;\n\n /* Effective Multipliers */\n --scale-headings-eff: 1.0;\n --scale-body-eff: 1.0;\n --scale-code-eff: 1.0;\n --scale-spacing-eff: 1.0;\n\n --bg: ${theme.palette.bg};\n --bg-secondary: ${theme.palette.bgSecondary || theme.palette.bg};\n --primary: ${theme.palette.primary};\n --accent: ${theme.palette.accent || theme.palette.primary};\n --text: ${theme.palette.text};\n --text-muted: ${theme.palette.textMuted};\n --border: ${theme.palette.border || 'rgba(0, 0, 0, 0.12)'};\n --font-display: ${theme.typography.display};\n --font-body: ${theme.typography.body};\n --font-code: ${theme.typography.code || 'monospace'};\n\n /* Dynamic Typography calculated from effective multipliers */\n --font-size-xs: calc(14px * var(--scale-code-eff));\n --font-size-sm: calc(16px * var(--scale-code-eff));\n --font-size-base: calc(18px * var(--scale-body-eff));\n --font-size-lg: calc(20px * var(--scale-body-eff));\n --font-size-xl: calc(22px * var(--scale-body-eff));\n --font-size-2xl: calc(26px * var(--scale-body-eff));\n --font-size-3xl: calc(32px * var(--scale-headings-eff));\n --font-size-4xl: calc(38px * var(--scale-headings-eff));\n --font-size-5xl: calc(46px * var(--scale-headings-eff));\n --font-size-6xl: calc(58px * var(--scale-headings-eff));\n --font-size-7xl: calc(72px * var(--scale-headings-eff));\n --font-size-8xl: calc(96px * var(--scale-headings-eff));\n }\n\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html, body {\n width: 100%;\n height: 100%;\n overflow: hidden;\n background-color: #050507;\n font-family: var(--font-body);\n color: var(--text);\n user-select: none;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n\n #viewport {\n position: absolute;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: #050507;\n overflow: hidden;\n }\n\n .slide-canvas {\n position: absolute;\n top: 50%;\n left: 50%;\n width: 1920px;\n height: 1080px;\n transform: translate(-50%, -50%);\n transform-origin: center center;\n background-color: var(--bg);\n overflow: hidden;\n box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);\n border-radius: 4px;\n }\n\n .slide-page {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: none;\n opacity: 0;\n transition: opacity 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n .slide-page.active {\n display: block;\n opacity: 1;\n }\n\n /* ── Bespoke (authored) slides ────────────────────────────────────────────\n The frontend-slides mode hands the model the whole slide surface, so the\n shell owns only what must stay invariant: full-stage sizing, border-box\n maths, theme type defaults, and one orchestrated entrance. */\n .bespoke-slide {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n .bespoke-slide,\n .bespoke-slide *,\n .bespoke-slide *::before,\n .bespoke-slide *::after {\n box-sizing: border-box;\n }\n\n .bespoke-slide h1,\n .bespoke-slide h2 {\n font-family: var(--font-display);\n letter-spacing: -0.02em;\n }\n\n .bespoke-slide img,\n .bespoke-slide svg {\n max-width: 100%;\n height: auto;\n }\n\n .slide-page.active .bespoke-slide > * {\n animation: bespokeReveal 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;\n }\n\n .slide-page.active .bespoke-slide > *:nth-child(2) { animation-delay: 0.08s; }\n .slide-page.active .bespoke-slide > *:nth-child(3) { animation-delay: 0.16s; }\n .slide-page.active .bespoke-slide > *:nth-child(4) { animation-delay: 0.24s; }\n .slide-page.active .bespoke-slide > *:nth-child(5) { animation-delay: 0.32s; }\n .slide-page.active .bespoke-slide > *:nth-child(n + 6) { animation-delay: 0.4s; }\n\n @keyframes bespokeReveal {\n from { opacity: 0; transform: translateY(14px); }\n to { opacity: 1; transform: none; }\n }\n\n @media (prefers-reduced-motion: reduce) {\n .slide-page.active .bespoke-slide > * {\n animation: none;\n opacity: 1;\n transform: none;\n }\n }\n\n /* Top Progress Bar */\n #progress-bar {\n position: absolute;\n top: 0;\n left: 0;\n height: 5px;\n background-color: var(--primary);\n transition: width 0.25s cubic-bezier(0.16, 1, 0.3, 1);\n z-index: 50;\n }\n\n /* Floating Navigation Controls */\n #controls {\n position: absolute;\n bottom: 28px;\n right: 28px;\n display: flex;\n align-items: center;\n gap: 12px;\n background: rgba(15, 15, 20, 0.75);\n backdrop-filter: blur(16px);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: 9999px;\n padding: 8px 20px;\n z-index: 100;\n opacity: 0;\n transition: opacity 0.25s ease;\n }\n\n body:hover #controls {\n opacity: 1;\n }\n\n .control-btn {\n background: transparent;\n border: none;\n color: #ffffff;\n cursor: pointer;\n font-size: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 6px;\n border-radius: 8px;\n transition: background-color 0.15s, transform 0.1s;\n }\n\n .control-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(1.08);\n }\n\n #slide-counter {\n color: #ffffff;\n font-size: 15px;\n font-family: var(--font-code);\n font-weight: 600;\n letter-spacing: 0.08em;\n padding: 0 4px;\n }\n\n /* Interactive Quiz Styling */\n .quiz-option {\n cursor: pointer;\n user-select: none;\n transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n .quiz-option:hover {\n transform: translateY(-2px);\n border-color: var(--primary) !important;\n box-shadow: 0 12px 30px -8px rgba(0, 0, 0, 0.15);\n }\n\n .quiz-option.selected {\n border-color: var(--primary) !important;\n box-shadow: 0 0 0 3px var(--primary);\n }\n\n .quiz-option.selected .opt-badge {\n background-color: var(--primary) !important;\n color: #ffffff !important;\n border-color: var(--primary) !important;\n }\n\n .quiz-explanation {\n transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n /* ── WYSIWYG Inline Editing (Level 1) Styles ── */\n .edit-hotzone {\n position: fixed;\n top: 0;\n left: 0;\n width: 90px;\n height: 90px;\n z-index: 9999;\n cursor: pointer;\n }\n\n .edit-toggle {\n position: fixed;\n top: 24px;\n left: 24px;\n z-index: 10000;\n background: rgba(15, 15, 20, 0.85);\n backdrop-filter: blur(14px);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 9999px;\n color: #ffffff;\n padding: 7px 16px;\n font-size: 14px;\n font-family: var(--font-body);\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n opacity: 0;\n pointer-events: none;\n transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1), transform 0.15s;\n }\n\n .edit-toggle:hover {\n background: rgba(255, 255, 255, 0.2);\n transform: scale(1.04);\n }\n\n .edit-toggle.show,\n .edit-toggle.active {\n opacity: 1;\n pointer-events: auto;\n }\n\n .edit-toggle.active {\n background: var(--primary);\n border-color: var(--primary);\n color: #ffffff;\n box-shadow: 0 0 20px rgba(0, 0, 0, 0.4);\n }\n\n #edit-bar {\n position: fixed;\n bottom: 28px;\n left: 50%;\n transform: translateX(-50%);\n background: rgba(15, 15, 20, 0.92);\n backdrop-filter: blur(16px);\n border: 1px solid rgba(255, 255, 255, 0.22);\n border-radius: 9999px;\n padding: 6px 18px;\n display: flex;\n align-items: center;\n gap: 10px;\n z-index: 10000;\n box-shadow: 0 20px 45px rgba(0, 0, 0, 0.5);\n animation: editBarSlideUp 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes editBarSlideUp {\n from { opacity: 0; transform: translate(-50%, 15px); }\n to { opacity: 1; transform: translate(-50%, 0); }\n }\n\n .edit-bar-btn {\n background: transparent;\n border: none;\n color: #ffffff;\n font-size: 14px;\n font-weight: 600;\n padding: 6px 12px;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: background 0.15s, transform 0.1s;\n }\n\n .edit-bar-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n transform: translateY(-1px);\n }\n\n .edit-bar-btn.save-btn {\n background: var(--primary);\n color: #ffffff;\n }\n .edit-bar-btn.save-btn:hover {\n filter: brightness(1.12);\n }\n\n .edit-bar-divider {\n width: 1px;\n height: 20px;\n background: rgba(255, 255, 255, 0.2);\n }\n\n /* Editable active styles */\n body.edit-mode-active [contenteditable=\"true\"] {\n outline: 1px dashed rgba(100, 149, 237, 0.35);\n outline-offset: 3px;\n cursor: text;\n border-radius: 4px;\n }\n\n body.edit-mode-active [contenteditable=\"true\"]:hover {\n outline: 2px dashed var(--primary);\n }\n\n body.edit-mode-active [contenteditable=\"true\"]:focus {\n outline: 2px solid var(--primary);\n background: rgba(100, 149, 237, 0.08);\n }\n\n /* ─── Floating Scale Tuning Panel (Unified vs Independent) ─── */\n #scale-panel {\n position: fixed;\n bottom: 82px;\n left: 50%;\n transform: translateX(-50%);\n width: 390px;\n background: rgba(18, 18, 26, 0.94);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 16px;\n padding: 16px 20px;\n z-index: 10002;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.65), 0 0 0 1px rgba(255, 255, 255, 0.08);\n color: #f4f4f5;\n font-family: var(--font-body);\n animation: scalePanelPop 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes scalePanelPop {\n from { opacity: 0; transform: translate(-50%, 10px) scale(0.97); }\n to { opacity: 1; transform: translate(-50%, 0) scale(1); }\n }\n\n .scale-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 14px;\n padding-bottom: 10px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.12);\n }\n\n .scale-panel-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n font-weight: 700;\n letter-spacing: -0.01em;\n color: #ffffff;\n text-transform: uppercase;\n }\n\n .scale-mode-switch {\n display: flex;\n background: rgba(0, 0, 0, 0.35);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 9999px;\n padding: 2px;\n gap: 2px;\n }\n\n .mode-tab {\n background: transparent;\n border: none;\n color: #a1a1aa;\n font-size: 11px;\n font-weight: 600;\n padding: 3px 10px;\n border-radius: 9999px;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .mode-tab.active {\n background: var(--primary);\n color: #ffffff;\n box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);\n }\n\n .scale-panel-close {\n background: transparent;\n border: none;\n color: #71717a;\n cursor: pointer;\n font-size: 14px;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n transition: color 0.15s, background 0.15s;\n }\n\n .scale-panel-close:hover {\n color: #ffffff;\n background: rgba(255, 255, 255, 0.1);\n }\n\n .scale-slider-row {\n margin-bottom: 12px;\n }\n\n .scale-slider-row:last-child {\n margin-bottom: 0;\n }\n\n .scale-slider-label-wrap {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 12px;\n font-weight: 500;\n color: #d4d4d8;\n margin-bottom: 5px;\n }\n\n .scale-badge {\n font-family: var(--font-code);\n font-size: 11px;\n font-weight: 600;\n background: rgba(255, 255, 255, 0.1);\n padding: 1px 6px;\n border-radius: 4px;\n color: #38bdf8;\n }\n\n .scale-range-slider {\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n height: 6px;\n border-radius: 3px;\n background: rgba(255, 255, 255, 0.18);\n outline: none;\n cursor: pointer;\n transition: background 0.15s;\n }\n\n .scale-range-slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--primary);\n cursor: pointer;\n box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);\n transition: transform 0.1s;\n }\n\n .scale-range-slider::-webkit-slider-thumb:hover {\n transform: scale(1.2);\n }\n\n .density-presets {\n display: flex;\n gap: 8px;\n margin-top: 14px;\n }\n\n .density-chip {\n flex: 1;\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-radius: 8px;\n color: #d4d4d8;\n font-size: 11px;\n font-weight: 600;\n padding: 6px 0;\n cursor: pointer;\n text-align: center;\n transition: all 0.15s ease;\n }\n\n .density-chip:hover {\n background: rgba(255, 255, 255, 0.12);\n color: #ffffff;\n }\n\n .density-chip.active {\n background: rgba(56, 189, 248, 0.2);\n border-color: #38bdf8;\n color: #38bdf8;\n }\n\n .scale-independent-footer {\n display: flex;\n justify-content: flex-end;\n margin-top: 12px;\n padding-top: 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n }\n\n .btn-reset-scales {\n background: transparent;\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: 6px;\n color: #a1a1aa;\n font-size: 11px;\n font-weight: 600;\n padding: 4px 10px;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .btn-reset-scales:hover {\n background: rgba(255, 255, 255, 0.1);\n color: #ffffff;\n border-color: rgba(255, 255, 255, 0.3);\n }\n\n /* ─── Step-by-Step Reveal (Animation Steps / Build-In) ─── */\n .step-reveal,\n [data-step],\n [data-reveal],\n [data-click],\n .v-click {\n opacity: 0;\n transform: translateY(10px);\n transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n pointer-events: none;\n }\n\n .step-reveal.revealed,\n [data-step].revealed,\n [data-reveal].revealed,\n [data-click].revealed,\n .v-click.visible,\n body.is-editing .step-reveal,\n body.is-editing [data-step],\n body.is-editing [data-reveal],\n body.is-editing [data-click],\n body.is-editing .v-click,\n body.mode-deck .step-reveal,\n body.mode-deck [data-step],\n body.mode-deck [data-reveal],\n body.mode-deck [data-click],\n body.mode-deck .v-click {\n opacity: 1 !important;\n transform: translateY(0) !important;\n pointer-events: auto !important;\n }\n\n /* ─── Deck Mode (Continuous Vertical Scroll) ─── */\n body.mode-deck {\n overflow-y: auto !important;\n height: auto !important;\n min-height: 100vh;\n background-color: #08080c !important;\n user-select: auto !important;\n }\n\n body.mode-deck #viewport {\n position: relative !important;\n width: 100% !important;\n height: auto !important;\n min-height: 100vh;\n overflow-y: visible !important;\n display: flex !important;\n flex-direction: column !important;\n align-items: center !important;\n padding: 60px 24px 140px 24px !important;\n gap: 36px !important;\n background-color: transparent !important;\n }\n\n body.mode-deck .slide-canvas {\n position: relative !important;\n top: auto !important;\n left: auto !important;\n transform: none !important;\n width: 100% !important;\n max-width: 1200px !important;\n height: auto !important;\n aspect-ratio: 16 / 9 !important;\n box-shadow: 0 16px 48px rgba(0, 0, 0, 0.6) !important;\n border: 1px solid rgba(255, 255, 255, 0.1) !important;\n border-radius: 8px !important;\n }\n\n body.mode-deck .slide-page {\n display: block !important;\n opacity: 1 !important;\n position: relative !important;\n width: 100% !important;\n height: 100% !important;\n }\n\n /* Deck Notes Card under each slide */\n .deck-notes-card {\n display: none;\n width: 100%;\n max-width: 1200px;\n margin-top: -16px;\n margin-bottom: 24px;\n background: rgba(20, 20, 28, 0.95);\n border: 1px solid rgba(255, 255, 255, 0.12);\n border-left: 4px solid var(--primary);\n border-radius: 8px;\n padding: 16px 22px;\n color: #e4e4e7;\n font-size: 14px;\n line-height: 1.65;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);\n }\n\n .deck-notes-card-header {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--primary);\n margin-bottom: 6px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n body.mode-deck .deck-notes-card,\n body.show-deck-notes .deck-notes-card {\n display: block;\n }\n\n /* ─── Presenter Drawer (In-Page) ─── */\n #presenter-drawer {\n position: fixed;\n bottom: 0;\n left: 0;\n width: 100%;\n max-height: 380px;\n background: rgba(14, 14, 20, 0.96);\n backdrop-filter: blur(24px);\n -webkit-backdrop-filter: blur(24px);\n border-top: 1px solid rgba(255, 255, 255, 0.18);\n border-top-left-radius: 18px;\n border-top-right-radius: 18px;\n padding: 18px 28px 24px 28px;\n z-index: 10005;\n box-shadow: 0 -15px 40px rgba(0, 0, 0, 0.7);\n color: #f4f4f5;\n display: flex;\n flex-direction: column;\n gap: 12px;\n transform: translateY(100%);\n transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n #presenter-drawer.open {\n transform: translateY(0);\n }\n\n .presenter-drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n padding-bottom: 12px;\n }\n\n .presenter-meta-group {\n display: flex;\n align-items: center;\n gap: 18px;\n }\n\n .presenter-timer-badge {\n font-family: var(--font-code);\n font-size: 15px;\n font-weight: 700;\n color: #38bdf8;\n background: rgba(56, 189, 248, 0.12);\n border: 1px solid rgba(56, 189, 248, 0.3);\n padding: 3px 10px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .presenter-notes-box {\n flex: 1;\n overflow-y: auto;\n font-size: 16px;\n line-height: 1.7;\n color: #f1f5f9;\n background: rgba(0, 0, 0, 0.25);\n border: 1px solid rgba(255, 255, 255, 0.08);\n border-radius: 8px;\n padding: 14px 18px;\n max-height: 220px;\n user-select: text;\n }\n\n /* ─── Print Modal / Dialog ─── */\n #print-modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n background: rgba(0, 0, 0, 0.65);\n backdrop-filter: blur(8px);\n z-index: 10010;\n display: none;\n align-items: center;\n justify-content: center;\n }\n\n #print-modal.open {\n display: flex;\n }\n\n .print-dialog-card {\n width: 440px;\n background: #181822;\n border: 1px solid rgba(255, 255, 255, 0.2);\n border-radius: 16px;\n padding: 24px;\n box-shadow: 0 25px 60px rgba(0, 0, 0, 0.7);\n color: #ffffff;\n display: flex;\n flex-direction: column;\n gap: 18px;\n }\n\n .print-dialog-title {\n font-size: 17px;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .print-option-row {\n display: flex;\n align-items: center;\n gap: 10px;\n background: rgba(255, 255, 255, 0.05);\n padding: 12px 14px;\n border-radius: 8px;\n cursor: pointer;\n font-size: 14px;\n }\n\n .print-dialog-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 10px;\n }\n\n /* ─── Controls Extra Buttons & Badges ─── */\n .step-badge {\n font-family: var(--font-code);\n font-size: 11px;\n font-weight: 600;\n background: rgba(255, 255, 255, 0.12);\n color: #a5f3fc;\n padding: 2px 7px;\n border-radius: 4px;\n display: none;\n }\n\n .control-divider {\n width: 1px;\n height: 16px;\n background: rgba(255, 255, 255, 0.18);\n margin: 0 2px;\n }\n\n /* ─── Print Engine (@media print) ─── */\n @media print {\n @page {\n size: landscape;\n margin: 0;\n }\n\n html, body {\n width: 100% !important;\n height: auto !important;\n background: #ffffff !important;\n color: #000000 !important;\n overflow: visible !important;\n user-select: text !important;\n }\n\n #controls,\n #edit-bar,\n #scale-panel,\n #progress-bar,\n .edit-hotzone,\n .edit-toggle,\n #presenter-drawer,\n #print-modal {\n display: none !important;\n }\n\n #viewport {\n position: static !important;\n width: 100% !important;\n height: auto !important;\n display: block !important;\n padding: 0 !important;\n background: transparent !important;\n overflow: visible !important;\n }\n\n .slide-canvas {\n position: relative !important;\n top: auto !important;\n left: auto !important;\n transform: none !important;\n width: 100vw !important;\n height: 56.25vw !important; /* 16:9 ratio */\n max-height: 100vh !important;\n margin: 0 auto !important;\n page-break-inside: avoid !important;\n break-inside: avoid !important;\n page-break-after: always !important;\n break-after: page !important;\n box-shadow: none !important;\n border: none !important;\n border-radius: 0 !important;\n background-color: var(--bg) !important;\n -webkit-print-color-adjust: exact !important;\n print-color-adjust: exact !important;\n }\n\n .slide-page {\n display: block !important;\n opacity: 1 !important;\n position: relative !important;\n width: 100% !important;\n height: 100% !important;\n }\n\n /* Step elements always visible when printing */\n .step-reveal, [data-step], [data-reveal], [data-click], .v-click {\n opacity: 1 !important;\n transform: none !important;\n }\n\n /* When printing with notes */\n body.print-with-notes .slide-canvas {\n height: 48vw !important;\n page-break-after: avoid !important;\n break-after: avoid !important;\n }\n\n body.print-with-notes .deck-notes-card {\n display: block !important;\n page-break-after: always !important;\n break-after: page !important;\n background: #f8fafc !important;\n border: 1px solid #cbd5e1 !important;\n border-left: 5px solid #2563eb !important;\n color: #1e293b !important;\n margin: 12px auto 32px auto !important;\n width: 95vw !important;\n box-shadow: none !important;\n font-size: 12pt !important;\n line-height: 1.5 !important;\n padding: 12px 18px !important;\n }\n\n body.print-with-notes .deck-notes-card-header {\n color: #1e40af !important;\n font-size: 10pt !important;\n }\n }\n\n ${theme.customCss || ''}\n </style>\n ${customHeadTags}\n</head>\n<body>\n <div id=\"viewport\">\n <div class=\"slide-canvas\" id=\"canvas\">\n <div id=\"progress-bar\"></div>\n ${slidesHtml}\n </div>\n </div>\n\n <!-- WYSIWYG Inline Edit UI (Level 1) -->\n <div class=\"edit-hotzone\" id=\"edit-hotzone\"></div>\n <button class=\"edit-toggle\" id=\"btn-edit-toggle\" title=\"Toggle Inline Edit Mode (E)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 20h9\"/><path d=\"M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z\"/></svg>\n <span id=\"edit-toggle-label\">Edit Mode (E)</span>\n </button>\n\n <!-- Floating Scale Tuning Panel -->\n <div id=\"scale-panel\" style=\"display: none;\">\n <div class=\"scale-panel-header\">\n <div class=\"scale-panel-title\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\"><path d=\"M21 3 3 21\"/><path d=\"m14 4 6 6\"/><path d=\"m4 14 6 6\"/></svg>\n <span>Scale Tuning</span>\n </div>\n <div class=\"scale-mode-switch\">\n <button type=\"button\" class=\"mode-tab active\" id=\"tab-mode-unified\">Unified</button>\n <button type=\"button\" class=\"mode-tab\" id=\"tab-mode-independent\">Independent</button>\n </div>\n <button type=\"button\" class=\"scale-panel-close\" id=\"btn-scale-panel-close\" title=\"Close Panel\" style=\"display: flex; align-items: center; justify-content: center;\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>\n </button>\n </div>\n\n <!-- Unified Mode Content -->\n <div id=\"scale-unified-content\" class=\"scale-content-block\">\n <div class=\"scale-slider-row\">\n <div class=\"scale-slider-label-wrap\">\n <span class=\"scale-slider-label\">Scale All</span>\n <span class=\"scale-badge\" id=\"badge-scale-factor\">100%</span>\n </div>\n <input type=\"range\" id=\"input-scale-factor\" min=\"70\" max=\"150\" value=\"100\" class=\"scale-range-slider\" />\n </div>\n <div class=\"density-presets\">\n <button type=\"button\" class=\"density-chip\" data-scale=\"85\">Compact (85%)</button>\n <button type=\"button\" class=\"density-chip active\" data-scale=\"100\">Default (100%)</button>\n <button type=\"button\" class=\"density-chip\" data-scale=\"120\">Spacious (120%)</button>\n </div>\n </div>\n\n <!-- Independent Mode Content -->\n <div id=\"scale-independent-content\" class=\"scale-content-block\" style=\"display: none;\">\n <div class=\"scale-slider-row\">\n <div class=\"scale-slider-label-wrap\">\n <span class=\"scale-slider-label\">Headings (H1–H4)</span>\n <span class=\"scale-badge\" id=\"badge-scale-headings\">100%</span>\n </div>\n <input type=\"range\" id=\"input-scale-headings\" min=\"70\" max=\"150\" value=\"100\" class=\"scale-range-slider\" />\n </div>\n <div class=\"scale-slider-row\">\n <div class=\"scale-slider-label-wrap\">\n <span class=\"scale-slider-label\">Body &amp; Lists</span>\n <span class=\"scale-badge\" id=\"badge-scale-body\">100%</span>\n </div>\n <input type=\"range\" id=\"input-scale-body\" min=\"70\" max=\"150\" value=\"100\" class=\"scale-range-slider\" />\n </div>\n <div class=\"scale-slider-row\">\n <div class=\"scale-slider-label-wrap\">\n <span class=\"scale-slider-label\">Code &amp; Notes</span>\n <span class=\"scale-badge\" id=\"badge-scale-code\">100%</span>\n </div>\n <input type=\"range\" id=\"input-scale-code\" min=\"70\" max=\"150\" value=\"100\" class=\"scale-range-slider\" />\n </div>\n <div class=\"scale-slider-row\">\n <div class=\"scale-slider-label-wrap\">\n <span class=\"scale-slider-label\">Padding &amp; Gaps</span>\n <span class=\"scale-badge\" id=\"badge-scale-spacing\">100%</span>\n </div>\n <input type=\"range\" id=\"input-scale-spacing\" min=\"70\" max=\"150\" value=\"100\" class=\"scale-range-slider\" />\n </div>\n <div class=\"scale-independent-footer\">\n <button type=\"button\" class=\"btn-reset-scales\" id=\"btn-reset-scales\" style=\"display: flex; align-items: center; gap: 4px;\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/><path d=\"M3 3v5h5\"/></svg>\n <span>Reset All</span>\n </button>\n </div>\n </div>\n </div>\n\n <div id=\"edit-bar\" style=\"display: none;\">\n <span style=\"font-size: 13px; font-weight: 700; color: #a1a1aa; text-transform: uppercase; letter-spacing: 0.05em; padding-left: 4px;\">Editing</span>\n <div class=\"edit-bar-divider\"></div>\n <button class=\"edit-bar-btn\" id=\"btn-format-bold\" title=\"Bold (Cmd+B)\"><strong>B</strong></button>\n <button class=\"edit-bar-btn\" id=\"btn-format-italic\" title=\"Italic (Cmd+I)\"><em>I</em></button>\n <button class=\"edit-bar-btn\" id=\"btn-format-underline\" title=\"Underline (Cmd+U)\"><u>U</u></button>\n <div class=\"edit-bar-divider\"></div>\n <button class=\"edit-bar-btn\" id=\"btn-editbar-scale\" title=\"Tune Element Scales (Unified / Independent)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 3 3 21\"/><path d=\"m14 4 6 6\"/><path d=\"m4 14 6 6\"/></svg>\n <span>Scale</span>\n </button>\n <div class=\"edit-bar-divider\"></div>\n <button class=\"edit-bar-btn\" id=\"btn-reset-edits\" title=\"Restore original content\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/><path d=\"M3 3v5h5\"/></svg>\n <span>Reset</span>\n </button>\n <button class=\"edit-bar-btn save-btn\" id=\"btn-save-html\" title=\"Save and download clean HTML file\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\"/><polyline points=\"17 21 17 13 7 13 7 21\"/><polyline points=\"7 3 7 8 15 8\"/></svg>\n <span>Save HTML</span>\n </button>\n <button class=\"edit-bar-btn\" id=\"btn-done-edit\" title=\"Done editing (E)\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M20 6 9 17l-5-5\"/></svg>\n <span>Done</span>\n </button>\n </div>\n\n <!-- Presenter Drawer (Quick In-Page Notes & Timer) -->\n <div id=\"presenter-drawer\">\n <div class=\"presenter-drawer-header\">\n <div class=\"presenter-meta-group\">\n <span style=\"font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #a1a1aa;\">Presenter Console</span>\n <div class=\"presenter-timer-badge\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/></svg>\n <span id=\"presenter-timer-text\">00:00</span>\n </div>\n </div>\n <div style=\"display: flex; align-items: center; gap: 8px;\">\n <button type=\"button\" class=\"btn-reset-scales\" id=\"btn-open-dual-presenter\" title=\"Open separate synced presenter console window for multi-monitor display\" style=\"display: flex; align-items: center; gap: 4px;\">\n <span>Launch 2nd Window</span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 17 17 7\"/><path d=\"M7 7h10v10\"/></svg>\n </button>\n <button type=\"button\" class=\"scale-panel-close\" id=\"btn-close-presenter-drawer\" title=\"Close Drawer (P)\" style=\"display: flex; align-items: center; justify-content: center;\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>\n </button>\n </div>\n </div>\n <div class=\"presenter-notes-box\" id=\"presenter-notes-text\">\n No notes for this slide.\n </div>\n </div>\n\n <!-- Print Configuration Modal -->\n <div id=\"print-modal\">\n <div class=\"print-dialog-card\">\n <div class=\"print-dialog-title\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"6 9 6 2 18 2 18 9\"></polyline><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\"></path><rect x=\"6\" y=\"14\" width=\"12\" height=\"8\"></rect></svg>\n <span>Print &amp; Export PDF</span>\n </div>\n <label class=\"print-option-row\">\n <input type=\"checkbox\" id=\"chk-print-notes\" style=\"width: 18px; height: 18px; accent-color: var(--primary); cursor: pointer;\" />\n <span style=\"user-select: none;\">In kèm ghi chú giảng viên (Handout / Teacher Notes)</span>\n </label>\n <p style=\"font-size: 12px; color: #a1a1aa; line-height: 1.5; margin: 0;\">\n Lưu ý: Chọn trang in Khổ ngang (Landscape), Bật &quot;Background graphics&quot; trong hộp thoại in của trình duyệt để màu sắc hiển thị đúng 100%.\n </p>\n <div class=\"print-dialog-actions\">\n <button type=\"button\" class=\"btn-reset-scales\" id=\"btn-cancel-print\">Hủy</button>\n <button type=\"button\" class=\"edit-bar-btn save-btn\" id=\"btn-start-print\" style=\"padding: 6px 16px; display: flex; align-items: center; gap: 6px;\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"6 9 6 2 18 2 18 9\"></polyline><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\"></path><rect x=\"6\" y=\"14\" width=\"12\" height=\"8\"></rect></svg>\n <span>In ngay (Print)</span>\n </button>\n </div>\n </div>\n </div>\n\n <div id=\"controls\">\n <button class=\"control-btn\" id=\"btn-prev\" title=\"Previous Slide (←)\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"15 18 9 12 15 6\"></polyline></svg>\n </button>\n <span id=\"slide-counter\">01 / ${String(slideCount).padStart(2, '0')}</span>\n <span id=\"step-badge\" class=\"step-badge\">1/1</span>\n <button class=\"control-btn\" id=\"btn-next\" title=\"Next Slide (→)\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg>\n </button>\n <div class=\"control-divider\"></div>\n <button class=\"control-btn\" id=\"btn-controls-presenter\" title=\"Presenter Console &amp; Notes (P)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect width=\"20\" height=\"14\" x=\"2\" y=\"3\" rx=\"2\"/><line x1=\"8\" x2=\"16\" y1=\"21\" y2=\"21\"/><line x1=\"12\" x2=\"12\" y1=\"17\" y2=\"21\"/></svg>\n </button>\n <button class=\"control-btn\" id=\"btn-controls-view\" title=\"Toggle Slide / Deck View (D)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><line x1=\"3\" x2=\"21\" y1=\"6\" y2=\"6\"/><line x1=\"3\" x2=\"21\" y1=\"12\" y2=\"12\"/><line x1=\"3\" x2=\"21\" y1=\"18\" y2=\"18\"/></svg>\n </button>\n <button class=\"control-btn\" id=\"btn-controls-print\" title=\"Print &amp; Export PDF (Cmd+P)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"6 9 6 2 18 2 18 9\"></polyline><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\"></path><rect x=\"6\" y=\"14\" width=\"12\" height=\"8\"></rect></svg>\n </button>\n <button class=\"control-btn\" id=\"btn-controls-scale\" title=\"Tune Scale\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 3 3 21\"/><path d=\"m14 4 6 6\"/><path d=\"m4 14 6 6\"/></svg>\n </button>\n <button class=\"control-btn\" id=\"btn-fullscreen\" title=\"Toggle Fullscreen (F)\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\"></path></svg>\n </button>\n </div>\n\n <script>\n (function() {\n const TOTAL_SLIDES = ${slideCount};\n let currentSlide = 0;\n let isEditMode = false;\n const canvas = document.getElementById('canvas');\n const originalCanvasHtml = canvas ? canvas.innerHTML : '';\n\n function computeDeckContentHash(str) {\n let h = 0;\n for (let i = 0; i < str.length; i++) {\n h = (Math.imul(31, h) + str.charCodeAt(i)) | 0;\n }\n return Math.abs(h).toString(36);\n }\n\n const deckIdAttr = document.documentElement.getAttribute('data-deck-id');\n const contentHash = computeDeckContentHash(originalCanvasHtml);\n const deckKeyPrefix = deckIdAttr ? encodeURIComponent(deckIdAttr) : (encodeURIComponent(document.title.trim() || 'deck') + '_' + contentHash);\n const STORAGE_KEY = 'deck_edit_' + deckKeyPrefix;\n const slides = document.querySelectorAll('.slide-page');\n const progressBar = document.getElementById('progress-bar');\n const counter = document.getElementById('slide-counter');\n\n // Edit UI elements\n const hotzone = document.getElementById('edit-hotzone');\n const editToggleBtn = document.getElementById('btn-edit-toggle');\n const editBar = document.getElementById('edit-bar');\n const btnBold = document.getElementById('btn-format-bold');\n const btnItalic = document.getElementById('btn-format-italic');\n const btnUnderline = document.getElementById('btn-format-underline');\n const btnReset = document.getElementById('btn-reset-edits');\n const btnSave = document.getElementById('btn-save-html');\n const btnDone = document.getElementById('btn-done-edit');\n\n // Scale Panel UI Elements\n const scalePanel = document.getElementById('scale-panel');\n const tabModeUnified = document.getElementById('tab-mode-unified');\n const tabModeIndependent = document.getElementById('tab-mode-independent');\n const btnScalePanelClose = document.getElementById('btn-scale-panel-close');\n const scaleUnifiedContent = document.getElementById('scale-unified-content');\n const scaleIndependentContent = document.getElementById('scale-independent-content');\n\n const inputScaleFactor = document.getElementById('input-scale-factor');\n const badgeScaleFactor = document.getElementById('badge-scale-factor');\n const inputScaleHeadings = document.getElementById('input-scale-headings');\n const badgeScaleHeadings = document.getElementById('badge-scale-headings');\n const inputScaleBody = document.getElementById('input-scale-body');\n const badgeScaleBody = document.getElementById('badge-scale-body');\n const inputScaleCode = document.getElementById('input-scale-code');\n const badgeScaleCode = document.getElementById('badge-scale-code');\n const inputScaleSpacing = document.getElementById('input-scale-spacing');\n const badgeScaleSpacing = document.getElementById('badge-scale-spacing');\n const btnResetScales = document.getElementById('btn-reset-scales');\n const densityChips = document.querySelectorAll('.density-chip');\n\n const btnEditbarScale = document.getElementById('btn-editbar-scale');\n const btnControlsScale = document.getElementById('btn-controls-scale');\n\n // Presenter, Deck & Print UI Elements\n const stepBadge = document.getElementById('step-badge');\n const btnPresenter = document.getElementById('btn-controls-presenter');\n const btnViewMode = document.getElementById('btn-controls-view');\n const btnPrint = document.getElementById('btn-controls-print');\n const presenterDrawer = document.getElementById('presenter-drawer');\n const btnClosePresenterDrawer = document.getElementById('btn-close-presenter-drawer');\n const btnOpenDualPresenter = document.getElementById('btn-open-dual-presenter');\n const presenterNotesText = document.getElementById('presenter-notes-text');\n const printModal = document.getElementById('print-modal');\n const chkPrintNotes = document.getElementById('chk-print-notes');\n const btnStartPrint = document.getElementById('btn-start-print');\n const btnCancelPrint = document.getElementById('btn-cancel-print');\n\n let isDeckMode = false;\n let presenterWindow = null;\n let timerSeconds = 0;\n let timerInterval = null;\n let isTimerRunning = false;\n\n const SCALE_STORAGE_KEY = 'deck_scale_' + deckKeyPrefix;\n let scaleState = {\n mode: 'unified', // 'unified' | 'independent'\n factor: 1.0,\n headings: 1.0,\n body: 1.0,\n code: 1.0,\n spacing: 1.0\n };\n\n // Restore saved scale state if present\n try {\n const savedScale = localStorage.getItem(SCALE_STORAGE_KEY);\n if (savedScale) {\n const parsed = JSON.parse(savedScale);\n if (parsed && typeof parsed === 'object') {\n scaleState = Object.assign(scaleState, parsed);\n }\n }\n } catch (e) {}\n\n // Auto-scale 1920x1080 canvas to fit window\n function resizeCanvas() {\n if (isDeckMode) return;\n const vw = window.innerWidth || document.documentElement.clientWidth || 1920;\n const vh = window.innerHeight || document.documentElement.clientHeight || 1080;\n if (vw <= 0 || vh <= 0) return;\n const scaleX = vw / 1920;\n const scaleY = vh / 1080;\n const scale = Math.min(scaleX, scaleY);\n canvas.style.transform = 'translate(-50%, -50%) scale(' + scale + ')';\n }\n\n window.addEventListener('resize', resizeCanvas);\n window.addEventListener('load', resizeCanvas);\n document.addEventListener('DOMContentLoaded', resizeCanvas);\n resizeCanvas();\n requestAnimationFrame(resizeCanvas);\n\n function getSlideNotes(slideIndex) {\n if (slideIndex < 0 || slideIndex >= slides.length) return '';\n const slide = slides[slideIndex];\n const rawNotes = slide.getAttribute('data-notes');\n if (!rawNotes) return '';\n try {\n return decodeURIComponent(rawNotes);\n } catch (e) {\n return rawNotes;\n }\n }\n\n function renderTimerText() {\n const mins = String(Math.floor(timerSeconds / 60)).padStart(2, '0');\n const secs = String(timerSeconds % 60).padStart(2, '0');\n const str = mins + ':' + secs;\n const badge = document.getElementById('presenter-timer-text');\n if (badge) badge.textContent = str;\n return str;\n }\n\n function startTimer() {\n if (timerInterval) return;\n isTimerRunning = true;\n timerInterval = setInterval(() => {\n timerSeconds++;\n renderTimerText();\n }, 1000);\n }\n\n function pauseTimer() {\n if (timerInterval) {\n clearInterval(timerInterval);\n timerInterval = null;\n }\n isTimerRunning = false;\n }\n\n function resetTimer() {\n pauseTimer();\n timerSeconds = 0;\n renderTimerText();\n }\n\n // Step-by-Step Reveal (Animation Steps / Build-In)\n function getSlideSteps(slideIndex) {\n if (slideIndex < 0 || slideIndex >= slides.length) return [];\n const slide = slides[slideIndex];\n let stepEls = Array.from(slide.querySelectorAll('[data-step], [data-reveal], [data-click], .step-reveal, .v-click, .step-item'));\n if (stepEls.length === 0) {\n const autoContainers = slide.querySelectorAll('[data-auto-reveal=\"true\"], .auto-reveal, .step-group');\n autoContainers.forEach(container => {\n const children = container.querySelectorAll('li, .card-item, .step-card, .compare-col');\n children.forEach(c => {\n c.classList.add('step-reveal');\n stepEls.push(c);\n });\n });\n }\n return stepEls;\n }\n\n function updateStepBadge() {\n if (!stepBadge) return;\n if (isDeckMode) {\n stepBadge.style.display = 'none';\n return;\n }\n const steps = getSlideSteps(currentSlide);\n if (steps.length === 0) {\n stepBadge.style.display = 'none';\n return;\n }\n const revealedCount = steps.filter(el => el.classList.contains('revealed') || el.classList.contains('visible')).length;\n stepBadge.style.display = 'inline-block';\n stepBadge.textContent = 'Step ' + revealedCount + '/' + steps.length;\n }\n\n function stepForward() {\n if (isEditMode || isDeckMode) {\n nextSlide();\n return;\n }\n const steps = getSlideSteps(currentSlide);\n if (steps.length > 0) {\n const nextUnrevealed = steps.find(el => !el.classList.contains('revealed') && !el.classList.contains('visible'));\n if (nextUnrevealed) {\n nextUnrevealed.classList.add('revealed', 'visible');\n updateStepBadge();\n broadcastDeckState();\n return;\n }\n }\n if (currentSlide < TOTAL_SLIDES - 1) {\n showSlide(currentSlide + 1);\n }\n }\n\n function stepBackward() {\n if (isEditMode || isDeckMode) {\n prevSlide();\n return;\n }\n const steps = getSlideSteps(currentSlide);\n if (steps.length > 0) {\n const revealedList = steps.filter(el => el.classList.contains('revealed') || el.classList.contains('visible'));\n if (revealedList.length > 0) {\n const lastRevealed = revealedList[revealedList.length - 1];\n lastRevealed.classList.remove('revealed', 'visible');\n updateStepBadge();\n broadcastDeckState();\n return;\n }\n }\n if (currentSlide > 0) {\n showSlide(currentSlide - 1);\n const prevSteps = getSlideSteps(currentSlide);\n prevSteps.forEach(s => s.classList.add('revealed', 'visible'));\n updateStepBadge();\n broadcastDeckState();\n }\n }\n\n // Presenter State & Sync\n const SYNC_CHANNEL_NAME = 'deck_sync_' + encodeURIComponent(document.title.trim() || 'default');\n let syncChannel = null;\n try {\n syncChannel = new BroadcastChannel(SYNC_CHANNEL_NAME);\n syncChannel.onmessage = function(e) {\n if (!e.data) return;\n if (e.data.type === 'COMMAND') {\n if (e.data.action === 'NEXT') stepForward();\n else if (e.data.action === 'PREV') stepBackward();\n else if (e.data.action === 'GOTO' && typeof e.data.index === 'number') showSlide(e.data.index);\n else if (e.data.action === 'RESET_TIMER') resetTimer();\n else if (e.data.action === 'TOGGLE_TIMER') {\n if (isTimerRunning) pauseTimer(); else startTimer();\n }\n } else if (e.data.type === 'REQUEST_STATE') {\n broadcastDeckState();\n }\n };\n } catch (err) {}\n\n function broadcastDeckState() {\n const currentNotes = getSlideNotes(currentSlide);\n const nextNotes = getSlideNotes(currentSlide + 1);\n const payload = {\n type: 'STATE_UPDATE',\n currentSlide: currentSlide,\n totalSlides: TOTAL_SLIDES,\n notes: currentNotes,\n nextNotes: nextNotes,\n title: document.title,\n timerSeconds: timerSeconds,\n timerRunning: isTimerRunning\n };\n\n if (syncChannel) {\n try { syncChannel.postMessage(payload); } catch (e) {}\n }\n if (presenterWindow && !presenterWindow.closed) {\n try { presenterWindow.postMessage(payload, '*'); } catch (e) {}\n }\n updatePresenterDrawerUI();\n }\n\n function updatePresenterDrawerUI() {\n if (!presenterNotesText) return;\n const notes = getSlideNotes(currentSlide);\n if (notes && notes.trim()) {\n presenterNotesText.innerHTML = notes.split('\\\\n').join('<br/>');\n } else {\n presenterNotesText.innerHTML = '<span style=\"opacity: 0.5; font-style: italic;\">No speaker notes for Slide ' + (currentSlide + 1) + '.</span>';\n }\n }\n\n function togglePresenterDrawer() {\n if (!presenterDrawer) return;\n const isOpen = presenterDrawer.classList.toggle('open');\n if (isOpen) {\n startTimer();\n updatePresenterDrawerUI();\n }\n }\n\n function openPresenterConsole() {\n startTimer();\n const popupName = 'PresenterConsole_' + encodeURIComponent(document.title.replace(/[^a-zA-Z0-9_]/g, ''));\n const win = window.open('', popupName, 'width=1100,height=720,menubar=no,toolbar=no,location=no,status=no');\n if (!win) {\n togglePresenterDrawer();\n return;\n }\n presenterWindow = win;\n\n const currentNotes = getSlideNotes(currentSlide);\n const curSlideTitle = 'Slide ' + (currentSlide + 1) + ' / ' + TOTAL_SLIDES;\n\n const consoleHtml = [\n '<!DOCTYPE html>',\n '<html>',\n '<head>',\n '<meta charset=\"UTF-8\"><title>Presenter Console — ' + (document.title || 'Slides') + '</title>',\n '<style>',\n '* { box-sizing: border-box; margin: 0; padding: 0; }',\n 'body { background: #0c0d14; color: #f4f4f5; font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; display: flex; flex-direction: column; height: 100vh; overflow: hidden; user-select: none; }',\n '.top-bar { display: flex; align-items: center; justify-content: space-between; padding: 12px 24px; background: #141522; border-bottom: 1px solid rgba(255,255,255,0.12); }',\n '.timer-badge { font-family: monospace; font-size: 20px; font-weight: 700; color: #38bdf8; background: rgba(56,189,248,0.15); border: 1px solid rgba(56,189,248,0.3); padding: 4px 14px; border-radius: 8px; display: flex; align-items: center; gap: 8px; }',\n '.grid-container { flex: 1; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; padding: 20px 24px; overflow: hidden; }',\n '.panel-card { background: #181926; border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; display: flex; flex-direction: column; overflow: hidden; }',\n '.card-header { padding: 10px 16px; background: rgba(255,255,255,0.04); border-bottom: 1px solid rgba(255,255,255,0.08); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #a1a1aa; display: flex; justify-content: space-between; }',\n '.notes-content { flex: 1; padding: 20px; font-size: 19px; line-height: 1.7; overflow-y: auto; color: #f1f5f9; user-select: text; white-space: pre-wrap; }',\n '.preview-box { flex: 1; display: flex; align-items: center; justify-content: center; background: #000; overflow: hidden; padding: 16px; }',\n '.btn { background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.15); color: #fff; padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 600; }',\n '.btn:hover { background: rgba(255,255,255,0.2); }',\n '.btn-nav { font-size: 16px; padding: 10px 24px; border-radius: 8px; }',\n '.bottom-nav { display: flex; align-items: center; justify-content: space-between; padding: 14px 24px; background: #141522; border-top: 1px solid rgba(255,255,255,0.12); }',\n '</style>',\n '</head>',\n '<body>',\n '<div class=\"top-bar\">',\n ' <div style=\"font-weight: 700; font-size: 16px;\">' + (document.title || 'Slide Deck') + '</div>',\n ' <div style=\"display: flex; align-items: center; gap: 12px;\">',\n ' <div class=\"timer-badge\">⏱ <span id=\"timer-val\">00:00</span></div>',\n ' <button class=\"btn\" id=\"btn-toggle-t\">Pause</button>',\n ' <button class=\"btn\" id=\"btn-reset-t\">Reset</button>',\n ' </div>',\n '</div>',\n '<div class=\"grid-container\">',\n ' <div class=\"panel-card\">',\n ' <div class=\"card-header\"><span>CURRENT SLIDE</span><span id=\"slide-num\">' + curSlideTitle + '</span></div>',\n ' <div class=\"preview-box\"><div style=\"font-size: 22px; color: #94a3b8; text-align: center;\">Active Slide On Screen<br/><span style=\"font-size: 14px; color: #38bdf8;\">(Broadcasting Synced)</span></div></div>',\n ' <div class=\"card-header\" style=\"border-top: 1px solid rgba(255,255,255,0.08);\"><span>NEXT SLIDE PREVIEW</span></div>',\n ' <div style=\"padding: 12px 16px; font-size: 14px; color: #94a3b8; background: rgba(0,0,0,0.3); min-height: 80px;\" id=\"next-preview-text\">Upcoming: Slide ' + (currentSlide + 2) + '</div>',\n ' </div>',\n ' <div class=\"panel-card\">',\n ' <div class=\"card-header\"><span>SPEAKER NOTES / TALK TRACK</span><div><button class=\"btn\" id=\"font-dn\" style=\"padding: 2px 8px;\">A-</button> <button class=\"btn\" id=\"font-up\" style=\"padding: 2px 8px;\">A+</button></div></div>',\n ' <div class=\"notes-content\" id=\"speaker-notes\">' + (currentNotes ? currentNotes : '<span style=\"opacity: 0.4;\">No speaker notes for this slide.</span>') + '</div>',\n ' </div>',\n '</div>',\n '<div class=\"bottom-nav\">',\n ' <button class=\"btn btn-nav\" id=\"btn-prev-p\">‹ Previous (←)</button>',\n ' <span style=\"font-size: 14px; color: #a1a1aa;\">Use Arrow Keys or Space to advance</span>',\n ' <button class=\"btn btn-nav\" id=\"btn-next-p\" style=\"background: #0284c7; border-color: #0284c7;\">Next (→ / Space) ›</button>',\n '</div>',\n '<script>',\n ' const bc = new BroadcastChannel(\"' + SYNC_CHANNEL_NAME + '\");',\n ' let curSecs = ' + timerSeconds + ';',\n ' function sendCmd(action, idx) { bc.postMessage({ type: \"COMMAND\", action: action, index: idx }); }',\n ' document.getElementById(\"btn-prev-p\").onclick = () => sendCmd(\"PREV\");',\n ' document.getElementById(\"btn-next-p\").onclick = () => sendCmd(\"NEXT\");',\n ' document.getElementById(\"btn-reset-t\").onclick = () => sendCmd(\"RESET_TIMER\");',\n ' document.getElementById(\"btn-toggle-t\").onclick = () => sendCmd(\"TOGGLE_TIMER\");',\n ' let curFontSize = 19;',\n ' document.getElementById(\"font-up\").onclick = () => { curFontSize += 2; document.getElementById(\"speaker-notes\").style.fontSize = curFontSize + \"px\"; };',\n ' document.getElementById(\"font-dn\").onclick = () => { curFontSize = Math.max(12, curFontSize - 2); document.getElementById(\"speaker-notes\").style.fontSize = curFontSize + \"px\"; };',\n ' window.onkeydown = (e) => {',\n ' if ([\"ArrowRight\", \"Space\", \"PageDown\"].includes(e.code)) { e.preventDefault(); sendCmd(\"NEXT\"); }',\n ' else if ([\"ArrowLeft\", \"PageUp\"].includes(e.code)) { e.preventDefault(); sendCmd(\"PREV\"); }',\n ' };',\n ' bc.onmessage = (e) => {',\n ' if (!e.data || e.data.type !== \"STATE_UPDATE\") return;',\n ' document.getElementById(\"slide-num\").textContent = \"Slide \" + (e.data.currentSlide + 1) + \" / \" + e.data.totalSlides;',\n ' document.getElementById(\"speaker-notes\").textContent = e.data.notes || \"No speaker notes for this slide.\";',\n ' document.getElementById(\"next-preview-text\").textContent = e.data.nextNotes ? \"Next Notes: \" + e.data.nextNotes : \"Next: Slide \" + (e.data.currentSlide + 2);',\n ' curSecs = e.data.timerSeconds || 0;',\n ' const m = String(Math.floor(curSecs / 60)).padStart(2, \"0\");',\n ' const s = String(curSecs % 60).padStart(2, \"0\");',\n ' document.getElementById(\"timer-val\").textContent = m + \":\" + s;',\n ' document.getElementById(\"btn-toggle-t\").textContent = e.data.timerRunning ? \"Pause\" : \"Start\";',\n ' };',\n ' bc.postMessage({ type: \"REQUEST_STATE\" });',\n '<' + '/script>',\n '</body>',\n '</html>'\n ].join('\\\\n');\n\n win.document.open();\n win.document.write(consoleHtml);\n win.document.close();\n }\n\n // Deck Mode & Print Engine\n function ensureDeckNotesCards() {\n slides.forEach((slide, idx) => {\n const notes = getSlideNotes(idx);\n let existingCard = slide.parentElement.querySelector('.deck-notes-card[data-for-slide=\"' + idx + '\"]');\n if (!existingCard && notes) {\n const card = document.createElement('div');\n card.className = 'deck-notes-card';\n card.setAttribute('data-for-slide', String(idx));\n card.innerHTML = '<div class=\"deck-notes-card-header\"><span>📝</span><span>Presenter Notes (Slide ' + (idx + 1) + ')</span></div><div>' + notes.split('\\\\n').join('<br/>') + '</div>';\n slide.after(card);\n }\n });\n }\n\n function toggleDeckMode(force) {\n isDeckMode = typeof force === 'boolean' ? force : !isDeckMode;\n document.body.classList.toggle('mode-deck', isDeckMode);\n if (isDeckMode) {\n ensureDeckNotesCards();\n document.querySelectorAll('.step-reveal, [data-step], [data-reveal], [data-click], .v-click').forEach(el => {\n el.classList.add('revealed', 'visible');\n });\n } else {\n resizeCanvas();\n }\n updateStepBadge();\n }\n\n function openPrintModal() {\n if (printModal) printModal.classList.add('open');\n }\n\n function closePrintModal() {\n if (printModal) printModal.classList.remove('open');\n }\n\n function executePrint() {\n closePrintModal();\n const withNotes = chkPrintNotes ? chkPrintNotes.checked : false;\n document.body.classList.toggle('print-with-notes', withNotes);\n ensureDeckNotesCards();\n document.querySelectorAll('.step-reveal, [data-step], [data-reveal], [data-click], .v-click').forEach(el => {\n el.classList.add('revealed', 'visible');\n });\n setTimeout(() => {\n window.print();\n }, 100);\n }\n\n // Show slide by index\n function showSlide(index) {\n if (index < 0) index = 0;\n if (index >= TOTAL_SLIDES) index = TOTAL_SLIDES - 1;\n currentSlide = index;\n\n slides.forEach((s, idx) => {\n if (idx === currentSlide) {\n s.classList.add('active');\n } else {\n s.classList.remove('active');\n }\n });\n\n // Reset step state for newly shown slide\n const steps = getSlideSteps(currentSlide);\n steps.forEach(s => s.classList.remove('revealed', 'visible'));\n updateStepBadge();\n updatePresenterDrawerUI();\n broadcastDeckState();\n\n // Update progress bar\n const pct = TOTAL_SLIDES > 1 ? ((currentSlide) / (TOTAL_SLIDES - 1)) * 100 : 100;\n if (progressBar) progressBar.style.width = pct + '%';\n\n // Update counter\n if (counter) {\n const curStr = String(currentSlide + 1).padStart(2, '0');\n const totStr = String(TOTAL_SLIDES).padStart(2, '0');\n counter.textContent = curStr + ' / ' + totStr;\n }\n\n // Send postMessage to parent window if embedded in iframe\n if (window.parent && window.parent !== window) {\n window.parent.postMessage({\n type: 'PRESENTATION_SLIDE_CHANGE',\n currentIndex: currentSlide,\n totalSlides: TOTAL_SLIDES\n }, '*');\n }\n }\n\n function nextSlide() {\n showSlide(currentSlide + 1);\n }\n\n function prevSlide() {\n showSlide(currentSlide - 1);\n }\n\n function toggleFullscreen() {\n if (!document.fullscreenElement) {\n document.documentElement.requestFullscreen().catch(() => {});\n } else {\n document.exitFullscreen().catch(() => {});\n }\n }\n\n // ─── Scale Architecture Controller (Unified vs Independent) ───\n function applyScales() {\n const root = document.documentElement;\n root.style.setProperty('--scale-mode', scaleState.mode);\n root.style.setProperty('--scale-factor', scaleState.factor);\n root.style.setProperty('--scale-headings', scaleState.headings);\n root.style.setProperty('--scale-body', scaleState.body);\n root.style.setProperty('--scale-code', scaleState.code);\n root.style.setProperty('--scale-spacing', scaleState.spacing);\n\n const isUnified = scaleState.mode === 'unified';\n const effHeadings = isUnified ? scaleState.factor : scaleState.headings;\n const effBody = isUnified ? scaleState.factor : scaleState.body;\n const effCode = isUnified ? scaleState.factor : scaleState.code;\n const effSpacing = isUnified ? scaleState.factor : scaleState.spacing;\n\n root.style.setProperty('--scale-headings-eff', effHeadings);\n root.style.setProperty('--scale-body-eff', effBody);\n root.style.setProperty('--scale-code-eff', effCode);\n root.style.setProperty('--scale-spacing-eff', effSpacing);\n\n // Synchronize UI widgets\n if (inputScaleFactor) inputScaleFactor.value = Math.round(scaleState.factor * 100);\n if (badgeScaleFactor) badgeScaleFactor.textContent = Math.round(scaleState.factor * 100) + '%';\n\n if (inputScaleHeadings) inputScaleHeadings.value = Math.round(scaleState.headings * 100);\n if (badgeScaleHeadings) badgeScaleHeadings.textContent = Math.round(scaleState.headings * 100) + '%';\n\n if (inputScaleBody) inputScaleBody.value = Math.round(scaleState.body * 100);\n if (badgeScaleBody) badgeScaleBody.textContent = Math.round(scaleState.body * 100) + '%';\n\n if (inputScaleCode) inputScaleCode.value = Math.round(scaleState.code * 100);\n if (badgeScaleCode) badgeScaleCode.textContent = Math.round(scaleState.code * 100) + '%';\n\n if (inputScaleSpacing) inputScaleSpacing.value = Math.round(scaleState.spacing * 100);\n if (badgeScaleSpacing) badgeScaleSpacing.textContent = Math.round(scaleState.spacing * 100) + '%';\n\n // Update density chips\n densityChips.forEach(chip => {\n const s = parseInt(chip.getAttribute('data-scale'), 10);\n chip.classList.toggle('active', isUnified && Math.round(scaleState.factor * 100) === s);\n });\n\n // Save to localStorage\n try {\n localStorage.setItem(SCALE_STORAGE_KEY, JSON.stringify(scaleState));\n } catch (e) {}\n\n // Notify parent React app if embedded\n if (window.parent && window.parent !== window) {\n window.parent.postMessage({\n type: 'SLIDE_SCALE_CHANGED',\n scaleState: Object.assign({}, scaleState),\n effective: {\n headings: effHeadings,\n body: effBody,\n code: effCode,\n spacing: effSpacing\n }\n }, '*');\n }\n }\n\n function setScaleMode(mode) {\n scaleState.mode = mode;\n if (tabModeUnified && tabModeIndependent) {\n tabModeUnified.classList.toggle('active', mode === 'unified');\n tabModeIndependent.classList.toggle('active', mode === 'independent');\n }\n if (scaleUnifiedContent && scaleIndependentContent) {\n scaleUnifiedContent.style.display = mode === 'unified' ? 'block' : 'none';\n scaleIndependentContent.style.display = mode === 'independent' ? 'block' : 'none';\n }\n applyScales();\n }\n\n function toggleScalePanel(show) {\n if (!scalePanel) return;\n const currentOpen = scalePanel.style.display !== 'none';\n const willOpen = show !== undefined ? show : !currentOpen;\n scalePanel.style.display = willOpen ? 'block' : 'none';\n if (willOpen) {\n setScaleMode(scaleState.mode);\n }\n }\n\n // ─── Scale Panel Event Listeners ───\n if (tabModeUnified) tabModeUnified.addEventListener('click', () => setScaleMode('unified'));\n if (tabModeIndependent) tabModeIndependent.addEventListener('click', () => setScaleMode('independent'));\n if (btnScalePanelClose) btnScalePanelClose.addEventListener('click', () => toggleScalePanel(false));\n\n if (inputScaleFactor) {\n inputScaleFactor.addEventListener('input', (e) => {\n scaleState.factor = parseFloat(e.target.value) / 100;\n applyScales();\n });\n }\n if (inputScaleHeadings) {\n inputScaleHeadings.addEventListener('input', (e) => {\n scaleState.headings = parseFloat(e.target.value) / 100;\n applyScales();\n });\n }\n if (inputScaleBody) {\n inputScaleBody.addEventListener('input', (e) => {\n scaleState.body = parseFloat(e.target.value) / 100;\n applyScales();\n });\n }\n if (inputScaleCode) {\n inputScaleCode.addEventListener('input', (e) => {\n scaleState.code = parseFloat(e.target.value) / 100;\n applyScales();\n });\n }\n if (inputScaleSpacing) {\n inputScaleSpacing.addEventListener('input', (e) => {\n scaleState.spacing = parseFloat(e.target.value) / 100;\n applyScales();\n });\n }\n\n densityChips.forEach(chip => {\n chip.addEventListener('click', () => {\n const val = parseInt(chip.getAttribute('data-scale'), 10) / 100;\n scaleState.factor = val;\n setScaleMode('unified');\n });\n });\n\n if (btnResetScales) {\n btnResetScales.addEventListener('click', () => {\n scaleState.factor = 1.0;\n scaleState.headings = 1.0;\n scaleState.body = 1.0;\n scaleState.code = 1.0;\n scaleState.spacing = 1.0;\n applyScales();\n });\n }\n\n if (btnEditbarScale) btnEditbarScale.addEventListener('click', () => toggleScalePanel());\n if (btnControlsScale) btnControlsScale.addEventListener('click', () => toggleScalePanel());\n\n // ─── Level 1 Inline Editing Controller ──────────────────────────────\n function toggleEditMode(force) {\n isEditMode = force !== undefined ? force : !isEditMode;\n\n document.body.classList.toggle('edit-mode-active', isEditMode);\n editToggleBtn.classList.toggle('active', isEditMode);\n editBar.style.display = isEditMode ? 'flex' : 'none';\n\n const editableSelectors = 'h1, h2, h3, h4, p, li, span, pre code, .opt-badge';\n const targets = canvas.querySelectorAll(editableSelectors);\n\n targets.forEach(el => {\n if (isEditMode) {\n el.setAttribute('contenteditable', 'true');\n el.setAttribute('spellcheck', 'false');\n } else {\n el.removeAttribute('contenteditable');\n }\n });\n\n if (window.parent && window.parent !== window) {\n window.parent.postMessage({\n type: 'SLIDE_EDIT_MODE_CHANGED',\n isEditMode: isEditMode\n }, '*');\n }\n }\n\n // Hotzone hover with 400ms delay timeout (frontend-slides standard)\n let hideHotzoneTimeout = null;\n if (hotzone && editToggleBtn) {\n hotzone.addEventListener('mouseenter', () => {\n clearTimeout(hideHotzoneTimeout);\n editToggleBtn.classList.add('show');\n });\n hotzone.addEventListener('mouseleave', () => {\n hideHotzoneTimeout = setTimeout(() => {\n if (!isEditMode) editToggleBtn.classList.remove('show');\n }, 400);\n });\n editToggleBtn.addEventListener('mouseenter', () => {\n clearTimeout(hideHotzoneTimeout);\n });\n editToggleBtn.addEventListener('mouseleave', () => {\n hideHotzoneTimeout = setTimeout(() => {\n if (!isEditMode) editToggleBtn.classList.remove('show');\n }, 400);\n });\n hotzone.addEventListener('click', () => toggleEditMode());\n editToggleBtn.addEventListener('click', () => toggleEditMode());\n }\n\n // Format actions\n if (btnBold) btnBold.addEventListener('click', () => document.execCommand('bold'));\n if (btnItalic) btnItalic.addEventListener('click', () => document.execCommand('italic'));\n if (btnUnderline) btnUnderline.addEventListener('click', () => document.execCommand('underline'));\n if (btnDone) btnDone.addEventListener('click', () => toggleEditMode(false));\n\n // Reset action\n if (btnReset) {\n btnReset.addEventListener('click', () => {\n if (confirm('Are you sure you want to reset all edits back to original?')) {\n canvas.innerHTML = originalCanvasHtml;\n try { localStorage.removeItem(STORAGE_KEY); } catch (e) {}\n showSlide(currentSlide);\n if (isEditMode) toggleEditMode(true);\n }\n });\n }\n\n // Auto-save to LocalStorage on edit\n let saveTimer = null;\n canvas.addEventListener('input', () => {\n if (!isEditMode) return;\n clearTimeout(saveTimer);\n saveTimer = setTimeout(() => {\n try {\n localStorage.setItem(STORAGE_KEY, canvas.innerHTML);\n } catch (e) {}\n }, 500);\n });\n\n // Restore from localStorage if present\n try {\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved && saved.trim()) {\n canvas.innerHTML = saved;\n }\n } catch (e) {}\n\n // Save & Clean HTML Export\n function exportAndSaveHtml() {\n const wasEditing = isEditMode;\n if (wasEditing) toggleEditMode(false);\n if (scalePanel) scalePanel.style.display = 'none';\n\n // Take snapshot of full HTML\n let cleanHtml = '<!DOCTYPE html>' + '\\\\n' + document.documentElement.outerHTML;\n\n // Clean any temporary runtime inline styles or attributes\n cleanHtml = cleanHtml\n .replace(/contenteditable=\"true\"/g, '')\n .replace(/spellcheck=\"false\"/g, '')\n .replace(/edit-mode-active/g, '')\n .replace(/class=\"edit-toggle[^\"]*\"/g, 'class=\"edit-toggle\"');\n\n // Embed current scale settings into :root style definition so exported HTML is permanently styled with chosen scales\n const scaleVarsReplacement = [\n '--scale-mode: ' + scaleState.mode + ';',\n ' --scale-factor: ' + scaleState.factor + ';',\n ' --scale-headings: ' + scaleState.headings + ';',\n ' --scale-body: ' + scaleState.body + ';',\n ' --scale-code: ' + scaleState.code + ';',\n ' --scale-spacing: ' + scaleState.spacing + ';',\n ' --scale-headings-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.headings) + ';',\n ' --scale-body-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.body) + ';',\n ' --scale-code-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.code) + ';',\n ' --scale-spacing-eff: ' + (scaleState.mode === 'unified' ? scaleState.factor : scaleState.spacing) + ';'\n ].join('\\\\n');\n\n cleanHtml = cleanHtml.replace(/--scale-mode:\\\\s*[^;]+;[\\\\s\\\\S]*?--scale-spacing-eff:\\\\s*[^;]+;/, scaleVarsReplacement);\n\n // Create Blob and trigger download\n const blob = new Blob([cleanHtml], { type: 'text/html;charset=utf-8' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n const filename = (document.title.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase() || 'presentation') + '.html';\n a.href = url;\n a.download = filename;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n\n // Notify parent React frame if embedded\n if (window.parent && window.parent !== window) {\n window.parent.postMessage({\n type: 'SLIDE_HTML_SAVED',\n html: cleanHtml,\n filename: filename\n }, '*');\n }\n\n if (wasEditing) toggleEditMode(true);\n }\n\n if (btnSave) btnSave.addEventListener('click', exportAndSaveHtml);\n\n // Keyboard navigation & Shortcuts\n window.addEventListener('keydown', function(e) {\n // Guard: If typing inside contenteditable, allow normal cursor movement and typing\n if (e.target.isContentEditable || e.target.getAttribute('contenteditable') === 'true') {\n if (e.key === 'Escape') {\n e.target.blur();\n }\n return;\n }\n\n // Shortcut: E key toggles edit mode\n if (e.key === 'e' || e.key === 'E') {\n e.preventDefault();\n toggleEditMode();\n return;\n }\n\n // Shortcut: Cmd+S / Ctrl+S saves HTML\n if ((e.metaKey || e.ctrlKey) && (e.key === 's' || e.key === 'S')) {\n e.preventDefault();\n exportAndSaveHtml();\n return;\n }\n\n // Shortcut: Cmd+P / Ctrl+P opens Print & PDF dialog\n if ((e.metaKey || e.ctrlKey) && (e.key === 'p' || e.key === 'P')) {\n e.preventDefault();\n openPrintModal();\n return;\n }\n\n // Shortcut: P toggles Presenter Console / Drawer\n if (e.key === 'p' || e.key === 'P') {\n e.preventDefault();\n togglePresenterDrawer();\n return;\n }\n\n // Shortcut: D toggles Deck Mode (continuous scroll)\n if (e.key === 'd' || e.key === 'D') {\n e.preventDefault();\n toggleDeckMode();\n return;\n }\n\n if (['ArrowRight', 'ArrowDown', 'Space', 'PageDown'].includes(e.code)) {\n e.preventDefault();\n stepForward();\n } else if (['ArrowLeft', 'ArrowUp', 'PageUp'].includes(e.code)) {\n e.preventDefault();\n stepBackward();\n } else if (e.code === 'KeyF') {\n e.preventDefault();\n toggleFullscreen();\n } else if (e.code === 'Home') {\n e.preventDefault();\n showSlide(0);\n } else if (e.code === 'End') {\n e.preventDefault();\n showSlide(TOTAL_SLIDES - 1);\n }\n });\n\n // Canvas click to advance step / slide in Presentation mode\n document.getElementById('viewport').addEventListener('click', function(e) {\n if (isEditMode || isDeckMode) return;\n if (e.target.closest('#controls, #edit-bar, #scale-panel, #presenter-drawer, #print-modal, .edit-toggle, .quiz-option, button, a, input, select')) return;\n stepForward();\n });\n\n // Quiz interactivity: click option to toggle selected state\n document.addEventListener('click', function(e) {\n if (isEditMode) return; // In edit mode, let user click to edit text instead of quiz logic\n const option = e.target.closest('.quiz-option');\n if (option) {\n const parentGrid = option.parentElement;\n if (parentGrid) {\n parentGrid.querySelectorAll('.quiz-option').forEach(el => el.classList.remove('selected'));\n }\n option.classList.add('selected');\n\n const currentSlideEl = option.closest('.slide-page');\n if (currentSlideEl) {\n const exp = currentSlideEl.querySelector('.quiz-explanation');\n if (exp) {\n exp.style.opacity = '1';\n exp.style.filter = 'none';\n }\n }\n }\n });\n\n // Controls click\n document.getElementById('btn-next').addEventListener('click', stepForward);\n document.getElementById('btn-prev').addEventListener('click', stepBackward);\n document.getElementById('btn-fullscreen').addEventListener('click', toggleFullscreen);\n\n if (btnPresenter) btnPresenter.addEventListener('click', togglePresenterDrawer);\n if (btnViewMode) btnViewMode.addEventListener('click', () => toggleDeckMode());\n if (btnPrint) btnPrint.addEventListener('click', openPrintModal);\n if (btnClosePresenterDrawer) btnClosePresenterDrawer.addEventListener('click', togglePresenterDrawer);\n if (btnOpenDualPresenter) btnOpenDualPresenter.addEventListener('click', openPresenterConsole);\n if (btnStartPrint) btnStartPrint.addEventListener('click', executePrint);\n if (btnCancelPrint) btnCancelPrint.addEventListener('click', closePrintModal);\n if (printModal) {\n printModal.addEventListener('click', function(e) {\n if (e.target === printModal) closePrintModal();\n });\n }\n\n // Listen for messages from parent container\n window.addEventListener('message', function(e) {\n if (!e.data || typeof e.data !== 'object') return;\n if (e.data.type === 'GOTO_SLIDE' && typeof e.data.index === 'number') {\n showSlide(e.data.index);\n } else if (e.data.type === 'NEXT_SLIDE' || e.data.type === 'STEP_FORWARD') {\n stepForward();\n } else if (e.data.type === 'PREV_SLIDE' || e.data.type === 'STEP_BACKWARD') {\n stepBackward();\n } else if (e.data.type === 'TOGGLE_EDIT_MODE') {\n toggleEditMode();\n } else if (e.data.type === 'EXPORT_HTML') {\n exportAndSaveHtml();\n } else if (e.data.type === 'TOGGLE_PRESENTER') {\n togglePresenterDrawer();\n } else if (e.data.type === 'OPEN_PRESENTER_CONSOLE') {\n openPresenterConsole();\n } else if (e.data.type === 'TOGGLE_DECK_MODE') {\n toggleDeckMode();\n } else if (e.data.type === 'PRINT_DECK') {\n executePrint();\n } else if (e.data.type === 'SET_SCALE') {\n if (e.data.mode) setScaleMode(e.data.mode);\n if (typeof e.data.factor === 'number') scaleState.factor = e.data.factor;\n if (typeof e.data.headings === 'number') scaleState.headings = e.data.headings;\n if (typeof e.data.body === 'number') scaleState.body = e.data.body;\n if (typeof e.data.code === 'number') scaleState.code = e.data.code;\n if (typeof e.data.spacing === 'number') scaleState.spacing = e.data.spacing;\n applyScales();\n } else if (e.data.type === 'TOGGLE_SCALE_PANEL') {\n toggleScalePanel();\n } else if (e.data.type === 'RESIZE') {\n resizeCanvas();\n }\n });\n\n // Initial setup\n applyScales();\n setScaleMode(scaleState.mode);\n showSlide(0);\n })();\n </script>\n</body>\n</html>`;\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit — Slide Slot Normalizer & Resilience Layer\n// Safely maps raw/flat LLM responses into strictly populated layout slots\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { HtmlLayoutPresetId } from './types.js';\n\nexport function normalizeSlideSlots(slide: any): Record<string, any> {\n if (!slide || typeof slide !== 'object') {\n return { title: 'Slide' };\n }\n\n // Flatten: merge top-level slide properties with nested slide.slots (slots takes precedence)\n const slots: Record<string, any> = {\n ...slide,\n ...(slide.slots || {}),\n };\n delete slots.slots;\n\n // 1. Universal title and tag normalization\n slots.title = String(slots.title || slide.title || slide.heading || slide.name || '').trim();\n slots.tag = String(slots.tag || slide.tag || slide.badge || '').trim();\n\n // Extract raw text content if provided by LLM\n const rawContent = typeof slots.content === 'string' && slots.content.trim() ? slots.content.trim() :\n typeof slide.content === 'string' && slide.content.trim() ? slide.content.trim() : '';\n\n const layoutId = (slide.layoutId || 'hero-cover') as HtmlLayoutPresetId;\n\n switch (layoutId) {\n case 'hero-cover': {\n slots.subtitle = slots.subtitle || slide.subtitle || slots.desc || rawContent || '';\n slots.author = slots.author || slide.author || '';\n slots.date = slots.date || slide.date || '';\n break;\n }\n\n case 'split-concept-code': {\n // Extract code if not explicitly given\n if (!slots.code) {\n if (slide.code) {\n slots.code = slide.code;\n } else if (rawContent) {\n const fenceMatch = rawContent.match(/```(?:\\w+)?\\s*\\n?([\\s\\S]*?)\\n?```/);\n if (fenceMatch) {\n slots.code = fenceMatch[1].trim();\n } else if (/Code(?:\\s+snippet)?:\\s*/i.test(rawContent)) {\n const parts = rawContent.split(/Code(?:\\s+snippet)?:\\s*/i);\n if (parts[1] && parts[1].trim()) {\n slots.code = parts[1].trim();\n }\n }\n }\n }\n\n // Extract bullet points if missing\n if (!Array.isArray(slots.points) || slots.points.length === 0) {\n if (Array.isArray(slide.points) && slide.points.length > 0) {\n slots.points = slide.points;\n } else if (rawContent) {\n const textOnly = rawContent\n .replace(/```[\\s\\S]*?```/g, '')\n .replace(/Code(?:\\s+snippet)?:\\s*[\\s\\S]*/i, '')\n .trim();\n\n const lines = textOnly\n .split(/\\n|\\.\\s+/)\n .map((l: string) => l.replace(/^(?:Concept:\\s*|[-*•]|\\d+\\.)\\s*/i, '').trim())\n .filter((l: string) => l.length > 3);\n\n slots.points = lines.length > 0 ? lines.slice(0, 4) : [textOnly || slots.title || 'Core Concept'];\n }\n }\n\n // If code is still missing, synthesize an illustrative snippet instead of rendering a broken empty box\n if (!slots.code || slots.code === '...') {\n const lang = (slots.language || 'code').toLowerCase();\n if (lang.includes('swift')) {\n slots.code = `// ${slots.title || 'Swift Implementation'}\\nimport SwiftUI\\n\\nstruct PreviewView: View {\\n var body: some View {\\n Text(\"${slots.title || 'Hello, World!'}\")\\n .padding()\\n }\\n}`;\n slots.language = slots.language || 'swift';\n } else if (lang.includes('py')) {\n slots.code = `# ${slots.title || 'Python Implementation'}\\ndef execute():\\n print(\"${slots.title || 'Ready'}\")\\n\\nexecute()`;\n slots.language = slots.language || 'python';\n } else {\n slots.code = `// ${slots.title || 'Code Example'}\\nfunction main() {\\n console.log(\"${slots.title || 'Executing'}\");\\n}\\nmain();`;\n slots.language = slots.language || 'typescript';\n }\n }\n break;\n }\n\n case 'two-columns-compare': {\n slots.col1Title = slots.col1Title || slide.col1Title || 'Approach A';\n slots.col2Title = slots.col2Title || slide.col2Title || 'Approach B';\n\n if (!Array.isArray(slots.col1Items) || slots.col1Items.length === 0) {\n if (Array.isArray(slide.col1Items) && slide.col1Items.length > 0) {\n slots.col1Items = slide.col1Items;\n } else if (rawContent) {\n const items = rawContent\n .split(/\\n|\\.\\s+/)\n .map((l: string) => l.replace(/^(?:[-*•]|\\d+\\.)\\s*/, '').trim())\n .filter((l: string) => l.length > 3);\n\n const half = Math.max(1, Math.ceil(items.length / 2));\n slots.col1Items = items.slice(0, half);\n if (!slots.col2Items || !Array.isArray(slots.col2Items)) {\n slots.col2Items = items.slice(half);\n }\n }\n }\n\n slots.col1Items = Array.isArray(slots.col1Items) && slots.col1Items.length > 0 ? slots.col1Items : ['Primary Characteristics', 'Standard Practice'];\n slots.col2Items = Array.isArray(slots.col2Items) && slots.col2Items.length > 0 ? slots.col2Items : (Array.isArray(slide.col2Items) ? slide.col2Items : ['Alternative Paradigm', 'Trade-offs & Edge Cases']);\n break;\n }\n\n case 'three-cards-grid': {\n if (!Array.isArray(slots.cards) || slots.cards.length === 0) {\n if (Array.isArray(slide.cards) && slide.cards.length > 0) {\n slots.cards = slide.cards;\n } else if (rawContent) {\n const cardTokens = rawContent\n .split(/Card\\s*\\d+:\\s*|(?:\\r?\\n){2,}|\\.\\s+/i)\n .map((c: string) => c.trim())\n .filter(Boolean);\n\n if (cardTokens.length >= 2) {\n slots.cards = cardTokens.slice(0, 3).map((txt: string, idx: number) => {\n const colonIdx = txt.indexOf(':');\n const cardTitle = colonIdx > 0 && colonIdx < 30 ? txt.slice(0, colonIdx).trim() : `Pillar ${idx + 1}`;\n const cardDesc = colonIdx > 0 && colonIdx < 30 ? txt.slice(colonIdx + 1).trim() : txt;\n return { badge: `0${idx + 1}`, title: cardTitle, desc: cardDesc };\n });\n } else {\n const lines = rawContent.split(/\\n|\\.\\s+/).map((l: string) => l.replace(/^(?:[-*•]|\\d+\\.)\\s*/, '').trim()).filter((l: string) => l.length > 3);\n slots.cards = lines.slice(0, 3).map((line: string, idx: number) => ({\n badge: `0${idx + 1}`,\n title: line.split(':')[0]?.slice(0, 30) || `Pillar 0${idx + 1}`,\n desc: line,\n }));\n }\n }\n }\n\n // Ensure cards array has at least 3 elements\n if (!Array.isArray(slots.cards) || slots.cards.length === 0) {\n slots.cards = [\n { badge: '01', title: 'Foundation', desc: rawContent || 'Core fundamentals and design principles' },\n { badge: '02', title: 'Architecture', desc: 'Component relationships and data flow patterns' },\n { badge: '03', title: 'Practice', desc: 'Real-world application and hands-on synthesis' },\n ];\n }\n break;\n }\n\n case 'timeline-steps': {\n if (!Array.isArray(slots.steps) || slots.steps.length === 0) {\n if (Array.isArray(slide.steps) && slide.steps.length > 0) {\n slots.steps = slide.steps;\n } else if (rawContent) {\n const stepMatches = rawContent\n .split(/(?:\\d+\\.|\\bStep\\s*\\d+:)\\s*/i)\n .map((s: string) => s.trim())\n .filter(Boolean);\n\n if (stepMatches.length > 0) {\n slots.steps = stepMatches.slice(0, 5).map((txt: string, idx: number) => {\n const periodIdx = txt.indexOf('.');\n const stepTitle = periodIdx > 0 && periodIdx < 35 ? txt.slice(0, periodIdx).trim() : txt.slice(0, 30);\n const stepDesc = periodIdx > 0 && periodIdx < 35 ? txt.slice(periodIdx + 1).trim() : txt;\n return { stepNum: idx + 1, title: stepTitle || `Step ${idx + 1}`, desc: stepDesc || stepTitle };\n });\n }\n }\n }\n\n if (!Array.isArray(slots.steps) || slots.steps.length === 0) {\n slots.steps = [\n { stepNum: 1, title: 'Analyze', desc: 'Deconstruct requirements and constraints' },\n { stepNum: 2, title: 'Implement', desc: rawContent || 'Build component logic and structure' },\n { stepNum: 3, title: 'Verify', desc: 'Test behavior and validate outcomes' },\n ];\n }\n break;\n }\n\n case 'metric-callout': {\n slots.desc = slots.desc || slide.desc || rawContent || '';\n slots.metricLabel = slots.metricLabel || slide.metricLabel || 'KEY METRIC';\n if (!slots.metric) {\n const numMatch = (slots.desc || slots.title).match(/(\\d+(?:\\.\\d+)?%?|O\\([^)]+\\)|\\d+x|\\b[A-Z0-9_-]{2,}\\b)/i);\n slots.metric = numMatch ? numMatch[1] : '100%';\n }\n break;\n }\n\n case 'checkpoint-quiz': {\n slots.question = slots.question || slide.question || slots.title || 'Check Your Understanding';\n if (!Array.isArray(slots.options) || slots.options.length === 0) {\n if (Array.isArray(slide.options) && slide.options.length > 0) {\n slots.options = slide.options;\n } else if (rawContent) {\n const items = rawContent\n .split(/(?:\\d+\\.|\\b[A-D]\\))\\s*/i)\n .map((l: string) => l.trim())\n .filter((l: string) => l.length > 3);\n\n if (items.length >= 2) {\n slots.options = items.slice(0, 4).map((text: string, idx: number) => ({\n label: String.fromCharCode(65 + idx),\n text,\n }));\n }\n }\n }\n\n if (!Array.isArray(slots.options) || slots.options.length === 0) {\n slots.options = [\n { label: 'A', text: 'Option A (Core Concept)' },\n { label: 'B', text: 'Option B (Alternative Approach)' },\n { label: 'C', text: 'Option C (Edge Case)' },\n ];\n }\n slots.explanation = slots.explanation || slide.explanation || '';\n break;\n }\n\n case 'summary-takeaways': {\n if (!Array.isArray(slots.takeaways) || slots.takeaways.length === 0) {\n if (Array.isArray(slide.takeaways) && slide.takeaways.length > 0) {\n slots.takeaways = slide.takeaways;\n } else if (rawContent) {\n const lines = rawContent\n .split(/\\n|\\.\\s+/)\n .map((l: string) => l.replace(/^(?:[-*•]|\\d+\\.)\\s*/, '').trim())\n .filter((l: string) => l.length > 3);\n\n slots.takeaways = lines.length > 0 ? lines.slice(0, 4) : [rawContent];\n }\n }\n\n if (!Array.isArray(slots.takeaways) || slots.takeaways.length === 0) {\n slots.takeaways = [\n slots.title || 'Key lesson principles mastered',\n 'Applied hands-on practice through guided examples',\n 'Ready for independent challenge and synthesis',\n ];\n }\n slots.nextStep = slots.nextStep || slide.nextStep || 'Next: Practical Hands-On Lab';\n break;\n }\n }\n\n // Preserve raw content for template fallbacks\n if (rawContent && !slots.rawContent) {\n slots.rawContent = rawContent;\n }\n\n return slots;\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit — Bespoke Slide HTML Sanitizer & Validator\n//\n// The frontend-slides mode lets the model author HTML/CSS per slide. That makes\n// generated markup an untrusted trust boundary: it is injected into the deck\n// shell the learner opens, and a deck must stay self-contained (no scripts, no\n// external requests). This module is the ONLY path from authored markup to the\n// compiled deck:\n//\n// sanitizeSlideHtml() — strips everything that can execute or fetch\n// validateBespokeSlideHtml() — fail-closed content contract (structure,\n// hierarchy, self-containment, size budget)\n//\n// Both are deliberately pure string transforms: no DOM, so they run identically\n// in Node, edge, and tests.\n// ═══════════════════════════════════════════════════════════════════════════════\n\n/**\n * Elements that can execute code, fetch resources, or hijack navigation.\n * Container tags are removed WITH their content — matching only up to the first\n * `>` leaves `<script>` bodies behind as visible, harmless-looking text (a real\n * leak this module used to have).\n */\nconst CONTAINER_TAGS = ['script', 'iframe', 'object', 'embed', 'applet', 'form', 'button', 'textarea', 'select', 'template'];\n/** Void/self-closing tags: the tag alone is the whole element. */\nconst VOID_TAGS = ['link', 'meta', 'base', 'input'];\n\nconst CONTAINER_ELEMENT_RE = new RegExp(\n `<(${CONTAINER_TAGS.join('|')})\\\\b[\\\\s\\\\S]*?<\\\\/\\\\1\\\\s*>`,\n 'gi'\n);\n/** Unclosed openings and stray closings left after the element pass. */\nconst STRAY_TAG_RE = new RegExp(`<\\\\/?(?:${[...CONTAINER_TAGS, ...VOID_TAGS].join('|')})\\\\b[^>]*>`, 'gi');\n\n/** `onclick`, `onload`, … — inline handlers are never legitimate in a deck. */\nconst EVENT_ATTR_RE = /\\s+on[a-z]+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi;\n\n/** Off-box references: a deck must render with no network at all. */\nconst EXTERNAL_URL_RE = /^(?:https?:)?\\/\\//i;\n/** Matches a WHOLE `url(...)` — partial matches used to leave the hostname behind. */\nconst CSS_URL_RE = /url\\(\\s*(?:\"([^\"]*)\"|'([^']*)'|([^)'\"\\s]*))\\s*\\)/gi;\nconst CSS_IMPORT_RE = /@import[^;]+;/gi;\n\nfunction isOffBoxUrl(url: string): boolean {\n const value = url.trim();\n if (!value) return false;\n return EXTERNAL_URL_RE.test(value) || /^javascript:/i.test(value) || /^data:(?!image\\/)/i.test(value);\n}\n\nexport interface SanitizedSlideHtml {\n html: string;\n /** Human-readable notes about what was stripped — surfaced to logs/repair prompts. */\n removed: string[];\n}\n\n/**\n * Strips executable and externally-fetching content from authored slide markup.\n * Idempotent: sanitizing sanitized output changes nothing.\n */\nexport function sanitizeSlideHtml(raw: string): SanitizedSlideHtml {\n const removed: string[] = [];\n let html = String(raw ?? '');\n\n // 1. Whole elements first (content included), then any stray tags.\n html = html.replace(CONTAINER_ELEMENT_RE, (_match, tag: string) => {\n removed.push(`<${String(tag).toLowerCase()}>`);\n return '';\n });\n html = html.replace(STRAY_TAG_RE, (match) => {\n const tag = (match.match(/^<\\/?\\s*([a-z]+)/i) || [])[1]?.toLowerCase();\n if (tag) removed.push(`<${tag}>`);\n return '';\n });\n\n // 2. Inline handlers.\n const beforeEvents = html.length;\n html = html.replace(EVENT_ATTR_RE, '');\n if (html.length !== beforeEvents) removed.push('inline event handler');\n\n // 3. Off-box CSS assets: replace the ENTIRE url(...) so no hostname survives.\n html = html.replace(CSS_URL_RE, (match, dq: string, sq: string, bare: string) => {\n if (!isOffBoxUrl(String(dq ?? sq ?? bare ?? ''))) return match;\n removed.push('external url()');\n return 'none';\n });\n\n const beforeImports = html.length;\n html = html.replace(CSS_IMPORT_RE, '');\n if (html.length !== beforeImports) removed.push('@import');\n\n // 4. Off-box link/asset attributes.\n html = html.replace(/\\s+(href|src|srcset|poster)\\s*=\\s*(\"([^\"]*)\"|'([^']*)')/gi, (match, attr, _q, dq, sq) => {\n const value = String(dq ?? sq ?? '').trim();\n if (!isOffBoxUrl(value)) return match;\n removed.push(`${attr}=\"${value.slice(0, 60)}\"`);\n return '';\n });\n\n return { html: html.trim(), removed: Array.from(new Set(removed)) };\n}\n\nexport interface BespokeSlideValidation {\n valid: boolean;\n issues: string[];\n}\n\nexport interface BespokeSlideContract {\n /** Hard cap on authored characters — bounds prompt/token waste per slide. */\n maxChars?: number;\n /** Minimum characters of real content, i.e. not an empty shell. */\n minChars?: number;\n}\n\n/**\n * Fail-closed contract for authored slide markup. A slide that fails here must\n * be rejected (or sent back for repair) — never compiled into the deck.\n */\nexport function validateBespokeSlideHtml(\n raw: string,\n contract: BespokeSlideContract = {}\n): BespokeSlideValidation {\n const maxChars = contract.maxChars ?? 16_000;\n const minChars = contract.minChars ?? 120;\n const issues: string[] = [];\n const html = String(raw ?? '').trim();\n\n if (!html) return { valid: false, issues: ['slide html is empty'] };\n if (html.length > maxChars) issues.push(`slide html exceeds ${maxChars} chars (${html.length})`);\n if (html.replace(/<[^>]*>/g, '').replace(/\\s+/g, '').length < minChars) {\n issues.push('slide has too little text content to teach anything');\n }\n if (!/<h[12]\\b/i.test(html)) {\n issues.push('slide has no <h1>/<h2> — typographic hierarchy is required');\n }\n\n // Self-containment: sanitize() is the guard, re-check its output so a slide\n // that ASKED for a network dependency is rejected rather than silently fixed.\n const { html: clean, removed } = sanitizeSlideHtml(html);\n if (removed.length > 0) {\n issues.push(`slide is not self-contained (stripped: ${removed.join(', ')})`);\n }\n if (/<script\\b/i.test(clean)) issues.push('slide still contains a script tag after sanitizing');\n if (/\\b(todo|tbd|lorem ipsum|<code>)\\b/i.test(clean) && !/<pre\\b/i.test(clean)) {\n issues.push('slide contains placeholder text');\n }\n\n // The compiler wraps every slide in the fixed 16:9 stage; authored markup must\n // not try to re-lay-out the deck (that is the shell's job).\n if (/<html\\b|<body\\b|<section\\b[^>]*class=\"[^\"]*slide-page/i.test(clean)) {\n issues.push('slide markup carries deck-level structure (html/body/section.slide-page)');\n }\n\n // Decorative rotated pseudo-elements (clock hands, spindles, watermarks) are\n // absolutely positioned by nature and routinely overlap centered text —\n // observed live as a stopwatch \"hand\" striking through \"2.0s\" (2026-09-20).\n // Empty-content ::before/::after with absolute positioning + a 2D transform\n // is exactly that class; decorations belong inside their own box instead.\n const pseudoRe = /::(before|after)\\s*\\{([^}]*)\\}/g;\n for (const [, , body] of clean.matchAll(pseudoRe)) {\n if (/content:\\s*['\"]{2}/.test(body) && /position:\\s*absolute/.test(body) && /rotate\\(|translate\\(/.test(body)) {\n issues.push(\n 'decorative pseudo-element with absolute positioning + rotate/translate overlaps text — draw the decoration inside a sized box instead'\n );\n break;\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\n/** Wraps sanitized authored markup into the stage contract the deck shell expects. */\nexport function buildBespokeSlideMarkup(raw: string): string {\n const { html } = sanitizeSlideHtml(raw);\n // `bespoke-slide` carries the full-stage sizing contract defined in the shell;\n // authored markup composes inside it at the fixed 1920×1080 design size.\n return `<div class=\"bespoke-slide\">${html}</div>`;\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// @learnwell/presentation-kit — HTML Slide Deck Compiler\n// Compiles structured slide slot data into a single self-contained HTML deck\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type {\n HtmlDeckData,\n HtmlCompileOptions,\n CompiledHtmlDeckResult,\n} from './types.js';\nimport { resolveHtmlTheme } from './themes.js';\nimport { HTML_SLIDE_PRESETS } from './presets.js';\nimport { generateHtmlShell } from './shell.js';\nimport { normalizeSlideSlots } from './normalizer.js';\nimport { buildBespokeSlideMarkup } from './sanitize.js';\nimport { getHighlighter } from '../plugins/shiki.js';\n\n/** Known Shiki languages (mirrors plugins/shiki SUPPORTED_LANGS). */\nconst SHIKI_LANGS = new Set([\n 'javascript', 'typescript', 'python', 'cpp', 'c', 'java', 'html', 'css',\n 'json', 'yaml', 'bash', 'shell', 'sql', 'go', 'rust', 'swift', 'kotlin',\n 'ruby', 'php', 'xml', 'markdown', 'dockerfile', 'toml', 'ini', 'graphql',\n 'prisma', 'solidity',\n]);\n\nfunction normalizeShikiLang(lang: string | undefined): string | null {\n if (!lang) return null;\n const l = String(lang).toLowerCase().trim();\n if (SHIKI_LANGS.has(l)) return l;\n // Common aliases\n if (l === 'js') return 'javascript';\n if (l === 'ts') return 'typescript';\n if (l === 'py') return 'python';\n if (l === 'sh') return 'bash';\n if (l === 'yml') return 'yaml';\n if (l === 'c++') return 'cpp';\n return null;\n}\n\nfunction decodeHtmlEntities(html: string): string {\n return html\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#039;/g, \"'\")\n .replace(/&#x27;/g, \"'\");\n}\n\nexport function compileHtmlDeck(\n deckData: HtmlDeckData,\n options?: HtmlCompileOptions\n): CompiledHtmlDeckResult {\n const theme = options?.themeOverride || resolveHtmlTheme(deckData.theme);\n const slides = deckData.slides || [];\n const slideCount = Math.max(slides.length, 1);\n\n const renderedSlides = slides.map((slide, index) => {\n // Authored-HTML slide (frontend-slides mode): the model owns the markup,\n // the compiler only sanitizes it and places it on the fixed stage.\n if (typeof slide.html === 'string' && slide.html.trim()) {\n const notesAttr = slide.notes ? ` data-notes=\"${encodeURIComponent(slide.notes)}\"` : '';\n const customClass = slide.customClass ? ` ${slide.customClass}` : '';\n return `\n <!-- Slide ${index + 1}: authored html -->\n <section class=\"slide-page${customClass}\" data-index=\"${index}\" data-layout=\"bespoke\"${notesAttr}>\n ${buildBespokeSlideMarkup(slide.html)}\n </section>\n `;\n }\n\n const preset = HTML_SLIDE_PRESETS[slide.layoutId] || HTML_SLIDE_PRESETS['hero-cover'];\n const normalizedSlots = normalizeSlideSlots(slide);\n let innerHtml = preset.template(normalizedSlots, theme);\n\n // Tag code blocks with their language so the async Shiki highlighter can\n // find them post-compilation (see compileHtmlDeckAsync). Slides without a\n // code slot are left untouched — plain escapeHtml rendering still applies.\n if (normalizedSlots.code != null && String(normalizedSlots.code).trim()) {\n const lang = normalizeShikiLang(normalizedSlots.language) || 'typescript';\n innerHtml = innerHtml.replace('<code>', `<code class=\"language-${lang}\">`);\n }\n\n const notesAttr = slide.notes ? ` data-notes=\"${encodeURIComponent(slide.notes)}\"` : '';\n const customClass = slide.customClass ? ` ${slide.customClass}` : '';\n\n return `\n <!-- Slide ${index + 1}: ${preset.name} -->\n <section class=\"slide-page${customClass}\" data-index=\"${index}\" data-layout=\"${preset.id}\"${notesAttr}>\n ${innerHtml}\n </section>\n `;\n });\n\n const slidesHtml = renderedSlides.join('\\n');\n\n const fullHtml = generateHtmlShell({\n title: deckData.title || 'Presentation',\n theme,\n slidesHtml,\n slideCount,\n customHeadTags: options?.customHeadTags,\n deckId: (deckData as any)?.id || (options as any)?.deckId,\n });\n\n return {\n html: fullHtml,\n slideCount,\n theme,\n title: deckData.title,\n };\n}\n\n/**\n * Highlights every `<pre ...><code class=\"language-xxx\">` block in a compiled\n * HTML deck with Shiki (vitesse-dark for dark themes, vitesse-light for light).\n * Falls back to the input HTML unchanged when Shiki fails to load or a language\n * is unsupported — the deck must never break because of highlighting.\n */\nexport async function highlightDeckCodeBlocks(html: string, scheme: 'light' | 'dark' = 'dark'): Promise<string> {\n if (!html.includes('<code class=\"language-')) return html;\n\n const highlighter = await getHighlighter();\n const shikiTheme = scheme === 'light' ? 'vitesse-light' : 'vitesse-dark';\n\n return html.replace(\n /<pre([^>]*)><code class=\"language-(\\w+)\">([\\s\\S]*?)<\\/code><\\/pre>/g,\n (full, preAttrs: string, lang: string, escapedCode: string) => {\n try {\n const decoded = decodeHtmlEntities(escapedCode);\n return highlighter.codeToHtml(decoded, { lang, theme: shikiTheme })\n // Carry over the original pre classes (layout/typography) so the\n // highlighted output keeps the deck's visual contract.\n .replace('<pre', `<pre${preAttrs}`);\n } catch {\n return full; // Unsupported language / highlight error — keep plain escaped code\n }\n },\n );\n}\n\n/**\n * Async compiler variant: identical to {@link compileHtmlDeck} but applies\n * Shiki syntax highlighting to all code blocks (plan Task 4.2).\n * Falls back gracefully to non-highlighted output when Shiki is unavailable.\n */\nexport async function compileHtmlDeckAsync(\n deckData: HtmlDeckData,\n options?: HtmlCompileOptions & { highlight?: boolean }\n): Promise<CompiledHtmlDeckResult> {\n const result = compileHtmlDeck(deckData, options);\n if (options?.highlight === false) return result;\n try {\n const html = await highlightDeckCodeBlocks(result.html, result.theme.scheme);\n return { ...result, html };\n } catch (err) {\n console.warn('[html-engine] Shiki highlighting unavailable, using plain escaped code:', err);\n return result;\n }\n}\n"]}