@sanity-labs/slides 0.0.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 (224) hide show
  1. package/README.md +241 -0
  2. package/SKILL.md +119 -0
  3. package/dist/cli.d.ts +38 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +386 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/core/components.d.ts +179 -0
  8. package/dist/core/components.d.ts.map +1 -0
  9. package/dist/core/components.js +40 -0
  10. package/dist/core/components.js.map +1 -0
  11. package/dist/core/fake-runtime.d.ts +138 -0
  12. package/dist/core/fake-runtime.d.ts.map +1 -0
  13. package/dist/core/fake-runtime.js +210 -0
  14. package/dist/core/fake-runtime.js.map +1 -0
  15. package/dist/core/font-resolver.d.ts +28 -0
  16. package/dist/core/font-resolver.d.ts.map +1 -0
  17. package/dist/core/font-resolver.js +30 -0
  18. package/dist/core/font-resolver.js.map +1 -0
  19. package/dist/core/geometry.d.ts +71 -0
  20. package/dist/core/geometry.d.ts.map +1 -0
  21. package/dist/core/geometry.js +44 -0
  22. package/dist/core/geometry.js.map +1 -0
  23. package/dist/core/index.d.ts +19 -0
  24. package/dist/core/index.d.ts.map +1 -0
  25. package/dist/core/index.js +20 -0
  26. package/dist/core/index.js.map +1 -0
  27. package/dist/core/manifest.d.ts +123 -0
  28. package/dist/core/manifest.d.ts.map +1 -0
  29. package/dist/core/manifest.js +43 -0
  30. package/dist/core/manifest.js.map +1 -0
  31. package/dist/core/op-translator-pptx.d.ts +150 -0
  32. package/dist/core/op-translator-pptx.d.ts.map +1 -0
  33. package/dist/core/op-translator-pptx.js +245 -0
  34. package/dist/core/op-translator-pptx.js.map +1 -0
  35. package/dist/core/pptx-runtime.d.ts +103 -0
  36. package/dist/core/pptx-runtime.d.ts.map +1 -0
  37. package/dist/core/pptx-runtime.js +405 -0
  38. package/dist/core/pptx-runtime.js.map +1 -0
  39. package/dist/core/reconciler.d.ts +113 -0
  40. package/dist/core/reconciler.d.ts.map +1 -0
  41. package/dist/core/reconciler.js +453 -0
  42. package/dist/core/reconciler.js.map +1 -0
  43. package/dist/core/runtime.d.ts +161 -0
  44. package/dist/core/runtime.d.ts.map +1 -0
  45. package/dist/core/runtime.js +11 -0
  46. package/dist/core/runtime.js.map +1 -0
  47. package/dist/core/template.d.ts +32 -0
  48. package/dist/core/template.d.ts.map +1 -0
  49. package/dist/core/template.js +3 -0
  50. package/dist/core/template.js.map +1 -0
  51. package/dist/dev/auto-examples.d.ts +6 -0
  52. package/dist/dev/auto-examples.d.ts.map +1 -0
  53. package/dist/dev/auto-examples.js +79 -0
  54. package/dist/dev/auto-examples.js.map +1 -0
  55. package/dist/dev/bin/slides-dev.d.ts +3 -0
  56. package/dist/dev/bin/slides-dev.d.ts.map +1 -0
  57. package/dist/dev/bin/slides-dev.js +87 -0
  58. package/dist/dev/bin/slides-dev.js.map +1 -0
  59. package/dist/dev/bin/slides-dev.mjs +24 -0
  60. package/dist/dev/compose-deck.d.ts +18 -0
  61. package/dist/dev/compose-deck.d.ts.map +1 -0
  62. package/dist/dev/compose-deck.js +19 -0
  63. package/dist/dev/compose-deck.js.map +1 -0
  64. package/dist/dev/deck-viewer.d.ts +19 -0
  65. package/dist/dev/deck-viewer.d.ts.map +1 -0
  66. package/dist/dev/deck-viewer.js +237 -0
  67. package/dist/dev/deck-viewer.js.map +1 -0
  68. package/dist/dev/dev-server/client/entry.d.ts +2 -0
  69. package/dist/dev/dev-server/client/entry.d.ts.map +1 -0
  70. package/dist/dev/dev-server/client/entry.js +12 -0
  71. package/dist/dev/dev-server/client/entry.js.map +1 -0
  72. package/dist/dev/dev-server/output.d.ts +8 -0
  73. package/dist/dev/dev-server/output.d.ts.map +1 -0
  74. package/dist/dev/dev-server/output.js +32 -0
  75. package/dist/dev/dev-server/output.js.map +1 -0
  76. package/dist/dev/dev-server/server-only-stub.d.ts +7 -0
  77. package/dist/dev/dev-server/server-only-stub.d.ts.map +1 -0
  78. package/dist/dev/dev-server/server-only-stub.js +12 -0
  79. package/dist/dev/dev-server/server-only-stub.js.map +1 -0
  80. package/dist/dev/dev-server/start.d.ts +14 -0
  81. package/dist/dev/dev-server/start.d.ts.map +1 -0
  82. package/dist/dev/dev-server/start.js +135 -0
  83. package/dist/dev/dev-server/start.js.map +1 -0
  84. package/dist/dev/index.d.ts +5 -0
  85. package/dist/dev/index.d.ts.map +1 -0
  86. package/dist/dev/index.js +5 -0
  87. package/dist/dev/index.js.map +1 -0
  88. package/dist/dev/lib/cn.d.ts +3 -0
  89. package/dist/dev/lib/cn.d.ts.map +1 -0
  90. package/dist/dev/lib/cn.js +3 -0
  91. package/dist/dev/lib/cn.js.map +1 -0
  92. package/dist/dev/slide-canvas.d.ts +12 -0
  93. package/dist/dev/slide-canvas.d.ts.map +1 -0
  94. package/dist/dev/slide-canvas.js +123 -0
  95. package/dist/dev/slide-canvas.js.map +1 -0
  96. package/dist/dev/styles.css +37 -0
  97. package/dist/dev/ui/icon-button.d.ts +12 -0
  98. package/dist/dev/ui/icon-button.d.ts.map +1 -0
  99. package/dist/dev/ui/icon-button.js +6 -0
  100. package/dist/dev/ui/icon-button.js.map +1 -0
  101. package/dist/dev/ui/kbd.d.ts +6 -0
  102. package/dist/dev/ui/kbd.d.ts.map +1 -0
  103. package/dist/dev/ui/kbd.js +4 -0
  104. package/dist/dev/ui/kbd.js.map +1 -0
  105. package/dist/dev/ui/text-button.d.ts +10 -0
  106. package/dist/dev/ui/text-button.d.ts.map +1 -0
  107. package/dist/dev/ui/text-button.js +6 -0
  108. package/dist/dev/ui/text-button.js.map +1 -0
  109. package/dist/dev/url-state.d.ts +7 -0
  110. package/dist/dev/url-state.d.ts.map +1 -0
  111. package/dist/dev/url-state.js +13 -0
  112. package/dist/dev/url-state.js.map +1 -0
  113. package/dist/dev/use-keyboard-nav.d.ts +17 -0
  114. package/dist/dev/use-keyboard-nav.d.ts.map +1 -0
  115. package/dist/dev/use-keyboard-nav.js +53 -0
  116. package/dist/dev/use-keyboard-nav.js.map +1 -0
  117. package/dist/index.d.ts +17 -0
  118. package/dist/index.d.ts.map +1 -0
  119. package/dist/index.js +17 -0
  120. package/dist/index.js.map +1 -0
  121. package/dist/mcp/errors.d.ts +57 -0
  122. package/dist/mcp/errors.d.ts.map +1 -0
  123. package/dist/mcp/errors.js +44 -0
  124. package/dist/mcp/errors.js.map +1 -0
  125. package/dist/mcp/index.d.ts +29 -0
  126. package/dist/mcp/index.d.ts.map +1 -0
  127. package/dist/mcp/index.js +29 -0
  128. package/dist/mcp/index.js.map +1 -0
  129. package/dist/mcp/naming.d.ts +37 -0
  130. package/dist/mcp/naming.d.ts.map +1 -0
  131. package/dist/mcp/naming.js +43 -0
  132. package/dist/mcp/naming.js.map +1 -0
  133. package/dist/mcp/render.d.ts +45 -0
  134. package/dist/mcp/render.d.ts.map +1 -0
  135. package/dist/mcp/render.js +77 -0
  136. package/dist/mcp/render.js.map +1 -0
  137. package/dist/mcp/schema.d.ts +54 -0
  138. package/dist/mcp/schema.d.ts.map +1 -0
  139. package/dist/mcp/schema.js +55 -0
  140. package/dist/mcp/schema.js.map +1 -0
  141. package/dist/mcp/server.d.ts +63 -0
  142. package/dist/mcp/server.d.ts.map +1 -0
  143. package/dist/mcp/server.js +196 -0
  144. package/dist/mcp/server.js.map +1 -0
  145. package/dist/scaffold/index.d.ts +39 -0
  146. package/dist/scaffold/index.d.ts.map +1 -0
  147. package/dist/scaffold/index.js +84 -0
  148. package/dist/scaffold/index.js.map +1 -0
  149. package/dist/scaffold/template-base/README.md +134 -0
  150. package/dist/scaffold/template-base/_gitignore +4 -0
  151. package/dist/scaffold/template-base/package.json +35 -0
  152. package/dist/scaffold/template-base/src/components/Cover.tsx +30 -0
  153. package/dist/scaffold/template-base/src/index.ts +27 -0
  154. package/dist/scaffold/template-base/src/preview.tsx +9 -0
  155. package/dist/scaffold/template-base/tsconfig.build.json +10 -0
  156. package/dist/scaffold/template-base/tsconfig.json +18 -0
  157. package/package.json +164 -0
  158. package/src/__tests__/fixtures/test-template/index.tsx +77 -0
  159. package/src/__tests__/pptx-mcp.test.ts +85 -0
  160. package/src/__tests__/pptx-smoke.test.ts +45 -0
  161. package/src/__tests__/preview.test.ts +28 -0
  162. package/src/cli.ts +426 -0
  163. package/src/core/__snapshots__/reconciler.test.ts.snap +320 -0
  164. package/src/core/components.test.ts +57 -0
  165. package/src/core/components.ts +196 -0
  166. package/src/core/fake-runtime.test.ts +174 -0
  167. package/src/core/fake-runtime.ts +302 -0
  168. package/src/core/font-resolver.ts +46 -0
  169. package/src/core/geometry.test.ts +58 -0
  170. package/src/core/geometry.ts +91 -0
  171. package/src/core/index.ts +69 -0
  172. package/src/core/manifest.test.ts +33 -0
  173. package/src/core/manifest.ts +150 -0
  174. package/src/core/op-translator-pptx.test.ts +204 -0
  175. package/src/core/op-translator-pptx.ts +365 -0
  176. package/src/core/pptx-runtime.test.ts +137 -0
  177. package/src/core/pptx-runtime.ts +504 -0
  178. package/src/core/reconciler.test.ts +644 -0
  179. package/src/core/reconciler.ts +603 -0
  180. package/src/core/runtime.ts +150 -0
  181. package/src/core/template.test.ts +136 -0
  182. package/src/core/template.ts +37 -0
  183. package/src/dev/auto-examples.ts +89 -0
  184. package/src/dev/bin/slides-dev.mjs +24 -0
  185. package/src/dev/bin/slides-dev.ts +101 -0
  186. package/src/dev/compose-deck.test.ts +68 -0
  187. package/src/dev/compose-deck.ts +40 -0
  188. package/src/dev/deck-viewer.tsx +677 -0
  189. package/src/dev/dev-server/client/entry.tsx +15 -0
  190. package/src/dev/dev-server/client/index.html +24 -0
  191. package/src/dev/dev-server/output.ts +37 -0
  192. package/src/dev/dev-server/server-only-stub.ts +12 -0
  193. package/src/dev/dev-server/start.ts +155 -0
  194. package/src/dev/index.ts +4 -0
  195. package/src/dev/lib/cn.ts +3 -0
  196. package/src/dev/slide-canvas.test.tsx +66 -0
  197. package/src/dev/slide-canvas.tsx +170 -0
  198. package/src/dev/styles.css +37 -0
  199. package/src/dev/ui/icon-button.tsx +31 -0
  200. package/src/dev/ui/kbd.tsx +20 -0
  201. package/src/dev/ui/text-button.tsx +31 -0
  202. package/src/dev/url-state.test.ts +22 -0
  203. package/src/dev/url-state.ts +17 -0
  204. package/src/dev/use-keyboard-nav.ts +64 -0
  205. package/src/index.ts +17 -0
  206. package/src/mcp/errors.test.ts +51 -0
  207. package/src/mcp/errors.ts +76 -0
  208. package/src/mcp/index.ts +45 -0
  209. package/src/mcp/naming.test.ts +39 -0
  210. package/src/mcp/naming.ts +49 -0
  211. package/src/mcp/render.ts +110 -0
  212. package/src/mcp/schema.test.ts +86 -0
  213. package/src/mcp/schema.ts +93 -0
  214. package/src/mcp/server.test.ts +309 -0
  215. package/src/mcp/server.ts +276 -0
  216. package/src/scaffold/index.ts +102 -0
  217. package/src/scaffold/template-base/README.md +134 -0
  218. package/src/scaffold/template-base/_gitignore +4 -0
  219. package/src/scaffold/template-base/package.json +35 -0
  220. package/src/scaffold/template-base/src/components/Cover.tsx +30 -0
  221. package/src/scaffold/template-base/src/index.ts +27 -0
  222. package/src/scaffold/template-base/src/preview.tsx +9 -0
  223. package/src/scaffold/template-base/tsconfig.build.json +10 -0
  224. package/src/scaffold/template-base/tsconfig.json +18 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * `SlidesRuntime` that emits a .pptx file to disk via pptxgenjs.
