@litfamily/litgrok 1.0.34 → 1.0.35

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 (966) hide show
  1. package/.grok/hooks/lit-status-line.mjs +59 -1
  2. package/.grok/rules/00-litgrok.md +4 -6
  3. package/.grok/skills/browser-drive/scripts/capability-probe.mjs +1 -1
  4. package/.grok/skills/frontend-ui-ux/SKILL.md +47 -39
  5. package/.grok/skills/lit-diagram-drawer/SKILL.md +32 -93
  6. package/.grok/skills/lit-docx/SKILL.md +31 -77
  7. package/.grok/skills/lit-humanizer/scripts/extract.mjs +1 -1
  8. package/.grok/skills/lit-humanizer/scripts/test-fixtures.mjs +1 -1
  9. package/.grok/skills/lit-pptx/SKILL.md +30 -90
  10. package/.grok/skills/lit-schematic/SKILL.md +30 -115
  11. package/.grok/skills/lit-scientific-visualization/SKILL.md +36 -221
  12. package/.grok/skills/lit-typographic-motion/SKILL.md +40 -29
  13. package/.grok/skills/litgrok/SKILL.md +6 -3
  14. package/.grok/skills/litwork/SKILL.md +1 -1
  15. package/.grok/skills/lsp-setup/scripts/lsp-server-table.mjs +1 -1
  16. package/.grok/skills/readme-studio/SKILL.md +40 -24
  17. package/.grok/skills/visual-qa/SKILL.md +33 -28
  18. package/.grok-plugin/plugin.json +1 -1
  19. package/CHANGELOG.md +12 -1
  20. package/CONTRIBUTING.md +0 -1
  21. package/README.md +9 -9
  22. package/README_ko-KR.md +9 -9
  23. package/bin/litgrok.mjs +351 -30
  24. package/docs/assets/readme/README.md +7 -0
  25. package/docs/assets/readme/badge-version.svg +1 -1
  26. package/docs/reference.md +11 -5
  27. package/docs/reference_ko-KR.md +10 -5
  28. package/package.json +4 -6
  29. package/plugin.json +1 -1
  30. package/.grok/skills/frontend-ui-ux/ATTRIBUTION.md +0 -23
  31. package/.grok/skills/frontend-ui-ux/LICENSE +0 -21
  32. package/.grok/skills/frontend-ui-ux/PROVENANCE.json +0 -38
  33. package/.grok/skills/frontend-ui-ux/SOURCE-MANIFEST.json +0 -1060
  34. package/.grok/skills/frontend-ui-ux/THIRD-PARTY-NOTICE.txt +0 -24
  35. package/.grok/skills/frontend-ui-ux/data/design-intelligence.json +0 -1
  36. package/.grok/skills/frontend-ui-ux/examples/phase-3-readme-ab/README.md +0 -9
  37. package/.grok/skills/frontend-ui-ux/examples/phase-3-readme-motion-ab.md +0 -24
  38. package/.grok/skills/frontend-ui-ux/kit/components.css +0 -414
  39. package/.grok/skills/frontend-ui-ux/kit/fonts.json +0 -22
  40. package/.grok/skills/frontend-ui-ux/kit/layouts/landing.html +0 -164
  41. package/.grok/skills/frontend-ui-ux/kit/layouts/settings.html +0 -114
  42. package/.grok/skills/frontend-ui-ux/kit/layouts/workspace.html +0 -115
  43. package/.grok/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +0 -227
  44. package/.grok/skills/frontend-ui-ux/kit/styles/field-notes.json +0 -205
  45. package/.grok/skills/frontend-ui-ux/kit/styles/graphite.json +0 -200
  46. package/.grok/skills/frontend-ui-ux/kit/styles/meadow.json +0 -199
  47. package/.grok/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +0 -222
  48. package/.grok/skills/frontend-ui-ux/kit/ui.js +0 -226
  49. package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/legal/frontend-ATTRIBUTION.md +0 -217
  50. package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/legal/frontend-LICENSE-Apache-2.0.txt +0 -201
  51. package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/legal/root-LICENSE +0 -21
  52. package/.grok/skills/frontend-ui-ux/references/_canonical-corpus/manifest.json +0 -873
  53. package/.grok/skills/frontend-ui-ux/references/adaptive-layout.md +0 -88
  54. package/.grok/skills/frontend-ui-ux/references/brand-and-imagery.md +0 -87
  55. package/.grok/skills/frontend-ui-ux/references/complete-contract.md +0 -572
  56. package/.grok/skills/frontend-ui-ux/references/composition.md +0 -79
  57. package/.grok/skills/frontend-ui-ux/references/craft-floor.md +0 -262
  58. package/.grok/skills/frontend-ui-ux/references/creative-directions.md +0 -74
  59. package/.grok/skills/frontend-ui-ux/references/default-editorial-pixel.json +0 -28
  60. package/.grok/skills/frontend-ui-ux/references/design/README.md +0 -248
  61. package/.grok/skills/frontend-ui-ux/references/design/_INDEX.md +0 -191
  62. package/.grok/skills/frontend-ui-ux/references/design/airbnb.md +0 -393
  63. package/.grok/skills/frontend-ui-ux/references/design/airtable.md +0 -92
  64. package/.grok/skills/frontend-ui-ux/references/design/apple.md +0 -250
  65. package/.grok/skills/frontend-ui-ux/references/design/aside.md +0 -209
  66. package/.grok/skills/frontend-ui-ux/references/design/binance.md +0 -348
  67. package/.grok/skills/frontend-ui-ux/references/design/bmw.md +0 -183
  68. package/.grok/skills/frontend-ui-ux/references/design/brutalist-skill.md +0 -92
  69. package/.grok/skills/frontend-ui-ux/references/design/bugatti.md +0 -271
  70. package/.grok/skills/frontend-ui-ux/references/design/cal.md +0 -262
  71. package/.grok/skills/frontend-ui-ux/references/design/claude.md +0 -315
  72. package/.grok/skills/frontend-ui-ux/references/design/clay.md +0 -307
  73. package/.grok/skills/frontend-ui-ux/references/design/clickhouse.md +0 -284
  74. package/.grok/skills/frontend-ui-ux/references/design/clone-from-url.md +0 -65
  75. package/.grok/skills/frontend-ui-ux/references/design/cohere.md +0 -269
  76. package/.grok/skills/frontend-ui-ux/references/design/coinbase.md +0 -132
  77. package/.grok/skills/frontend-ui-ux/references/design/composio.md +0 -310
  78. package/.grok/skills/frontend-ui-ux/references/design/cursor.md +0 -312
  79. package/.grok/skills/frontend-ui-ux/references/design/design-system-architecture.md +0 -244
  80. package/.grok/skills/frontend-ui-ux/references/design/elevenlabs.md +0 -268
  81. package/.grok/skills/frontend-ui-ux/references/design/expo.md +0 -284
  82. package/.grok/skills/frontend-ui-ux/references/design/ferrari.md +0 -317
  83. package/.grok/skills/frontend-ui-ux/references/design/figma.md +0 -223
  84. package/.grok/skills/frontend-ui-ux/references/design/framer.md +0 -249
  85. package/.grok/skills/frontend-ui-ux/references/design/gpt-tasteskill.md +0 -74
  86. package/.grok/skills/frontend-ui-ux/references/design/hashicorp.md +0 -281
  87. package/.grok/skills/frontend-ui-ux/references/design/ibm.md +0 -335
  88. package/.grok/skills/frontend-ui-ux/references/design/image-to-code-skill.md +0 -1228
  89. package/.grok/skills/frontend-ui-ux/references/design/imagegen-brandkit.md +0 -798
  90. package/.grok/skills/frontend-ui-ux/references/design/imagegen-frontend-mobile.md +0 -1465
  91. package/.grok/skills/frontend-ui-ux/references/design/imagegen-frontend-web.md +0 -987
  92. package/.grok/skills/frontend-ui-ux/references/design/intercom.md +0 -149
  93. package/.grok/skills/frontend-ui-ux/references/design/kraken.md +0 -128
  94. package/.grok/skills/frontend-ui-ux/references/design/lamborghini.md +0 -291
  95. package/.grok/skills/frontend-ui-ux/references/design/layout-skill.md +0 -107
  96. package/.grok/skills/frontend-ui-ux/references/design/lazyweb.md +0 -77
  97. package/.grok/skills/frontend-ui-ux/references/design/linear.app.md +0 -370
  98. package/.grok/skills/frontend-ui-ux/references/design/lovable.md +0 -301
  99. package/.grok/skills/frontend-ui-ux/references/design/mastercard.md +0 -368
  100. package/.grok/skills/frontend-ui-ux/references/design/meta.md +0 -369
  101. package/.grok/skills/frontend-ui-ux/references/design/minimalist-skill.md +0 -85
  102. package/.grok/skills/frontend-ui-ux/references/design/minimax.md +0 -260
  103. package/.grok/skills/frontend-ui-ux/references/design/mintlify.md +0 -329
  104. package/.grok/skills/frontend-ui-ux/references/design/miro.md +0 -111
  105. package/.grok/skills/frontend-ui-ux/references/design/mistral.ai.md +0 -264
  106. package/.grok/skills/frontend-ui-ux/references/design/mongodb.md +0 -269
  107. package/.grok/skills/frontend-ui-ux/references/design/nike.md +0 -366
  108. package/.grok/skills/frontend-ui-ux/references/design/notion.md +0 -312
  109. package/.grok/skills/frontend-ui-ux/references/design/nvidia.md +0 -296
  110. package/.grok/skills/frontend-ui-ux/references/design/ollama.md +0 -270
  111. package/.grok/skills/frontend-ui-ux/references/design/opencode.ai.md +0 -284
  112. package/.grok/skills/frontend-ui-ux/references/design/output-skill.md +0 -49
  113. package/.grok/skills/frontend-ui-ux/references/design/pinterest.md +0 -233
  114. package/.grok/skills/frontend-ui-ux/references/design/playstation.md +0 -367
  115. package/.grok/skills/frontend-ui-ux/references/design/posthog.md +0 -259
  116. package/.grok/skills/frontend-ui-ux/references/design/raycast.md +0 -271
  117. package/.grok/skills/frontend-ui-ux/references/design/react-dev-tooling-skill.md +0 -230
  118. package/.grok/skills/frontend-ui-ux/references/design/redesign-skill.md +0 -178
  119. package/.grok/skills/frontend-ui-ux/references/design/renault.md +0 -314
  120. package/.grok/skills/frontend-ui-ux/references/design/replicate.md +0 -264
  121. package/.grok/skills/frontend-ui-ux/references/design/resend.md +0 -306
  122. package/.grok/skills/frontend-ui-ux/references/design/revolut.md +0 -188
  123. package/.grok/skills/frontend-ui-ux/references/design/runwayml.md +0 -247
  124. package/.grok/skills/frontend-ui-ux/references/design/sanity.md +0 -360
  125. package/.grok/skills/frontend-ui-ux/references/design/sentry.md +0 -265
  126. package/.grok/skills/frontend-ui-ux/references/design/shopify.md +0 -353
  127. package/.grok/skills/frontend-ui-ux/references/design/soft-skill.md +0 -98
  128. package/.grok/skills/frontend-ui-ux/references/design/spacex.md +0 -197
  129. package/.grok/skills/frontend-ui-ux/references/design/spotify.md +0 -249
  130. package/.grok/skills/frontend-ui-ux/references/design/starbucks.md +0 -583
  131. package/.grok/skills/frontend-ui-ux/references/design/stitch-design-example.md +0 -121
  132. package/.grok/skills/frontend-ui-ux/references/design/stitch-skill.md +0 -184
  133. package/.grok/skills/frontend-ui-ux/references/design/stripe.md +0 -325
  134. package/.grok/skills/frontend-ui-ux/references/design/supabase.md +0 -258
  135. package/.grok/skills/frontend-ui-ux/references/design/superhuman.md +0 -255
  136. package/.grok/skills/frontend-ui-ux/references/design/taste-skill.md +0 -1206
  137. package/.grok/skills/frontend-ui-ux/references/design/tesla.md +0 -289
  138. package/.grok/skills/frontend-ui-ux/references/design/theverge.md +0 -342
  139. package/.grok/skills/frontend-ui-ux/references/design/together.ai.md +0 -266
  140. package/.grok/skills/frontend-ui-ux/references/design/uber.md +0 -298
  141. package/.grok/skills/frontend-ui-ux/references/design/vercel.md +0 -313
  142. package/.grok/skills/frontend-ui-ux/references/design/vodafone.md +0 -426
  143. package/.grok/skills/frontend-ui-ux/references/design/voltagent.md +0 -326
  144. package/.grok/skills/frontend-ui-ux/references/design/warp.md +0 -256
  145. package/.grok/skills/frontend-ui-ux/references/design/webflow.md +0 -95
  146. package/.grok/skills/frontend-ui-ux/references/design/wired.md +0 -281
  147. package/.grok/skills/frontend-ui-ux/references/design/wise.md +0 -176
  148. package/.grok/skills/frontend-ui-ux/references/design/x.ai.md +0 -260
  149. package/.grok/skills/frontend-ui-ux/references/design/zapier.md +0 -331
  150. package/.grok/skills/frontend-ui-ux/references/designpowers/EVIDENCE.md +0 -97
  151. package/.grok/skills/frontend-ui-ux/references/designpowers/README.md +0 -48
  152. package/.grok/skills/frontend-ui-ux/references/designpowers/UPSTREAM.md +0 -80
  153. package/.grok/skills/frontend-ui-ux/references/designpowers/lane-a-direction.md +0 -64
  154. package/.grok/skills/frontend-ui-ux/references/designpowers/lane-b-execution.md +0 -65
  155. package/.grok/skills/frontend-ui-ux/references/designpowers/lane-c-review.md +0 -65
  156. package/.grok/skills/frontend-ui-ux/references/designpowers/lane-d-memory.md +0 -83
  157. package/.grok/skills/frontend-ui-ux/references/designpowers/orchestration.md +0 -80
  158. package/.grok/skills/frontend-ui-ux/references/designpowers/routing.md +0 -79
  159. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/LICENSE +0 -21
  160. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/accessibility-reviewer.md +0 -83
  161. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/content-writer.md +0 -132
  162. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-builder.md +0 -109
  163. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-critic.md +0 -89
  164. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-lead.md +0 -113
  165. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-scout.md +0 -78
  166. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-strategist.md +0 -121
  167. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/heuristic-evaluator.md +0 -268
  168. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/inspiration-scout.md +0 -107
  169. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/agents/motion-designer.md +0 -120
  170. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/accessible-content/reference.md +0 -101
  171. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/adaptive-interfaces/reference.md +0 -109
  172. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/cognitive-accessibility/reference.md +0 -107
  173. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-debate/reference.md +0 -199
  174. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-debt-tracker/reference.md +0 -174
  175. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-handoff/reference.md +0 -125
  176. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-md/reference.md +0 -106
  177. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-retrospective/reference.md +0 -266
  178. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-review/reference.md +0 -123
  179. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-system-alignment/reference.md +0 -120
  180. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/designpowers-critique/reference.md +0 -164
  181. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/heuristic-evaluation/reference.md +0 -85
  182. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/inclusive-personas/reference.md +0 -98
  183. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/inspiration-scouting/reference.md +0 -165
  184. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/interaction-design/reference.md +0 -122
  185. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/motion-choreography/reference.md +0 -81
  186. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/research-planning/reference.md +0 -96
  187. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/responsive-patterns/reference.md +0 -77
  188. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/synthetic-user-testing/reference.md +0 -192
  189. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/taste-feedback/reference.md +0 -165
  190. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/taste-report/reference.md +0 -78
  191. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/token-architecture/reference.md +0 -75
  192. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/ui-composition/reference.md +0 -117
  193. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/usability-testing/reference.md +0 -78
  194. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/verification-before-shipping/reference.md +0 -125
  195. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/voice-and-tone/reference.md +0 -79
  196. package/.grok/skills/frontend-ui-ux/references/designpowers/vendor/skills/writing-design-plans/reference.md +0 -119
  197. package/.grok/skills/frontend-ui-ux/references/evidence-review.md +0 -126
  198. package/.grok/skills/frontend-ui-ux/references/house-styles.md +0 -234
  199. package/.grok/skills/frontend-ui-ux/references/implementation-platforms.md +0 -103
  200. package/.grok/skills/frontend-ui-ux/references/inclusive-interface.md +0 -88
  201. package/.grok/skills/frontend-ui-ux/references/interaction-motion.md +0 -47
  202. package/.grok/skills/frontend-ui-ux/references/motion-guide.md +0 -130
  203. package/.grok/skills/frontend-ui-ux/references/operating-lanes.md +0 -92
  204. package/.grok/skills/frontend-ui-ux/references/perfection/README.md +0 -160
  205. package/.grok/skills/frontend-ui-ux/references/perfection/react-perf-tooling.md +0 -127
  206. package/.grok/skills/frontend-ui-ux/references/performance-delivery.md +0 -85
  207. package/.grok/skills/frontend-ui-ux/references/probe-loop.md +0 -27
  208. package/.grok/skills/frontend-ui-ux/references/product-direction.md +0 -80
  209. package/.grok/skills/frontend-ui-ux/references/production-interview.md +0 -17
  210. package/.grok/skills/frontend-ui-ux/references/production.md +0 -27
  211. package/.grok/skills/frontend-ui-ux/references/redesign-playbook.md +0 -91
  212. package/.grok/skills/frontend-ui-ux/references/slop-register.md +0 -150
  213. package/.grok/skills/frontend-ui-ux/references/system-foundations.md +0 -80
  214. package/.grok/skills/frontend-ui-ux/references/taste-direction.md +0 -86
  215. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/README.md +0 -659
  216. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/charts.csv +0 -26
  217. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/colors.csv +0 -162
  218. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/icons.csv +0 -106
  219. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/landing.csv +0 -35
  220. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/products.csv +0 -162
  221. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/react-performance.csv +0 -45
  222. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/astro.csv +0 -54
  223. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/flutter.csv +0 -53
  224. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/html-tailwind.csv +0 -56
  225. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/jetpack-compose.csv +0 -53
  226. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nextjs.csv +0 -53
  227. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nuxt-ui.csv +0 -51
  228. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nuxtjs.csv +0 -59
  229. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/react-native.csv +0 -52
  230. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/react.csv +0 -54
  231. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/shadcn.csv +0 -61
  232. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/svelte.csv +0 -54
  233. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/swiftui.csv +0 -51
  234. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/vue.csv +0 -50
  235. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/styles.csv +0 -85
  236. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/typography.csv +0 -74
  237. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/ui-reasoning.csv +0 -162
  238. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/ux-guidelines.csv +0 -100
  239. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/data/web-interface.csv +0 -31
  240. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/scripts/core.py +0 -262
  241. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/scripts/design_system.py +0 -1148
  242. package/.grok/skills/frontend-ui-ux/references/ui-ux-db/scripts/search.py +0 -114
  243. package/.grok/skills/frontend-ui-ux/references/visual-language.md +0 -80
  244. package/.grok/skills/frontend-ui-ux/references/visual-reconstruction.md +0 -51
  245. package/.grok/skills/frontend-ui-ux/schemas/design-contract-v1alpha1.schema.json +0 -306
  246. package/.grok/skills/frontend-ui-ux/schemas/design-contract-v1beta1.schema.json +0 -291
  247. package/.grok/skills/frontend-ui-ux/schemas/design-contract-v1beta2.schema.json +0 -311
  248. package/.grok/skills/frontend-ui-ux/scripts/design-contract-format.mjs +0 -286
  249. package/.grok/skills/frontend-ui-ux/scripts/design-contract-inventory-rules.mjs +0 -354
  250. package/.grok/skills/frontend-ui-ux/scripts/design-contract-rules.mjs +0 -361
  251. package/.grok/skills/frontend-ui-ux/scripts/design-contract-surface-rules.mjs +0 -313
  252. package/.grok/skills/frontend-ui-ux/scripts/design-data.mjs +0 -68
  253. package/.grok/skills/frontend-ui-ux/scripts/design-lock.mjs +0 -400
  254. package/.grok/skills/frontend-ui-ux/scripts/errors.mjs +0 -12
  255. package/.grok/skills/frontend-ui-ux/scripts/import-design-intelligence.mjs +0 -124
  256. package/.grok/skills/frontend-ui-ux/scripts/interface-probe-driver.mjs +0 -33
  257. package/.grok/skills/frontend-ui-ux/scripts/interface-probe-lock.mjs +0 -196
  258. package/.grok/skills/frontend-ui-ux/scripts/interface-probe-page.mjs +0 -1555
  259. package/.grok/skills/frontend-ui-ux/scripts/interface-probe-rules.mjs +0 -153
  260. package/.grok/skills/frontend-ui-ux/scripts/interface-probe-server.mjs +0 -57
  261. package/.grok/skills/frontend-ui-ux/scripts/interface-probe-static.mjs +0 -139
  262. package/.grok/skills/frontend-ui-ux/scripts/interface-probe.mjs +0 -363
  263. package/.grok/skills/frontend-ui-ux/scripts/json-boundary.mjs +0 -63
  264. package/.grok/skills/frontend-ui-ux/scripts/pixel-art.mjs +0 -60
  265. package/.grok/skills/frontend-ui-ux/scripts/query-design-intelligence.mjs +0 -106
  266. package/.grok/skills/frontend-ui-ux/scripts/search.mjs +0 -43
  267. package/.grok/skills/frontend-ui-ux/scripts/source-replay.mjs +0 -153
  268. package/.grok/skills/frontend-ui-ux/scripts/strict-json.mjs +0 -143
  269. package/.grok/skills/frontend-ui-ux/scripts/validate-design-contract.mjs +0 -52
  270. package/.grok/skills/frontend-ui-ux/scripts/verify-canonical-corpus.mjs +0 -214
  271. package/.grok/skills/lit-diagram-drawer/NOTICE +0 -33
  272. package/.grok/skills/lit-diagram-drawer/assets/examples/type-architecture-dark.html +0 -1
  273. package/.grok/skills/lit-diagram-drawer/assets/examples/type-architecture-full.html +0 -1
  274. package/.grok/skills/lit-diagram-drawer/assets/examples/type-architecture-light.html +0 -1
  275. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bar-dark.html +0 -1
  276. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bar-full.html +0 -1
  277. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bar-light.html +0 -1
  278. package/.grok/skills/lit-diagram-drawer/assets/examples/type-beeswarm-dark.html +0 -1
  279. package/.grok/skills/lit-diagram-drawer/assets/examples/type-beeswarm-full.html +0 -1
  280. package/.grok/skills/lit-diagram-drawer/assets/examples/type-beeswarm-light.html +0 -1
  281. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bubble-dark.html +0 -1
  282. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bubble-full.html +0 -1
  283. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bubble-light.html +0 -1
  284. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bump-dark.html +0 -1
  285. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bump-full.html +0 -1
  286. package/.grok/skills/lit-diagram-drawer/assets/examples/type-bump-light.html +0 -1
  287. package/.grok/skills/lit-diagram-drawer/assets/examples/type-data-flow-dark.html +0 -1
  288. package/.grok/skills/lit-diagram-drawer/assets/examples/type-data-flow-full.html +0 -1
  289. package/.grok/skills/lit-diagram-drawer/assets/examples/type-data-flow-light.html +0 -1
  290. package/.grok/skills/lit-diagram-drawer/assets/examples/type-datalake-dark.html +0 -1
  291. package/.grok/skills/lit-diagram-drawer/assets/examples/type-datalake-full.html +0 -1
  292. package/.grok/skills/lit-diagram-drawer/assets/examples/type-datalake-light.html +0 -1
  293. package/.grok/skills/lit-diagram-drawer/assets/examples/type-db-schema-dark.html +0 -1
  294. package/.grok/skills/lit-diagram-drawer/assets/examples/type-db-schema-full.html +0 -1
  295. package/.grok/skills/lit-diagram-drawer/assets/examples/type-db-schema-light.html +0 -1
  296. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dependency-dark.html +0 -1
  297. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dependency-full.html +0 -1
  298. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dependency-light.html +0 -1
  299. package/.grok/skills/lit-diagram-drawer/assets/examples/type-deployment-dark.html +0 -1
  300. package/.grok/skills/lit-diagram-drawer/assets/examples/type-deployment-full.html +0 -1
  301. package/.grok/skills/lit-diagram-drawer/assets/examples/type-deployment-light.html +0 -1
  302. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dp-integration-dark.html +0 -1
  303. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dp-integration-full.html +0 -1
  304. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dp-integration-light.html +0 -1
  305. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-dark.html +0 -1
  306. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-full.html +0 -1
  307. package/.grok/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-light.html +0 -1
  308. package/.grok/skills/lit-diagram-drawer/assets/examples/type-er-dark.html +0 -1
  309. package/.grok/skills/lit-diagram-drawer/assets/examples/type-er-full.html +0 -1
  310. package/.grok/skills/lit-diagram-drawer/assets/examples/type-er-light.html +0 -1
  311. package/.grok/skills/lit-diagram-drawer/assets/examples/type-fishbone-dark.html +0 -1
  312. package/.grok/skills/lit-diagram-drawer/assets/examples/type-fishbone-full.html +0 -1
  313. package/.grok/skills/lit-diagram-drawer/assets/examples/type-fishbone-light.html +0 -1
  314. package/.grok/skills/lit-diagram-drawer/assets/examples/type-flowchart-dark.html +0 -1
  315. package/.grok/skills/lit-diagram-drawer/assets/examples/type-flowchart-full.html +0 -1
  316. package/.grok/skills/lit-diagram-drawer/assets/examples/type-flowchart-light.html +0 -1
  317. package/.grok/skills/lit-diagram-drawer/assets/examples/type-gantt-dark.html +0 -1
  318. package/.grok/skills/lit-diagram-drawer/assets/examples/type-gantt-full.html +0 -1
  319. package/.grok/skills/lit-diagram-drawer/assets/examples/type-gantt-light.html +0 -1
  320. package/.grok/skills/lit-diagram-drawer/assets/examples/type-heatmap-dark.html +0 -1
  321. package/.grok/skills/lit-diagram-drawer/assets/examples/type-heatmap-full.html +0 -1
  322. package/.grok/skills/lit-diagram-drawer/assets/examples/type-heatmap-light.html +0 -1
  323. package/.grok/skills/lit-diagram-drawer/assets/examples/type-high-level-dark.html +0 -1
  324. package/.grok/skills/lit-diagram-drawer/assets/examples/type-high-level-full.html +0 -1
  325. package/.grok/skills/lit-diagram-drawer/assets/examples/type-high-level-light.html +0 -1
  326. package/.grok/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-dark.html +0 -1
  327. package/.grok/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-full.html +0 -1
  328. package/.grok/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-light.html +0 -1
  329. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-drawio-dark.html +0 -1
  330. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-drawio-full.html +0 -1
  331. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-drawio-light.html +0 -1
  332. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-dark.html +0 -1
  333. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-full.html +0 -1
  334. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-light.html +0 -1
  335. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-dark.html +0 -1
  336. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-full.html +0 -1
  337. package/.grok/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-light.html +0 -1
  338. package/.grok/skills/lit-diagram-drawer/assets/examples/type-it-state-dark.html +0 -1
  339. package/.grok/skills/lit-diagram-drawer/assets/examples/type-it-state-full.html +0 -1
  340. package/.grok/skills/lit-diagram-drawer/assets/examples/type-it-state-light.html +0 -1
  341. package/.grok/skills/lit-diagram-drawer/assets/examples/type-journey-dark.html +0 -1
  342. package/.grok/skills/lit-diagram-drawer/assets/examples/type-journey-full.html +0 -1
  343. package/.grok/skills/lit-diagram-drawer/assets/examples/type-journey-light.html +0 -1
  344. package/.grok/skills/lit-diagram-drawer/assets/examples/type-kanban-dark.html +0 -1
  345. package/.grok/skills/lit-diagram-drawer/assets/examples/type-kanban-full.html +0 -1
  346. package/.grok/skills/lit-diagram-drawer/assets/examples/type-kanban-light.html +0 -1
  347. package/.grok/skills/lit-diagram-drawer/assets/examples/type-layers-dark.html +0 -1
  348. package/.grok/skills/lit-diagram-drawer/assets/examples/type-layers-full.html +0 -1
  349. package/.grok/skills/lit-diagram-drawer/assets/examples/type-layers-light.html +0 -1
  350. package/.grok/skills/lit-diagram-drawer/assets/examples/type-line-dark.html +0 -1
  351. package/.grok/skills/lit-diagram-drawer/assets/examples/type-line-full.html +0 -1
  352. package/.grok/skills/lit-diagram-drawer/assets/examples/type-line-light.html +0 -1
  353. package/.grok/skills/lit-diagram-drawer/assets/examples/type-loop-dark.html +0 -1
  354. package/.grok/skills/lit-diagram-drawer/assets/examples/type-loop-full.html +0 -1
  355. package/.grok/skills/lit-diagram-drawer/assets/examples/type-loop-light.html +0 -1
  356. package/.grok/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-dark.html +0 -1
  357. package/.grok/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-full.html +0 -1
  358. package/.grok/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-light.html +0 -1
  359. package/.grok/skills/lit-diagram-drawer/assets/examples/type-marimekko-dark.html +0 -1
  360. package/.grok/skills/lit-diagram-drawer/assets/examples/type-marimekko-full.html +0 -1
  361. package/.grok/skills/lit-diagram-drawer/assets/examples/type-marimekko-light.html +0 -1
  362. package/.grok/skills/lit-diagram-drawer/assets/examples/type-medallion-dark.html +0 -1
  363. package/.grok/skills/lit-diagram-drawer/assets/examples/type-medallion-full.html +0 -1
  364. package/.grok/skills/lit-diagram-drawer/assets/examples/type-medallion-light.html +0 -1
  365. package/.grok/skills/lit-diagram-drawer/assets/examples/type-nested-dark.html +0 -1
  366. package/.grok/skills/lit-diagram-drawer/assets/examples/type-nested-full.html +0 -1
  367. package/.grok/skills/lit-diagram-drawer/assets/examples/type-nested-light.html +0 -1
  368. package/.grok/skills/lit-diagram-drawer/assets/examples/type-org-chart-dark.html +0 -1
  369. package/.grok/skills/lit-diagram-drawer/assets/examples/type-org-chart-full.html +0 -1
  370. package/.grok/skills/lit-diagram-drawer/assets/examples/type-org-chart-light.html +0 -1
  371. package/.grok/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-dark.html +0 -1
  372. package/.grok/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-full.html +0 -1
  373. package/.grok/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-light.html +0 -1
  374. package/.grok/skills/lit-diagram-drawer/assets/examples/type-polar-dark.html +0 -1
  375. package/.grok/skills/lit-diagram-drawer/assets/examples/type-polar-full.html +0 -1
  376. package/.grok/skills/lit-diagram-drawer/assets/examples/type-polar-light.html +0 -1
  377. package/.grok/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-dark.html +0 -1
  378. package/.grok/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-full.html +0 -1
  379. package/.grok/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-light.html +0 -1
  380. package/.grok/skills/lit-diagram-drawer/assets/examples/type-process-dark.html +0 -1
  381. package/.grok/skills/lit-diagram-drawer/assets/examples/type-process-full.html +0 -1
  382. package/.grok/skills/lit-diagram-drawer/assets/examples/type-process-light.html +0 -1
  383. package/.grok/skills/lit-diagram-drawer/assets/examples/type-pyramid-dark.html +0 -1
  384. package/.grok/skills/lit-diagram-drawer/assets/examples/type-pyramid-full.html +0 -1
  385. package/.grok/skills/lit-diagram-drawer/assets/examples/type-pyramid-light.html +0 -1
  386. package/.grok/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-dark.html +0 -1
  387. package/.grok/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-full.html +0 -1
  388. package/.grok/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-light.html +0 -1
  389. package/.grok/skills/lit-diagram-drawer/assets/examples/type-quadrant-dark.html +0 -1
  390. package/.grok/skills/lit-diagram-drawer/assets/examples/type-quadrant-full.html +0 -1
  391. package/.grok/skills/lit-diagram-drawer/assets/examples/type-quadrant-light.html +0 -1
  392. package/.grok/skills/lit-diagram-drawer/assets/examples/type-queue-animated-dark.html +0 -1
  393. package/.grok/skills/lit-diagram-drawer/assets/examples/type-queue-animated-full.html +0 -1
  394. package/.grok/skills/lit-diagram-drawer/assets/examples/type-queue-animated-light.html +0 -1
  395. package/.grok/skills/lit-diagram-drawer/assets/examples/type-radar-dark.html +0 -1
  396. package/.grok/skills/lit-diagram-drawer/assets/examples/type-radar-full.html +0 -1
  397. package/.grok/skills/lit-diagram-drawer/assets/examples/type-radar-light.html +0 -1
  398. package/.grok/skills/lit-diagram-drawer/assets/examples/type-ridgeline-dark.html +0 -1
  399. package/.grok/skills/lit-diagram-drawer/assets/examples/type-ridgeline-full.html +0 -1
  400. package/.grok/skills/lit-diagram-drawer/assets/examples/type-ridgeline-light.html +0 -1
  401. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sankey-dark.html +0 -1
  402. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sankey-full.html +0 -1
  403. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sankey-light.html +0 -1
  404. package/.grok/skills/lit-diagram-drawer/assets/examples/type-scatter-dark.html +0 -1
  405. package/.grok/skills/lit-diagram-drawer/assets/examples/type-scatter-full.html +0 -1
  406. package/.grok/skills/lit-diagram-drawer/assets/examples/type-scatter-light.html +0 -1
  407. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sequence-dark.html +0 -1
  408. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sequence-full.html +0 -1
  409. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sequence-light.html +0 -1
  410. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-dark.html +0 -1
  411. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-full.html +0 -1
  412. package/.grok/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-light.html +0 -1
  413. package/.grok/skills/lit-diagram-drawer/assets/examples/type-slopegraph-dark.html +0 -1
  414. package/.grok/skills/lit-diagram-drawer/assets/examples/type-slopegraph-full.html +0 -1
  415. package/.grok/skills/lit-diagram-drawer/assets/examples/type-slopegraph-light.html +0 -1
  416. package/.grok/skills/lit-diagram-drawer/assets/examples/type-state-dark.html +0 -1
  417. package/.grok/skills/lit-diagram-drawer/assets/examples/type-state-full.html +0 -1
  418. package/.grok/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-dark.html +0 -1
  419. package/.grok/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-full.html +0 -1
  420. package/.grok/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-light.html +0 -1
  421. package/.grok/skills/lit-diagram-drawer/assets/examples/type-state-light.html +0 -1
  422. package/.grok/skills/lit-diagram-drawer/assets/examples/type-story-map-dark.html +0 -1
  423. package/.grok/skills/lit-diagram-drawer/assets/examples/type-story-map-full.html +0 -1
  424. package/.grok/skills/lit-diagram-drawer/assets/examples/type-story-map-light.html +0 -1
  425. package/.grok/skills/lit-diagram-drawer/assets/examples/type-streamgraph-dark.html +0 -1
  426. package/.grok/skills/lit-diagram-drawer/assets/examples/type-streamgraph-full.html +0 -1
  427. package/.grok/skills/lit-diagram-drawer/assets/examples/type-streamgraph-light.html +0 -1
  428. package/.grok/skills/lit-diagram-drawer/assets/examples/type-swimlane-dark.html +0 -1
  429. package/.grok/skills/lit-diagram-drawer/assets/examples/type-swimlane-full.html +0 -1
  430. package/.grok/skills/lit-diagram-drawer/assets/examples/type-swimlane-light.html +0 -1
  431. package/.grok/skills/lit-diagram-drawer/assets/examples/type-timeline-dark.html +0 -1
  432. package/.grok/skills/lit-diagram-drawer/assets/examples/type-timeline-full.html +0 -1
  433. package/.grok/skills/lit-diagram-drawer/assets/examples/type-timeline-light.html +0 -1
  434. package/.grok/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-dark.html +0 -1
  435. package/.grok/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-full.html +0 -1
  436. package/.grok/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-light.html +0 -1
  437. package/.grok/skills/lit-diagram-drawer/assets/examples/type-tree-dark.html +0 -1
  438. package/.grok/skills/lit-diagram-drawer/assets/examples/type-tree-full.html +0 -1
  439. package/.grok/skills/lit-diagram-drawer/assets/examples/type-tree-light.html +0 -1
  440. package/.grok/skills/lit-diagram-drawer/assets/examples/type-treemap-dark.html +0 -1
  441. package/.grok/skills/lit-diagram-drawer/assets/examples/type-treemap-full.html +0 -1
  442. package/.grok/skills/lit-diagram-drawer/assets/examples/type-treemap-light.html +0 -1
  443. package/.grok/skills/lit-diagram-drawer/assets/examples/type-uml-class-dark.html +0 -1
  444. package/.grok/skills/lit-diagram-drawer/assets/examples/type-uml-class-full.html +0 -1
  445. package/.grok/skills/lit-diagram-drawer/assets/examples/type-uml-class-light.html +0 -1
  446. package/.grok/skills/lit-diagram-drawer/assets/examples/type-venn-dark.html +0 -1
  447. package/.grok/skills/lit-diagram-drawer/assets/examples/type-venn-full.html +0 -1
  448. package/.grok/skills/lit-diagram-drawer/assets/examples/type-venn-light.html +0 -1
  449. package/.grok/skills/lit-diagram-drawer/assets/examples/type-wardley-dark.html +0 -1
  450. package/.grok/skills/lit-diagram-drawer/assets/examples/type-wardley-full.html +0 -1
  451. package/.grok/skills/lit-diagram-drawer/assets/examples/type-wardley-light.html +0 -1
  452. package/.grok/skills/lit-diagram-drawer/assets/examples/type-waterfall-dark.html +0 -1
  453. package/.grok/skills/lit-diagram-drawer/assets/examples/type-waterfall-full.html +0 -1
  454. package/.grok/skills/lit-diagram-drawer/assets/examples/type-waterfall-light.html +0 -1
  455. package/.grok/skills/lit-diagram-drawer/assets/fonts/OFL.txt +0 -94
  456. package/.grok/skills/lit-diagram-drawer/assets/fonts/PretendardVariable.woff2 +0 -0
  457. package/.grok/skills/lit-diagram-drawer/assets/fonts/provenance.json +0 -22
  458. package/.grok/skills/lit-diagram-drawer/assets/icons.html +0 -231
  459. package/.grok/skills/lit-diagram-drawer/assets/index.html +0 -1
  460. package/.grok/skills/lit-diagram-drawer/assets/licenses/devicon-MIT.txt +0 -20
  461. package/.grok/skills/lit-diagram-drawer/assets/licenses/diagram-design-MIT.txt +0 -21
  462. package/.grok/skills/lit-diagram-drawer/assets/licenses/log-z-MIT.txt +0 -21
  463. package/.grok/skills/lit-diagram-drawer/assets/licenses/simple-icons-CC0.md +0 -30
  464. package/.grok/skills/lit-diagram-drawer/assets/licenses/tabler-MIT.txt +0 -21
  465. package/.grok/skills/lit-diagram-drawer/examples/01-service-architecture/after.html +0 -1
  466. package/.grok/skills/lit-diagram-drawer/examples/01-service-architecture/brief.md +0 -33
  467. package/.grok/skills/lit-diagram-drawer/examples/01-service-architecture/constructed-naive-foil.html +0 -1
  468. package/.grok/skills/lit-diagram-drawer/examples/02-release-decision/after.html +0 -1
  469. package/.grok/skills/lit-diagram-drawer/examples/02-release-decision/brief.md +0 -18
  470. package/.grok/skills/lit-diagram-drawer/examples/02-release-decision/constructed-naive-foil.html +0 -1
  471. package/.grok/skills/lit-diagram-drawer/examples/03-data-retention/after.html +0 -1
  472. package/.grok/skills/lit-diagram-drawer/examples/03-data-retention/brief.md +0 -19
  473. package/.grok/skills/lit-diagram-drawer/examples/03-data-retention/constructed-naive-foil.html +0 -1
  474. package/.grok/skills/lit-diagram-drawer/examples/04-oauth-sequence/after.html +0 -1
  475. package/.grok/skills/lit-diagram-drawer/examples/04-oauth-sequence/brief.md +0 -17
  476. package/.grok/skills/lit-diagram-drawer/examples/04-oauth-sequence/constructed-naive-foil.html +0 -1
  477. package/.grok/skills/lit-diagram-drawer/examples/05-review-lanes/after.html +0 -1
  478. package/.grok/skills/lit-diagram-drawer/examples/05-review-lanes/brief.md +0 -17
  479. package/.grok/skills/lit-diagram-drawer/examples/05-review-lanes/constructed-naive-foil.html +0 -1
  480. package/.grok/skills/lit-diagram-drawer/examples/06-rollout-gates/after.html +0 -1
  481. package/.grok/skills/lit-diagram-drawer/examples/06-rollout-gates/brief.md +0 -17
  482. package/.grok/skills/lit-diagram-drawer/examples/06-rollout-gates/constructed-naive-foil.html +0 -1
  483. package/.grok/skills/lit-diagram-drawer/examples/07-deployment-boundary/after.html +0 -1
  484. package/.grok/skills/lit-diagram-drawer/examples/07-deployment-boundary/brief.md +0 -19
  485. package/.grok/skills/lit-diagram-drawer/examples/07-deployment-boundary/constructed-naive-foil.html +0 -1
  486. package/.grok/skills/lit-diagram-drawer/examples/08-policy-traces/after.html +0 -1
  487. package/.grok/skills/lit-diagram-drawer/examples/08-policy-traces/brief.md +0 -20
  488. package/.grok/skills/lit-diagram-drawer/examples/08-policy-traces/constructed-naive-foil.html +0 -1
  489. package/.grok/skills/lit-diagram-drawer/references/accessibility.md +0 -37
  490. package/.grok/skills/lit-diagram-drawer/references/export.md +0 -29
  491. package/.grok/skills/lit-diagram-drawer/references/import-drawio.md +0 -21
  492. package/.grok/skills/lit-diagram-drawer/references/import-excalidraw.md +0 -14
  493. package/.grok/skills/lit-diagram-drawer/references/import-mermaid.md +0 -18
  494. package/.grok/skills/lit-diagram-drawer/references/import-schema.md +0 -28
  495. package/.grok/skills/lit-diagram-drawer/references/korean-typography.md +0 -34
  496. package/.grok/skills/lit-diagram-drawer/references/motion.md +0 -29
  497. package/.grok/skills/lit-diagram-drawer/references/office-pptx-docx.md +0 -42
  498. package/.grok/skills/lit-diagram-drawer/references/output-spec.md +0 -48
  499. package/.grok/skills/lit-diagram-drawer/references/primitive-annotation.md +0 -12
  500. package/.grok/skills/lit-diagram-drawer/references/primitive-icons.md +0 -21
  501. package/.grok/skills/lit-diagram-drawer/references/primitive-sketchy.md +0 -11
  502. package/.grok/skills/lit-diagram-drawer/references/primitive-terminal.md +0 -11
  503. package/.grok/skills/lit-diagram-drawer/references/profiles.md +0 -24
  504. package/.grok/skills/lit-diagram-drawer/references/semantic-patterns.md +0 -27
  505. package/.grok/skills/lit-diagram-drawer/references/style-guide.md +0 -76
  506. package/.grok/skills/lit-diagram-drawer/references/type-architecture.md +0 -51
  507. package/.grok/skills/lit-diagram-drawer/references/type-bar.md +0 -51
  508. package/.grok/skills/lit-diagram-drawer/references/type-beeswarm.md +0 -49
  509. package/.grok/skills/lit-diagram-drawer/references/type-bubble.md +0 -49
  510. package/.grok/skills/lit-diagram-drawer/references/type-bump.md +0 -49
  511. package/.grok/skills/lit-diagram-drawer/references/type-catalog.json +0 -1433
  512. package/.grok/skills/lit-diagram-drawer/references/type-data-flow.md +0 -51
  513. package/.grok/skills/lit-diagram-drawer/references/type-datalake.md +0 -49
  514. package/.grok/skills/lit-diagram-drawer/references/type-db-schema.md +0 -49
  515. package/.grok/skills/lit-diagram-drawer/references/type-dependency.md +0 -49
  516. package/.grok/skills/lit-diagram-drawer/references/type-deployment.md +0 -49
  517. package/.grok/skills/lit-diagram-drawer/references/type-dp-integration.md +0 -49
  518. package/.grok/skills/lit-diagram-drawer/references/type-dp-security-matrix.md +0 -49
  519. package/.grok/skills/lit-diagram-drawer/references/type-er.md +0 -49
  520. package/.grok/skills/lit-diagram-drawer/references/type-fishbone.md +0 -49
  521. package/.grok/skills/lit-diagram-drawer/references/type-flowchart.md +0 -49
  522. package/.grok/skills/lit-diagram-drawer/references/type-gantt.md +0 -49
  523. package/.grok/skills/lit-diagram-drawer/references/type-heatmap.md +0 -53
  524. package/.grok/skills/lit-diagram-drawer/references/type-high-level-vertical.md +0 -53
  525. package/.grok/skills/lit-diagram-drawer/references/type-high-level.md +0 -53
  526. package/.grok/skills/lit-diagram-drawer/references/type-import-drawio.md +0 -53
  527. package/.grok/skills/lit-diagram-drawer/references/type-import-excalidraw.md +0 -53
  528. package/.grok/skills/lit-diagram-drawer/references/type-import-mermaid.md +0 -53
  529. package/.grok/skills/lit-diagram-drawer/references/type-it-state.md +0 -54
  530. package/.grok/skills/lit-diagram-drawer/references/type-journey.md +0 -54
  531. package/.grok/skills/lit-diagram-drawer/references/type-kanban.md +0 -54
  532. package/.grok/skills/lit-diagram-drawer/references/type-layers.md +0 -54
  533. package/.grok/skills/lit-diagram-drawer/references/type-line.md +0 -54
  534. package/.grok/skills/lit-diagram-drawer/references/type-loop-terminal.md +0 -53
  535. package/.grok/skills/lit-diagram-drawer/references/type-loop.md +0 -54
  536. package/.grok/skills/lit-diagram-drawer/references/type-marimekko.md +0 -54
  537. package/.grok/skills/lit-diagram-drawer/references/type-medallion.md +0 -54
  538. package/.grok/skills/lit-diagram-drawer/references/type-nested.md +0 -59
  539. package/.grok/skills/lit-diagram-drawer/references/type-org-chart.md +0 -58
  540. package/.grok/skills/lit-diagram-drawer/references/type-paved-road-animated.md +0 -72
  541. package/.grok/skills/lit-diagram-drawer/references/type-polar.md +0 -67
  542. package/.grok/skills/lit-diagram-drawer/references/type-policy-trace-animated.md +0 -69
  543. package/.grok/skills/lit-diagram-drawer/references/type-process.md +0 -58
  544. package/.grok/skills/lit-diagram-drawer/references/type-pyramid.md +0 -59
  545. package/.grok/skills/lit-diagram-drawer/references/type-quadrant-consultant.md +0 -60
  546. package/.grok/skills/lit-diagram-drawer/references/type-quadrant.md +0 -57
  547. package/.grok/skills/lit-diagram-drawer/references/type-queue-animated.md +0 -63
  548. package/.grok/skills/lit-diagram-drawer/references/type-radar.md +0 -57
  549. package/.grok/skills/lit-diagram-drawer/references/type-ridgeline.md +0 -63
  550. package/.grok/skills/lit-diagram-drawer/references/type-sankey.md +0 -68
  551. package/.grok/skills/lit-diagram-drawer/references/type-scatter.md +0 -60
  552. package/.grok/skills/lit-diagram-drawer/references/type-sequence-oauth.md +0 -66
  553. package/.grok/skills/lit-diagram-drawer/references/type-sequence.md +0 -63
  554. package/.grok/skills/lit-diagram-drawer/references/type-slopegraph.md +0 -65
  555. package/.grok/skills/lit-diagram-drawer/references/type-state-lifecycle.md +0 -53
  556. package/.grok/skills/lit-diagram-drawer/references/type-state.md +0 -59
  557. package/.grok/skills/lit-diagram-drawer/references/type-story-map.md +0 -52
  558. package/.grok/skills/lit-diagram-drawer/references/type-streamgraph.md +0 -53
  559. package/.grok/skills/lit-diagram-drawer/references/type-swimlane.md +0 -52
  560. package/.grok/skills/lit-diagram-drawer/references/type-timeline.md +0 -52
  561. package/.grok/skills/lit-diagram-drawer/references/type-tree-block-decomposition.md +0 -57
  562. package/.grok/skills/lit-diagram-drawer/references/type-tree.md +0 -54
  563. package/.grok/skills/lit-diagram-drawer/references/type-treemap.md +0 -54
  564. package/.grok/skills/lit-diagram-drawer/references/type-uml-class.md +0 -52
  565. package/.grok/skills/lit-diagram-drawer/references/type-venn.md +0 -51
  566. package/.grok/skills/lit-diagram-drawer/references/type-wardley.md +0 -52
  567. package/.grok/skills/lit-diagram-drawer/references/type-waterfall.md +0 -54
  568. package/.grok/skills/lit-diagram-drawer/references/verifier-guide.md +0 -64
  569. package/.grok/skills/lit-diagram-drawer/scripts/block-registry.mjs +0 -43
  570. package/.grok/skills/lit-diagram-drawer/scripts/brief-contract.mjs +0 -94
  571. package/.grok/skills/lit-diagram-drawer/scripts/check-visible-text.mjs +0 -15
  572. package/.grok/skills/lit-diagram-drawer/scripts/diagram-metrics.mjs +0 -219
  573. package/.grok/skills/lit-diagram-drawer/scripts/doctor.mjs +0 -22
  574. package/.grok/skills/lit-diagram-drawer/scripts/drawio-extract.mjs +0 -359
  575. package/.grok/skills/lit-diagram-drawer/scripts/excalidraw-extract.mjs +0 -196
  576. package/.grok/skills/lit-diagram-drawer/scripts/export.mjs +0 -117
  577. package/.grok/skills/lit-diagram-drawer/scripts/import-common.mjs +0 -68
  578. package/.grok/skills/lit-diagram-drawer/scripts/mermaid-extract.mjs +0 -232
  579. package/.grok/skills/lit-diagram-drawer/scripts/office-safety.mjs +0 -54
  580. package/.grok/skills/lit-diagram-drawer/scripts/renderer-status.mjs +0 -43
  581. package/.grok/skills/lit-diagram-drawer/scripts/verify-all.mjs +0 -73
  582. package/.grok/skills/lit-diagram-drawer/scripts/verify-brief.mjs +0 -16
  583. package/.grok/skills/lit-diagram-drawer/scripts/verify-coverage.mjs +0 -56
  584. package/.grok/skills/lit-diagram-drawer/scripts/verify-diagram.mjs +0 -22
  585. package/.grok/skills/lit-diagram-drawer/scripts/verify-motion.mjs +0 -12
  586. package/.grok/skills/lit-diagram-drawer/scripts/verify-type.mjs +0 -48
  587. package/.grok/skills/lit-diagram-drawer/scripts/visual-quality.mjs +0 -344
  588. package/.grok/skills/lit-docx/LICENSE +0 -21
  589. package/.grok/skills/lit-docx/NOTICE.md +0 -1
  590. package/.grok/skills/lit-docx/examples/01-report-ko.md +0 -137
  591. package/.grok/skills/lit-docx/examples/02-brief-ko.md +0 -82
  592. package/.grok/skills/lit-docx/examples/03-manual-ko.md +0 -118
  593. package/.grok/skills/lit-docx/examples/04-proposal-ko.md +0 -92
  594. package/.grok/skills/lit-docx/examples/05-memo-en.md +0 -42
  595. package/.grok/skills/lit-docx/examples/06-journal-en.md +0 -90
  596. package/.grok/skills/lit-docx/examples/07-report-en.md +0 -103
  597. package/.grok/skills/lit-docx/examples/08-proposal-en.md +0 -90
  598. package/.grok/skills/lit-docx/examples/09-memo-ko.md +0 -41
  599. package/.grok/skills/lit-docx/references/components.md +0 -151
  600. package/.grok/skills/lit-docx/references/direction-step.md +0 -129
  601. package/.grok/skills/lit-docx/references/frontmatter_schema.md +0 -42
  602. package/.grok/skills/lit-docx/references/journal_style_spec.md +0 -17
  603. package/.grok/skills/lit-docx/references/markdown_quality_checklist.md +0 -37
  604. package/.grok/skills/lit-docx/references/page-composition.md +0 -129
  605. package/.grok/skills/lit-docx/references/slop_phrase_list.yaml +0 -58
  606. package/.grok/skills/lit-docx/references/slop_rules.md +0 -35
  607. package/.grok/skills/lit-docx/references/tonalities/brief.md +0 -98
  608. package/.grok/skills/lit-docx/references/tonalities/journal.md +0 -100
  609. package/.grok/skills/lit-docx/references/tonalities/manual.md +0 -107
  610. package/.grok/skills/lit-docx/references/tonalities/memo.md +0 -95
  611. package/.grok/skills/lit-docx/references/tonalities/proposal.md +0 -104
  612. package/.grok/skills/lit-docx/references/tonalities/report.md +0 -114
  613. package/.grok/skills/lit-docx/requirements.lock +0 -12
  614. package/.grok/skills/lit-docx/scripts/clean_markdown.py +0 -95
  615. package/.grok/skills/lit-docx/scripts/convert_md_to_docx.py +0 -1377
  616. package/.grok/skills/lit-docx/scripts/convert_md_to_pdf.py +0 -284
  617. package/.grok/skills/lit-docx/scripts/convert_pdf.py +0 -130
  618. package/.grok/skills/lit-docx/scripts/docx_design.py +0 -2446
  619. package/.grok/skills/lit-docx/scripts/docx_gate.py +0 -206
  620. package/.grok/skills/lit-docx/scripts/docx_layout.py +0 -959
  621. package/.grok/skills/lit-docx/scripts/edit_docx.py +0 -297
  622. package/.grok/skills/lit-docx/scripts/embed_images.py +0 -78
  623. package/.grok/skills/lit-docx/scripts/generate_docx_templates.py +0 -491
  624. package/.grok/skills/lit-docx/scripts/pdf_fonts.py +0 -63
  625. package/.grok/skills/lit-docx/scripts/run.mjs +0 -23
  626. package/.grok/skills/lit-docx/scripts/slop_lint.py +0 -563
  627. package/.grok/skills/lit-docx/scripts/visual_audit.py +0 -41
  628. package/.grok/skills/lit-docx/templates/docx/acs.docx +0 -0
  629. package/.grok/skills/lit-docx/templates/docx/elsevier.docx +0 -0
  630. package/.grok/skills/lit-docx/templates/docx/ieee.docx +0 -0
  631. package/.grok/skills/lit-docx/templates/docx/korean-generic.docx +0 -0
  632. package/.grok/skills/lit-docx/templates/docx/nature.docx +0 -0
  633. package/.grok/skills/lit-docx/templates/latex/acs/README.md +0 -5
  634. package/.grok/skills/lit-docx/templates/latex/acs/template.tex +0 -42
  635. package/.grok/skills/lit-docx/templates/latex/elsevier/README.md +0 -5
  636. package/.grok/skills/lit-docx/templates/latex/elsevier/template.tex +0 -43
  637. package/.grok/skills/lit-docx/templates/latex/ieee/README.md +0 -5
  638. package/.grok/skills/lit-docx/templates/latex/ieee/template.tex +0 -42
  639. package/.grok/skills/lit-docx/templates/latex/korean-generic/README.md +0 -5
  640. package/.grok/skills/lit-docx/templates/latex/korean-generic/template.tex +0 -42
  641. package/.grok/skills/lit-docx/templates/latex/nature/README.md +0 -5
  642. package/.grok/skills/lit-docx/templates/latex/nature/template.tex +0 -42
  643. package/.grok/skills/lit-docx/templates/registry.yaml +0 -1366
  644. package/.grok/skills/lit-docx/templates/tonalities/brief.yaml +0 -28
  645. package/.grok/skills/lit-docx/templates/tonalities/journal.yaml +0 -25
  646. package/.grok/skills/lit-docx/templates/tonalities/manual.yaml +0 -26
  647. package/.grok/skills/lit-docx/templates/tonalities/memo.yaml +0 -26
  648. package/.grok/skills/lit-docx/templates/tonalities/proposal.yaml +0 -26
  649. package/.grok/skills/lit-docx/templates/tonalities/report.yaml +0 -26
  650. package/.grok/skills/lit-pptx/FORBIDDEN_TERMS.json +0 -55
  651. package/.grok/skills/lit-pptx/NOTICE.md +0 -1
  652. package/.grok/skills/lit-pptx/agents/analyst.md +0 -49
  653. package/.grok/skills/lit-pptx/agents/antithesis.md +0 -59
  654. package/.grok/skills/lit-pptx/agents/architect.md +0 -44
  655. package/.grok/skills/lit-pptx/agents/critic.md +0 -42
  656. package/.grok/skills/lit-pptx/agents/synthesizer.md +0 -47
  657. package/.grok/skills/lit-pptx/agents/thesis.md +0 -33
  658. package/.grok/skills/lit-pptx/assets/azure/circle-lg.png +0 -0
  659. package/.grok/skills/lit-pptx/assets/azure/circle-md.png +0 -0
  660. package/.grok/skills/lit-pptx/assets/azure/circle-sm.png +0 -0
  661. package/.grok/skills/lit-pptx/examples/01-pitch-signal-ko.md +0 -175
  662. package/.grok/skills/lit-pptx/examples/02-business-review-ledger-ko.md +0 -199
  663. package/.grok/skills/lit-pptx/examples/03-research-talk-paper-en.md +0 -177
  664. package/.grok/skills/lit-pptx/examples/04-lecture-chalk-ko.md +0 -176
  665. package/.grok/skills/lit-pptx/examples/05-data-review-night-en.md +0 -221
  666. package/.grok/skills/lit-pptx/examples/06-image-led-atlas-en.md +0 -143
  667. package/.grok/skills/lit-pptx/examples/07-briefing-gazette-ko.md +0 -192
  668. package/.grok/skills/lit-pptx/examples/08-status-update-ledger-en.md +0 -168
  669. package/.grok/skills/lit-pptx/examples/09-pitch-studio-en.md +0 -157
  670. package/.grok/skills/lit-pptx/examples/10-image-led-studio-ko.md +0 -175
  671. package/.grok/skills/lit-pptx/examples/11-business-review-night-en.md +0 -181
  672. package/.grok/skills/lit-pptx/examples/12-status-update-gazette-ko.md +0 -199
  673. package/.grok/skills/lit-pptx/examples/13-engineering-review-computational-brutalism-ko.md +0 -173
  674. package/.grok/skills/lit-pptx/examples/14-launch-pitch-neo-brutalism-ko.md +0 -154
  675. package/.grok/skills/lit-pptx/examples/15-design-lecture-bauhaus-ko.md +0 -139
  676. package/.grok/skills/lit-pptx/examples/16-community-programme-riso-ko.md +0 -158
  677. package/.grok/skills/lit-pptx/examples/17-public-data-briefing-newsroom-ko.md +0 -179
  678. package/.grok/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
  679. package/.grok/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
  680. package/.grok/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +0 -93
  681. package/.grok/skills/lit-pptx/fonts/jetbrains-mono/provenance.json +0 -1
  682. package/.grok/skills/lit-pptx/package-lock.json +0 -795
  683. package/.grok/skills/lit-pptx/package.json +0 -13
  684. package/.grok/skills/lit-pptx/pretendard-font/OFL.txt +0 -94
  685. package/.grok/skills/lit-pptx/pretendard-font/public/static/Pretendard-Bold.otf +0 -0
  686. package/.grok/skills/lit-pptx/pretendard-font/public/static/Pretendard-Regular.otf +0 -0
  687. package/.grok/skills/lit-pptx/references/density-and-fill.md +0 -155
  688. package/.grok/skills/lit-pptx/references/direction-step.md +0 -237
  689. package/.grok/skills/lit-pptx/references/layout-families.md +0 -193
  690. package/.grok/skills/lit-pptx/references/title-treatments.md +0 -134
  691. package/.grok/skills/lit-pptx/references/tonalities/atlas.md +0 -121
  692. package/.grok/skills/lit-pptx/references/tonalities/bauhaus.md +0 -140
  693. package/.grok/skills/lit-pptx/references/tonalities/chalk.md +0 -133
  694. package/.grok/skills/lit-pptx/references/tonalities/computational-brutalism.md +0 -150
  695. package/.grok/skills/lit-pptx/references/tonalities/gazette.md +0 -125
  696. package/.grok/skills/lit-pptx/references/tonalities/ledger.md +0 -132
  697. package/.grok/skills/lit-pptx/references/tonalities/neo-brutalism.md +0 -161
  698. package/.grok/skills/lit-pptx/references/tonalities/newsroom.md +0 -144
  699. package/.grok/skills/lit-pptx/references/tonalities/night.md +0 -129
  700. package/.grok/skills/lit-pptx/references/tonalities/paper.md +0 -125
  701. package/.grok/skills/lit-pptx/references/tonalities/riso.md +0 -137
  702. package/.grok/skills/lit-pptx/references/tonalities/signal.md +0 -116
  703. package/.grok/skills/lit-pptx/references/tonalities/studio.md +0 -121
  704. package/.grok/skills/lit-pptx/requirements.lock +0 -12
  705. package/.grok/skills/lit-pptx/scripts/compile-deck.js +0 -451
  706. package/.grok/skills/lit-pptx/scripts/craft_extras.py +0 -307
  707. package/.grok/skills/lit-pptx/scripts/deck_output.py +0 -612
  708. package/.grok/skills/lit-pptx/scripts/deps.cjs +0 -83
  709. package/.grok/skills/lit-pptx/scripts/embed_fonts.py +0 -213
  710. package/.grok/skills/lit-pptx/scripts/inventory.py +0 -74
  711. package/.grok/skills/lit-pptx/scripts/learn_template.py +0 -433
  712. package/.grok/skills/lit-pptx/scripts/lib/derive-palette.js +0 -176
  713. package/.grok/skills/lit-pptx/scripts/lib/directive-parser.js +0 -244
  714. package/.grok/skills/lit-pptx/scripts/lib/font-map.js +0 -79
  715. package/.grok/skills/lit-pptx/scripts/lib/gen-bg.js +0 -85
  716. package/.grok/skills/lit-pptx/scripts/lib/gen-gradient-assets.js +0 -36
  717. package/.grok/skills/lit-pptx/scripts/lib/grid-resolver.js +0 -512
  718. package/.grok/skills/lit-pptx/scripts/lib/layout-resolver.js +0 -1113
  719. package/.grok/skills/lit-pptx/scripts/lib/markdown-loader.js +0 -138
  720. package/.grok/skills/lit-pptx/scripts/lib/render-adapter-html.js +0 -356
  721. package/.grok/skills/lit-pptx/scripts/lib/render-adapter-pptx.js +0 -2236
  722. package/.grok/skills/lit-pptx/scripts/lib/render-pack.js +0 -3365
  723. package/.grok/skills/lit-pptx/scripts/lib/slide-ast.js +0 -565
  724. package/.grok/skills/lit-pptx/scripts/lib/slide-grid.js +0 -104
  725. package/.grok/skills/lit-pptx/scripts/lib/spec-validator.js +0 -94
  726. package/.grok/skills/lit-pptx/scripts/lib/template-registry.js +0 -730
  727. package/.grok/skills/lit-pptx/scripts/ooxml_integrity.py +0 -37
  728. package/.grok/skills/lit-pptx/scripts/qa_deck.py +0 -809
  729. package/.grok/skills/lit-pptx/scripts/run.mjs +0 -25
  730. package/.grok/skills/lit-pptx/scripts/validate_pptx.py +0 -223
  731. package/.grok/skills/lit-pptx/specs/markdown-slide-spec-v1.md +0 -251
  732. package/.grok/skills/lit-pptx/specs/markdown-slide-spec-v2.md +0 -207
  733. package/.grok/skills/lit-pptx/specs/slide-ast-v1.schema.json +0 -229
  734. package/.grok/skills/lit-pptx/specs/slide-ast-v2.schema.json +0 -144
  735. package/.grok/skills/lit-pptx/specs/template-enrollment-contract.md +0 -379
  736. package/.grok/skills/lit-pptx/templates/bauhaus_light.css +0 -4
  737. package/.grok/skills/lit-pptx/templates/boilerplate_professional.css +0 -508
  738. package/.grok/skills/lit-pptx/templates/enrolled/AZURE-A2Z/capabilities.yaml +0 -146
  739. package/.grok/skills/lit-pptx/templates/enrolled/AZURE-A2Z/layout-mapping.yaml +0 -454
  740. package/.grok/skills/lit-pptx/templates/enrolled/AZURE-A2Z/template.yaml +0 -40
  741. package/.grok/skills/lit-pptx/templates/enrolled/AZURE-PRO/capabilities.yaml +0 -154
  742. package/.grok/skills/lit-pptx/templates/enrolled/AZURE-PRO/layout-mapping.yaml +0 -435
  743. package/.grok/skills/lit-pptx/templates/enrolled/AZURE-PRO/template.yaml +0 -40
  744. package/.grok/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/capabilities.yaml +0 -179
  745. package/.grok/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/layout-mapping.yaml +0 -299
  746. package/.grok/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/template.yaml +0 -45
  747. package/.grok/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/capabilities.yaml +0 -181
  748. package/.grok/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/layout-mapping.yaml +0 -299
  749. package/.grok/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/template.yaml +0 -44
  750. package/.grok/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +0 -81
  751. package/.grok/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +0 -98
  752. package/.grok/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +0 -87
  753. package/.grok/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +0 -99
  754. package/.grok/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +0 -81
  755. package/.grok/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +0 -82
  756. package/.grok/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +0 -107
  757. package/.grok/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +0 -99
  758. package/.grok/skills/lit-pptx/templates/tonalities/night/pack.yaml +0 -82
  759. package/.grok/skills/lit-pptx/templates/tonalities/paper/pack.yaml +0 -87
  760. package/.grok/skills/lit-pptx/templates/tonalities/riso/pack.yaml +0 -99
  761. package/.grok/skills/lit-pptx/templates/tonalities/signal/pack.yaml +0 -83
  762. package/.grok/skills/lit-pptx/templates/tonalities/studio/pack.yaml +0 -81
  763. package/.grok/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +0 -38
  764. package/.grok/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +0 -133
  765. package/.grok/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +0 -87
  766. package/.grok/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +0 -85
  767. package/.grok/skills/lit-schematic/references/cutaway-outputs.md +0 -70
  768. package/.grok/skills/lit-schematic/references/isometric-craft.md +0 -101
  769. package/.grok/skills/lit-schematic/references/journal-figures.md +0 -72
  770. package/.grok/skills/lit-schematic/references/kit-api.md +0 -134
  771. package/.grok/skills/lit-schematic/references/pfd-and-layout.md +0 -53
  772. package/.grok/skills/lit-schematic/references/pid-drafting.md +0 -103
  773. package/.grok/skills/lit-schematic/references/pid-symbols.md +0 -163
  774. package/.grok/skills/lit-schematic/references/verification.md +0 -100
  775. package/.grok/skills/lit-schematic/scripts/doctor.mjs +0 -57
  776. package/.grok/skills/lit-schematic/scripts/lib/figure.mjs +0 -81
  777. package/.grok/skills/lit-schematic/scripts/lib/fonts.mjs +0 -27
  778. package/.grok/skills/lit-schematic/scripts/lib/geom.mjs +0 -113
  779. package/.grok/skills/lit-schematic/scripts/lib/interactive.mjs +0 -292
  780. package/.grok/skills/lit-schematic/scripts/lib/isa.mjs +0 -115
  781. package/.grok/skills/lit-schematic/scripts/lib/iso.mjs +0 -719
  782. package/.grok/skills/lit-schematic/scripts/lib/legend-sheet.mjs +0 -162
  783. package/.grok/skills/lit-schematic/scripts/lib/pid.mjs +0 -937
  784. package/.grok/skills/lit-schematic/scripts/lib/png.mjs +0 -132
  785. package/.grok/skills/lit-schematic/scripts/lib/qa-core.mjs +0 -658
  786. package/.grok/skills/lit-schematic/scripts/lib/route.mjs +0 -218
  787. package/.grok/skills/lit-schematic/scripts/lib/sheet.mjs +0 -890
  788. package/.grok/skills/lit-schematic/scripts/lib/svg.mjs +0 -82
  789. package/.grok/skills/lit-schematic/scripts/lib/theme.mjs +0 -115
  790. package/.grok/skills/lit-schematic/scripts/motion.mjs +0 -129
  791. package/.grok/skills/lit-schematic/scripts/qa.mjs +0 -92
  792. package/.grok/skills/lit-schematic/scripts/render.mjs +0 -233
  793. package/.grok/skills/lit-schematic/scripts/scaffold.mjs +0 -60
  794. package/.grok/skills/lit-scientific-visualization/scripts/verify-canonical-corpus.mjs +0 -181
  795. package/.grok/skills/lit-typographic-motion/NOTICE +0 -23
  796. package/.grok/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +0 -56
  797. package/.grok/skills/lit-typographic-motion/fonts/Archivo-OFL.txt +0 -93
  798. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w100-wt400.ttf +0 -0
  799. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w100-wt700.ttf +0 -0
  800. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w100-wt900.ttf +0 -0
  801. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w125-wt400.ttf +0 -0
  802. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w125-wt700.ttf +0 -0
  803. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w125-wt900.ttf +0 -0
  804. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w75-wt400.ttf +0 -0
  805. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w75-wt700.ttf +0 -0
  806. package/.grok/skills/lit-typographic-motion/fonts/Archivo-w75-wt900.ttf +0 -0
  807. package/.grok/skills/lit-typographic-motion/fonts/JetBrainsMono-OFL.txt +0 -93
  808. package/.grok/skills/lit-typographic-motion/fonts/JetBrainsMono-wght-latin.woff2 +0 -0
  809. package/.grok/skills/lit-typographic-motion/fonts/Silkscreen-Bold.ttf +0 -0
  810. package/.grok/skills/lit-typographic-motion/fonts/Silkscreen-OFL.txt +0 -93
  811. package/.grok/skills/lit-typographic-motion/fonts/Silkscreen-Regular.ttf +0 -0
  812. package/.grok/skills/lit-typographic-motion/fonts/VT323-OFL.txt +0 -93
  813. package/.grok/skills/lit-typographic-motion/fonts/VT323-Regular.ttf +0 -0
  814. package/.grok/skills/lit-typographic-motion/fonts/pins.json +0 -386
  815. package/.grok/skills/lit-typographic-motion/fonts/stroke/CREDITS +0 -41
  816. package/.grok/skills/lit-typographic-motion/fonts/stroke/EMSAllure.svg +0 -216
  817. package/.grok/skills/lit-typographic-motion/fonts/stroke/EMSFelix.svg +0 -216
  818. package/.grok/skills/lit-typographic-motion/fonts/stroke/EMSOsmotron.svg +0 -216
  819. package/.grok/skills/lit-typographic-motion/fonts/stroke/EMSReadability.svg +0 -216
  820. package/.grok/skills/lit-typographic-motion/fonts/stroke/EMSTech.svg +0 -217
  821. package/.grok/skills/lit-typographic-motion/fonts/stroke/OFL-1.1.txt +0 -91
  822. package/.grok/skills/lit-typographic-motion/package-lock.json +0 -63
  823. package/.grok/skills/lit-typographic-motion/package.json +0 -10
  824. package/.grok/skills/lit-typographic-motion/recipes/hero-3d/expected.json +0 -62
  825. package/.grok/skills/lit-typographic-motion/recipes/hero-3d/index.html +0 -82
  826. package/.grok/skills/lit-typographic-motion/recipes/negatives/card-scenes/expected.json +0 -16
  827. package/.grok/skills/lit-typographic-motion/recipes/negatives/card-scenes/index.html +0 -36
  828. package/.grok/skills/lit-typographic-motion/recipes/negatives/reset-sweep/expected.json +0 -16
  829. package/.grok/skills/lit-typographic-motion/recipes/negatives/reset-sweep/index.html +0 -24
  830. package/.grok/skills/lit-typographic-motion/recipes/negatives/still-glow/expected.json +0 -17
  831. package/.grok/skills/lit-typographic-motion/recipes/negatives/still-glow/index.html +0 -25
  832. package/.grok/skills/lit-typographic-motion/recipes/negatives/title-slam/expected.json +0 -17
  833. package/.grok/skills/lit-typographic-motion/recipes/negatives/title-slam/index.html +0 -37
  834. package/.grok/skills/lit-typographic-motion/recipes/orbit-loop/expected.json +0 -67
  835. package/.grok/skills/lit-typographic-motion/recipes/orbit-loop/index.html +0 -119
  836. package/.grok/skills/lit-typographic-motion/recipes/painted-light/expected.json +0 -95
  837. package/.grok/skills/lit-typographic-motion/recipes/painted-light/index.html +0 -192
  838. package/.grok/skills/lit-typographic-motion/recipes/weight-tide/expected.json +0 -67
  839. package/.grok/skills/lit-typographic-motion/recipes/weight-tide/index.html +0 -85
  840. package/.grok/skills/lit-typographic-motion/references/choice-first.md +0 -30
  841. package/.grok/skills/lit-typographic-motion/references/complete-contract.md +0 -124
  842. package/.grok/skills/lit-typographic-motion/references/cover.md +0 -389
  843. package/.grok/skills/lit-typographic-motion/references/craft-loop.md +0 -112
  844. package/.grok/skills/lit-typographic-motion/references/depth.md +0 -119
  845. package/.grok/skills/lit-typographic-motion/references/runtime.md +0 -138
  846. package/.grok/skills/lit-typographic-motion/references/scene-contract.md +0 -98
  847. package/.grok/skills/lit-typographic-motion/references/stage.md +0 -194
  848. package/.grok/skills/lit-typographic-motion/references/style-bible.md +0 -102
  849. package/.grok/skills/lit-typographic-motion/references/treatment.md +0 -153
  850. package/.grok/skills/lit-typographic-motion/references/type-craft.md +0 -94
  851. package/.grok/skills/lit-typographic-motion/requirements-audio.txt +0 -794
  852. package/.grok/skills/lit-typographic-motion/scripts/beat_grid.py +0 -29
  853. package/.grok/skills/lit-typographic-motion/scripts/blender/runner.py +0 -218
  854. package/.grok/skills/lit-typographic-motion/scripts/lib/assets.mjs +0 -230
  855. package/.grok/skills/lit-typographic-motion/scripts/lib/blender.mjs +0 -136
  856. package/.grok/skills/lit-typographic-motion/scripts/lib/board.mjs +0 -153
  857. package/.grok/skills/lit-typographic-motion/scripts/lib/bundle.mjs +0 -23
  858. package/.grok/skills/lit-typographic-motion/scripts/lib/cache.mjs +0 -107
  859. package/.grok/skills/lit-typographic-motion/scripts/lib/chrome.mjs +0 -276
  860. package/.grok/skills/lit-typographic-motion/scripts/lib/clips.mjs +0 -119
  861. package/.grok/skills/lit-typographic-motion/scripts/lib/completion.mjs +0 -43
  862. package/.grok/skills/lit-typographic-motion/scripts/lib/constants.mjs +0 -244
  863. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-assets.mjs +0 -88
  864. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-capture.mjs +0 -153
  865. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-gate.mjs +0 -312
  866. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-image.mjs +0 -111
  867. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-pool.mjs +0 -62
  868. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-sheet.mjs +0 -73
  869. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-treatment.mjs +0 -128
  870. package/.grok/skills/lit-typographic-motion/scripts/lib/cover-webp.mjs +0 -134
  871. package/.grok/skills/lit-typographic-motion/scripts/lib/cover.mjs +0 -354
  872. package/.grok/skills/lit-typographic-motion/scripts/lib/encode.mjs +0 -33
  873. package/.grok/skills/lit-typographic-motion/scripts/lib/flash.mjs +0 -170
  874. package/.grok/skills/lit-typographic-motion/scripts/lib/fonts.mjs +0 -150
  875. package/.grok/skills/lit-typographic-motion/scripts/lib/frame-pool.mjs +0 -162
  876. package/.grok/skills/lit-typographic-motion/scripts/lib/gallery.mjs +0 -160
  877. package/.grok/skills/lit-typographic-motion/scripts/lib/gate.mjs +0 -586
  878. package/.grok/skills/lit-typographic-motion/scripts/lib/house.mjs +0 -43
  879. package/.grok/skills/lit-typographic-motion/scripts/lib/look.mjs +0 -289
  880. package/.grok/skills/lit-typographic-motion/scripts/lib/png.mjs +0 -95
  881. package/.grok/skills/lit-typographic-motion/scripts/lib/presets.mjs +0 -161
  882. package/.grok/skills/lit-typographic-motion/scripts/lib/render.mjs +0 -563
  883. package/.grok/skills/lit-typographic-motion/scripts/lib/report.mjs +0 -65
  884. package/.grok/skills/lit-typographic-motion/scripts/lib/scaffold.mjs +0 -174
  885. package/.grok/skills/lit-typographic-motion/scripts/lib/score.mjs +0 -640
  886. package/.grok/skills/lit-typographic-motion/scripts/lib/seed.mjs +0 -31
  887. package/.grok/skills/lit-typographic-motion/scripts/lib/session.mjs +0 -102
  888. package/.grok/skills/lit-typographic-motion/scripts/lib/sound.mjs +0 -517
  889. package/.grok/skills/lit-typographic-motion/scripts/lib/soundtrack.mjs +0 -46
  890. package/.grok/skills/lit-typographic-motion/scripts/lib/stage-gate.mjs +0 -139
  891. package/.grok/skills/lit-typographic-motion/scripts/lib/stage-qa.mjs +0 -400
  892. package/.grok/skills/lit-typographic-motion/scripts/lib/stage-session.mjs +0 -166
  893. package/.grok/skills/lit-typographic-motion/scripts/lib/stage.mjs +0 -504
  894. package/.grok/skills/lit-typographic-motion/scripts/lib/stills.mjs +0 -90
  895. package/.grok/skills/lit-typographic-motion/scripts/lib/styles.mjs +0 -224
  896. package/.grok/skills/lit-typographic-motion/scripts/lib/text.mjs +0 -114
  897. package/.grok/skills/lit-typographic-motion/scripts/lib/timeline.mjs +0 -223
  898. package/.grok/skills/lit-typographic-motion/scripts/lib/treatment.mjs +0 -446
  899. package/.grok/skills/lit-typographic-motion/scripts/lib/vendor/three/LICENSE +0 -21
  900. package/.grok/skills/lit-typographic-motion/scripts/lib/vendor/three/addons/environments/RoomEnvironment.js +0 -144
  901. package/.grok/skills/lit-typographic-motion/scripts/lib/vendor/three/addons/geometries/RoundedBoxGeometry.js +0 -155
  902. package/.grok/skills/lit-typographic-motion/scripts/lib/vendor/three/three.module.js +0 -54571
  903. package/.grok/skills/lit-typographic-motion/scripts/motion.mjs +0 -225
  904. package/.grok/skills/lit-typographic-motion/scripts/page/engine.js +0 -344
  905. package/.grok/skills/lit-typographic-motion/scripts/page/gl.js +0 -365
  906. package/.grok/skills/lit-typographic-motion/scripts/page/passes.js +0 -211
  907. package/.grok/skills/lit-typographic-motion/scripts/page/post.js +0 -127
  908. package/.grok/skills/lit-typographic-motion/scripts/page/scenes.js +0 -359
  909. package/.grok/skills/lit-typographic-motion/scripts/page/stroke.js +0 -154
  910. package/.grok/skills/lit-typographic-motion/scripts/page/type.js +0 -142
  911. package/.grok/skills/lit-typographic-motion/scripts/page/util.js +0 -68
  912. package/.grok/skills/lit-typographic-motion/scripts/prewarm.mjs +0 -216
  913. package/.grok/skills/lit-typographic-motion/scripts/stage/cover-init.js +0 -56
  914. package/.grok/skills/lit-typographic-motion/scripts/stage/host.js +0 -200
  915. package/.grok/skills/lit-typographic-motion/scripts/stage/loop-kit.js +0 -96
  916. package/.grok/skills/lit-typographic-motion/scripts/stage/qa.js +0 -137
  917. package/.grok/skills/lit-typographic-motion/scripts/stage/stage-kit.js +0 -523
  918. package/.grok/skills/lit-typographic-motion/styles/paper.json +0 -1
  919. package/.grok/skills/lit-typographic-motion/styles/swiss-signal.json +0 -1
  920. package/.grok/skills/lit-typographic-motion/styles/terminalcore.json +0 -1
  921. package/.grok/skills/lit-typographic-motion/styles/tidal.json +0 -1
  922. package/.grok/skills/readme-studio/references/choice-first.md +0 -24
  923. package/.grok/skills/readme-studio/references/complete-contract.md +0 -26
  924. package/.grok/skills/readme-studio/references/decoration-patterns.md +0 -29
  925. package/.grok/skills/readme-studio/references/facts.md +0 -15
  926. package/.grok/skills/readme-studio/references/motion.md +0 -42
  927. package/.grok/skills/readme-studio/references/production.md +0 -9
  928. package/.grok/skills/readme-studio/references/typography.md +0 -16
  929. package/.grok/skills/readme-studio/scripts/validate-facts.mjs +0 -52
  930. package/.grok/skills/readme-studio/templates/cover-source.json +0 -27
  931. package/.grok/skills/readme-studio/templates/facts.json +0 -13
  932. package/.grok/skills/readme-studio/templates/hyperframes/index.html +0 -41
  933. package/.grok/skills/readme-studio/templates/hyperframes/index.motion.json +0 -4
  934. package/.grok/skills/readme-studio/templates/hyperframes/package-lock.json +0 -2016
  935. package/.grok/skills/readme-studio/templates/hyperframes/package.json +0 -12
  936. package/.grok/skills/readme-studio/templates/picture.md +0 -61
  937. package/.grok/skills/readme-studio/templates/remotion/package-lock.json +0 -3785
  938. package/.grok/skills/readme-studio/templates/remotion/package.json +0 -19
  939. package/.grok/skills/readme-studio/templates/remotion/src/index.jsx +0 -31
  940. package/.grok/skills/readme-studio/templates/typography/outline.mjs +0 -62
  941. package/.grok/skills/readme-studio/templates/typography/package-lock.json +0 -135
  942. package/.grok/skills/readme-studio/templates/typography/package.json +0 -10
  943. package/.grok/skills/visual-qa/references/capture-playbook.md +0 -47
  944. package/.grok/skills/visual-qa/references/complete-contract.md +0 -721
  945. package/.grok/skills/visual-qa/references/verdict-taxonomy.md +0 -34
  946. package/.grok/skills/visual-qa/scripts/verify-evidence-manifest.mjs +0 -91
  947. package/.grok/vendor/NOTICE.md +0 -16
  948. package/.grok/vendor/licenses/045_scientific-visualization-MIT.txt +0 -21
  949. package/.grok/vendor/provenance/045_scientific-visualization.md +0 -37
  950. package/.grok/vendor/scientific-visualization/assets/color_palettes.py +0 -197
  951. package/.grok/vendor/scientific-visualization/assets/nature.mplstyle +0 -75
  952. package/.grok/vendor/scientific-visualization/assets/presentation.mplstyle +0 -74
  953. package/.grok/vendor/scientific-visualization/assets/publication.mplstyle +0 -78
  954. package/.grok/vendor/scientific-visualization/evals/evals.json +0 -158
  955. package/.grok/vendor/scientific-visualization/references/_canonical-corpus/manifest.json +0 -38
  956. package/.grok/vendor/scientific-visualization/references/color_palettes.md +0 -380
  957. package/.grok/vendor/scientific-visualization/references/journal_requirements.md +0 -359
  958. package/.grok/vendor/scientific-visualization/references/matplotlib_examples.md +0 -608
  959. package/.grok/vendor/scientific-visualization/references/mdanalysis_martini_visualization.md +0 -85
  960. package/.grok/vendor/scientific-visualization/references/publication_guidelines.md +0 -217
  961. package/.grok/vendor/scientific-visualization/references/seaborn_for_publications.md +0 -293
  962. package/.grok/vendor/scientific-visualization/scripts/figure_export.py +0 -238
  963. package/.grok/vendor/scientific-visualization/scripts/style_presets.py +0 -467
  964. package/.grok/vendor/scientific-visualization/tests/test_figure_export.py +0 -51
  965. package/.grok/vendor/scientific-visualization/tests/test_style_presets.py +0 -114
  966. /package/.grok/skills/{lit-pptx → litgrok}/scripts/proc.cjs +0 -0
