@jack200714/mafw 4.5.1 → 4.5.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 (320) hide show
  1. package/README.md +5 -2
  2. package/bin/mafw.js +16 -14
  3. package/package.json +1 -7
  4. package/.opencode/mafw/.backup-v61/parametric/base-skill-manifest.yaml +0 -2
  5. package/.opencode/mafw/STATUS.md +0 -6
  6. package/.opencode/mafw/ledger.md +0 -20
  7. package/.opencode/mafw/memory/.harmonic_index.json +0 -28
  8. package/.opencode/mafw/memory/.review_queue.json +0 -1
  9. package/.opencode/mafw/memory/tier3/parametric.json +0 -13
  10. package/.opencode/mafw/memory/tier3/test-goal.json +0 -19
  11. package/.opencode/mafw/memory-index.json +0 -6
  12. package/.opencode/mafw/parametric/base-skill-manifest.yaml +0 -2
  13. package/.opencode/mafw/waves.json +0 -4
  14. package/.opencode/opencode.json +0 -3
  15. package/.opencode/plugins/mafw-plugin.ts +0 -8
  16. package/.opencode/skills/banner-design/SKILL.md +0 -196
  17. package/.opencode/skills/banner-design/references/banner-sizes-and-styles.md +0 -118
  18. package/.opencode/skills/brand/SKILL.md +0 -97
  19. package/.opencode/skills/brand/references/approval-checklist.md +0 -169
  20. package/.opencode/skills/brand/references/asset-organization.md +0 -157
  21. package/.opencode/skills/brand/references/brand-guideline-template.md +0 -140
  22. package/.opencode/skills/brand/references/color-palette-management.md +0 -186
  23. package/.opencode/skills/brand/references/consistency-checklist.md +0 -94
  24. package/.opencode/skills/brand/references/logo-usage-rules.md +0 -185
  25. package/.opencode/skills/brand/references/messaging-framework.md +0 -85
  26. package/.opencode/skills/brand/references/typography-specifications.md +0 -214
  27. package/.opencode/skills/brand/references/update.md +0 -118
  28. package/.opencode/skills/brand/references/visual-identity.md +0 -96
  29. package/.opencode/skills/brand/references/voice-framework.md +0 -88
  30. package/.opencode/skills/brand/scripts/extract-colors.cjs +0 -341
  31. package/.opencode/skills/brand/scripts/inject-brand-context.cjs +0 -349
  32. package/.opencode/skills/brand/scripts/sync-brand-to-tokens.cjs +0 -248
  33. package/.opencode/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +0 -52
  34. package/.opencode/skills/brand/scripts/validate-asset.cjs +0 -387
  35. package/.opencode/skills/brand/templates/brand-guidelines-starter.md +0 -275
  36. package/.opencode/skills/design/SKILL.md +0 -313
  37. package/.opencode/skills/design/data/cip/deliverables.csv +0 -51
  38. package/.opencode/skills/design/data/cip/industries.csv +0 -21
  39. package/.opencode/skills/design/data/cip/mockup-contexts.csv +0 -21
  40. package/.opencode/skills/design/data/cip/styles.csv +0 -21
  41. package/.opencode/skills/design/data/icon/styles.csv +0 -16
  42. package/.opencode/skills/design/data/logo/colors.csv +0 -56
  43. package/.opencode/skills/design/data/logo/industries.csv +0 -56
  44. package/.opencode/skills/design/data/logo/styles.csv +0 -56
  45. package/.opencode/skills/design/references/banner-sizes-and-styles.md +0 -118
  46. package/.opencode/skills/design/references/cip-deliverable-guide.md +0 -95
  47. package/.opencode/skills/design/references/cip-design.md +0 -121
  48. package/.opencode/skills/design/references/cip-prompt-engineering.md +0 -84
  49. package/.opencode/skills/design/references/cip-style-guide.md +0 -68
  50. package/.opencode/skills/design/references/design-routing.md +0 -207
  51. package/.opencode/skills/design/references/icon-design.md +0 -122
  52. package/.opencode/skills/design/references/logo-color-psychology.md +0 -101
  53. package/.opencode/skills/design/references/logo-design.md +0 -92
  54. package/.opencode/skills/design/references/logo-prompt-engineering.md +0 -158
  55. package/.opencode/skills/design/references/logo-style-guide.md +0 -109
  56. package/.opencode/skills/design/references/slides-copywriting-formulas.md +0 -84
  57. package/.opencode/skills/design/references/slides-create.md +0 -4
  58. package/.opencode/skills/design/references/slides-html-template.md +0 -295
  59. package/.opencode/skills/design/references/slides-layout-patterns.md +0 -137
  60. package/.opencode/skills/design/references/slides-strategies.md +0 -94
  61. package/.opencode/skills/design/references/slides.md +0 -42
  62. package/.opencode/skills/design/references/social-photos-design.md +0 -329
  63. package/.opencode/skills/design/scripts/cip/core.py +0 -215
  64. package/.opencode/skills/design/scripts/cip/generate.py +0 -484
  65. package/.opencode/skills/design/scripts/cip/render-html.py +0 -424
  66. package/.opencode/skills/design/scripts/cip/search.py +0 -127
  67. package/.opencode/skills/design/scripts/icon/generate.py +0 -487
  68. package/.opencode/skills/design/scripts/logo/core.py +0 -175
  69. package/.opencode/skills/design/scripts/logo/generate.py +0 -362
  70. package/.opencode/skills/design/scripts/logo/search.py +0 -114
  71. package/.opencode/skills/design-system/SKILL.md +0 -244
  72. package/.opencode/skills/design-system/data/slide-backgrounds.csv +0 -11
  73. package/.opencode/skills/design-system/data/slide-charts.csv +0 -26
  74. package/.opencode/skills/design-system/data/slide-color-logic.csv +0 -14
  75. package/.opencode/skills/design-system/data/slide-copy.csv +0 -26
  76. package/.opencode/skills/design-system/data/slide-layout-logic.csv +0 -16
  77. package/.opencode/skills/design-system/data/slide-layouts.csv +0 -26
  78. package/.opencode/skills/design-system/data/slide-strategies.csv +0 -16
  79. package/.opencode/skills/design-system/data/slide-typography.csv +0 -15
  80. package/.opencode/skills/design-system/references/component-specs.md +0 -236
  81. package/.opencode/skills/design-system/references/component-tokens.md +0 -214
  82. package/.opencode/skills/design-system/references/primitive-tokens.md +0 -203
  83. package/.opencode/skills/design-system/references/semantic-tokens.md +0 -215
  84. package/.opencode/skills/design-system/references/states-and-variants.md +0 -241
  85. package/.opencode/skills/design-system/references/tailwind-integration.md +0 -251
  86. package/.opencode/skills/design-system/references/token-architecture.md +0 -224
  87. package/.opencode/skills/design-system/scripts/embed-tokens.cjs +0 -99
  88. package/.opencode/skills/design-system/scripts/fetch-background.py +0 -317
  89. package/.opencode/skills/design-system/scripts/generate-slide.py +0 -770
  90. package/.opencode/skills/design-system/scripts/generate-tokens.cjs +0 -205
  91. package/.opencode/skills/design-system/scripts/html-token-validator.py +0 -327
  92. package/.opencode/skills/design-system/scripts/search-slides.py +0 -218
  93. package/.opencode/skills/design-system/scripts/slide-token-validator.py +0 -35
  94. package/.opencode/skills/design-system/scripts/slide_search_core.py +0 -453
  95. package/.opencode/skills/design-system/scripts/tests/test_validate_tokens.py +0 -48
  96. package/.opencode/skills/design-system/scripts/validate-tokens.cjs +0 -246
  97. package/.opencode/skills/design-system/templates/design-tokens-starter.json +0 -143
  98. package/.opencode/skills/frontend-design/LICENSE.txt +0 -21
  99. package/.opencode/skills/frontend-design/SKILL.md +0 -115
  100. package/.opencode/skills/mafw-automation-interview/SKILL.md +0 -43
  101. package/.opencode/skills/mafw-cli-scanner/SKILL.md +0 -37
  102. package/.opencode/skills/mafw-compression-verifier/SKILL.md +0 -52
  103. package/.opencode/skills/mafw-desktop-inspector/SKILL.md +0 -170
  104. package/.opencode/skills/mafw-execute/SKILL.md +0 -62
  105. package/.opencode/skills/mafw-gateway-restart/SKILL.md +0 -87
  106. package/.opencode/skills/mafw-github-scanner/SKILL.md +0 -38
  107. package/.opencode/skills/mafw-goal/SKILL.md +0 -76
  108. package/.opencode/skills/mafw-interview/SKILL.md +0 -68
  109. package/.opencode/skills/mafw-memory-extractor/SKILL.md +0 -65
  110. package/.opencode/skills/mafw-plan/SKILL.md +0 -58
  111. package/.opencode/skills/mafw-review/SKILL.md +0 -85
  112. package/.opencode/skills/runtime-plugin-authoring/SKILL.md +0 -791
  113. package/.opencode/skills/slides/SKILL.md +0 -40
  114. package/.opencode/skills/slides/references/copywriting-formulas.md +0 -84
  115. package/.opencode/skills/slides/references/create.md +0 -4
  116. package/.opencode/skills/slides/references/html-template.md +0 -295
  117. package/.opencode/skills/slides/references/layout-patterns.md +0 -137
  118. package/.opencode/skills/slides/references/slide-strategies.md +0 -94
  119. package/.opencode/skills/ui-styling/LICENSE.txt +0 -202
  120. package/.opencode/skills/ui-styling/SKILL.md +0 -324
  121. package/.opencode/skills/ui-styling/references/canvas-design-system.md +0 -320
  122. package/.opencode/skills/ui-styling/references/shadcn-accessibility.md +0 -471
  123. package/.opencode/skills/ui-styling/references/shadcn-components.md +0 -424
  124. package/.opencode/skills/ui-styling/references/shadcn-theming.md +0 -373
  125. package/.opencode/skills/ui-styling/references/tailwind-customization.md +0 -483
  126. package/.opencode/skills/ui-styling/references/tailwind-responsive.md +0 -382
  127. package/.opencode/skills/ui-styling/references/tailwind-utilities.md +0 -455
  128. package/.opencode/skills/ui-styling/scripts/requirements.txt +0 -17
  129. package/.opencode/skills/ui-styling/scripts/shadcn_add.py +0 -308
  130. package/.opencode/skills/ui-styling/scripts/tailwind_config_gen.py +0 -473
  131. package/.opencode/skills/ui-styling/scripts/tests/coverage-ui.json +0 -1
  132. package/.opencode/skills/ui-styling/scripts/tests/requirements.txt +0 -3
  133. package/.opencode/skills/ui-styling/scripts/tests/test_shadcn_add.py +0 -266
  134. package/.opencode/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +0 -394
  135. package/.opencode/skills/ui-ux-pro-max/SKILL.md +0 -388
  136. package/.opencode/skills/ui-ux-pro-max/data/app-interface.csv +0 -31
  137. package/.opencode/skills/ui-ux-pro-max/data/charts.csv +0 -26
  138. package/.opencode/skills/ui-ux-pro-max/data/colors.csv +0 -193
  139. package/.opencode/skills/ui-ux-pro-max/data/google-fonts.csv +0 -1924
  140. package/.opencode/skills/ui-ux-pro-max/data/icons.csv +0 -106
  141. package/.opencode/skills/ui-ux-pro-max/data/landing.csv +0 -35
  142. package/.opencode/skills/ui-ux-pro-max/data/motion.csv +0 -17
  143. package/.opencode/skills/ui-ux-pro-max/data/products.csv +0 -193
  144. package/.opencode/skills/ui-ux-pro-max/data/react-performance.csv +0 -45
  145. package/.opencode/skills/ui-ux-pro-max/data/stacks/angular.csv +0 -51
  146. package/.opencode/skills/ui-ux-pro-max/data/stacks/astro.csv +0 -54
  147. package/.opencode/skills/ui-ux-pro-max/data/stacks/avalonia.csv +0 -57
  148. package/.opencode/skills/ui-ux-pro-max/data/stacks/flutter.csv +0 -53
  149. package/.opencode/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +0 -56
  150. package/.opencode/skills/ui-ux-pro-max/data/stacks/javafx.csv +0 -76
  151. package/.opencode/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +0 -53
  152. package/.opencode/skills/ui-ux-pro-max/data/stacks/laravel.csv +0 -51
  153. package/.opencode/skills/ui-ux-pro-max/data/stacks/nextjs.csv +0 -53
  154. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +0 -71
  155. package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +0 -59
  156. package/.opencode/skills/ui-ux-pro-max/data/stacks/react-native.csv +0 -52
  157. package/.opencode/skills/ui-ux-pro-max/data/stacks/react.csv +0 -54
  158. package/.opencode/skills/ui-ux-pro-max/data/stacks/shadcn.csv +0 -61
  159. package/.opencode/skills/ui-ux-pro-max/data/stacks/svelte.csv +0 -54
  160. package/.opencode/skills/ui-ux-pro-max/data/stacks/swiftui.csv +0 -51
  161. package/.opencode/skills/ui-ux-pro-max/data/stacks/threejs.csv +0 -54
  162. package/.opencode/skills/ui-ux-pro-max/data/stacks/uno.csv +0 -60
  163. package/.opencode/skills/ui-ux-pro-max/data/stacks/uwp.csv +0 -56
  164. package/.opencode/skills/ui-ux-pro-max/data/stacks/vue.csv +0 -50
  165. package/.opencode/skills/ui-ux-pro-max/data/stacks/winui.csv +0 -60
  166. package/.opencode/skills/ui-ux-pro-max/data/stacks/wpf.csv +0 -57
  167. package/.opencode/skills/ui-ux-pro-max/data/styles.csv +0 -85
  168. package/.opencode/skills/ui-ux-pro-max/data/typography.csv +0 -75
  169. package/.opencode/skills/ui-ux-pro-max/data/ui-reasoning.csv +0 -162
  170. package/.opencode/skills/ui-ux-pro-max/data/ux-guidelines.csv +0 -100
  171. package/.opencode/skills/ui-ux-pro-max/scripts/core.py +0 -464
  172. package/.opencode/skills/ui-ux-pro-max/scripts/design_system.py +0 -1479
  173. package/.opencode/skills/ui-ux-pro-max/scripts/search.py +0 -162
  174. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_core.py +0 -134
  175. package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +0 -159
  176. package/.opencode/skills/ui-ux-pro-max/scripts/validate_data.py +0 -114
  177. package/dist/hooks/bash-python-guide.d.ts +0 -4
  178. package/dist/hooks/bash-python-guide.d.ts.map +0 -1
  179. package/dist/hooks/bash-python-guide.js +0 -67
  180. package/dist/hooks/bash-python-guide.js.map +0 -1
  181. package/dist/hooks/handoff.d.ts +0 -9
  182. package/dist/hooks/handoff.d.ts.map +0 -1
  183. package/dist/hooks/handoff.js +0 -8
  184. package/dist/hooks/handoff.js.map +0 -1
  185. package/dist/hooks/hook-manager.d.ts +0 -32
  186. package/dist/hooks/hook-manager.d.ts.map +0 -1
  187. package/dist/hooks/hook-manager.js +0 -91
  188. package/dist/hooks/hook-manager.js.map +0 -1
  189. package/dist/hooks/llm-after.d.ts +0 -8
  190. package/dist/hooks/llm-after.d.ts.map +0 -1
  191. package/dist/hooks/llm-after.js +0 -8
  192. package/dist/hooks/llm-after.js.map +0 -1
  193. package/dist/hooks/media-ingest.d.ts +0 -43
  194. package/dist/hooks/media-ingest.d.ts.map +0 -1
  195. package/dist/hooks/media-ingest.js +0 -269
  196. package/dist/hooks/media-ingest.js.map +0 -1
  197. package/dist/hooks/memory-guide.d.ts +0 -3
  198. package/dist/hooks/memory-guide.d.ts.map +0 -1
  199. package/dist/hooks/memory-guide.js +0 -47
  200. package/dist/hooks/memory-guide.js.map +0 -1
  201. package/dist/hooks/observation-capture.d.ts +0 -17
  202. package/dist/hooks/observation-capture.d.ts.map +0 -1
  203. package/dist/hooks/observation-capture.js +0 -20
  204. package/dist/hooks/observation-capture.js.map +0 -1
  205. package/dist/hooks/session-compacting.d.ts +0 -6
  206. package/dist/hooks/session-compacting.d.ts.map +0 -1
  207. package/dist/hooks/session-compacting.js +0 -6
  208. package/dist/hooks/session-compacting.js.map +0 -1
  209. package/dist/hooks/session-ending.d.ts +0 -20
  210. package/dist/hooks/session-ending.d.ts.map +0 -1
  211. package/dist/hooks/session-ending.js +0 -113
  212. package/dist/hooks/session-ending.js.map +0 -1
  213. package/dist/hooks/session-recall.d.ts +0 -2
  214. package/dist/hooks/session-recall.d.ts.map +0 -1
  215. package/dist/hooks/session-recall.js +0 -103
  216. package/dist/hooks/session-recall.js.map +0 -1
  217. package/dist/hooks/session-start.d.ts +0 -9
  218. package/dist/hooks/session-start.d.ts.map +0 -1
  219. package/dist/hooks/session-start.js +0 -42
  220. package/dist/hooks/session-start.js.map +0 -1
  221. package/dist/hooks/tool-before.d.ts +0 -10
  222. package/dist/hooks/tool-before.d.ts.map +0 -1
  223. package/dist/hooks/tool-before.js +0 -6
  224. package/dist/hooks/tool-before.js.map +0 -1
  225. package/dist/hooks/tool-executed.d.ts +0 -19
  226. package/dist/hooks/tool-executed.d.ts.map +0 -1
  227. package/dist/hooks/tool-executed.js +0 -12
  228. package/dist/hooks/tool-executed.js.map +0 -1
  229. package/dist/hooks/user-profile.d.ts +0 -2
  230. package/dist/hooks/user-profile.d.ts.map +0 -1
  231. package/dist/hooks/user-profile.js +0 -30
  232. package/dist/hooks/user-profile.js.map +0 -1
  233. package/dist/hooks/user-prompt.d.ts +0 -9
  234. package/dist/hooks/user-prompt.d.ts.map +0 -1
  235. package/dist/hooks/user-prompt.js +0 -8
  236. package/dist/hooks/user-prompt.js.map +0 -1
  237. package/dist/hooks/voice-guide.d.ts +0 -8
  238. package/dist/hooks/voice-guide.d.ts.map +0 -1
  239. package/dist/hooks/voice-guide.js +0 -101
  240. package/dist/hooks/voice-guide.js.map +0 -1
  241. package/dist/plugin.d.ts +0 -55
  242. package/dist/plugin.d.ts.map +0 -1
  243. package/dist/plugin.js +0 -342
  244. package/dist/plugin.js.map +0 -1
  245. package/dist/tools/add-memory.d.ts +0 -17
  246. package/dist/tools/add-memory.d.ts.map +0 -1
  247. package/dist/tools/add-memory.js +0 -60
  248. package/dist/tools/add-memory.js.map +0 -1
  249. package/dist/tools/media-ask.d.ts +0 -14
  250. package/dist/tools/media-ask.d.ts.map +0 -1
  251. package/dist/tools/media-ask.js +0 -224
  252. package/dist/tools/media-ask.js.map +0 -1
  253. package/dist/tools/media-speak.d.ts +0 -16
  254. package/dist/tools/media-speak.d.ts.map +0 -1
  255. package/dist/tools/media-speak.js +0 -78
  256. package/dist/tools/media-speak.js.map +0 -1
  257. package/dist/tools/media-upload.d.ts +0 -14
  258. package/dist/tools/media-upload.d.ts.map +0 -1
  259. package/dist/tools/media-upload.js +0 -155
  260. package/dist/tools/media-upload.js.map +0 -1
  261. package/dist/tools/python-exec.d.ts +0 -13
  262. package/dist/tools/python-exec.d.ts.map +0 -1
  263. package/dist/tools/python-exec.js +0 -99
  264. package/dist/tools/python-exec.js.map +0 -1
  265. package/dist/tools/python-restart.d.ts +0 -11
  266. package/dist/tools/python-restart.d.ts.map +0 -1
  267. package/dist/tools/python-restart.js +0 -33
  268. package/dist/tools/python-restart.js.map +0 -1
  269. package/dist/utils/circuit-breaker.d.ts +0 -23
  270. package/dist/utils/circuit-breaker.d.ts.map +0 -1
  271. package/dist/utils/circuit-breaker.js +0 -75
  272. package/dist/utils/circuit-breaker.js.map +0 -1
  273. package/dist/utils/config-loader.d.ts +0 -20
  274. package/dist/utils/config-loader.d.ts.map +0 -1
  275. package/dist/utils/config-loader.js +0 -194
  276. package/dist/utils/config-loader.js.map +0 -1
  277. package/dist/utils/fallback.d.ts +0 -7
  278. package/dist/utils/fallback.d.ts.map +0 -1
  279. package/dist/utils/fallback.js +0 -20
  280. package/dist/utils/fallback.js.map +0 -1
  281. package/dist/utils/git.d.ts +0 -24
  282. package/dist/utils/git.d.ts.map +0 -1
  283. package/dist/utils/git.js +0 -71
  284. package/dist/utils/git.js.map +0 -1
  285. package/dist/utils/github.d.ts +0 -37
  286. package/dist/utils/github.d.ts.map +0 -1
  287. package/dist/utils/github.js +0 -53
  288. package/dist/utils/github.js.map +0 -1
  289. package/dist/utils/global-path.d.ts +0 -2
  290. package/dist/utils/global-path.d.ts.map +0 -1
  291. package/dist/utils/global-path.js +0 -32
  292. package/dist/utils/global-path.js.map +0 -1
  293. package/dist/utils/logger.d.ts +0 -6
  294. package/dist/utils/logger.d.ts.map +0 -1
  295. package/dist/utils/logger.js +0 -69
  296. package/dist/utils/logger.js.map +0 -1
  297. package/dist/utils/obs-capture.d.ts +0 -7
  298. package/dist/utils/obs-capture.d.ts.map +0 -1
  299. package/dist/utils/obs-capture.js +0 -49
  300. package/dist/utils/obs-capture.js.map +0 -1
  301. package/dist/utils/retry.d.ts +0 -11
  302. package/dist/utils/retry.d.ts.map +0 -1
  303. package/dist/utils/retry.js +0 -45
  304. package/dist/utils/retry.js.map +0 -1
  305. package/dist/utils/self-wiring.d.ts +0 -13
  306. package/dist/utils/self-wiring.d.ts.map +0 -1
  307. package/dist/utils/self-wiring.js +0 -86
  308. package/dist/utils/self-wiring.js.map +0 -1
  309. package/dist/utils/state.d.ts +0 -106
  310. package/dist/utils/state.d.ts.map +0 -1
  311. package/dist/utils/state.js +0 -166
  312. package/dist/utils/state.js.map +0 -1
  313. package/dist/utils/status.d.ts +0 -47
  314. package/dist/utils/status.d.ts.map +0 -1
  315. package/dist/utils/status.js +0 -147
  316. package/dist/utils/status.js.map +0 -1
  317. package/dist/utils/ttl-map.d.ts +0 -17
  318. package/dist/utils/ttl-map.d.ts.map +0 -1
  319. package/dist/utils/ttl-map.js +0 -67
  320. package/dist/utils/ttl-map.js.map +0 -1
