wand-kit 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/.gitkeep +0 -0
  2. package/PROMPT.md +255 -0
  3. package/SKILL.md +117 -0
  4. package/START-HERE.md +16 -0
  5. package/brands/current/assets/bg-cover-dark.png +0 -0
  6. package/brands/current/assets/bg-overlay-dark.png +0 -0
  7. package/brands/current/assets/bg-statement-dark.png +0 -0
  8. package/brands/current/assets/closing-art.png +0 -0
  9. package/brands/current/assets/cover-art.png +0 -0
  10. package/brands/current/assets/divider-art.png +0 -0
  11. package/brands/current/assets/glyphs/README.md +29 -0
  12. package/brands/current/assets/glyphs/activity.svg +15 -0
  13. package/brands/current/assets/glyphs/atom.svg +17 -0
  14. package/brands/current/assets/glyphs/badge-check.svg +16 -0
  15. package/brands/current/assets/glyphs/bell.svg +16 -0
  16. package/brands/current/assets/glyphs/blocks.svg +16 -0
  17. package/brands/current/assets/glyphs/book-open.svg +16 -0
  18. package/brands/current/assets/glyphs/bot.svg +20 -0
  19. package/brands/current/assets/glyphs/boxes.svg +26 -0
  20. package/brands/current/assets/glyphs/brain.svg +23 -0
  21. package/brands/current/assets/glyphs/briefcase.svg +16 -0
  22. package/brands/current/assets/glyphs/building-2.svg +21 -0
  23. package/brands/current/assets/glyphs/calendar.svg +18 -0
  24. package/brands/current/assets/glyphs/chart-column.svg +18 -0
  25. package/brands/current/assets/glyphs/chart-line.svg +16 -0
  26. package/brands/current/assets/glyphs/chart-pie.svg +16 -0
  27. package/brands/current/assets/glyphs/circle-check.svg +16 -0
  28. package/brands/current/assets/glyphs/clipboard-check.svg +17 -0
  29. package/brands/current/assets/glyphs/clock.svg +16 -0
  30. package/brands/current/assets/glyphs/cloud.svg +15 -0
  31. package/brands/current/assets/glyphs/code.svg +15 -0
  32. package/brands/current/assets/glyphs/coins.svg +18 -0
  33. package/brands/current/assets/glyphs/compass.svg +16 -0
  34. package/brands/current/assets/glyphs/cpu.svg +24 -0
  35. package/brands/current/assets/glyphs/database.svg +17 -0
  36. package/brands/current/assets/glyphs/eye.svg +16 -0
  37. package/brands/current/assets/glyphs/file-check.svg +17 -0
  38. package/brands/current/assets/glyphs/filter.svg +15 -0
  39. package/brands/current/assets/glyphs/fingerprint.svg +23 -0
  40. package/brands/current/assets/glyphs/flag.svg +16 -0
  41. package/brands/current/assets/glyphs/gauge.svg +16 -0
  42. package/brands/current/assets/glyphs/gavel.svg +19 -0
  43. package/brands/current/assets/glyphs/git-branch.svg +18 -0
  44. package/brands/current/assets/glyphs/globe.svg +17 -0
  45. package/brands/current/assets/glyphs/graduation-cap.svg +17 -0
  46. package/brands/current/assets/glyphs/handshake.svg +19 -0
  47. package/brands/current/assets/glyphs/hexagon.svg +15 -0
  48. package/brands/current/assets/glyphs/index.json +177 -0
  49. package/brands/current/assets/glyphs/infinity.svg +15 -0
  50. package/brands/current/assets/glyphs/key.svg +17 -0
  51. package/brands/current/assets/glyphs/landmark.svg +20 -0
  52. package/brands/current/assets/glyphs/layers.svg +17 -0
  53. package/brands/current/assets/glyphs/lightbulb.svg +17 -0
  54. package/brands/current/assets/glyphs/link.svg +16 -0
  55. package/brands/current/assets/glyphs/list-checks.svg +19 -0
  56. package/brands/current/assets/glyphs/lock.svg +16 -0
  57. package/brands/current/assets/glyphs/map.svg +17 -0
  58. package/brands/current/assets/glyphs/megaphone.svg +16 -0
  59. package/brands/current/assets/glyphs/message-square.svg +15 -0
  60. package/brands/current/assets/glyphs/milestone.svg +17 -0
  61. package/brands/current/assets/glyphs/network.svg +19 -0
  62. package/brands/current/assets/glyphs/orbit.svg +19 -0
  63. package/brands/current/assets/glyphs/package.svg +18 -0
  64. package/brands/current/assets/glyphs/plug.svg +18 -0
  65. package/brands/current/assets/glyphs/puzzle.svg +15 -0
  66. package/brands/current/assets/glyphs/radar.svg +22 -0
  67. package/brands/current/assets/glyphs/receipt.svg +17 -0
  68. package/brands/current/assets/glyphs/refresh-cw.svg +18 -0
  69. package/brands/current/assets/glyphs/rocket.svg +18 -0
  70. package/brands/current/assets/glyphs/route.svg +17 -0
  71. package/brands/current/assets/glyphs/scale.svg +19 -0
  72. package/brands/current/assets/glyphs/search.svg +16 -0
  73. package/brands/current/assets/glyphs/server.svg +18 -0
  74. package/brands/current/assets/glyphs/settings.svg +16 -0
  75. package/brands/current/assets/glyphs/share-2.svg +19 -0
  76. package/brands/current/assets/glyphs/shield-check.svg +16 -0
  77. package/brands/current/assets/glyphs/shuffle.svg +19 -0
  78. package/brands/current/assets/glyphs/sparkles.svg +19 -0
  79. package/brands/current/assets/glyphs/square-stack.svg +17 -0
  80. package/brands/current/assets/glyphs/target.svg +17 -0
  81. package/brands/current/assets/glyphs/trending-up.svg +16 -0
  82. package/brands/current/assets/glyphs/triangle-alert.svg +17 -0
  83. package/brands/current/assets/glyphs/trophy.svg +20 -0
  84. package/brands/current/assets/glyphs/users.svg +18 -0
  85. package/brands/current/assets/glyphs/wallet.svg +16 -0
  86. package/brands/current/assets/glyphs/wand-sparkles.svg +22 -0
  87. package/brands/current/assets/glyphs/workflow.svg +17 -0
  88. package/brands/current/assets/glyphs/zap.svg +15 -0
  89. package/brands/current/assets/mark-dark.png +0 -0
  90. package/brands/current/assets/mark-white.png +0 -0
  91. package/brands/current/assets/orb-cover.png +0 -0
  92. package/brands/current/assets/orb-glow.png +0 -0
  93. package/brands/current/assets/wordmark-dark.png +0 -0
  94. package/brands/current/assets/wordmark-white.png +0 -0
  95. package/brands/current/fonts/Geist-Bold.ttf +0 -0
  96. package/brands/current/fonts/Geist-BoldItalic.ttf +0 -0
  97. package/brands/current/fonts/Geist-ExtraBold.ttf +0 -0
  98. package/brands/current/fonts/Geist-ExtraBoldItalic.ttf +0 -0
  99. package/brands/current/fonts/Geist-Italic.ttf +0 -0
  100. package/brands/current/fonts/Geist-Light.ttf +0 -0
  101. package/brands/current/fonts/Geist-LightItalic.ttf +0 -0
  102. package/brands/current/fonts/Geist-Medium.ttf +0 -0
  103. package/brands/current/fonts/Geist-MediumItalic.ttf +0 -0
  104. package/brands/current/fonts/Geist-Regular.ttf +0 -0
  105. package/brands/current/fonts/Geist-SemiBold.ttf +0 -0
  106. package/brands/current/fonts/Geist-SemiBoldItalic.ttf +0 -0
  107. package/brands/current/lib/glyphs.js +87 -0
  108. package/brands/current/lib/measure.js +115 -0
  109. package/brands/current/lib/pdf.js +603 -0
  110. package/brands/current/lib/ttf.py +167 -0
  111. package/brands/current/lib/wandxml.py +37 -0
  112. package/brands/current/tokens.json +206 -0
  113. package/brands/next/assets/closing-art.png +0 -0
  114. package/brands/next/assets/cover-art.png +0 -0
  115. package/brands/next/assets/divider-art.png +0 -0
  116. package/brands/next/assets/glyphs/README.md +29 -0
  117. package/brands/next/assets/glyphs/activity.svg +15 -0
  118. package/brands/next/assets/glyphs/atom.svg +17 -0
  119. package/brands/next/assets/glyphs/badge-check.svg +16 -0
  120. package/brands/next/assets/glyphs/bell.svg +16 -0
  121. package/brands/next/assets/glyphs/blocks.svg +16 -0
  122. package/brands/next/assets/glyphs/book-open.svg +16 -0
  123. package/brands/next/assets/glyphs/bot.svg +20 -0
  124. package/brands/next/assets/glyphs/boxes.svg +26 -0
  125. package/brands/next/assets/glyphs/brain.svg +23 -0
  126. package/brands/next/assets/glyphs/briefcase.svg +16 -0
  127. package/brands/next/assets/glyphs/building-2.svg +21 -0
  128. package/brands/next/assets/glyphs/calendar.svg +18 -0
  129. package/brands/next/assets/glyphs/chart-column.svg +18 -0
  130. package/brands/next/assets/glyphs/chart-line.svg +16 -0
  131. package/brands/next/assets/glyphs/chart-pie.svg +16 -0
  132. package/brands/next/assets/glyphs/circle-check.svg +16 -0
  133. package/brands/next/assets/glyphs/clipboard-check.svg +17 -0
  134. package/brands/next/assets/glyphs/clock.svg +16 -0
  135. package/brands/next/assets/glyphs/cloud.svg +15 -0
  136. package/brands/next/assets/glyphs/code.svg +15 -0
  137. package/brands/next/assets/glyphs/coins.svg +18 -0
  138. package/brands/next/assets/glyphs/compass.svg +16 -0
  139. package/brands/next/assets/glyphs/cpu.svg +24 -0
  140. package/brands/next/assets/glyphs/database.svg +17 -0
  141. package/brands/next/assets/glyphs/eye.svg +16 -0
  142. package/brands/next/assets/glyphs/file-check.svg +17 -0
  143. package/brands/next/assets/glyphs/filter.svg +15 -0
  144. package/brands/next/assets/glyphs/fingerprint.svg +23 -0
  145. package/brands/next/assets/glyphs/flag.svg +16 -0
  146. package/brands/next/assets/glyphs/gauge.svg +16 -0
  147. package/brands/next/assets/glyphs/gavel.svg +19 -0
  148. package/brands/next/assets/glyphs/git-branch.svg +18 -0
  149. package/brands/next/assets/glyphs/globe.svg +17 -0
  150. package/brands/next/assets/glyphs/graduation-cap.svg +17 -0
  151. package/brands/next/assets/glyphs/handshake.svg +19 -0
  152. package/brands/next/assets/glyphs/hexagon.svg +15 -0
  153. package/brands/next/assets/glyphs/index.json +177 -0
  154. package/brands/next/assets/glyphs/infinity.svg +15 -0
  155. package/brands/next/assets/glyphs/key.svg +17 -0
  156. package/brands/next/assets/glyphs/landmark.svg +20 -0
  157. package/brands/next/assets/glyphs/layers.svg +17 -0
  158. package/brands/next/assets/glyphs/lightbulb.svg +17 -0
  159. package/brands/next/assets/glyphs/link.svg +16 -0
  160. package/brands/next/assets/glyphs/list-checks.svg +19 -0
  161. package/brands/next/assets/glyphs/lock.svg +16 -0
  162. package/brands/next/assets/glyphs/map.svg +17 -0
  163. package/brands/next/assets/glyphs/megaphone.svg +16 -0
  164. package/brands/next/assets/glyphs/message-square.svg +15 -0
  165. package/brands/next/assets/glyphs/milestone.svg +17 -0
  166. package/brands/next/assets/glyphs/network.svg +19 -0
  167. package/brands/next/assets/glyphs/orbit.svg +19 -0
  168. package/brands/next/assets/glyphs/package.svg +18 -0
  169. package/brands/next/assets/glyphs/plug.svg +18 -0
  170. package/brands/next/assets/glyphs/puzzle.svg +15 -0
  171. package/brands/next/assets/glyphs/radar.svg +22 -0
  172. package/brands/next/assets/glyphs/receipt.svg +17 -0
  173. package/brands/next/assets/glyphs/refresh-cw.svg +18 -0
  174. package/brands/next/assets/glyphs/rocket.svg +18 -0
  175. package/brands/next/assets/glyphs/route.svg +17 -0
  176. package/brands/next/assets/glyphs/scale.svg +19 -0
  177. package/brands/next/assets/glyphs/search.svg +16 -0
  178. package/brands/next/assets/glyphs/server.svg +18 -0
  179. package/brands/next/assets/glyphs/settings.svg +16 -0
  180. package/brands/next/assets/glyphs/share-2.svg +19 -0
  181. package/brands/next/assets/glyphs/shield-check.svg +16 -0
  182. package/brands/next/assets/glyphs/shuffle.svg +19 -0
  183. package/brands/next/assets/glyphs/sparkles.svg +19 -0
  184. package/brands/next/assets/glyphs/square-stack.svg +17 -0
  185. package/brands/next/assets/glyphs/target.svg +17 -0
  186. package/brands/next/assets/glyphs/trending-up.svg +16 -0
  187. package/brands/next/assets/glyphs/triangle-alert.svg +17 -0
  188. package/brands/next/assets/glyphs/trophy.svg +20 -0
  189. package/brands/next/assets/glyphs/users.svg +18 -0
  190. package/brands/next/assets/glyphs/wallet.svg +16 -0
  191. package/brands/next/assets/glyphs/wand-sparkles.svg +22 -0
  192. package/brands/next/assets/glyphs/workflow.svg +17 -0
  193. package/brands/next/assets/glyphs/zap.svg +15 -0
  194. package/brands/next/assets/layers-dark.png +0 -0
  195. package/brands/next/assets/layers-light.png +0 -0
  196. package/brands/next/assets/ornaments/burst-dark.png +0 -0
  197. package/brands/next/assets/ornaments/burst-light.png +0 -0
  198. package/brands/next/assets/ornaments/chain-dark.png +0 -0
  199. package/brands/next/assets/ornaments/chain-light.png +0 -0
  200. package/brands/next/assets/ornaments/coil-dark.png +0 -0
  201. package/brands/next/assets/ornaments/coil-light.png +0 -0
  202. package/brands/next/assets/ornaments/cone-dark.png +0 -0
  203. package/brands/next/assets/ornaments/cone-light.png +0 -0
  204. package/brands/next/assets/ornaments/diamond-dark.png +0 -0
  205. package/brands/next/assets/ornaments/diamond-light.png +0 -0
  206. package/brands/next/assets/ornaments/fan-dark.png +0 -0
  207. package/brands/next/assets/ornaments/fan-light.png +0 -0
  208. package/brands/next/assets/ornaments/globe-dark.png +0 -0
  209. package/brands/next/assets/ornaments/globe-light.png +0 -0
  210. package/brands/next/assets/ornaments/orbit-dark.png +0 -0
  211. package/brands/next/assets/ornaments/orbit-light.png +0 -0
  212. package/brands/next/assets/ornaments/rings-dark.png +0 -0
  213. package/brands/next/assets/ornaments/rings-light.png +0 -0
  214. package/brands/next/assets/ornaments/sphere-dark.png +0 -0
  215. package/brands/next/assets/ornaments/sphere-light.png +0 -0
  216. package/brands/next/assets/ornaments/sun-dark.png +0 -0
  217. package/brands/next/assets/ornaments/sun-light.png +0 -0
  218. package/brands/next/assets/photos/atrium.jpg +0 -0
  219. package/brands/next/assets/photos/cantilever.jpg +0 -0
  220. package/brands/next/assets/photos/corridor.jpg +0 -0
  221. package/brands/next/assets/photos/dome.jpg +0 -0
  222. package/brands/next/assets/photos/facade.jpg +0 -0
  223. package/brands/next/assets/photos/glass.jpg +0 -0
  224. package/brands/next/assets/photos/stairs.jpg +0 -0
  225. package/brands/next/assets/photos/tower.jpg +0 -0
  226. package/brands/next/assets/photos/walk.jpg +0 -0
  227. package/brands/next/assets/spark-gold.png +0 -0
  228. package/brands/next/assets/statement-art.png +0 -0
  229. package/brands/next/assets/wordmark-dark.png +0 -0
  230. package/brands/next/assets/wordmark-light.png +0 -0
  231. package/brands/next/assets/wordmark.svg +7 -0
  232. package/brands/next/fonts/FKRomanStandard-Light.ttf +0 -0
  233. package/brands/next/fonts/OFL-OverusedGrotesk.txt +93 -0
  234. package/brands/next/fonts/OverusedGrotesk-Italic.ttf +0 -0
  235. package/brands/next/fonts/OverusedGrotesk-Medium.ttf +0 -0
  236. package/brands/next/fonts/OverusedGrotesk-MediumItalic.ttf +0 -0
  237. package/brands/next/fonts/OverusedGrotesk-Roman.ttf +0 -0
  238. package/brands/next/fonts/OverusedGrotesk-SemiBold.ttf +0 -0
  239. package/brands/next/fonts/OverusedGrotesk-SemiBoldItalic.ttf +0 -0
  240. package/brands/next/fonts/faces.json +27 -0
  241. package/brands/next/lib/glyphs.js +89 -0
  242. package/brands/next/lib/measure.js +121 -0
  243. package/brands/next/lib/pdf.js +597 -0
  244. package/brands/next/lib/ttf.py +169 -0
  245. package/brands/next/lib/wandxml.py +37 -0
  246. package/brands/next/tokens.json +174 -0
  247. package/examples/gallery/job.mjs +83 -0
  248. package/examples/overview/job.mjs +99 -0
  249. package/lib/brand.js +135 -0
  250. package/lib/components.js +313 -0
  251. package/lib/content.js +66 -0
  252. package/lib/engine.js +280 -0
  253. package/lib/html_export.js +71 -0
  254. package/lib/pptx.js +446 -0
  255. package/lib/surfaces.js +62 -0
  256. package/package.json +17 -4
  257. package/templates/card.mjs +30 -0
  258. package/templates/page.mjs +52 -0
  259. package/templates/slide.mjs +46 -0
  260. package/tools/embed_fonts.py +154 -0
  261. package/wand.js +179 -0
  262. package/README.md +0 -3
