@openpresentation/opf-editor 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/design-controls.js +12 -0
- package/dist/persistence-ui.js +1 -1
- package/dist/switches.d.ts +12 -6
- package/dist/switches.js +61 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Unfinished prepared shaping work is preserved in the [September 15 roadmap](docs/roadmap-shaping-20260915.md); it is not part of the published runtime.
|
|
4
4
|
|
|
5
|
-
Version 0.11.0 requires `@openpresentation/opf` ^0.12.0 (composed font sizes on PowerPoint's 0.01 pt grid, hanging wrap whitespace, promoted regions in reading order, right-to-left decks composed mirrored; install it with renderer 0.12.0 and PPTX 0.12.0 so the canvas, preview and export resolve the same core) and ships the new entry points listed in the changelog. Version 0.10.6 changes no editor source: it requires `@openpresentation/opf` ^0.11.4 (the design fields compose, cover tag and subtitle follow the title alignment, picture bullets and furniture images change geometry; install it with renderer 0.11.9 and PPTX 0.11.7 so the canvas, preview and export resolve the same core). Version 0.10.5 changes no editor source: its playground example loads its base faces through the renderer's `extraLazyFonts` (renderer 0.11.7 or later; the editor package itself runs with the optional peer ^0.11.0 as before). Version 0.10.4 changes no editor behavior: it requires `@openpresentation/opf` ^0.11.3 (the 70 legacy gallery layout ids and their geometry; install it with renderer 0.11.6 and PPTX 0.11.4 so the canvas, preview and export resolve the same core). Version 0.10.3 passes the host's render options (`catalogs`) through the font gate to the registry, so layouts and font schemes that only the host's catalogs know load their faces, and asks the script and vendored-face loaders separately; it keeps the 0.10.2 requirements and gates nothing new on renderers before 0.11.5. Version 0.10.2 adds pointer caret entry on the canvas (one click puts the caret where you click; new `textEntry` option; keyboard entry still selects all) and keeps the 0.10.1 and 0.10.0 requirements. Version 0.10.1 adds the opt-in font gate (`createFontGate`, FF-41) and keeps the 0.10.0 requirements; develop and test it against renderer 0.11.2 and PPTX 0.11.1. Version 0.10.0 requires `@openpresentation/opf` ^0.11.2, renderer ^0.11.0, and PPTX ^0.11.0 (install them together so cover centering resolves the same core everywhere; renderer 0.11.0 also activates the playground's automatic script fonts and lazy Intos fonts). Version 0.9.0 required `@openpresentation/opf` ^0.11.1, renderer ^0.10.0, and PPTX ^0.10.0. Composition and slide transfer fall back to the shared `aptos` font scheme and report unknown scheme ids through `onDiagnostic`; gallery apply keeps every font-scheme role.
|
|
5
|
+
Version 0.11.2 adds the slide-size and purpose switches (additive; same requirements as 0.11.0). Version 0.11.0 requires `@openpresentation/opf` ^0.12.0 (composed font sizes on PowerPoint's 0.01 pt grid, hanging wrap whitespace, promoted regions in reading order, right-to-left decks composed mirrored; install it with renderer 0.12.0 and PPTX 0.12.0 so the canvas, preview and export resolve the same core) and ships the new entry points listed in the changelog. Version 0.10.6 changes no editor source: it requires `@openpresentation/opf` ^0.11.4 (the design fields compose, cover tag and subtitle follow the title alignment, picture bullets and furniture images change geometry; install it with renderer 0.11.9 and PPTX 0.11.7 so the canvas, preview and export resolve the same core). Version 0.10.5 changes no editor source: its playground example loads its base faces through the renderer's `extraLazyFonts` (renderer 0.11.7 or later; the editor package itself runs with the optional peer ^0.11.0 as before). Version 0.10.4 changes no editor behavior: it requires `@openpresentation/opf` ^0.11.3 (the 70 legacy gallery layout ids and their geometry; install it with renderer 0.11.6 and PPTX 0.11.4 so the canvas, preview and export resolve the same core). Version 0.10.3 passes the host's render options (`catalogs`) through the font gate to the registry, so layouts and font schemes that only the host's catalogs know load their faces, and asks the script and vendored-face loaders separately; it keeps the 0.10.2 requirements and gates nothing new on renderers before 0.11.5. Version 0.10.2 adds pointer caret entry on the canvas (one click puts the caret where you click; new `textEntry` option; keyboard entry still selects all) and keeps the 0.10.1 and 0.10.0 requirements. Version 0.10.1 adds the opt-in font gate (`createFontGate`, FF-41) and keeps the 0.10.0 requirements; develop and test it against renderer 0.11.2 and PPTX 0.11.1. Version 0.10.0 requires `@openpresentation/opf` ^0.11.2, renderer ^0.11.0, and PPTX ^0.11.0 (install them together so cover centering resolves the same core everywhere; renderer 0.11.0 also activates the playground's automatic script fonts and lazy Intos fonts). Version 0.9.0 required `@openpresentation/opf` ^0.11.1, renderer ^0.10.0, and PPTX ^0.10.0. Composition and slide transfer fall back to the shared `aptos` font scheme and report unknown scheme ids through `onDiagnostic`; gallery apply keeps every font-scheme role.
|
|
6
6
|
|
|
7
7
|
Version 0.8.0 required `@openpresentation/opf` ^0.11.0, renderer ^0.9.0, and PPTX ^0.9.0. Named ColorRefs (scheme slots, roles, and `var:<id>`) paint through the published renderer and hex-resolve on export. Payload ids share the slide id namespace for pagination and transfer. Native `schemeClr`, theme write, and `p:hf` remain out of scope.
|
|
8
8
|
|
|
@@ -205,7 +205,7 @@ Invalid IDs throw before they reach the document. Existing object-form reference
|
|
|
205
205
|
|
|
206
206
|
## Dimension switches
|
|
207
207
|
|
|
208
|
-
`@openpresentation/opf-editor/switches` turns "switch this pptx.gallery dimension to X" into one validated, undoable transaction. It covers the 14 gallery dimensions (FF-16, [font-fidelity-everywhere](https://github.com/OpenPresentation/opf/tree/main/docs/programs/font-fidelity-everywhere)):
|
|
208
|
+
`@openpresentation/opf-editor/switches` turns "switch this pptx.gallery dimension to X" into one validated, undoable transaction. It covers the 14 gallery dimensions (FF-16, [font-fidelity-everywhere](https://github.com/OpenPresentation/opf/tree/main/docs/programs/font-fidelity-everywhere)) and, after them in `SWITCH_DIMENSIONS`, the two document-level choices the gallery does not page yet: `slide-sizes` and `purposes` (RR-41, [opf#293](https://github.com/OpenPresentation/opf/issues/293)):
|
|
209
209
|
|
|
210
210
|
```js
|
|
211
211
|
import { switchDimension, prepareDimensionSwitch, SWITCH_DIMENSIONS } from "@openpresentation/opf-editor/switches";
|
|
@@ -229,6 +229,8 @@ const { patches, document } = prepareDimensionSwitch(editor.document, "themes",
|
|
|
229
229
|
| `socials` | `/speaker/socials/<platform>` or the `organization` | `{platform, handle}` with `owner` and `index`; the owner must exist. |
|
|
230
230
|
| `charts` | `<chart owner>/chart/type` | The slide's first chart, or the block named by `path`. The data is kept as it is and only the document schema is checked: switching does not verify that the data suits the new type (`compatibleChartTypes` lists the types the data can use; map types, for example, expect their own data). |
|
|
231
231
|
| `blocks` | replaces one block, or with `convert: true` moves its content to the new kind | `path` names a complete `blocks/N` block or a slide/region with one content field. The value is a block kind or a block object. See *Content-type conversion*. |
|
|
232
|
+
| `slide-sizes` | `/design/dimensions` | One of the schema's seven presets (`SLIDE_SIZE_PRESETS`: `16:9`, `4:3`, `16:10`, `letter`, `a4`, `widescreen`, `standard`). Deck only (`slideIndex` is refused: a presentation has one slide size). The preview recomposes at the new canvas and the PowerPoint export writes the matching `p:sldSz`; a custom size (inches) is replaced by the preset. `listSwitchOptions` labels each preset with its inches; `currentSwitchValue` reads `design.dimensions`, else the theme's size. Slides that carry their own `design.dimensions` are listed in `shadowed`, and `clearSlideOverrides` removes them. A theme switch still writes its own size. |
|
|
233
|
+
| `purposes` | `/purpose` | A `purposes` catalog id, any free-form goal text (not checked against the catalog), or a Purpose object. `record` adds a gallery item's purpose record inline. Authoring metadata: the preview does not change. |
|
|
232
234
|
|
|
233
235
|
A deck-level design switch cannot reach a slide that carries its own value for that key. The result lists those slides in `shadowed`; `clearSlideOverrides: true` removes the overrides in the same transaction. `record` adds a gallery item's catalog record inline in the same transaction when neither the document nor the bundled catalog defines its id (a gallery-only layout or font scheme). Every switch is validated: an unknown catalog id, an invalid value or an invalid resulting document throws before anything changes, and switching to the current value commits nothing. The editor session emits the usual `patch`, `undo` and `redo` events with `meta.source: "dimension-switch"` and `meta.dimension`, so the canvas and any host preview recompose from the switched document. `resolveSlideFonts(document, slideIndex)` returns the heading, body and code families the preview measures and the export names.
|
|
234
236
|
|
|
@@ -446,7 +448,7 @@ Run `npm run test:design-controls-browser` (after `npm run build:playground`) fo
|
|
|
446
448
|
|
|
447
449
|
### What a switch does not establish
|
|
448
450
|
|
|
449
|
-
A switch changes the document; it does not change what the engines support. Language changes recompose fonts only as far as the installed core, renderer and PPTX packages implement the language and script model (FF-18, FF-19); the editor's own composition measures the Latin families. `image-treatments` previews only where the installed renderer draws `design.slideImage`. `test/switches.mjs` checks the patch, one undo step, undo/redo, and preview refresh for all 14 dimensions. `test/switches-export.mjs` exports after each switch, undo and redo and applies opf-pptx's FF-08 typeface check (`checkPptxTypefaces`) when the installed package has it; set `OPF_REQUIRE_FF08=1` to fail instead of skip when it does not. Published opf-pptx 0.9.1 does not include it.
|
|
451
|
+
A switch changes the document; it does not change what the engines support. Language changes recompose fonts only as far as the installed core, renderer and PPTX packages implement the language and script model (FF-18, FF-19); the editor's own composition measures the Latin families. `image-treatments` previews only where the installed renderer draws `design.slideImage`. `test/switches.mjs` checks the patch, one undo step, undo/redo, and preview refresh for all 16 dimensions (the 14 gallery dimensions, slide sizes and purposes), and for every slide-size preset the composed canvas and the preview viewBox. `test/switches-export.mjs` exports after each switch, undo and redo, checks that every slide-size preset writes the matching `p:sldSz` and re-imports as that preset, and applies opf-pptx's FF-08 typeface check (`checkPptxTypefaces`) when the installed package has it; set `OPF_REQUIRE_FF08=1` to fail instead of skip when it does not. Published opf-pptx 0.9.1 does not include it.
|
|
450
452
|
|
|
451
453
|
## Autosave and restore (RR-22)
|
|
452
454
|
|
package/dist/design-controls.js
CHANGED
|
@@ -402,6 +402,17 @@ export function createDesignControls(container, options = {}) {
|
|
|
402
402
|
catalogField("color-scheme", "Color scheme", "color-schemes");
|
|
403
403
|
catalogField("font-scheme", "Font scheme", "font-schemes", { help: "Fonts the preview and export use. The preview shows an open look-alike where a font is not bundled." });
|
|
404
404
|
catalogField("language", "Language", "languages", { perSlide: false, help: "Sets the language for the whole presentation, including its script fonts." });
|
|
405
|
+
// RR-41: one size for the whole presentation; the slides recompose and the export writes the matching slide size.
|
|
406
|
+
const slideSize = selectField("slide-size", "Slide size", {
|
|
407
|
+
help: "The size and shape of every slide. The preview recomposes at the new size.",
|
|
408
|
+
onChange: (value) => run(() => switchDimension(editor, "slide-sizes", value), `Slide size set to ${slideSize.select.selectedOptions[0]?.textContent ?? value}.`),
|
|
409
|
+
});
|
|
410
|
+
body.append(slideSize.wrap);
|
|
411
|
+
syncs.push(() => {
|
|
412
|
+
slideSize.setOptions(listSwitchOptions(editor.document, "slide-sizes").map((entry) => ({ value: entry.id, label: entry.label })));
|
|
413
|
+
const { value } = currentSwitchValue(editor.document, "slide-sizes");
|
|
414
|
+
slideSize.set(value && typeof value === "object" ? "Custom size" : value);
|
|
415
|
+
});
|
|
405
416
|
|
|
406
417
|
const layout = selectField("layout", "Layout of this slide", {
|
|
407
418
|
help: "Adds the blank placeholders the layout declares and keeps your content.",
|
|
@@ -827,6 +838,7 @@ export function createDesignControls(container, options = {}) {
|
|
|
827
838
|
};
|
|
828
839
|
single("narrative", "Narrative", "narratives", "The storyline the deck follows. Authoring metadata: the slides do not change.");
|
|
829
840
|
single("tone", "Tone", "tones");
|
|
841
|
+
single("purpose", "Purpose", "purposes", "What the deck is for. Authoring metadata: the slides do not change. A goal written in the JSON source shows as custom.");
|
|
830
842
|
const audience = selectField("audience", "Audience", {
|
|
831
843
|
multiple: true,
|
|
832
844
|
size: 6,
|
package/dist/persistence-ui.js
CHANGED
|
@@ -53,7 +53,7 @@ export function createPersistenceUi(options) {
|
|
|
53
53
|
restore.addEventListener("click", async () => { restore.disabled = discard.disabled = true; const done = await actions.restore(); if (done) { hide(); say("Restored. Undo goes back to what you had."); } else { restore.disabled = discard.disabled = false; } });
|
|
54
54
|
discard.addEventListener("click", async () => { await actions.discard(); hide(); say("Discarded the stored copy."); });
|
|
55
55
|
earlier?.addEventListener("click", async () => { const done = await options.restoreEarlier?.(); if (done) { hide(); say("Restored the older copy. Undo goes back to what you had."); } });
|
|
56
|
-
banner.replaceChildren(el(doc, "p", { class: "restore-text", id: `${banner.id || "restore"}-text`, text: intro }), note, el(doc, "div", { class: "restore-actions" }, restore, discard, earlier));
|
|
56
|
+
banner.replaceChildren(el(doc, "p", { class: "restore-text", id: `${banner.id || "restore"}-text`, text: intro }), ...(note ? [note] : []), el(doc, "div", { class: "restore-actions" }, restore, discard, earlier));
|
|
57
57
|
banner.setAttribute("role", "region");
|
|
58
58
|
banner.setAttribute("aria-label", "Restore your work");
|
|
59
59
|
banner.hidden = false;
|
package/dist/switches.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { ConvertOptions } from "@openpresentation/opf/convert";
|
|
2
2
|
import type { EditorChange, EditorSession, JsonPatchOperation } from "./index.js";
|
|
3
3
|
|
|
4
|
-
/** The
|
|
4
|
+
/** The schema's DimensionPreset values: the values of the slide-sizes switch. */
|
|
5
|
+
export declare const SLIDE_SIZE_PRESETS: readonly ["16:9", "4:3", "16:10", "letter", "a4", "widescreen", "standard"];
|
|
6
|
+
export type SlideSizePreset = (typeof SLIDE_SIZE_PRESETS)[number];
|
|
7
|
+
|
|
8
|
+
/** Every switchable dimension: the 14 pptx.gallery dimensions, then `slide-sizes` and `purposes`. */
|
|
5
9
|
export declare const SWITCH_DIMENSIONS: readonly [
|
|
6
10
|
"layouts",
|
|
7
11
|
"color-schemes",
|
|
@@ -17,6 +21,8 @@ export declare const SWITCH_DIMENSIONS: readonly [
|
|
|
17
21
|
"headers-footers",
|
|
18
22
|
"blocks",
|
|
19
23
|
"image-treatments",
|
|
24
|
+
"slide-sizes",
|
|
25
|
+
"purposes",
|
|
20
26
|
];
|
|
21
27
|
export type SwitchDimension = (typeof SWITCH_DIMENSIONS)[number];
|
|
22
28
|
|
|
@@ -32,7 +38,7 @@ export interface DimensionSwitchOptions {
|
|
|
32
38
|
catalogSources?: Record<string, unknown>;
|
|
33
39
|
/** themes: also write the theme's color scheme, font scheme, background and dimensions to the deck design (default true). */
|
|
34
40
|
bundle?: boolean;
|
|
35
|
-
/** Deck-scope design switches: remove slide-level values that would hide the switch. */
|
|
41
|
+
/** Deck-scope design switches (and slide-sizes): remove slide-level values that would hide the switch. */
|
|
36
42
|
clearSlideOverrides?: boolean;
|
|
37
43
|
/** socials: which document field holds the handle (default "speaker") and, for an array, which entry (default 0). */
|
|
38
44
|
owner?: "speaker" | "organization";
|
|
@@ -49,8 +55,8 @@ export interface DimensionSwitchOptions {
|
|
|
49
55
|
|
|
50
56
|
export type DimensionSwitchValue =
|
|
51
57
|
| null /* backgrounds: remove it */
|
|
52
|
-
| string
|
|
53
|
-
| string[]
|
|
58
|
+
| string /* slide-sizes: a SlideSizePreset. purposes: a catalog id or free-form goal text. */
|
|
59
|
+
| string[] /* audiences: catalog ids */
|
|
54
60
|
| { platform: string; handle: string }
|
|
55
61
|
| { header?: unknown; footer?: unknown }
|
|
56
62
|
| { slideImage?: unknown; imageFill?: string | null }
|
|
@@ -94,10 +100,10 @@ export interface CompatibleChartType extends SwitchOption {
|
|
|
94
100
|
/** True for the chart's present type, which is always listed. */
|
|
95
101
|
current: boolean;
|
|
96
102
|
}
|
|
97
|
-
/** Values a picker can offer for a catalog-backed dimension (document inline records, caller catalogs, then the bundled catalog, without duplicates); `blocks` lists the content kinds. */
|
|
103
|
+
/** Values a picker can offer for a catalog-backed dimension (document inline records, caller catalogs, then the bundled catalog, without duplicates); `blocks` lists the content kinds and `slide-sizes` the presets (labelled with their inches). `purposes` lists the catalog; any other goal text is also a valid switch value. */
|
|
98
104
|
export declare function listSwitchOptions(document: unknown, dimension: SwitchDimension, options?: Pick<DimensionSwitchOptions, "catalogs" | "catalogSources">): SwitchOption[];
|
|
99
105
|
/** Chart types the chart's inline data can use as it is (data-shape compatibility, not an engine-support claim). `path` or `slideIndex` picks the chart. */
|
|
100
106
|
export declare function compatibleChartTypes(document: unknown, options?: Pick<DimensionSwitchOptions, "slideIndex" | "path" | "catalogs" | "catalogSources">): CompatibleChartType[];
|
|
101
|
-
/** The value a dimension currently has: `{ value, scope }`, with the catalog id for catalog dimensions. */
|
|
107
|
+
/** The value a dimension currently has: `{ value, scope }`, with the catalog id for catalog dimensions. `slide-sizes` reads the deck's design.dimensions, else its theme's (a preset string, or the object for a custom size); `purposes` reads the goal text or Purpose id. */
|
|
102
108
|
export declare function currentSwitchValue(document: unknown, dimension: SwitchDimension, options?: Pick<DimensionSwitchOptions, "slideIndex" | "path" | "owner" | "index">): { value: unknown; scope: "deck" | "slide" | "block" };
|
|
103
109
|
export { blockConversionTargets } from "./block-convert.js";
|
package/dist/switches.js
CHANGED
|
@@ -17,7 +17,25 @@ import { checkedDocument, designPatches, fail, same } from "./edit-helpers.js";
|
|
|
17
17
|
import { populateLayoutPlaceholders } from "./layout-placeholders.js";
|
|
18
18
|
import { blockConversionTargets, prepareBlockConversion } from "./block-convert.js";
|
|
19
19
|
|
|
20
|
-
/** The
|
|
20
|
+
/** The schema's DimensionPreset values (RR-41), the values of the slide-sizes switch. */
|
|
21
|
+
export const SLIDE_SIZE_PRESETS = Object.freeze(["16:9", "4:3", "16:10", "letter", "a4", "widescreen", "standard"]);
|
|
22
|
+
|
|
23
|
+
// Slide size in inches per preset, as core's resolveCanvasDimensions composes it (for picker labels).
|
|
24
|
+
const SLIDE_SIZE_LABELS = Object.freeze({
|
|
25
|
+
"16:9": "16:9 (13.33 x 7.5 in)",
|
|
26
|
+
"4:3": "4:3 (10 x 7.5 in)",
|
|
27
|
+
"16:10": "16:10 (10 x 6.25 in)",
|
|
28
|
+
letter: "Letter (11 x 8.5 in)",
|
|
29
|
+
a4: "A4 (11.69 x 8.27 in)",
|
|
30
|
+
widescreen: "Widescreen (13.33 x 7.5 in)",
|
|
31
|
+
standard: "Standard (10 x 7.5 in)",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Every switchable dimension, in the gallery's order: the 14 pptx.gallery dimensions (gallery-support.md), then the
|
|
36
|
+
* two document-level ones the gallery does not page yet (RR-41): `slide-sizes` (design.dimensions) and `purposes`
|
|
37
|
+
* (the document's purpose).
|
|
38
|
+
*/
|
|
21
39
|
export const SWITCH_DIMENSIONS = Object.freeze([
|
|
22
40
|
"layouts",
|
|
23
41
|
"color-schemes",
|
|
@@ -33,6 +51,8 @@ export const SWITCH_DIMENSIONS = Object.freeze([
|
|
|
33
51
|
"headers-footers",
|
|
34
52
|
"blocks",
|
|
35
53
|
"image-treatments",
|
|
54
|
+
"slide-sizes",
|
|
55
|
+
"purposes",
|
|
36
56
|
]);
|
|
37
57
|
|
|
38
58
|
// Catalog kind behind each catalog-backed dimension.
|
|
@@ -47,6 +67,7 @@ const CATALOG_KIND = Object.freeze({
|
|
|
47
67
|
tones: "tones",
|
|
48
68
|
socials: "socialPlatforms",
|
|
49
69
|
charts: "chartTypes",
|
|
70
|
+
purposes: "purposes",
|
|
50
71
|
});
|
|
51
72
|
// Top-level document field for the simple metadata dimensions.
|
|
52
73
|
const ROOT_FIELD = Object.freeze({ languages: "language", narratives: "narrative", tones: "tone", audiences: "audience" });
|
|
@@ -184,7 +205,26 @@ export function prepareDimensionSwitch(document, dimension, value, options = {})
|
|
|
184
205
|
let slideIndex;
|
|
185
206
|
let conversionLoss;
|
|
186
207
|
|
|
187
|
-
if (dimension === "
|
|
208
|
+
if (dimension === "slide-sizes") {
|
|
209
|
+
// A preset string; a custom size (design.dimensions as an object with inches) is replaced by it.
|
|
210
|
+
if (!SLIDE_SIZE_PRESETS.includes(value)) throw fail("invalid-switch-value", `Switch slide-sizes to one of ${SLIDE_SIZE_PRESETS.join(", ")}.`, { value });
|
|
211
|
+
if (scopeIndex !== undefined) throw fail("deck-scope-only", "A presentation has one slide size: switch slide-sizes for the deck, without slideIndex.", { slideIndex: scopeIndex });
|
|
212
|
+
const own = document.design?.dimensions;
|
|
213
|
+
// {preset} alone is the same size as the bare preset string.
|
|
214
|
+
const sameSize = own && typeof own === "object" && !Array.isArray(own) && own.preset === value && Object.keys(own).length === 1;
|
|
215
|
+
patches = sameSize ? [] : designPatches(document, [], { dimensions: value });
|
|
216
|
+
shadowed = shadowedSlides(document, ["dimensions"]);
|
|
217
|
+
if (options.clearSlideOverrides) {
|
|
218
|
+
for (const index of shadowed) patches.push({ op: "remove", path: opfPathToJsonPointer(["slides", String(index), "design", "dimensions"]) });
|
|
219
|
+
shadowed = [];
|
|
220
|
+
}
|
|
221
|
+
} else if (dimension === "purposes") {
|
|
222
|
+
// A catalog id, free-form goal text (no id check: any goal is valid) or an inline Purpose object; the schema validates it.
|
|
223
|
+
if (typeof value !== "string" && !(value && typeof value === "object" && !Array.isArray(value)))
|
|
224
|
+
throw fail("invalid-switch-value", "Switch purposes to a catalog id, a goal string or a purpose object.", { value });
|
|
225
|
+
if (typeof value === "string" && value.length === 0) throw fail("invalid-switch-value", "Switch purposes to a non-empty goal.", { value });
|
|
226
|
+
patches = [...catalogRecordPatches(document, dimension, options), ...rootPatch(document, "purpose", value)];
|
|
227
|
+
} else if (dimension === "layouts") {
|
|
188
228
|
slideIndex = scopeIndex;
|
|
189
229
|
slideAt(document, slideIndex, "a layout switch");
|
|
190
230
|
scope = "slide";
|
|
@@ -336,6 +376,7 @@ const labelOf = (record) => record.label ?? record.name ?? record.id;
|
|
|
336
376
|
*/
|
|
337
377
|
export function listSwitchOptions(document, dimension, options = {}) {
|
|
338
378
|
if (dimension === "blocks") return BLOCK_KINDS.map((kind) => ({ id: kind, label: kind[0].toUpperCase() + kind.slice(1) }));
|
|
379
|
+
if (dimension === "slide-sizes") return SLIDE_SIZE_PRESETS.map((preset) => ({ id: preset, label: SLIDE_SIZE_LABELS[preset] }));
|
|
339
380
|
const kind = CATALOG_KIND[dimension];
|
|
340
381
|
if (!kind) return [];
|
|
341
382
|
const seen = new Set();
|
|
@@ -407,6 +448,24 @@ export function currentSwitchValue(document, dimension, options = {}) {
|
|
|
407
448
|
return { value: owner ? getValueAtPath(document, [...owner, "chart", "type"]) : undefined, scope: "slide" };
|
|
408
449
|
}
|
|
409
450
|
if (dimension === "blocks") return { value: undefined, scope: "block" };
|
|
451
|
+
if (dimension === "slide-sizes") {
|
|
452
|
+
// The size the deck composes at: its own design.dimensions, else the theme's; a {preset} object reads as its preset.
|
|
453
|
+
// A custom size (inches without a preset) reads as the object itself. Unset reads as undefined (composed as widescreen).
|
|
454
|
+
const own = design("dimensions");
|
|
455
|
+
let size = own.value;
|
|
456
|
+
let scope = own.scope;
|
|
457
|
+
if (size === undefined) {
|
|
458
|
+
const themeId = idOf(design("theme").value);
|
|
459
|
+
size = themeId ? findCatalogRecord(document, "themes", themeId, {})?.dimensions : undefined;
|
|
460
|
+
scope = "deck";
|
|
461
|
+
}
|
|
462
|
+
return { value: size && typeof size === "object" && !Array.isArray(size) && Object.keys(size).length === 1 && size.preset ? size.preset : size, scope };
|
|
463
|
+
}
|
|
464
|
+
if (dimension === "purposes") {
|
|
465
|
+
// A catalog id or goal text reads as itself, a Purpose object as its id (else the object).
|
|
466
|
+
const purpose = document.purpose;
|
|
467
|
+
return { value: purpose && typeof purpose === "object" && !Array.isArray(purpose) ? (purpose.id ?? purpose) : purpose, scope: "deck" };
|
|
468
|
+
}
|
|
410
469
|
if (dimension === "socials") {
|
|
411
470
|
const host = document[options.owner ?? "speaker"];
|
|
412
471
|
const target = Array.isArray(host) ? host[options.index ?? 0] : host;
|