@@ -1,770 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Slide Generator - Generates HTML slides using design tokens
5
- ALL styles MUST use CSS variables from design-tokens.css
6
- NO hardcoded colors, fonts, or spacing allowed
7
- """
8
-
9
- import argparse
10
- import json
11
- from html import escape
12
- from pathlib import Path
13
- from datetime import datetime
14
-
15
-
16
- def _e(value, default=''):
17
- """HTML-escape a user-supplied value for safe embedding in HTML content."""
18
- return escape(str(value if value is not None else default))
19
-
20
-
21
- def _safe_url(url, default='#'):
22
- """Validate and escape a URL for use in href attributes.
23
-
24
- Only allows http://, https://, #, and / schemes to prevent
25
- javascript: URI injection (CWE-79).
26
- """
27
- if url and str(url).strip().lower().startswith(('http://', 'https://', '#', '/')):
28
- return escape(str(url), quote=True)
29
- return default
30
-
31
- # Paths
32
- SCRIPT_DIR = Path(__file__).parent
33
- DATA_DIR = SCRIPT_DIR.parent / "data"
34
- TOKENS_CSS = Path(__file__).resolve().parents[4] / "assets" / "design-tokens.css"
35
- TOKENS_JSON = Path(__file__).resolve().parents[4] / "assets" / "design-tokens.json"
36
- OUTPUT_DIR = Path(__file__).resolve().parents[4] / "assets" / "designs" / "slides"
37
-
38
- # ============ BRAND-COMPLIANT SLIDE TEMPLATE ============
39
- # ALL values reference CSS variables from design-tokens.css
40
-
41
- SLIDE_TEMPLATE = '''<!DOCTYPE html>
42
- <html lang="en" data-theme="dark">
43
- <head>
44
- <meta charset="UTF-8">
45
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
46
- <title>{title}</title>
47
-
48
- <!-- Brand Fonts -->
49
- <link rel="preconnect" href="https://fonts.googleapis.com">
50
- <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">
51
-
52
- <!-- Design Tokens - SINGLE SOURCE OF TRUTH -->
53
- <link rel="stylesheet" href="{tokens_css_path}">
54
-
55
- <style>
56
- /* ============================================
57
- STRICT TOKEN USAGE - NO HARDCODED VALUES
58
- All styles MUST use var(--token-name)
59
- ============================================ */
60
-
61
- * {{
62
- margin: 0;
63
- padding: 0;
64
- box-sizing: border-box;
65
- }}
66
-
67
- html, body {{
68
- width: 100%;
69
- height: 100%;
70
- }}
71
-
72
- body {{
73
- font-family: var(--typography-font-body);
74
- background: var(--color-background);
75
- color: var(--color-foreground);
76
- line-height: var(--primitive-lineHeight-relaxed);
77
- }}
78
-
79
- /* Slide Container - 16:9 aspect ratio */
80
- .slide-deck {{
81
- width: 100%;
82
- max-width: 1920px;
83
- margin: 0 auto;
84
- }}
85
-
86
- .slide {{
87
- width: 100%;
88
- aspect-ratio: 16 / 9;
89
- padding: var(--slide-padding);
90
- background: var(--slide-bg);
91
- display: flex;
92
- flex-direction: column;
93
- position: relative;
94
- overflow: hidden;
95
- }}
96
-
97
- .slide + .slide {{
98
- margin-top: var(--primitive-spacing-8);
99
- }}
100
-
101
- /* Background Variants */
102
- .slide--surface {{
103
- background: var(--slide-bg-surface);
104
- }}
105
-
106
- .slide--gradient {{
107
- background: var(--slide-bg-gradient);
108
- }}
109
-
110
- .slide--glow::before {{
111
- content: '';
112
- position: absolute;
113
- top: 50%;
114
- left: 50%;
115
- transform: translate(-50%, -50%);
116
- width: 150%;
117
- height: 150%;
118
- background: var(--primitive-gradient-glow);
119
- pointer-events: none;
120
- }}
121
-
122
- /* Typography - MUST use token fonts and sizes */
123
- h1, h2, h3, h4, h5, h6 {{
124
- font-family: var(--typography-font-heading);
125
- font-weight: var(--primitive-fontWeight-bold);
126
- line-height: var(--primitive-lineHeight-tight);
127
- }}
128
-
129
- .slide-title {{
130
- font-size: var(--slide-title-size);
131
- background: var(--primitive-gradient-primary);
132
- -webkit-background-clip: text;
133
- -webkit-text-fill-color: transparent;
134
- background-clip: text;
135
- }}
136
-
137
- .slide-heading {{
138
- font-size: var(--slide-heading-size);
139
- color: var(--color-foreground);
140
- }}
141
-
142
- .slide-subheading {{
143
- font-size: var(--primitive-fontSize-3xl);
144
- color: var(--color-foreground-secondary);
145
- font-weight: var(--primitive-fontWeight-medium);
146
- }}
147
-
148
- .slide-body {{
149
- font-size: var(--slide-body-size);
150
- color: var(--color-foreground-secondary);
151
- max-width: 80ch;
152
- }}
153
-
154
- /* Brand Colors - Primary/Secondary/Accent */
155
- .text-primary {{ color: var(--color-primary); }}
156
- .text-secondary {{ color: var(--color-secondary); }}
157
- .text-accent {{ color: var(--color-accent); }}
158
- .text-muted {{ color: var(--color-foreground-muted); }}
159
-
160
- .bg-primary {{ background: var(--color-primary); }}
161
- .bg-secondary {{ background: var(--color-secondary); }}
162
- .bg-accent {{ background: var(--color-accent); }}
163
- .bg-surface {{ background: var(--color-surface); }}
164
-
165
- /* Cards - Using component tokens */
166
- .card {{
167
- background: var(--card-bg);
168
- border: 1px solid var(--card-border);
169
- border-radius: var(--card-radius);
170
- padding: var(--card-padding);
171
- box-shadow: var(--card-shadow);
172
- transition: border-color var(--primitive-duration-base) var(--primitive-easing-out);
173
- }}
174
-
175
- .card:hover {{
176
- border-color: var(--card-border-hover);
177
- }}
178
-
179
- /* Buttons - Using component tokens */
180
- .btn {{
181
- display: inline-flex;
182
- align-items: center;
183
- justify-content: center;
184
- padding: var(--button-primary-padding-y) var(--button-primary-padding-x);
185
- border-radius: var(--button-primary-radius);
186
- font-size: var(--button-primary-font-size);
187
- font-weight: var(--button-primary-font-weight);
188
- font-family: var(--typography-font-body);
189
- text-decoration: none;
190
- cursor: pointer;
191
- border: none;
192
- transition: all var(--primitive-duration-base) var(--primitive-easing-out);
193
- }}
194
-
195
- .btn-primary {{
196
- background: var(--button-primary-bg);
197
- color: var(--button-primary-fg);
198
- box-shadow: var(--button-primary-shadow);
199
- }}
200
-
201
- .btn-primary:hover {{
202
- background: var(--button-primary-bg-hover);
203
- }}
204
-
205
- .btn-secondary {{
206
- background: transparent;
207
- color: var(--color-primary);
208
- border: 2px solid var(--color-primary);
209
- }}
210
-
211
- /* Layout Utilities */
212
- .flex {{ display: flex; }}
213
- .flex-col {{ flex-direction: column; }}
214
- .items-center {{ align-items: center; }}
215
- .justify-center {{ justify-content: center; }}
216
- .justify-between {{ justify-content: space-between; }}
217
- .gap-4 {{ gap: var(--primitive-spacing-4); }}
218
- .gap-6 {{ gap: var(--primitive-spacing-6); }}
219
- .gap-8 {{ gap: var(--primitive-spacing-8); }}
220
-
221
- .grid {{ display: grid; }}
222
- .grid-2 {{ grid-template-columns: repeat(2, 1fr); }}
223
- .grid-3 {{ grid-template-columns: repeat(3, 1fr); }}
224
- .grid-4 {{ grid-template-columns: repeat(4, 1fr); }}
225
-
226
- .text-center {{ text-align: center; }}
227
- .mt-auto {{ margin-top: auto; }}
228
- .mb-4 {{ margin-bottom: var(--primitive-spacing-4); }}
229
- .mb-6 {{ margin-bottom: var(--primitive-spacing-6); }}
230
- .mb-8 {{ margin-bottom: var(--primitive-spacing-8); }}
231
-
232
- /* Metric Cards */
233
- .metric {{
234
- text-align: center;
235
- padding: var(--primitive-spacing-6);
236
- }}
237
-
238
- .metric-value {{
239
- font-family: var(--typography-font-heading);
240
- font-size: var(--primitive-fontSize-6xl);
241
- font-weight: var(--primitive-fontWeight-bold);
242
- background: var(--primitive-gradient-primary);
243
- -webkit-background-clip: text;
244
- -webkit-text-fill-color: transparent;
245
- background-clip: text;
246
- }}
247
-
248
- .metric-label {{
249
- font-size: var(--primitive-fontSize-lg);
250
- color: var(--color-foreground-secondary);
251
- margin-top: var(--primitive-spacing-2);
252
- }}
253
-
254
- /* Feature List */
255
- .feature-item {{
256
- display: flex;
257
- align-items: flex-start;
258
- gap: var(--primitive-spacing-4);
259
- padding: var(--primitive-spacing-4) 0;
260
- }}
261
-
262
- .feature-icon {{
263
- width: 48px;
264
- height: 48px;
265
- border-radius: var(--primitive-radius-lg);
266
- background: var(--color-surface-elevated);
267
- display: flex;
268
- align-items: center;
269
- justify-content: center;
270
- color: var(--color-primary);
271
- font-size: var(--primitive-fontSize-xl);
272
- flex-shrink: 0;
273
- }}
274
-
275
- .feature-content h4 {{
276
- font-size: var(--primitive-fontSize-xl);
277
- color: var(--color-foreground);
278
- margin-bottom: var(--primitive-spacing-2);
279
- }}
280
-
281
- .feature-content p {{
282
- color: var(--color-foreground-secondary);
283
- font-size: var(--primitive-fontSize-base);
284
- }}
285
-
286
- /* Testimonial */
287
- .testimonial {{
288
- background: var(--color-surface);
289
- border-radius: var(--primitive-radius-xl);
290
- padding: var(--primitive-spacing-8);
291
- border-left: 4px solid var(--color-primary);
292
- }}
293
-
294
- .testimonial-quote {{
295
- font-size: var(--primitive-fontSize-2xl);
296
- color: var(--color-foreground);
297
- font-style: italic;
298
- margin-bottom: var(--primitive-spacing-6);
299
- }}
300
-
301
- .testimonial-author {{
302
- font-size: var(--primitive-fontSize-lg);
303
- color: var(--color-primary);
304
- font-weight: var(--primitive-fontWeight-semibold);
305
- }}
306
-
307
- .testimonial-role {{
308
- font-size: var(--primitive-fontSize-base);
309
- color: var(--color-foreground-muted);
310
- }}
311
-
312
- /* Badge/Tag */
313
- .badge {{
314
- display: inline-block;
315
- padding: var(--primitive-spacing-2) var(--primitive-spacing-4);
316
- background: var(--color-surface-elevated);
317
- border-radius: var(--primitive-radius-full);
318
- font-size: var(--primitive-fontSize-sm);
319
- color: var(--color-accent);
320
- font-weight: var(--primitive-fontWeight-medium);
321
- }}
322
-
323
- /* Chart Container */
324
- .chart-container {{
325
- background: var(--color-surface);
326
- border-radius: var(--primitive-radius-xl);
327
- padding: var(--primitive-spacing-6);
328
- height: 100%;
329
- display: flex;
330
- flex-direction: column;
331
- }}
332
-
333
- .chart-title {{
334
- font-family: var(--typography-font-heading);
335
- font-size: var(--primitive-fontSize-xl);
336
- color: var(--color-foreground);
337
- margin-bottom: var(--primitive-spacing-4);
338
- }}
339
-
340
- /* CSS-only Bar Chart */
341
- .bar-chart {{
342
- display: flex;
343
- align-items: flex-end;
344
- gap: var(--primitive-spacing-4);
345
- height: 200px;
346
- padding-top: var(--primitive-spacing-4);
347
- }}
348
-
349
- .bar {{
350
- flex: 1;
351
- background: var(--primitive-gradient-primary);
352
- border-radius: var(--primitive-radius-md) var(--primitive-radius-md) 0 0;
353
- position: relative;
354
- min-width: 40px;
355
- }}
356
-
357
- .bar-label {{
358
- position: absolute;
359
- bottom: -30px;
360
- left: 50%;
361
- transform: translateX(-50%);
362
- font-size: var(--primitive-fontSize-sm);
363
- color: var(--color-foreground-muted);
364
- white-space: nowrap;
365
- }}
366
-
367
- .bar-value {{
368
- position: absolute;
369
- top: -25px;
370
- left: 50%;
371
- transform: translateX(-50%);
372
- font-size: var(--primitive-fontSize-sm);
373
- color: var(--color-foreground);
374
- font-weight: var(--primitive-fontWeight-semibold);
375
- }}
376
-
377
- /* Progress Bar */
378
- .progress {{
379
- height: 12px;
380
- background: var(--color-surface-elevated);
381
- border-radius: var(--primitive-radius-full);
382
- overflow: hidden;
383
- }}
384
-
385
- .progress-fill {{
386
- height: 100%;
387
- background: var(--primitive-gradient-primary);
388
- border-radius: var(--primitive-radius-full);
389
- }}
390
-
391
- /* Footer */
392
- .slide-footer {{
393
- margin-top: auto;
394
- display: flex;
395
- justify-content: space-between;
396
- align-items: center;
397
- padding-top: var(--primitive-spacing-6);
398
- border-top: 1px solid var(--color-border);
399
- color: var(--color-foreground-muted);
400
- font-size: var(--primitive-fontSize-sm);
401
- }}
402
-
403
- /* Glow Effects */
404
- .glow-coral {{
405
- box-shadow: var(--primitive-shadow-glow-coral);
406
- }}
407
-
408
- .glow-purple {{
409
- box-shadow: var(--primitive-shadow-glow-purple);
410
- }}
411
-
412
- .glow-mint {{
413
- box-shadow: var(--primitive-shadow-glow-mint);
414
- }}
415
- </style>
416
- </head>
417
- <body>
418
- <div class="slide-deck">
419
- {slides_content}
420
- </div>
421
- </body>
422
- </html>
423
- '''
424
-
425
-
426
- # ============ SLIDE GENERATORS ============
427
-
428
- def generate_title_slide(data):
429
- """Title slide with gradient headline"""
430
- return f'''
431
- <section class="slide slide--glow flex flex-col items-center justify-center text-center">
432
- <div class="badge mb-6">{_e(data.get('badge', 'Pitch Deck'))}</div>
433
- <h1 class="slide-title mb-6">{_e(data.get('title', 'Your Title Here'))}</h1>
434
- <p class="slide-subheading mb-8">{_e(data.get('subtitle', 'Your compelling subtitle'))}</p>
435
- <div class="flex gap-4">
436
- <a href="#" class="btn btn-primary">{_e(data.get('cta', 'Get Started'))}</a>
437
- <a href="#" class="btn btn-secondary">{_e(data.get('secondary_cta', 'Learn More'))}</a>
438
- </div>
439
- <div class="slide-footer">
440
- <span>{_e(data.get('company', 'Company Name'))}</span>
441
- <span>{_e(data.get('date', datetime.now().strftime('%B %Y')))}</span>
442
- </div>
443
- </section>
444
- '''
445
-
446
-
447
- def generate_problem_slide(data):
448
- """Problem statement slide using PAS formula"""
449
- return f'''
450
- <section class="slide slide--surface">
451
- <div class="badge mb-6">The Problem</div>
452
- <h2 class="slide-heading mb-8">{_e(data.get('headline', 'The problem your audience faces'))}</h2>
453
- <div class="grid grid-3 gap-8">
454
- <div class="card">
455
- <div class="text-primary" style="font-size: var(--primitive-fontSize-4xl); margin-bottom: var(--primitive-spacing-4);">01</div>
456
- <h4 style="margin-bottom: var(--primitive-spacing-2); font-size: var(--primitive-fontSize-xl);">{_e(data.get('pain_1_title', 'Pain Point 1'))}</h4>
457
- <p class="text-muted">{_e(data.get('pain_1_desc', 'Description of the first pain point'))}</p>
458
- </div>
459
- <div class="card">
460
- <div class="text-secondary" style="font-size: var(--primitive-fontSize-4xl); margin-bottom: var(--primitive-spacing-4);">02</div>
461
- <h4 style="margin-bottom: var(--primitive-spacing-2); font-size: var(--primitive-fontSize-xl);">{_e(data.get('pain_2_title', 'Pain Point 2'))}</h4>
462
- <p class="text-muted">{_e(data.get('pain_2_desc', 'Description of the second pain point'))}</p>
463
- </div>
464
- <div class="card">
465
- <div class="text-accent" style="font-size: var(--primitive-fontSize-4xl); margin-bottom: var(--primitive-spacing-4);">03</div>
466
- <h4 style="margin-bottom: var(--primitive-spacing-2); font-size: var(--primitive-fontSize-xl);">{_e(data.get('pain_3_title', 'Pain Point 3'))}</h4>
467
- <p class="text-muted">{_e(data.get('pain_3_desc', 'Description of the third pain point'))}</p>
468
- </div>
469
- </div>
470
- <div class="slide-footer">
471
- <span>{_e(data.get('company', 'Company Name'))}</span>
472
- <span>{_e(data.get('page', '2'))}</span>
473
- </div>
474
- </section>
475
- '''
476
-
477
-
478
- def generate_solution_slide(data):
479
- """Solution slide with feature highlights"""
480
- return f'''
481
- <section class="slide">
482
- <div class="badge mb-6">The Solution</div>
483
- <h2 class="slide-heading mb-8">{_e(data.get('headline', 'How we solve this'))}</h2>
484
- <div class="flex gap-8" style="flex: 1;">
485
- <div style="flex: 1;">
486
- <div class="feature-item">
487
- <div class="feature-icon">&#10003;</div>
488
- <div class="feature-content">
489
- <h4>{_e(data.get('feature_1_title', 'Feature 1'))}</h4>
490
- <p>{_e(data.get('feature_1_desc', 'Description of feature 1'))}</p>
491
- </div>
492
- </div>
493
- <div class="feature-item">
494
- <div class="feature-icon">&#10003;</div>
495
- <div class="feature-content">
496
- <h4>{_e(data.get('feature_2_title', 'Feature 2'))}</h4>
497
- <p>{_e(data.get('feature_2_desc', 'Description of feature 2'))}</p>
498
- </div>
499
- </div>
500
- <div class="feature-item">
501
- <div class="feature-icon">&#10003;</div>
502
- <div class="feature-content">
503
- <h4>{_e(data.get('feature_3_title', 'Feature 3'))}</h4>
504
- <p>{_e(data.get('feature_3_desc', 'Description of feature 3'))}</p>
505
- </div>
506
- </div>
507
- </div>
508
- <div style="flex: 1;" class="card flex items-center justify-center">
509
- <div class="text-center">
510
- <div class="text-accent" style="font-size: 80px; margin-bottom: var(--primitive-spacing-4);">&#9670;</div>
511
- <p class="text-muted">Product screenshot or demo</p>
512
- </div>
513
- </div>
514
- </div>
515
- <div class="slide-footer">
516
- <span>{_e(data.get('company', 'Company Name'))}</span>
517
- <span>{_e(data.get('page', '3'))}</span>
518
- </div>
519
- </section>
520
- '''
521
-
522
-
523
- def generate_metrics_slide(data):
524
- """Traction/metrics slide with large numbers"""
525
- metrics = data.get('metrics', [
526
- {'value': '10K+', 'label': 'Active Users'},
527
- {'value': '95%', 'label': 'Retention Rate'},
528
- {'value': '3x', 'label': 'Revenue Growth'},
529
- {'value': '$2M', 'label': 'ARR'}
530
- ])
531
-
532
- metrics_html = ''.join([f'''
533
- <div class="card metric">
534
- <div class="metric-value">{_e(m.get('value', ''))}</div>
535
- <div class="metric-label">{_e(m.get('label', ''))}</div>
536
- </div>
537
- ''' for m in metrics[:4]])
538
-
539
- return f'''
540
- <section class="slide slide--surface slide--glow">
541
- <div class="badge mb-6">Traction</div>
542
- <h2 class="slide-heading mb-8 text-center">{_e(data.get('headline', 'Our Growth'))}</h2>
543
- <div class="grid grid-4 gap-6" style="flex: 1; align-items: center;">
544
- {metrics_html}
545
- </div>
546
- <div class="slide-footer">
547
- <span>{_e(data.get('company', 'Company Name'))}</span>
548
- <span>{_e(data.get('page', '4'))}</span>
549
- </div>
550
- </section>
551
- '''
552
-
553
-
554
- def generate_chart_slide(data):
555
- """Chart slide with CSS bar chart"""
556
- bars = data.get('bars', [
557
- {'label': 'Q1', 'value': 40},
558
- {'label': 'Q2', 'value': 60},
559
- {'label': 'Q3', 'value': 80},
560
- {'label': 'Q4', 'value': 100}
561
- ])
562
-
563
- bars_html = ''.join([f'''
564
- <div class="bar" style="height: {int(b.get('value', 0))}%;">
565
- <span class="bar-value">{_e(b.get('display', str(b.get('value', 0)) + '%'))}</span>
566
- <span class="bar-label">{_e(b.get('label', ''))}</span>
567
- </div>
568
- ''' for b in bars])
569
-
570
- return f'''
571
- <section class="slide">
572
- <div class="badge mb-6">{_e(data.get('badge', 'Growth'))}</div>
573
- <h2 class="slide-heading mb-8">{_e(data.get('headline', 'Revenue Growth'))}</h2>
574
- <div class="chart-container" style="flex: 1;">
575
- <div class="chart-title">{_e(data.get('chart_title', 'Quarterly Revenue'))}</div>
576
- <div class="bar-chart" style="flex: 1; padding-bottom: 40px;">
577
- {bars_html}
578
- </div>
579
- </div>
580
- <div class="slide-footer">
581
- <span>{_e(data.get('company', 'Company Name'))}</span>
582
- <span>{_e(data.get('page', '5'))}</span>
583
- </div>
584
- </section>
585
- '''
586
-
587
-
588
- def generate_testimonial_slide(data):
589
- """Social proof slide"""
590
- return f'''
591
- <section class="slide slide--surface flex flex-col justify-center">
592
- <div class="badge mb-6">What They Say</div>
593
- <div class="testimonial" style="max-width: 900px;">
594
- <p class="testimonial-quote">"{_e(data.get('quote', 'This product changed how we work. Incredible results.'))}"</p>
595
- <p class="testimonial-author">{_e(data.get('author', 'Jane Doe'))}</p>
596
- <p class="testimonial-role">{_e(data.get('role', 'CEO, Example Company'))}</p>
597
- </div>
598
- <div class="slide-footer">
599
- <span>{_e(data.get('company', 'Company Name'))}</span>
600
- <span>{_e(data.get('page', '6'))}</span>
601
- </div>
602
- </section>
603
- '''
604
-
605
-
606
- def generate_cta_slide(data):
607
- """Closing CTA slide"""
608
- return f'''
609
- <section class="slide slide--gradient flex flex-col items-center justify-center text-center">
610
- <h2 class="slide-heading mb-6" style="color: var(--color-foreground);">{_e(data.get('headline', 'Ready to get started?'))}</h2>
611
- <p class="slide-body mb-8" style="color: rgba(255,255,255,0.8);">{_e(data.get('subheadline', 'Join thousands of teams already using our solution.'))}</p>
612
- <div class="flex gap-4">
613
- <a href="{_safe_url(data.get('cta_url', '#'))}" class="btn" style="background: var(--color-foreground); color: var(--color-primary);">{_e(data.get('cta', 'Start Free Trial'))}</a>
614
- </div>
615
- <div class="slide-footer" style="border-color: rgba(255,255,255,0.2); color: rgba(255,255,255,0.6);">
616
- <span>{_e(data.get('contact', 'contact@example.com'))}</span>
617
- <span>{_e(data.get('website', 'www.example.com'))}</span>
618
- </div>
619
- </section>
620
- '''
621
-
622
-
623
- # Slide type mapping
624
- SLIDE_GENERATORS = {
625
- 'title': generate_title_slide,
626
- 'problem': generate_problem_slide,
627
- 'solution': generate_solution_slide,
628
- 'metrics': generate_metrics_slide,
629
- 'traction': generate_metrics_slide,
630
- 'chart': generate_chart_slide,
631
- 'testimonial': generate_testimonial_slide,
632
- 'cta': generate_cta_slide,
633
- 'closing': generate_cta_slide
634
- }
635
-
636
-
637
- def generate_deck(slides_data, title="Pitch Deck"):
638
- """Generate complete deck from slide data list"""
639
- slides_html = ""
640
- for slide in slides_data:
641
- slide_type = slide.get('type', 'title')
642
- generator = SLIDE_GENERATORS.get(slide_type)
643
- if generator:
644
- slides_html += generator(slide)
645
- else:
646
- print(f"Warning: Unknown slide type '{slide_type}'")
647
-
648
- # Calculate relative path to tokens CSS
649
- tokens_rel_path = "../../../assets/design-tokens.css"
650
-
651
- return SLIDE_TEMPLATE.format(
652
- title=escape(str(title)),
653
- tokens_css_path=tokens_rel_path,
654
- slides_content=slides_html
655
- )
656
-
657
-
658
- def main():
659
- parser = argparse.ArgumentParser(description="Generate brand-compliant slides")
660
- parser.add_argument("--json", "-j", help="JSON file with slide data")
661
- parser.add_argument("--output", "-o", help="Output HTML file path")
662
- parser.add_argument("--demo", action="store_true", help="Generate demo deck")
663
-
664
- args = parser.parse_args()
665
-
666
- if args.demo:
667
- # Demo deck showcasing all slide types
668
- demo_slides = [
669
- {
670
- 'type': 'title',
671
- 'badge': 'Investor Deck 2024',
672
- 'title': 'ClaudeKit Marketing',
673
- 'subtitle': 'Your AI marketing team. Always on.',
674
- 'cta': 'Join Waitlist',
675
- 'secondary_cta': 'See Demo',
676
- 'company': 'ClaudeKit',
677
- 'date': 'December 2024'
678
- },
679
- {
680
- 'type': 'problem',
681
- 'headline': 'Marketing teams are drowning',
682
- 'pain_1_title': 'Content Overload',
683
- 'pain_1_desc': 'Need to produce 10x content with same headcount',
684
- 'pain_2_title': 'Tool Fatigue',
685
- 'pain_2_desc': '15+ tools that don\'t talk to each other',
686
- 'pain_3_title': 'No Time to Think',
687
- 'pain_3_desc': 'Strategy suffers when execution consumes all hours',
688
- 'company': 'ClaudeKit',
689
- 'page': '2'
690
- },
691
- {
692
- 'type': 'solution',
693
- 'headline': 'AI agents that actually get marketing',
694
- 'feature_1_title': 'Content Creation',
695
- 'feature_1_desc': 'Blog posts, social, email - all on brand, all on time',
696
- 'feature_2_title': 'Campaign Management',
697
- 'feature_2_desc': 'Multi-channel orchestration with one command',
698
- 'feature_3_title': 'Analytics & Insights',
699
- 'feature_3_desc': 'Real-time optimization without the spreadsheets',
700
- 'company': 'ClaudeKit',
701
- 'page': '3'
702
- },
703
- {
704
- 'type': 'metrics',
705
- 'headline': 'Early traction speaks volumes',
706
- 'metrics': [
707
- {'value': '500+', 'label': 'Beta Users'},
708
- {'value': '85%', 'label': 'Weekly Active'},
709
- {'value': '4.9', 'label': 'NPS Score'},
710
- {'value': '50hrs', 'label': 'Saved/Week'}
711
- ],
712
- 'company': 'ClaudeKit',
713
- 'page': '4'
714
- },
715
- {
716
- 'type': 'chart',
717
- 'badge': 'Revenue',
718
- 'headline': 'Growing month over month',
719
- 'chart_title': 'MRR Growth ($K)',
720
- 'bars': [
721
- {'label': 'Sep', 'value': 20, 'display': '$5K'},
722
- {'label': 'Oct', 'value': 40, 'display': '$12K'},
723
- {'label': 'Nov', 'value': 70, 'display': '$28K'},
724
- {'label': 'Dec', 'value': 100, 'display': '$45K'}
725
- ],
726
- 'company': 'ClaudeKit',
727
- 'page': '5'
728
- },
729
- {
730
- 'type': 'testimonial',
731
- 'quote': 'ClaudeKit replaced 3 tools and 2 contractors. Our content output tripled while costs dropped 60%.',
732
- 'author': 'Sarah Chen',
733
- 'role': 'Head of Marketing, TechStartup',
734
- 'company': 'ClaudeKit',
735
- 'page': '6'
736
- },
737
- {
738
- 'type': 'cta',
739
- 'headline': 'Ship campaigns while you sleep',
740
- 'subheadline': 'Early access available. Limited spots.',
741
- 'cta': 'Join the Waitlist',
742
- 'contact': 'hello@claudekit.ai',
743
- 'website': 'claudekit.ai'
744
- }
745
- ]
746
-
747
- html = generate_deck(demo_slides, "ClaudeKit Marketing - Pitch Deck")
748
-
749
- OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
750
- output_path = OUTPUT_DIR / f"demo-pitch-{datetime.now().strftime('%y%m%d')}.html"
751
- output_path.write_text(html, encoding='utf-8')
752
- print(f"Demo deck generated: {output_path}")
753
-
754
- elif args.json:
755
- with open(args.json, 'r') as f:
756
- data = json.load(f)
757
-
758
- html = generate_deck(data.get('slides', []), data.get('title', 'Presentation'))
759
-
760
- output_path = Path(args.output) if args.output else OUTPUT_DIR / f"deck-{datetime.now().strftime('%y%m%d-%H%M')}.html"
761
- output_path.parent.mkdir(parents=True, exist_ok=True)
762
- output_path.write_text(html, encoding='utf-8')
763
- print(f"Deck generated: {output_path}")
764
-
765
- else:
766
- parser.print_help()
767
-
768
-
769
- if __name__ == "__main__":
770
- main()