@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.
- package/README.md +241 -0
- package/SKILL.md +119 -0
- package/dist/cli.d.ts +38 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +386 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/components.d.ts +179 -0
- package/dist/core/components.d.ts.map +1 -0
- package/dist/core/components.js +40 -0
- package/dist/core/components.js.map +1 -0
- package/dist/core/fake-runtime.d.ts +138 -0
- package/dist/core/fake-runtime.d.ts.map +1 -0
- package/dist/core/fake-runtime.js +210 -0
- package/dist/core/fake-runtime.js.map +1 -0
- package/dist/core/font-resolver.d.ts +28 -0
- package/dist/core/font-resolver.d.ts.map +1 -0
- package/dist/core/font-resolver.js +30 -0
- package/dist/core/font-resolver.js.map +1 -0
- package/dist/core/geometry.d.ts +71 -0
- package/dist/core/geometry.d.ts.map +1 -0
- package/dist/core/geometry.js +44 -0
- package/dist/core/geometry.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +20 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/manifest.d.ts +123 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +43 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/op-translator-pptx.d.ts +150 -0
- package/dist/core/op-translator-pptx.d.ts.map +1 -0
- package/dist/core/op-translator-pptx.js +245 -0
- package/dist/core/op-translator-pptx.js.map +1 -0
- package/dist/core/pptx-runtime.d.ts +103 -0
- package/dist/core/pptx-runtime.d.ts.map +1 -0
- package/dist/core/pptx-runtime.js +405 -0
- package/dist/core/pptx-runtime.js.map +1 -0
- package/dist/core/reconciler.d.ts +113 -0
- package/dist/core/reconciler.d.ts.map +1 -0
- package/dist/core/reconciler.js +453 -0
- package/dist/core/reconciler.js.map +1 -0
- package/dist/core/runtime.d.ts +161 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +11 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/core/template.d.ts +32 -0
- package/dist/core/template.d.ts.map +1 -0
- package/dist/core/template.js +3 -0
- package/dist/core/template.js.map +1 -0
- package/dist/dev/auto-examples.d.ts +6 -0
- package/dist/dev/auto-examples.d.ts.map +1 -0
- package/dist/dev/auto-examples.js +79 -0
- package/dist/dev/auto-examples.js.map +1 -0
- package/dist/dev/bin/slides-dev.d.ts +3 -0
- package/dist/dev/bin/slides-dev.d.ts.map +1 -0
- package/dist/dev/bin/slides-dev.js +87 -0
- package/dist/dev/bin/slides-dev.js.map +1 -0
- package/dist/dev/bin/slides-dev.mjs +24 -0
- package/dist/dev/compose-deck.d.ts +18 -0
- package/dist/dev/compose-deck.d.ts.map +1 -0
- package/dist/dev/compose-deck.js +19 -0
- package/dist/dev/compose-deck.js.map +1 -0
- package/dist/dev/deck-viewer.d.ts +19 -0
- package/dist/dev/deck-viewer.d.ts.map +1 -0
- package/dist/dev/deck-viewer.js +237 -0
- package/dist/dev/deck-viewer.js.map +1 -0
- package/dist/dev/dev-server/client/entry.d.ts +2 -0
- package/dist/dev/dev-server/client/entry.d.ts.map +1 -0
- package/dist/dev/dev-server/client/entry.js +12 -0
- package/dist/dev/dev-server/client/entry.js.map +1 -0
- package/dist/dev/dev-server/output.d.ts +8 -0
- package/dist/dev/dev-server/output.d.ts.map +1 -0
- package/dist/dev/dev-server/output.js +32 -0
- package/dist/dev/dev-server/output.js.map +1 -0
- package/dist/dev/dev-server/server-only-stub.d.ts +7 -0
- package/dist/dev/dev-server/server-only-stub.d.ts.map +1 -0
- package/dist/dev/dev-server/server-only-stub.js +12 -0
- package/dist/dev/dev-server/server-only-stub.js.map +1 -0
- package/dist/dev/dev-server/start.d.ts +14 -0
- package/dist/dev/dev-server/start.d.ts.map +1 -0
- package/dist/dev/dev-server/start.js +135 -0
- package/dist/dev/dev-server/start.js.map +1 -0
- package/dist/dev/index.d.ts +5 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/lib/cn.d.ts +3 -0
- package/dist/dev/lib/cn.d.ts.map +1 -0
- package/dist/dev/lib/cn.js +3 -0
- package/dist/dev/lib/cn.js.map +1 -0
- package/dist/dev/slide-canvas.d.ts +12 -0
- package/dist/dev/slide-canvas.d.ts.map +1 -0
- package/dist/dev/slide-canvas.js +123 -0
- package/dist/dev/slide-canvas.js.map +1 -0
- package/dist/dev/styles.css +37 -0
- package/dist/dev/ui/icon-button.d.ts +12 -0
- package/dist/dev/ui/icon-button.d.ts.map +1 -0
- package/dist/dev/ui/icon-button.js +6 -0
- package/dist/dev/ui/icon-button.js.map +1 -0
- package/dist/dev/ui/kbd.d.ts +6 -0
- package/dist/dev/ui/kbd.d.ts.map +1 -0
- package/dist/dev/ui/kbd.js +4 -0
- package/dist/dev/ui/kbd.js.map +1 -0
- package/dist/dev/ui/text-button.d.ts +10 -0
- package/dist/dev/ui/text-button.d.ts.map +1 -0
- package/dist/dev/ui/text-button.js +6 -0
- package/dist/dev/ui/text-button.js.map +1 -0
- package/dist/dev/url-state.d.ts +7 -0
- package/dist/dev/url-state.d.ts.map +1 -0
- package/dist/dev/url-state.js +13 -0
- package/dist/dev/url-state.js.map +1 -0
- package/dist/dev/use-keyboard-nav.d.ts +17 -0
- package/dist/dev/use-keyboard-nav.d.ts.map +1 -0
- package/dist/dev/use-keyboard-nav.js +53 -0
- package/dist/dev/use-keyboard-nav.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/errors.d.ts +57 -0
- package/dist/mcp/errors.d.ts.map +1 -0
- package/dist/mcp/errors.js +44 -0
- package/dist/mcp/errors.js.map +1 -0
- package/dist/mcp/index.d.ts +29 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +29 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/naming.d.ts +37 -0
- package/dist/mcp/naming.d.ts.map +1 -0
- package/dist/mcp/naming.js +43 -0
- package/dist/mcp/naming.js.map +1 -0
- package/dist/mcp/render.d.ts +45 -0
- package/dist/mcp/render.d.ts.map +1 -0
- package/dist/mcp/render.js +77 -0
- package/dist/mcp/render.js.map +1 -0
- package/dist/mcp/schema.d.ts +54 -0
- package/dist/mcp/schema.d.ts.map +1 -0
- package/dist/mcp/schema.js +55 -0
- package/dist/mcp/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +63 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +196 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/scaffold/index.d.ts +39 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +84 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/template-base/README.md +134 -0
- package/dist/scaffold/template-base/_gitignore +4 -0
- package/dist/scaffold/template-base/package.json +35 -0
- package/dist/scaffold/template-base/src/components/Cover.tsx +30 -0
- package/dist/scaffold/template-base/src/index.ts +27 -0
- package/dist/scaffold/template-base/src/preview.tsx +9 -0
- package/dist/scaffold/template-base/tsconfig.build.json +10 -0
- package/dist/scaffold/template-base/tsconfig.json +18 -0
- package/package.json +164 -0
- package/src/__tests__/fixtures/test-template/index.tsx +77 -0
- package/src/__tests__/pptx-mcp.test.ts +85 -0
- package/src/__tests__/pptx-smoke.test.ts +45 -0
- package/src/__tests__/preview.test.ts +28 -0
- package/src/cli.ts +426 -0
- package/src/core/__snapshots__/reconciler.test.ts.snap +320 -0
- package/src/core/components.test.ts +57 -0
- package/src/core/components.ts +196 -0
- package/src/core/fake-runtime.test.ts +174 -0
- package/src/core/fake-runtime.ts +302 -0
- package/src/core/font-resolver.ts +46 -0
- package/src/core/geometry.test.ts +58 -0
- package/src/core/geometry.ts +91 -0
- package/src/core/index.ts +69 -0
- package/src/core/manifest.test.ts +33 -0
- package/src/core/manifest.ts +150 -0
- package/src/core/op-translator-pptx.test.ts +204 -0
- package/src/core/op-translator-pptx.ts +365 -0
- package/src/core/pptx-runtime.test.ts +137 -0
- package/src/core/pptx-runtime.ts +504 -0
- package/src/core/reconciler.test.ts +644 -0
- package/src/core/reconciler.ts +603 -0
- package/src/core/runtime.ts +150 -0
- package/src/core/template.test.ts +136 -0
- package/src/core/template.ts +37 -0
- package/src/dev/auto-examples.ts +89 -0
- package/src/dev/bin/slides-dev.mjs +24 -0
- package/src/dev/bin/slides-dev.ts +101 -0
- package/src/dev/compose-deck.test.ts +68 -0
- package/src/dev/compose-deck.ts +40 -0
- package/src/dev/deck-viewer.tsx +677 -0
- package/src/dev/dev-server/client/entry.tsx +15 -0
- package/src/dev/dev-server/client/index.html +24 -0
- package/src/dev/dev-server/output.ts +37 -0
- package/src/dev/dev-server/server-only-stub.ts +12 -0
- package/src/dev/dev-server/start.ts +155 -0
- package/src/dev/index.ts +4 -0
- package/src/dev/lib/cn.ts +3 -0
- package/src/dev/slide-canvas.test.tsx +66 -0
- package/src/dev/slide-canvas.tsx +170 -0
- package/src/dev/styles.css +37 -0
- package/src/dev/ui/icon-button.tsx +31 -0
- package/src/dev/ui/kbd.tsx +20 -0
- package/src/dev/ui/text-button.tsx +31 -0
- package/src/dev/url-state.test.ts +22 -0
- package/src/dev/url-state.ts +17 -0
- package/src/dev/use-keyboard-nav.ts +64 -0
- package/src/index.ts +17 -0
- package/src/mcp/errors.test.ts +51 -0
- package/src/mcp/errors.ts +76 -0
- package/src/mcp/index.ts +45 -0
- package/src/mcp/naming.test.ts +39 -0
- package/src/mcp/naming.ts +49 -0
- package/src/mcp/render.ts +110 -0
- package/src/mcp/schema.test.ts +86 -0
- package/src/mcp/schema.ts +93 -0
- package/src/mcp/server.test.ts +309 -0
- package/src/mcp/server.ts +276 -0
- package/src/scaffold/index.ts +102 -0
- package/src/scaffold/template-base/README.md +134 -0
- package/src/scaffold/template-base/_gitignore +4 -0
- package/src/scaffold/template-base/package.json +35 -0
- package/src/scaffold/template-base/src/components/Cover.tsx +30 -0
- package/src/scaffold/template-base/src/index.ts +27 -0
- package/src/scaffold/template-base/src/preview.tsx +9 -0
- package/src/scaffold/template-base/tsconfig.build.json +10 -0
- package/src/scaffold/template-base/tsconfig.json +18 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generation manifest — what the reconciler emits *alongside* the SlideOps.
|
|
3
|
+
*
|
|
4
|
+
* The manifest captures everything needed to:
|
|
5
|
+
*
|
|
6
|
+
* 1. Re-fill the deck later (`replaceAllText` against named slots, preserving
|
|
7
|
+
* manual polish on the rest of the deck).
|
|
8
|
+
* 2. Audit which decks were AI-generated and from which template version.
|
|
9
|
+
* 3. Detect drift between resolved-at-generation-time brand artifacts
|
|
10
|
+
* (textures, logos, master templates) and their current upstream state.
|
|
11
|
+
*
|
|
12
|
+
* Template-agnostic: any brand package reads it the same way; the substrate
|
|
13
|
+
* doesn't encode brand-specific slot keys.
|
|
14
|
+
*/
|
|
15
|
+
import type { SlideOp } from './runtime.js';
|
|
16
|
+
/**
|
|
17
|
+
* A stable identity for a slot in a generated deck.
|
|
18
|
+
*
|
|
19
|
+
* Slot IDs are encoded into the Slides shape's *alt text* at generation time —
|
|
20
|
+
* the alt-text-as-ID pattern documented in `generation-model.md`. This is what
|
|
21
|
+
* makes in-place re-fill robust: when re-running with new data, we look up
|
|
22
|
+
* shapes by the alt-text token rather than by index/position (which can drift
|
|
23
|
+
* if the marketer moved or duplicated a shape).
|
|
24
|
+
*
|
|
25
|
+
* Format: `<componentName>:<slotName>` (colon-separated, both segments are
|
|
26
|
+
* `[a-z0-9-]+`). E.g., `cover:title`, `two-column:left-body`.
|
|
27
|
+
*
|
|
28
|
+
* Restricting the character set keeps the alt-text round-trippable across
|
|
29
|
+
* backends, some of which are picky about non-ASCII content.
|
|
30
|
+
*/
|
|
31
|
+
export type SlotId = `${string}:${string}`;
|
|
32
|
+
/**
|
|
33
|
+
* Encode a SlotId into the alt-text representation written on a Slides shape.
|
|
34
|
+
*
|
|
35
|
+
* The wrapper prefix `rgs-slot:` is what `decodeAltText` keys off when reading
|
|
36
|
+
* an existing deck back. Anything else in the alt-text field — a marketer's
|
|
37
|
+
* accessibility caption, an empty string — is treated as "not a managed slot."
|
|
38
|
+
*/
|
|
39
|
+
export declare const encodeAltText: (slotId: SlotId) => string;
|
|
40
|
+
/**
|
|
41
|
+
* Decode a Slides shape's alt-text field back into a SlotId, or return
|
|
42
|
+
* `undefined` if the alt-text isn't a managed slot tag.
|
|
43
|
+
*
|
|
44
|
+
* Mirror of `encodeAltText`. Used at re-fill time to identify which shapes
|
|
45
|
+
* the system owns.
|
|
46
|
+
*/
|
|
47
|
+
export declare const decodeAltText: (altText: string | undefined | null) => SlotId | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* The reconciler's record of where each slot landed in the emitted ops.
|
|
50
|
+
*
|
|
51
|
+
* Keys are SlotIds. Values reference the Slides object IDs (`shapeId` /
|
|
52
|
+
* `imageId`) the reconciler assigned at op-emission time. Re-fill resolves a
|
|
53
|
+
* SlotId → object ID via this map, then issues a `replaceAllText` op against
|
|
54
|
+
* that object.
|
|
55
|
+
*/
|
|
56
|
+
export type SlotRegistry = Readonly<Record<SlotId, string>>;
|
|
57
|
+
/**
|
|
58
|
+
* A reference to a brand artifact (texture, logo, font, master template, image)
|
|
59
|
+
* the deck depends on at generation time.
|
|
60
|
+
*
|
|
61
|
+
* Recorded in the manifest so re-fill can detect 404s and (optionally)
|
|
62
|
+
* verify content integrity via `contentHash`. The substrate type doesn't
|
|
63
|
+
* fetch or validate — it just records what the brand resolver produced.
|
|
64
|
+
*/
|
|
65
|
+
export interface ArtifactRef {
|
|
66
|
+
/** What kind of artifact this is. Drives any artifact-type-specific re-fill behavior. */
|
|
67
|
+
readonly type: 'texture' | 'logo' | 'font' | 'master-template' | 'image';
|
|
68
|
+
/**
|
|
69
|
+
* Template-meaningful identifier, e.g., `"dots-grid-base-medium-dark"`. Stable
|
|
70
|
+
* across generations; the brand's resolver maps this to a current URL.
|
|
71
|
+
*/
|
|
72
|
+
readonly identifier: string;
|
|
73
|
+
/** The URL the resolver produced for this artifact at generation time. */
|
|
74
|
+
readonly resolvedUrl: string;
|
|
75
|
+
/** ISO-8601 timestamp the resolution occurred. */
|
|
76
|
+
readonly resolvedAt: string;
|
|
77
|
+
/**
|
|
78
|
+
* Optional SHA-256 (or other) of the artifact bytes, hex-encoded.
|
|
79
|
+
*
|
|
80
|
+
* The reconciler does not compute or verify this; it's a forward-compat
|
|
81
|
+
* slot brands can populate when they want stricter integrity checks at
|
|
82
|
+
* re-fill time.
|
|
83
|
+
*/
|
|
84
|
+
readonly contentHash?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The full record the reconciler emits alongside its SlideOp stream.
|
|
88
|
+
*
|
|
89
|
+
* Persisted somewhere accessible at re-fill time. The persistence strategy
|
|
90
|
+
* is a runtime concern (each backend picks its own — file metadata, a
|
|
91
|
+
* sidecar JSON, embedded XML, etc.); this type just defines the shape.
|
|
92
|
+
*/
|
|
93
|
+
export interface GenerationManifest {
|
|
94
|
+
/** Schema version of the manifest itself. Bump on breaking changes to this shape. */
|
|
95
|
+
readonly manifestVersion: '1';
|
|
96
|
+
/** The substrate that produced this manifest. Locked literal for forward compat. */
|
|
97
|
+
readonly generatedBy: 'react-pptx';
|
|
98
|
+
/** ISO-8601 timestamp of generation. */
|
|
99
|
+
readonly generatedAt: string;
|
|
100
|
+
/** Name of the template whose components produced this deck (`template.name`). */
|
|
101
|
+
readonly templateName: string;
|
|
102
|
+
/** ID of the deck this manifest applies to. `null` if the deck wasn't created yet. */
|
|
103
|
+
readonly deckId: string | null;
|
|
104
|
+
/** Map of SlotId → Slides object ID, for re-fill lookups. */
|
|
105
|
+
readonly slots: SlotRegistry;
|
|
106
|
+
/** Template artifacts the deck depends on. See `ArtifactRef`. */
|
|
107
|
+
readonly artifacts: readonly ArtifactRef[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The full output of a single `renderToOps` call.
|
|
111
|
+
*
|
|
112
|
+
* Carries both the op stream (what to send to the runtime) and the manifest
|
|
113
|
+
* (what to persist). They're emitted together because they're computed
|
|
114
|
+
* together — the slot map can't exist without the ops that created the
|
|
115
|
+
* shapes it points at.
|
|
116
|
+
*/
|
|
117
|
+
export interface ReconcileResult {
|
|
118
|
+
/** The op stream, in the order the reconciler emitted them. */
|
|
119
|
+
readonly ops: readonly SlideOp[];
|
|
120
|
+
/** The generation manifest. */
|
|
121
|
+
readonly manifest: GenerationManifest;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/core/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,MAAM,EAAE,CAAC;AAK3C;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,MAAuC,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,MAAM,GAAG,SAO3E,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,iBAAiB,GAAG,OAAO,CAAC;IAEzE;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,0EAA0E;IAC1E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,kDAAkD;IAClD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,QAAQ,CAAC,eAAe,EAAE,GAAG,CAAC;IAE9B,oFAAoF;IACpF,QAAQ,CAAC,WAAW,EAAE,YAAY,CAAC;IAEnC,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,kFAAkF;IAClF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAE9B,sFAAsF;IACtF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAE7B,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,SAAS,WAAW,EAAE,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,SAAS,OAAO,EAAE,CAAC;IACjC,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;CACvC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generation manifest — what the reconciler emits *alongside* the SlideOps.
|
|
3
|
+
*
|
|
4
|
+
* The manifest captures everything needed to:
|
|
5
|
+
*
|
|
6
|
+
* 1. Re-fill the deck later (`replaceAllText` against named slots, preserving
|
|
7
|
+
* manual polish on the rest of the deck).
|
|
8
|
+
* 2. Audit which decks were AI-generated and from which template version.
|
|
9
|
+
* 3. Detect drift between resolved-at-generation-time brand artifacts
|
|
10
|
+
* (textures, logos, master templates) and their current upstream state.
|
|
11
|
+
*
|
|
12
|
+
* Template-agnostic: any brand package reads it the same way; the substrate
|
|
13
|
+
* doesn't encode brand-specific slot keys.
|
|
14
|
+
*/
|
|
15
|
+
/** The token we wrap a SlotId in when storing it as alt-text on a Slides shape. */
|
|
16
|
+
const ALT_TEXT_PREFIX = 'rgs-slot:';
|
|
17
|
+
/**
|
|
18
|
+
* Encode a SlotId into the alt-text representation written on a Slides shape.
|
|
19
|
+
*
|
|
20
|
+
* The wrapper prefix `rgs-slot:` is what `decodeAltText` keys off when reading
|
|
21
|
+
* an existing deck back. Anything else in the alt-text field — a marketer's
|
|
22
|
+
* accessibility caption, an empty string — is treated as "not a managed slot."
|
|
23
|
+
*/
|
|
24
|
+
export const encodeAltText = (slotId) => `${ALT_TEXT_PREFIX}${slotId}`;
|
|
25
|
+
/**
|
|
26
|
+
* Decode a Slides shape's alt-text field back into a SlotId, or return
|
|
27
|
+
* `undefined` if the alt-text isn't a managed slot tag.
|
|
28
|
+
*
|
|
29
|
+
* Mirror of `encodeAltText`. Used at re-fill time to identify which shapes
|
|
30
|
+
* the system owns.
|
|
31
|
+
*/
|
|
32
|
+
export const decodeAltText = (altText) => {
|
|
33
|
+
if (typeof altText !== 'string')
|
|
34
|
+
return undefined;
|
|
35
|
+
if (!altText.startsWith(ALT_TEXT_PREFIX))
|
|
36
|
+
return undefined;
|
|
37
|
+
const candidate = altText.slice(ALT_TEXT_PREFIX.length);
|
|
38
|
+
// Must look like `a:b` with both halves non-empty and matching the safe charset.
|
|
39
|
+
if (!/^[a-z0-9-]+:[a-z0-9-]+$/.test(candidate))
|
|
40
|
+
return undefined;
|
|
41
|
+
return candidate;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/core/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAqBH,mFAAmF;AACnF,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,GAAG,eAAe,GAAG,MAAM,EAAE,CAAC;AAEvF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,OAAkC,EAAsB,EAAE;IACtF,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAClD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IACxD,iFAAiF;IACjF,IAAI,CAAC,yBAAyB,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACjE,OAAO,SAAmB,CAAC;AAC7B,CAAC,CAAC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure translation from `SlideOp` (the reconciler's typed intent) to a
|
|
3
|
+
* compact intermediate representation that pptxgenjs can consume.
|
|
4
|
+
*
|
|
5
|
+
* # Why not emit pptxgenjs calls directly
|
|
6
|
+
*
|
|
7
|
+
* pptxgenjs is stateful: `slide.addText(...)` mutates the Slide. The
|
|
8
|
+
* `SlideOp` stream is sequential — `createShape` is followed by
|
|
9
|
+
* `insertText`, `updateTextStyle`, etc. — so a one-pass-per-op translator
|
|
10
|
+
* would have to issue `addText` immediately on `createShape`, then mutate
|
|
11
|
+
* the already-added text on style ops, which pptxgenjs doesn't support.
|
|
12
|
+
*
|
|
13
|
+
* Instead, this module collapses an op stream into an array of
|
|
14
|
+
* `PptxObject` records — one per slide-level object. The runtime then walks
|
|
15
|
+
* the records and emits pptxgenjs calls in one shot per shape.
|
|
16
|
+
*
|
|
17
|
+
* # Unit conversion
|
|
18
|
+
*
|
|
19
|
+
* SlideOps carry positions in EMU. pptxgenjs uses inches. This module is
|
|
20
|
+
* the single boundary that converts EMU → inches; the substrate's
|
|
21
|
+
* pt-then-EMU-at-emit choice is unchanged.
|
|
22
|
+
*
|
|
23
|
+
* # Per-runtime font substitution
|
|
24
|
+
*
|
|
25
|
+
* The translator accepts a `fontSubstitution` map. When a SlideOp's
|
|
26
|
+
* `updateTextStyle.fontFamily` matches a key, the value replaces it on
|
|
27
|
+
* emit. PPTX cannot embed fonts; viewers need the family installed locally.
|
|
28
|
+
* Brands ship their own substitution table (see `@sanity-labs/slides` for an
|
|
29
|
+
* example); the substrate's default is empty.
|
|
30
|
+
*
|
|
31
|
+
* # Known gaps
|
|
32
|
+
*
|
|
33
|
+
* - **Outline weight + outline color on TEXT_BOX shapes.** pptxgenjs
|
|
34
|
+
* supports outlines via `line` on `addShape`, but text boxes go through
|
|
35
|
+
* `addText` whose options don't carry an outline. If a brand needs
|
|
36
|
+
* outlined text boxes, emit a sibling rectangle.
|
|
37
|
+
* - **Manifest persistence into the .pptx file.** The runtime holds the
|
|
38
|
+
* manifest in memory only; re-fill against a previously-generated .pptx
|
|
39
|
+
* is out of scope.
|
|
40
|
+
*/
|
|
41
|
+
import type { HexColor, ParagraphStyle, SlideOp, TextStyle } from './runtime.js';
|
|
42
|
+
/**
|
|
43
|
+
* The pptxgenjs-shaped representation of a single object on a slide.
|
|
44
|
+
* Discriminated by `kind` so the runtime can dispatch `addText` vs
|
|
45
|
+
* `addShape` vs `addImage`.
|
|
46
|
+
*/
|
|
47
|
+
export type PptxObject = PptxText | PptxRectangle | PptxImage;
|
|
48
|
+
/** A text box (TEXT_BOX shape) — emitted via `slide.addText`. */
|
|
49
|
+
export interface PptxText {
|
|
50
|
+
readonly kind: 'text';
|
|
51
|
+
readonly slideId: string;
|
|
52
|
+
readonly objectId: string;
|
|
53
|
+
/** Position, in inches. */
|
|
54
|
+
readonly position: PptxPosition;
|
|
55
|
+
/** Background fill color, no leading `#`. */
|
|
56
|
+
readonly fill?: string;
|
|
57
|
+
/** Plain text content. Empty string when no `insertText` op was applied. */
|
|
58
|
+
readonly text: string;
|
|
59
|
+
/**
|
|
60
|
+
* Per-range text-style spans. The runtime composes these into an array
|
|
61
|
+
* of `TextProps` segments for pptxgenjs.
|
|
62
|
+
*
|
|
63
|
+
* Spans are recorded in op order; later spans on overlapping ranges win
|
|
64
|
+
* at composition time (matches FakeSlidesRuntime semantics).
|
|
65
|
+
*/
|
|
66
|
+
readonly textSpans: ReadonlyArray<{
|
|
67
|
+
start: number;
|
|
68
|
+
end: number;
|
|
69
|
+
style: TextStyle;
|
|
70
|
+
}>;
|
|
71
|
+
/** Paragraph-level style — pptxgenjs applies one set per addText call. */
|
|
72
|
+
readonly paragraphStyle?: ParagraphStyle;
|
|
73
|
+
}
|
|
74
|
+
/** A non-text rectangle/ellipse — emitted via `slide.addShape`. */
|
|
75
|
+
export interface PptxRectangle {
|
|
76
|
+
readonly kind: 'rectangle';
|
|
77
|
+
readonly slideId: string;
|
|
78
|
+
readonly objectId: string;
|
|
79
|
+
readonly position: PptxPosition;
|
|
80
|
+
readonly shape: 'rect' | 'ellipse' | 'line';
|
|
81
|
+
readonly fill?: string;
|
|
82
|
+
readonly outlineColor?: string;
|
|
83
|
+
readonly outlineWeight?: number;
|
|
84
|
+
}
|
|
85
|
+
/** An image — emitted via `slide.addImage`. */
|
|
86
|
+
export interface PptxImage {
|
|
87
|
+
readonly kind: 'image';
|
|
88
|
+
readonly slideId: string;
|
|
89
|
+
readonly objectId: string;
|
|
90
|
+
readonly position: PptxPosition;
|
|
91
|
+
readonly url: string;
|
|
92
|
+
readonly altText?: string;
|
|
93
|
+
}
|
|
94
|
+
/** Position in inches — pptxgenjs's native unit. */
|
|
95
|
+
export interface PptxPosition {
|
|
96
|
+
readonly x: number;
|
|
97
|
+
readonly y: number;
|
|
98
|
+
readonly w: number;
|
|
99
|
+
readonly h: number;
|
|
100
|
+
}
|
|
101
|
+
/** What the translator emits for a whole `applyOps` batch. */
|
|
102
|
+
export interface PptxBatch {
|
|
103
|
+
/**
|
|
104
|
+
* Slides to create, in op order. For each entry, `insertAt` mirrors the
|
|
105
|
+
* SlideOp's index (currently informational; pptxgenjs appends in
|
|
106
|
+
* call order).
|
|
107
|
+
*/
|
|
108
|
+
readonly slides: ReadonlyArray<{
|
|
109
|
+
slideId: string;
|
|
110
|
+
insertAt: number | undefined;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* Per-slide objects, in creation order. The runtime iterates and emits
|
|
114
|
+
* the corresponding pptxgenjs call.
|
|
115
|
+
*/
|
|
116
|
+
readonly objects: readonly PptxObject[];
|
|
117
|
+
/** Object IDs (slide / shape / image) created by this batch. */
|
|
118
|
+
readonly createdObjectIds: readonly string[];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Per-call translator options. The font-substitution map is the Option X
|
|
122
|
+
* font resolver — see `pptx-runtime.ts` for the rationale.
|
|
123
|
+
*/
|
|
124
|
+
export interface TranslateOptions {
|
|
125
|
+
/**
|
|
126
|
+
* Map of resolved-font-name → output-font-name. When an op's
|
|
127
|
+
* `fontFamily` is a key, the value replaces it. Pass-through when not.
|
|
128
|
+
*/
|
|
129
|
+
readonly fontSubstitution?: Readonly<Record<string, string>>;
|
|
130
|
+
/**
|
|
131
|
+
* Optional warning hook for fonts that aren't in the substitution table.
|
|
132
|
+
* Default: no-op. The runtime itself decides whether to log; libraries
|
|
133
|
+
* should not write to stdout/stderr without opt-in.
|
|
134
|
+
*/
|
|
135
|
+
readonly onUnknownFont?: ((fontFamily: string) => void) | null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Walk a SlideOp stream and produce the pptxgenjs-shaped batch.
|
|
139
|
+
*
|
|
140
|
+
* The translator is stateful within a single call: shapes accumulate
|
|
141
|
+
* styles from later ops. Across calls, it's pure — given the same ops
|
|
142
|
+
* and options, output is deterministic.
|
|
143
|
+
*/
|
|
144
|
+
export declare const translateOpsToPptx: (ops: readonly SlideOp[], options?: TranslateOptions) => PptxBatch;
|
|
145
|
+
/**
|
|
146
|
+
* Strip the leading `#` from a hex color and return uppercase. pptxgenjs
|
|
147
|
+
* uses bare hex (e.g., `'FF5500'`).
|
|
148
|
+
*/
|
|
149
|
+
export declare const hexToPptxColor: (hex: HexColor) => string;
|
|
150
|
+
//# sourceMappingURL=op-translator-pptx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"op-translator-pptx.d.ts","sourceRoot":"","sources":["../../src/core/op-translator-pptx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure translation from `SlideOp` (the reconciler's typed intent) to a
|
|
3
|
+
* compact intermediate representation that pptxgenjs can consume.
|
|
4
|
+
*
|
|
5
|
+
* # Why not emit pptxgenjs calls directly
|
|
6
|
+
*
|
|
7
|
+
* pptxgenjs is stateful: `slide.addText(...)` mutates the Slide. The
|
|
8
|
+
* `SlideOp` stream is sequential — `createShape` is followed by
|
|
9
|
+
* `insertText`, `updateTextStyle`, etc. — so a one-pass-per-op translator
|
|
10
|
+
* would have to issue `addText` immediately on `createShape`, then mutate
|
|
11
|
+
* the already-added text on style ops, which pptxgenjs doesn't support.
|
|
12
|
+
*
|
|
13
|
+
* Instead, this module collapses an op stream into an array of
|
|
14
|
+
* `PptxObject` records — one per slide-level object. The runtime then walks
|
|
15
|
+
* the records and emits pptxgenjs calls in one shot per shape.
|
|
16
|
+
*
|
|
17
|
+
* # Unit conversion
|
|
18
|
+
*
|
|
19
|
+
* SlideOps carry positions in EMU. pptxgenjs uses inches. This module is
|
|
20
|
+
* the single boundary that converts EMU → inches; the substrate's
|
|
21
|
+
* pt-then-EMU-at-emit choice is unchanged.
|
|
22
|
+
*
|
|
23
|
+
* # Per-runtime font substitution
|
|
24
|
+
*
|
|
25
|
+
* The translator accepts a `fontSubstitution` map. When a SlideOp's
|
|
26
|
+
* `updateTextStyle.fontFamily` matches a key, the value replaces it on
|
|
27
|
+
* emit. PPTX cannot embed fonts; viewers need the family installed locally.
|
|
28
|
+
* Brands ship their own substitution table (see `@sanity-labs/slides` for an
|
|
29
|
+
* example); the substrate's default is empty.
|
|
30
|
+
*
|
|
31
|
+
* # Known gaps
|
|
32
|
+
*
|
|
33
|
+
* - **Outline weight + outline color on TEXT_BOX shapes.** pptxgenjs
|
|
34
|
+
* supports outlines via `line` on `addShape`, but text boxes go through
|
|
35
|
+
* `addText` whose options don't carry an outline. If a brand needs
|
|
36
|
+
* outlined text boxes, emit a sibling rectangle.
|
|
37
|
+
* - **Manifest persistence into the .pptx file.** The runtime holds the
|
|
38
|
+
* manifest in memory only; re-fill against a previously-generated .pptx
|
|
39
|
+
* is out of scope.
|
|
40
|
+
*/
|
|
41
|
+
import { EMU_PER_INCH } from './geometry.js';
|
|
42
|
+
/**
|
|
43
|
+
* Walk a SlideOp stream and produce the pptxgenjs-shaped batch.
|
|
44
|
+
*
|
|
45
|
+
* The translator is stateful within a single call: shapes accumulate
|
|
46
|
+
* styles from later ops. Across calls, it's pure — given the same ops
|
|
47
|
+
* and options, output is deterministic.
|
|
48
|
+
*/
|
|
49
|
+
export const translateOpsToPptx = (ops, options = {}) => {
|
|
50
|
+
const slides = [];
|
|
51
|
+
const objects = [];
|
|
52
|
+
const objectIndex = new Map();
|
|
53
|
+
const createdObjectIds = [];
|
|
54
|
+
const subs = options.fontSubstitution ?? {};
|
|
55
|
+
const warned = new Set();
|
|
56
|
+
// Default to a no-op: libraries shouldn't write to stdout/stderr without
|
|
57
|
+
// explicit opt-in. The runtime can pass `console.warn` (or its own logger)
|
|
58
|
+
// when it actually wants user-facing warnings.
|
|
59
|
+
const warn = options.onUnknownFont ?? noopWarn;
|
|
60
|
+
const upsertText = (slideId, objectId, position) => {
|
|
61
|
+
const idx = objectIndex.get(objectId);
|
|
62
|
+
if (idx !== undefined) {
|
|
63
|
+
const existing = objects[idx];
|
|
64
|
+
if (existing && existing.kind === 'text')
|
|
65
|
+
return existing;
|
|
66
|
+
throw new Error(`translateOpsToPptx: object "${objectId}" already exists as a non-text ${existing?.kind ?? 'unknown'}.`);
|
|
67
|
+
}
|
|
68
|
+
const text = {
|
|
69
|
+
kind: 'text',
|
|
70
|
+
slideId,
|
|
71
|
+
objectId,
|
|
72
|
+
position,
|
|
73
|
+
text: '',
|
|
74
|
+
textSpans: [],
|
|
75
|
+
};
|
|
76
|
+
objectIndex.set(objectId, objects.length);
|
|
77
|
+
objects.push(text);
|
|
78
|
+
createdObjectIds.push(objectId);
|
|
79
|
+
return text;
|
|
80
|
+
};
|
|
81
|
+
const findText = (objectId, opName) => {
|
|
82
|
+
const idx = objectIndex.get(objectId);
|
|
83
|
+
if (idx === undefined) {
|
|
84
|
+
throw new Error(`translateOpsToPptx: ${opName} targets unknown object "${objectId}".`);
|
|
85
|
+
}
|
|
86
|
+
const obj = objects[idx];
|
|
87
|
+
if (!obj || obj.kind !== 'text') {
|
|
88
|
+
throw new Error(`translateOpsToPptx: ${opName} targets non-text object "${objectId}".`);
|
|
89
|
+
}
|
|
90
|
+
return obj;
|
|
91
|
+
};
|
|
92
|
+
const replaceObject = (idx, replacement) => {
|
|
93
|
+
objects[idx] = replacement;
|
|
94
|
+
};
|
|
95
|
+
for (const op of ops) {
|
|
96
|
+
switch (op.type) {
|
|
97
|
+
case 'createSlide': {
|
|
98
|
+
slides.push({ slideId: op.slideId, insertAt: op.insertAt });
|
|
99
|
+
createdObjectIds.push(op.slideId);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'createShape': {
|
|
103
|
+
const position = emuRectToInches(op.rect);
|
|
104
|
+
if (op.shape === 'TEXT_BOX') {
|
|
105
|
+
upsertText(op.slideId, op.shapeId, position);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// RECTANGLE / ELLIPSE / LINE — emit as pptxgenjs shape.
|
|
109
|
+
const rect = {
|
|
110
|
+
kind: 'rectangle',
|
|
111
|
+
slideId: op.slideId,
|
|
112
|
+
objectId: op.shapeId,
|
|
113
|
+
position,
|
|
114
|
+
shape: shapeKindToPptx(op.shape),
|
|
115
|
+
};
|
|
116
|
+
objectIndex.set(op.shapeId, objects.length);
|
|
117
|
+
objects.push(rect);
|
|
118
|
+
createdObjectIds.push(op.shapeId);
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case 'createImage': {
|
|
123
|
+
const image = {
|
|
124
|
+
kind: 'image',
|
|
125
|
+
slideId: op.slideId,
|
|
126
|
+
objectId: op.imageId,
|
|
127
|
+
position: emuRectToInches(op.rect),
|
|
128
|
+
url: op.url,
|
|
129
|
+
...(op.altText !== undefined ? { altText: op.altText } : {}),
|
|
130
|
+
};
|
|
131
|
+
objectIndex.set(op.imageId, objects.length);
|
|
132
|
+
objects.push(image);
|
|
133
|
+
createdObjectIds.push(op.imageId);
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
case 'insertText': {
|
|
137
|
+
const target = findText(op.objectId, 'insertText');
|
|
138
|
+
const idx = objectIndex.get(op.objectId);
|
|
139
|
+
if (idx === undefined)
|
|
140
|
+
break;
|
|
141
|
+
replaceObject(idx, { ...target, text: op.text });
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case 'updateTextStyle': {
|
|
145
|
+
const target = findText(op.objectId, 'updateTextStyle');
|
|
146
|
+
const idx = objectIndex.get(op.objectId);
|
|
147
|
+
if (idx === undefined)
|
|
148
|
+
break;
|
|
149
|
+
const style = applyFontSubstitution(op.style, subs, warn, warned);
|
|
150
|
+
const span = { start: op.range.start, end: op.range.end, style };
|
|
151
|
+
replaceObject(idx, { ...target, textSpans: [...target.textSpans, span] });
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'updateParagraphStyle': {
|
|
155
|
+
const target = findText(op.objectId, 'updateParagraphStyle');
|
|
156
|
+
const idx = objectIndex.get(op.objectId);
|
|
157
|
+
if (idx === undefined)
|
|
158
|
+
break;
|
|
159
|
+
replaceObject(idx, {
|
|
160
|
+
...target,
|
|
161
|
+
paragraphStyle: { ...(target.paragraphStyle ?? {}), ...op.style },
|
|
162
|
+
});
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case 'updateShapeProperties': {
|
|
166
|
+
const idx = objectIndex.get(op.objectId);
|
|
167
|
+
if (idx === undefined) {
|
|
168
|
+
throw new Error(`translateOpsToPptx: updateShapeProperties targets unknown object "${op.objectId}".`);
|
|
169
|
+
}
|
|
170
|
+
const obj = objects[idx];
|
|
171
|
+
if (!obj)
|
|
172
|
+
break;
|
|
173
|
+
replaceObject(idx, mergeShapeProperties(obj, op.properties));
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { slides, objects, createdObjectIds };
|
|
179
|
+
};
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Helpers
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
/** Convert EMU rect to inches (pptxgenjs's native unit). */
|
|
184
|
+
const emuRectToInches = (rect) => ({
|
|
185
|
+
x: rect.x / EMU_PER_INCH,
|
|
186
|
+
y: rect.y / EMU_PER_INCH,
|
|
187
|
+
w: rect.w / EMU_PER_INCH,
|
|
188
|
+
h: rect.h / EMU_PER_INCH,
|
|
189
|
+
});
|
|
190
|
+
const shapeKindToPptx = (kind) => {
|
|
191
|
+
switch (kind) {
|
|
192
|
+
case 'RECTANGLE':
|
|
193
|
+
return 'rect';
|
|
194
|
+
case 'ELLIPSE':
|
|
195
|
+
return 'ellipse';
|
|
196
|
+
case 'LINE':
|
|
197
|
+
return 'line';
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Strip the leading `#` from a hex color and return uppercase. pptxgenjs
|
|
202
|
+
* uses bare hex (e.g., `'FF5500'`).
|
|
203
|
+
*/
|
|
204
|
+
export const hexToPptxColor = (hex) => {
|
|
205
|
+
const stripped = hex.startsWith('#') ? hex.slice(1) : hex;
|
|
206
|
+
return stripped.toUpperCase();
|
|
207
|
+
};
|
|
208
|
+
const mergeShapeProperties = (obj, props) => {
|
|
209
|
+
if (obj.kind === 'text') {
|
|
210
|
+
return {
|
|
211
|
+
...obj,
|
|
212
|
+
...(props.fillColor !== undefined ? { fill: hexToPptxColor(props.fillColor) } : {}),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (obj.kind === 'rectangle') {
|
|
216
|
+
const next = { ...obj };
|
|
217
|
+
if (props.fillColor !== undefined)
|
|
218
|
+
next.fill = hexToPptxColor(props.fillColor);
|
|
219
|
+
if (props.outlineColor !== undefined)
|
|
220
|
+
next.outlineColor = hexToPptxColor(props.outlineColor);
|
|
221
|
+
if (props.outlineWeight !== undefined)
|
|
222
|
+
next.outlineWeight = props.outlineWeight;
|
|
223
|
+
return next;
|
|
224
|
+
}
|
|
225
|
+
// image — no shape properties apply
|
|
226
|
+
return obj;
|
|
227
|
+
};
|
|
228
|
+
const applyFontSubstitution = (style, subs, warn, warned) => {
|
|
229
|
+
if (style.fontFamily === undefined)
|
|
230
|
+
return style;
|
|
231
|
+
const sub = subs[style.fontFamily];
|
|
232
|
+
if (sub !== undefined) {
|
|
233
|
+
if (sub === style.fontFamily)
|
|
234
|
+
return style;
|
|
235
|
+
return { ...style, fontFamily: sub };
|
|
236
|
+
}
|
|
237
|
+
// Not in substitution table — emit warning once per font, then pass through.
|
|
238
|
+
if (!warned.has(style.fontFamily)) {
|
|
239
|
+
warned.add(style.fontFamily);
|
|
240
|
+
warn(style.fontFamily);
|
|
241
|
+
}
|
|
242
|
+
return style;
|
|
243
|
+
};
|
|
244
|
+
const noopWarn = (_font) => { };
|
|
245
|
+
//# sourceMappingURL=op-translator-pptx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"op-translator-pptx.js","sourceRoot":"","sources":["../../src/core/op-translator-pptx.ts"],"names":[],"mappings":"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"}
|