@@ -0,0 +1,167 @@
1
+ """
2
+ ttf.py — the ~1% of TrueType that embed_fonts.py needs, with no fontTools.
3
+
4
+ Reads `head` and `OS/2` for the fields an EOT header carries, and rewrites the
5
+ `name` table so a face reports the family we address it by. Weight in this
6
+ system is carried by the family name — `Geist SemiBold` regular, not `Geist`
7
+ bold — so retagging is not cosmetic: without it PowerPoint resolves four of the
8
+ five families to the same face.
9
+
10
+ Everything else in the font is copied through untouched.
11
+ """
12
+
13
+ import struct
14
+
15
+ _HEAD_CHECKSUM_ADJ = 8 # offset of checkSumAdjustment within `head`
16
+ _MAGIC = 0xB1B0AFBA
17
+
18
+
19
+ class TTFError(ValueError):
20
+ pass
21
+
22
+
23
+ class TTF:
24
+ def __init__(self, data):
25
+ if len(data) < 12:
26
+ raise TTFError("not a font: too short")
27
+ tag = data[:4]
28
+ if tag not in (b"\x00\x01\x00\x00", b"true", b"ttcf", b"OTTO"):
29
+ raise TTFError(f"unrecognised sfnt tag {tag!r}")
30
+ if tag == b"ttcf":
31
+ raise TTFError("font collections (.ttc) are not supported — pass a single face")
32
+ self.data = data
33
+ self.sfnt = tag
34
+ (num,) = struct.unpack(">H", data[4:6])
35
+ self.tables = {}
36
+ for i in range(num):
37
+ off = 12 + i * 16
38
+ t, csum, o, ln = struct.unpack(">4sIII", data[off:off + 16])
39
+ self.tables[t] = (o, ln, csum)
40
+
41
+ # -- reading -----------------------------------------------------------
42
+
43
+ def table(self, tag):
44
+ if isinstance(tag, str):
45
+ tag = tag.encode()
46
+ if tag not in self.tables:
47
+ raise TTFError(f"font has no {tag.decode()} table")
48
+ o, ln, _ = self.tables[tag]
49
+ return self.data[o:o + ln]
50
+
51
+ @property
52
+ def check_sum_adjustment(self):
53
+ return struct.unpack(">I", self.table("head")[8:12])[0]
54
+
55
+ @property
56
+ def os2(self):
57
+ """The OS/2 fields the EOT header needs."""
58
+ t = self.table("OS/2")
59
+ version = struct.unpack(">H", t[0:2])[0]
60
+ d = {
61
+ "version": version,
62
+ "usWeightClass": struct.unpack(">H", t[4:6])[0],
63
+ "panose": t[32:42],
64
+ "ulUnicodeRange": struct.unpack(">IIII", t[42:58]),
65
+ "fsSelection": struct.unpack(">H", t[62:64])[0],
66
+ }
67
+ # code page ranges only exist from version 1
68
+ d["ulCodePageRange"] = struct.unpack(">II", t[78:86]) if version >= 1 and len(t) >= 86 else (0, 0)
69
+ return d
70
+
71
+ def names(self):
72
+ """{nameID: string} for the Windows/Unicode records, best effort."""
73
+ t = self.table("name")
74
+ count, storage = struct.unpack(">HH", t[2:6])
75
+ out = {}
76
+ for i in range(count):
77
+ p = 6 + i * 12
78
+ plat, enc, lang, nid, ln, off = struct.unpack(">HHHHHH", t[p:p + 12])
79
+ raw = t[storage + off: storage + off + ln]
80
+ try:
81
+ s = raw.decode("utf-16-be") if plat == 3 or (plat == 0) else raw.decode("latin-1")
82
+ except UnicodeDecodeError:
83
+ continue
84
+ out.setdefault(nid, s)
85
+ return out
86
+
87
+ # -- writing -----------------------------------------------------------
88
+
89
+ def retagged(self, family, subfamily, full, postscript):
90
+ """Return new font bytes with name IDs 1/2/4/6 replaced."""
91
+ replace = {1: family, 2: subfamily, 4: full, 6: postscript}
92
+ name = _rebuild_name(self.table("name"), replace)
93
+ return _reassemble(self, {b"name": name})
94
+
95
+
96
+ def _rebuild_name(t, replace):
97
+ count, storage = struct.unpack(">HH", t[2:6])
98
+ records, strings, cursor = [], bytearray(), 0
99
+
100
+ for i in range(count):
101
+ p = 6 + i * 12
102
+ plat, enc, lang, nid, ln, off = struct.unpack(">HHHHHH", t[p:p + 12])
103
+ if nid in replace:
104
+ s = replace[nid]
105
+ raw = s.encode("utf-16-be") if plat in (0, 3) else s.encode("latin-1", "replace")
106
+ else:
107
+ raw = t[storage + off: storage + off + ln]
108
+ records.append((plat, enc, lang, nid, len(raw), cursor))
109
+ strings += raw
110
+ cursor += len(raw)
111
+
112
+ # sort order is required by the spec: platform, encoding, language, name
113
+ records.sort(key=lambda r: (r[0], r[1], r[2], r[3]))
114
+ head = struct.pack(">HHH", 0, len(records), 6 + len(records) * 12)
115
+ body = b"".join(struct.pack(">HHHHHH", *r) for r in records)
116
+ return head + body + bytes(strings)
117
+
118
+
119
+ def _checksum(b):
120
+ if len(b) % 4:
121
+ b = b + b"\0" * (4 - len(b) % 4)
122
+ return sum(struct.unpack(f">{len(b)//4}I", b)) & 0xFFFFFFFF
123
+
124
+
125
+ def _reassemble(font, overrides):
126
+ """Rewrite the whole sfnt with some tables replaced, fixing every offset,
127
+ every table checksum and the head checkSumAdjustment."""
128
+ tags = sorted(font.tables)
129
+ parts = {}
130
+ for t in tags:
131
+ parts[t] = overrides.get(t, font.table(t))
132
+
133
+ # head's checkSumAdjustment must be zero while the file checksum is taken
134
+ head = bytearray(parts[b"head"])
135
+ head[_HEAD_CHECKSUM_ADJ:_HEAD_CHECKSUM_ADJ + 4] = b"\0\0\0\0"
136
+ parts[b"head"] = bytes(head)
137
+
138
+ n = len(tags)
139
+ # binary-search fields the spec wants present; readers ignore them but
140
+ # validators complain when they are wrong
141
+ entry_selector = max(n.bit_length() - 1, 0)
142
+ search_range = (1 << entry_selector) * 16
143
+ out = bytearray(struct.pack(">4sHHHH", font.sfnt, n, search_range,
144
+ entry_selector, n * 16 - search_range))
145
+
146
+ offset = 12 + n * 16
147
+ directory, body = bytearray(), bytearray()
148
+ for t in tags:
149
+ d = parts[t]
150
+ directory += struct.pack(">4sIII", t, _checksum(d), offset, len(d))
151
+ body += d
152
+ pad = (-len(d)) % 4
153
+ body += b"\0" * pad
154
+ offset += len(d) + pad
155
+
156
+ out += directory + body
157
+ adj = (_MAGIC - _checksum(bytes(out))) & 0xFFFFFFFF
158
+
159
+ head_off = struct.unpack(">I", out[12 + tags.index(b"head") * 16 + 8:
160
+ 12 + tags.index(b"head") * 16 + 12])[0]
161
+ out[head_off + _HEAD_CHECKSUM_ADJ: head_off + _HEAD_CHECKSUM_ADJ + 4] = struct.pack(">I", adj)
162
+ return bytes(out)
163
+
164
+
165
+ def load(path):
166
+ with open(path, "rb") as f:
167
+ return TTF(f.read())
@@ -0,0 +1,37 @@
1
+ """
2
+ wandxml.py — XML parsing that works with nothing installed.
3
+
4
+ The kit has to run wherever someone unzips it. `defusedxml` is the better
5
+ parser and we use it when it's there, but requiring a pip install to get past
6
+ Phase 0 means the engine is unusable in exactly the situation it was built for:
7
+ a fresh container with a zip and a .pptx in it.
8
+
9
+ The stdlib fallback is guarded against the one attack that matters for a parser
10
+ pointed at an untrusted .pptx — entity expansion — by refusing any document with
11
+ a DOCTYPE. Office files never have one.
12
+ """
13
+
14
+ try:
15
+ from defusedxml import minidom as _md
16
+ HARDENED = True
17
+ except ImportError: # stdlib fallback
18
+ from xml.dom import minidom as _md
19
+ HARDENED = False
20
+
21
+
22
+ class UnsafeXML(ValueError):
23
+ pass
24
+
25
+
26
+ def parse(data):
27
+ """Parse .pptx part bytes (or str) into a minidom Document."""
28
+ probe = data[:8192]
29
+ if isinstance(probe, bytes):
30
+ probe = probe.decode("utf-8", "ignore")
31
+ if not HARDENED and ("<!DOCTYPE" in probe or "<!ENTITY" in probe):
32
+ raise UnsafeXML(
33
+ "document declares a DOCTYPE or ENTITY. Office parts never do. "
34
+ "Refusing to parse it with the stdlib parser — `pip install defusedxml` "
35
+ "if this file is genuinely trusted."
36
+ )
37
+ return _md.parseString(data)
@@ -0,0 +1,206 @@
1
+ {
2
+ "_": "The current brand as values a kit can draw with: the swatch is BRAND.md's table, measured from the Vivicta and Kearney decks; `roles` names what each value is for, once per ground, so a component asks for `ink` or `accent` and never for a hex. The studio kit (power-prompts/studio) reads this file; BRAND.md documents usage. If a hex here and in BRAND.md ever differ, BRAND.md is right and this file has drifted.",
3
+ "name": "current",
4
+ "title": "Wand, current brand (Geist)",
5
+ "swatch": {
6
+ "accent": "8B7CF6",
7
+ "accentDeep": "6C5CE7",
8
+ "accentSoft": "9481FF",
9
+ "accentPale": "DCD6F5",
10
+ "bgDark": "240F48",
11
+ "card": "291D5A",
12
+ "cardStroke": "5B4BB0",
13
+ "cardMuted": "1B1738",
14
+ "cardMutedStroke": "3A2E63",
15
+ "cardEmphFlat": "251C63",
16
+ "inset": "1A1033",
17
+ "eyebrow": "A78BFA",
18
+ "label": "8F86B8",
19
+ "lede": "AA9BFF",
20
+ "ledeSoft": "C4B5F4",
21
+ "footerDark": "746B98",
22
+ "bodyDark": "F5F5F5",
23
+ "mutedDark": "BBBBBB",
24
+ "bodyDarkAlt": "C6BFE4",
25
+ "emphRow": "4D2E8C",
26
+ "emphRowStroke": "7557AE",
27
+ "bgLight": "F8F6FB",
28
+ "inkLight": "1D1A33",
29
+ "bodyLight": "5C5779",
30
+ "mutedLight": "555077",
31
+ "cardLight": "FFFFFF",
32
+ "surface": "EBE8F8",
33
+ "track": "E9E6F5",
34
+ "divider": "E4E0F2",
35
+ "footerLight": "999999",
36
+ "white": "FFFFFF"
37
+ },
38
+ "roles": {
39
+ "light": {
40
+ "bg": "F8F6FB",
41
+ "paper": "FFFFFF",
42
+ "ink": "1D1A33",
43
+ "text": "5C5779",
44
+ "soft": "555077",
45
+ "muted": "999999",
46
+ "accent": "8B7CF6",
47
+ "accentText": "6C5CE7",
48
+ "label": "6C5CE7",
49
+ "emph": "251C63",
50
+ "emphInk": "FFFFFF",
51
+ "emphText": "DCD6F5",
52
+ "emphAccent": "AA9BFF",
53
+ "stroke": "E4E0F2",
54
+ "rule": "E4E0F2",
55
+ "track": "E9E6F5",
56
+ "surface": "EBE8F8"
57
+ },
58
+ "dark": {
59
+ "bg": "240F48",
60
+ "paper": "291D5A",
61
+ "ink": "FFFFFF",
62
+ "text": "F5F5F5",
63
+ "soft": "BBBBBB",
64
+ "muted": "746B98",
65
+ "accent": "9481FF",
66
+ "accentText": "9481FF",
67
+ "label": "A78BFA",
68
+ "emph": "4D2E8C",
69
+ "emphInk": "FFFFFF",
70
+ "emphText": "DCD6F5",
71
+ "emphAccent": "C4B5F4",
72
+ "stroke": "5B4BB0",
73
+ "rule": "3A2E63",
74
+ "track": "1A1033",
75
+ "surface": "1A1033"
76
+ }
77
+ },
78
+ "faces": {
79
+ "display": {
80
+ "family": "Geist",
81
+ "weight": 600,
82
+ "tracking": -0.02,
83
+ "leading": 1.08
84
+ },
85
+ "body": {
86
+ "family": "Geist",
87
+ "weight": 400,
88
+ "tracking": 0,
89
+ "leading": 1.6
90
+ },
91
+ "label": {
92
+ "family": "Geist",
93
+ "weight": 600,
94
+ "tracking": 0.18,
95
+ "leading": 1.2,
96
+ "caps": true
97
+ },
98
+ "files": [
99
+ {
100
+ "family": "Geist",
101
+ "weight": 300,
102
+ "italic": false,
103
+ "file": "Geist-Light.ttf",
104
+ "pptx": "Geist Light"
105
+ },
106
+ {
107
+ "family": "Geist",
108
+ "weight": 300,
109
+ "italic": true,
110
+ "file": "Geist-LightItalic.ttf",
111
+ "pptx": "Geist Light"
112
+ },
113
+ {
114
+ "family": "Geist",
115
+ "weight": 400,
116
+ "italic": false,
117
+ "file": "Geist-Regular.ttf",
118
+ "pptx": "Geist"
119
+ },
120
+ {
121
+ "family": "Geist",
122
+ "weight": 400,
123
+ "italic": true,
124
+ "file": "Geist-Italic.ttf",
125
+ "pptx": "Geist"
126
+ },
127
+ {
128
+ "family": "Geist",
129
+ "weight": 500,
130
+ "italic": false,
131
+ "file": "Geist-Medium.ttf",
132
+ "pptx": "Geist Medium"
133
+ },
134
+ {
135
+ "family": "Geist",
136
+ "weight": 500,
137
+ "italic": true,
138
+ "file": "Geist-MediumItalic.ttf",
139
+ "pptx": "Geist Medium"
140
+ },
141
+ {
142
+ "family": "Geist",
143
+ "weight": 600,
144
+ "italic": false,
145
+ "file": "Geist-SemiBold.ttf",
146
+ "pptx": "Geist SemiBold"
147
+ },
148
+ {
149
+ "family": "Geist",
150
+ "weight": 600,
151
+ "italic": true,
152
+ "file": "Geist-SemiBoldItalic.ttf",
153
+ "pptx": "Geist SemiBold"
154
+ },
155
+ {
156
+ "family": "Geist",
157
+ "weight": 700,
158
+ "italic": false,
159
+ "file": "Geist-Bold.ttf",
160
+ "pptx": "Geist Bold"
161
+ },
162
+ {
163
+ "family": "Geist",
164
+ "weight": 700,
165
+ "italic": true,
166
+ "file": "Geist-BoldItalic.ttf",
167
+ "pptx": "Geist Bold"
168
+ },
169
+ {
170
+ "family": "Geist",
171
+ "weight": 800,
172
+ "italic": false,
173
+ "file": "Geist-ExtraBold.ttf",
174
+ "pptx": "Geist ExtraBold"
175
+ },
176
+ {
177
+ "family": "Geist",
178
+ "weight": 800,
179
+ "italic": true,
180
+ "file": "Geist-ExtraBoldItalic.ttf",
181
+ "pptx": "Geist ExtraBold"
182
+ }
183
+ ],
184
+ "license": "Geist: SIL OFL 1.1 (npm geist@1.7.2)"
185
+ },
186
+ "shape": {
187
+ "radius": 1,
188
+ "cardStroke": 1,
189
+ "emphStroke": 0
190
+ },
191
+ "art": {
192
+ "cover": "bg-cover-dark.png",
193
+ "overlay": "bg-overlay-dark.png",
194
+ "statement": "bg-statement-dark.png",
195
+ "closing": "closing-art.png",
196
+ "coverArt": "cover-art.png",
197
+ "divider": "divider-art.png",
198
+ "orb": "orb-cover.png",
199
+ "glow": "orb-glow.png",
200
+ "markOnLight": "mark-dark.png",
201
+ "markOnDark": "mark-dark.png",
202
+ "wordmarkOnLight": "wordmark-dark.png",
203
+ "wordmarkOnDark": "wordmark-white.png",
204
+ "markInvertOnDark": true
205
+ }
206
+ }
Binary file
@@ -0,0 +1,29 @@
1
+ # Glyphs — Lucide
2
+
3
+ 75 icons from [Lucide](https://lucide.dev) v0.469.0, **ISC licensed** (permissive:
4
+ use, modify and redistribute, keep the notice). They replace the numbered PNGs
5
+ that were reverse-extracted from a reference deck.
6
+
7
+ Two problems they fix, both of which were standing known gaps:
8
+
9
+ **Polarity.** The old glyphs were baked white or baked accent, so a white one on
10
+ a light-theme `surface` tile was invisible. These are single-colour stroke SVGs
11
+ with `stroke="currentColor"`, and the kit substitutes the theme's ink colour at
12
+ build time. There is no variant to pick and nothing to get wrong.
13
+
14
+ **Meaning.** They have names. A spec says `icon: "shield-check"`, not
15
+ `icon: "icon-17.png"`. `index.json` groups them by what a deck actually needs —
16
+ governance, workforce, platform, process, outcome, signal.
17
+
18
+ ## How they reach the deck
19
+
20
+ As vector. pptxgenjs writes the OOXML `asvg:svgBlip` extension with a raster
21
+ fallback, so PowerPoint 2016+ renders the SVG and anything older gets the PNG.
22
+ Nothing is rasterised at build time, so the kit still needs no native
23
+ dependency.
24
+
25
+ ## The old set
26
+
27
+ `../icons/*.png` is still there and still works — every existing spec references
28
+ those filenames. New specs should use glyph names. The two sets can coexist
29
+ indefinitely; `iconTile` resolves a name first and falls back to a filename.
@@ -0,0 +1,15 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-activity"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" />
15
+ </svg>
@@ -0,0 +1,17 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-atom"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <circle cx="12" cy="12" r="1" />
15
+ <path d="M20.2 20.2c2.04-2.03.02-7.36-4.5-11.9-4.54-4.52-9.87-6.54-11.9-4.5-2.04 2.03-.02 7.36 4.5 11.9 4.54 4.52 9.87 6.54 11.9 4.5Z" />
16
+ <path d="M15.7 15.7c4.52-4.54 6.54-9.87 4.5-11.9-2.03-2.04-7.36-.02-11.9 4.5-4.52 4.54-6.54 9.87-4.5 11.9 2.03 2.04 7.36.02 11.9-4.5Z" />
17
+ </svg>
@@ -0,0 +1,16 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-badge-check"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" />
15
+ <path d="m9 12 2 2 4-4" />
16
+ </svg>
@@ -0,0 +1,16 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-bell"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M10.268 21a2 2 0 0 0 3.464 0" />
15
+ <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" />
16
+ </svg>
@@ -0,0 +1,16 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-blocks"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <rect width="7" height="7" x="14" y="3" rx="1" />
15
+ <path d="M10 21V8a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H3" />
16
+ </svg>
@@ -0,0 +1,16 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-book-open"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M12 7v14" />
15
+ <path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" />
16
+ </svg>
@@ -0,0 +1,20 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-bot"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M12 8V4H8" />
15
+ <rect width="16" height="12" x="4" y="8" rx="2" />
16
+ <path d="M2 14h2" />
17
+ <path d="M20 14h2" />
18
+ <path d="M15 13v2" />
19
+ <path d="M9 13v2" />
20
+ </svg>
@@ -0,0 +1,26 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-boxes"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z" />
15
+ <path d="m7 16.5-4.74-2.85" />
16
+ <path d="m7 16.5 5-3" />
17
+ <path d="M7 16.5v5.17" />
18
+ <path d="M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z" />
19
+ <path d="m17 16.5-5-3" />
20
+ <path d="m17 16.5 4.74-2.85" />
21
+ <path d="M17 16.5v5.17" />
22
+ <path d="M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z" />
23
+ <path d="M12 8 7.26 5.15" />
24
+ <path d="m12 8 4.74-2.85" />
25
+ <path d="M12 13.5V8" />
26
+ </svg>
@@ -0,0 +1,23 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-brain"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z" />
15
+ <path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z" />
16
+ <path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4" />
17
+ <path d="M17.599 6.5a3 3 0 0 0 .399-1.375" />
18
+ <path d="M6.003 5.125A3 3 0 0 0 6.401 6.5" />
19
+ <path d="M3.477 10.896a4 4 0 0 1 .585-.396" />
20
+ <path d="M19.938 10.5a4 4 0 0 1 .585.396" />
21
+ <path d="M6 18a4 4 0 0 1-1.967-.516" />
22
+ <path d="M19.967 17.484A4 4 0 0 1 18 18" />
23
+ </svg>
@@ -0,0 +1,16 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-briefcase"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16" />
15
+ <rect width="20" height="14" x="2" y="6" rx="2" />
16
+ </svg>
@@ -0,0 +1,21 @@
1
+ <!-- @license lucide-static v0.469.0 - ISC -->
2
+ <svg
3
+ class="lucide lucide-building-2"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="24"
6
+ height="24"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ stroke-width="2"
11
+ stroke-linecap="round"
12
+ stroke-linejoin="round"
13
+ >
14
+ <path d="M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18Z" />
15
+ <path d="M6 12H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2" />
16
+ <path d="M18 9h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2h-2" />
17
+ <path d="M10 6h4" />
18
+ <path d="M10 10h4" />
19
+ <path d="M10 14h4" />
20
+ <path d="M10 18h4" />
21
+ </svg>