forge-server 0.1.0

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 (412) hide show
  1. package/.claude/hooks/worktree-create.sh +64 -0
  2. package/.claude/hooks/worktree-remove.sh +57 -0
  3. package/.claude/settings.local.json +29 -0
  4. package/.forge/knowledge/conventions.yaml +1 -0
  5. package/.forge/knowledge/decisions.yaml +1 -0
  6. package/.forge/knowledge/gotchas.yaml +1 -0
  7. package/.forge/knowledge/patterns.yaml +1 -0
  8. package/.forge/manifest.yaml +6 -0
  9. package/CLAUDE.md +144 -0
  10. package/bin/setup-forge.sh +132 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +553 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/context/codebase.d.ts +57 -0
  16. package/dist/context/codebase.d.ts.map +1 -0
  17. package/dist/context/codebase.js +301 -0
  18. package/dist/context/codebase.js.map +1 -0
  19. package/dist/context/injector.d.ts +147 -0
  20. package/dist/context/injector.d.ts.map +1 -0
  21. package/dist/context/injector.js +533 -0
  22. package/dist/context/injector.js.map +1 -0
  23. package/dist/context/memory.d.ts +32 -0
  24. package/dist/context/memory.d.ts.map +1 -0
  25. package/dist/context/memory.js +140 -0
  26. package/dist/context/memory.js.map +1 -0
  27. package/dist/context/session-index.d.ts +54 -0
  28. package/dist/context/session-index.d.ts.map +1 -0
  29. package/dist/context/session-index.js +265 -0
  30. package/dist/context/session-index.js.map +1 -0
  31. package/dist/context/session.d.ts +42 -0
  32. package/dist/context/session.d.ts.map +1 -0
  33. package/dist/context/session.js +121 -0
  34. package/dist/context/session.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +37 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/ingestion/chunker.d.ts +19 -0
  40. package/dist/ingestion/chunker.d.ts.map +1 -0
  41. package/dist/ingestion/chunker.js +189 -0
  42. package/dist/ingestion/chunker.js.map +1 -0
  43. package/dist/ingestion/embedder.d.ts +45 -0
  44. package/dist/ingestion/embedder.d.ts.map +1 -0
  45. package/dist/ingestion/embedder.js +152 -0
  46. package/dist/ingestion/embedder.js.map +1 -0
  47. package/dist/ingestion/git-analyzer.d.ts +77 -0
  48. package/dist/ingestion/git-analyzer.d.ts.map +1 -0
  49. package/dist/ingestion/git-analyzer.js +437 -0
  50. package/dist/ingestion/git-analyzer.js.map +1 -0
  51. package/dist/ingestion/indexer.d.ts +79 -0
  52. package/dist/ingestion/indexer.d.ts.map +1 -0
  53. package/dist/ingestion/indexer.js +766 -0
  54. package/dist/ingestion/indexer.js.map +1 -0
  55. package/dist/ingestion/markdown-chunker.d.ts +19 -0
  56. package/dist/ingestion/markdown-chunker.d.ts.map +1 -0
  57. package/dist/ingestion/markdown-chunker.js +243 -0
  58. package/dist/ingestion/markdown-chunker.js.map +1 -0
  59. package/dist/ingestion/markdown-knowledge.d.ts +21 -0
  60. package/dist/ingestion/markdown-knowledge.d.ts.map +1 -0
  61. package/dist/ingestion/markdown-knowledge.js +129 -0
  62. package/dist/ingestion/markdown-knowledge.js.map +1 -0
  63. package/dist/ingestion/parser.d.ts +20 -0
  64. package/dist/ingestion/parser.d.ts.map +1 -0
  65. package/dist/ingestion/parser.js +429 -0
  66. package/dist/ingestion/parser.js.map +1 -0
  67. package/dist/ingestion/watcher.d.ts +28 -0
  68. package/dist/ingestion/watcher.d.ts.map +1 -0
  69. package/dist/ingestion/watcher.js +147 -0
  70. package/dist/ingestion/watcher.js.map +1 -0
  71. package/dist/knowledge/hydrator.d.ts +37 -0
  72. package/dist/knowledge/hydrator.d.ts.map +1 -0
  73. package/dist/knowledge/hydrator.js +220 -0
  74. package/dist/knowledge/hydrator.js.map +1 -0
  75. package/dist/knowledge/registry.d.ts +129 -0
  76. package/dist/knowledge/registry.d.ts.map +1 -0
  77. package/dist/knowledge/registry.js +361 -0
  78. package/dist/knowledge/registry.js.map +1 -0
  79. package/dist/knowledge/search.d.ts +114 -0
  80. package/dist/knowledge/search.d.ts.map +1 -0
  81. package/dist/knowledge/search.js +428 -0
  82. package/dist/knowledge/search.js.map +1 -0
  83. package/dist/knowledge/store.d.ts +76 -0
  84. package/dist/knowledge/store.d.ts.map +1 -0
  85. package/dist/knowledge/store.js +230 -0
  86. package/dist/knowledge/store.js.map +1 -0
  87. package/dist/learning/confidence.d.ts +30 -0
  88. package/dist/learning/confidence.d.ts.map +1 -0
  89. package/dist/learning/confidence.js +165 -0
  90. package/dist/learning/confidence.js.map +1 -0
  91. package/dist/learning/patterns.d.ts +52 -0
  92. package/dist/learning/patterns.d.ts.map +1 -0
  93. package/dist/learning/patterns.js +290 -0
  94. package/dist/learning/patterns.js.map +1 -0
  95. package/dist/learning/trajectory.d.ts +55 -0
  96. package/dist/learning/trajectory.d.ts.map +1 -0
  97. package/dist/learning/trajectory.js +200 -0
  98. package/dist/learning/trajectory.js.map +1 -0
  99. package/dist/memory/memory-compat.d.ts +100 -0
  100. package/dist/memory/memory-compat.d.ts.map +1 -0
  101. package/dist/memory/memory-compat.js +146 -0
  102. package/dist/memory/memory-compat.js.map +1 -0
  103. package/dist/memory/observation-store.d.ts +57 -0
  104. package/dist/memory/observation-store.d.ts.map +1 -0
  105. package/dist/memory/observation-store.js +154 -0
  106. package/dist/memory/observation-store.js.map +1 -0
  107. package/dist/memory/session-tracker.d.ts +81 -0
  108. package/dist/memory/session-tracker.d.ts.map +1 -0
  109. package/dist/memory/session-tracker.js +262 -0
  110. package/dist/memory/session-tracker.js.map +1 -0
  111. package/dist/pipeline/engine.d.ts +179 -0
  112. package/dist/pipeline/engine.d.ts.map +1 -0
  113. package/dist/pipeline/engine.js +691 -0
  114. package/dist/pipeline/engine.js.map +1 -0
  115. package/dist/pipeline/events.d.ts +54 -0
  116. package/dist/pipeline/events.d.ts.map +1 -0
  117. package/dist/pipeline/events.js +157 -0
  118. package/dist/pipeline/events.js.map +1 -0
  119. package/dist/pipeline/parallel.d.ts +83 -0
  120. package/dist/pipeline/parallel.d.ts.map +1 -0
  121. package/dist/pipeline/parallel.js +277 -0
  122. package/dist/pipeline/parallel.js.map +1 -0
  123. package/dist/pipeline/state-machine.d.ts +65 -0
  124. package/dist/pipeline/state-machine.d.ts.map +1 -0
  125. package/dist/pipeline/state-machine.js +176 -0
  126. package/dist/pipeline/state-machine.js.map +1 -0
  127. package/dist/query/graph-queries.d.ts +84 -0
  128. package/dist/query/graph-queries.d.ts.map +1 -0
  129. package/dist/query/graph-queries.js +216 -0
  130. package/dist/query/graph-queries.js.map +1 -0
  131. package/dist/query/hybrid-search.d.ts +34 -0
  132. package/dist/query/hybrid-search.d.ts.map +1 -0
  133. package/dist/query/hybrid-search.js +263 -0
  134. package/dist/query/hybrid-search.js.map +1 -0
  135. package/dist/query/intent-detector.d.ts +35 -0
  136. package/dist/query/intent-detector.d.ts.map +1 -0
  137. package/dist/query/intent-detector.js +115 -0
  138. package/dist/query/intent-detector.js.map +1 -0
  139. package/dist/query/ranking.d.ts +57 -0
  140. package/dist/query/ranking.d.ts.map +1 -0
  141. package/dist/query/ranking.js +109 -0
  142. package/dist/query/ranking.js.map +1 -0
  143. package/dist/server.d.ts +3 -0
  144. package/dist/server.d.ts.map +1 -0
  145. package/dist/server.js +291 -0
  146. package/dist/server.js.map +1 -0
  147. package/dist/storage/falkordb-store.d.ts +73 -0
  148. package/dist/storage/falkordb-store.d.ts.map +1 -0
  149. package/dist/storage/falkordb-store.js +346 -0
  150. package/dist/storage/falkordb-store.js.map +1 -0
  151. package/dist/storage/file-cache.d.ts +32 -0
  152. package/dist/storage/file-cache.d.ts.map +1 -0
  153. package/dist/storage/file-cache.js +115 -0
  154. package/dist/storage/file-cache.js.map +1 -0
  155. package/dist/storage/interfaces.d.ts +151 -0
  156. package/dist/storage/interfaces.d.ts.map +1 -0
  157. package/dist/storage/interfaces.js +7 -0
  158. package/dist/storage/interfaces.js.map +1 -0
  159. package/dist/storage/qdrant-store.d.ts +110 -0
  160. package/dist/storage/qdrant-store.d.ts.map +1 -0
  161. package/dist/storage/qdrant-store.js +467 -0
  162. package/dist/storage/qdrant-store.js.map +1 -0
  163. package/dist/storage/schema.d.ts +4 -0
  164. package/dist/storage/schema.d.ts.map +1 -0
  165. package/dist/storage/schema.js +136 -0
  166. package/dist/storage/schema.js.map +1 -0
  167. package/dist/storage/sqlite.d.ts +35 -0
  168. package/dist/storage/sqlite.d.ts.map +1 -0
  169. package/dist/storage/sqlite.js +132 -0
  170. package/dist/storage/sqlite.js.map +1 -0
  171. package/dist/tools/collaboration-tools.d.ts +111 -0
  172. package/dist/tools/collaboration-tools.d.ts.map +1 -0
  173. package/dist/tools/collaboration-tools.js +174 -0
  174. package/dist/tools/collaboration-tools.js.map +1 -0
  175. package/dist/tools/context-tools.d.ts +293 -0
  176. package/dist/tools/context-tools.d.ts.map +1 -0
  177. package/dist/tools/context-tools.js +437 -0
  178. package/dist/tools/context-tools.js.map +1 -0
  179. package/dist/tools/graph-tools.d.ts +129 -0
  180. package/dist/tools/graph-tools.d.ts.map +1 -0
  181. package/dist/tools/graph-tools.js +237 -0
  182. package/dist/tools/graph-tools.js.map +1 -0
  183. package/dist/tools/ingestion-tools.d.ts +96 -0
  184. package/dist/tools/ingestion-tools.d.ts.map +1 -0
  185. package/dist/tools/ingestion-tools.js +90 -0
  186. package/dist/tools/ingestion-tools.js.map +1 -0
  187. package/dist/tools/learning-tools.d.ts +168 -0
  188. package/dist/tools/learning-tools.d.ts.map +1 -0
  189. package/dist/tools/learning-tools.js +158 -0
  190. package/dist/tools/learning-tools.js.map +1 -0
  191. package/dist/tools/memory-tools.d.ts +183 -0
  192. package/dist/tools/memory-tools.d.ts.map +1 -0
  193. package/dist/tools/memory-tools.js +197 -0
  194. package/dist/tools/memory-tools.js.map +1 -0
  195. package/dist/tools/phase-tools.d.ts +954 -0
  196. package/dist/tools/phase-tools.d.ts.map +1 -0
  197. package/dist/tools/phase-tools.js +1215 -0
  198. package/dist/tools/phase-tools.js.map +1 -0
  199. package/dist/tools/pipeline-tools.d.ts +140 -0
  200. package/dist/tools/pipeline-tools.d.ts.map +1 -0
  201. package/dist/tools/pipeline-tools.js +162 -0
  202. package/dist/tools/pipeline-tools.js.map +1 -0
  203. package/dist/tools/registration-tools.d.ts +220 -0
  204. package/dist/tools/registration-tools.d.ts.map +1 -0
  205. package/dist/tools/registration-tools.js +391 -0
  206. package/dist/tools/registration-tools.js.map +1 -0
  207. package/dist/util/circuit-breaker.d.ts +75 -0
  208. package/dist/util/circuit-breaker.d.ts.map +1 -0
  209. package/dist/util/circuit-breaker.js +159 -0
  210. package/dist/util/circuit-breaker.js.map +1 -0
  211. package/dist/util/config.d.ts +23 -0
  212. package/dist/util/config.d.ts.map +1 -0
  213. package/dist/util/config.js +164 -0
  214. package/dist/util/config.js.map +1 -0
  215. package/dist/util/logger.d.ts +13 -0
  216. package/dist/util/logger.d.ts.map +1 -0
  217. package/dist/util/logger.js +45 -0
  218. package/dist/util/logger.js.map +1 -0
  219. package/dist/util/token-counter.d.ts +24 -0
  220. package/dist/util/token-counter.d.ts.map +1 -0
  221. package/dist/util/token-counter.js +48 -0
  222. package/dist/util/token-counter.js.map +1 -0
  223. package/dist/util/types.d.ts +525 -0
  224. package/dist/util/types.d.ts.map +1 -0
  225. package/dist/util/types.js +5 -0
  226. package/dist/util/types.js.map +1 -0
  227. package/docker-compose.yml +20 -0
  228. package/docs/plans/2026-02-27-swarm-coordination/architecture.md +203 -0
  229. package/docs/plans/2026-02-27-swarm-coordination/vision.md +57 -0
  230. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/architecture.md +1 -0
  231. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/vision.md +300 -0
  232. package/docs/plans/completed/2026-02-27-forge-swarm-learning/architecture.md +480 -0
  233. package/docs/plans/completed/2026-02-27-forge-swarm-learning/verification-checklist.md +462 -0
  234. package/docs/plans/completed/2026-02-27-git-history-atlassian/git-jira-plan.md +181 -0
  235. package/package.json +39 -0
  236. package/plugin/.claude-plugin/plugin.json +8 -0
  237. package/plugin/.mcp.json +15 -0
  238. package/plugin/README.md +134 -0
  239. package/plugin/agents/architect.md +367 -0
  240. package/plugin/agents/backend-specialist.md +263 -0
  241. package/plugin/agents/brainstormer.md +122 -0
  242. package/plugin/agents/data-specialist.md +266 -0
  243. package/plugin/agents/designer.md +408 -0
  244. package/plugin/agents/frontend-specialist.md +241 -0
  245. package/plugin/agents/inspector.md +406 -0
  246. package/plugin/agents/knowledge-keeper.md +443 -0
  247. package/plugin/agents/platform-engineer.md +326 -0
  248. package/plugin/agents/product-manager.md +268 -0
  249. package/plugin/agents/product-owner.md +438 -0
  250. package/plugin/agents/pulse-checker.md +73 -0
  251. package/plugin/agents/qa-strategist.md +500 -0
  252. package/plugin/agents/self-improver.md +310 -0
  253. package/plugin/agents/strategist.md +360 -0
  254. package/plugin/agents/supervisor.md +380 -0
  255. package/plugin/commands/brainstorm.md +25 -0
  256. package/plugin/commands/forge.md +88 -0
  257. package/plugin/docs/atlassian-integration.md +110 -0
  258. package/plugin/docs/workflow.md +126 -0
  259. package/plugin/skills/agent-development/.skillfish.json +10 -0
  260. package/plugin/skills/agent-development/SKILL.md +415 -0
  261. package/plugin/skills/agent-development/examples/agent-creation-prompt.md +238 -0
  262. package/plugin/skills/agent-development/examples/complete-agent-examples.md +427 -0
  263. package/plugin/skills/agent-development/references/agent-creation-system-prompt.md +207 -0
  264. package/plugin/skills/agent-development/references/system-prompt-design.md +411 -0
  265. package/plugin/skills/agent-development/references/triggering-examples.md +491 -0
  266. package/plugin/skills/agent-development/scripts/validate-agent.sh +217 -0
  267. package/plugin/skills/agent-handoff/SKILL.md +335 -0
  268. package/plugin/skills/anti-stub/SKILL.md +317 -0
  269. package/plugin/skills/brainstorm/SKILL.md +31 -0
  270. package/plugin/skills/debugging/SKILL.md +276 -0
  271. package/plugin/skills/fix/SKILL.md +62 -0
  272. package/plugin/skills/frontend-design/.skillfish.json +10 -0
  273. package/plugin/skills/frontend-design/SKILL.md +42 -0
  274. package/plugin/skills/gotchas/SKILL.md +61 -0
  275. package/plugin/skills/graph-orchestrator/SKILL.md +38 -0
  276. package/plugin/skills/history/SKILL.md +58 -0
  277. package/plugin/skills/impact/SKILL.md +59 -0
  278. package/plugin/skills/implementation-execution/SKILL.md +291 -0
  279. package/plugin/skills/index-repo/SKILL.md +55 -0
  280. package/plugin/skills/interviewing/SKILL.md +225 -0
  281. package/plugin/skills/knowledge-curation/SKILL.md +393 -0
  282. package/plugin/skills/learn/SKILL.md +69 -0
  283. package/plugin/skills/mcp-integration/.skillfish.json +10 -0
  284. package/plugin/skills/mcp-integration/SKILL.md +554 -0
  285. package/plugin/skills/mcp-integration/examples/http-server.json +20 -0
  286. package/plugin/skills/mcp-integration/examples/sse-server.json +19 -0
  287. package/plugin/skills/mcp-integration/examples/stdio-server.json +26 -0
  288. package/plugin/skills/mcp-integration/references/authentication.md +549 -0
  289. package/plugin/skills/mcp-integration/references/server-types.md +536 -0
  290. package/plugin/skills/mcp-integration/references/tool-usage.md +538 -0
  291. package/plugin/skills/nestjs/.skillfish.json +10 -0
  292. package/plugin/skills/nestjs/SKILL.md +669 -0
  293. package/plugin/skills/nestjs/drizzle-reference.md +1894 -0
  294. package/plugin/skills/nestjs/reference.md +1447 -0
  295. package/plugin/skills/nestjs/workflow-optimization.md +229 -0
  296. package/plugin/skills/parallel-dispatch/SKILL.md +308 -0
  297. package/plugin/skills/project-discovery/SKILL.md +304 -0
  298. package/plugin/skills/search/SKILL.md +56 -0
  299. package/plugin/skills/security-audit/SKILL.md +362 -0
  300. package/plugin/skills/skill-development/.skillfish.json +10 -0
  301. package/plugin/skills/skill-development/SKILL.md +637 -0
  302. package/plugin/skills/skill-development/references/skill-creator-original.md +209 -0
  303. package/plugin/skills/tdd/SKILL.md +273 -0
  304. package/plugin/skills/terminal-presentation/SKILL.md +395 -0
  305. package/plugin/skills/test-strategy/SKILL.md +365 -0
  306. package/plugin/skills/verification-protocol/SKILL.md +256 -0
  307. package/plugin/skills/visual-explainer/CHANGELOG.md +97 -0
  308. package/plugin/skills/visual-explainer/LICENSE +21 -0
  309. package/plugin/skills/visual-explainer/README.md +137 -0
  310. package/plugin/skills/visual-explainer/SKILL.md +352 -0
  311. package/plugin/skills/visual-explainer/banner.png +0 -0
  312. package/plugin/skills/visual-explainer/package.json +11 -0
  313. package/plugin/skills/visual-explainer/prompts/diff-review.md +68 -0
  314. package/plugin/skills/visual-explainer/prompts/fact-check.md +63 -0
  315. package/plugin/skills/visual-explainer/prompts/generate-slides.md +18 -0
  316. package/plugin/skills/visual-explainer/prompts/generate-web-diagram.md +10 -0
  317. package/plugin/skills/visual-explainer/prompts/plan-review.md +86 -0
  318. package/plugin/skills/visual-explainer/prompts/project-recap.md +61 -0
  319. package/plugin/skills/visual-explainer/references/css-patterns.md +1188 -0
  320. package/plugin/skills/visual-explainer/references/libraries.md +470 -0
  321. package/plugin/skills/visual-explainer/references/responsive-nav.md +212 -0
  322. package/plugin/skills/visual-explainer/references/slide-patterns.md +1403 -0
  323. package/plugin/skills/visual-explainer/templates/architecture.html +596 -0
  324. package/plugin/skills/visual-explainer/templates/data-table.html +540 -0
  325. package/plugin/skills/visual-explainer/templates/mermaid-flowchart.html +435 -0
  326. package/plugin/skills/visual-explainer/templates/slide-deck.html +913 -0
  327. package/src/cli.ts +655 -0
  328. package/src/context/.gitkeep +0 -0
  329. package/src/context/codebase.ts +393 -0
  330. package/src/context/injector.ts +797 -0
  331. package/src/context/memory.ts +187 -0
  332. package/src/context/session-index.ts +327 -0
  333. package/src/context/session.ts +152 -0
  334. package/src/index.ts +47 -0
  335. package/src/ingestion/.gitkeep +0 -0
  336. package/src/ingestion/chunker.ts +277 -0
  337. package/src/ingestion/embedder.ts +167 -0
  338. package/src/ingestion/git-analyzer.ts +545 -0
  339. package/src/ingestion/indexer.ts +984 -0
  340. package/src/ingestion/markdown-chunker.ts +337 -0
  341. package/src/ingestion/markdown-knowledge.ts +175 -0
  342. package/src/ingestion/parser.ts +475 -0
  343. package/src/ingestion/watcher.ts +182 -0
  344. package/src/knowledge/.gitkeep +0 -0
  345. package/src/knowledge/hydrator.ts +246 -0
  346. package/src/knowledge/registry.ts +463 -0
  347. package/src/knowledge/search.ts +565 -0
  348. package/src/knowledge/store.ts +262 -0
  349. package/src/learning/.gitkeep +0 -0
  350. package/src/learning/confidence.ts +193 -0
  351. package/src/learning/patterns.ts +360 -0
  352. package/src/learning/trajectory.ts +268 -0
  353. package/src/memory/.gitkeep +0 -0
  354. package/src/memory/memory-compat.ts +233 -0
  355. package/src/memory/observation-store.ts +224 -0
  356. package/src/memory/session-tracker.ts +332 -0
  357. package/src/pipeline/.gitkeep +0 -0
  358. package/src/pipeline/engine.ts +1139 -0
  359. package/src/pipeline/events.ts +253 -0
  360. package/src/pipeline/parallel.ts +394 -0
  361. package/src/pipeline/state-machine.ts +199 -0
  362. package/src/query/.gitkeep +0 -0
  363. package/src/query/graph-queries.ts +262 -0
  364. package/src/query/hybrid-search.ts +337 -0
  365. package/src/query/intent-detector.ts +131 -0
  366. package/src/query/ranking.ts +161 -0
  367. package/src/server.ts +352 -0
  368. package/src/storage/.gitkeep +0 -0
  369. package/src/storage/falkordb-store.ts +388 -0
  370. package/src/storage/file-cache.ts +141 -0
  371. package/src/storage/interfaces.ts +201 -0
  372. package/src/storage/qdrant-store.ts +557 -0
  373. package/src/storage/schema.ts +139 -0
  374. package/src/storage/sqlite.ts +168 -0
  375. package/src/tools/.gitkeep +0 -0
  376. package/src/tools/collaboration-tools.ts +208 -0
  377. package/src/tools/context-tools.ts +493 -0
  378. package/src/tools/graph-tools.ts +295 -0
  379. package/src/tools/ingestion-tools.ts +122 -0
  380. package/src/tools/learning-tools.ts +181 -0
  381. package/src/tools/memory-tools.ts +234 -0
  382. package/src/tools/phase-tools.ts +1452 -0
  383. package/src/tools/pipeline-tools.ts +188 -0
  384. package/src/tools/registration-tools.ts +450 -0
  385. package/src/util/.gitkeep +0 -0
  386. package/src/util/circuit-breaker.ts +193 -0
  387. package/src/util/config.ts +177 -0
  388. package/src/util/logger.ts +53 -0
  389. package/src/util/token-counter.ts +52 -0
  390. package/src/util/types.ts +710 -0
  391. package/tests/context/.gitkeep +0 -0
  392. package/tests/integration/.gitkeep +0 -0
  393. package/tests/knowledge/.gitkeep +0 -0
  394. package/tests/learning/.gitkeep +0 -0
  395. package/tests/pipeline/.gitkeep +0 -0
  396. package/tests/tools/.gitkeep +0 -0
  397. package/tsconfig.json +21 -0
  398. package/vitest.config.ts +10 -0
  399. package/vscode-extension/.vscodeignore +7 -0
  400. package/vscode-extension/README.md +43 -0
  401. package/vscode-extension/out/edge-collector.js +274 -0
  402. package/vscode-extension/out/edge-collector.js.map +1 -0
  403. package/vscode-extension/out/extension.js +264 -0
  404. package/vscode-extension/out/extension.js.map +1 -0
  405. package/vscode-extension/out/forge-client.js +318 -0
  406. package/vscode-extension/out/forge-client.js.map +1 -0
  407. package/vscode-extension/package-lock.json +59 -0
  408. package/vscode-extension/package.json +71 -0
  409. package/vscode-extension/src/edge-collector.ts +320 -0
  410. package/vscode-extension/src/extension.ts +269 -0
  411. package/vscode-extension/src/forge-client.ts +364 -0
  412. package/vscode-extension/tsconfig.json +19 -0
