@hiai-gg/hiai-opencode 0.2.1 → 0.2.2

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 (477) hide show
  1. package/.env.example +4 -0
  2. package/AGENTS.md +34 -38
  3. package/ARCHITECTURE.md +4 -3
  4. package/LICENSE.md +14 -0
  5. package/README.md +52 -21
  6. package/config/hiai-opencode.schema.json +11 -13
  7. package/dist/agents/{bob.d.ts → bob/claude.d.ts} +6 -2
  8. package/dist/agents/bob/core.d.ts +6 -0
  9. package/dist/agents/bob/gpt.d.ts +11 -0
  10. package/dist/agents/bob/index.d.ts +3 -0
  11. package/dist/agents/coder/core.d.ts +4 -0
  12. package/dist/agents/coder/gpt.d.ts +1 -4
  13. package/dist/agents/coder/index.d.ts +1 -0
  14. package/dist/agents/manager/agent.d.ts +1 -1
  15. package/dist/agents/manager/default-prompt-sections.d.ts +3 -3
  16. package/dist/agents/manager/guard-integration.d.ts +1 -0
  17. package/dist/agents/prompt-library/index.d.ts +0 -1
  18. package/dist/agents/prompt-library/shared-execution.d.ts +9 -0
  19. package/dist/agents/strategist/behavioral-summary.d.ts +1 -1
  20. package/dist/agents/strategist/identity-constraints.d.ts +1 -1
  21. package/dist/agents/strategist/plan-generation.d.ts +1 -1
  22. package/dist/agents/types.d.ts +2 -1
  23. package/dist/config/defaults.d.ts +1 -0
  24. package/dist/config/platform-schema.d.ts +26 -26
  25. package/dist/config/schema/agent-names.d.ts +6 -6
  26. package/dist/config/schema/agent-overrides.d.ts +0 -128
  27. package/dist/config/schema/hiai-opencode-config.d.ts +0 -128
  28. package/dist/config/types.d.ts +2 -2
  29. package/dist/features/background-agent/manager-notifier.d.ts +46 -0
  30. package/dist/features/background-agent/manager-types.d.ts +40 -0
  31. package/dist/features/background-agent/manager.d.ts +3 -19
  32. package/dist/features/background-agent/polling-manager.d.ts +51 -0
  33. package/dist/features/boulder-state/storage.d.ts +1 -0
  34. package/dist/features/builtin-commands/templates/loop.d.ts +2 -0
  35. package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
  36. package/dist/features/builtin-skills/skills/interview-me.d.ts +2 -0
  37. package/dist/features/builtin-skills/skills/planning-and-task-breakdown.d.ts +2 -0
  38. package/dist/hooks/reasoning-content-cache/hook.d.ts +11 -0
  39. package/dist/hooks/reasoning-content-cache/index.d.ts +1 -0
  40. package/dist/hooks/session-recovery/checkpoint.d.ts +48 -0
  41. package/dist/hooks/session-recovery/enhanced-hook.d.ts +30 -0
  42. package/dist/hooks/session-recovery/state-backup.d.ts +76 -0
  43. package/dist/hooks/shared/compaction-in-progress.d.ts +4 -0
  44. package/dist/hooks/start-work/git-operations.d.ts +47 -0
  45. package/dist/hooks/token-budget.d.ts +30 -0
  46. package/dist/index.js +1185 -1078
  47. package/dist/mcp/rate-limiter.d.ts +68 -0
  48. package/dist/plugin/chat-message.d.ts +8 -0
  49. package/dist/plugin/command-execute-before.d.ts +1 -1
  50. package/dist/plugin/event-handlers/message-updated.d.ts +2 -0
  51. package/dist/plugin/event-handlers/session-error.d.ts +2 -0
  52. package/dist/plugin/event-handlers/session-status.d.ts +2 -0
  53. package/dist/plugin/event-handlers/types.d.ts +62 -0
  54. package/dist/plugin/event-handlers/utils.d.ts +11 -0
  55. package/dist/plugin/event.d.ts +1 -1
  56. package/dist/shared/data-path.d.ts +1 -1
  57. package/dist/shared/errors.d.ts +70 -0
  58. package/dist/shared/extract-session-id.d.ts +8 -0
  59. package/dist/shared/git-worktree/get-git-state-summary.d.ts +14 -0
  60. package/dist/shared/index.d.ts +67 -68
  61. package/dist/shared/internal-initiator-marker.d.ts +1 -1
  62. package/dist/shared/logger.d.ts +5 -1
  63. package/dist/shared/reasoning-content-cache.d.ts +68 -0
  64. package/dist/shared/safe-create-hook.d.ts +4 -4
  65. package/dist/tools/call-hiai-agent/constants.d.ts +2 -2
  66. package/dist/tools/delegate-task/sub-agent.d.ts +1 -1
  67. package/dist/tools/look-at/constants.d.ts +1 -1
  68. package/docs/architecture/bob-manager-architecture.md +244 -0
  69. package/docs/hiai-opencode/adr/ADR-001-agent-identity-section-injection.md +66 -0
  70. package/docs/hiai-opencode/adr/ADR-002-anti-loop-guard-priority.md +63 -0
  71. package/docs/hiai-opencode/adr/ADR-003-compaction-mechanism.md +71 -0
  72. package/docs/hiai-opencode/adr/ADR-004-session-recovery.md +76 -0
  73. package/docs/hiai-opencode/api.md +305 -0
  74. package/docs/hiai-opencode/hooks-architecture.md +225 -0
  75. package/docs/hiai-opencode/migration.md +209 -0
  76. package/docs/skill-discovery.md +288 -0
  77. package/package.json +1 -1
  78. package/skills/agent-browser/SKILL.md +193 -0
  79. package/skills/apple-hig/SKILL.md +43 -0
  80. package/skills/article-magazine/SKILL.md +46 -0
  81. package/skills/article-magazine/example.html +81 -0
  82. package/skills/article-magazine/example.md +38 -0
  83. package/skills/canvas-design/SKILL.md +45 -0
  84. package/skills/design-templates/audio-jingle/SKILL.md +132 -0
  85. package/skills/design-templates/audio-jingle/example.html +128 -0
  86. package/skills/design-templates/blog-post/SKILL.md +80 -0
  87. package/skills/design-templates/blog-post/example.html +80 -0
  88. package/skills/design-templates/clinical-case-report/SKILL.md +209 -0
  89. package/skills/design-templates/clinical-case-report/example.html +698 -0
  90. package/skills/design-templates/clinical-case-report/examples/example-stemi.html +698 -0
  91. package/skills/design-templates/clinical-case-report/references/case-formats.md +94 -0
  92. package/skills/design-templates/clinical-case-report/references/checklist.md +41 -0
  93. package/skills/design-templates/critique/SKILL.md +258 -0
  94. package/skills/design-templates/critique/example.html +671 -0
  95. package/skills/design-templates/dashboard/SKILL.md +76 -0
  96. package/skills/design-templates/dashboard/example.html +118 -0
  97. package/skills/design-templates/dating-web/SKILL.md +92 -0
  98. package/skills/design-templates/dating-web/example.html +265 -0
  99. package/skills/design-templates/dcf-valuation/SKILL.md +140 -0
  100. package/skills/design-templates/dcf-valuation/references/sector-wacc.md +42 -0
  101. package/skills/design-templates/digital-eguide/SKILL.md +94 -0
  102. package/skills/design-templates/digital-eguide/example.html +204 -0
  103. package/skills/design-templates/docs-page/SKILL.md +80 -0
  104. package/skills/design-templates/docs-page/example.html +122 -0
  105. package/skills/design-templates/email-marketing/SKILL.md +84 -0
  106. package/skills/design-templates/email-marketing/example.html +159 -0
  107. package/skills/design-templates/eng-runbook/SKILL.md +51 -0
  108. package/skills/design-templates/eng-runbook/example.html +250 -0
  109. package/skills/design-templates/finance-report/SKILL.md +61 -0
  110. package/skills/design-templates/finance-report/example.html +242 -0
  111. package/skills/design-templates/flowai-live-dashboard-template/SKILL.md +87 -0
  112. package/skills/design-templates/flowai-live-dashboard-template/assets/template.html +387 -0
  113. package/skills/design-templates/flowai-live-dashboard-template/example.html +13 -0
  114. package/skills/design-templates/flowai-live-dashboard-template/references/checklist.md +35 -0
  115. package/skills/design-templates/gamified-app/SKILL.md +108 -0
  116. package/skills/design-templates/gamified-app/example.html +292 -0
  117. package/skills/design-templates/github-dashboard/SKILL.md +130 -0
  118. package/skills/design-templates/github-dashboard/example.html +473 -0
  119. package/skills/design-templates/github-dashboard/references/README.md +10 -0
  120. package/skills/design-templates/github-dashboard/references/artifact-example.json +15 -0
  121. package/skills/design-templates/github-dashboard/references/example-data.json +138 -0
  122. package/skills/design-templates/github-dashboard/references/provenance-example.json +92 -0
  123. package/skills/design-templates/github-dashboard/references/template.html +473 -0
  124. package/skills/design-templates/guizang-ppt/LICENSE +21 -0
  125. package/skills/design-templates/guizang-ppt/README.en.md +119 -0
  126. package/skills/design-templates/guizang-ppt/README.md +120 -0
  127. package/skills/design-templates/guizang-ppt/README.pt-BR.md +121 -0
  128. package/skills/design-templates/guizang-ppt/SKILL.md +313 -0
  129. package/skills/design-templates/guizang-ppt/assets/example-slides.html +318 -0
  130. package/skills/design-templates/guizang-ppt/assets/template.html +647 -0
  131. package/skills/design-templates/guizang-ppt/references/checklist.md +265 -0
  132. package/skills/design-templates/guizang-ppt/references/components.md +363 -0
  133. package/skills/design-templates/guizang-ppt/references/layouts.md +630 -0
  134. package/skills/design-templates/guizang-ppt/references/styles.md +195 -0
  135. package/skills/design-templates/guizang-ppt/references/themes.md +122 -0
  136. package/skills/design-templates/hr-onboarding/SKILL.md +52 -0
  137. package/skills/design-templates/hr-onboarding/example.html +219 -0
  138. package/skills/design-templates/html-ppt/.clawscan-allow +12 -0
  139. package/skills/design-templates/html-ppt/LICENSE +21 -0
  140. package/skills/design-templates/html-ppt/README.md +234 -0
  141. package/skills/design-templates/html-ppt/README.pt-BR.md +239 -0
  142. package/skills/design-templates/html-ppt/README.zh-CN.md +238 -0
  143. package/skills/design-templates/html-ppt/SKILL.md +250 -0
  144. package/skills/design-templates/html-ppt/assets/animations/animations.css +138 -0
  145. package/skills/design-templates/html-ppt/assets/animations/fx/_util.js +63 -0
  146. package/skills/design-templates/html-ppt/assets/animations/fx/chain-react.js +41 -0
  147. package/skills/design-templates/html-ppt/assets/animations/fx/confetti-cannon.js +49 -0
  148. package/skills/design-templates/html-ppt/assets/animations/fx/constellation.js +44 -0
  149. package/skills/design-templates/html-ppt/assets/animations/fx/counter-explosion.js +58 -0
  150. package/skills/design-templates/html-ppt/assets/animations/fx/data-stream.js +45 -0
  151. package/skills/design-templates/html-ppt/assets/animations/fx/firework.js +51 -0
  152. package/skills/design-templates/html-ppt/assets/animations/fx/galaxy-swirl.js +33 -0
  153. package/skills/design-templates/html-ppt/assets/animations/fx/gradient-blob.js +39 -0
  154. package/skills/design-templates/html-ppt/assets/animations/fx/knowledge-graph.js +69 -0
  155. package/skills/design-templates/html-ppt/assets/animations/fx/letter-explode.js +50 -0
  156. package/skills/design-templates/html-ppt/assets/animations/fx/magnetic-field.js +40 -0
  157. package/skills/design-templates/html-ppt/assets/animations/fx/matrix-rain.js +33 -0
  158. package/skills/design-templates/html-ppt/assets/animations/fx/neural-net.js +75 -0
  159. package/skills/design-templates/html-ppt/assets/animations/fx/orbit-ring.js +38 -0
  160. package/skills/design-templates/html-ppt/assets/animations/fx/particle-burst.js +42 -0
  161. package/skills/design-templates/html-ppt/assets/animations/fx/shockwave.js +39 -0
  162. package/skills/design-templates/html-ppt/assets/animations/fx/sparkle-trail.js +62 -0
  163. package/skills/design-templates/html-ppt/assets/animations/fx/starfield.js +30 -0
  164. package/skills/design-templates/html-ppt/assets/animations/fx/typewriter-multi.js +51 -0
  165. package/skills/design-templates/html-ppt/assets/animations/fx/word-cascade.js +47 -0
  166. package/skills/design-templates/html-ppt/assets/animations/fx-runtime.js +99 -0
  167. package/skills/design-templates/html-ppt/assets/base.css +150 -0
  168. package/skills/design-templates/html-ppt/assets/fonts.css +9 -0
  169. package/skills/design-templates/html-ppt/assets/runtime.js +960 -0
  170. package/skills/design-templates/html-ppt/assets/themes/academic-paper.css +23 -0
  171. package/skills/design-templates/html-ppt/assets/themes/arctic-cool.css +14 -0
  172. package/skills/design-templates/html-ppt/assets/themes/aurora.css +20 -0
  173. package/skills/design-templates/html-ppt/assets/themes/bauhaus.css +16 -0
  174. package/skills/design-templates/html-ppt/assets/themes/blueprint.css +19 -0
  175. package/skills/design-templates/html-ppt/assets/themes/catppuccin-latte.css +14 -0
  176. package/skills/design-templates/html-ppt/assets/themes/catppuccin-mocha.css +14 -0
  177. package/skills/design-templates/html-ppt/assets/themes/corporate-clean.css +19 -0
  178. package/skills/design-templates/html-ppt/assets/themes/cyberpunk-neon.css +23 -0
  179. package/skills/design-templates/html-ppt/assets/themes/dracula.css +14 -0
  180. package/skills/design-templates/html-ppt/assets/themes/editorial-serif.css +18 -0
  181. package/skills/design-templates/html-ppt/assets/themes/engineering-whiteprint.css +26 -0
  182. package/skills/design-templates/html-ppt/assets/themes/glassmorphism.css +21 -0
  183. package/skills/design-templates/html-ppt/assets/themes/gruvbox-dark.css +14 -0
  184. package/skills/design-templates/html-ppt/assets/themes/japanese-minimal.css +21 -0
  185. package/skills/design-templates/html-ppt/assets/themes/magazine-bold.css +21 -0
  186. package/skills/design-templates/html-ppt/assets/themes/memphis-pop.css +20 -0
  187. package/skills/design-templates/html-ppt/assets/themes/midcentury.css +19 -0
  188. package/skills/design-templates/html-ppt/assets/themes/minimal-white.css +16 -0
  189. package/skills/design-templates/html-ppt/assets/themes/neo-brutalism.css +17 -0
  190. package/skills/design-templates/html-ppt/assets/themes/news-broadcast.css +20 -0
  191. package/skills/design-templates/html-ppt/assets/themes/nord.css +14 -0
  192. package/skills/design-templates/html-ppt/assets/themes/pitch-deck-vc.css +21 -0
  193. package/skills/design-templates/html-ppt/assets/themes/rainbow-gradient.css +16 -0
  194. package/skills/design-templates/html-ppt/assets/themes/retro-tv.css +22 -0
  195. package/skills/design-templates/html-ppt/assets/themes/rose-pine.css +14 -0
  196. package/skills/design-templates/html-ppt/assets/themes/sharp-mono.css +17 -0
  197. package/skills/design-templates/html-ppt/assets/themes/soft-pastel.css +14 -0
  198. package/skills/design-templates/html-ppt/assets/themes/solarized-light.css +14 -0
  199. package/skills/design-templates/html-ppt/assets/themes/sunset-warm.css +14 -0
  200. package/skills/design-templates/html-ppt/assets/themes/swiss-grid.css +17 -0
  201. package/skills/design-templates/html-ppt/assets/themes/terminal-green.css +18 -0
  202. package/skills/design-templates/html-ppt/assets/themes/tokyo-night.css +14 -0
  203. package/skills/design-templates/html-ppt/assets/themes/vaporwave.css +21 -0
  204. package/skills/design-templates/html-ppt/assets/themes/xiaohongshu-white.css +16 -0
  205. package/skills/design-templates/html-ppt/assets/themes/y2k-chrome.css +20 -0
  206. package/skills/design-templates/html-ppt/docs/readme/_theme-cell.html +56 -0
  207. package/skills/design-templates/html-ppt/docs/readme/animations.png +0 -0
  208. package/skills/design-templates/html-ppt/docs/readme/hero.gif +0 -0
  209. package/skills/design-templates/html-ppt/docs/readme/layouts-live.gif +0 -0
  210. package/skills/design-templates/html-ppt/docs/readme/layouts.png +0 -0
  211. package/skills/design-templates/html-ppt/docs/readme/montage-animations.html +61 -0
  212. package/skills/design-templates/html-ppt/docs/readme/montage-layouts.html +72 -0
  213. package/skills/design-templates/html-ppt/docs/readme/montage-templates.html +72 -0
  214. package/skills/design-templates/html-ppt/docs/readme/montage-themes.html +38 -0
  215. package/skills/design-templates/html-ppt/docs/readme/presenter-mode.png +0 -0
  216. package/skills/design-templates/html-ppt/docs/readme/templates.png +0 -0
  217. package/skills/design-templates/html-ppt/docs/readme/themes.png +0 -0
  218. package/skills/design-templates/html-ppt/examples/demo-deck/index.html +161 -0
  219. package/skills/design-templates/html-ppt/references/animations.md +147 -0
  220. package/skills/design-templates/html-ppt/references/authoring-guide.md +141 -0
  221. package/skills/design-templates/html-ppt/references/full-decks.md +98 -0
  222. package/skills/design-templates/html-ppt/references/layouts.md +103 -0
  223. package/skills/design-templates/html-ppt/references/presenter-mode.md +240 -0
  224. package/skills/design-templates/html-ppt/references/themes.md +107 -0
  225. package/skills/design-templates/html-ppt/scripts/new-deck.sh +46 -0
  226. package/skills/design-templates/html-ppt/scripts/render.sh +71 -0
  227. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_01.png +0 -0
  228. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_02.png +0 -0
  229. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_03.png +0 -0
  230. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_04.png +0 -0
  231. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_05.png +0 -0
  232. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_06.png +0 -0
  233. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_07.png +0 -0
  234. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_08.png +0 -0
  235. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_09.png +0 -0
  236. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_10.png +0 -0
  237. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_11.png +0 -0
  238. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_12.png +0 -0
  239. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_13.png +0 -0
  240. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_14.png +0 -0
  241. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_15.png +0 -0
  242. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_16.png +0 -0
  243. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_17.png +0 -0
  244. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_18.png +0 -0
  245. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_19.png +0 -0
  246. package/skills/design-templates/html-ppt/scripts/verify-output/animation-showcase/animation-showcase_20.png +0 -0
  247. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_01.png +0 -0
  248. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_02.png +0 -0
  249. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_03.png +0 -0
  250. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_04.png +0 -0
  251. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_05.png +0 -0
  252. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_06.png +0 -0
  253. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_07.png +0 -0
  254. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_08.png +0 -0
  255. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_09.png +0 -0
  256. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_10.png +0 -0
  257. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_11.png +0 -0
  258. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_12.png +0 -0
  259. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_13.png +0 -0
  260. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_14.png +0 -0
  261. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_15.png +0 -0
  262. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_16.png +0 -0
  263. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_17.png +0 -0
  264. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_18.png +0 -0
  265. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_19.png +0 -0
  266. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_20.png +0 -0
  267. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_21.png +0 -0
  268. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_22.png +0 -0
  269. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_23.png +0 -0
  270. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_24.png +0 -0
  271. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_25.png +0 -0
  272. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_26.png +0 -0
  273. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_27.png +0 -0
  274. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_28.png +0 -0
  275. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_29.png +0 -0
  276. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_30.png +0 -0
  277. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_31.png +0 -0
  278. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_32.png +0 -0
  279. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_33.png +0 -0
  280. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_34.png +0 -0
  281. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_35.png +0 -0
  282. package/skills/design-templates/html-ppt/scripts/verify-output/theme-showcase/theme-showcase_36.png +0 -0
  283. package/skills/design-templates/html-ppt/templates/animation-showcase.html +172 -0
  284. package/skills/design-templates/html-ppt/templates/deck.html +69 -0
  285. package/skills/design-templates/html-ppt/templates/full-decks/course-module/README.md +8 -0
  286. package/skills/design-templates/html-ppt/templates/full-decks/course-module/index.html +189 -0
  287. package/skills/design-templates/html-ppt/templates/full-decks/course-module/style.css +46 -0
  288. package/skills/design-templates/html-ppt/templates/full-decks/dir-key-nav-minimal/README.md +11 -0
  289. package/skills/design-templates/html-ppt/templates/full-decks/dir-key-nav-minimal/index.html +138 -0
  290. package/skills/design-templates/html-ppt/templates/full-decks/dir-key-nav-minimal/style.css +60 -0
  291. package/skills/design-templates/html-ppt/templates/full-decks/graphify-dark-graph/README.md +11 -0
  292. package/skills/design-templates/html-ppt/templates/full-decks/graphify-dark-graph/index.html +180 -0
  293. package/skills/design-templates/html-ppt/templates/full-decks/graphify-dark-graph/style.css +54 -0
  294. package/skills/design-templates/html-ppt/templates/full-decks/hermes-cyber-terminal/README.md +11 -0
  295. package/skills/design-templates/html-ppt/templates/full-decks/hermes-cyber-terminal/index.html +199 -0
  296. package/skills/design-templates/html-ppt/templates/full-decks/hermes-cyber-terminal/style.css +55 -0
  297. package/skills/design-templates/html-ppt/templates/full-decks/knowledge-arch-blueprint/README.md +11 -0
  298. package/skills/design-templates/html-ppt/templates/full-decks/knowledge-arch-blueprint/index.html +190 -0
  299. package/skills/design-templates/html-ppt/templates/full-decks/knowledge-arch-blueprint/style.css +49 -0
  300. package/skills/design-templates/html-ppt/templates/full-decks/obsidian-claude-gradient/README.md +11 -0
  301. package/skills/design-templates/html-ppt/templates/full-decks/obsidian-claude-gradient/index.html +144 -0
  302. package/skills/design-templates/html-ppt/templates/full-decks/obsidian-claude-gradient/style.css +59 -0
  303. package/skills/design-templates/html-ppt/templates/full-decks/pitch-deck/README.md +9 -0
  304. package/skills/design-templates/html-ppt/templates/full-decks/pitch-deck/index.html +148 -0
  305. package/skills/design-templates/html-ppt/templates/full-decks/pitch-deck/style.css +40 -0
  306. package/skills/design-templates/html-ppt/templates/full-decks/presenter-mode-reveal/README.md +102 -0
  307. package/skills/design-templates/html-ppt/templates/full-decks/presenter-mode-reveal/index.html +187 -0
  308. package/skills/design-templates/html-ppt/templates/full-decks/presenter-mode-reveal/style.css +216 -0
  309. package/skills/design-templates/html-ppt/templates/full-decks/product-launch/README.md +8 -0
  310. package/skills/design-templates/html-ppt/templates/full-decks/product-launch/index.html +121 -0
  311. package/skills/design-templates/html-ppt/templates/full-decks/product-launch/style.css +39 -0
  312. package/skills/design-templates/html-ppt/templates/full-decks/tech-sharing/README.md +8 -0
  313. package/skills/design-templates/html-ppt/templates/full-decks/tech-sharing/index.html +156 -0
  314. package/skills/design-templates/html-ppt/templates/full-decks/tech-sharing/style.css +49 -0
  315. package/skills/design-templates/html-ppt/templates/full-decks/testing-safety-alert/README.md +11 -0
  316. package/skills/design-templates/html-ppt/templates/full-decks/testing-safety-alert/index.html +183 -0
  317. package/skills/design-templates/html-ppt/templates/full-decks/testing-safety-alert/style.css +62 -0
  318. package/skills/design-templates/html-ppt/templates/full-decks/weekly-report/README.md +8 -0
  319. package/skills/design-templates/html-ppt/templates/full-decks/weekly-report/index.html +127 -0
  320. package/skills/design-templates/html-ppt/templates/full-decks/weekly-report/style.css +55 -0
  321. package/skills/design-templates/html-ppt/templates/full-decks/xhs-pastel-card/README.md +11 -0
  322. package/skills/design-templates/html-ppt/templates/full-decks/xhs-pastel-card/index.html +147 -0
  323. package/skills/design-templates/html-ppt/templates/full-decks/xhs-pastel-card/style.css +66 -0
  324. package/skills/design-templates/html-ppt/templates/full-decks/xhs-post/README.md +9 -0
  325. package/skills/design-templates/html-ppt/templates/full-decks/xhs-post/index.html +133 -0
  326. package/skills/design-templates/html-ppt/templates/full-decks/xhs-post/style.css +47 -0
  327. package/skills/design-templates/html-ppt/templates/full-decks/xhs-white-editorial/README.md +11 -0
  328. package/skills/design-templates/html-ppt/templates/full-decks/xhs-white-editorial/index.html +187 -0
  329. package/skills/design-templates/html-ppt/templates/full-decks/xhs-white-editorial/style.css +63 -0
  330. package/skills/design-templates/html-ppt/templates/full-decks-index.html +82 -0
  331. package/skills/design-templates/html-ppt/templates/layout-showcase.html +47 -0
  332. package/skills/design-templates/html-ppt/templates/single-page/arch-diagram.html +46 -0
  333. package/skills/design-templates/html-ppt/templates/single-page/big-quote.html +18 -0
  334. package/skills/design-templates/html-ppt/templates/single-page/bullets.html +19 -0
  335. package/skills/design-templates/html-ppt/templates/single-page/chart-bar.html +30 -0
  336. package/skills/design-templates/html-ppt/templates/single-page/chart-line.html +35 -0
  337. package/skills/design-templates/html-ppt/templates/single-page/chart-pie.html +36 -0
  338. package/skills/design-templates/html-ppt/templates/single-page/chart-radar.html +31 -0
  339. package/skills/design-templates/html-ppt/templates/single-page/code.html +33 -0
  340. package/skills/design-templates/html-ppt/templates/single-page/comparison.html +47 -0
  341. package/skills/design-templates/html-ppt/templates/single-page/cover.html +32 -0
  342. package/skills/design-templates/html-ppt/templates/single-page/cta.html +27 -0
  343. package/skills/design-templates/html-ppt/templates/single-page/diff.html +35 -0
  344. package/skills/design-templates/html-ppt/templates/single-page/flow-diagram.html +33 -0
  345. package/skills/design-templates/html-ppt/templates/single-page/gantt.html +29 -0
  346. package/skills/design-templates/html-ppt/templates/single-page/image-grid.html +34 -0
  347. package/skills/design-templates/html-ppt/templates/single-page/image-hero.html +33 -0
  348. package/skills/design-templates/html-ppt/templates/single-page/kpi-grid.html +19 -0
  349. package/skills/design-templates/html-ppt/templates/single-page/mindmap.html +38 -0
  350. package/skills/design-templates/html-ppt/templates/single-page/process-steps.html +27 -0
  351. package/skills/design-templates/html-ppt/templates/single-page/pros-cons.html +31 -0
  352. package/skills/design-templates/html-ppt/templates/single-page/roadmap.html +46 -0
  353. package/skills/design-templates/html-ppt/templates/single-page/section-divider.html +17 -0
  354. package/skills/design-templates/html-ppt/templates/single-page/stat-highlight.html +17 -0
  355. package/skills/design-templates/html-ppt/templates/single-page/table.html +33 -0
  356. package/skills/design-templates/html-ppt/templates/single-page/terminal.html +35 -0
  357. package/skills/design-templates/html-ppt/templates/single-page/thanks.html +21 -0
  358. package/skills/design-templates/html-ppt/templates/single-page/three-column.html +18 -0
  359. package/skills/design-templates/html-ppt/templates/single-page/timeline.html +32 -0
  360. package/skills/design-templates/html-ppt/templates/single-page/toc.html +26 -0
  361. package/skills/design-templates/html-ppt/templates/single-page/todo-checklist.html +33 -0
  362. package/skills/design-templates/html-ppt/templates/single-page/two-column.html +39 -0
  363. package/skills/design-templates/html-ppt/templates/theme-showcase.html +151 -0
  364. package/skills/design-templates/html-ppt-course-module/SKILL.md +78 -0
  365. package/skills/design-templates/html-ppt-course-module/example.html +542 -0
  366. package/skills/design-templates/html-ppt-dir-key-nav-minimal/SKILL.md +77 -0
  367. package/skills/design-templates/html-ppt-dir-key-nav-minimal/example.html +366 -0
  368. package/skills/design-templates/html-ppt-graphify-dark-graph/SKILL.md +77 -0
  369. package/skills/design-templates/html-ppt-graphify-dark-graph/example.html +402 -0
  370. package/skills/design-templates/html-ppt-hermes-cyber-terminal/SKILL.md +77 -0
  371. package/skills/design-templates/html-ppt-hermes-cyber-terminal/example.html +422 -0
  372. package/skills/design-templates/html-ppt-knowledge-arch-blueprint/SKILL.md +77 -0
  373. package/skills/design-templates/html-ppt-knowledge-arch-blueprint/example.html +407 -0
  374. package/skills/design-templates/html-ppt-obsidian-claude-gradient/SKILL.md +77 -0
  375. package/skills/design-templates/html-ppt-obsidian-claude-gradient/example.html +371 -0
  376. package/skills/design-templates/html-ppt-pitch-deck/SKILL.md +78 -0
  377. package/skills/design-templates/html-ppt-pitch-deck/example.html +495 -0
  378. package/skills/design-templates/html-ppt-presenter-mode-reveal/SKILL.md +78 -0
  379. package/skills/design-templates/html-ppt-presenter-mode-reveal/example.html +725 -0
  380. package/skills/design-templates/html-ppt-product-launch/SKILL.md +77 -0
  381. package/skills/design-templates/html-ppt-product-launch/example.html +467 -0
  382. package/skills/design-templates/html-ppt-taste-brutalist/SKILL.md +70 -0
  383. package/skills/design-templates/html-ppt-taste-brutalist/example.html +774 -0
  384. package/skills/design-templates/html-ppt-taste-editorial/SKILL.md +62 -0
  385. package/skills/design-templates/html-ppt-taste-editorial/example.html +689 -0
  386. package/skills/design-templates/html-ppt-tech-sharing/SKILL.md +77 -0
  387. package/skills/design-templates/html-ppt-tech-sharing/example.html +512 -0
  388. package/skills/design-templates/html-ppt-testing-safety-alert/SKILL.md +78 -0
  389. package/skills/design-templates/html-ppt-testing-safety-alert/example.html +413 -0
  390. package/skills/design-templates/html-ppt-weekly-report/SKILL.md +77 -0
  391. package/skills/design-templates/html-ppt-weekly-report/example.html +489 -0
  392. package/skills/design-templates/html-ppt-xhs-pastel-card/SKILL.md +78 -0
  393. package/skills/design-templates/html-ppt-xhs-pastel-card/example.html +381 -0
  394. package/skills/design-templates/html-ppt-xhs-post/SKILL.md +78 -0
  395. package/skills/design-templates/html-ppt-xhs-post/example.html +487 -0
  396. package/skills/design-templates/html-ppt-xhs-white-editorial/SKILL.md +77 -0
  397. package/skills/design-templates/html-ppt-xhs-white-editorial/example.html +418 -0
  398. package/skills/design-templates/html-ppt-zhangzara-8-bit-orbit/LICENSE +21 -0
  399. package/skills/design-templates/html-ppt-zhangzara-8-bit-orbit/SKILL.md +93 -0
  400. package/skills/design-templates/html-ppt-zhangzara-8-bit-orbit/example.html +1640 -0
  401. package/skills/design-templates/html-ppt-zhangzara-8-bit-orbit/template.json +48 -0
  402. package/skills/design-templates/html-ppt-zhangzara-biennale-yellow/LICENSE +21 -0
  403. package/skills/design-templates/html-ppt-zhangzara-biennale-yellow/SKILL.md +93 -0
  404. package/skills/design-templates/html-ppt-zhangzara-biennale-yellow/example.html +833 -0
  405. package/skills/design-templates/html-ppt-zhangzara-biennale-yellow/template.json +49 -0
  406. package/skills/design-templates/html-ppt-zhangzara-block-frame/LICENSE +21 -0
  407. package/skills/design-templates/html-ppt-zhangzara-block-frame/SKILL.md +93 -0
  408. package/skills/design-templates/html-ppt-zhangzara-block-frame/example.html +1453 -0
  409. package/skills/design-templates/html-ppt-zhangzara-block-frame/template.json +47 -0
  410. package/skills/design-templates/html-ppt-zhangzara-blue-professional/LICENSE +21 -0
  411. package/skills/design-templates/html-ppt-zhangzara-blue-professional/SKILL.md +93 -0
  412. package/skills/design-templates/html-ppt-zhangzara-blue-professional/example.html +1423 -0
  413. package/skills/design-templates/html-ppt-zhangzara-blue-professional/template.json +44 -0
  414. package/skills/design-templates/html-ppt-zhangzara-bold-poster/LICENSE +21 -0
  415. package/skills/design-templates/html-ppt-zhangzara-bold-poster/SKILL.md +93 -0
  416. package/skills/design-templates/html-ppt-zhangzara-bold-poster/example.html +876 -0
  417. package/skills/design-templates/html-ppt-zhangzara-bold-poster/template.json +45 -0
  418. package/skills/design-templates/html-ppt-zhangzara-broadside/LICENSE +21 -0
  419. package/skills/design-templates/html-ppt-zhangzara-broadside/SKILL.md +92 -0
  420. package/skills/design-templates/html-ppt-zhangzara-broadside/example.html +2144 -0
  421. package/skills/design-templates/html-ppt-zhangzara-broadside/template.json +49 -0
  422. package/skills/design-templates/html-ppt-zhangzara-capsule/LICENSE +21 -0
  423. package/skills/design-templates/html-ppt-zhangzara-capsule/SKILL.md +92 -0
  424. package/skills/design-templates/html-ppt-zhangzara-capsule/example.html +1413 -0
  425. package/skills/design-templates/html-ppt-zhangzara-capsule/template.json +51 -0
  426. package/skills/design-templates/html-ppt-zhangzara-cartesian/LICENSE +21 -0
  427. package/skills/design-templates/html-ppt-zhangzara-cartesian/SKILL.md +92 -0
  428. package/skills/design-templates/html-ppt-zhangzara-cartesian/example.html +1136 -0
  429. package/skills/design-templates/html-ppt-zhangzara-cartesian/template.json +47 -0
  430. package/skills/design-templates/html-ppt-zhangzara-cobalt-grid/LICENSE +21 -0
  431. package/skills/design-templates/html-ppt-zhangzara-cobalt-grid/SKILL.md +93 -0
  432. package/skills/design-templates/html-ppt-zhangzara-cobalt-grid/example.html +1205 -0
  433. package/skills/design-templates/html-ppt-zhangzara-cobalt-grid/template.json +49 -0
  434. package/skills/design-templates/html-ppt-zhangzara-coral/LICENSE +21 -0
  435. package/skills/design-templates/html-ppt-zhangzara-coral/SKILL.md +92 -0
  436. package/skills/design-templates/html-ppt-zhangzara-coral/example.html +1487 -0
  437. package/skills/design-templates/html-ppt-zhangzara-coral/template.json +45 -0
  438. package/skills/design-templates/html-ppt-zhangzara-creative-mode/LICENSE +21 -0
  439. package/skills/design-templates/html-ppt-zhangzara-creative-mode/SKILL.md +99 -0
  440. package/skills/design-templates/html-ppt-zhangzara-creative-mode/assets/deck-stage.js +619 -0
  441. package/skills/design-templates/html-ppt-zhangzara-creative-mode/example.html +636 -0
  442. package/skills/design-templates/html-ppt-zhangzara-creative-mode/template.json +47 -0
  443. package/skills/design-templates/html-ppt-zhangzara-daisy-days/LICENSE +21 -0
  444. package/skills/design-templates/html-ppt-zhangzara-daisy-days/SKILL.md +93 -0
  445. package/skills/design-templates/html-ppt-zhangzara-daisy-days/example.html +469 -0
  446. package/skills/design-templates/html-ppt-zhangzara-daisy-days/template.json +54 -0
  447. package/skills/design-templates/html-ppt-zhangzara-editorial-tri-tone/LICENSE +21 -0
  448. package/skills/design-templates/html-ppt-zhangzara-editorial-tri-tone/SKILL.md +98 -0
  449. package/skills/design-templates/html-ppt-zhangzara-editorial-tri-tone/assets/deck-stage.js +619 -0
  450. package/skills/design-templates/html-ppt-zhangzara-editorial-tri-tone/example.html +737 -0
  451. package/skills/design-templates/html-ppt-zhangzara-editorial-tri-tone/template.json +44 -0
  452. package/skills/design-templates/html-ppt-zhangzara-grove/LICENSE +21 -0
  453. package/skills/design-templates/html-ppt-zhangzara-grove/SKILL.md +92 -0
  454. package/skills/design-templates/html-ppt-zhangzara-grove/example.html +1676 -0
  455. package/skills/design-templates/html-ppt-zhangzara-grove/template.json +51 -0
  456. package/skills/figma-code-connect-components/SKILL.md +42 -0
  457. package/skills/figma-create-design-system-rules/SKILL.md +42 -0
  458. package/skills/figma-create-new-file/SKILL.md +41 -0
  459. package/skills/figma-generate-design/SKILL.md +42 -0
  460. package/skills/figma-generate-library/SKILL.md +42 -0
  461. package/skills/figma-implement-design/SKILL.md +42 -0
  462. package/skills/figma-use/SKILL.md +42 -0
  463. package/skills/full-page-screenshot/SKILL.md +42 -0
  464. package/skills/interview-me/SKILL.md +64 -0
  465. package/skills/planning-and-task-breakdown/SKILL.md +52 -0
  466. package/skills/sora/SKILL.md +43 -0
  467. package/skills/theme-factory/SKILL.md +43 -0
  468. package/skills/web-design-guidelines/SKILL.md +42 -0
  469. package/dist/agents/prompt-library/orchestration.d.ts +0 -4
  470. package/skills/brainstorming/SKILL.md +0 -164
  471. package/skills/brainstorming/scripts/frame-template.html +0 -214
  472. package/skills/brainstorming/scripts/helper.js +0 -88
  473. package/skills/brainstorming/scripts/server.cjs +0 -354
  474. package/skills/brainstorming/scripts/start-server.sh +0 -148
  475. package/skills/brainstorming/scripts/stop-server.sh +0 -56
  476. package/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  477. package/skills/brainstorming/visual-companion.md +0 -287