3
+ *
4
+ * The brand visuals (colors, layout, typography sizes) are preserved
5
+ * end-to-end. Fonts that aren't installed on the viewer's machine get
6
+ * substituted via the brand-supplied substitution table (see "Font
7
+ * substitution" below).
8
+ *
9
+ * # Known limitations
10
+ *
11
+ * - **Re-fill against an existing .pptx.** Not supported; the manifest is
12
+ * held in memory only.
13
+ * - **Master templates.** PPTX has no master-ref concept here; the
14
+ * `masterRef` argument to `createDeckFromMaster` is recorded as the
15
+ * presentation's subject metadata and otherwise ignored.
16
+ *
17
+ * # Font substitution
18
+ *
19
+ * PPTX cannot embed fonts. Template-specific families (e.g., a brand's display
20
+ * face) may not be installed on the viewer's machine. The runtime accepts a
21
+ * `fontSubstitution` map keyed by the literal font name (as the reconciler
22
+ * emits it after role resolution) → the family name to write into the
23
+ * .pptx file. Anything not in the map passes through unchanged.
24
+ *
25
+ * The substrate ships an empty default. Brands provide their own table
26
+ * (see `@sanity-labs/slides` for an example). Use `onUnknownFont` if you want
27
+ * to surface diagnostics for unmapped families.
28
+ *
29
+ * # Manifest persistence
30
+ *
31
+ * Held in memory and accessible via `getManifest()`. Persistence into the
32
+ * .pptx file itself (custom XML / hidden shape) is not implemented; re-fill
33
+ * against a previously-generated .pptx is out of scope for this runtime.
34
+ */
35
+ import type { GenerationManifest } from './manifest.js';
36
+ import type { ApplyOpsResult, SlideOp, SlidesRuntime } from './runtime.js';
37
+ /** Configuration for a `PptxSlidesRuntime` instance. */
38
+ export interface PptxSlidesRuntimeOptions {
39
+ /** Output directory. Created if missing. Default: `process.cwd()`. */
40
+ readonly outputDir?: string;
41
+ /**
42
+ * Substitution map: literal font name (as the reconciler emits it) → the
43
+ * family name to write into the .pptx file. Default: `{}` (pass-through).
44
+ * Templates inject their own table here.
45
+ */
46
+ readonly fontSubstitution?: Readonly<Record<string, string>>;
47
+ /**
48
+ * Optional hook called once per unknown font (a font not in the
49
+ * substitution table). Default: no-op. Pass `console.warn` (or your
50
+ * logger) to surface diagnostics.
51
+ */
52
+ readonly onUnknownFont?: ((fontFamily: string) => void) | null;
53
+ /** Author metadata baked into the .pptx file. Default: `'react-pptx'`. */
54
+ readonly author?: string;
55
+ /** Company metadata. Default: undefined. */
56
+ readonly company?: string;
57
+ }
58
+ /**
59
+ * Default font substitution table for the substrate: empty.
60
+ *
61
+ * PPTX cannot embed fonts. Template packages ship their own substitution table
62
+ * (see `@sanity-labs/slides` for an example) and pass it via
63
+ * `PptxSlidesRuntimeOptions.fontSubstitution`. The substrate stays
64
+ * brand-agnostic.
65
+ */
66
+ export declare const DEFAULT_PPTX_FONT_SUBSTITUTION: Readonly<Record<string, string>>;
67
+ /**
68
+ * A `SlidesRuntime` that emits .pptx files via pptxgenjs.
69
+ *
70
+ * See module docstring for limitations and font-substitution behavior.
71
+ */
72
+ export declare class PptxSlidesRuntime implements SlidesRuntime {
73
+ private readonly outputDir;
74
+ private readonly fontSubstitution;
75
+ private readonly onUnknownFont;
76
+ private readonly author;
77
+ private readonly company;
78
+ private readonly decks;
79
+ private deckCounter;
80
+ constructor(options?: PptxSlidesRuntimeOptions);
81
+ applyOps(deckId: string, ops: readonly SlideOp[]): Promise<ApplyOpsResult>;
82
+ createDeckFromMaster(masterRef: string, title: string): Promise<{
83
+ deckId: string;
84
+ }>;
85
+ /**
86
+ * Serialize the in-memory presentation for a deck to a .pptx file.
87
+ *
88
+ * Returns the absolute path to the generated file. The filename is
89
+ * derived from the deck title (sanitized) with a `.pptx` extension.
90
+ */
91
+ write(deckId: string): Promise<{
92
+ filePath: string;
93
+ }>;
94
+ toBuffer(deckId: string): Promise<Buffer>;
95
+ /** Attach a manifest to a deck (held in memory; not embedded into the .pptx file). */
96
+ attachManifest(deckId: string, manifest: GenerationManifest): void;
97
+ /** Retrieve a deck's manifest (if attached). */
98
+ getManifest(deckId: string): GenerationManifest | undefined;
99
+ /** List deck IDs the runtime is tracking. */
100
+ listDeckIds(): readonly string[];
101
+ private requireDeck;
102
+ }
103
+ //# sourceMappingURL=pptx-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pptx-runtime.d.ts","sourceRoot":"","sources":["../../src/core/pptx-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAUxD,OAAO,KAAK,EACV,cAAc,EAEd,OAAO,EACP,aAAa,EAEd,MAAM,cAAc,CAAC;AAEtB,wDAAwD;AACxD,MAAM,WAAW,wBAAwB;IACvC,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/D,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAqB,CAAC;AAelG;;;;GAIG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmC;IACpE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,WAAW,CAAK;gBAEZ,OAAO,GAAE,wBAA6B;IAU5C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAe1E,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBzF;;;;;OAKG;IACG,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAUpD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK/C,sFAAsF;IACtF,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAOlE,gDAAgD;IAChD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAI3D,6CAA6C;IAC7C,WAAW,IAAI,SAAS,MAAM,EAAE;IAIhC,OAAO,CAAC,WAAW;CASpB"}
@@ -0,0 +1,405 @@
1
+ /**
2
+ * `SlidesRuntime` that emits a .pptx file to disk via pptxgenjs.
3
+ *
4
+ * The brand visuals (colors, layout, typography sizes) are preserved
5
+ * end-to-end. Fonts that aren't installed on the viewer's machine get
6
+ * substituted via the brand-supplied substitution table (see "Font
7
+ * substitution" below).
8
+ *
9
+ * # Known limitations
10
+ *
11
+ * - **Re-fill against an existing .pptx.** Not supported; the manifest is
12
+ * held in memory only.
13
+ * - **Master templates.** PPTX has no master-ref concept here; the
14
+ * `masterRef` argument to `createDeckFromMaster` is recorded as the
15
+ * presentation's subject metadata and otherwise ignored.
16
+ *
17
+ * # Font substitution
18
+ *
19
+ * PPTX cannot embed fonts. Template-specific families (e.g., a brand's display
20
+ * face) may not be installed on the viewer's machine. The runtime accepts a
21
+ * `fontSubstitution` map keyed by the literal font name (as the reconciler
22
+ * emits it after role resolution) → the family name to write into the
23
+ * .pptx file. Anything not in the map passes through unchanged.
24
+ *
25
+ * The substrate ships an empty default. Brands provide their own table
26
+ * (see `@sanity-labs/slides` for an example). Use `onUnknownFont` if you want
27
+ * to surface diagnostics for unmapped families.
28
+ *
29
+ * # Manifest persistence
30
+ *
31
+ * Held in memory and accessible via `getManifest()`. Persistence into the
32
+ * .pptx file itself (custom XML / hidden shape) is not implemented; re-fill
33
+ * against a previously-generated .pptx is out of scope for this runtime.
34
+ */
35
+ import { promises as fs, readFileSync } from 'node:fs';
36
+ import * as path from 'node:path';
37
+ import { Resvg } from '@resvg/resvg-js';
38
+ import PptxGenJS from 'pptxgenjs';
39
+ import { hexToPptxColor, translateOpsToPptx, } from './op-translator-pptx.js';
40
+ /**
41
+ * Default font substitution table for the substrate: empty.
42
+ *
43
+ * PPTX cannot embed fonts. Template packages ship their own substitution table
44
+ * (see `@sanity-labs/slides` for an example) and pass it via
45
+ * `PptxSlidesRuntimeOptions.fontSubstitution`. The substrate stays
46
+ * brand-agnostic.
47
+ */
48
+ export const DEFAULT_PPTX_FONT_SUBSTITUTION = Object.freeze({});
49
+ /**
50
+ * A `SlidesRuntime` that emits .pptx files via pptxgenjs.
51
+ *
52
+ * See module docstring for limitations and font-substitution behavior.
53
+ */
54
+ export class PptxSlidesRuntime {
55
+ outputDir;
56
+ fontSubstitution;
57
+ onUnknownFont;
58
+ author;
59
+ company;
60
+ decks = new Map();
61
+ deckCounter = 0;
62
+ constructor(options = {}) {
63
+ this.outputDir = options.outputDir ?? process.cwd();
64
+ this.fontSubstitution = options.fontSubstitution ?? DEFAULT_PPTX_FONT_SUBSTITUTION;
65
+ this.onUnknownFont = options.onUnknownFont ?? null;
66
+ this.author = options.author ?? 'react-pptx';
67
+ this.company = options.company;
68
+ }
69
+ // -- SlidesRuntime --------------------------------------------------------
70
+ async applyOps(deckId, ops) {
71
+ const deck = this.requireDeck(deckId);
72
+ const batch = translateOpsToPptx(ops, {
73
+ fontSubstitution: this.fontSubstitution,
74
+ onUnknownFont: this.onUnknownFont,
75
+ });
76
+ applyBatchToPresentation(deck, batch);
77
+ deck.revision += 1;
78
+ const createdObjectIds = {};
79
+ for (const id of batch.createdObjectIds) {
80
+ createdObjectIds[id] = id;
81
+ }
82
+ return { createdObjectIds, revisionId: `pptx-rev-${deck.revision}` };
83
+ }
84
+ async createDeckFromMaster(masterRef, title) {
85
+ // PPTX has no master-ref concept; `masterRef` is informational only,
86
+ // recorded for diagnostic purposes. We initialize a blank 16:9
87
+ // presentation matching the substrate's default canvas.
88
+ const pres = new PptxGenJS();
89
+ pres.layout = 'LAYOUT_WIDE'; // 13.333" x 7.5" — matches CANVAS_16_9 (960×540 pt)
90
+ pres.title = title;
91
+ pres.author = this.author;
92
+ if (this.company !== undefined)
93
+ pres.company = this.company;
94
+ // Tag the presentation so curious file inspectors know what produced it.
95
+ pres.subject = `Generated by react-pptx (template: ${masterRef})`;
96
+ this.deckCounter += 1;
97
+ const deckId = `pptx-deck-${this.deckCounter}`;
98
+ this.decks.set(deckId, {
99
+ title,
100
+ pres,
101
+ slides: new Map(),
102
+ revision: 0,
103
+ });
104
+ return { deckId };
105
+ }
106
+ // -- SlidesRuntime (continued) --------------------------------------------
107
+ /**
108
+ * Serialize the in-memory presentation for a deck to a .pptx file.
109
+ *
110
+ * Returns the absolute path to the generated file. The filename is
111
+ * derived from the deck title (sanitized) with a `.pptx` extension.
112
+ */
113
+ async write(deckId) {
114
+ const deck = this.requireDeck(deckId);
115
+ await fs.mkdir(this.outputDir, { recursive: true });
116
+ const filename = sanitizeFilename(deck.title);
117
+ const filePath = path.join(this.outputDir, `${filename}.pptx`);
118
+ const buffer = (await deck.pres.write({ outputType: 'nodebuffer' }));
119
+ await fs.writeFile(filePath, buffer);
120
+ return { filePath };
121
+ }
122
+ async toBuffer(deckId) {
123
+ const deck = this.requireDeck(deckId);
124
+ return (await deck.pres.write({ outputType: 'nodebuffer' }));
125
+ }
126
+ /** Attach a manifest to a deck (held in memory; not embedded into the .pptx file). */
127
+ attachManifest(deckId, manifest) {
128
+ const deck = this.requireDeck(deckId);
129
+ deck.manifest = manifest;
130
+ }
131
+ // -- PPTX-specific extras -------------------------------------------------
132
+ /** Retrieve a deck's manifest (if attached). */
133
+ getManifest(deckId) {
134
+ return this.decks.get(deckId)?.manifest;
135
+ }
136
+ /** List deck IDs the runtime is tracking. */
137
+ listDeckIds() {
138
+ return [...this.decks.keys()];
139
+ }
140
+ requireDeck(deckId) {
141
+ const deck = this.decks.get(deckId);
142
+ if (!deck) {
143
+ throw new Error(`PptxSlidesRuntime: deck "${deckId}" does not exist. Create it via createDeckFromMaster() first.`);
144
+ }
145
+ return deck;
146
+ }
147
+ }
148
+ // ---------------------------------------------------------------------------
149
+ // Apply translated batch to a pptxgenjs Presentation.
150
+ // ---------------------------------------------------------------------------
151
+ /** Apply a translated batch to a pptxgenjs presentation. */
152
+ const applyBatchToPresentation = (deck, batch) => {
153
+ for (const slideSpec of batch.slides) {
154
+ if (deck.slides.has(slideSpec.slideId)) {
155
+ throw new Error(`PptxSlidesRuntime: slide "${slideSpec.slideId}" already exists.`);
156
+ }
157
+ const slide = deck.pres.addSlide();
158
+ deck.slides.set(slideSpec.slideId, slide);
159
+ }
160
+ for (const obj of batch.objects) {
161
+ const slide = deck.slides.get(obj.slideId);
162
+ if (!slide) {
163
+ throw new Error(`PptxSlidesRuntime: object "${obj.objectId}" references unknown slide "${obj.slideId}".`);
164
+ }
165
+ emitObjectToSlide(slide, obj);
166
+ }
167
+ };
168
+ const emitObjectToSlide = (slide, obj) => {
169
+ switch (obj.kind) {
170
+ case 'text':
171
+ emitText(slide, obj);
172
+ return;
173
+ case 'rectangle':
174
+ emitRectangle(slide, obj);
175
+ return;
176
+ case 'image':
177
+ emitImage(slide, obj);
178
+ return;
179
+ }
180
+ };
181
+ const emitText = (slide, obj) => {
182
+ const positionOpts = positionToPptx(obj.position);
183
+ // If the text is empty AND we have a fill, emit a sibling rectangle for
184
+ // the background. pptxgenjs's `addText` with no text won't emit a fill
185
+ // reliably across renderers; `addShape` does.
186
+ if (obj.text.length === 0 && obj.fill !== undefined) {
187
+ slide.addShape('rect', { ...positionOpts, fill: { color: obj.fill }, line: { type: 'none' } });
188
+ return;
189
+ }
190
+ if (obj.text.length === 0 && obj.fill === undefined) {
191
+ // Empty text box, no fill — nothing to render. Skip; mirrors the
192
+ // FakeSlidesRuntime (which keeps the shape but the live deck has no
193
+ // visible artifact).
194
+ return;
195
+ }
196
+ const textProps = composeTextSegments(obj);
197
+ const opts = {
198
+ ...positionOpts,
199
+ ...(obj.fill !== undefined ? { fill: { color: obj.fill } } : {}),
200
+ ...paragraphStyleToPptx(obj.paragraphStyle),
201
+ // Default vertical alignment to `top` so text starts at the top of the
202
+ // box.
203
+ valign: paragraphValignFromStyle(obj.paragraphStyle) ?? 'top',
204
+ // Margin 0 — the substrate emits exact rects; pptxgenjs's default text
205
+ // box padding would offset the visible text.
206
+ margin: 0,
207
+ // `fit: 'shrink'` lets PowerPoint shrink overflowing text rather than
208
+ // clipping.
209
+ fit: 'shrink',
210
+ };
211
+ slide.addText(textProps, opts);
212
+ };
213
+ const emitRectangle = (slide, obj) => {
214
+ const opts = {
215
+ ...positionToPptx(obj.position),
216
+ ...(obj.fill !== undefined ? { fill: { color: obj.fill } } : {}),
217
+ };
218
+ if (obj.outlineColor !== undefined || obj.outlineWeight !== undefined) {
219
+ const line = {};
220
+ if (obj.outlineColor !== undefined)
221
+ line.color = obj.outlineColor;
222
+ if (obj.outlineWeight !== undefined)
223
+ line.width = obj.outlineWeight;
224
+ opts.line = line;
225
+ }
226
+ else {
227
+ // Explicit `type: 'none'` so PowerPoint doesn't draw the default 1pt
228
+ // outline (visually noticeable on full-bleed backgrounds).
229
+ opts.line = { type: 'none' };
230
+ }
231
+ slide.addShape(obj.shape, opts);
232
+ };
233
+ const emitImage = (slide, obj) => {
234
+ const opts = {
235
+ ...positionToPptx(obj.position),
236
+ ...(obj.altText !== undefined ? { altText: obj.altText } : {}),
237
+ };
238
+ const source = imageSourceForPptx(obj.url);
239
+ if (source.kind === 'data') {
240
+ opts.data = source.value;
241
+ }
242
+ else {
243
+ opts.path = source.value;
244
+ }
245
+ slide.addImage(opts);
246
+ };
247
+ const imageSourceForPptx = (url) => {
248
+ if (isSvgDataUri(url)) {
249
+ return { kind: 'data', value: svgToPngDataUri(decodeSvgDataUri(url), 'data URI') };
250
+ }
251
+ if (isLocalSvgPath(url)) {
252
+ const filePath = localSvgPath(url);
253
+ return { kind: 'data', value: svgToPngDataUri(readFileSync(filePath, 'utf8'), filePath) };
254
+ }
255
+ if (url.startsWith('data:')) {
256
+ return { kind: 'data', value: url };
257
+ }
258
+ return { kind: 'path', value: url };
259
+ };
260
+ const isSvgDataUri = (url) => /^data:image\/svg\+xml[;,]/i.test(url);
261
+ const decodeSvgDataUri = (url) => {
262
+ const commaIndex = url.indexOf(',');
263
+ if (commaIndex === -1) {
264
+ throw new Error('PptxSlidesRuntime: invalid SVG data URI; missing comma separator.');
265
+ }
266
+ const meta = url.slice(0, commaIndex).toLowerCase();
267
+ const payload = url.slice(commaIndex + 1);
268
+ return meta.includes(';base64')
269
+ ? Buffer.from(payload, 'base64').toString('utf8')
270
+ : decodeURIComponent(payload);
271
+ };
272
+ const isLocalSvgPath = (url) => !/^https?:\/\//i.test(url) && !url.startsWith('data:') && /\.svg(?:$|[?#])/i.test(url);
273
+ const localSvgPath = (url) => {
274
+ if (url.startsWith('file://')) {
275
+ return new URL(url).pathname;
276
+ }
277
+ const queryIndex = url.search(/[?#]/);
278
+ return queryIndex === -1 ? url : url.slice(0, queryIndex);
279
+ };
280
+ const svgToPngDataUri = (svg, sourceLabel) => {
281
+ try {
282
+ const png = new Resvg(svg).render().asPng();
283
+ return `data:image/png;base64,${Buffer.from(png).toString('base64')}`;
284
+ }
285
+ catch (err) {
286
+ const message = err instanceof Error ? err.message : String(err);
287
+ throw new Error(`PptxSlidesRuntime: failed to rasterize SVG image (${sourceLabel}): ${message}`);
288
+ }
289
+ };
290
+ // ---------------------------------------------------------------------------
291
+ // Style adapters
292
+ // ---------------------------------------------------------------------------
293
+ const positionToPptx = (position) => ({
294
+ x: position.x,
295
+ y: position.y,
296
+ w: position.w,
297
+ h: position.h,
298
+ });
299
+ /**
300
+ * Compose the per-range text-style spans into a flat array of pptxgenjs
301
+ * `TextProps` segments, applying spans in op order with last-write-wins
302
+ * semantics on overlap.
303
+ */
304
+ const composeTextSegments = (obj) => {
305
+ const len = obj.text.length;
306
+ if (len === 0)
307
+ return [];
308
+ // Per-character resolved style.
309
+ const styles = Array.from({ length: len }, () => ({}));
310
+ for (const span of obj.textSpans) {
311
+ const start = Math.max(0, Math.min(len, span.start));
312
+ const end = Math.max(0, Math.min(len, span.end));
313
+ for (let i = start; i < end; i++) {
314
+ styles[i] = { ...styles[i], ...span.style };
315
+ }
316
+ }
317
+ // Coalesce consecutive characters with identical resolved style.
318
+ const segments = [];
319
+ let segStart = 0;
320
+ while (segStart < len) {
321
+ let segEnd = segStart + 1;
322
+ while (segEnd < len && styleEquals(styles[segStart] ?? {}, styles[segEnd] ?? {})) {
323
+ segEnd++;
324
+ }
325
+ segments.push({
326
+ text: obj.text.slice(segStart, segEnd),
327
+ options: textStyleToPptx(styles[segStart] ?? {}),
328
+ });
329
+ segStart = segEnd;
330
+ }
331
+ return segments;
332
+ };
333
+ const styleEquals = (a, b) => {
334
+ const keysA = Object.keys(a);
335
+ const keysB = Object.keys(b);
336
+ if (keysA.length !== keysB.length)
337
+ return false;
338
+ return keysA.every((k) => a[k] === b[k]);
339
+ };
340
+ const textStyleToPptx = (style) => {
341
+ const opts = {};
342
+ if (style.fontFamily !== undefined)
343
+ opts.fontFace = style.fontFamily;
344
+ if (style.fontSize !== undefined)
345
+ opts.fontSize = style.fontSize;
346
+ if (style.bold !== undefined)
347
+ opts.bold = style.bold;
348
+ if (style.italic !== undefined)
349
+ opts.italic = style.italic;
350
+ if (style.underline !== undefined && style.underline) {
351
+ opts.underline = { style: 'sng' };
352
+ }
353
+ if (style.foregroundColor !== undefined)
354
+ opts.color = hexToPptxColor(style.foregroundColor);
355
+ if (style.backgroundColor !== undefined)
356
+ opts.highlight = hexToPptxColor(style.backgroundColor);
357
+ return opts;
358
+ };
359
+ const paragraphStyleToPptx = (style) => {
360
+ if (!style)
361
+ return {};
362
+ const out = {};
363
+ if (style.alignment !== undefined) {
364
+ out.align = pptxAlign(style.alignment);
365
+ }
366
+ if (style.lineSpacing !== undefined) {
367
+ // pptxgenjs's `lineSpacingMultiple` matches the substrate's
368
+ // multiplier (1.0 = normal). 1:1.
369
+ out.lineSpacingMultiple = style.lineSpacing;
370
+ }
371
+ if (style.spaceAbove !== undefined)
372
+ out.paraSpaceBefore = style.spaceAbove;
373
+ if (style.spaceBelow !== undefined)
374
+ out.paraSpaceAfter = style.spaceBelow;
375
+ return out;
376
+ };
377
+ const pptxAlign = (alignment) => {
378
+ switch (alignment) {
379
+ case 'START':
380
+ return 'left';
381
+ case 'CENTER':
382
+ return 'center';
383
+ case 'END':
384
+ return 'right';
385
+ case 'JUSTIFIED':
386
+ return 'justify';
387
+ }
388
+ };
389
+ const paragraphValignFromStyle = (_style) => {
390
+ // The substrate's `ParagraphStyle` doesn't carry vertical alignment;
391
+ // returning undefined preserves pptxgenjs's default. Reserved for a
392
+ // future v-align addition.
393
+ return undefined;
394
+ };
395
+ const sanitizeFilename = (title) => {
396
+ // Replace anything non-alphanumeric / non-dash / non-underscore with `-`;
397
+ // collapse runs; trim leading/trailing dashes. Falls back to "deck" if
398
+ // the result would be empty.
399
+ const cleaned = title
400
+ .replace(/[^a-zA-Z0-9_-]+/g, '-')
401
+ .replace(/-+/g, '-')
402
+ .replace(/^-|-$/g, '');
403
+ return cleaned.length > 0 ? cleaned : 'deck';
404
+ };
405
+ //# sourceMappingURL=pptx-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pptx-runtime.js","sourceRoot":"","sources":["../../src/core/pptx-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * The reconciler — walks a React element tree and emits a sequence of
3
+ * `SlideOp`s plus a generation manifest.
4
+ *
5
+ * # Why a custom walker (not `react-reconciler`)
6
+ *
7
+ * The natural reach is for the `react-reconciler` package — it's what
8
+ * react-three-fiber, react-pdf, and Ink use. But the constraints of this
9
+ * surface make a custom walker a strictly simpler fit:
10
+ *
11
+ * 1. **No state.** Template components are pure. No hooks, no effects, no refs.
12
+ * The whole reason to use `react-reconciler` is the scheduler + commit
13
+ * phases that make state-driven re-renders work. We don't have state.
14
+ * 2. **Write-once.** A `renderToOps()` call produces one batch and exits.
15
+ * There's no incremental update model to honor.
16
+ * 3. **No host node mutation.** Output is an immutable op list. The 30+ host-
17
+ * config methods `react-reconciler` requires (`appendChild`,
18
+ * `removeChild`, `commitMount`, etc.) all collapse to "push to an array."
19
+ * 4. **Tree shape is shallow and known.** `Slide` → `Box` | `Image` →
20
+ * text runs. The walker can encode this structure directly with much
21
+ * better error messages than a generic reconciler would surface.
22
+ *
23
+ * If we ever add state-driven re-renders (probably never; the docs commit to
24
+ * forward-only generation), we can swap in `react-reconciler` behind the same
25
+ * `renderToOps` signature. The signature is the contract.
26
+ *
27
+ * # What the walker does
28
+ *
29
+ * 1. Resolve function components by invoking them with their props (no hooks).
30
+ * 2. Flatten fragments and arrays.
31
+ * 3. For each `<Slide>`, emit `createSlide`, then walk its children for
32
+ * `<Box>`s and `<Image>`s.
33
+ * 4. For each `<Box>`, emit `createShape` (TEXT_BOX), then — if `fill` is
34
+ * set — `updateShapeProperties` carrying the resolved fill color, then
35
+ * collect its text runs into a single `insertText` + per-run
36
+ * `updateTextStyle` calls.
37
+ * 5. For each `<Image>`, emit `createImage` with the resolved URL and (if
38
+ * no slotId is set) the user-supplied alt-text. Record the artifact
39
+ * reference in the manifest, deduped by identifier.
40
+ *
41
+ * Errors throw with paths like "Slide[0] > Box[1] > unexpected child" so the
42
+ * brand-component author has enough information to localize the problem.
43
+ *
44
+ * # Determinism
45
+ *
46
+ * Object IDs are generated from a counter seeded per call. Same input tree →
47
+ * same output ops → same snapshot. This is what makes layer-2 golden tests in
48
+ * `docs/testing-strategy.md` work.
49
+ */
50
+ import { type ReactNode } from 'react';
51
+ import type { ArtifactRef, ReconcileResult, SlotId } from './manifest.js';
52
+ import type { Template } from './template.js';
53
+ import type { SlideOp } from './runtime.js';
54
+ /** Inputs to a single render. */
55
+ export interface RenderToOpsInput {
56
+ /** The React element tree. Top-level should be a `<Slide>` or a fragment of `<Slide>`s. */
57
+ readonly tree: ReactNode;
58
+ /** The template whose tokens are in scope. */
59
+ readonly template: Template;
60
+ /**
61
+ * Target deck ID (re-fill case) or `null` for a new deck.
62
+ *
63
+ * Ops emission doesn't differ between the two today, but the manifest
64
+ * records it, and downstream runtime adapters may use it (e.g., to skip
65
+ * `createSlide` if the deck is being re-filled rather than rebuilt).
66
+ */
67
+ readonly deckId: string | null;
68
+ /**
69
+ * Template artifacts the deck references *in addition to* any artifacts the
70
+ * reconciler itself records by walking `<Image>` primitives. Provided by
71
+ * brand-component code that resolved textures/logos/etc. before invoking
72
+ * the reconciler. The reconciler merges these with the artifacts it
73
+ * discovers, deduping by identifier (last-wins on duplicates within either
74
+ * source).
75
+ */
76
+ readonly artifacts?: readonly ArtifactRef[];
77
+ /**
78
+ * Override for the manifest's `generatedAt` timestamp. Tests pin this to
79
+ * keep snapshots deterministic.
80
+ */
81
+ readonly now?: () => string;
82
+ }
83
+ /** Internal mutable state carried through the walk. */
84
+ interface WalkContext {
85
+ readonly template: Template;
86
+ readonly ops: SlideOp[];
87
+ readonly slots: Map<SlotId, string>;
88
+ /**
89
+ * Template artifacts discovered while walking, keyed by identifier so a single
90
+ * texture or logo referenced from multiple slides only appears once in the
91
+ * manifest. Insertion order is preserved by the underlying Map.
92
+ */
93
+ readonly artifacts: Map<string, ArtifactRef>;
94
+ /** Counter for generating unique slide / shape / image IDs. */
95
+ idCounter: number;
96
+ /** The slide currently being walked, for error context. */
97
+ currentSlideIndex: number;
98
+ /** The box being walked (if any), for error context. */
99
+ currentBoxIndex: number;
100
+ }
101
+ /**
102
+ * Render a React tree to ops + manifest.
103
+ *
104
+ * The function is synchronous: function components are invoked inline, and the
105
+ * walk is purely structural. There's no scheduling, no async boundaries.
106
+ */
107
+ export declare const renderToOps: (input: RenderToOpsInput) => ReconcileResult;
108
+ /** Error thrown by the reconciler with location info pre-formatted. */
109
+ export declare class ReconcilerError extends Error {
110
+ constructor(message: string, ctx: Pick<WalkContext, 'currentSlideIndex' | 'currentBoxIndex'>);
111
+ }
112
+ export {};
113
+ //# sourceMappingURL=reconciler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconciler.d.ts","sourceRoot":"","sources":["../../src/core/reconciler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAiBf,OAAO,KAAK,EAAE,WAAW,EAAsB,eAAe,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC9F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAA4B,OAAO,EAAwB,MAAM,cAAc,CAAC;AAE5F,iCAAiC;AACjC,MAAM,WAAW,gBAAgB;IAC/B,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC5C;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED,uDAAuD;AACvD,UAAU,WAAW;IACnB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAC7C,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,gBAAgB,KAAG,eAsCrD,CAAC;AAqaF,uEAAuE;AACvE,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,EAAE,mBAAmB,GAAG,iBAAiB,CAAC;CAQ7F"}