@@ -0,0 +1,1403 @@
1
+ # Slide Deck Patterns
2
+
3
+ CSS patterns, JS engine, slide type layouts, transitions, navigation chrome, and curated presets for self-contained HTML slide presentations. All slides are viewport-fit (100dvh), single-file, same philosophy as scrollable pages.
4
+
5
+ **When to use slides:** Only when the user explicitly requests them — `/generate-slides`, `--slides` flag on an existing prompt, or natural language like "as a slide deck." Never auto-select slide format.
6
+
7
+ **Before generating**, also read `./css-patterns.md` for shared patterns (Mermaid zoom controls, overflow protection, depth tiers, status badges) and `./libraries.md` for Mermaid theming, Chart.js, and font pairings. Those patterns apply to slides too — this file adds slide-specific patterns on top.
8
+
9
+ ## Planning a Deck from a Source Document
10
+
11
+ When converting a plan, spec, review, or any structured document into slides, follow this process before writing any HTML. Skipping it leads to polished-looking decks that silently drop 30–40% of the source material.
12
+
13
+ **Step 1 — Inventory the source.** Read the entire source document and enumerate every section, subsection, card, table row, decision, specification, collapsible detail, and footnote. Count them. A plan with 7 sections, 6 decision cards, a 7-row file table, 4 presets, 6 technique guides, and an engine spec with 3 sub-specs and 2 collapsibles is ~25 distinct content items that all need slide real estate.
14
+
15
+ **Step 2 — Map source to slides.** Assign each inventory item to one or more slides. Every item must appear somewhere. Rules:
16
+ - If a section has 6 decisions, all 6 need slides — not the 2 that fit on one split slide.
17
+ - If a table has 7 rows, all 7 rows show up.
18
+ - Collapsible/expandable details in the source are not optional in the deck — they become their own slides.
19
+ - Subsections with multiple cards (e.g., "6 Visual Technique cards") may need 2–3 slides to cover at readable density.
20
+ - Each plan section typically needs a divider slide + 1–3 content slides depending on density.
21
+
22
+ **Step 3 — Choose layouts.** For each planned slide, pick a slide type and spatial composition. Vary across the sequence (see Compositional Variety below). This is where narrative pacing happens — alternate dense slides with sparse ones.
23
+
24
+ **Step 4 — Plan images.** Run `which surf`. If surf-cli is available, plan 2–4 generated images for the deck. At minimum, target the **title slide** (16:9 background that sets the visual tone) and **one full-bleed slide** (immersive background for a key moment). Content slides with conceptual topics also benefit from a 1:1 illustration in the aside area. Generate these images early — before writing HTML — so you can embed them as base64 data URIs. See the Proactive Imagery section below for the full workflow. If surf isn't available, degrade to CSS gradients and SVG decorations — note the fallback in a comment but don't error.
25
+
26
+ **Step 5 — Verify before writing HTML.** Scan the inventory from Step 1. Is anything unmapped? Would a reader of the source document notice something missing from the deck? If yes, add slides. A source document with 7 sections typically produces 18–25 slides, not 10–13.
27
+
28
+ **The test:** After generating the deck, a reader who has never seen the source document should be able to reconstruct every major point from the slides alone. If they'd miss entire sections, the deck is incomplete.
29
+
30
+ ## Slide Engine Base
31
+
32
+ The deck is a scroll-snap container. Each slide is exactly one viewport tall.
33
+
34
+ ```html
35
+ <body>
36
+ <div class="deck">
37
+ <section class="slide slide--title"> ... </section>
38
+ <section class="slide slide--content"> ... </section>
39
+ <section class="slide slide--diagram"> ... </section>
40
+ <!-- one <section> per slide -->
41
+ </div>
42
+ </body>
43
+ ```
44
+
45
+ ```css
46
+ /* Scroll-snap container */
47
+ .deck {
48
+ height: 100dvh;
49
+ overflow-y: auto;
50
+ scroll-snap-type: y mandatory;
51
+ scroll-behavior: smooth;
52
+ -webkit-overflow-scrolling: touch;
53
+ }
54
+
55
+ /* Individual slide */
56
+ .slide {
57
+ height: 100dvh;
58
+ scroll-snap-align: start;
59
+ overflow: hidden;
60
+ position: relative;
61
+ display: flex;
62
+ flex-direction: column;
63
+ justify-content: center;
64
+ padding: clamp(40px, 6vh, 80px) clamp(40px, 8vw, 120px);
65
+ isolation: isolate; /* contain z-index stacking */
66
+ }
67
+ ```
68
+
69
+ ## Typography Scale
70
+
71
+ Slide typography is 2–3× larger than scrollable pages. Page-sized text on a viewport-sized canvas looks like a mistake.
72
+
73
+ ```css
74
+ .slide__display {
75
+ font-size: clamp(48px, 10vw, 120px);
76
+ font-weight: 800;
77
+ letter-spacing: -3px;
78
+ line-height: 0.95;
79
+ text-wrap: balance;
80
+ }
81
+
82
+ .slide__heading {
83
+ font-size: clamp(28px, 5vw, 48px);
84
+ font-weight: 700;
85
+ letter-spacing: -1px;
86
+ line-height: 1.1;
87
+ text-wrap: balance;
88
+ }
89
+
90
+ .slide__body {
91
+ font-size: clamp(16px, 2.2vw, 24px);
92
+ line-height: 1.6;
93
+ text-wrap: pretty;
94
+ }
95
+
96
+ .slide__label {
97
+ font-family: var(--font-mono);
98
+ font-size: clamp(10px, 1.2vw, 14px);
99
+ font-weight: 600;
100
+ text-transform: uppercase;
101
+ letter-spacing: 1.5px;
102
+ color: var(--text-dim);
103
+ }
104
+
105
+ .slide__subtitle {
106
+ font-family: var(--font-mono);
107
+ font-size: clamp(14px, 1.8vw, 20px);
108
+ color: var(--text-dim);
109
+ letter-spacing: 0.5px;
110
+ }
111
+ ```
112
+
113
+ | Element | Size range | Notes |
114
+ |---------|-----------|-------|
115
+ | Display (title slides) | 48–120px | `10vw` preferred, weight 800 |
116
+ | Section numbers | 100–240px | Ultra-light (weight 200), decorative |
117
+ | Headings | 28–48px | `5vw` preferred, weight 700 |
118
+ | Body / bullets | 16–24px | `2.2vw` preferred, 1.6 line-height |
119
+ | Code blocks | 14–18px | `1.8vw` preferred, mono |
120
+ | Quotes | 24–48px | `4vw` preferred, serif italic |
121
+ | Labels / captions | 10–14px | Mono, uppercase, dimmed |
122
+
123
+ ## Cinematic Transitions
124
+
125
+ IntersectionObserver adds `.visible` when a slide enters the viewport. Slides animate in once and stay visible when scrolling back.
126
+
127
+ ```css
128
+ /* Slide entrance — fade + lift + subtle scale */
129
+ .slide {
130
+ opacity: 0;
131
+ transform: translateY(40px) scale(0.98);
132
+ transition:
133
+ opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
134
+ transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
135
+ }
136
+
137
+ .slide.visible {
138
+ opacity: 1;
139
+ transform: none;
140
+ }
141
+
142
+ /* Staggered child reveals — add .reveal to each content element */
143
+ .slide .reveal {
144
+ opacity: 0;
145
+ transform: translateY(20px);
146
+ transition:
147
+ opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1),
148
+ transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
149
+ }
150
+
151
+ .slide.visible .reveal {
152
+ opacity: 1;
153
+ transform: none;
154
+ }
155
+
156
+ /* Stagger delays — up to 6 children per slide */
157
+ .slide.visible .reveal:nth-child(1) { transition-delay: 0.1s; }
158
+ .slide.visible .reveal:nth-child(2) { transition-delay: 0.2s; }
159
+ .slide.visible .reveal:nth-child(3) { transition-delay: 0.3s; }
160
+ .slide.visible .reveal:nth-child(4) { transition-delay: 0.4s; }
161
+ .slide.visible .reveal:nth-child(5) { transition-delay: 0.5s; }
162
+ .slide.visible .reveal:nth-child(6) { transition-delay: 0.6s; }
163
+
164
+ @media (prefers-reduced-motion: reduce) {
165
+ .slide,
166
+ .slide .reveal {
167
+ opacity: 1 !important;
168
+ transform: none !important;
169
+ transition: none !important;
170
+ }
171
+ }
172
+ ```
173
+
174
+ ## Navigation Chrome
175
+
176
+ All navigation is `position: fixed` with high z-index, layered above slides. Styled to be visible on any background.
177
+
178
+ ### Progress Bar
179
+
180
+ ```css
181
+ .deck-progress {
182
+ position: fixed;
183
+ top: 0;
184
+ left: 0;
185
+ height: 3px;
186
+ background: var(--accent);
187
+ z-index: 100;
188
+ transition: width 0.3s ease;
189
+ pointer-events: none;
190
+ }
191
+ ```
192
+
193
+ ### Nav Dots
194
+
195
+ ```css
196
+ .deck-dots {
197
+ position: fixed;
198
+ right: clamp(12px, 2vw, 24px);
199
+ top: 50%;
200
+ transform: translateY(-50%);
201
+ display: flex;
202
+ flex-direction: column;
203
+ gap: 8px;
204
+ z-index: 100;
205
+ }
206
+
207
+ .deck-dot {
208
+ width: 8px;
209
+ height: 8px;
210
+ border-radius: 50%;
211
+ background: var(--text-dim);
212
+ opacity: 0.3;
213
+ border: none;
214
+ padding: 0;
215
+ cursor: pointer;
216
+ transition: opacity 0.2s ease, transform 0.2s ease;
217
+ }
218
+
219
+ .deck-dot:hover {
220
+ opacity: 0.6;
221
+ }
222
+
223
+ .deck-dot.active {
224
+ opacity: 1;
225
+ transform: scale(1.5);
226
+ background: var(--accent);
227
+ }
228
+ ```
229
+
230
+ ### Slide Counter
231
+
232
+ ```css
233
+ .deck-counter {
234
+ position: fixed;
235
+ bottom: clamp(12px, 2vh, 24px);
236
+ right: clamp(12px, 2vw, 24px);
237
+ font-family: var(--font-mono);
238
+ font-size: 12px;
239
+ color: var(--text-dim);
240
+ z-index: 100;
241
+ font-variant-numeric: tabular-nums;
242
+ }
243
+ ```
244
+
245
+ ### Keyboard Hints
246
+
247
+ Auto-fade after first interaction or after 4 seconds.
248
+
249
+ ```css
250
+ .deck-hints {
251
+ position: fixed;
252
+ bottom: clamp(12px, 2vh, 24px);
253
+ left: 50%;
254
+ transform: translateX(-50%);
255
+ font-family: var(--font-mono);
256
+ font-size: 11px;
257
+ color: var(--text-dim);
258
+ opacity: 0.6;
259
+ z-index: 100;
260
+ transition: opacity 0.5s ease;
261
+ white-space: nowrap;
262
+ }
263
+
264
+ .deck-hints.faded {
265
+ opacity: 0;
266
+ pointer-events: none;
267
+ }
268
+ ```
269
+
270
+ ### Chrome Visibility on Mixed Backgrounds
271
+
272
+ For decks where some slides are light and some dark (especially full-bleed slides), nav chrome needs to remain visible. Two approaches:
273
+
274
+ ```css
275
+ /* Approach A: subtle backdrop on chrome elements */
276
+ .deck-dots,
277
+ .deck-counter {
278
+ background: color-mix(in srgb, var(--bg) 70%, transparent 30%);
279
+ padding: 6px;
280
+ border-radius: 20px;
281
+ backdrop-filter: blur(4px);
282
+ -webkit-backdrop-filter: blur(4px);
283
+ }
284
+
285
+ /* Approach B: text shadow for legibility on any background */
286
+ .deck-counter,
287
+ .deck-hints {
288
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
289
+ }
290
+ ```
291
+
292
+ ## SlideEngine JavaScript
293
+
294
+ Add once at the end of the page. Handles navigation, chrome updates, and scroll-triggered reveals. Event delegation ensures slide-internal interactions (Mermaid zoom, scrollable code, overflow tables) don't trigger slide navigation.
295
+
296
+ ```javascript
297
+ class SlideEngine {
298
+ constructor() {
299
+ this.deck = document.querySelector('.deck');
300
+ this.slides = [...document.querySelectorAll('.slide')];
301
+ this.current = 0;
302
+ this.total = this.slides.length;
303
+ this.buildChrome();
304
+ this.bindEvents();
305
+ this.observe();
306
+ this.update();
307
+ }
308
+
309
+ buildChrome() {
310
+ // Progress bar
311
+ var bar = document.createElement('div');
312
+ bar.className = 'deck-progress';
313
+ document.body.appendChild(bar);
314
+ this.bar = bar;
315
+
316
+ // Nav dots
317
+ var dots = document.createElement('div');
318
+ dots.className = 'deck-dots';
319
+ var self = this;
320
+ this.slides.forEach(function(_, i) {
321
+ var d = document.createElement('button');
322
+ d.className = 'deck-dot';
323
+ d.title = 'Slide ' + (i + 1);
324
+ d.onclick = function() { self.goTo(i); };
325
+ dots.appendChild(d);
326
+ });
327
+ document.body.appendChild(dots);
328
+ this.dots = [].slice.call(dots.children);
329
+
330
+ // Counter
331
+ var ctr = document.createElement('div');
332
+ ctr.className = 'deck-counter';
333
+ document.body.appendChild(ctr);
334
+ this.counter = ctr;
335
+
336
+ // Keyboard hints
337
+ var hints = document.createElement('div');
338
+ hints.className = 'deck-hints';
339
+ hints.textContent = '\u2190 \u2192 or scroll to navigate';
340
+ document.body.appendChild(hints);
341
+ this.hints = hints;
342
+ this.hintTimer = setTimeout(function() {
343
+ hints.classList.add('faded');
344
+ }, 4000);
345
+ }
346
+
347
+ bindEvents() {
348
+ var self = this;
349
+ // Keyboard — skip if focus is inside interactive content
350
+ document.addEventListener('keydown', function(e) {
351
+ if (e.target.closest('.mermaid-wrap, .table-scroll, .code-scroll, input, textarea, [contenteditable]')) return;
352
+ if (['ArrowDown', 'ArrowRight', ' ', 'PageDown'].includes(e.key)) {
353
+ e.preventDefault(); self.next();
354
+ } else if (['ArrowUp', 'ArrowLeft', 'PageUp'].includes(e.key)) {
355
+ e.preventDefault(); self.prev();
356
+ } else if (e.key === 'Home') {
357
+ e.preventDefault(); self.goTo(0);
358
+ } else if (e.key === 'End') {
359
+ e.preventDefault(); self.goTo(self.total - 1);
360
+ }
361
+ self.fadeHints();
362
+ });
363
+
364
+ // Touch swipe
365
+ var touchY;
366
+ this.deck.addEventListener('touchstart', function(e) {
367
+ touchY = e.touches[0].clientY;
368
+ }, { passive: true });
369
+ this.deck.addEventListener('touchend', function(e) {
370
+ var dy = touchY - e.changedTouches[0].clientY;
371
+ if (Math.abs(dy) > 50) { dy > 0 ? self.next() : self.prev(); }
372
+ });
373
+ }
374
+
375
+ observe() {
376
+ var self = this;
377
+ var obs = new IntersectionObserver(function(entries) {
378
+ entries.forEach(function(entry) {
379
+ if (entry.isIntersecting) {
380
+ entry.target.classList.add('visible');
381
+ self.current = self.slides.indexOf(entry.target);
382
+ self.update();
383
+ }
384
+ });
385
+ }, { threshold: 0.5 });
386
+ this.slides.forEach(function(s) { obs.observe(s); });
387
+ }
388
+
389
+ goTo(i) {
390
+ this.slides[Math.max(0, Math.min(i, this.total - 1))]
391
+ .scrollIntoView({ behavior: 'smooth' });
392
+ }
393
+
394
+ next() { if (this.current < this.total - 1) this.goTo(this.current + 1); }
395
+ prev() { if (this.current > 0) this.goTo(this.current - 1); }
396
+
397
+ update() {
398
+ this.bar.style.width = ((this.current + 1) / this.total * 100) + '%';
399
+ var self = this;
400
+ this.dots.forEach(function(d, i) { d.classList.toggle('active', i === self.current); });
401
+ this.counter.textContent = (this.current + 1) + ' / ' + this.total;
402
+ }
403
+
404
+ fadeHints() {
405
+ clearTimeout(this.hintTimer);
406
+ this.hints.classList.add('faded');
407
+ }
408
+ }
409
+ ```
410
+
411
+ **Usage:** Instantiate after the DOM is ready and any libraries (Mermaid, Chart.js) have rendered. Always call `autoFit()` before `new SlideEngine()` so content is sized correctly before intersection observers fire.
412
+
413
+ ```html
414
+ <script>
415
+ // After Mermaid/Chart.js initialization (if used), or at end of <body>:
416
+ document.addEventListener('DOMContentLoaded', function() {
417
+ autoFit();
418
+ new SlideEngine();
419
+ });
420
+ </script>
421
+ ```
422
+
423
+ ## Auto-Fit
424
+
425
+ A single post-render function that handles all known content overflow cases. Agents can't perfectly predict how text reflows at every viewport size, so `autoFit()` is a required safety net. Call it after Mermaid/Chart.js render but before SlideEngine init.
426
+
427
+ ```javascript
428
+ function autoFit() {
429
+ // Mermaid SVGs: fill container instead of rendering at intrinsic size
430
+ document.querySelectorAll('.mermaid svg').forEach(function(svg) {
431
+ svg.removeAttribute('height');
432
+ svg.style.width = '100%';
433
+ svg.style.maxWidth = '100%';
434
+ svg.style.height = 'auto';
435
+ svg.parentElement.style.width = '100%';
436
+ });
437
+
438
+ // KPI values: visually scale down text that overflows card width
439
+ document.querySelectorAll('.slide__kpi-val').forEach(function(el) {
440
+ if (el.scrollWidth > el.clientWidth) {
441
+ var s = el.clientWidth / el.scrollWidth;
442
+ el.style.transform = 'scale(' + s + ')';
443
+ el.style.transformOrigin = 'left top';
444
+ }
445
+ });
446
+
447
+ // Blockquotes: reduce font proportionally for long text
448
+ document.querySelectorAll('.slide--quote blockquote').forEach(function(el) {
449
+ var len = el.textContent.trim().length;
450
+ if (len > 100) {
451
+ var scale = Math.max(0.5, 100 / len);
452
+ var fs = parseFloat(getComputedStyle(el).fontSize);
453
+ el.style.fontSize = Math.max(16, Math.round(fs * scale)) + 'px';
454
+ }
455
+ });
456
+ }
457
+ ```
458
+
459
+ Three cases, one function:
460
+ - **Mermaid:** SVGs render with fixed dimensions inside flex containers — force them to fill available width.
461
+ - **KPI values:** Long text strings at hero scale overflow card boundaries — `transform: scale()` shrinks visually without reflow.
462
+ - **Blockquotes:** Quotes longer than ~100 characters get proportionally smaller font. The 0.5 floor prevents unreadably small text; if it needs more than 50% shrink, it should have been a content slide.
463
+
464
+ ## Slide Type Layouts
465
+
466
+ Each type has a defined HTML structure and CSS layout. The agent can adapt colors, fonts, and spacing per aesthetic, but the structural patterns stay consistent.
467
+
468
+ ### Title Slide
469
+
470
+ Full-viewport hero. Background treatment via gradient, texture, or surf-generated image. 80–120px display type.
471
+
472
+ ```html
473
+ <section class="slide slide--title">
474
+ <svg class="slide__decor" ...><!-- optional decorative accent --></svg>
475
+ <div class="slide__content reveal">
476
+ <h1 class="slide__display">Deck Title</h1>
477
+ <p class="slide__subtitle reveal">Subtitle or date</p>
478
+ </div>
479
+ </section>
480
+ ```
481
+
482
+ ```css
483
+ .slide--title {
484
+ justify-content: center;
485
+ align-items: center;
486
+ text-align: center;
487
+ }
488
+ ```
489
+
490
+ ### Section Divider
491
+
492
+ Oversized decorative number (200px+, ultra-light weight) with heading. Breathing room between topics. SVG accent marks optional.
493
+
494
+ ```html
495
+ <section class="slide slide--divider">
496
+ <span class="slide__number">02</span>
497
+ <div class="slide__content">
498
+ <h2 class="slide__heading reveal">Section Title</h2>
499
+ <p class="slide__subtitle reveal">Optional subheading</p>
500
+ </div>
501
+ </section>
502
+ ```
503
+
504
+ ```css
505
+ .slide--divider {
506
+ justify-content: center;
507
+ }
508
+
509
+ .slide--divider .slide__number {
510
+ font-size: clamp(100px, 22vw, 260px);
511
+ font-weight: 200;
512
+ line-height: 0.85;
513
+ opacity: 0.08;
514
+ position: absolute;
515
+ top: 50%;
516
+ left: 50%;
517
+ transform: translate(-50%, -55%);
518
+ pointer-events: none;
519
+ font-variant-numeric: tabular-nums;
520
+ }
521
+ ```
522
+
523
+ ### Content Slide
524
+
525
+ Heading + bullets or paragraphs. Asymmetric layout — content offset to one side. Max 5–6 bullets (2 lines each).
526
+
527
+ ```html
528
+ <section class="slide slide--content">
529
+ <div class="slide__inner">
530
+ <div class="slide__text">
531
+ <h2 class="slide__heading reveal">Heading</h2>
532
+ <ul class="slide__bullets">
533
+ <li class="reveal">First point</li>
534
+ <li class="reveal">Second point</li>
535
+ </ul>
536
+ </div>
537
+ <div class="slide__aside reveal">
538
+ <!-- optional: illustration, icon, mini-diagram, accent SVG -->
539
+ </div>
540
+ </div>
541
+ </section>
542
+ ```
543
+
544
+ ```css
545
+ .slide--content .slide__inner {
546
+ display: grid;
547
+ grid-template-columns: 3fr 2fr;
548
+ gap: clamp(24px, 4vw, 60px);
549
+ align-items: center;
550
+ width: 100%;
551
+ }
552
+
553
+ /* For right-heavy variant: swap to 2fr 3fr */
554
+ .slide--content .slide__bullets {
555
+ list-style: none;
556
+ padding: 0;
557
+ }
558
+
559
+ .slide--content .slide__bullets li {
560
+ padding: 8px 0 8px 20px;
561
+ position: relative;
562
+ font-size: clamp(16px, 2vw, 22px);
563
+ line-height: 1.6;
564
+ color: var(--text-dim);
565
+ }
566
+
567
+ .slide--content .slide__bullets li::before {
568
+ content: '';
569
+ position: absolute;
570
+ left: 0;
571
+ top: 18px;
572
+ width: 6px;
573
+ height: 6px;
574
+ border-radius: 50%;
575
+ background: var(--accent);
576
+ }
577
+ ```
578
+
579
+ ### Split Slide
580
+
581
+ Asymmetric two-panel (60/40 or 70/30). Before/after, text+diagram, text+image. Each panel has its own background tier. Zero padding on the slide itself — panels fill edge to edge.
582
+
583
+ ```html
584
+ <section class="slide slide--split">
585
+ <div class="slide__panels">
586
+ <div class="slide__panel slide__panel--primary">
587
+ <h2 class="slide__heading reveal">Left Panel</h2>
588
+ <div class="slide__body reveal">Content...</div>
589
+ </div>
590
+ <div class="slide__panel slide__panel--secondary">
591
+ <!-- diagram, image, code block, or contrasting content -->
592
+ </div>
593
+ </div>
594
+ </section>
595
+ ```
596
+
597
+ ```css
598
+ .slide--split {
599
+ padding: 0;
600
+ }
601
+
602
+ .slide--split .slide__panels {
603
+ display: grid;
604
+ grid-template-columns: 3fr 2fr;
605
+ height: 100%;
606
+ }
607
+
608
+ .slide--split .slide__panel {
609
+ padding: clamp(40px, 6vh, 80px) clamp(32px, 4vw, 60px);
610
+ display: flex;
611
+ flex-direction: column;
612
+ justify-content: center;
613
+ }
614
+
615
+ .slide--split .slide__panel--primary {
616
+ background: var(--surface);
617
+ }
618
+
619
+ .slide--split .slide__panel--secondary {
620
+ background: var(--surface2);
621
+ }
622
+ ```
623
+
624
+ ### Diagram Slide
625
+
626
+ Full-viewport Mermaid diagram. Max 8–10 nodes (presentation scale — fewer, larger than page diagrams). Node labels at 18px+, edges at 2px+. Zoom controls from `css-patterns.md` apply here.
627
+
628
+ **When to use Mermaid vs CSS in slides.** Mermaid renders SVGs at a fixed size the agent can't control — node dimensions are set by the library, not by CSS. This creates a recurring problem: small diagrams (fewer than ~7 nodes, no branching) render as tiny elements floating in a huge viewport with acres of dead space. The rule:
629
+
630
+ - **Use Mermaid** for complex graphs: 8+ nodes, branching paths, cycles, multiple edge crossings — anything where automatic edge routing saves real effort.
631
+ - **Use CSS Pipeline** (below) for simple linear flows: A → B → C → D sequences, build steps, deployment stages. CSS cards give full control over sizing, typography, and fill the viewport naturally.
632
+ - **Never leave a small Mermaid diagram alone on a slide.** If the diagram is small, either switch to CSS, or pair it with supporting content (description cards, bullet annotations, a summary panel) in a split layout. A slide with a tiny diagram and empty space is a failed slide.
633
+
634
+ **Mermaid centering fix.** When you do use Mermaid, add `display: flex; align-items: center; justify-content: center;` to `.mermaid-wrap` so the SVG centers within its container instead of hugging the top-left corner. Change `transform-origin` to `center center` so zoom radiates from the middle.
635
+
636
+ ```html
637
+ <section class="slide slide--diagram">
638
+ <h2 class="slide__heading reveal">Diagram Title</h2>
639
+ <div class="mermaid-wrap reveal" style="flex:1; min-height:0;">
640
+ <div class="zoom-controls">
641
+ <button onclick="zoomDiagram(this,1.2)" title="Zoom in">+</button>
642
+ <button onclick="zoomDiagram(this,0.8)" title="Zoom out">&minus;</button>
643
+ <button onclick="resetZoom(this)" title="Reset">&#8634;</button>
644
+ </div>
645
+ <pre class="mermaid">
646
+ graph TD
647
+ A --> B
648
+ </pre>
649
+ </div>
650
+ </section>
651
+ ```
652
+
653
+ ```css
654
+ .slide--diagram {
655
+ padding: clamp(24px, 4vh, 48px) clamp(24px, 4vw, 60px);
656
+ }
657
+
658
+ .slide--diagram .slide__heading {
659
+ margin-bottom: clamp(8px, 1.5vh, 20px);
660
+ }
661
+
662
+ .slide--diagram .mermaid-wrap {
663
+ border-radius: 12px;
664
+ overflow: auto;
665
+ display: flex;
666
+ align-items: center;
667
+ justify-content: center;
668
+ }
669
+
670
+ .slide--diagram .mermaid-wrap .mermaid {
671
+ transform-origin: center center;
672
+ }
673
+ ```
674
+
675
+ **Auto-fit SVG to container.** Mermaid renders SVGs with fixed dimensions and an inline `max-width` style that keeps diagrams tiny inside large slides. The `autoFit()` function (see above) handles this at runtime. Keep the CSS as a belt-and-suspenders fallback:
676
+
677
+ ```css
678
+ .slide--diagram .mermaid svg {
679
+ width: 100% !important;
680
+ height: auto !important;
681
+ max-width: 100% !important;
682
+ }
683
+ ```
684
+
685
+ **Mermaid overrides for presentation scale** (add alongside the standard Mermaid CSS overrides from `libraries.md`):
686
+
687
+ ```css
688
+ .slide--diagram .mermaid .nodeLabel {
689
+ font-size: 18px !important;
690
+ }
691
+
692
+ .slide--diagram .mermaid .edgeLabel {
693
+ font-size: 14px !important;
694
+ }
695
+
696
+ .slide--diagram .mermaid .node rect,
697
+ .slide--diagram .mermaid .node circle,
698
+ .slide--diagram .mermaid .node polygon {
699
+ stroke-width: 2px;
700
+ }
701
+
702
+ .slide--diagram .mermaid .edge-pattern-solid {
703
+ stroke-width: 2px;
704
+ }
705
+ ```
706
+
707
+ ### CSS Pipeline Slide
708
+
709
+ For simple linear flows (build steps, deployment stages, data pipelines) where Mermaid would render too small. CSS cards with arrow connectors give full control over sizing and fill the viewport naturally. Each step card expands to fill available space via `flex: 1`.
710
+
711
+ ```html
712
+ <section class="slide" style="background-image:radial-gradient(...);">
713
+ <p class="slide__label reveal">Pipeline Label</p>
714
+ <h2 class="slide__heading reveal">Pipeline Title</h2>
715
+ <div class="pipeline reveal">
716
+ <div class="pipeline__step" style="border-top-color:var(--accent);">
717
+ <div class="pipeline__num">01</div>
718
+ <div class="pipeline__name">Step Name</div>
719
+ <div class="pipeline__desc">What this step produces or does</div>
720
+ <div class="pipeline__file">output-file.md</div>
721
+ </div>
722
+ <div class="pipeline__arrow">
723
+ <svg viewBox="0 0 24 24" width="20" height="20"><path d="M5 12h14m-4-4l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
724
+ </div>
725
+ <div class="pipeline__step"> ... </div>
726
+ <!-- repeat step + arrow pairs -->
727
+ </div>
728
+ </section>
729
+ ```
730
+
731
+ ```css
732
+ .pipeline {
733
+ display: flex;
734
+ align-items: stretch;
735
+ gap: 0;
736
+ flex: 1;
737
+ min-height: 0;
738
+ margin-top: clamp(12px, 2vh, 24px);
739
+ }
740
+
741
+ .pipeline__step {
742
+ flex: 1;
743
+ background: var(--surface);
744
+ border: 1px solid var(--border);
745
+ border-top: 3px solid var(--accent);
746
+ border-radius: 10px;
747
+ padding: clamp(14px, 2.5vh, 28px) clamp(12px, 1.5vw, 22px);
748
+ display: flex;
749
+ flex-direction: column;
750
+ min-width: 0;
751
+ overflow-wrap: break-word;
752
+ }
753
+
754
+ .pipeline__num {
755
+ font-size: clamp(10px, 1.2vw, 13px);
756
+ font-weight: 600;
757
+ color: var(--accent);
758
+ letter-spacing: 1px;
759
+ }
760
+
761
+ .pipeline__name {
762
+ font-size: clamp(16px, 2vw, 24px);
763
+ font-weight: 700;
764
+ margin: clamp(4px, 0.8vh, 8px) 0;
765
+ }
766
+
767
+ .pipeline__desc {
768
+ font-size: clamp(12px, 1.3vw, 16px);
769
+ color: var(--text-dim);
770
+ line-height: 1.5;
771
+ flex: 1;
772
+ }
773
+
774
+ .pipeline__file {
775
+ font-size: clamp(10px, 1.1vw, 12px);
776
+ color: var(--accent);
777
+ background: var(--accent-dim);
778
+ padding: 3px 8px;
779
+ border-radius: 4px;
780
+ margin-top: clamp(8px, 1.5vh, 16px);
781
+ align-self: flex-start;
782
+ }
783
+
784
+ .pipeline__arrow {
785
+ display: flex;
786
+ align-items: center;
787
+ padding: 0 clamp(3px, 0.4vw, 6px);
788
+ color: var(--accent);
789
+ flex-shrink: 0;
790
+ opacity: 0.4;
791
+ }
792
+
793
+ @media (max-width: 768px) {
794
+ .pipeline { flex-direction: column; }
795
+ .pipeline__arrow { justify-content: center; padding: 4px 0; transform: rotate(90deg); }
796
+ }
797
+ ```
798
+
799
+ Each `.pipeline__step` uses `flex: 1` to fill available width equally, and the pipeline container itself uses `flex: 1` to fill available vertical space in the slide. Step cards stretch to fill, so the content isn't floating in empty space. The `.pipeline__file` badge at the bottom anchors each card and adds a practical detail. Max 5–6 steps — beyond that, split across two slides.
800
+
801
+ ### Dashboard Slide
802
+
803
+ KPI cards at presentation scale (48–64px hero numbers). Mini-charts via Chart.js or SVG sparklines. Max 6 KPIs.
804
+
805
+ ```html
806
+ <section class="slide slide--dashboard">
807
+ <h2 class="slide__heading reveal">Metrics Overview</h2>
808
+ <div class="slide__kpis">
809
+ <div class="slide__kpi reveal">
810
+ <div class="slide__kpi-val" style="color:var(--accent)">247</div>
811
+ <div class="slide__kpi-label">Lines Added</div>
812
+ </div>
813
+ <!-- more KPI cards -->
814
+ </div>
815
+ </section>
816
+ ```
817
+
818
+ ```css
819
+ .slide--dashboard .slide__kpis {
820
+ display: grid;
821
+ grid-template-columns: repeat(auto-fit, minmax(clamp(140px, 20vw, 220px), 1fr));
822
+ gap: clamp(12px, 2vw, 24px);
823
+ }
824
+
825
+ .slide__kpi {
826
+ background: var(--surface);
827
+ border: 1px solid var(--border);
828
+ border-radius: 12px;
829
+ padding: clamp(16px, 3vh, 32px) clamp(16px, 2vw, 24px);
830
+ min-width: 0;
831
+ overflow: hidden;
832
+ }
833
+
834
+ .slide__kpi-val {
835
+ font-size: clamp(36px, 6vw, 64px);
836
+ font-weight: 800;
837
+ letter-spacing: -1.5px;
838
+ line-height: 1.1;
839
+ font-variant-numeric: tabular-nums;
840
+ white-space: nowrap;
841
+ }
842
+
843
+ .slide__kpi-label {
844
+ font-family: var(--font-mono);
845
+ font-size: clamp(9px, 1.2vw, 13px);
846
+ font-weight: 600;
847
+ text-transform: uppercase;
848
+ letter-spacing: 1.5px;
849
+ color: var(--text-dim);
850
+ margin-top: 8px;
851
+ }
852
+ ```
853
+
854
+ **KPI hero values should be short** — numbers, percentages, 1–3 word labels. Ideal length is 1–6 characters at hero scale. Longer strings like `store=false` break the layout at 64px. If you must show a longer value, put it in the label or body text instead. The `autoFit()` function (see below) will scale down overflows as a safety net.
855
+
856
+ ### Table Slide
857
+
858
+ 18–20px cell text for projection readability. Max 8 rows per slide — overflow paginates to the next slide. Stronger alternating row contrast than page tables.
859
+
860
+ ```html
861
+ <section class="slide slide--table">
862
+ <h2 class="slide__heading reveal">Data Title</h2>
863
+ <div class="table-wrap reveal" style="flex:1; min-height:0;">
864
+ <div class="table-scroll">
865
+ <table class="data-table"> ... </table>
866
+ </div>
867
+ </div>
868
+ </section>
869
+ ```
870
+
871
+ ```css
872
+ .slide--table {
873
+ padding: clamp(24px, 4vh, 48px) clamp(24px, 4vw, 60px);
874
+ }
875
+
876
+ .slide--table .data-table {
877
+ font-size: clamp(14px, 1.8vw, 20px);
878
+ }
879
+
880
+ .slide--table .data-table th {
881
+ font-size: clamp(10px, 1.3vw, 14px);
882
+ padding: clamp(8px, 1.5vh, 14px) clamp(12px, 2vw, 20px);
883
+ }
884
+
885
+ .slide--table .data-table td {
886
+ padding: clamp(10px, 1.5vh, 16px) clamp(12px, 2vw, 20px);
887
+ }
888
+ ```
889
+
890
+ ### Code Slide
891
+
892
+ 18px mono on a recessed dark background. Max 10 lines. Floating filename label. Centered on the viewport for focus.
893
+
894
+ ```html
895
+ <section class="slide slide--code">
896
+ <h2 class="slide__heading reveal">What Changed</h2>
897
+ <div class="slide__code-block reveal">
898
+ <span class="slide__code-filename">worker.ts</span>
899
+ <pre><code>function processQueue(items) {
900
+ // highlighted code here
901
+ }</code></pre>
902
+ </div>
903
+ </section>
904
+ ```
905
+
906
+ ```css
907
+ .slide--code {
908
+ align-items: center;
909
+ }
910
+
911
+ .slide__code-block {
912
+ background: var(--code-bg, #1a1a2e);
913
+ border: 1px solid var(--border);
914
+ border-radius: 12px;
915
+ padding: clamp(24px, 4vh, 48px) clamp(24px, 4vw, 48px);
916
+ max-width: 900px;
917
+ width: 100%;
918
+ position: relative;
919
+ }
920
+
921
+ .slide__code-filename {
922
+ position: absolute;
923
+ top: -12px;
924
+ left: 24px;
925
+ font-family: var(--font-mono);
926
+ font-size: 11px;
927
+ font-weight: 600;
928
+ padding: 4px 12px;
929
+ border-radius: 4px;
930
+ background: var(--accent);
931
+ color: var(--bg);
932
+ }
933
+
934
+ .slide__code-block pre {
935
+ margin: 0;
936
+ overflow-x: auto;
937
+ }
938
+
939
+ .slide__code-block code {
940
+ font-family: var(--font-mono);
941
+ font-size: clamp(14px, 1.6vw, 18px);
942
+ line-height: 1.7;
943
+ color: var(--code-text, #e6edf3);
944
+ }
945
+ ```
946
+
947
+ ### Quote Slide
948
+
949
+ 36–48px serif with dramatic line-height. Oversized quotation mark as SVG or typographic decoration. Generous whitespace is the design.
950
+
951
+ ```html
952
+ <section class="slide slide--quote">
953
+ <div class="slide__quote-mark reveal">&ldquo;</div>
954
+ <blockquote class="reveal">
955
+ The best code is the code you don't have to write.
956
+ </blockquote>
957
+ <cite class="reveal">&mdash; Someone Wise</cite>
958
+ </section>
959
+ ```
960
+
961
+ ```css
962
+ .slide--quote {
963
+ justify-content: center;
964
+ align-items: center;
965
+ text-align: center;
966
+ padding: clamp(60px, 10vh, 120px) clamp(60px, 12vw, 200px);
967
+ }
968
+
969
+ .slide__quote-mark {
970
+ font-size: clamp(80px, 14vw, 180px);
971
+ line-height: 0.5;
972
+ opacity: 0.08;
973
+ font-family: Georgia, serif;
974
+ pointer-events: none;
975
+ margin-bottom: -20px;
976
+ }
977
+
978
+ .slide--quote blockquote {
979
+ font-size: clamp(24px, 4vw, 48px);
980
+ font-weight: 400;
981
+ line-height: 1.35;
982
+ font-style: italic;
983
+ margin: 0;
984
+ }
985
+
986
+ .slide--quote cite {
987
+ font-family: var(--font-mono);
988
+ font-size: clamp(11px, 1.4vw, 14px);
989
+ font-style: normal;
990
+ margin-top: clamp(16px, 3vh, 32px);
991
+ display: block;
992
+ letter-spacing: 1.5px;
993
+ text-transform: uppercase;
994
+ color: var(--text-dim);
995
+ }
996
+ ```
997
+
998
+ ### Full-Bleed Slide
999
+
1000
+ Background image (surf-generated or CSS gradient) dominates the viewport. Text overlay with gradient scrim ensuring contrast. Zero slide padding.
1001
+
1002
+ ```html
1003
+ <section class="slide slide--bleed">
1004
+ <div class="slide__bg" style="background-image:url('data:image/png;base64,...')"></div>
1005
+ <div class="slide__scrim"></div>
1006
+ <div class="slide__content">
1007
+ <h2 class="slide__heading reveal">Headline Over Image</h2>
1008
+ <p class="slide__subtitle reveal">Supporting text</p>
1009
+ </div>
1010
+ </section>
1011
+ ```
1012
+
1013
+ ```css
1014
+ .slide--bleed {
1015
+ padding: 0;
1016
+ justify-content: flex-end;
1017
+ color: #ffffff;
1018
+ }
1019
+
1020
+ .slide__bg {
1021
+ position: absolute;
1022
+ inset: 0;
1023
+ background-size: cover;
1024
+ background-position: center;
1025
+ z-index: 0;
1026
+ }
1027
+
1028
+ .slide__scrim {
1029
+ position: absolute;
1030
+ inset: 0;
1031
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.1) 50%, transparent 100%);
1032
+ z-index: 1;
1033
+ }
1034
+
1035
+ .slide--bleed .slide__content {
1036
+ position: relative;
1037
+ z-index: 2;
1038
+ padding: clamp(40px, 6vh, 80px) clamp(40px, 8vw, 120px);
1039
+ }
1040
+
1041
+ /* When no generated image, use a bold CSS gradient background */
1042
+ .slide__bg--gradient {
1043
+ background: linear-gradient(135deg, var(--accent) 0%, color-mix(in srgb, var(--accent) 60%, var(--bg) 40%) 100%);
1044
+ }
1045
+ ```
1046
+
1047
+ ## Decorative SVG Elements
1048
+
1049
+ Inline SVG accents lift slides from functional to editorial. Use sparingly — one or two per slide, never on every slide.
1050
+
1051
+ ### Corner Accent
1052
+
1053
+ ```html
1054
+ <!-- Top-right corner mark -->
1055
+ <svg class="slide__decor slide__decor--corner" width="120" height="120" viewBox="0 0 120 120">
1056
+ <line x1="120" y1="0" x2="120" y2="40" stroke="var(--accent)" stroke-width="2" opacity="0.2"/>
1057
+ <line x1="80" y1="0" x2="120" y2="0" stroke="var(--accent)" stroke-width="2" opacity="0.2"/>
1058
+ </svg>
1059
+ ```
1060
+
1061
+ ```css
1062
+ .slide__decor {
1063
+ position: absolute;
1064
+ pointer-events: none;
1065
+ z-index: 0;
1066
+ }
1067
+
1068
+ .slide__decor--corner {
1069
+ top: 0;
1070
+ right: 0;
1071
+ }
1072
+ ```
1073
+
1074
+ ### Section Divider Mark
1075
+
1076
+ ```html
1077
+ <!-- Horizontal rule with diamond -->
1078
+ <svg class="slide__decor slide__decor--divider" width="200" height="20" viewBox="0 0 200 20">
1079
+ <line x1="0" y1="10" x2="85" y2="10" stroke="var(--accent)" stroke-width="1" opacity="0.3"/>
1080
+ <rect x="92" y="3" width="14" height="14" transform="rotate(45 99 10)" fill="none" stroke="var(--accent)" stroke-width="1" opacity="0.3"/>
1081
+ <line x1="115" y1="10" x2="200" y2="10" stroke="var(--accent)" stroke-width="1" opacity="0.3"/>
1082
+ </svg>
1083
+ ```
1084
+
1085
+ ### Geometric Background Pattern
1086
+
1087
+ ```css
1088
+ /* Faint grid dots behind a slide */
1089
+ .slide--with-grid::before {
1090
+ content: '';
1091
+ position: absolute;
1092
+ inset: 0;
1093
+ background-image: radial-gradient(circle, var(--border) 1px, transparent 1px);
1094
+ background-size: 32px 32px;
1095
+ opacity: 0.5;
1096
+ pointer-events: none;
1097
+ z-index: 0;
1098
+ }
1099
+ ```
1100
+
1101
+ ### Per-Slide Background Variation
1102
+
1103
+ Vary gradient direction and accent glow position across slides to create visual rhythm. Don't use a uniform background for every slide.
1104
+
1105
+ ```css
1106
+ /* Vary these per slide via inline style or nth-child */
1107
+ .slide:nth-child(odd) {
1108
+ background-image: radial-gradient(ellipse at 20% 80%, var(--accent-dim) 0%, transparent 50%);
1109
+ }
1110
+
1111
+ .slide:nth-child(even) {
1112
+ background-image: radial-gradient(ellipse at 80% 20%, var(--accent-dim) 0%, transparent 50%);
1113
+ }
1114
+ ```
1115
+
1116
+ ## Proactive Imagery
1117
+
1118
+ Slides should reach for visuals before defaulting to text alone. If a slide could be more compelling with an image, chart, or diagram, add one.
1119
+
1120
+ **surf-cli integration:** Check `which surf` at the start of every slide deck generation. If available, **generate 2–4 images minimum** for any deck over 10 slides. This is not optional when surf is available — a deck with AI-generated imagery is dramatically more compelling than one with only CSS gradients. Target these slides in priority order:
1121
+
1122
+ 1. **Title slide** (always): background image that sets the deck's visual tone. Match the topic and palette. Use `--aspect-ratio 16:9`. Prompt example: "abstract dark geometric pattern with green accent lines, technical and minimal" for Terminal Mono preset.
1123
+ 2. **Full-bleed slide** (always if deck has one): immersive background for the deck's visual anchor moment. Style should match the preset — photo-realistic for Midnight Editorial, abstract/geometric for Swiss Clean, circuit-board or terminal aesthetic for Terminal Mono.
1124
+ 3. **Content slides with conceptual topics** (1–2 if the deck has room): illustration in the `.slide__aside` area for slides about abstract concepts. Use `--aspect-ratio 1:1`.
1125
+
1126
+ **Generate images before writing HTML** so they're ready to embed. The workflow:
1127
+
1128
+ ```bash
1129
+ # Check availability
1130
+ which surf
1131
+
1132
+ # Generate (one per target slide)
1133
+ surf gemini "descriptive prompt matching deck palette" --generate-image /tmp/ve-slide-title.png --aspect-ratio 16:9
1134
+
1135
+ # Base64 encode for self-containment (macOS)
1136
+ TITLE_IMG=$(base64 -i /tmp/ve-slide-title.png)
1137
+ # Linux: TITLE_IMG=$(base64 -w 0 /tmp/ve-slide-title.png)
1138
+
1139
+ # Embed in the slide
1140
+ # <div class="slide__bg" style="background-image:url('data:image/png;base64,${TITLE_IMG}')"></div>
1141
+
1142
+ # Clean up
1143
+ rm /tmp/ve-slide-title.png
1144
+ ```
1145
+
1146
+ **Prompt craft for slides:** Be specific about style, dominant colors, and mood. Pull colors from the preset's CSS variables. Examples:
1147
+ - Terminal Mono: "dark abstract circuit board pattern, green (#50fa7b) traces on near-black (#0a0e14), minimal, technical"
1148
+ - Midnight Editorial: "deep navy abstract composition, warm gold accent light, cinematic depth of field, premium editorial feel"
1149
+ - Warm Signal: "warm cream textured paper with terracotta geometric accents, confident modern design"
1150
+
1151
+ **When surf fails or isn't available:** Degrade gracefully to CSS gradients and SVG decorations. Use the `.slide__bg--gradient` pattern with bold `linear-gradient` or `radial-gradient` backgrounds. The deck should stand on its own visually without generated images — they enhance, they don't carry. Note the fallback in an HTML comment (`<!-- surf unavailable, using CSS gradient fallback -->`) so future edits know to retry.
1152
+
1153
+ **Inline data visualizations:** Proactively add SVG sparklines next to numbers, mini-charts on dashboard slides, and small Mermaid diagrams on split slides even when not explicitly requested. A number with a sparkline next to it tells a better story than a number alone.
1154
+
1155
+ **When to skip images:** If surf isn't available, degrade gracefully — use CSS gradients and SVG decorations instead. Never error on missing surf. Pure structural or data-heavy decks (code reviews, table comparisons) may not need generated images.
1156
+
1157
+ ## Compositional Variety
1158
+
1159
+ Consecutive slides must vary their spatial approach. Three centered slides in a row means push one off-axis.
1160
+
1161
+ **Composition patterns to alternate between:**
1162
+ - Centered (title slides, quotes)
1163
+ - Left-heavy: content on the left 60%, breathing room on the right
1164
+ - Right-heavy: content on the right 60%, visual or whitespace on the left
1165
+ - Edge-aligned: content pushed to bottom or top, large empty space opposite
1166
+ - Split: two distinct panels filling the viewport
1167
+ - Full-bleed: background dominates, minimal overlaid text
1168
+
1169
+ The agent should plan the slide sequence considering layout rhythm, not just content order. When outlining a deck, assign a composition to each slide before writing HTML.
1170
+
1171
+ ## Presentation Readability
1172
+
1173
+ Slides get projected, screen-shared, viewed at distance. Design accordingly:
1174
+
1175
+ - **Minimum body text: 16px.** Nothing smaller except labels and captions.
1176
+ - **One focal point per slide.** Not three competing elements.
1177
+ - **Higher contrast than pages.** Dimmed text (`--text-dim`) should still be easily readable at distance — test against the background.
1178
+ - **Nav chrome opacity.** Dots and progress bar must be visible on any slide background (light or dark) without being distracting. Use the backdrop blur or text-shadow approach from the Nav Chrome section.
1179
+ - **Simpler Mermaid diagrams.** Max 8–10 nodes, 18px+ labels, 2px+ edges. The diagram should be readable without zoom at presentation distance. Zoom controls remain available for detail inspection.
1180
+
1181
+ ## Content Density Limits
1182
+
1183
+ Each slide must fit in exactly 100dvh. If content exceeds these limits, the agent splits across multiple slides — never scrolls within a slide.
1184
+
1185
+ | Slide type | Max content |
1186
+ |-----------|-------------|
1187
+ | Title | 1 heading + 1 subtitle |
1188
+ | Section Divider | 1 number + 1 heading + optional subhead |
1189
+ | Content | 1 heading + 5–6 bullets (max 2 lines each) |
1190
+ | Split | 1 heading + 2 panels, each follows its inner type's limits |
1191
+ | Diagram | 1 heading + 1 Mermaid diagram (max 8–10 nodes) |
1192
+ | Dashboard | 1 heading + 6 KPI cards. Hero values ≤6 chars (numbers, %, short labels). Longer strings belong in the label row. |
1193
+ | Table | 1 heading + 8 rows; overflow paginates to next slide |
1194
+ | Code | 1 heading + 10 lines of code |
1195
+ | Quote | 1 short quote (~25 words / ~150 chars max) + 1 attribution. Longer quotes are content slides, not quote slides. |
1196
+ | Full-Bleed | 1 heading + 1 subtitle over background |
1197
+
1198
+ ## Responsive Height Breakpoints
1199
+
1200
+ Height-based scaling is more critical for slides than width. Each breakpoint progressively reduces padding, font sizes, and hides decorative elements.
1201
+
1202
+ ```css
1203
+ /* Compact viewports */
1204
+ @media (max-height: 700px) {
1205
+ .slide {
1206
+ padding: clamp(24px, 4vh, 40px) clamp(32px, 6vw, 80px);
1207
+ }
1208
+ .slide__display { font-size: clamp(36px, 8vw, 72px); }
1209
+ .slide--divider .slide__number { font-size: clamp(80px, 16vw, 160px); }
1210
+ }
1211
+
1212
+ /* Small tablets / landscape phones */
1213
+ @media (max-height: 600px) {
1214
+ .slide__decor { display: none; } /* hide decorative SVGs */
1215
+ .slide--quote { padding: clamp(32px, 6vh, 60px) clamp(40px, 8vw, 100px); }
1216
+ .slide__quote-mark { display: none; }
1217
+ }
1218
+
1219
+ /* Aggressive: landscape phones */
1220
+ @media (max-height: 500px) {
1221
+ .slide {
1222
+ padding: clamp(16px, 3vh, 24px) clamp(24px, 5vw, 48px);
1223
+ }
1224
+ .deck-dots { display: none; } /* dots clutter tiny viewports */
1225
+ .slide__display { font-size: clamp(28px, 7vw, 48px); }
1226
+ }
1227
+
1228
+ /* Width breakpoint for grids */
1229
+ @media (max-width: 768px) {
1230
+ .slide--content .slide__inner { grid-template-columns: 1fr; }
1231
+ .slide--content .slide__aside { display: none; }
1232
+ .slide--split .slide__panels { grid-template-columns: 1fr; }
1233
+ .slide--dashboard .slide__kpis { grid-template-columns: repeat(2, 1fr); }
1234
+ }
1235
+ ```
1236
+
1237
+ ## Curated Presets
1238
+
1239
+ Starting points the agent can riff on. Each defines a font pairing, palette, and background treatment. The agent adapts these to the content — different decks with the same preset should still feel distinct.
1240
+
1241
+ ### Midnight Editorial
1242
+
1243
+ Deep navy, serif display, warm gold accents. Cinematic, premium. Dark-first.
1244
+
1245
+ ```css
1246
+ :root {
1247
+ --font-body: 'Instrument Serif', Georgia, serif;
1248
+ --font-mono: 'JetBrains Mono', 'SF Mono', monospace;
1249
+ --bg: #0f1729;
1250
+ --surface: #162040;
1251
+ --surface2: #1d2b52;
1252
+ --surface-elevated: #243362;
1253
+ --border: rgba(200, 180, 140, 0.08);
1254
+ --border-bright: rgba(200, 180, 140, 0.16);
1255
+ --text: #e8e4d8;
1256
+ --text-dim: #9a9484;
1257
+ --accent: #d4a73a;
1258
+ --accent-dim: rgba(212, 167, 58, 0.1);
1259
+ --code-bg: #0a0f1e;
1260
+ --code-text: #d4d0c4;
1261
+ }
1262
+ @media (prefers-color-scheme: light) {
1263
+ :root {
1264
+ --bg: #faf8f2;
1265
+ --surface: #ffffff;
1266
+ --surface2: #f5f0e6;
1267
+ --surface-elevated: #fffdf5;
1268
+ --border: rgba(30, 30, 50, 0.08);
1269
+ --border-bright: rgba(30, 30, 50, 0.16);
1270
+ --text: #1a1814;
1271
+ --text-dim: #7a7468;
1272
+ --accent: #b8860b;
1273
+ --accent-dim: rgba(184, 134, 11, 0.08);
1274
+ --code-bg: #2a2520;
1275
+ --code-text: #e8e4d8;
1276
+ }
1277
+ }
1278
+ ```
1279
+
1280
+ Background: radial gold glow at top center. Decorative corner marks in gold. Title slides use dramatic serif at max scale.
1281
+
1282
+ ### Warm Signal
1283
+
1284
+ Cream paper, bold sans, terracotta/coral accents. Confident and modern. Light-first.
1285
+
1286
+ ```css
1287
+ :root {
1288
+ --font-body: 'Plus Jakarta Sans', system-ui, sans-serif;
1289
+ --font-mono: 'Azeret Mono', 'SF Mono', monospace;
1290
+ --bg: #faf6f0;
1291
+ --surface: #ffffff;
1292
+ --surface2: #f5ece0;
1293
+ --surface-elevated: #fffdf5;
1294
+ --border: rgba(60, 40, 20, 0.08);
1295
+ --border-bright: rgba(60, 40, 20, 0.16);
1296
+ --text: #2c2a25;
1297
+ --text-dim: #7c756a;
1298
+ --accent: #c2410c;
1299
+ --accent-dim: rgba(194, 65, 12, 0.08);
1300
+ --code-bg: #2c2520;
1301
+ --code-text: #f5ece0;
1302
+ }
1303
+ @media (prefers-color-scheme: dark) {
1304
+ :root {
1305
+ --bg: #1c1916;
1306
+ --surface: #262220;
1307
+ --surface2: #302b28;
1308
+ --surface-elevated: #3a3430;
1309
+ --border: rgba(200, 180, 160, 0.08);
1310
+ --border-bright: rgba(200, 180, 160, 0.16);
1311
+ --text: #f0e8dc;
1312
+ --text-dim: #a09888;
1313
+ --accent: #e85d2a;
1314
+ --accent-dim: rgba(232, 93, 42, 0.1);
1315
+ --code-bg: #141210;
1316
+ --code-text: #f0e8dc;
1317
+ }
1318
+ }
1319
+ ```
1320
+
1321
+ Background: warm radial glow at bottom left. Terracotta accent borders on cards. Section divider numbers in ultra-light coral.
1322
+
1323
+ ### Terminal Mono
1324
+
1325
+ Dark, monospace everything, green/cyan accents, faint grid. Developer-native. Dark-first.
1326
+
1327
+ ```css
1328
+ :root {
1329
+ --font-body: 'Geist Mono', 'SF Mono', Consolas, monospace;
1330
+ --font-mono: 'Geist Mono', 'SF Mono', Consolas, monospace;
1331
+ --bg: #0a0e14;
1332
+ --surface: #12161e;
1333
+ --surface2: #1a1f2a;
1334
+ --surface-elevated: #222836;
1335
+ --border: rgba(80, 250, 123, 0.06);
1336
+ --border-bright: rgba(80, 250, 123, 0.12);
1337
+ --text: #c8d6e5;
1338
+ --text-dim: #5a6a7a;
1339
+ --accent: #50fa7b;
1340
+ --accent-dim: rgba(80, 250, 123, 0.08);
1341
+ --code-bg: #060a10;
1342
+ --code-text: #c8d6e5;
1343
+ }
1344
+ @media (prefers-color-scheme: light) {
1345
+ :root {
1346
+ --bg: #f4f6f8;
1347
+ --surface: #ffffff;
1348
+ --surface2: #eaecf0;
1349
+ --surface-elevated: #f8f9fa;
1350
+ --border: rgba(0, 80, 40, 0.08);
1351
+ --border-bright: rgba(0, 80, 40, 0.16);
1352
+ --text: #1a2332;
1353
+ --text-dim: #5a6a7a;
1354
+ --accent: #0d7a3e;
1355
+ --accent-dim: rgba(13, 122, 62, 0.08);
1356
+ --code-bg: #1a2332;
1357
+ --code-text: #c8d6e5;
1358
+ }
1359
+ }
1360
+ ```
1361
+
1362
+ Background: faint dot grid. Everything in mono. Title slides use large weight-400 mono instead of bold display. Code slides feel native.
1363
+
1364
+ ### Swiss Clean
1365
+
1366
+ White, geometric sans, single bold accent, visible grid. Minimal and precise. Light-first.
1367
+
1368
+ ```css
1369
+ :root {
1370
+ --font-body: 'DM Sans', system-ui, sans-serif;
1371
+ --font-mono: 'Fira Code', 'SF Mono', monospace;
1372
+ --bg: #ffffff;
1373
+ --surface: #f8f8f8;
1374
+ --surface2: #f0f0f0;
1375
+ --surface-elevated: #ffffff;
1376
+ --border: rgba(0, 0, 0, 0.08);
1377
+ --border-bright: rgba(0, 0, 0, 0.16);
1378
+ --text: #111111;
1379
+ --text-dim: #666666;
1380
+ --accent: #0055ff;
1381
+ --accent-dim: rgba(0, 85, 255, 0.06);
1382
+ --code-bg: #18181b;
1383
+ --code-text: #e4e4e7;
1384
+ }
1385
+ @media (prefers-color-scheme: dark) {
1386
+ :root {
1387
+ --bg: #111111;
1388
+ --surface: #1a1a1a;
1389
+ --surface2: #222222;
1390
+ --surface-elevated: #2a2a2a;
1391
+ --border: rgba(255, 255, 255, 0.08);
1392
+ --border-bright: rgba(255, 255, 255, 0.16);
1393
+ --text: #f0f0f0;
1394
+ --text-dim: #888888;
1395
+ --accent: #3b82f6;
1396
+ --accent-dim: rgba(59, 130, 246, 0.08);
1397
+ --code-bg: #0a0a0a;
1398
+ --code-text: #e4e4e7;
1399
+ }
1400
+ }
1401
+ ```
1402
+
1403
+ Background: clean white or near-black, no gradients. Visible grid lines (the `--with-grid` pattern). Tight geometric layouts. Single accent color used sparingly for emphasis. Data-heavy and analytical content shines here.