@@ -0,0 +1,960 @@
1
+ /* html-ppt :: runtime.js
2
+ * Keyboard-driven deck runtime. Zero dependencies.
3
+ *
4
+ * Features:
5
+ * ← → / space / PgUp PgDn / Home End navigation
6
+ * F fullscreen
7
+ * S presenter mode (opens a NEW WINDOW with current/next slide preview + notes + timer)
8
+ * The original window stays as audience view, synced via BroadcastChannel.
9
+ * Slide previews use CSS transform:scale() at design resolution for pixel-perfect layout.
10
+ * N quick notes overlay (bottom drawer)
11
+ * O slide overview grid
12
+ * T cycle themes (reads data-themes on <html> or <body>)
13
+ * A cycle demo animation on current slide
14
+ * URL hash #/N deep-link to slide N (1-based)
15
+ * Progress bar auto-managed
16
+ */
17
+ (function () {
18
+ 'use strict';
19
+
20
+ const ANIMS = ['fade-up','fade-down','fade-left','fade-right','rise-in','drop-in',
21
+ 'zoom-pop','blur-in','glitch-in','typewriter','neon-glow','shimmer-sweep',
22
+ 'gradient-flow','stagger-list','counter-up','path-draw','parallax-tilt',
23
+ 'card-flip-3d','cube-rotate-3d','page-turn-3d','perspective-zoom',
24
+ 'marquee-scroll','kenburns','confetti-burst','spotlight','morph-shape','ripple-reveal'];
25
+
26
+ function ready(fn){ if(document.readyState!='loading')fn(); else document.addEventListener('DOMContentLoaded',fn);}
27
+
28
+ /* ========== Parse URL for preview-only mode ==========
29
+ * When loaded as iframe.src = "index.html?preview=3", runtime enters a
30
+ * locked single-slide mode: only slide N is visible, no chrome, no keys,
31
+ * no hash updates. This is how the presenter window shows pixel-perfect
32
+ * previews — by loading the actual deck file in an iframe and telling it
33
+ * to display only a specific slide.
34
+ */
35
+ function getPreviewIdx() {
36
+ const m = /[?&]preview=(\d+)/.exec(location.search || '');
37
+ return m ? parseInt(m[1], 10) - 1 : -1;
38
+ }
39
+
40
+ ready(function () {
41
+ const deck = document.querySelector('.deck');
42
+ if (!deck) return;
43
+ const slides = Array.from(deck.querySelectorAll('.slide'));
44
+ if (!slides.length) return;
45
+
46
+ const previewOnlyIdx = getPreviewIdx();
47
+ const isPreviewMode = previewOnlyIdx >= 0 && previewOnlyIdx < slides.length;
48
+
49
+ /* ===== Preview-only mode: show one slide, hide everything else ===== */
50
+ if (isPreviewMode) {
51
+ function showSlide(i) {
52
+ slides.forEach((s, j) => {
53
+ const active = (j === i);
54
+ s.classList.toggle('is-active', active);
55
+ s.style.display = active ? '' : 'none';
56
+ if (active) {
57
+ s.style.opacity = '1';
58
+ s.style.transform = 'none';
59
+ s.style.pointerEvents = 'auto';
60
+ }
61
+ });
62
+ }
63
+ showSlide(previewOnlyIdx);
64
+ /* Hide chrome that the presenter shouldn't see in preview */
65
+ const hideSel = '.progress-bar, .notes-overlay, .overview, .notes, aside.notes, .speaker-notes';
66
+ document.querySelectorAll(hideSel).forEach(el => { el.style.display = 'none'; });
67
+ document.documentElement.setAttribute('data-preview', '1');
68
+ document.body.setAttribute('data-preview', '1');
69
+ /* Auto-detect theme base path for theme switching in preview mode */
70
+ function getPreviewThemeBase() {
71
+ const base = document.documentElement.getAttribute('data-theme-base');
72
+ if (base) return base;
73
+ const tl = document.getElementById('theme-link');
74
+ if (tl) {
75
+ const raw = tl.getAttribute('href') || '';
76
+ const ls = raw.lastIndexOf('/');
77
+ if (ls >= 0) return raw.substring(0, ls + 1);
78
+ }
79
+ return 'assets/themes/';
80
+ }
81
+ const previewThemeBase = getPreviewThemeBase();
82
+
83
+ /* Listen for postMessage from parent presenter window:
84
+ * - preview-goto: switch visible slide WITHOUT reloading
85
+ * - preview-theme: switch theme CSS link to match audience window */
86
+ window.addEventListener('message', function(e) {
87
+ if (!e.data) return;
88
+ if (e.data.type === 'preview-goto') {
89
+ const n = parseInt(e.data.idx, 10);
90
+ if (n >= 0 && n < slides.length) showSlide(n);
91
+ } else if (e.data.type === 'preview-theme' && e.data.name) {
92
+ let link = document.getElementById('theme-link');
93
+ if (!link) {
94
+ link = document.createElement('link');
95
+ link.rel = 'stylesheet';
96
+ link.id = 'theme-link';
97
+ document.head.appendChild(link);
98
+ }
99
+ link.href = previewThemeBase + e.data.name + '.css';
100
+ document.documentElement.setAttribute('data-theme', e.data.name);
101
+ }
102
+ });
103
+ /* Signal to parent that preview iframe is ready */
104
+ try { window.parent && window.parent.postMessage({ type: 'preview-ready' }, '*'); } catch(e) {}
105
+ return;
106
+ }
107
+
108
+ let idx = 0;
109
+ const total = slides.length;
110
+
111
+ /* ===== BroadcastChannel for presenter sync ===== */
112
+ const CHANNEL_NAME = 'html-ppt-presenter-' + location.pathname;
113
+ let bc;
114
+ try { bc = new BroadcastChannel(CHANNEL_NAME); } catch(e) { bc = null; }
115
+
116
+ // Are we running inside the presenter popup? (legacy flag, now unused)
117
+ const isPresenterWindow = false;
118
+
119
+ /* ===== progress bar ===== */
120
+ let bar = document.querySelector('.progress-bar');
121
+ if (!bar) {
122
+ bar = document.createElement('div');
123
+ bar.className = 'progress-bar';
124
+ bar.innerHTML = '<span></span>';
125
+ document.body.appendChild(bar);
126
+ }
127
+ const barFill = bar.querySelector('span');
128
+
129
+ /* ===== notes overlay (N key) ===== */
130
+ let notes = document.querySelector('.notes-overlay');
131
+ if (!notes) {
132
+ notes = document.createElement('div');
133
+ notes.className = 'notes-overlay';
134
+ document.body.appendChild(notes);
135
+ }
136
+
137
+ /* ===== overview grid (O key) ===== */
138
+ let overview = document.querySelector('.overview');
139
+ if (!overview) {
140
+ overview = document.createElement('div');
141
+ overview.className = 'overview';
142
+ slides.forEach((s, i) => {
143
+ const t = document.createElement('div');
144
+ t.className = 'thumb';
145
+ // Force 16:9 aspect ratio robustly
146
+ t.style.padding = '0 0 56.25% 0';
147
+ t.style.height = '0';
148
+ t.style.position = 'relative';
149
+ t.style.overflow = 'hidden';
150
+
151
+ const title = s.getAttribute('data-title') ||
152
+ (s.querySelector('h1,h2,h3')||{}).textContent || ('Slide '+(i+1));
153
+
154
+ // Create a container for the mini-slide
155
+ const mini = document.createElement('div');
156
+ mini.className = 'mini-slide';
157
+ mini.style.position = 'absolute';
158
+ mini.style.top = '0';
159
+ mini.style.left = '0';
160
+ mini.style.width = '1920px';
161
+ mini.style.height = '1080px';
162
+ mini.style.transformOrigin = 'top left';
163
+ mini.style.pointerEvents = 'none';
164
+ mini.style.background = 'var(--bg)';
165
+
166
+ // Clone the slide content
167
+ const clone = s.cloneNode(true);
168
+ clone.className = 'slide is-active'; // force active styles
169
+ clone.style.position = 'absolute';
170
+ clone.style.inset = '0';
171
+ clone.style.transform = 'none';
172
+ clone.style.opacity = '1';
173
+ clone.style.padding = '72px 96px'; // ensure padding is kept
174
+
175
+ mini.appendChild(clone);
176
+ t.appendChild(mini);
177
+
178
+ // Add the number and title overlay
179
+ const overlay = document.createElement('div');
180
+ overlay.style.position = 'absolute';
181
+ overlay.style.inset = '0';
182
+ overlay.style.background = 'linear-gradient(to bottom, rgba(0,0,0,0.2) 0%, transparent 40%, transparent 60%, rgba(0,0,0,0.8) 100%)';
183
+ overlay.style.color = '#fff';
184
+ overlay.style.zIndex = '10';
185
+ overlay.style.pointerEvents = 'none';
186
+
187
+ const n = document.createElement('div');
188
+ n.className = 'n';
189
+ n.textContent = i + 1;
190
+ n.style.position = 'absolute';
191
+ n.style.top = '12px';
192
+ n.style.left = '16px';
193
+ n.style.fontWeight = '700';
194
+ n.style.fontSize = '16px';
195
+ n.style.color = '#fff';
196
+ n.style.textShadow = '0 1px 4px rgba(0,0,0,0.8)';
197
+
198
+ const text = document.createElement('div');
199
+ text.className = 't';
200
+ text.textContent = title.trim().slice(0,80);
201
+ text.style.position = 'absolute';
202
+ text.style.bottom = '12px';
203
+ text.style.left = '16px';
204
+ text.style.right = '16px';
205
+ text.style.fontWeight = '600';
206
+ text.style.fontSize = '14px';
207
+ text.style.color = '#fff';
208
+ text.style.textShadow = '0 1px 4px rgba(0,0,0,0.8)';
209
+
210
+ overlay.appendChild(n);
211
+ overlay.appendChild(text);
212
+ t.appendChild(overlay);
213
+
214
+ t.addEventListener('click', () => { go(i); toggleOverview(false); });
215
+ overview.appendChild(t);
216
+ });
217
+ document.body.appendChild(overview);
218
+ }
219
+
220
+ /* ===== navigation ===== */
221
+ function go(n, fromRemote){
222
+ n = Math.max(0, Math.min(total-1, n));
223
+ slides.forEach((s,i) => {
224
+ s.classList.toggle('is-active', i===n);
225
+ s.classList.toggle('is-prev', i<n);
226
+ });
227
+ idx = n;
228
+ barFill.style.width = ((n+1)/total*100)+'%';
229
+ const numEl = document.querySelector('.slide-number');
230
+ if (numEl) { numEl.setAttribute('data-current', n+1); numEl.setAttribute('data-total', total); }
231
+
232
+ // notes (bottom overlay)
233
+ const note = slides[n].querySelector('.notes, aside.notes, .speaker-notes');
234
+ notes.innerHTML = note ? note.innerHTML : '';
235
+
236
+ // hash
237
+ const hashTarget = '#/'+(n+1);
238
+ if (location.hash !== hashTarget && !isPresenterWindow) {
239
+ history.replaceState(null,'', hashTarget);
240
+ }
241
+
242
+ // re-trigger entry animations
243
+ slides[n].querySelectorAll('[data-anim]').forEach(el => {
244
+ const a = el.getAttribute('data-anim');
245
+ el.classList.remove('anim-'+a);
246
+ void el.offsetWidth;
247
+ el.classList.add('anim-'+a);
248
+ });
249
+
250
+ // counter-up
251
+ slides[n].querySelectorAll('.counter').forEach(el => {
252
+ const target = parseFloat(el.getAttribute('data-to')||el.textContent);
253
+ const dur = parseInt(el.getAttribute('data-dur')||'1200',10);
254
+ const start = performance.now();
255
+ const from = 0;
256
+ function tick(now){
257
+ const t = Math.min(1,(now-start)/dur);
258
+ const v = from + (target-from)*(1-Math.pow(1-t,3));
259
+ el.textContent = (target % 1 === 0) ? Math.round(v) : v.toFixed(1);
260
+ if (t<1) requestAnimationFrame(tick);
261
+ }
262
+ requestAnimationFrame(tick);
263
+ });
264
+
265
+ // Broadcast to other window (audience ↔ presenter)
266
+ if (!fromRemote && bc) {
267
+ bc.postMessage({ type: 'go', idx: n });
268
+ }
269
+ }
270
+
271
+ /* ===== listen for remote navigation / theme changes ===== */
272
+ if (bc) {
273
+ bc.onmessage = function(e) {
274
+ if (!e.data) return;
275
+ if (e.data.type === 'go' && typeof e.data.idx === 'number') {
276
+ go(e.data.idx, true);
277
+ } else if (e.data.type === 'theme' && e.data.name) {
278
+ /* Sync theme across windows */
279
+ const i = themes.indexOf(e.data.name);
280
+ if (i >= 0) themeIdx = i;
281
+ applyTheme(e.data.name);
282
+ }
283
+ };
284
+ }
285
+
286
+ function toggleNotes(force){ notes.classList.toggle('open', force!==undefined?force:!notes.classList.contains('open')); }
287
+ function toggleOverview(force){
288
+ const isOpen = force!==undefined ? force : !overview.classList.contains('open');
289
+ overview.classList.toggle('open', isOpen);
290
+ if (isOpen) {
291
+ requestAnimationFrame(() => {
292
+ const thumbs = overview.querySelectorAll('.thumb');
293
+ if (thumbs.length) {
294
+ const scale = thumbs[0].clientWidth / 1920;
295
+ overview.querySelectorAll('.mini-slide').forEach(m => {
296
+ m.style.transform = 'scale(' + scale + ')';
297
+ });
298
+ }
299
+ });
300
+ }
301
+ }
302
+
303
+ /* ========== PRESENTER MODE — Magnetic-card popup window ========== */
304
+ /* Opens a new window with 4 draggable, resizable cards:
305
+ * CURRENT — iframe(?preview=N) pixel-perfect preview of current slide
306
+ * NEXT — iframe(?preview=N+1) pixel-perfect preview of next slide
307
+ * SCRIPT — large speaker notes (逐字稿)
308
+ * TIMER — elapsed timer + page counter + controls
309
+ * Cards remember position/size in localStorage.
310
+ * Two windows sync via BroadcastChannel.
311
+ */
312
+ let presenterWin = null;
313
+
314
+ function openPresenterWindow() {
315
+ if (presenterWin && !presenterWin.closed) {
316
+ presenterWin.focus();
317
+ return;
318
+ }
319
+
320
+ // Build absolute URL of THIS deck file (without hash/query)
321
+ const deckUrl = location.protocol + '//' + location.host + location.pathname;
322
+
323
+ // Collect slide titles + notes (HTML strings)
324
+ const slideMeta = slides.map((s, i) => {
325
+ const note = s.querySelector('.notes, aside.notes, .speaker-notes');
326
+ return {
327
+ title: s.getAttribute('data-title') ||
328
+ (s.querySelector('h1,h2,h3')||{}).textContent || ('Slide '+(i+1)),
329
+ notes: note ? note.innerHTML : ''
330
+ };
331
+ });
332
+
333
+ /* Capture current theme so presenter previews match the audience */
334
+ const currentTheme = root.getAttribute('data-theme') || (themes[themeIdx] || '');
335
+ const presenterHTML = buildPresenterHTML(deckUrl, slideMeta, total, idx, CHANNEL_NAME, currentTheme);
336
+
337
+ presenterWin = window.open('', 'html-ppt-presenter', 'width=1280,height=820,menubar=no,toolbar=no');
338
+ if (!presenterWin) {
339
+ alert('请允许弹出窗口以使用演讲者视图');
340
+ return;
341
+ }
342
+ presenterWin.document.open();
343
+ presenterWin.document.write(presenterHTML);
344
+ presenterWin.document.close();
345
+ }
346
+
347
+ function buildPresenterHTML(deckUrl, slideMeta, total, startIdx, channelName, currentTheme) {
348
+ const metaJSON = JSON.stringify(slideMeta);
349
+ const deckUrlJSON = JSON.stringify(deckUrl);
350
+ const channelJSON = JSON.stringify(channelName);
351
+ const themeJSON = JSON.stringify(currentTheme || '');
352
+ const storageKey = 'html-ppt-presenter:' + location.pathname;
353
+
354
+ // Build the document as a single template string for clarity
355
+ return `<!DOCTYPE html>
356
+ <html lang="zh-CN">
357
+ <head>
358
+ <meta charset="utf-8">
359
+ <title>Presenter View</title>
360
+ <style>
361
+ * { margin: 0; padding: 0; box-sizing: border-box; }
362
+ html, body {
363
+ width: 100%; height: 100%; overflow: hidden;
364
+ background: #1a1d24;
365
+ background-image:
366
+ radial-gradient(circle at 20% 30%, rgba(88,166,255,.04), transparent 50%),
367
+ radial-gradient(circle at 80% 70%, rgba(188,140,255,.04), transparent 50%);
368
+ color: #e6edf3;
369
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", sans-serif;
370
+ }
371
+ /* Stage: positioned area where cards live */
372
+ #stage { position: absolute; inset: 0; overflow: hidden; }
373
+
374
+ /* Magnetic card */
375
+ .pcard {
376
+ position: absolute;
377
+ background: #0d1117;
378
+ border: 1px solid rgba(255,255,255,.1);
379
+ border-radius: 12px;
380
+ box-shadow: 0 8px 32px rgba(0,0,0,.45), 0 0 0 1px rgba(255,255,255,.02);
381
+ display: flex; flex-direction: column;
382
+ overflow: hidden;
383
+ min-width: 180px; min-height: 100px;
384
+ transition: box-shadow .2s, border-color .2s;
385
+ }
386
+ .pcard.dragging { box-shadow: 0 16px 48px rgba(0,0,0,.6), 0 0 0 2px rgba(88,166,255,.5); border-color: #58a6ff; transition: none; z-index: 9999; }
387
+ .pcard.resizing { box-shadow: 0 16px 48px rgba(0,0,0,.6), 0 0 0 2px rgba(63,185,80,.5); border-color: #3fb950; transition: none; z-index: 9999; }
388
+ .pcard:hover { border-color: rgba(88,166,255,.3); }
389
+
390
+ /* Card header (drag handle) */
391
+ .pcard-head {
392
+ display: flex; align-items: center; gap: 10px;
393
+ padding: 8px 12px;
394
+ background: rgba(255,255,255,.04);
395
+ border-bottom: 1px solid rgba(255,255,255,.06);
396
+ cursor: move;
397
+ user-select: none;
398
+ flex-shrink: 0;
399
+ }
400
+ .pcard-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dot-color, #58a6ff); flex-shrink: 0; }
401
+ .pcard-title {
402
+ font-size: 11px; letter-spacing: .15em; text-transform: uppercase;
403
+ font-weight: 700; color: #8b949e; flex: 1;
404
+ }
405
+ .pcard-meta { font-size: 11px; color: #6e7681; }
406
+
407
+ /* Card body */
408
+ .pcard-body { flex: 1; position: relative; overflow: hidden; min-height: 0; }
409
+
410
+ /* Preview cards (CURRENT/NEXT) — iframe-based pixel-perfect render */
411
+ .pcard-preview .pcard-body { background: #000; }
412
+ .pcard-preview iframe {
413
+ position: absolute; top: 0; left: 0;
414
+ width: 1920px; height: 1080px;
415
+ border: none;
416
+ transform-origin: top left;
417
+ pointer-events: none;
418
+ background: transparent;
419
+ }
420
+ .pcard-preview .preview-end {
421
+ position: absolute; inset: 0;
422
+ display: flex; align-items: center; justify-content: center;
423
+ color: #484f58; font-size: 14px; letter-spacing: .12em;
424
+ }
425
+
426
+ /* Notes card */
427
+ .pcard-notes .pcard-body {
428
+ padding: 14px 18px;
429
+ overflow-y: auto;
430
+ font-size: 18px; line-height: 1.75;
431
+ color: #d0d7de;
432
+ font-family: "Noto Sans SC", -apple-system, sans-serif;
433
+ }
434
+ .pcard-notes .pcard-body p { margin: 0 0 .7em 0; }
435
+ .pcard-notes .pcard-body strong { color: #f0883e; }
436
+ .pcard-notes .pcard-body em { color: #58a6ff; font-style: normal; }
437
+ .pcard-notes .pcard-body code {
438
+ font-family: "SF Mono", monospace; font-size: .9em;
439
+ background: rgba(255,255,255,.08); padding: 1px 6px; border-radius: 4px;
440
+ }
441
+ .pcard-notes .empty { color: #484f58; font-style: italic; }
442
+
443
+ /* Timer card */
444
+ .pcard-timer .pcard-body {
445
+ display: flex; flex-direction: column; gap: 14px;
446
+ padding: 18px 20px; justify-content: center;
447
+ }
448
+ .timer-display {
449
+ font-family: "SF Mono", "JetBrains Mono", monospace;
450
+ font-size: 42px; font-weight: 700;
451
+ color: #3fb950;
452
+ letter-spacing: .04em;
453
+ line-height: 1;
454
+ }
455
+ .timer-row {
456
+ display: flex; align-items: center; gap: 12px;
457
+ font-size: 14px; color: #8b949e;
458
+ }
459
+ .timer-row .label { font-size: 10px; letter-spacing: .15em; text-transform: uppercase; color: #6e7681; }
460
+ .timer-row .val { color: #e6edf3; font-weight: 600; font-family: "SF Mono", monospace; }
461
+ .timer-controls { display: flex; gap: 8px; flex-wrap: wrap; }
462
+ .timer-btn {
463
+ background: rgba(255,255,255,.06);
464
+ border: 1px solid rgba(255,255,255,.1);
465
+ color: #e6edf3;
466
+ padding: 6px 12px;
467
+ border-radius: 6px;
468
+ font-size: 12px;
469
+ cursor: pointer;
470
+ font-family: inherit;
471
+ }
472
+ .timer-btn:hover { background: rgba(88,166,255,.15); border-color: #58a6ff; }
473
+ .timer-btn:active { transform: translateY(1px); }
474
+
475
+ /* Resize handle */
476
+ .pcard-resize {
477
+ position: absolute; right: 0; bottom: 0;
478
+ width: 18px; height: 18px;
479
+ cursor: nwse-resize;
480
+ background: linear-gradient(135deg, transparent 50%, rgba(255,255,255,.25) 50%, rgba(255,255,255,.25) 60%, transparent 60%, transparent 70%, rgba(255,255,255,.25) 70%, rgba(255,255,255,.25) 80%, transparent 80%);
481
+ z-index: 5;
482
+ }
483
+ .pcard-resize:hover { background: linear-gradient(135deg, transparent 50%, #58a6ff 50%, #58a6ff 60%, transparent 60%, transparent 70%, #58a6ff 70%, #58a6ff 80%, transparent 80%); }
484
+
485
+ /* Bottom hint bar */
486
+ .hint-bar {
487
+ position: fixed; bottom: 0; left: 0; right: 0;
488
+ background: rgba(0,0,0,.6);
489
+ backdrop-filter: blur(10px);
490
+ border-top: 1px solid rgba(255,255,255,.08);
491
+ padding: 6px 16px;
492
+ font-size: 11px; color: #8b949e;
493
+ display: flex; gap: 18px; align-items: center;
494
+ z-index: 1000;
495
+ }
496
+ .hint-bar kbd {
497
+ background: rgba(255,255,255,.08);
498
+ padding: 1px 6px; border-radius: 3px;
499
+ font-family: "SF Mono", monospace;
500
+ font-size: 10px;
501
+ border: 1px solid rgba(255,255,255,.1);
502
+ color: #e6edf3;
503
+ }
504
+ .hint-bar .reset-layout {
505
+ margin-left: auto;
506
+ background: transparent; border: 1px solid rgba(255,255,255,.15);
507
+ color: #8b949e; padding: 3px 10px; border-radius: 4px;
508
+ font-size: 11px; cursor: pointer; font-family: inherit;
509
+ }
510
+ .hint-bar .reset-layout:hover { background: rgba(248,81,73,.15); border-color: #f85149; color: #f85149; }
511
+
512
+ body.is-dragging-card * { user-select: none !important; }
513
+ body.is-dragging-card iframe { pointer-events: none !important; }
514
+ </style>
515
+ </head>
516
+ <body>
517
+
518
+ <div id="stage">
519
+ <div class="pcard pcard-preview" id="card-cur" style="--dot-color:#58a6ff">
520
+ <div class="pcard-head" data-drag>
521
+ <span class="pcard-dot"></span>
522
+ <span class="pcard-title">CURRENT</span>
523
+ <span class="pcard-meta" id="cur-meta">—</span>
524
+ </div>
525
+ <div class="pcard-body"><iframe id="iframe-cur"></iframe></div>
526
+ <div class="pcard-resize" data-resize></div>
527
+ </div>
528
+
529
+ <div class="pcard pcard-preview" id="card-nxt" style="--dot-color:#bc8cff">
530
+ <div class="pcard-head" data-drag>
531
+ <span class="pcard-dot"></span>
532
+ <span class="pcard-title">NEXT</span>
533
+ <span class="pcard-meta" id="nxt-meta">—</span>
534
+ </div>
535
+ <div class="pcard-body"><iframe id="iframe-nxt"></iframe></div>
536
+ <div class="pcard-resize" data-resize></div>
537
+ </div>
538
+
539
+ <div class="pcard pcard-notes" id="card-notes" style="--dot-color:#f0883e">
540
+ <div class="pcard-head" data-drag>
541
+ <span class="pcard-dot"></span>
542
+ <span class="pcard-title">SPEAKER SCRIPT · 逐字稿</span>
543
+ </div>
544
+ <div class="pcard-body" id="notes-body"></div>
545
+ <div class="pcard-resize" data-resize></div>
546
+ </div>
547
+
548
+ <div class="pcard pcard-timer" id="card-timer" style="--dot-color:#3fb950">
549
+ <div class="pcard-head" data-drag>
550
+ <span class="pcard-dot"></span>
551
+ <span class="pcard-title">TIMER</span>
552
+ </div>
553
+ <div class="pcard-body">
554
+ <div class="timer-display" id="timer-display">00:00</div>
555
+ <div class="timer-row">
556
+ <span class="label">Slide</span>
557
+ <span class="val" id="timer-count">1 / ${total}</span>
558
+ </div>
559
+ <div class="timer-controls">
560
+ <button class="timer-btn" id="btn-prev">← Prev</button>
561
+ <button class="timer-btn" id="btn-next">Next →</button>
562
+ <button class="timer-btn" id="btn-reset">⏱ Reset</button>
563
+ </div>
564
+ </div>
565
+ <div class="pcard-resize" data-resize></div>
566
+ </div>
567
+ </div>
568
+
569
+ <div class="hint-bar">
570
+ <span><kbd>← →</kbd> 翻页</span>
571
+ <span><kbd>R</kbd> 重置计时</span>
572
+ <span><kbd>Esc</kbd> 关闭</span>
573
+ <span style="color:#6e7681">拖动卡片头部移动 · 拖动右下角调整大小</span>
574
+ <button class="reset-layout" id="reset-layout">重置布局</button>
575
+ </div>
576
+
577
+ <script>
578
+ (function(){
579
+ var slideMeta = ${metaJSON};
580
+ var total = ${total};
581
+ var idx = ${startIdx};
582
+ var deckUrl = ${deckUrlJSON};
583
+ var STORAGE_KEY = ${JSON.stringify(storageKey)};
584
+ var bc;
585
+ try { bc = new BroadcastChannel(${channelJSON}); } catch(e) {}
586
+
587
+ var iframeCur = document.getElementById('iframe-cur');
588
+ var iframeNxt = document.getElementById('iframe-nxt');
589
+ var notesBody = document.getElementById('notes-body');
590
+ var curMeta = document.getElementById('cur-meta');
591
+ var nxtMeta = document.getElementById('nxt-meta');
592
+ var timerDisplay = document.getElementById('timer-display');
593
+ var timerCount = document.getElementById('timer-count');
594
+
595
+ /* ===== Default card layout ===== */
596
+ function defaultLayout() {
597
+ var w = window.innerWidth;
598
+ var h = window.innerHeight - 36; /* leave room for hint bar */
599
+ return {
600
+ 'card-cur': { x: 16, y: 16, w: Math.round(w*0.55) - 24, h: Math.round(h*0.62) - 16 },
601
+ 'card-nxt': { x: Math.round(w*0.55) + 8, y: 16, w: w - Math.round(w*0.55) - 24, h: Math.round(h*0.42) - 16 },
602
+ 'card-notes': { x: Math.round(w*0.55) + 8, y: Math.round(h*0.42) + 8, w: w - Math.round(w*0.55) - 24, h: h - Math.round(h*0.42) - 16 },
603
+ 'card-timer': { x: 16, y: Math.round(h*0.62) + 8, w: Math.round(w*0.55) - 24, h: h - Math.round(h*0.62) - 16 }
604
+ };
605
+ }
606
+
607
+ /* ===== Apply / save / restore layout ===== */
608
+ function applyLayout(layout) {
609
+ Object.keys(layout).forEach(function(id){
610
+ var el = document.getElementById(id);
611
+ var l = layout[id];
612
+ if (el && l) {
613
+ el.style.left = l.x + 'px';
614
+ el.style.top = l.y + 'px';
615
+ el.style.width = l.w + 'px';
616
+ el.style.height = l.h + 'px';
617
+ }
618
+ });
619
+ rescaleAll();
620
+ }
621
+ function readLayout() {
622
+ try {
623
+ var saved = localStorage.getItem(STORAGE_KEY);
624
+ if (saved) return JSON.parse(saved);
625
+ } catch(e) {}
626
+ return defaultLayout();
627
+ }
628
+ function saveLayout() {
629
+ var layout = {};
630
+ ['card-cur','card-nxt','card-notes','card-timer'].forEach(function(id){
631
+ var el = document.getElementById(id);
632
+ if (el) {
633
+ layout[id] = {
634
+ x: parseInt(el.style.left,10) || 0,
635
+ y: parseInt(el.style.top,10) || 0,
636
+ w: parseInt(el.style.width,10) || 300,
637
+ h: parseInt(el.style.height,10) || 200
638
+ };
639
+ }
640
+ });
641
+ try { localStorage.setItem(STORAGE_KEY, JSON.stringify(layout)); } catch(e) {}
642
+ }
643
+
644
+ /* ===== iframe rescale to fit card body ===== */
645
+ function rescaleIframe(iframe) {
646
+ if (!iframe || iframe.style.display === 'none') return;
647
+ var body = iframe.parentElement;
648
+ var cw = body.clientWidth, ch = body.clientHeight;
649
+ if (!cw || !ch) return;
650
+ var s = Math.min(cw / 1920, ch / 1080);
651
+ iframe.style.transform = 'scale(' + s + ')';
652
+ /* Center the scaled iframe in the body */
653
+ var sw = 1920 * s, sh = 1080 * s;
654
+ iframe.style.left = Math.max(0, (cw - sw) / 2) + 'px';
655
+ iframe.style.top = Math.max(0, (ch - sh) / 2) + 'px';
656
+ }
657
+ function rescaleAll() {
658
+ rescaleIframe(iframeCur);
659
+ rescaleIframe(iframeNxt);
660
+ }
661
+ window.addEventListener('resize', rescaleAll);
662
+
663
+ /* ===== Drag (move card by header) ===== */
664
+ document.querySelectorAll('[data-drag]').forEach(function(handle){
665
+ handle.addEventListener('mousedown', function(e){
666
+ if (e.button !== 0) return;
667
+ var card = handle.closest('.pcard');
668
+ if (!card) return;
669
+ e.preventDefault();
670
+ card.classList.add('dragging');
671
+ document.body.classList.add('is-dragging-card');
672
+ var startX = e.clientX, startY = e.clientY;
673
+ var startL = parseInt(card.style.left,10) || 0;
674
+ var startT = parseInt(card.style.top,10) || 0;
675
+ function onMove(ev){
676
+ var nx = Math.max(0, Math.min(window.innerWidth - 100, startL + ev.clientX - startX));
677
+ var ny = Math.max(0, Math.min(window.innerHeight - 50, startT + ev.clientY - startY));
678
+ card.style.left = nx + 'px';
679
+ card.style.top = ny + 'px';
680
+ }
681
+ function onUp(){
682
+ card.classList.remove('dragging');
683
+ document.body.classList.remove('is-dragging-card');
684
+ document.removeEventListener('mousemove', onMove);
685
+ document.removeEventListener('mouseup', onUp);
686
+ saveLayout();
687
+ }
688
+ document.addEventListener('mousemove', onMove);
689
+ document.addEventListener('mouseup', onUp);
690
+ });
691
+ });
692
+
693
+ /* ===== Resize (drag bottom-right corner) ===== */
694
+ document.querySelectorAll('[data-resize]').forEach(function(handle){
695
+ handle.addEventListener('mousedown', function(e){
696
+ if (e.button !== 0) return;
697
+ var card = handle.closest('.pcard');
698
+ if (!card) return;
699
+ e.preventDefault(); e.stopPropagation();
700
+ card.classList.add('resizing');
701
+ document.body.classList.add('is-dragging-card');
702
+ var startX = e.clientX, startY = e.clientY;
703
+ var startW = parseInt(card.style.width,10) || card.offsetWidth;
704
+ var startH = parseInt(card.style.height,10) || card.offsetHeight;
705
+ function onMove(ev){
706
+ var nw = Math.max(180, startW + ev.clientX - startX);
707
+ var nh = Math.max(100, startH + ev.clientY - startY);
708
+ card.style.width = nw + 'px';
709
+ card.style.height = nh + 'px';
710
+ if (card.querySelector('iframe')) rescaleIframe(card.querySelector('iframe'));
711
+ }
712
+ function onUp(){
713
+ card.classList.remove('resizing');
714
+ document.body.classList.remove('is-dragging-card');
715
+ document.removeEventListener('mousemove', onMove);
716
+ document.removeEventListener('mouseup', onUp);
717
+ rescaleAll();
718
+ saveLayout();
719
+ }
720
+ document.addEventListener('mousemove', onMove);
721
+ document.addEventListener('mouseup', onUp);
722
+ });
723
+ });
724
+
725
+ /* ===== Preview iframe ready tracking =====
726
+ * Each iframe loads the deck ONCE with ?preview=1 on init. Subsequent
727
+ * slide changes are sent via postMessage('preview-goto') so the iframe
728
+ * just toggles visibility of a different .slide — no reload, no flicker.
729
+ */
730
+ var iframeReady = { cur: false, nxt: false };
731
+ var currentTheme = ${themeJSON};
732
+ window.addEventListener('message', function(e) {
733
+ if (!e.data || e.data.type !== 'preview-ready') return;
734
+ var iframe = null;
735
+ if (e.source === iframeCur.contentWindow) {
736
+ iframeReady.cur = true;
737
+ iframe = iframeCur;
738
+ postPreviewGoto(iframeCur, idx);
739
+ } else if (e.source === iframeNxt.contentWindow) {
740
+ iframeReady.nxt = true;
741
+ iframe = iframeNxt;
742
+ postPreviewGoto(iframeNxt, idx + 1 < total ? idx + 1 : idx);
743
+ }
744
+ /* Sync current theme to the iframe */
745
+ if (iframe && currentTheme) {
746
+ try { iframe.contentWindow.postMessage({ type: 'preview-theme', name: currentTheme }, '*'); } catch(err) {}
747
+ }
748
+ if (iframe) rescaleIframe(iframe);
749
+ });
750
+
751
+ function postPreviewGoto(iframe, n) {
752
+ try {
753
+ iframe.contentWindow.postMessage({ type: 'preview-goto', idx: n }, '*');
754
+ } catch(e) {}
755
+ }
756
+
757
+ /* ===== Update content =====
758
+ * Smooth (no-reload) navigation: send postMessage to iframes instead of
759
+ * resetting src. Iframes stay loaded, just switch visible .slide.
760
+ */
761
+ function update(n) {
762
+ n = Math.max(0, Math.min(total - 1, n));
763
+ idx = n;
764
+
765
+ /* Current preview — postMessage (smooth) */
766
+ if (iframeReady.cur) postPreviewGoto(iframeCur, n);
767
+ curMeta.textContent = (n + 1) + '/' + total;
768
+
769
+ /* Next preview */
770
+ if (n + 1 < total) {
771
+ iframeNxt.style.display = '';
772
+ var endEl = document.querySelector('#card-nxt .preview-end');
773
+ if (endEl) endEl.remove();
774
+ if (iframeReady.nxt) postPreviewGoto(iframeNxt, n + 1);
775
+ nxtMeta.textContent = (n + 2) + '/' + total;
776
+ } else {
777
+ iframeNxt.style.display = 'none';
778
+ var body = document.querySelector('#card-nxt .pcard-body');
779
+ if (body && !body.querySelector('.preview-end')) {
780
+ var end = document.createElement('div');
781
+ end.className = 'preview-end';
782
+ end.textContent = '— END OF DECK —';
783
+ body.appendChild(end);
784
+ }
785
+ nxtMeta.textContent = 'END';
786
+ }
787
+
788
+ /* Notes */
789
+ var note = slideMeta[n].notes;
790
+ notesBody.innerHTML = note || '<span class="empty">(这一页还没有逐字稿)</span>';
791
+
792
+ /* Timer count */
793
+ timerCount.textContent = (n + 1) + ' / ' + total;
794
+ }
795
+
796
+ /* ===== Timer ===== */
797
+ var tStart = Date.now();
798
+ setInterval(function(){
799
+ var s = Math.floor((Date.now() - tStart) / 1000);
800
+ var mm = String(Math.floor(s/60)).padStart(2,'0');
801
+ var ss = String(s%60).padStart(2,'0');
802
+ timerDisplay.textContent = mm + ':' + ss;
803
+ }, 1000);
804
+ function resetTimer(){ tStart = Date.now(); timerDisplay.textContent = '00:00'; }
805
+
806
+ /* ===== BroadcastChannel sync ===== */
807
+ if (bc) {
808
+ bc.onmessage = function(e){
809
+ if (!e.data) return;
810
+ if (e.data.type === 'go') update(e.data.idx);
811
+ else if (e.data.type === 'theme' && e.data.name) {
812
+ currentTheme = e.data.name;
813
+ /* Forward theme change to preview iframes */
814
+ [iframeCur, iframeNxt].forEach(function(iframe){
815
+ try {
816
+ iframe.contentWindow.postMessage({ type: 'preview-theme', name: e.data.name }, '*');
817
+ } catch(err) {}
818
+ });
819
+ }
820
+ };
821
+ }
822
+ function go(n) {
823
+ update(n);
824
+ if (bc) bc.postMessage({ type: 'go', idx: idx });
825
+ }
826
+
827
+ /* ===== Buttons ===== */
828
+ document.getElementById('btn-prev').addEventListener('click', function(){ go(idx - 1); });
829
+ document.getElementById('btn-next').addEventListener('click', function(){ go(idx + 1); });
830
+ document.getElementById('btn-reset').addEventListener('click', resetTimer);
831
+ document.getElementById('reset-layout').addEventListener('click', function(){
832
+ if (confirm('恢复默认卡片布局?')) {
833
+ try { localStorage.removeItem(STORAGE_KEY); } catch(e){}
834
+ applyLayout(defaultLayout());
835
+ }
836
+ });
837
+
838
+ /* ===== Keyboard ===== */
839
+ document.addEventListener('keydown', function(e){
840
+ if (e.metaKey || e.ctrlKey || e.altKey) return;
841
+ switch(e.key) {
842
+ case 'ArrowRight': case ' ': case 'PageDown': go(idx + 1); e.preventDefault(); break;
843
+ case 'ArrowLeft': case 'PageUp': go(idx - 1); e.preventDefault(); break;
844
+ case 'Home': go(0); break;
845
+ case 'End': go(total - 1); break;
846
+ case 'r': case 'R': resetTimer(); break;
847
+ case 'Escape': window.close(); break;
848
+ }
849
+ });
850
+
851
+ /* ===== Iframe load → rescale (catches initial size) ===== */
852
+ iframeCur.addEventListener('load', function(){ rescaleIframe(iframeCur); });
853
+ iframeNxt.addEventListener('load', function(){ rescaleIframe(iframeNxt); });
854
+
855
+ /* ===== Init =====
856
+ * Load each iframe ONCE with the deck file. After they post
857
+ * 'preview-ready', all subsequent navigation is via postMessage
858
+ * (smooth, no reload, no flicker).
859
+ */
860
+ applyLayout(readLayout());
861
+ iframeCur.src = deckUrl + '?preview=' + (idx + 1);
862
+ if (idx + 1 < total) iframeNxt.src = deckUrl + '?preview=' + (idx + 2);
863
+ /* Initialize notes/timer/count without touching iframes */
864
+ notesBody.innerHTML = slideMeta[idx].notes || '<span class="empty">(这一页还没有逐字稿)</span>';
865
+ curMeta.textContent = (idx + 1) + '/' + total;
866
+ nxtMeta.textContent = (idx + 2) + '/' + total;
867
+ timerCount.textContent = (idx + 1) + ' / ' + total;
868
+ })();
869
+ </` + `script>
870
+ </body></html>`;
871
+ }
872
+
873
+ function fullscreen(){ const el=document.documentElement;
874
+ if (!document.fullscreenElement) el.requestFullscreen&&el.requestFullscreen();
875
+ else document.exitFullscreen&&document.exitFullscreen();
876
+ }
877
+
878
+ // theme cycling
879
+ const root = document.documentElement;
880
+ const themesAttr = root.getAttribute('data-themes') || document.body.getAttribute('data-themes');
881
+ const themes = themesAttr ? themesAttr.split(',').map(s=>s.trim()).filter(Boolean) : [];
882
+ let themeIdx = 0;
883
+
884
+ // Auto-detect theme base path from existing <link id="theme-link">
885
+ let themeBase = root.getAttribute('data-theme-base');
886
+ if (!themeBase) {
887
+ const existingLink = document.getElementById('theme-link');
888
+ if (existingLink) {
889
+ // el.getAttribute('href') gives the raw relative path written in HTML
890
+ const rawHref = existingLink.getAttribute('href') || '';
891
+ const lastSlash = rawHref.lastIndexOf('/');
892
+ themeBase = lastSlash >= 0 ? rawHref.substring(0, lastSlash + 1) : 'assets/themes/';
893
+ } else {
894
+ themeBase = 'assets/themes/';
895
+ }
896
+ }
897
+
898
+ function applyTheme(name) {
899
+ let link = document.getElementById('theme-link');
900
+ if (!link) {
901
+ link = document.createElement('link');
902
+ link.rel = 'stylesheet';
903
+ link.id = 'theme-link';
904
+ document.head.appendChild(link);
905
+ }
906
+ link.href = themeBase + name + '.css';
907
+ root.setAttribute('data-theme', name);
908
+ const ind = document.querySelector('.theme-indicator');
909
+ if (ind) ind.textContent = name;
910
+ }
911
+ function cycleTheme(fromRemote){
912
+ if (!themes.length) return;
913
+ themeIdx = (themeIdx+1) % themes.length;
914
+ const name = themes[themeIdx];
915
+ applyTheme(name);
916
+ /* Broadcast to other window (audience ↔ presenter) */
917
+ if (!fromRemote && bc) bc.postMessage({ type: 'theme', name: name });
918
+ }
919
+
920
+ // animation cycling on current slide
921
+ let animIdx = 0;
922
+ function cycleAnim(){
923
+ animIdx = (animIdx+1) % ANIMS.length;
924
+ const a = ANIMS[animIdx];
925
+ const target = slides[idx].querySelector('[data-anim-target]') || slides[idx];
926
+ ANIMS.forEach(x => target.classList.remove('anim-'+x));
927
+ void target.offsetWidth;
928
+ target.classList.add('anim-'+a);
929
+ target.setAttribute('data-anim', a);
930
+ const ind = document.querySelector('.anim-indicator');
931
+ if (ind) ind.textContent = a;
932
+ }
933
+
934
+ document.addEventListener('keydown', function (e) {
935
+ if (e.metaKey||e.ctrlKey||e.altKey) return;
936
+ switch (e.key) {
937
+ case 'ArrowRight': case ' ': case 'PageDown': case 'Enter': go(idx+1); e.preventDefault(); break;
938
+ case 'ArrowLeft': case 'PageUp': case 'Backspace': go(idx-1); e.preventDefault(); break;
939
+ case 'Home': go(0); break;
940
+ case 'End': go(total-1); break;
941
+ case 'f': case 'F': fullscreen(); break;
942
+ case 's': case 'S': openPresenterWindow(); break;
943
+ case 'n': case 'N': toggleNotes(); break;
944
+ case 'o': case 'O': toggleOverview(); break;
945
+ case 't': case 'T': cycleTheme(); break;
946
+ case 'a': case 'A': cycleAnim(); break;
947
+ case 'Escape': toggleOverview(false); toggleNotes(false); break;
948
+ }
949
+ });
950
+
951
+ // hash deep-link
952
+ function fromHash(){
953
+ const m = /^#\/(\d+)/.exec(location.hash||'');
954
+ if (m) go(Math.max(0, parseInt(m[1],10)-1));
955
+ }
956
+ window.addEventListener('hashchange', fromHash);
957
+ fromHash();
958
+ go(idx);
959
+ });
960
+ })();