@@ -1,2236 +0,0 @@
1
- "use strict";
2
-
3
- /**
4
- * render-adapter-pptx.js — Render a resolved slide spec to a PPTX file.
5
- *
6
- * Architecture: Each text line is its own separate shape (individual addText()
7
- * call). Bullet items use manual "• " prefix in green (#00AE41) Bold 16pt.
8
- * Section headers are separate shapes in Bold 18pt black.
9
- *
10
- * Input: resolved spec from layout-resolver, templateObj from registry, output path.
11
- * Output: writes a .pptx file via pptxgenjs.
12
- */
13
-
14
- const fs = require("fs");
15
- const os = require("os");
16
- const path = require("path");
17
- const { extractContent, looksLikeKpi } = require("./layout-resolver");
18
- const { LEGACY_TOKENS } = require("./template-registry");
19
-
20
- // Narrowest a KPI badge can be and still read as one glance (template token kpi.minCardWidth).
21
- let KPI_MIN_CARD_WIDTH_IN = LEGACY_TOKENS.kpi.minCardWidth;
22
- let KPI_GAP = LEGACY_TOKENS.kpi.gap;
23
-
24
- const TOOLKIT_ROOT = path.resolve(__dirname, "../..");
25
- const ASSETS_AZURE = path.join(TOOLKIT_ROOT, "assets", "azure");
26
-
27
- /** Resolve a decoration asset path: explicit absolute override (custom-accent
28
- * regenerated gradients), otherwise only the enrolled Azure assets. */
29
- function decorAssetPath(d) {
30
- if (d.assetPath) return d.assetPath;
31
- if (d.dir === "azure") return path.join(ASSETS_AZURE, d.asset);
32
- throw new Error(`Decoration asset is not enrolled: ${d.asset}`);
33
- }
34
- const hex = (c) => (c || "#000000").replace("#", "");
35
-
36
- // A template may set its own disclaimer (`text:` on the decoration); the default
37
- // names no organisation and claims no secrecy.
38
- const DEFAULT_DISCLAIMER = "※ 이 문서의 사용 범위는 작성자가 지정한 권한을 따릅니다.";
39
-
40
- function linkedTableCell(text, options) {
41
- const original = cleanMd(text);
42
- let display = original;
43
- let url = null;
44
- const markdown = original.match(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/i);
45
- if (markdown) {
46
- display = original.replace(markdown[0], markdown[1]);
47
- url = markdown[2];
48
- } else {
49
- const rawUrl = original.match(/https?:\/\/[^\s<>\])}]+/i);
50
- const doi = original.match(/\b10\.\d{4,9}\/[^\s<>\])}]+/i);
51
- if (rawUrl) url = rawUrl[0].replace(/[.,;:]+$/, "");
52
- else if (doi) url = `https://doi.org/${doi[0].replace(/[.,;:]+$/, "")}`;
53
- }
54
- return {
55
- text: display,
56
- options: {
57
- ...options,
58
- ...(url ? { hyperlink: { url } } : {}),
59
- },
60
- };
61
- }
62
-
63
- // ── Text style constants (defaults; every template reassigns them in render()).
64
- // These are reassigned from the template at the start of render() so the same
65
- // adapter serves any enrolled template (e.g. BOILERPLATE-PRETENDARD).
66
- let FONT_BOLD = "Pretendard";
67
- let FONT_MEDIUM = "Pretendard";
68
- let FONT_LIGHT = "Pretendard";
69
- let CHAR_SPACING = -0.7;
70
- // Chart colours and faces for templates without the rich palette; set per render().
71
- let PLAIN_CHART_STYLE = { series: ["3C3836", "689D6A", "D79921", "B16286"], ink: "3C3836", ink_muted: "6E6256", line: "CBC2B5", titleFont: "Pretendard", bodyFont: "Pretendard" };
72
-
73
- function expandHome(p) {
74
- if (p === "~") return os.homedir();
75
- if (p && p.startsWith(`~${path.sep}`)) return path.join(os.homedir(), p.slice(2));
76
- return p;
77
- }
78
-
79
- function resolveImagePath(src, sourceDir) {
80
- if (!src) throw new Error("Image source is empty");
81
- const expanded = expandHome(src);
82
- const candidates = [];
83
- if (path.isAbsolute(expanded)) candidates.push(expanded);
84
- else {
85
- if (sourceDir) candidates.push(path.resolve(sourceDir, expanded));
86
- candidates.push(path.resolve(process.cwd(), expanded));
87
- candidates.push(path.resolve(TOOLKIT_ROOT, expanded));
88
- }
89
- const found = candidates.find((candidate) => fs.existsSync(candidate));
90
- if (!found) {
91
- throw new Error(`Image not found: ${src}. Checked: ${candidates.join(", ")}`);
92
- }
93
- return found;
94
- }
95
-
96
- function readImageSize(filePath) {
97
- const buf = fs.readFileSync(filePath);
98
- if (buf.length >= 24 && buf.toString("ascii", 1, 4) === "PNG") {
99
- return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
100
- }
101
- if (buf.length >= 4 && buf[0] === 0xff && buf[1] === 0xd8) {
102
- let offset = 2;
103
- while (offset + 9 < buf.length) {
104
- if (buf[offset] !== 0xff) { offset++; continue; }
105
- const marker = buf[offset + 1];
106
- const length = buf.readUInt16BE(offset + 2);
107
- if (length < 2) break;
108
- if ((marker >= 0xc0 && marker <= 0xc3) || (marker >= 0xc5 && marker <= 0xc7) ||
109
- (marker >= 0xc9 && marker <= 0xcb) || (marker >= 0xcd && marker <= 0xcf)) {
110
- return { width: buf.readUInt16BE(offset + 7), height: buf.readUInt16BE(offset + 5) };
111
- }
112
- offset += 2 + length;
113
- }
114
- }
115
- return null;
116
- }
117
-
118
- function containBox(x, y, w, h, imageSize) {
119
- if (!imageSize || !imageSize.width || !imageSize.height) return { x, y, w, h };
120
- const boxRatio = w / h;
121
- const imageRatio = imageSize.width / imageSize.height;
122
- if (imageRatio > boxRatio) {
123
- const fittedH = w / imageRatio;
124
- return { x, y: y + (h - fittedH) / 2, w, h: fittedH };
125
- }
126
- const fittedW = h * imageRatio;
127
- return { x: x + (w - fittedW) / 2, y, w: fittedW, h };
128
- }
129
-
130
- // Bullet items. Geometry comes from the template's tokens (bullet.*, secHeader.*, summary.*,
131
- // toc.*), reassigned in render(); these defaults are the legacy values.
132
- let BULLET_COLOR = "1D4ED8"; // bullet color (reassigned per template)
133
- let SECTION_ACCENT = "1D4ED8"; // section-card / accent color (reassigned per template)
134
- const BULLET_PREFIX = "\u2022 "; // "• " (bullet + two spaces)
135
- let BULLET_X = LEGACY_TOKENS.bullet.x;
136
- let BULLET_W = LEGACY_TOKENS.bullet.w;
137
-
138
- // Section headers
139
- let SEC_HEADER_FONT = FONT_BOLD;
140
- let SEC_HEADER_SIZE = LEGACY_TOKENS.secHeader.size;
141
- const SEC_HEADER_COLOR = "000000";
142
- let SEC_HEADER_X = LEGACY_TOKENS.secHeader.x;
143
- let SEC_HEADER_W = LEGACY_TOKENS.secHeader.w;
144
- let SEC_HEADER_H = LEGACY_TOKENS.secHeader.h;
145
-
146
- // Summary (narrow width)
147
- let SUMMARY_HEADER_W = LEGACY_TOKENS.summary.headerW;
148
- let SUMMARY_BULLET_W = LEGACY_TOKENS.summary.bulletW;
149
- let SUMMARY_BULLET_SIZE = LEGACY_TOKENS.summary.bulletSize;
150
- let SUMMARY_BULLET_H = LEGACY_TOKENS.summary.bulletH;
151
- let SUMMARY_BULLET_Y_SPACING = LEGACY_TOKENS.summary.ySpacing;
152
- let SUMMARY_BULLET_LINE_SPACING = LEGACY_TOKENS.summary.lineSpacing;
153
-
154
- // TOC items (separate shapes)
155
- let TOC_FONT = FONT_BOLD;
156
- let TOC_SIZE = LEGACY_TOKENS.toc.size;
157
- const TOC_COLOR = "000000";
158
- let TOC_X = LEGACY_TOKENS.toc.x;
159
- let TOC_W = LEGACY_TOKENS.toc.w;
160
- let TOC_H = LEGACY_TOKENS.toc.h;
161
- let TOC_Y_START = LEGACY_TOKENS.toc.yStart;
162
- let TOC_Y_SPACING = LEGACY_TOKENS.toc.ySpacing;
163
-
164
- /** Reassign the token-driven geometry from the template's pack (legacy values when absent). */
165
- function applyTokens(tokens) {
166
- const t = tokens || LEGACY_TOKENS;
167
- ({ x: BULLET_X, w: BULLET_W } = t.bullet);
168
- ({ size: SEC_HEADER_SIZE, x: SEC_HEADER_X, w: SEC_HEADER_W, h: SEC_HEADER_H } = t.secHeader);
169
- ({ headerW: SUMMARY_HEADER_W, bulletW: SUMMARY_BULLET_W, bulletSize: SUMMARY_BULLET_SIZE, bulletH: SUMMARY_BULLET_H,
170
- ySpacing: SUMMARY_BULLET_Y_SPACING, lineSpacing: SUMMARY_BULLET_LINE_SPACING } = t.summary);
171
- ({ size: TOC_SIZE, x: TOC_X, w: TOC_W, h: TOC_H, yStart: TOC_Y_START, ySpacing: TOC_Y_SPACING } = t.toc);
172
- ({ pad: CARD_PAD, chip: CARD_CHIP, minH: CARD_MIN_H, gap: CARD_GAP, radius: CARD_RADIUS } = t.card);
173
- ({ minCardWidth: KPI_MIN_CARD_WIDTH_IN, gap: KPI_GAP } = t.kpi);
174
- NOTICE = { ...t.notice };
175
- }
176
-
177
- function weightedTextLength(text) {
178
- let total = 0;
179
- for (const ch of String(text || "")) {
180
- if (/\s/.test(ch)) total += 0.35;
181
- else if (/^[\x00-\x7F]$/.test(ch)) total += 0.58;
182
- else total += 1.0;
183
- }
184
- return total;
185
- }
186
-
187
- function estimateWrappedLines(text, widthIn, fontSize) {
188
- const usablePt = Math.max(12, (widthIn || 4) * 72 - 10);
189
- // Conservative on purpose: PowerPoint wraps dense mixed
190
- // Korean-English strings earlier than a naive ASCII width estimate.
191
- const unitsPerLine = Math.max(4, usablePt / Math.max(4, fontSize * 0.95));
192
- return Math.max(1, Math.ceil(weightedTextLength(text) / unitsPerLine));
193
- }
194
-
195
- function estimateTextHeight(text, widthIn, fontSize, lineSpacingMultiple) {
196
- const lines = estimateWrappedLines(text, widthIn, fontSize);
197
- return Math.max(0.14, ((lines * fontSize * lineSpacingMultiple) / 72) * 1.4 + 0.08);
198
- }
199
-
200
- function bodyElements(items) {
201
- const elements = [];
202
- let numberedCounter = 0;
203
- for (const item of items) {
204
- if (item.type === "section") {
205
- elements.push({ kind: "section", text: item.heading || "" });
206
- numberedCounter = 0;
207
- for (const child of item.children || []) {
208
- numberedCounter++;
209
- elements.push({ kind: "numbered", text: `(${numberedCounter}) ${child.text || ""}` });
210
- }
211
- } else if (item.type === "bullet") {
212
- elements.push({ kind: "bullet", text: item.text || "" });
213
- numberedCounter = 0;
214
- for (const child of item.children || []) {
215
- numberedCounter++;
216
- elements.push({ kind: "numbered", text: `(${numberedCounter}) ${child.text || ""}` });
217
- }
218
- } else if (item.type === "numbered") {
219
- numberedCounter++;
220
- elements.push({ kind: "numbered", text: `(${numberedCounter}) ${item.text || ""}` });
221
- } else if (item.type === "text") {
222
- elements.push({ kind: "text", text: item.text || "" });
223
- }
224
- }
225
- return elements;
226
- }
227
-
228
- function chooseBodyStyle(items, widthIn, startY, bottomY) {
229
- const available = Math.max(0.5, (bottomY || 7.0) - startY);
230
- const sizes = [16, 15, 14, 13];
231
- for (const bodySize of sizes) {
232
- const sectionSize = Math.min(18, bodySize + 2);
233
- const lineSpacing = bodySize <= 10 ? 1.05 : 1.18;
234
- const gap = bodySize <= 10 ? 0.035 : 0.05;
235
- const total = bodyElements(items).reduce((sum, el) => {
236
- const size = el.kind === "section" ? sectionSize : bodySize;
237
- const w = el.kind === "section" ? Math.min(widthIn, SEC_HEADER_W) : widthIn;
238
- return sum + estimateTextHeight(el.text, w, size, lineSpacing) + gap;
239
- }, 0);
240
- if (total <= available) return { bodySize, sectionSize, lineSpacing, gap };
241
- }
242
- return { bodySize: 13, sectionSize: 15, lineSpacing: 1.08, gap: 0.04 };
243
- }
244
-
245
- function estimateSectionHeight(item, secW, bulletW, style) {
246
- let total = estimateTextHeight(item.heading || "", secW, style.sectionSize, style.lineSpacing) + style.gap;
247
- let numberedCounter = 0;
248
- for (const child of item.children || []) {
249
- numberedCounter++;
250
- total += estimateTextHeight(`(${numberedCounter}) ${child.text || ""}`, bulletW, style.bodySize, style.lineSpacing) + style.gap;
251
- }
252
- return total + 0.12;
253
- }
254
-
255
- function addSectionCard(slide, x, y, w, h) {
256
- slide.addShape("rect", {
257
- x,
258
- y: y - 0.035,
259
- w,
260
- h: Math.max(0.28, h),
261
- fill: { color: "F7F9F8", transparency: 12 },
262
- line: { color: "D8E2DD", width: 0.5, transparency: 20 },
263
- });
264
- }
265
-
266
- function bodyItemsToDiagnosticLines(items) {
267
- const lines = [];
268
- let numberedCounter = 0;
269
- for (const item of items || []) {
270
- if (item.type === "section") {
271
- lines.push(`■ ${item.heading || ""}`);
272
- numberedCounter = 0;
273
- for (const child of item.children || []) {
274
- numberedCounter++;
275
- lines.push(`(${numberedCounter}) ${child.text || ""}`);
276
- }
277
- } else if (item.type === "bullet") {
278
- lines.push(`• ${item.text || ""}`);
279
- numberedCounter = 0;
280
- for (const child of item.children || []) {
281
- numberedCounter++;
282
- lines.push(`(${numberedCounter}) ${child.text || ""}`);
283
- }
284
- } else if (item.type === "numbered") {
285
- numberedCounter++;
286
- lines.push(`(${numberedCounter}) ${item.text || ""}`);
287
- } else if (item.type === "text") {
288
- lines.push(item.text || "");
289
- }
290
- }
291
- return lines.filter(Boolean);
292
- }
293
-
294
- function bodyItemsToDiagnosticCards(items) {
295
- const cards = [];
296
- let numberedCounter = 0;
297
- for (const item of items || []) {
298
- if (item.type === "section" || item.type === "bullet") {
299
- const title = item.heading || item.text || "";
300
- const body = [];
301
- numberedCounter = 0;
302
- for (const child of item.children || []) {
303
- numberedCounter++;
304
- body.push(`(${numberedCounter}) ${child.text || ""}`);
305
- }
306
- if (title || body.length) cards.push({ title, body });
307
- } else if (item.type === "numbered") {
308
- numberedCounter++;
309
- cards.push({ title: `(${numberedCounter})`, body: [item.text || ""] });
310
- } else if (item.type === "text" && item.text) {
311
- cards.push({ title: "", body: [item.text] });
312
- }
313
- }
314
- return cards;
315
- }
316
-
317
- function splitLinesByWeight(lines, columns) {
318
- const groups = Array.from({ length: columns }, () => []);
319
- const weights = Array(columns).fill(0);
320
- for (const line of lines) {
321
- let target = 0;
322
- for (let i = 1; i < columns; i++) {
323
- if (weights[i] < weights[target]) target = i;
324
- }
325
- groups[target].push(line);
326
- weights[target] += Math.max(20, weightedTextLength(line));
327
- }
328
- return groups;
329
- }
330
-
331
- function splitCardsByWeight(cards, columns) {
332
- const groups = Array.from({ length: columns }, () => []);
333
- const weights = Array(columns).fill(0);
334
- for (const card of cards) {
335
- let target = 0;
336
- for (let i = 1; i < columns; i++) {
337
- if (weights[i] < weights[target]) target = i;
338
- }
339
- groups[target].push(card);
340
- const cardWeight = weightedTextLength(card.title || "") * 1.4
341
- + (card.body || []).reduce((sum, line) => sum + weightedTextLength(line), 0);
342
- weights[target] += Math.max(80, cardWeight);
343
- }
344
- return groups;
345
- }
346
-
347
- function renderDiagnosticCard(slide, card, x, y, w, h, idx) {
348
- slide.addShape("rect", {
349
- x,
350
- y,
351
- w,
352
- h,
353
- fill: { color: idx % 2 ? "F8FAF9" : "F3F6F4", transparency: 2 },
354
- line: { color: "D6E2DB", width: 0.35, transparency: 12 },
355
- });
356
- slide.addShape("rect", {
357
- x,
358
- y,
359
- w: 0.028,
360
- h,
361
- fill: { color: SECTION_ACCENT, transparency: 0 },
362
- line: { color: SECTION_ACCENT, transparency: 100 },
363
- });
364
-
365
- const padX = 0.055;
366
- const padY = 0.05;
367
- const titleH = card.title ? Math.min(0.28, Math.max(0.18, h * 0.16)) : 0;
368
- if (card.title) {
369
- slide.addText(card.title, {
370
- x: x + padX,
371
- y: y + padY,
372
- w: w - padX * 1.45,
373
- h: titleH,
374
- fontFace: FONT_BOLD,
375
- fontSize: 7.4,
376
- color: "002554",
377
- charSpacing: -0.25,
378
- fit: "shrink",
379
- margin: 0.01,
380
- breakLine: false,
381
- valign: "top",
382
- });
383
- }
384
-
385
- const bodyText = (card.body || []).join("\n");
386
- if (bodyText) {
387
- slide.addText(bodyText, {
388
- x: x + padX,
389
- y: y + padY + titleH + 0.015,
390
- w: w - padX * 1.45,
391
- h: Math.max(0.16, h - titleH - padY * 1.6),
392
- fontFace: FONT_MEDIUM,
393
- fontSize: 5.85,
394
- color: "1F1F1F",
395
- charSpacing: -0.18,
396
- fit: "shrink",
397
- margin: 0.008,
398
- paraSpaceAfterPt: 0,
399
- lineSpacingMultiple: 0.96,
400
- breakLine: false,
401
- valign: "top",
402
- });
403
- }
404
- }
405
-
406
- function estimateDiagnosticCardHeight(card, w) {
407
- const padY = 0.05;
408
- const titleH = card.title ? 0.24 : 0;
409
- const bodyText = (card.body || []).join("\n");
410
- const bodyH = bodyText ? estimateTextHeight(bodyText, Math.max(0.8, w - 0.12), 5.85, 1.02) : 0;
411
- return Math.max(0.56, Math.min(2.35, padY * 2 + titleH + bodyH));
412
- }
413
-
414
- function renderCramStressBody(slide, items, pageH) {
415
- const cards = bodyItemsToDiagnosticCards(items);
416
- const columns = 3;
417
- const groups = cards.length ? splitCardsByWeight(cards, columns) : splitLinesByWeight(bodyItemsToDiagnosticLines(items), columns).map((group) => [{ title: "", body: group }]);
418
- const x = 0.28;
419
- const y = 0.98;
420
- const w = 7.78;
421
- const h = pageH - y - 0.34;
422
- const gap = 0.105;
423
- const colW = (w - gap * (columns - 1)) / columns;
424
- groups.forEach((group, idx) => {
425
- const colX = x + idx * (colW + gap);
426
- const cardGap = 0.075;
427
- const availableH = h - cardGap * Math.max(0, group.length - 1);
428
- const desiredHeights = group.map((card) => estimateDiagnosticCardHeight(card, colW));
429
- const desiredTotal = desiredHeights.reduce((sum, value) => sum + value, 0) || 1;
430
- const scale = desiredTotal > availableH ? availableH / desiredTotal : 1;
431
- let cursorY = y;
432
- group.forEach((card, cardIdx) => {
433
- const remainingCards = group.length - cardIdx - 1;
434
- const rawH = desiredHeights[cardIdx] * scale;
435
- const cardH = Math.max(0.62, Math.min(rawH, h - (cursorY - y) - remainingCards * (0.62 + cardGap)));
436
- renderDiagnosticCard(slide, card, colX, cursorY, colW, cardH, idx + cardIdx);
437
- cursorY += cardH + cardGap;
438
- });
439
- });
440
- }
441
-
442
- function renderCramStressImageSheet(slide, content, sourceDir, pageW, pageH) {
443
- if (!content) return;
444
- const images = content.type === "image-grid"
445
- ? (Array.isArray(content.images) ? content.images : [])
446
- : content.type === "image" ? [content] : [];
447
- if (images.length === 0) return;
448
- const x = 8.24;
449
- const y = 0.98;
450
- const w = pageW - x - 0.25;
451
- const h = pageH - y - 0.34;
452
- const cols = images.length >= 8 ? 2 : 1;
453
- const rows = Math.ceil(images.length / cols);
454
- const gap = 0.04;
455
- const cellW = (w - gap * (cols - 1)) / cols;
456
- const headerH = 0.28;
457
- const sheetH = Math.min(h - 0.9, rows * 0.58 + gap * (rows - 1));
458
- const cellH = (sheetH - gap * (rows - 1)) / rows;
459
- const noteY = y + headerH + sheetH + 0.16;
460
- const noteH = Math.min(1.0, h - (noteY - y) - 0.08);
461
- const panelH = noteY - y + noteH;
462
- slide.addShape("rect", {
463
- x: x - 0.04,
464
- y: y - 0.04,
465
- w: w + 0.08,
466
- h: panelH + 0.08,
467
- fill: { color: "F5F8F6", transparency: 0 },
468
- line: { color: "CAD4CE", width: 0.5 },
469
- });
470
- slide.addText("RAW EVIDENCE CONTACT SHEET", {
471
- x,
472
- y: y + 0.02,
473
- w,
474
- h: 0.16,
475
- fontFace: FONT_BOLD,
476
- fontSize: 5.8,
477
- color: "002554",
478
- charSpacing: -0.15,
479
- fit: "shrink",
480
- margin: 0,
481
- });
482
- slide.addShape("rect", {
483
- x,
484
- y: y + 0.22,
485
- w,
486
- h: 0,
487
- line: { color: SECTION_ACCENT, width: 0.45 },
488
- });
489
- images.forEach((image, idx) => {
490
- const imgPath = resolveImagePath(image.src, sourceDir);
491
- const col = idx % cols;
492
- const row = Math.floor(idx / cols);
493
- const fit = containBox(
494
- x + col * (cellW + gap),
495
- y + headerH + row * (cellH + gap),
496
- cellW,
497
- cellH,
498
- readImageSize(imgPath),
499
- );
500
- slide.addImage({
501
- path: imgPath,
502
- ...fit,
503
- altText: image.caption || path.basename(image.src || imgPath),
504
- });
505
- });
506
- slide.addShape("rect", {
507
- x,
508
- y: noteY,
509
- w,
510
- h: noteH,
511
- fill: { color: "FFFFFF", transparency: 0 },
512
- line: { color: "D6E2DB", width: 0.35, transparency: 20 },
513
- });
514
- slide.addText("diagnostic cram-stress\nmicrotext is intentional\nproduction decks must synthesize\noverflow / marker / missing media = fail", {
515
- x: x + 0.06,
516
- y: noteY + 0.07,
517
- w: w - 0.12,
518
- h: Math.max(0.24, noteH - 0.14),
519
- fontFace: FONT_MEDIUM,
520
- fontSize: 5.3,
521
- color: "333333",
522
- charSpacing: -0.15,
523
- fit: "shrink",
524
- margin: 0.01,
525
- breakLine: false,
526
- lineSpacingMultiple: 1.02,
527
- valign: "mid",
528
- });
529
- }
530
-
531
- function isCramStressDeck(resolved) {
532
- const meta = (resolved && resolved.deck && resolved.deck.metadata) || {};
533
- return String(meta.render_mode || meta.renderMode || "").toLowerCase() === "cram-stress";
534
- }
535
-
536
- function renderCramStressSlide(slide, slideSpec, pageW, pageH, sourceDir) {
537
- const regions = slideSpec.regions || {};
538
- const title = regions.title && regions.title.content;
539
- if (title) {
540
- slide.addText(String(title), {
541
- x: 0.24,
542
- y: 0.24,
543
- w: pageW - 1.72,
544
- h: 0.46,
545
- fontFace: FONT_BOLD,
546
- fontSize: 13.5,
547
- color: "000000",
548
- charSpacing: CHAR_SPACING,
549
- fit: "shrink",
550
- });
551
- slide.addShape("rect", {
552
- x: 0.24,
553
- y: 0.76,
554
- w: pageW - 0.48,
555
- h: 0,
556
- line: { color: SECTION_ACCENT, width: 1.2 },
557
- });
558
- }
559
- if (regions.body && regions.body.content && regions.body.content.type === "body") {
560
- renderCramStressBody(slide, regions.body.content.items || [], pageH);
561
- }
562
- if (regions.image && regions.image.content) {
563
- renderCramStressImageSheet(slide, regions.image.content, sourceDir, pageW, pageH);
564
- }
565
- }
566
-
567
- function shouldUseDenseBodyFlow(items, widthIn, startY, bottomY) {
568
- const elements = bodyElements(items);
569
- const charCount = elements.reduce((sum, el) => sum + String(el.text || "").length, 0);
570
- const available = Math.max(0.5, (bottomY || 7.0) - startY);
571
- const estimatedAtMin = elements.reduce((sum, el) => {
572
- const size = el.kind === "section" ? 12.8 : 11.5;
573
- return sum + estimateTextHeight(el.text, Math.max(1.8, widthIn / 2), size, 1.02) + 0.025;
574
- }, 0);
575
- return widthIn >= 4.4 && (elements.length >= 18 || charCount >= 2400 || estimatedAtMin > available * 1.35);
576
- }
577
-
578
- function chooseDenseBodyStyle() {
579
- return { bodySize: 11.5, sectionSize: 12.8, lineSpacing: 1.02, gap: 0.025 };
580
- }
581
-
582
- // ── Font role helpers ───────────────────────────────────────────────────────
583
-
584
- /**
585
- * Build font role map from template capabilities.
586
- * Maps YAML field names to pptxgenjs option names.
587
- */
588
- function buildFontRoles(templateObj) {
589
- if (!templateObj || !templateObj.capabilities || !templateObj.capabilities.font_roles) {
590
- return {};
591
- }
592
-
593
- const roles = {};
594
- for (const [name, def] of Object.entries(templateObj.capabilities.font_roles)) {
595
- roles[name] = {
596
- font: def.font,
597
- size: def.size,
598
- color: (def.color || "#000000").replace("#", ""),
599
- align: def.align || "left",
600
- charSpacing: def.char_spacing,
601
- lineSpacing: def.line_spacing,
602
- bold: def.bold,
603
- spaceAfter: def.space_after,
604
- bulletType: def.bullet_type,
605
- bulletChar: def.bullet_char,
606
- bulletFont: def.bullet_font,
607
- numberType: def.number_type,
608
- marL: def.marL,
609
- indent: def.indent,
610
- fill: def.fill ? def.fill.replace("#", "") : undefined,
611
- borderColor: def.border_color ? def.border_color.replace("#", "") : undefined,
612
- borderWidth: def.border_width,
613
- };
614
- }
615
- return roles;
616
- }
617
-
618
- // ── Decoration rendering ────────────────────────────────────────────────────
619
-
620
- /**
621
- * Add decoration elements to a pptxgenjs slide.
622
- * Note: slide_number is intentionally omitted — the reference template has none.
623
- */
624
- function addDecorations(slide, decorations, fontRoles) {
625
- if (!decorations || !Array.isArray(decorations)) return;
626
-
627
- for (const d of decorations) {
628
- if (d.type === "image") {
629
- slide.addImage({
630
- path: decorAssetPath(d),
631
- x: d.x || 0,
632
- y: d.y || 0,
633
- w: d.w || 1,
634
- h: d.h || 1,
635
- rotate: d.rotate || undefined,
636
- altText: d.asset || "template-image",
637
- });
638
-
639
- } else if (d.type === "rect" || d.type === "roundRect" || d.type === "pill") {
640
- // Solid color block / rounded card / full-radius pill. Optional text label.
641
- const shape = d.type === "rect" ? "rect" : "roundRect";
642
- const opts = {
643
- x: d.x || 0, y: d.y || 0, w: d.w || 1, h: d.h || 0.5,
644
- };
645
- if (d.fill) opts.fill = { color: hex(d.fill), transparency: d.alpha || 0 };
646
- else opts.fill = { type: "none" };
647
- if (d.line) opts.line = { color: hex(d.line), width: d.line_width || 1 };
648
- else opts.line = { type: "none" };
649
- if (shape === "roundRect") opts.rectRadius = d.type === "pill" ? (d.h || 0.5) / 2 : (d.radius != null ? d.radius : 0.12);
650
- slide.addShape(shape, opts);
651
- if (d.text) {
652
- slide.addText(d.text, {
653
- x: d.x || 0, y: d.y || 0, w: d.w || 1, h: d.h || 0.5,
654
- fontFace: d.font || FONT_BOLD, fontSize: d.size || 12,
655
- color: hex(d.text_color || "#FFFFFF"), bold: !!d.bold,
656
- align: d.align || "center", valign: "middle",
657
- charSpacing: d.char_spacing != null ? d.char_spacing : CHAR_SPACING,
658
- });
659
- }
660
-
661
- } else if (d.type === "ellipse" || d.type === "ring") {
662
- const opts = { x: d.x || 0, y: d.y || 0, w: d.w || 1, h: d.h || (d.w || 1) };
663
- if (d.type === "ring" || !d.fill) {
664
- opts.fill = { type: "none" };
665
- opts.line = { color: hex(d.line || d.color || "#FFFFFF"), width: d.line_width || 2 };
666
- } else {
667
- opts.fill = { color: hex(d.fill), transparency: d.alpha || 0 };
668
- opts.line = { type: "none" };
669
- }
670
- slide.addShape("ellipse", opts);
671
-
672
- } else if (d.type === "text") {
673
- slide.addText(d.text || "", {
674
- x: d.x || 0, y: d.y || 0, w: d.w || 4, h: d.h || 1,
675
- fontFace: d.font || FONT_BOLD, fontSize: d.size || 14,
676
- color: hex(d.color || "#000000"), bold: !!d.bold,
677
- align: d.align || "left", valign: d.valign || "top",
678
- charSpacing: d.char_spacing != null ? d.char_spacing : CHAR_SPACING,
679
- transparency: d.transparency != null ? d.transparency : undefined,
680
- });
681
-
682
- } else if (d.type === "line") {
683
- slide.addShape("rect", {
684
- x: d.x || 0,
685
- y: d.y || 0,
686
- w: d.w || 1,
687
- h: 0,
688
- line: { color: (d.color || "#000000").replace("#", ""), width: d.width || 1 },
689
- });
690
-
691
- } else if (d.type === "confidential_mark") {
692
- const role = fontRoles.confidential_mark || {};
693
- const mX = d.x || 0, mY = d.y || 0, mW = d.w || 1, mH = d.h || 0.3;
694
- const bColor = role.borderColor || "C00000";
695
- const bPt = role.borderWidth || 1;
696
- slide.addShape("rect", {
697
- x: mX, y: mY, w: mW, h: mH,
698
- line: { color: bColor, width: bPt },
699
- });
700
- slide.addText(d.text || "내부 검토", {
701
- x: mX, y: mY, w: mW, h: mH,
702
- fontFace: role.font || FONT_MEDIUM,
703
- fontSize: role.size || 14,
704
- color: role.color || "C00000",
705
- align: role.align || "center",
706
- valign: "middle",
707
- });
708
-
709
- } else if (d.type === "disclaimer") {
710
- const role = fontRoles.disclaimer || {};
711
- slide.addText(
712
- d.text || DEFAULT_DISCLAIMER,
713
- {
714
- x: d.x || 0,
715
- y: d.y || 0,
716
- w: d.w || 1,
717
- h: d.h || 0.2,
718
- fontFace: role.font || FONT_LIGHT,
719
- fontSize: role.size || 6,
720
- color: role.color || "666666",
721
- align: role.align || "left",
722
- }
723
- );
724
- }
725
- // slide_number intentionally removed — reference template has none
726
- }
727
- }
728
-
729
- // ── Body shape rendering (individual shapes per line) ────────────────────
730
-
731
- /**
732
- * Add body items as individual shapes to a slide.
733
- * Each section header and bullet item is a separate positioned shape.
734
- *
735
- * @param {object} slide - pptxgenjs slide
736
- * @param {Array} items - AST items (section, bullet, numbered, text)
737
- * @param {number} startY - Starting y position
738
- * @param {number} maxX - Max width for positioning
739
- * @returns {number} The y position after the last item
740
- */
741
- function addBodyShapes(slide, items, startY, maxX, bottomY, originX) {
742
- const denseFlow = shouldUseDenseBodyFlow(items, maxX, startY, bottomY || 7.0);
743
- const columns = denseFlow ? 2 : 1;
744
- const colGap = denseFlow ? 0.16 : 0;
745
- const colW = (maxX - colGap * (columns - 1)) / columns;
746
- let col = 0;
747
- let y = startY;
748
- // Template regions have always drawn body text at the template's own left
749
- // margin. A placed box carries its own, and passes it here.
750
- const baseX = originX != null ? originX : SEC_HEADER_X;
751
- const bodyIndent = denseFlow ? 0.13 : (BULLET_X - SEC_HEADER_X);
752
- const BODY_FULL_W = 10.188;
753
- const scale = colW / BODY_FULL_W;
754
- const secW = denseFlow ? Math.max(1.4, colW - 0.04) : SEC_HEADER_W * scale;
755
- const bulletW = denseFlow ? Math.max(1.2, colW - bodyIndent - 0.02) : BULLET_W * scale;
756
- let numberedCounter = 0;
757
- const style = denseFlow ? chooseDenseBodyStyle() : chooseBodyStyle(items, bulletW, startY, bottomY || 7.0);
758
-
759
- function colX() { return baseX + col * (colW + colGap); }
760
- function secX() { return colX(); }
761
- function bulletX() { return colX() + bodyIndent; }
762
- function maybeAdvanceColumn(nextHeight) {
763
- if (!denseFlow || col >= columns - 1) return;
764
- if (y + nextHeight > (bottomY || 7.0)) {
765
- col++;
766
- y = startY;
767
- }
768
- }
769
-
770
- for (const item of items) {
771
- if (item.type === "section") {
772
- const sectionH = estimateTextHeight(item.heading, secW, style.sectionSize, style.lineSpacing);
773
- const cardH = estimateSectionHeight(item, secW, bulletW, style);
774
- maybeAdvanceColumn(Math.min(cardH, sectionH + 0.2));
775
- addSectionCard(slide, secX() - 0.035, y - 0.03, Math.max(secW, bulletW + bodyIndent) + 0.07, cardH);
776
- // Section header — separate shape, black, 1.5 line spacing
777
- slide.addText(mdRuns(item.heading, {
778
- fontFace: SEC_HEADER_FONT,
779
- fontSize: style.sectionSize,
780
- color: SEC_HEADER_COLOR,
781
- charSpacing: CHAR_SPACING,
782
- }), {
783
- x: secX(),
784
- y: y,
785
- w: secW,
786
- h: sectionH,
787
- lineSpacingMultiple: style.lineSpacing,
788
- });
789
- y += sectionH + style.gap;
790
-
791
- // Numbered children with (1)(2)(3) format
792
- if (item.children) {
793
- numberedCounter = 0;
794
- for (const child of item.children) {
795
- numberedCounter++;
796
- const numPrefix = `(${numberedCounter}) `;
797
- const text = numPrefix + cleanMd(child.text);
798
- const h = estimateTextHeight(text, bulletW, style.bodySize, style.lineSpacing);
799
- maybeAdvanceColumn(h);
800
- slide.addText([
801
- { text: numPrefix, options: { fontFace: FONT_MEDIUM, fontSize: style.bodySize, color: "000000", charSpacing: CHAR_SPACING } },
802
- ...mdRuns(child.text, { fontFace: FONT_MEDIUM, fontSize: style.bodySize, color: "000000", charSpacing: CHAR_SPACING }),
803
- ], {
804
- x: bulletX(),
805
- y: y,
806
- w: bulletW,
807
- h,
808
- lineSpacingMultiple: style.lineSpacing,
809
- });
810
- y += h + style.gap;
811
- }
812
- }
813
- y += 0.08;
814
-
815
- } else if (item.type === "bullet") {
816
- const h = estimateTextHeight(item.text, bulletW, style.bodySize, style.lineSpacing);
817
- maybeAdvanceColumn(h);
818
- // Bullet item — green prefix + black text
819
- slide.addText([
820
- { text: BULLET_PREFIX, options: { fontFace: FONT_BOLD, fontSize: style.bodySize, color: BULLET_COLOR, charSpacing: CHAR_SPACING } },
821
- ...mdRuns(item.text, { fontFace: FONT_MEDIUM, fontSize: style.bodySize, color: "000000", charSpacing: CHAR_SPACING }),
822
- ], {
823
- x: bulletX(),
824
- y: y,
825
- w: bulletW,
826
- h,
827
- lineSpacingMultiple: style.lineSpacing,
828
- });
829
- y += h + style.gap;
830
-
831
- // Children (numbered sub-items)
832
- if (item.children) {
833
- numberedCounter = 0;
834
- for (const child of item.children) {
835
- numberedCounter++;
836
- const numPrefix = `(${numberedCounter}) `;
837
- const text = numPrefix + cleanMd(child.text);
838
- const childH = estimateTextHeight(text, bulletW, style.bodySize, style.lineSpacing);
839
- maybeAdvanceColumn(childH);
840
- slide.addText([
841
- { text: numPrefix, options: { fontFace: FONT_MEDIUM, fontSize: style.bodySize, color: "000000", charSpacing: CHAR_SPACING } },
842
- ...mdRuns(child.text, { fontFace: FONT_MEDIUM, fontSize: style.bodySize, color: "000000", charSpacing: CHAR_SPACING }),
843
- ], {
844
- x: bulletX(),
845
- y: y,
846
- w: bulletW,
847
- h: childH,
848
- lineSpacingMultiple: style.lineSpacing,
849
- });
850
- y += childH + style.gap;
851
- }
852
- }
853
-
854
- } else if (item.type === "numbered") {
855
- numberedCounter++;
856
- const numPrefix = `(${numberedCounter}) `;
857
- const text = numPrefix + cleanMd(item.text);
858
- const h = estimateTextHeight(text, bulletW, style.bodySize, style.lineSpacing);
859
- maybeAdvanceColumn(h);
860
- slide.addText([
861
- { text: numPrefix, options: { fontFace: FONT_MEDIUM, fontSize: style.bodySize, color: "000000", charSpacing: CHAR_SPACING } },
862
- ...mdRuns(item.text, { fontFace: FONT_MEDIUM, fontSize: style.bodySize, color: "000000", charSpacing: CHAR_SPACING }),
863
- ], {
864
- x: bulletX(),
865
- y: y,
866
- w: bulletW,
867
- h,
868
- lineSpacingMultiple: style.lineSpacing,
869
- });
870
- y += h + style.gap;
871
-
872
- } else if (item.type === "text") {
873
- const h = estimateTextHeight(item.text, bulletW, style.bodySize, style.lineSpacing);
874
- maybeAdvanceColumn(h);
875
- slide.addText(mdRuns(item.text, {
876
- fontFace: FONT_MEDIUM,
877
- fontSize: style.bodySize,
878
- color: "000000",
879
- charSpacing: CHAR_SPACING,
880
- }), {
881
- x: bulletX(),
882
- y: y,
883
- w: bulletW,
884
- h,
885
- lineSpacingMultiple: style.lineSpacing,
886
- });
887
- y += h + style.gap;
888
- }
889
- }
890
-
891
- return y;
892
- }
893
-
894
- /**
895
- * Add summary group content as individual shapes.
896
- * Uses narrow width (~4.3" for headers, ~4.1" for bullets).
897
- */
898
- function addSummaryShapes(slide, content, region) {
899
- let y = region.y || 1.04;
900
-
901
- // Group heading
902
- if (content.heading) {
903
- slide.addText(mdRuns(content.heading, {
904
- fontFace: SEC_HEADER_FONT,
905
- fontSize: SEC_HEADER_SIZE,
906
- color: SEC_HEADER_COLOR,
907
- charSpacing: CHAR_SPACING,
908
- }), {
909
- x: region.x || 0.39,
910
- y: y,
911
- w: SUMMARY_HEADER_W,
912
- h: SEC_HEADER_H,
913
- });
914
- y += SEC_HEADER_H + 0.08;
915
- }
916
-
917
- // Items
918
- if (content.items) {
919
- for (const item of content.items) {
920
- if (item.type === "section") {
921
- // Section header
922
- slide.addText(mdRuns(item.heading, {
923
- fontFace: SEC_HEADER_FONT,
924
- fontSize: SEC_HEADER_SIZE,
925
- color: SEC_HEADER_COLOR,
926
- charSpacing: CHAR_SPACING,
927
- }), {
928
- x: region.x || 0.39,
929
- y: y,
930
- w: SUMMARY_HEADER_W,
931
- h: SEC_HEADER_H,
932
- });
933
- y += SEC_HEADER_H + 0.05;
934
-
935
- // Children as bullet items
936
- if (item.children) {
937
- for (const child of item.children) {
938
- slide.addText([
939
- { text: BULLET_PREFIX, options: { fontFace: FONT_BOLD, fontSize: SUMMARY_BULLET_SIZE, color: BULLET_COLOR, charSpacing: CHAR_SPACING } },
940
- ...mdRuns(child.text, { fontFace: FONT_MEDIUM, fontSize: SUMMARY_BULLET_SIZE, color: "000000", charSpacing: CHAR_SPACING }),
941
- ], {
942
- x: 0.59,
943
- y: y,
944
- w: SUMMARY_BULLET_W,
945
- h: SUMMARY_BULLET_H,
946
- lineSpacingMultiple: SUMMARY_BULLET_LINE_SPACING,
947
- });
948
- y += SUMMARY_BULLET_Y_SPACING;
949
- }
950
- }
951
-
952
- } else if (item.type === "bullet") {
953
- slide.addText([
954
- { text: BULLET_PREFIX, options: { fontFace: FONT_BOLD, fontSize: SUMMARY_BULLET_SIZE, color: BULLET_COLOR, charSpacing: CHAR_SPACING } },
955
- ...mdRuns(item.text, { fontFace: FONT_MEDIUM, fontSize: SUMMARY_BULLET_SIZE, color: "000000", charSpacing: CHAR_SPACING }),
956
- ], {
957
- x: 0.59,
958
- y: y,
959
- w: SUMMARY_BULLET_W,
960
- h: SUMMARY_BULLET_H,
961
- lineSpacingMultiple: SUMMARY_BULLET_LINE_SPACING,
962
- });
963
- y += SUMMARY_BULLET_Y_SPACING;
964
-
965
- if (item.children) {
966
- for (const child of item.children) {
967
- slide.addText([
968
- { text: BULLET_PREFIX, options: { fontFace: FONT_BOLD, fontSize: SUMMARY_BULLET_SIZE, color: BULLET_COLOR, charSpacing: CHAR_SPACING } },
969
- ...mdRuns(child.text, { fontFace: FONT_MEDIUM, fontSize: SUMMARY_BULLET_SIZE, color: "000000", charSpacing: CHAR_SPACING }),
970
- ], {
971
- x: 0.59,
972
- y: y,
973
- w: SUMMARY_BULLET_W,
974
- h: SUMMARY_BULLET_H,
975
- lineSpacingMultiple: SUMMARY_BULLET_LINE_SPACING,
976
- });
977
- y += SUMMARY_BULLET_Y_SPACING;
978
- }
979
- }
980
-
981
- } else if (item.type === "numbered") {
982
- slide.addText([
983
- { text: BULLET_PREFIX, options: { fontFace: FONT_BOLD, fontSize: SUMMARY_BULLET_SIZE, color: BULLET_COLOR, charSpacing: CHAR_SPACING } },
984
- ...mdRuns(item.text, { fontFace: FONT_MEDIUM, fontSize: SUMMARY_BULLET_SIZE, color: "000000", charSpacing: CHAR_SPACING }),
985
- ], {
986
- x: 0.59,
987
- y: y,
988
- w: SUMMARY_BULLET_W,
989
- h: SUMMARY_BULLET_H,
990
- lineSpacingMultiple: SUMMARY_BULLET_LINE_SPACING,
991
- });
992
- y += SUMMARY_BULLET_Y_SPACING;
993
- }
994
- }
995
- }
996
- }
997
-
998
- // ── TOC rendering (separate shapes per item) ─────────────────────────────
999
-
1000
- /**
1001
- * Add TOC items as separate text shapes, matching TEMPLATE-PPTX.pptx exactly.
1002
- * Each item is: "N. text" in the bold face at 18pt, w=7.5, h=0.55, spaced 0.65" apart.
1003
- */
1004
- function addTocShapes(slide, items) {
1005
- let y = TOC_Y_START;
1006
- let num = 1;
1007
-
1008
- for (const item of items) {
1009
- const text = `${num}. ${cleanMd(item.text || item.heading || "")}`;
1010
- slide.addText(text, {
1011
- x: TOC_X,
1012
- y: y,
1013
- w: TOC_W,
1014
- h: TOC_H,
1015
- fontFace: TOC_FONT,
1016
- fontSize: TOC_SIZE,
1017
- color: TOC_COLOR,
1018
- charSpacing: CHAR_SPACING,
1019
- });
1020
- y += TOC_Y_SPACING;
1021
- num++;
1022
- }
1023
- }
1024
-
1025
- /**
1026
- * Check if a slide is a TOC slide by inspecting the title region content.
1027
- */
1028
- function isTocSlide(regions) {
1029
- const title = regions.title;
1030
- if (!title || typeof title.content !== "string") return false;
1031
- return title.content.includes("목차");
1032
- }
1033
-
1034
- function adjustedRegionsForImageColumn(regions) {
1035
- const body = regions.body;
1036
- const image = regions.image;
1037
- if (!body || !image || !body.content || !image.content) return regions;
1038
- if (body.content.type !== "body" || !["image", "image-grid"].includes(image.content.type)) return regions;
1039
-
1040
- const bodyX = body.x || 0;
1041
- const bodyW = body.w || 10.188;
1042
- const imageX = image.x || 0;
1043
- const gap = 0.25;
1044
- if (imageX <= bodyX || imageX >= bodyX + bodyW) return regions;
1045
-
1046
- const denseImageGrid = image.content.type === "image-grid" && Array.isArray(image.content.images) && image.content.images.length >= 6;
1047
- return {
1048
- ...regions,
1049
- body: {
1050
- ...body,
1051
- w: Math.max(0.5, imageX - bodyX - gap),
1052
- },
1053
- image: denseImageGrid ? {
1054
- ...image,
1055
- y: Math.min(image.y || 1.5, 1.08),
1056
- h: Math.max(image.h || 3.0, 5.85),
1057
- } : image,
1058
- figure_caption: denseImageGrid && regions.figure_caption ? {
1059
- ...regions.figure_caption,
1060
- y: Math.max(regions.figure_caption.y || 4.55, 7.02),
1061
- } : regions.figure_caption,
1062
- };
1063
- }
1064
-
1065
- // ── Region rendering ────────────────────────────────────────────────────────
1066
-
1067
- /**
1068
- * Add a region as a positioned element on the slide.
1069
- *
1070
- * Content types:
1071
- * - string: title, main-box, notes
1072
- * - { type: "body" } bullet/section/numbered items → individual shapes
1073
- * - { type: "kpi-table" } table with headers and rows
1074
- * - { type: "image" } image element
1075
- * - { type: "figure-caption" } figure caption (auto-numbered "도 N.")
1076
- * - { type: "table-caption" } table caption (auto-numbered "표 N.")
1077
- * - { type: "summary-group" } heading + items → individual shapes
1078
- */
1079
- function addRegion(slide, name, region, fontRoles, pageH, regionBottom, isToc, sourceDir) {
1080
- if (!region) return;
1081
-
1082
- const content = region.content;
1083
- if (content === undefined || content === null) return;
1084
- if (typeof content === "object" && !content.type) return;
1085
-
1086
- const roleName = region.font_role || region.heading_role || region.sub_item_role;
1087
- const role = fontRoles[roleName] || {};
1088
-
1089
- // ── String content (title, main-box, notes) ────────────────────────
1090
- if (typeof content === "string") {
1091
- const opts = {
1092
- x: region.x || 0,
1093
- y: region.y || 0,
1094
- w: region.w || 4,
1095
- h: region.h || ((regionBottom || (pageH - 0.5)) - (region.y || 0)),
1096
- fontFace: role.font || FONT_BOLD,
1097
- fontSize: role.size || 16,
1098
- color: (role.color || "000000").replace("#", ""),
1099
- align: region.align || role.align || "left",
1100
- valign: region.valign || "top",
1101
- };
1102
-
1103
- if (role.charSpacing != null) opts.charSpacing = role.charSpacing;
1104
- if (role.lineSpacing) opts.lineSpacingMultiple = role.lineSpacing;
1105
-
1106
- // Border (main_box)
1107
- if (region.border) {
1108
- opts.line = {
1109
- color: (region.border.color || "#000000").replace("#", ""),
1110
- width: region.border.width || 1,
1111
- };
1112
- }
1113
-
1114
- // Autofit
1115
- if (region.autofit === "resize") opts.autoFit = true;
1116
- else if (region.autofit === "shrink") opts.shrinkText = true;
1117
-
1118
- slide.addText(mdRuns(content, {
1119
- fontFace: opts.fontFace,
1120
- fontSize: opts.fontSize,
1121
- color: opts.color,
1122
- ...(opts.charSpacing != null ? { charSpacing: opts.charSpacing } : {}),
1123
- }), name === "title" ? { ...opts, objectName: "title@legacy" } : opts);
1124
- return;
1125
- }
1126
-
1127
- // ── Body content → TOC shapes or individual shapes ──────────────────
1128
- if (content.type === "body") {
1129
- if (isToc) {
1130
- addTocShapes(slide, content.items || []);
1131
- } else {
1132
- const startY = region.y || 1.044;
1133
- addBodyShapes(slide, content.items || [], startY, region.w || 10.188, regionBottom || pageH - 0.5, region.originX);
1134
- }
1135
- return;
1136
- }
1137
-
1138
- // ── KPI Table ──────────────────────────────────────────────────────
1139
- if (content.type === "kpi-table" && content.chart) {
1140
- renderChart(slide, content, region, PLAIN_CHART_STYLE, regionBottom);
1141
- return;
1142
- }
1143
- if (content.type === "kpi-table") {
1144
- const numeric = numericColumns(content.headers || [], content.rows || []);
1145
- const metrics = tableMetrics((content.rows || []).length + 1, region.y || 0, regionBottom,
1146
- { rowH: 0.35, maxRowH: 0.55, headerSize: 14, dataSize: 14 });
1147
- const headerRole = fontRoles[region.font_role_header] || fontRoles.table_header;
1148
- const dataRole = fontRoles[region.font_role_data] || fontRoles.table_data;
1149
-
1150
- const tableData = [];
1151
-
1152
- if (content.headers && content.headers.length > 0) {
1153
- tableData.push(
1154
- content.headers.map((h) => ({
1155
- text: cleanMd(h),
1156
- options: {
1157
- bold: false,
1158
- fontSize: headerRole ? headerRole.size : 14,
1159
- fontFace: headerRole ? headerRole.font : FONT_BOLD,
1160
- color: headerRole ? headerRole.color : "000000",
1161
- align: numeric[content.headers.indexOf(h)] ? "right" : (headerRole ? headerRole.align : "left"),
1162
- valign: "middle",
1163
- ...(headerRole && headerRole.charSpacing != null
1164
- ? { charSpacing: headerRole.charSpacing }
1165
- : {}),
1166
- ...(headerRole && headerRole.fill
1167
- ? { fill: { color: headerRole.fill.replace("#", "") } }
1168
- : {}),
1169
- },
1170
- }))
1171
- );
1172
- }
1173
-
1174
- if (content.rows) {
1175
- for (const row of content.rows) {
1176
- tableData.push(
1177
- row.map((cell, ci) => linkedTableCell(cell, {
1178
- fontSize: dataRole ? dataRole.size : 14,
1179
- fontFace: dataRole ? dataRole.font : FONT_MEDIUM,
1180
- color: dataRole ? dataRole.color : "000000",
1181
- align: numeric[ci] ? "right" : (dataRole ? dataRole.align : "left"),
1182
- valign: "middle",
1183
- ...(dataRole && dataRole.charSpacing != null
1184
- ? { charSpacing: dataRole.charSpacing }
1185
- : {}),
1186
- }))
1187
- );
1188
- }
1189
- }
1190
-
1191
- const numCols = (content.headers && content.headers.length) || 1;
1192
- const tblX = region.x || 0;
1193
- const tblY = region.y || 0;
1194
- const tblW = region.w || 4;
1195
- slide.addTable(tableData, {
1196
- x: tblX,
1197
- y: tblY,
1198
- w: tblW,
1199
- border: { pt: 1.5, color: "000000" },
1200
- colW: numCols > 0 && content.headers ? proportionalWidths(content.headers, content.rows || [], tblW) : Array(numCols).fill(tblW / numCols),
1201
- margin: [3.6, 7.2, 3.6, 7.2],
1202
- rowH: metrics.rowH,
1203
- });
1204
- return;
1205
- }
1206
-
1207
- // ── Image ──────────────────────────────────────────────────────────
1208
- if (content.type === "image") {
1209
- const imgPath = resolveImagePath(content.src, sourceDir);
1210
- const fit = containBox(
1211
- region.x || 0,
1212
- region.y || 0,
1213
- region.w || 2,
1214
- region.h || 1,
1215
- readImageSize(imgPath),
1216
- );
1217
- slide.addImage({
1218
- path: imgPath,
1219
- ...fit,
1220
- altText: content.caption || path.basename(content.src || imgPath),
1221
- });
1222
- return;
1223
- }
1224
-
1225
- if (content.type === "image-grid") {
1226
- const images = Array.isArray(content.images) ? content.images : [];
1227
- const count = images.length;
1228
- if (count === 0) return;
1229
- const cols = count <= 2 ? count : 2;
1230
- const rows = Math.ceil(count / cols);
1231
- const gap = 0.06;
1232
- const baseX = region.x || 0;
1233
- const baseY = region.y || 0;
1234
- const baseW = region.w || 4;
1235
- const baseH = region.h || 3;
1236
- const cellW = (baseW - gap * (cols - 1)) / cols;
1237
- const cellH = (baseH - gap * (rows - 1)) / rows;
1238
- images.forEach((image, idx) => {
1239
- const imgPath = resolveImagePath(image.src, sourceDir);
1240
- const col = idx % cols;
1241
- const row = Math.floor(idx / cols);
1242
- const fit = containBox(
1243
- baseX + col * (cellW + gap),
1244
- baseY + row * (cellH + gap),
1245
- cellW,
1246
- cellH,
1247
- readImageSize(imgPath),
1248
- );
1249
- slide.addImage({
1250
- path: imgPath,
1251
- ...fit,
1252
- altText: image.caption || path.basename(image.src || imgPath),
1253
- });
1254
- });
1255
- return;
1256
- }
1257
-
1258
- // ── Figure Caption ────────────────────────────────────────────────
1259
- if (content.type === "figure-caption") {
1260
- const num = content.number || 1;
1261
- const prefix = content.prefix || "도";
1262
- const captionText = content.caption ? `${prefix} ${num}. ${cleanMd(content.caption)}` : `${prefix} ${num}.`;
1263
- const captionRole = fontRoles.figure_caption || {};
1264
- const captionW = region.w || 4;
1265
- const captionSize = captionRole.size || 16;
1266
- const captionSpacing = captionRole.lineSpacing || 1.3;
1267
- slide.addText(captionText, {
1268
- x: region.x || 0,
1269
- y: region.y || 0,
1270
- w: captionW,
1271
- // A caption that wraps needs a box tall enough to hold every line, or it
1272
- // overflows its frame. The declared region height is the floor, not the cap.
1273
- h: Math.max(
1274
- region.h || 0.456,
1275
- estimateTextHeight(captionText, captionW, captionSize, captionSpacing)
1276
- ),
1277
- fontFace: captionRole.font || FONT_MEDIUM,
1278
- fontSize: captionSize,
1279
- color: (captionRole.color || "000000").replace("#", ""),
1280
- align: region.align || captionRole.align || "center",
1281
- charSpacing: captionRole.charSpacing != null ? captionRole.charSpacing : CHAR_SPACING,
1282
- lineSpacingMultiple: captionRole.lineSpacing || 1.3,
1283
- });
1284
- return;
1285
- }
1286
-
1287
- // ── Table Caption ─────────────────────────────────────────────────
1288
- if (content.type === "table-caption") {
1289
- const num = content.number || 1;
1290
- const prefix = content.prefix || "표";
1291
- const captionText = content.caption ? `${prefix} ${num}. ${cleanMd(content.caption)}` : `${prefix} ${num}.`;
1292
- const captionRole = fontRoles.table_caption || {};
1293
- const captionW = region.w || 4;
1294
- const captionSize = captionRole.size || 16;
1295
- const captionSpacing = captionRole.lineSpacing || 1.3;
1296
- slide.addText(captionText, {
1297
- x: region.x || 0,
1298
- y: region.y || 0,
1299
- w: captionW,
1300
- // Same rule as the figure caption: the declared region height is the
1301
- // floor, not the cap, or a wrapping caption overflows its frame.
1302
- h: Math.max(
1303
- region.h || 0.456,
1304
- estimateTextHeight(captionText, captionW, captionSize, captionSpacing)
1305
- ),
1306
- fontFace: captionRole.font || FONT_MEDIUM,
1307
- fontSize: captionSize,
1308
- color: (captionRole.color || "000000").replace("#", ""),
1309
- align: region.align || captionRole.align || "center",
1310
- charSpacing: captionRole.charSpacing != null ? captionRole.charSpacing : CHAR_SPACING,
1311
- lineSpacingMultiple: captionRole.lineSpacing || 1.3,
1312
- });
1313
- return;
1314
- }
1315
-
1316
- // ── Summary Group → individual shapes ──────────────────────────────
1317
- if (content.type === "summary-group") {
1318
- addSummaryShapes(slide, content, region);
1319
- return;
1320
- }
1321
- }
1322
-
1323
- // ── Rich card, KPI and table rendering (templates with the rich-blocks capability) ──
1324
-
1325
- /** Split a title string into runs, emphasizing **word** in the primary color. */
1326
- function azureTitleRuns(text, P) {
1327
- return text.split(/(\*\*[^*]+\*\*)/g).filter(Boolean).map((p) =>
1328
- p.startsWith("**") && p.endsWith("**")
1329
- ? { text: p.slice(2, -2), options: { color: hex(P.primary) } }
1330
- : { text: p, options: {} }
1331
- );
1332
- }
1333
-
1334
- /** Strip leftover markdown markers (### heading, list dashes, **bold**, `code`)
1335
- * from inline text, returning a clean plain string. */
1336
- function cleanMd(s) {
1337
- return String(s || "")
1338
- .replace(/^#{1,6}\s+/, "")
1339
- .replace(/^\s*[*-]\s+/, "")
1340
- .replace(/\*\*(.+?)\*\*/g, "$1")
1341
- .replace(/(?<![\w*])\*([^*\s](?:[^*]*[^*\s])?)\*(?![\w*])/gu, "$1")
1342
- .replace(/`/g, "")
1343
- .trim();
1344
- }
1345
-
1346
- /** Convert inline markdown in `text` to pptxgenjs styled runs: **x** → a bold
1347
- * run, plain segments → normal runs. Leading heading/list markers and backticks
1348
- * are stripped. `runOpts` styles every run; bold runs additionally set bold.
1349
- * Guarantees no raw "**", "###", or backtick reaches the rendered <a:t>. */
1350
- function mdRuns(text, runOpts = {}) {
1351
- const clean = String(text || "")
1352
- .replace(/^#{1,6}\s+/, "")
1353
- .replace(/^\s*[*-]\s+/, "")
1354
- .replace(/`/g, "");
1355
- // **x** is bold; *x* (a journal or book name) is italic.
1356
- const parts = clean.split(/(\*\*[^*]+\*\*|(?<![\w*])\*[^*\s](?:[^*]*[^*\s])?\*(?![\w*]))/gu).filter((p) => p !== "");
1357
- if (parts.length === 0) return [{ text: "", options: { ...runOpts } }];
1358
- return parts.map((p) =>
1359
- p.startsWith("**") && p.endsWith("**")
1360
- ? { text: p.slice(2, -2), options: { ...runOpts, bold: true } }
1361
- : p.length > 2 && p.startsWith("*") && p.endsWith("*")
1362
- ? { text: p.slice(1, -1), options: { ...runOpts, italic: true } }
1363
- : { text: p, options: { ...runOpts } }
1364
- );
1365
- }
1366
-
1367
- /** Convert body items into up to 4 cards: {heading, lines[]}.
1368
- * Handles both authoring styles:
1369
- * - `### Title` (parsed as `text`) followed by `- **point**` (a `section`) → one card
1370
- * titled by the heading, the section folded into the body.
1371
- * - bare `- **Title**` sections → one card each (heading = bold title). */
1372
- function bodyToCards(items) {
1373
- const cards = [];
1374
- let cur = null;
1375
- for (const item of items || []) {
1376
- if (item.type === "text" && item.text) {
1377
- cur = { heading: cleanMd(item.text), lines: [] };
1378
- cards.push(cur);
1379
- } else if (item.type === "section" || item.type === "bullet") {
1380
- const head = cleanMd(item.heading || item.text || "");
1381
- const kids = (item.children || []).map((c) => cleanMd(c.text)).filter(Boolean);
1382
- if (cur && cur.lines.length === 0 && cur.heading) {
1383
- // fold this section under the preceding heading card
1384
- if (head) cur.lines.push(head);
1385
- kids.forEach((k) => cur.lines.push(" " + k));
1386
- } else {
1387
- cur = { heading: head, lines: kids };
1388
- cards.push(cur);
1389
- }
1390
- } else if (item.type === "numbered" && item.text) {
1391
- if (cur) cur.lines.push(cleanMd(item.text));
1392
- else { cur = { heading: "", lines: [cleanMd(item.text)] }; cards.push(cur); }
1393
- }
1394
- }
1395
- return cards.slice(0, 4);
1396
- }
1397
-
1398
- // Card geometry shared by drawing and sizing, so a card is exactly as tall as its text
1399
- // (template tokens card.*; reassigned in render()).
1400
- let CARD_PAD = LEGACY_TOKENS.card.pad;
1401
- let CARD_CHIP = LEGACY_TOKENS.card.chip;
1402
- let CARD_MIN_H = LEGACY_TOKENS.card.minH;
1403
- let CARD_GAP = LEGACY_TOKENS.card.gap;
1404
- let CARD_RADIUS = LEGACY_TOKENS.card.radius;
1405
-
1406
- // Primary reading text stays at or above the 12 pt floor of the design laws.
1407
- function cardBodySize(card) {
1408
- return card.lines && card.lines.length > 4 ? 12 : 13;
1409
- }
1410
-
1411
- // Card text height: the conservative wrap count, but line boxes at their real
1412
- // leading (estimateTextHeight pads every paragraph, which left short cards hollow).
1413
- function cardTextHeight(text, innerW, fontSize, lineSpacing) {
1414
- return (estimateWrappedLines(text, innerW, fontSize) * fontSize * lineSpacing * 1.2) / 72;
1415
- }
1416
-
1417
- function cardHeadingHeight(card, innerW) {
1418
- return card.heading ? cardTextHeight(card.heading, innerW, 15, 1.05) + 0.06 : 0;
1419
- }
1420
-
1421
- function cardBodyHeight(card, innerW) {
1422
- if (card.bodyRuns && card.bodyRuns.length) return card.bodyHeight || 0;
1423
- const fs = cardBodySize(card);
1424
- return (card.lines || []).reduce((sum, line) => sum + cardTextHeight(line, innerW, fs, 1.3), 0.1);
1425
- }
1426
-
1427
- /** Height a card needs for its chip, heading and body at the width it is drawn. */
1428
- function azureCardHeight(card, w) {
1429
- const innerW = w - 2 * CARD_PAD;
1430
- const head = card.heading ? 0.18 + Math.max(0.4, cardHeadingHeight(card, innerW)) + 0.04 : 0.18;
1431
- return CARD_PAD + CARD_CHIP + head + cardBodyHeight(card, innerW) + CARD_PAD;
1432
- }
1433
-
1434
- /**
1435
- * The height a row of cards is drawn at: the tallest card's content, never taller
1436
- * than the region the template reserves and never shorter than CARD_MIN_H. A fixed
1437
- * region height left short cards mostly empty; the space below a short row is free
1438
- * for the takeaway, and the craft gate reports a slide that stays thin.
1439
- */
1440
- function fittedCardHeight(cards, w, regionH) {
1441
- const need = Math.max(...cards.map((c) => azureCardHeight(c, w)));
1442
- return Math.min(regionH, Math.max(CARD_MIN_H, need));
1443
- }
1444
-
1445
- /** Draw one rounded card with a numbered chip, heading, and body lines. */
1446
- function drawAzureCard(slide, x, y, w, h, card, idx, P, emphasized) {
1447
- const bg = emphasized ? hex(P.primary_deep) : hex(P.tint);
1448
- const headColor = emphasized ? "FFFFFF" : hex(P.ink);
1449
- const bodyColor = emphasized ? hex(P.emphasisTextColor) : hex(P.ink_muted);
1450
- slide.addShape("roundRect", { x, y, w, h, rectRadius: CARD_RADIUS, fill: { color: bg }, line: { type: "none" } });
1451
- const pad = CARD_PAD;
1452
- const innerW = w - 2 * pad;
1453
- // numbered chip
1454
- const chip = CARD_CHIP;
1455
- slide.addShape("roundRect", {
1456
- x: x + pad, y: y + pad, w: chip, h: chip, rectRadius: 0.12,
1457
- fill: { color: emphasized ? "FFFFFF" : hex(P.primary) }, line: { type: "none" },
1458
- });
1459
- // Number sits in a text box whose rect is identical to the chip rect, with
1460
- // margin:0 and tight line spacing so the numeral reads dead-centre.
1461
- slide.addText(String(idx + 1).padStart(2, "0"), {
1462
- x: x + pad, y: y + pad, w: chip, h: chip, align: "center", valign: "middle",
1463
- fontFace: P.titleFont, fontSize: 15, bold: true,
1464
- color: emphasized ? hex(P.primary_deep) : "FFFFFF",
1465
- margin: 0, lineSpacingMultiple: 1,
1466
- });
1467
- const headH = cardHeadingHeight(card, innerW);
1468
- if (card.heading) {
1469
- slide.addText(card.heading, {
1470
- x: x + pad, y: y + pad + chip + 0.18, w: innerW, h: Math.max(0.4, headH),
1471
- fontFace: P.titleFont, fontSize: 15, bold: true, color: headColor, align: "left", valign: "top",
1472
- charSpacing: -0.3,
1473
- });
1474
- }
1475
- const bodyY = y + pad + chip + 0.18 + (card.heading ? Math.max(0.4, headH) + 0.04 : 0);
1476
- const bodyH = Math.max(0.3, h - (bodyY - y) - 0.18);
1477
- if (card.bodyRuns && card.bodyRuns.length) {
1478
- slide.addText(card.bodyRuns, {
1479
- x: x + pad, y: bodyY, w: innerW, h: bodyH,
1480
- color: bodyColor, align: "left", valign: "top", lineSpacingMultiple: card.lineSpacing || 1.28, charSpacing: -0.2,
1481
- });
1482
- } else if (card.lines && card.lines.length) {
1483
- // Shrink body font when there are many lines so it never exceeds the frame.
1484
- const fs = cardBodySize(card);
1485
- slide.addText(card.lines.join("\n"), {
1486
- x: x + pad, y: bodyY, w: innerW, h: bodyH,
1487
- fontFace: P.bodyFont, fontSize: fs, color: bodyColor, align: "left", valign: "top",
1488
- lineSpacingMultiple: 1.3, charSpacing: -0.2,
1489
- });
1490
- }
1491
- }
1492
-
1493
- /** Render content/main body as a row of cards, as tall as the longest card needs. */
1494
- function renderAzureCards(slide, items, region, P) {
1495
- const cards = bodyToCards(items);
1496
- if (!cards.length) return;
1497
- const n = cards.length;
1498
- const x0 = region.x || 0.6, y0 = region.y || 1.6, w = region.w || 12.1;
1499
- const gap = CARD_GAP;
1500
- const cardW = (w - (n - 1) * gap) / n;
1501
- const h = fittedCardHeight(cards, cardW, region.h || 3.0);
1502
- cards.forEach((c, i) => {
1503
- drawAzureCard(slide, x0 + i * (cardW + gap), y0, cardW, h, c, i, P, n >= 3 && i === n - 1);
1504
- });
1505
- return y0 + h;
1506
- }
1507
-
1508
- /** Flatten body/summary items (sections + children + bullets) into styled runs.
1509
- * `scale` (≤1) shrinks fonts to fit a fixed card height. */
1510
- function itemsToRuns(items, P, scale = 1) {
1511
- const s = (v) => Math.max(8, Math.round(v * scale * 10) / 10);
1512
- const runs = [];
1513
- for (const it of items || []) {
1514
- if (it.type === "section") {
1515
- runs.push({ text: cleanMd(it.heading || ""), options: { bold: true, fontFace: P.titleFont, fontSize: s(14), color: hex(P.ink), breakLine: true } });
1516
- for (const c of it.children || []) {
1517
- runs.push({ text: " " + cleanMd(c.text || ""), options: { fontFace: P.bodyFont, fontSize: s(12.5), color: hex(P.ink_muted), breakLine: true } });
1518
- }
1519
- } else if (it.text) {
1520
- runs.push({ text: (it.type === "numbered" ? " " : "• ") + cleanMd(it.text), options: { fontFace: P.bodyFont, fontSize: s(13), color: hex(P.ink_muted), breakLine: true } });
1521
- }
1522
- }
1523
- return runs;
1524
- }
1525
-
1526
- /** Estimate the rendered height of a group's items at a given font scale,
1527
- * using the same conservative metric inventory respects (estimateTextHeight). */
1528
- function estimateGroupHeight(items, innerW, scale, lineSpacing) {
1529
- let hsum = 0;
1530
- for (const it of items || []) {
1531
- if (it.type === "section") {
1532
- hsum += estimateTextHeight(it.heading || "", innerW, 14 * scale, lineSpacing);
1533
- for (const c of it.children || []) hsum += estimateTextHeight(" " + (c.text || ""), innerW - 0.1, 12.5 * scale, lineSpacing);
1534
- } else if (it.text) {
1535
- hsum += estimateTextHeight(it.text, innerW, 13 * scale, lineSpacing);
1536
- }
1537
- }
1538
- return hsum;
1539
- }
1540
-
1541
- /** The height a group's runs take at their real leading, for sizing (not fitting) the card. */
1542
- function groupTextHeight(items, innerW, scale, lineSpacing) {
1543
- let hsum = 0.1;
1544
- for (const it of items || []) {
1545
- if (it.type === "section") {
1546
- hsum += cardTextHeight(it.heading || "", innerW, 14 * scale, lineSpacing);
1547
- for (const c of it.children || []) hsum += cardTextHeight(" " + (c.text || ""), innerW - 0.1, 12.5 * scale, lineSpacing);
1548
- } else if (it.text) {
1549
- hsum += cardTextHeight("• " + it.text, innerW, 13 * scale, lineSpacing);
1550
- }
1551
- }
1552
- return hsum;
1553
- }
1554
-
1555
- /** A summary group as a card: the largest font scale (1 down to 0.55) whose text fits
1556
- * `maxH`, and the height that text needs at that scale. */
1557
- function groupCard(content, w, maxH, P) {
1558
- const innerW = w - 2 * CARD_PAD;
1559
- const heading = cleanMd(content.heading || "");
1560
- const headH = heading ? 0.18 + Math.max(0.4, cardHeadingHeight({ heading }, innerW)) + 0.04 : 0.18;
1561
- const fixed = CARD_PAD + CARD_CHIP + headH + 0.18;
1562
- let scale = 0.55, ls = 1.12;
1563
- for (const s of [1, 0.92, 0.85, 0.78, 0.72, 0.66, 0.6, 0.55]) {
1564
- if (estimateGroupHeight(content.items, innerW, s, s < 1 ? 1.12 : 1.28) <= maxH - fixed) { scale = s; ls = s < 1 ? 1.12 : 1.28; break; }
1565
- }
1566
- const bodyHeight = groupTextHeight(content.items, innerW, scale, ls);
1567
- return {
1568
- card: { heading, bodyRuns: itemsToRuns(content.items, P, scale), lineSpacing: ls, bodyHeight },
1569
- need: Math.min(maxH, fixed + bodyHeight + CARD_PAD - 0.18),
1570
- };
1571
- }
1572
-
1573
- /** Render a single summary-group as one card, at `opts.height` (shared by the
1574
- * groups of one slide) or at the height its own text needs. */
1575
- function renderAzureGroupCard(slide, content, region, P, idx, height) {
1576
- const w = region.w || 5.6, maxH = region.h || 2.4;
1577
- const { card, need } = groupCard(content, w, maxH, P);
1578
- const h = Math.min(maxH, Math.max(CARD_MIN_H, height || need));
1579
- drawAzureCard(slide, region.x || 0.6, region.y || 1.6, w, h, card, idx, P, false);
1580
- }
1581
-
1582
- /** Render a general data table (palette-styled): primary header, zebra tint rows. */
1583
- // ── Tables and charts that fill their region ────────────────────────────────
1584
-
1585
- // A cell reads as a number when, after units and signs, only digits remain.
1586
- const NUMERIC_CELL = /^(?:[▲▼△▽]\s*)?[+\-−±]?\s*[\d.,]+\s*(?:%p?|배|x|pt|bp|[가-힣]{1,3}|[A-Za-z]{1,3})?$/u;
1587
- const isNumericCell = (text) => NUMERIC_CELL.test(cleanMd(String(text)).trim());
1588
-
1589
- /** Columns whose data cells are all numbers are right-aligned; a dash for a missing value is blank. */
1590
- function numericColumns(headers, rows) {
1591
- return headers.map((_, ci) => {
1592
- const cells = rows.map((r) => r[ci]).filter((c) => c != null && !/^[\s—–\-]*$/u.test(cleanMd(String(c))));
1593
- return cells.length > 0 && cells.every(isNumericCell);
1594
- });
1595
- }
1596
-
1597
- /** Column widths proportional to the longest cell, Hangul counted wider, with a floor. */
1598
- function proportionalWidths(headers, rows, totalW) {
1599
- const len = (t) => [...cleanMd(String(t || ""))].reduce((n, ch) => n + (/[\u3131-\uD7A3]/u.test(ch) ? 1.7 : 1), 0);
1600
- const want = headers.map((h, ci) => Math.max(len(h), ...rows.map((r) => len(r[ci])), 4));
1601
- const floor = Math.min(0.9, totalW / headers.length);
1602
- const sum = want.reduce((a, b) => a + b, 0);
1603
- let widths = want.map((w) => Math.max(floor, (w / sum) * totalW));
1604
- const scale = totalW / widths.reduce((a, b) => a + b, 0);
1605
- widths = widths.map((w) => w * scale);
1606
- return widths;
1607
- }
1608
-
1609
- /**
1610
- * Row height and font size for a table that should use the room it has: rows
1611
- * grow toward `bottom` (up to a comfortable maximum) instead of leaving the lower
1612
- * half of the slide empty, and a roomy table gets a larger font.
1613
- */
1614
- function tableMetrics(rowCount, y, bottom, base) {
1615
- const room = Math.max(0, (bottom || y + rowCount * base.rowH) - y);
1616
- const rowH = Math.min(base.maxRowH, Math.max(base.rowH, (room * 0.92) / rowCount));
1617
- const scale = rowH >= 0.55 ? 1.2 : rowH >= 0.47 ? 1.1 : 1;
1618
- return { rowH, headerSize: +(base.headerSize * scale).toFixed(1), dataSize: +(base.dataSize * scale).toFixed(1) };
1619
- }
1620
-
1621
- function renderAzureTable(slide, content, region, P, bottom) {
1622
- const headers = content.headers || [];
1623
- const rows = content.rows || [];
1624
- if (!headers.length) return;
1625
- const x = region.x || 0.6, y = region.y || 1.6, w = region.w || 6.0;
1626
- const numeric = numericColumns(headers, rows);
1627
- const m = tableMetrics(rows.length + 1, y, bottom, { rowH: 0.4, maxRowH: 0.72, headerSize: 12, dataSize: 11.5 });
1628
- const tableRows = [];
1629
- tableRows.push(headers.map((h, ci) => ({
1630
- text: cleanMd(h),
1631
- options: { bold: true, color: "FFFFFF", fill: { color: hex(P.primary) }, fontFace: P.titleFont, fontSize: m.headerSize, align: numeric[ci] ? "right" : "left", valign: "middle" },
1632
- })));
1633
- rows.forEach((r, ri) => {
1634
- tableRows.push(r.map((c, ci) => linkedTableCell(c, {
1635
- color: hex(P.ink), fill: { color: ri % 2 ? "FFFFFF" : hex(P.tint) }, fontFace: P.bodyFont, fontSize: m.dataSize,
1636
- align: numeric[ci] ? "right" : "left", valign: "middle", bold: ci === 0 && !numeric[0],
1637
- })));
1638
- });
1639
- slide.addTable(tableRows, {
1640
- x, y, w, colW: proportionalWidths(headers, rows, w),
1641
- border: { type: "solid", color: hex(P.line), pt: 0.5 }, rowH: m.rowH, margin: [0.04, 0.12, 0.04, 0.12], autoPage: false,
1642
- });
1643
- }
1644
-
1645
- /** Parse "1,234", "▲12.5%", "−3" into a number; anything else is null. */
1646
- function chartNumber(text) {
1647
- const t = cleanMd(String(text || "")).trim().replace(/[,\s]/g, "").replace(/^[▲△+]/u, "").replace(/^[▼▽−]/u, "-");
1648
- const m = t.match(/^-?\d+(?:\.\d+)?/);
1649
- return m ? parseFloat(m[0]) : null;
1650
- }
1651
-
1652
- const CHART_TYPE = { bar: "bar", column: "bar", stacked: "bar", line: "line", area: "area", pie: "pie", doughnut: "doughnut" };
1653
-
1654
- /**
1655
- * A native PowerPoint chart (editable in PowerPoint: right-click > Edit Data)
1656
- * from a `::: chart` block. It takes the whole region down to `bottom`.
1657
- */
1658
- function renderChart(slide, content, region, style, bottom) {
1659
- const headers = content.headers || [];
1660
- const rows = content.rows || [];
1661
- const spec = content.chart || {};
1662
- const kind = CHART_TYPE[spec.type] || "bar";
1663
- const labels = rows.map((r) => cleanMd(String(r[0] || "")));
1664
- const series = headers.slice(1).map((name, si) => ({
1665
- name: cleanMd(String(name)),
1666
- labels,
1667
- // A dash or empty cell stays null and is written as a blank point; the line breaks instead of plunging to zero.
1668
- values: rows.map((r) => chartNumber(r[si + 1])),
1669
- }));
1670
- // Data labels keep the decimals the table writes: beside a table that says 18.0 the chart says 18.0 too.
1671
- const decimals = Math.min(3, Math.max(0, ...rows.flatMap((r) => r.slice(1).map((v) => ((/\.(\d+)/u.exec(cleanMd(String(v == null ? "" : v)).replace(/,/gu, "")) || [, ""])[1]).length))));
1672
- const x = region.x || 0.6, y = region.y || 1.6, w = region.w || 8;
1673
- const h = Math.max(1.6, ((bottom || y + (region.h || 3.2)) - y));
1674
- const colors = style.series.map((c) => hex(c));
1675
- const round = kind === "pie" || kind === "doughnut";
1676
- const data = round ? series.slice(0, 1) : series;
1677
- // Crowding, estimated in points: a category label wider than its slot is slanted; side-by-side
1678
- // value labels wider than their bars collide, so the values are read off a shown axis instead.
1679
- const ems = (t) => [...String(t)].reduce((a, ch) => a + (/[\u3131-\uD7A3]/u.test(ch) ? 0.94 : /\s/u.test(ch) ? 0.28 : 0.56), 0);
1680
- const labelPt = style.labelSize || 11;
1681
- const slot = (w * 72 * 0.85) / Math.max(1, labels.length);
1682
- // A renderer adds space between Hangul and digits ("25 년 2Q"), so a label needs a quarter of its slot spare.
1683
- // Horizontal bars run their categories down the side, where LibreOffice drops slanted labels.
1684
- const slanted = !round && kind !== "line" && spec.type !== "bar" && Math.max(0, ...labels.map(ems)) * labelPt > slot * 0.75;
1685
- const clustered = kind === "bar" && spec.type !== "stacked" && data.length > 1;
1686
- const valueW = Math.max(0, ...data.flatMap((d) => d.values.map((v) => ems(Number(v).toLocaleString("en-US"))))) * labelPt;
1687
- // Lines of two or more series share the plot's rows, so a label over each point covers its neighbours.
1688
- const crowded = !round && ((clustered && valueW > (slot / 1.6 / data.length) * 1.1) || (kind === "line" && data.length > 1));
1689
- const values = data.flatMap((d) => d.values).filter((v) => v !== null);
1690
- // Each row of a multi-series horizontal bar chart holds several thin bars; thinner than a label line, their value labels touch.
1691
- const horizontal = !round && spec.type === "bar";
1692
- const dataPt = style.labelSize || (round ? 12 : 10);
1693
- const barPt = (h * 72 - 54) / Math.max(1, labels.length) / (data.length + 0.6);
1694
- const thin = horizontal && data.length > 1 && spec.labels !== false && barPt < dataPt * 1.2;
1695
- // A narrow horizontal bar chart steps its value axis coarsely so the numbers stay level; LibreOffice slants a crowded axis.
1696
- const top = Math.max(0, ...values), low = Math.min(0, ...values);
1697
- const tickW = ems(Math.round(Math.max(top, -low)).toLocaleString("en-US")) * dataPt;
1698
- const catW = Math.min(w * 72 * 0.4, Math.max(0, ...labels.map(ems)) * labelPt + 8);
1699
- const ticks = Math.floor((w * 72 - catW - 16) / (tickW * 2));
1700
- const nice = (step) => { const p = 10 ** Math.floor(Math.log10(step)); return [1, 2, 2.5, 5, 10].find((m) => m * p >= step) * p; };
1701
- const majorUnit = horizontal && top > low && ticks < 6 ? nice((top - low) / Math.max(1, ticks - 1)) : null;
1702
- const zeroBased = !round && values.length > 0 && values.every((v) => v >= 0);
1703
- // The unit stands once: a legend whose series names carry it already says it, so the axis title goes;
1704
- // otherwise it is set level (a rotated title stacks Hangul a glyph a line and reads tiny; 360 degrees, since
1705
- // pptxgenjs writes no angle for 0 and the axis default is vertical).
1706
- const unitShown = spec.unit && data.length > 1 && data.every((d) => String(d.name || "").includes(spec.unit));
1707
- const axisTitle = spec.unit && !unitShown && !(Boolean(style.valAxisHidden) && !crowded && !thin);
1708
- slide.addChart(kind, data, {
1709
- x, y, w, h,
1710
- barDir: spec.type === "bar" ? "bar" : "col",
1711
- barGrouping: spec.type === "stacked" ? "stacked" : "clustered",
1712
- barGapWidthPct: 60,
1713
- chartColors: round ? labels.map((_, i) => colors[i % colors.length]) : colors,
1714
- showLegend: round || data.length > 1,
1715
- legendPos: round ? "r" : "t",
1716
- legendFontFace: style.bodyFont, legendFontSize: 11, legendColor: hex(style.ink),
1717
- showValue: !round && spec.labels !== false && !crowded && !thin,
1718
- showPercent: round && spec.labels !== false, showLabel: false,
1719
- dataLabelFontFace: style.bodyFont, dataLabelFontSize: style.labelSize || (round ? 12 : 10),
1720
- dataLabelColor: round ? "FFFFFF" : hex(style.ink),
1721
- dataLabelPosition: round ? "ctr" : kind === "line" ? "t" : "outEnd",
1722
- dataLabelFormatCode: round ? "0%" : decimals ? `#,##0.${"0".repeat(decimals)}` : "#,##0",
1723
- catAxisLabelFontFace: style.bodyFont, catAxisLabelFontSize: style.labelSize || 11, catAxisLabelColor: hex(style.ink),
1724
- valAxisLabelFontFace: style.bodyFont, valAxisLabelFontSize: style.labelSize || 10, valAxisLabelColor: hex(style.ink_muted),
1725
- valAxisLabelFormatCode: "#,##0",
1726
- // A pack with direct labels and no gridlines drops the value axis: the labels carry the values.
1727
- valAxisHidden: Boolean(style.valAxisHidden) && !crowded && !thin,
1728
- ...(majorUnit ? { valAxisMajorUnit: majorUnit } : {}),
1729
- // Negative values bring the zero line into the plot; category names go to the axis minimum, off the bars.
1730
- ...(horizontal && low < 0 ? { catAxisLabelPos: "low" } : {}),
1731
- // Blank points leave gaps.
1732
- displayBlanksAs: "gap",
1733
- ...(style.valMax ? { valAxisMaxVal: style.valMax } : {}),
1734
- ...(zeroBased ? { valAxisMinVal: 0 } : {}),
1735
- ...(slanted ? { catAxisLabelRotate: -45 } : {}),
1736
- catAxisLineShow: true, catAxisLineColor: hex(style.line),
1737
- valGridLine: style.grid === false && !crowded && !thin ? { style: "none" } : { color: hex(style.line), size: 0.5 },
1738
- valAxisLineShow: false,
1739
- lineSize: 2.5, lineDataSymbol: "circle", lineDataSymbolSize: 7,
1740
- holeSize: 58,
1741
- ...(axisTitle ? { showValAxisTitle: true, valAxisTitle: spec.unit, valAxisTitleRotate: 360, valAxisTitleFontSize: style.labelSize || 10, valAxisTitleColor: hex(style.ink_muted), valAxisTitleFontFace: style.bodyFont } : {}),
1742
- ...(spec.title ? { showTitle: true, title: spec.title, titleFontFace: style.titleFont, titleFontSize: 13, titleColor: hex(style.ink) } : {}),
1743
- });
1744
- }
1745
-
1746
- /** A main-box as a tinted callout sized to its text, not an outlined frame. */
1747
- function renderAzureCallout(slide, text, region, P) {
1748
- const x = region.x || 0.84, y = region.y || 5.8, w = region.w || 11.6;
1749
- const lines = Math.max(1, Math.ceil(cleanMd(text).length / Math.max(20, w * 9)));
1750
- const h = Math.min(region.h || 1.0, 0.28 + lines * 0.3);
1751
- slide.addShape("roundRect", { x, y, w, h, rectRadius: 0.1, fill: { color: hex(P.tint) }, line: { type: "none" } });
1752
- slide.addText(cleanMd(text), {
1753
- x: x + 0.24, y, w: w - 0.48, h, fontFace: P.bodyFont, fontSize: 13, bold: true,
1754
- color: hex(P.primary_deep), align: "left", valign: "middle", margin: 0,
1755
- });
1756
- }
1757
-
1758
- /** Render a kpi-table as a row of KPI cards (value + label); first card filled. */
1759
- function renderAzureKpi(slide, content, region, P) {
1760
- const cards = kpiCards(content);
1761
- const values = cards.map((c) => c.value);
1762
- const labels = cards.map((c) => c.label);
1763
- const n = Math.min(values.length, 4);
1764
- if (!n) return;
1765
- const x0 = region.x || 0.6, y0 = region.y || 1.6, w = region.w || 12.1, h = region.h || 1.7;
1766
- const gap = KPI_GAP;
1767
- const cardW = (w - (n - 1) * gap) / n;
1768
- for (let i = 0; i < n; i++) {
1769
- const fill = i === 0;
1770
- const x = x0 + i * (cardW + gap);
1771
- slide.addShape("roundRect", {
1772
- x, y: y0, w: cardW, h, rectRadius: 0.16,
1773
- fill: { color: fill ? hex(P.primary) : "FFFFFF" },
1774
- line: fill ? { type: "none" } : { color: hex(P.line), width: 1 },
1775
- });
1776
- slide.addText(cleanMd(values[i]), {
1777
- x: x + 0.28, y: y0 + 0.20, w: cardW - 0.56, h: 0.8,
1778
- fontFace: P.titleFont, fontSize: 30, bold: true,
1779
- color: fill ? "FFFFFF" : hex(P.primary), align: "left", valign: "middle", charSpacing: -1, margin: 0,
1780
- });
1781
- slide.addText(cleanMd(labels[i]).toUpperCase(), {
1782
- x: x + 0.30, y: y0 + h - 0.5, w: cardW - 0.56, h: 0.32,
1783
- fontFace: P.bodyFont, fontSize: 9.5, bold: false,
1784
- color: fill ? hex(P.emphasisTextColor) : hex(P.ink_muted), align: "left", valign: "middle", charSpacing: 0.4, margin: 0,
1785
- });
1786
- }
1787
- }
1788
-
1789
- /** A pipe table reads as KPI in two shapes:
1790
- * - TALL: 2 cols of value|label pairs, ≤4 pairs, value cell short/metric-like.
1791
- * - WIDE: 3–4 cols, a single data row of short metric values (labels = header).
1792
- * Anything else (multi-row, long cells) is a real data table. */
1793
-
1794
-
1795
- /** Normalize a kpi-table into [{value,label}] cards for both KPI shapes. */
1796
- /**
1797
- * Is there room to draw this as KPI badges?
1798
- *
1799
- * A badge stacks a value over a label and reads as one glance, which stops
1800
- * working the moment the card is narrower than its own words: in a column half
1801
- * the slide wide, four badges leave 1.4in each and a label like "영업이익" wraps
1802
- * to one character per line. Below the floor the same data is drawn as a table,
1803
- * which narrows gracefully.
1804
- */
1805
- function kpiFits(content, widthIn) {
1806
- const cards = kpiCards(content).length;
1807
- return cards > 0 && (widthIn || 0) / cards >= KPI_MIN_CARD_WIDTH_IN;
1808
- }
1809
-
1810
- function kpiCards(content) {
1811
- const headers = content.headers || [];
1812
- const rows = content.rows || [];
1813
- if (headers.length === 2) {
1814
- return [headers, ...rows].slice(0, 4).map((p) => ({ value: p[0], label: p[1] }));
1815
- }
1816
- const row = rows[0] || [];
1817
- // A second body row is each figure's basis or comparison ("전년 대비 +12%", "목표 50%").
1818
- const basis = rows[1] || [];
1819
- return headers.slice(0, 6).map((lab, i) => ({ value: row[i] != null ? row[i] : "", label: lab, ...(basis[i] ? { note: basis[i] } : {}) }));
1820
- }
1821
-
1822
- /** Azure region dispatch. Returns true if it fully handled the region.
1823
- * opts.hasImage = slide also has a populated image region (→ never card the body). */
1824
- function renderAzureRegion(slide, layout, name, region, fontRoles, P, opts = {}) {
1825
- const content = region.content;
1826
- if (content == null) return false;
1827
- if (name === "main_box" && typeof content === "string" && content.trim()) {
1828
- // Below a card row that came out shorter than its region, the takeaway follows
1829
- // the cards instead of leaving a band of empty slide between them.
1830
- const follow = opts.flow && opts.flow.cardsBottom != null ? opts.flow.cardsBottom + 0.32 : null;
1831
- renderAzureCallout(slide, content, follow != null && follow < (region.y || 5.8) ? { ...region, y: follow } : region, P);
1832
- return true;
1833
- }
1834
- if (layout === "cover" && name === "title" && typeof content === "string" && content.includes("**")) {
1835
- const role = fontRoles.cover_title || {};
1836
- slide.addText(azureTitleRuns(content, P), {
1837
- objectName: "title@legacy",
1838
- x: region.x || 0, y: region.y || 0, w: region.w || 8, h: region.h || 1,
1839
- fontFace: role.font || P.titleFont, fontSize: role.size || 48, bold: role.bold !== false,
1840
- align: role.align || "left", valign: "middle",
1841
- charSpacing: role.charSpacing != null ? role.charSpacing : -1, lineSpacingMultiple: role.lineSpacing || 1.08,
1842
- });
1843
- return true;
1844
- }
1845
- if (typeof content === "object") {
1846
- if (content.type === "body" && (layout === "content" || layout === "main")) {
1847
- // Cards ONLY for section-structured bodies (≥2 ### sections) with no image.
1848
- // Prose bullets, or any body sharing the slide with an image, render as a
1849
- // normal text column (fall through to addRegion) so nothing is cramped.
1850
- const sections = (content.items || []).filter((i) => i.type === "section");
1851
- if (sections.length >= 2 && !opts.hasImage) {
1852
- const bottom = renderAzureCards(slide, content.items, region, P);
1853
- if (opts.flow) opts.flow.cardsBottom = bottom;
1854
- return true;
1855
- }
1856
- return false;
1857
- }
1858
- if (content.type === "kpi-table") {
1859
- if (content.chart) {
1860
- // Pie and doughnut slices carry white labels, so they use the darker tones only.
1861
- const round = ["pie", "doughnut"].includes(content.chart.type);
1862
- const series = round ? [P.primary, P.primary_deep, P.azure, P.ink_muted] : [P.primary, P.azure_soft, P.primary_deep, P.ink_muted];
1863
- renderChart(slide, content, region, { ...P, series }, opts.bottom);
1864
- }
1865
- else if (looksLikeKpi(content) && kpiFits(content, region.w)) {
1866
- renderAzureKpi(slide, content, region, P);
1867
- }
1868
- else renderAzureTable(slide, content, region, P, opts.bottom);
1869
- return true;
1870
- }
1871
- if (content.type === "summary-group") {
1872
- const idx = name === "group_bottom" ? 1 : 0;
1873
- renderAzureGroupCard(slide, content, region, P, idx, opts.groupHeight);
1874
- return true;
1875
- }
1876
- }
1877
- return false;
1878
- }
1879
-
1880
- // ── Placements (author-positioned boxes and shapes) ─────────────────────────
1881
-
1882
- /** Region name a placed block should be rendered as. */
1883
- function partRegionName(blockType) {
1884
- switch (blockType) {
1885
- case "title": return "title";
1886
- case "kpi-table": return "table";
1887
- case "image": return "image";
1888
- case "figure-caption": return "figure_caption";
1889
- case "table-caption": return "table_caption";
1890
- default: return "body";
1891
- }
1892
- }
1893
-
1894
- /** font_role a placed block falls back to when its box names none. */
1895
- function defaultPartRole(blockType) {
1896
- switch (blockType) {
1897
- case "title": return "section_title";
1898
- case "figure-caption": return "figure_caption";
1899
- case "table-caption": return "table_caption";
1900
- case "notes": return "disclaimer";
1901
- default: return "body_text";
1902
- }
1903
- }
1904
-
1905
- function estimatePartHeight(content, widthIn, role) {
1906
- const size = role.size || 13;
1907
- const spacing = role.lineSpacing || 1.3;
1908
- if (typeof content === "string") {
1909
- return estimateTextHeight(content, widthIn, size, spacing) + 0.08;
1910
- }
1911
- if (content && content.type === "body") {
1912
- const text = (content.items || []).map((i) => i.heading || i.text || "").join("\n");
1913
- return estimateTextHeight(text, widthIn, size, spacing) + 0.12;
1914
- }
1915
- return 0.5;
1916
- }
1917
-
1918
- /**
1919
- * Turn a shape placement into the decoration element addDecorations reads.
1920
- *
1921
- * A rule is drawn as a zero-height rect whose stroke is the visible mark, so it
1922
- * reads its colour and thickness from `color`/`width` while every other
1923
- * primitive reads `line`/`line_width`. Authors write one spelling — `line=` and
1924
- * `lw=` — and the difference is absorbed here.
1925
- */
1926
- function shapeToDecoration(placement, sourceDir) {
1927
- const isRule = placement.shape === "line";
1928
- const element = {
1929
- type: placement.shape,
1930
- x: placement.x, y: placement.y, w: placement.w, h: placement.h,
1931
- };
1932
- for (const [key, value] of Object.entries(placement)) {
1933
- if (["kind", "z", "shape", "x", "y", "w", "h"].includes(key)) continue;
1934
- if (key === "lw") element[isRule ? "width" : "line_width"] = value;
1935
- else if (key === "line" && isRule) element.color = value;
1936
- else if (key === "src") element.assetPath = resolveImagePath(value, sourceDir);
1937
- else element[key] = value;
1938
- }
1939
- return element;
1940
- }
1941
-
1942
- /**
1943
- * Render one placed box by flowing its blocks down the box.
1944
- *
1945
- * Every block but the last takes the height its content needs; the last takes
1946
- * whatever is left, so a heading followed by body fills the box rather than
1947
- * floating in its top edge. Nothing here consults the auto-nudge rules: a box
1948
- * lands exactly where the author put it, and the QA gate reports the consequence.
1949
- */
1950
- function renderPlacementBox(slide, placement, ctx) {
1951
- const blocks = placement.blocks || [];
1952
- if (blocks.length === 0) return;
1953
-
1954
- // Columns replace the body region, so they render in the template's own idiom.
1955
- // An explicit box is literal: the author asked for this content, here.
1956
- const useRich = ctx.rich && placement.z === "content";
1957
- const bottom = placement.y + (placement.h != null ? placement.h : 1.0);
1958
- let y = placement.y;
1959
-
1960
- blocks.forEach((block, i) => {
1961
- const remaining = Math.max(0.2, bottom - y);
1962
- const roleName = placement.role || defaultPartRole(block.type);
1963
- const role = ctx.fontRoles[roleName] || {};
1964
- const content = extractContent(block);
1965
- const isLast = i === blocks.length - 1;
1966
- const h = isLast
1967
- ? remaining
1968
- : Math.min(remaining, Math.max(0.2, estimatePartHeight(content, placement.w, role)));
1969
-
1970
- const region = {
1971
- x: placement.x, y, w: placement.w, h,
1972
- content,
1973
- font_role: roleName,
1974
- originX: placement.x,
1975
- ...(placement.align ? { align: placement.align } : {}),
1976
- ...(placement.valign ? { valign: placement.valign } : {}),
1977
- };
1978
-
1979
- const name = partRegionName(block.type);
1980
- const rendered = useRich &&
1981
- renderAzureRegion(slide, ctx.layout, name, region, ctx.fontRoles, ctx.P, {});
1982
- if (!rendered) {
1983
- addRegion(slide, name, region, ctx.fontRoles, ctx.pageH, y + h, false, ctx.sourceDir);
1984
- }
1985
- y += h;
1986
- });
1987
- }
1988
-
1989
- function renderPlacements(slide, placements, band, ctx) {
1990
- for (const placement of placements) {
1991
- if (placement.z !== band) continue;
1992
- if (placement.kind === "shape") {
1993
- addDecorations(slide, [shapeToDecoration(placement, ctx.sourceDir)], ctx.fontRoles);
1994
- } else {
1995
- renderPlacementBox(slide, placement, ctx);
1996
- }
1997
- }
1998
- }
1999
-
2000
- // ── Main render function ────────────────────────────────────────────────────
2001
-
2002
- /**
2003
- * Render the full resolved spec to a PPTX file.
2004
- *
2005
- * @param {object} resolved - Resolved spec from layout-resolver
2006
- * @param {object} templateObj - Template object from template-registry
2007
- * @param {string} outputPath - Where to write the .pptx file
2008
- * @returns {Promise<string>} Resolves with the output path
2009
- */
2010
- /**
2011
- * The deck-wide `notice:` (for example "예시 데이터 — 실제 수치로 바꿔 주세요") as a
2012
- * coloured tag at the bottom left of every slide, cover included, so a sample or
2013
- * draft deck can never be mistaken for a sourced one. A 9 pt grey footnote was easy
2014
- * to miss; the tag keeps its own fill and text colour (template palette
2015
- * `notice_fill` / `notice_ink`, default a light red with dark red text, 6:1).
2016
- */
2017
- const NOTICE_FILL = "FDECEA";
2018
- const NOTICE_INK = "A21B12";
2019
- let NOTICE = { ...LEGACY_TOKENS.notice };
2020
-
2021
- function addNotice(slide, text, pageW, pageH, pal) {
2022
- const label = cleanMd(text);
2023
- const fontSize = NOTICE.size;
2024
- const textW = (weightedTextLength(label) * fontSize * 0.98) / 72;
2025
- const w = Math.min(pageW * 0.55, textW + 0.46);
2026
- const h = NOTICE.h;
2027
- const x = NOTICE.x, y = pageH - h - NOTICE.bottom;
2028
- const fill = ((pal && pal.notice_fill) || NOTICE_FILL).replace("#", "");
2029
- const ink = ((pal && pal.notice_ink) || NOTICE_INK).replace("#", "");
2030
- slide.addShape("roundRect", { x, y, w, h, rectRadius: 0.15, fill: { color: fill }, line: { type: "none" }, objectName: "lit-notice tag" });
2031
- slide.addText(label, {
2032
- objectName: "lit-notice text",
2033
- x: x + 0.16, y, w: w - 0.32, h,
2034
- fontFace: FONT_BOLD, fontSize, bold: true, color: ink,
2035
- align: "left", valign: "middle", margin: 0, fit: "shrink",
2036
- });
2037
- }
2038
-
2039
- /**
2040
- * Give the first object drawn on a slide that has no name of its own the name `family@<id>`, so a
2041
- * checker can read the layout family the source asked for and compare it with what it measures.
2042
- */
2043
- function stampFamily(slide, family) {
2044
- const at = { addText: 1, addShape: 1, addImage: 0, addTable: 1, addChart: 2 };
2045
- const originals = {};
2046
- let done = false;
2047
- const restore = () => { for (const m of Object.keys(originals)) slide[m] = originals[m]; };
2048
- for (const [method, index] of Object.entries(at)) {
2049
- originals[method] = slide[method];
2050
- slide[method] = (...args) => {
2051
- if (!(args[index] && args[index].objectName)) {
2052
- restore();
2053
- done = true;
2054
- args[index] = { ...(args[index] || {}), objectName: `family@${family}` };
2055
- }
2056
- return originals[method].apply(slide, args);
2057
- };
2058
- }
2059
- /** Whether a shape took the name; unwraps the slide either way. */
2060
- return () => { restore(); return done; };
2061
- }
2062
-
2063
- async function render(resolved, templateObj, outputPath) {
2064
- const pack = (templateObj && templateObj.pack) || null;
2065
- if (pack && !pack.legacy) return require("./render-pack").renderPack(resolved, templateObj, outputPath);
2066
- applyTokens(pack && pack.tokens);
2067
- const fontRoles = buildFontRoles(templateObj);
2068
-
2069
- // Brand-neutralize: derive font/spacing/accent from the template, falling back
2070
- // to Pretendard when a template names no family.
2071
- const tpl = (templateObj && templateObj.template) || {};
2072
- const tplFonts = tpl.fonts || {};
2073
- const tplType = tpl.global_typography || {};
2074
- FONT_BOLD = tplFonts.title || "Pretendard";
2075
- FONT_MEDIUM = tplFonts.body || "Pretendard";
2076
- FONT_LIGHT = tplFonts.light || "Pretendard";
2077
- CHAR_SPACING = tplType.char_spacing != null ? tplType.char_spacing : -0.7;
2078
- SEC_HEADER_FONT = FONT_BOLD;
2079
- TOC_FONT = FONT_BOLD;
2080
- BULLET_COLOR = (tplType.bullet_color || "1D4ED8").replace("#", "");
2081
- SECTION_ACCENT = (tplType.section_accent || "1D4ED8").replace("#", "");
2082
-
2083
- // Rich card/KPI/divider rendering — palette-driven, for templates with the rich-blocks
2084
- // capability. Tokens come from the template palette.
2085
- const rich = Boolean(pack && (pack.capabilities || []).includes("rich-blocks"));
2086
- const pal = tpl.palette || {};
2087
- PLAIN_CHART_STYLE = {
2088
- series: [pal.ink || "#3C3836", pal.accent_green_2 || pal.primary || "#689D6A", pal.accent_amber || "#D79921", pal.accent_purple || "#B16286"],
2089
- ink: pal.ink || "#3C3836", ink_muted: pal.ink_muted || "#6E6256", line: pal.line || "#CBC2B5",
2090
- titleFont: FONT_BOLD, bodyFont: FONT_MEDIUM,
2091
- };
2092
- const P = {
2093
- titleFont: FONT_BOLD, bodyFont: FONT_MEDIUM,
2094
- primary: pal.primary || "#1D4ED8", primary_deep: pal.primary_deep || "#0B2E6F",
2095
- ink: pal.ink || "#0E1B2C", ink_muted: pal.ink_muted || "#51607A",
2096
- tint: pal.tint || "#EEF4FF", line: pal.line || "#D5DEEC", azure_soft: pal.azure_soft || "#93C5FD", azure: pal.azure || "#3B82F6",
2097
- emphasisTextColor: pal.emphasis_text || "#C9D8F5",
2098
- };
2099
-
2100
- const dims =
2101
- (templateObj && templateObj.template && templateObj.template.dimensions) || {};
2102
- const pageW = dims.width || 10.833;
2103
- const pageH = dims.height || 7.5;
2104
-
2105
- // Loaded here, not at module load, so AST-only and --list-* runs never install.
2106
- const PptxGenJS = require("pptxgenjs");
2107
- const pptx = new PptxGenJS();
2108
- pptx.defineLayout({ name: "A4_LANDSCAPE", width: pageW, height: pageH });
2109
- pptx.layout = "A4_LANDSCAPE";
2110
-
2111
- const title = (resolved.deck && resolved.deck.title) || "Presentation";
2112
- pptx.title = title;
2113
- const sourceDir = resolved.sourceDir;
2114
- const cramStress = isCramStressDeck(resolved);
2115
- const deckMeta = (resolved.deck && resolved.deck.metadata) || {};
2116
- const notice = typeof deckMeta.notice === "string" && deckMeta.notice.trim() ? deckMeta.notice.trim() : null;
2117
-
2118
- for (const slideSpec of resolved.slides || []) {
2119
- const slide = pptx.addSlide();
2120
- stampFamily(slide, slideSpec.family || slideSpec.layout);
2121
-
2122
- // Draw order is a band at a time: template decorations, then anything the
2123
- // author put under the content, then the template's regions, then the
2124
- // columns that replaced the body, then anything sitting on top.
2125
- addDecorations(slide, slideSpec.decorations, fontRoles);
2126
-
2127
- const placements = slideSpec.placements || [];
2128
- const placementCtx = {
2129
- rich, P, fontRoles, pageH, sourceDir,
2130
- layout: slideSpec.layout,
2131
- };
2132
- renderPlacements(slide, placements, "under", placementCtx);
2133
-
2134
- const regions = adjustedRegionsForImageColumn(slideSpec.regions || {});
2135
- if (cramStress && ["content", "main"].includes(slideSpec.layout) && regions.body) {
2136
- renderCramStressSlide(slide, { ...slideSpec, regions }, pageW, pageH, sourceDir);
2137
- continue;
2138
- }
2139
-
2140
- // Calculate bottom boundary for regions without explicit h
2141
- const bottomBoundary = {};
2142
- for (const [name, region] of Object.entries(regions)) {
2143
- let bottom = pageH - 0.5;
2144
- for (const [otherName, otherRegion] of Object.entries(regions)) {
2145
- if (otherName === name || otherName === "title") continue;
2146
- if (name === "body" && ["image", "figure_caption"].includes(otherName)) continue;
2147
- if (otherRegion.y && otherRegion.y > (region.y || 0) && otherRegion.h) {
2148
- const candidateBottom = otherRegion.y - 0.15;
2149
- if (candidateBottom < bottom) bottom = candidateBottom;
2150
- }
2151
- }
2152
- bottomBoundary[name] = bottom;
2153
- }
2154
-
2155
- const tocSlide = isTocSlide(regions);
2156
- const hasImage = Object.entries(regions).some(([n, r]) =>
2157
- (n === "image" || n === "figure") && r.content &&
2158
- (r.content.type === "image" || r.content.type === "image-grid"));
2159
-
2160
- // Summary groups sit side by side, so they share the height the fuller one needs.
2161
- const groups = Object.values(regions).filter((r) => r.content && r.content.type === "summary-group");
2162
- const groupHeight = rich && groups.length
2163
- ? Math.max(...groups.map((r) => groupCard(r.content, r.w || 5.6, r.h || 2.4, P).need))
2164
- : null;
2165
-
2166
- const flow = {};
2167
- for (const [name, region] of Object.entries(regions)) {
2168
- if (rich && renderAzureRegion(slide, slideSpec.layout, name, region, fontRoles, P, { hasImage, bottom: bottomBoundary[name], groupHeight, flow })) continue;
2169
- addRegion(slide, name, region, fontRoles, pageH, bottomBoundary[name], tocSlide, sourceDir);
2170
- }
2171
-
2172
- renderPlacements(slide, placements, "content", placementCtx);
2173
- renderPlacements(slide, placements, "over", placementCtx);
2174
- if (notice) addNotice(slide, notice, pageW, pageH, pal);
2175
- }
2176
-
2177
- await writeTagged(pptx, outputPath);
2178
- return outputPath;
2179
- }
2180
-
2181
- const HANGUL = /[\u1100-\u11FF\u3130-\u318F\uAC00-\uD7A3]/u;
2182
-
2183
- /**
2184
- * Korean runs carry their language. pptxgenjs writes lang="en-US" on every run, and a renderer then sets
2185
- * Hangul by Latin rules (LibreOffice under a non-Korean system locale puts space between Hangul and
2186
- * digits, so "주 2일" reads "주 2 일"). A run holding Hangul becomes ko-KR with en-US as its alternate
2187
- * language, and a chart holding Hangul names ko-KR for its text; Latin-only runs keep en-US.
2188
- */
2189
- function tagLanguages(name, xml) {
2190
- if (/^ppt\/slides\/slide\d+\.xml$/u.test(name)) {
2191
- return xml.replace(/<a:r><a:rPr\b([^>]*?)(\/?>)([\s\S]*?)<a:t>([^<]*)<\/a:t><\/a:r>/gu, (m, attrs, close, mid, text) => {
2192
- if (!HANGUL.test(text)) return m;
2193
- const rest = attrs.replace(/\s(?:lang|altLang)="[^"]*"/gu, "");
2194
- return `<a:r><a:rPr lang="ko-KR" altLang="en-US"${rest}${close}${mid}<a:t>${text}</a:t></a:r>`;
2195
- });
2196
- }
2197
- if (/^ppt\/charts\/chart\d+\.xml$/u.test(name) && HANGUL.test(xml)) {
2198
- let out = xml.replace(/<a:defRPr\b(?![^>]*\blang=)/gu, '<a:defRPr lang="ko-KR" altLang="en-US"')
2199
- .replace(/<a:endParaRPr lang="en-US"/gu, '<a:endParaRPr lang="ko-KR" altLang="en-US"');
2200
- if (!/<c:lang\b/u.test(out)) out = out.replace(/(<c:date1904 val="[01]"\/>)/u, '$1<c:lang val="ko-KR"/>');
2201
- return out;
2202
- }
2203
- return xml;
2204
- }
2205
-
2206
- /** Write a deck with its Korean runs and charts tagged (tagLanguages); `edit` may rewrite a part first. */
2207
- async function writeTagged(pptx, outputPath, edit = (name, xml) => xml) {
2208
- const JSZip = require("jszip");
2209
- const zip = await JSZip.loadAsync(await pptx.write({ outputType: "nodebuffer" }));
2210
- for (const name of Object.keys(zip.files).filter((n) => /^ppt\/(slides\/slide|charts\/chart|theme\/theme)\d+\.xml$/u.test(n))) {
2211
- const xml = await zip.file(name).async("string");
2212
- zip.file(name, tagLanguages(name, edit(name, xml)));
2213
- }
2214
- fs.writeFileSync(outputPath, await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" }));
2215
- }
2216
-
2217
- // ── Tonality packs ──────────────────────────────────────────────────────────
2218
- //
2219
- // A pack deck is drawn by render-pack.js. It reuses the helpers below, and the font globals the
2220
- // free placement boxes read are switched to the pack's faces before it draws.
2221
-
2222
- function usePackFonts(titleFont, bodyFont, tokens) {
2223
- FONT_BOLD = titleFont;
2224
- FONT_MEDIUM = bodyFont;
2225
- FONT_LIGHT = bodyFont;
2226
- CHAR_SPACING = 0;
2227
- applyTokens(tokens);
2228
- }
2229
-
2230
- module.exports = {
2231
- render,
2232
- shared: {
2233
- hex, cleanMd, mdRuns, linkedTableCell, resolveImagePath, readImageSize, containBox,
2234
- kpiCards, numericColumns, proportionalWidths, renderChart, stampFamily, renderPlacements, usePackFonts, writeTagged,
2235
- },
2236
- };