@openpresentation/opf-editor 0.12.1 → 0.14.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 +71 -60
- package/dist/annotations.d.ts +16 -16
- package/dist/annotations.js +71 -79
- package/dist/assets.d.ts +7 -7
- package/dist/assets.js +15 -15
- package/dist/background-options.d.ts +2 -2
- package/dist/background-options.js +6 -6
- package/dist/block-controls.js +7 -7
- package/dist/block-convert.d.ts +7 -7
- package/dist/block-convert.js +22 -21
- package/dist/blocks.d.ts +8 -8
- package/dist/blocks.js +41 -40
- package/dist/canvas-fields.js +35 -12
- package/dist/canvas.d.ts +22 -30
- package/dist/canvas.js +57 -64
- package/dist/chart-data.d.ts +10 -10
- package/dist/chart-data.js +21 -21
- package/dist/chart-options-panel.d.ts +2 -2
- package/dist/chart-options-panel.js +82 -8
- package/dist/chart-options.d.ts +35 -9
- package/dist/chart-options.js +133 -40
- package/dist/checks.js +21 -0
- package/dist/color-authoring.js +1 -1
- package/dist/content-actions.d.ts +15 -15
- package/dist/content-actions.js +71 -70
- package/dist/content-controls.js +12 -12
- package/dist/data-grid.js +7 -8
- package/dist/data.d.ts +4 -4
- package/dist/data.js +5 -5
- package/dist/dataset-refs.d.ts +2 -2
- package/dist/dataset-refs.js +5 -5
- package/dist/design-controls.js +61 -44
- package/dist/design-options.d.ts +16 -14
- package/dist/design-options.js +86 -74
- package/dist/edit-helpers.js +8 -7
- package/dist/exact-source.js +5 -5
- package/dist/export.d.ts +5 -7
- package/dist/export.js +40 -14
- package/dist/find-panel.js +5 -5
- package/dist/find-replace.d.ts +3 -3
- package/dist/find-replace.js +23 -20
- package/dist/font-gate.js +49 -58
- package/dist/galleries.js +36 -52
- package/dist/grid-model.d.ts +25 -27
- package/dist/grid-model.js +92 -102
- package/dist/image-crop.d.ts +4 -4
- package/dist/image-crop.js +20 -20
- package/dist/image-cropper.js +3 -3
- package/dist/index.d.ts +28 -28
- package/dist/index.js +74 -90
- package/dist/json-options.js +17 -16
- package/dist/layout-handles.js +5 -5
- package/dist/layout-placeholders.js +4 -4
- package/dist/layout.d.ts +2 -2
- package/dist/layout.js +8 -7
- package/dist/numbering-panel.js +3 -5
- package/dist/numbering.d.ts +3 -5
- package/dist/numbering.js +14 -26
- package/dist/outline-view.js +24 -24
- package/dist/outline.d.ts +12 -12
- package/dist/outline.js +52 -50
- package/dist/persistence.d.ts +1 -1
- package/dist/persistence.js +19 -18
- package/dist/presentation-ids.js +2 -2
- package/dist/react.js +3 -3
- package/dist/review-panel.d.ts +30 -13
- package/dist/review-panel.js +154 -53
- package/dist/review.d.ts +36 -43
- package/dist/review.js +92 -53
- package/dist/rich-text-input.js +4 -3
- package/dist/rich-text-toolbar.js +5 -3
- package/dist/rich-text.js +5 -2
- package/dist/schema-inspector.d.ts +2 -2
- package/dist/schema-inspector.js +16 -11
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +25 -5
- package/dist/slide-manager.d.ts +2 -2
- package/dist/slide-manager.js +18 -18
- package/dist/slides.d.ts +20 -20
- package/dist/slides.js +99 -97
- package/dist/svelte.js +1 -1
- package/dist/switches.d.ts +10 -10
- package/dist/switches.js +124 -114
- package/dist/table-options.d.ts +8 -8
- package/dist/table-options.js +28 -27
- package/dist/table-structure.d.ts +10 -10
- package/dist/table-structure.js +21 -21
- package/dist/template-panel.d.ts +3 -3
- package/dist/template-panel.js +37 -13
- package/dist/templates.d.ts +22 -11
- package/dist/templates.js +48 -55
- package/dist/transfer.d.ts +4 -4
- package/dist/transfer.js +49 -59
- package/package.json +8 -6
- package/dist/font-defaults.js +0 -25
package/dist/svelte.js
CHANGED
|
@@ -52,7 +52,7 @@ export function opfCatalogSelect(node, params) {
|
|
|
52
52
|
replaceOptions(node, getCatalogOptions(catalogKind, {
|
|
53
53
|
catalogs: nextParams.catalogs,
|
|
54
54
|
catalogSources: nextParams.catalogSources,
|
|
55
|
-
presentation: nextParams.presentation ?? editor.
|
|
55
|
+
presentation: nextParams.presentation ?? editor.presentation
|
|
56
56
|
}));
|
|
57
57
|
|
|
58
58
|
const sync = () => {
|
package/dist/switches.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ConvertOptions } from "@openpresentation/opf/convert";
|
|
|
2
2
|
import type { EditorChange, EditorSession, JsonPatchOperation } from "./index.js";
|
|
3
3
|
|
|
4
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"];
|
|
5
|
+
export declare const SLIDE_SIZE_PRESETS: readonly ["16:9", "4:3", "16:10", "1:1", "4:5", "9:16", "letter", "a4", "widescreen", "standard"];
|
|
6
6
|
export type SlideSizePreset = (typeof SLIDE_SIZE_PRESETS)[number];
|
|
7
7
|
|
|
8
8
|
/** Every switchable dimension: the 14 pptx.gallery dimensions, then `slide-sizes` and `purposes`. */
|
|
@@ -36,9 +36,9 @@ export interface DimensionSwitchOptions {
|
|
|
36
36
|
/** Extra caller-loaded catalog records by kind. */
|
|
37
37
|
catalogs?: Record<string, unknown>;
|
|
38
38
|
catalogSources?: Record<string, unknown>;
|
|
39
|
-
/** themes: also write the theme's color scheme, font scheme
|
|
39
|
+
/** themes: also write the theme's color scheme, font scheme and background (and, for the deck, dimensions) to the design (default true). A slide's design cannot set dimensions. */
|
|
40
40
|
bundle?: boolean;
|
|
41
|
-
/** Deck-scope design switches
|
|
41
|
+
/** Deck-scope design switches: remove slide-level values that would hide the switch. */
|
|
42
42
|
clearSlideOverrides?: boolean;
|
|
43
43
|
/** socials: which document field holds the handle (default "speaker") and, for an array, which entry (default 0). */
|
|
44
44
|
owner?: "speaker" | "organization";
|
|
@@ -66,7 +66,7 @@ export interface PreparedDimensionSwitch {
|
|
|
66
66
|
dimension: SwitchDimension;
|
|
67
67
|
scope: "deck" | "slide" | "block";
|
|
68
68
|
slideIndex?: number;
|
|
69
|
-
|
|
69
|
+
presentation: unknown;
|
|
70
70
|
patches: JsonPatchOperation[];
|
|
71
71
|
changed: boolean;
|
|
72
72
|
/** Slides whose own design hides a deck-level switch. */
|
|
@@ -75,8 +75,8 @@ export interface PreparedDimensionSwitch {
|
|
|
75
75
|
loss?: string[];
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
export interface DimensionSwitchChange extends Omit<EditorChange, "
|
|
79
|
-
|
|
78
|
+
export interface DimensionSwitchChange extends Omit<EditorChange, "presentation" | "patches"> {
|
|
79
|
+
presentation: unknown;
|
|
80
80
|
patches: JsonPatchOperation[];
|
|
81
81
|
dimension: SwitchDimension;
|
|
82
82
|
scope: "deck" | "slide" | "block";
|
|
@@ -87,7 +87,7 @@ export interface DimensionSwitchChange extends Omit<EditorChange, "document" | "
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/** Compute and validate the patch for one dimension without changing any session. */
|
|
90
|
-
export declare function prepareDimensionSwitch(
|
|
90
|
+
export declare function prepareDimensionSwitch(presentation: unknown, dimension: SwitchDimension, value: DimensionSwitchValue, options?: DimensionSwitchOptions): PreparedDimensionSwitch;
|
|
91
91
|
/** Apply one dimension switch to a session as a single undoable transaction. */
|
|
92
92
|
export declare function switchDimension(editor: EditorSession, dimension: SwitchDimension, value: DimensionSwitchValue, options?: DimensionSwitchOptions): DimensionSwitchChange;
|
|
93
93
|
|
|
@@ -101,9 +101,9 @@ export interface CompatibleChartType extends SwitchOption {
|
|
|
101
101
|
current: boolean;
|
|
102
102
|
}
|
|
103
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. */
|
|
104
|
-
export declare function listSwitchOptions(
|
|
104
|
+
export declare function listSwitchOptions(presentation: unknown, dimension: SwitchDimension, options?: Pick<DimensionSwitchOptions, "catalogs" | "catalogSources">): SwitchOption[];
|
|
105
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. */
|
|
106
|
-
export declare function compatibleChartTypes(
|
|
106
|
+
export declare function compatibleChartTypes(presentation: unknown, options?: Pick<DimensionSwitchOptions, "slideIndex" | "path" | "catalogs" | "catalogSources">): CompatibleChartType[];
|
|
107
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. */
|
|
108
|
-
export declare function currentSwitchValue(
|
|
108
|
+
export declare function currentSwitchValue(presentation: unknown, dimension: SwitchDimension, options?: Pick<DimensionSwitchOptions, "slideIndex" | "path" | "owner" | "index">): { value: unknown; scope: "deck" | "slide" | "block" };
|
|
109
109
|
export { blockConversionTargets } from "./block-convert.js";
|
package/dist/switches.js
CHANGED
|
@@ -2,30 +2,26 @@
|
|
|
2
2
|
// dimension that turns "switch this dimension to X" into a validated JSON Patch, and applies
|
|
3
3
|
// it to an editor session as one undoable transaction. The preview recomposes and the PPTX
|
|
4
4
|
// export follows from the same document, so no dimension needs a special refresh path.
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
OPFEditorError,
|
|
9
|
-
createValuePatch,
|
|
10
|
-
applyJsonPatch,
|
|
11
|
-
getValueAtPath,
|
|
12
|
-
opfPathToJsonPointer,
|
|
13
|
-
splitOpfPath,
|
|
14
|
-
validateOpfDocument,
|
|
15
|
-
} from "./index.js";
|
|
5
|
+
import { catalogSchemaNames, catalogs as bundledCatalogs, isXYChartType, resolveChartData, schemas } from "@openpresentation/opf";
|
|
6
|
+
import { chartOptionTarget } from "@openpresentation/opf/composition";
|
|
7
|
+
import { OPFEditorError, createValuePatch, applyJsonPatch, getValueAtPath, opfPathToJsonPointer, splitOpfPath } from "./index.js";
|
|
16
8
|
import { createContentBlock, prepareBlockReplace } from "./blocks.js";
|
|
17
9
|
import { checkedDocument, designPatches, fail, same } from "./edit-helpers.js";
|
|
18
10
|
import { populateLayoutPlaceholders } from "./layout-placeholders.js";
|
|
19
11
|
import { blockConversionTargets, prepareBlockConversion } from "./block-convert.js";
|
|
12
|
+
import { checkFormat } from "./checks.js";
|
|
20
13
|
|
|
21
|
-
/** The schema's DimensionPreset values (RR-41), the values of the slide-sizes switch. */
|
|
22
|
-
export const SLIDE_SIZE_PRESETS = Object.freeze(["16:9", "4:3", "16:10", "letter", "a4", "widescreen", "standard"]);
|
|
14
|
+
/** The schema's DimensionPreset values (RR-41, FA-13: with the social-feed ratios), the values of the slide-sizes switch. */
|
|
15
|
+
export const SLIDE_SIZE_PRESETS = Object.freeze(["16:9", "4:3", "16:10", "1:1", "4:5", "9:16", "letter", "a4", "widescreen", "standard"]);
|
|
23
16
|
|
|
24
17
|
// Slide size in inches per preset, as core's resolveCanvasDimensions composes it (for picker labels).
|
|
25
18
|
const SLIDE_SIZE_LABELS = Object.freeze({
|
|
26
19
|
"16:9": "16:9 (13.33 x 7.5 in)",
|
|
27
20
|
"4:3": "4:3 (10 x 7.5 in)",
|
|
28
21
|
"16:10": "16:10 (10 x 6.25 in)",
|
|
22
|
+
"1:1": "1:1 square (7.5 x 7.5 in)",
|
|
23
|
+
"4:5": "4:5 portrait (7.5 x 9.375 in)",
|
|
24
|
+
"9:16": "9:16 portrait (7.5 x 13.33 in)",
|
|
29
25
|
letter: "Letter (11 x 8.5 in)",
|
|
30
26
|
a4: "A4 (11.69 x 8.27 in)",
|
|
31
27
|
widescreen: "Widescreen (13.33 x 7.5 in)",
|
|
@@ -90,55 +86,55 @@ function recordList(source) {
|
|
|
90
86
|
|
|
91
87
|
// Inline document records first (they override), then caller-loaded records, then the
|
|
92
88
|
// bundled catalog. Unlike getCatalogRecords, an inline catalog never hides bundled ids.
|
|
93
|
-
function findCatalogRecord(
|
|
89
|
+
function findCatalogRecord(presentation, kind, id, options) {
|
|
94
90
|
return (
|
|
95
|
-
recordList(
|
|
91
|
+
recordList(presentation.catalogs?.[kind]).find((record) => record.id === id) ??
|
|
96
92
|
recordList(options.catalogs?.[kind] ?? options.catalogSources?.[kind]).find((record) => record.id === id) ??
|
|
97
93
|
(options.record?.id === id ? options.record : undefined) ??
|
|
98
94
|
bundledCatalogs[kind]?.find((record) => record.id === id)
|
|
99
95
|
);
|
|
100
96
|
}
|
|
101
97
|
|
|
102
|
-
function requireCatalogId(
|
|
98
|
+
function requireCatalogId(presentation, dimension, id, options) {
|
|
103
99
|
const kind = CATALOG_KIND[dimension];
|
|
104
100
|
if (typeof id !== "string" || id.length === 0)
|
|
105
101
|
throw fail("invalid-catalog-id", `Switch ${dimension} to a non-empty catalog id.`, { dimension, id });
|
|
106
|
-
const record = findCatalogRecord(
|
|
102
|
+
const record = findCatalogRecord(presentation, kind, id, options);
|
|
107
103
|
if (!record) throw fail("unknown-catalog-id", `Unknown ${kind} catalog ID: ${id}.`, { dimension, catalogKind: kind, id });
|
|
108
104
|
return record;
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
// A gallery item supplies its own record; add it inline in the same transaction when the
|
|
112
108
|
// document and the bundled catalog do not already define the id.
|
|
113
|
-
function catalogRecordPatches(
|
|
109
|
+
function catalogRecordPatches(presentation, dimension, options) {
|
|
114
110
|
const record = options.record;
|
|
115
111
|
if (!record) return [];
|
|
116
112
|
const kind = CATALOG_KIND[dimension];
|
|
117
|
-
if (recordList(
|
|
113
|
+
if (recordList(presentation.catalogs?.[kind]).some((entry) => entry.id === record.id) || bundledCatalogs[kind]?.some((entry) => entry.id === record.id)) return [];
|
|
118
114
|
const value = { $schema: schemas[catalogSchemaNames[kind]].$id, ...record };
|
|
119
|
-
if (!
|
|
120
|
-
if (!
|
|
115
|
+
if (!presentation.catalogs || typeof presentation.catalogs !== "object") return [{ op: "add", path: "/catalogs", value: { [kind]: { records: [value] } } }];
|
|
116
|
+
if (!presentation.catalogs[kind] || typeof presentation.catalogs[kind] !== "object")
|
|
121
117
|
return [{ op: "add", path: `/catalogs/${kind}`, value: { records: [value] } }];
|
|
122
|
-
if (!Array.isArray(
|
|
118
|
+
if (!Array.isArray(presentation.catalogs[kind].records)) return [{ op: "add", path: `/catalogs/${kind}/records`, value: [value] }];
|
|
123
119
|
return [{ op: "add", path: `/catalogs/${kind}/records/-`, value }];
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
function slideAt(
|
|
127
|
-
if (!Number.isInteger(slideIndex) || !
|
|
122
|
+
function slideAt(presentation, slideIndex, what = "this switch") {
|
|
123
|
+
if (!Number.isInteger(slideIndex) || !presentation.slides?.[slideIndex])
|
|
128
124
|
throw fail("slide-index-out-of-range", `Choose an existing slide for ${what}.`, { slideIndex });
|
|
129
|
-
return
|
|
125
|
+
return presentation.slides[slideIndex];
|
|
130
126
|
}
|
|
131
127
|
|
|
132
|
-
function rootPatch(
|
|
133
|
-
return same(
|
|
128
|
+
function rootPatch(presentation, field, value) {
|
|
129
|
+
return same(presentation[field], value) ? [] : createValuePatch(presentation, [field], value);
|
|
134
130
|
}
|
|
135
131
|
|
|
136
132
|
// A deck-level switch does nothing for a slide that carries its own value for that key.
|
|
137
|
-
function shadowedSlides(
|
|
138
|
-
return (
|
|
133
|
+
function shadowedSlides(presentation, keys) {
|
|
134
|
+
return (presentation.slides ?? []).flatMap((slide, index) => (keys.some((key) => slide?.design?.[key] !== undefined) ? [index] : []));
|
|
139
135
|
}
|
|
140
136
|
|
|
141
|
-
function findChartOwner(
|
|
137
|
+
function findChartOwner(presentation, slideIndex) {
|
|
142
138
|
const visit = (node, path, depth) => {
|
|
143
139
|
if (!node || typeof node !== "object" || Array.isArray(node) || depth > 32) return undefined;
|
|
144
140
|
if (node.chart && typeof node.chart === "object") return path;
|
|
@@ -154,23 +150,23 @@ function findChartOwner(document, slideIndex) {
|
|
|
154
150
|
}
|
|
155
151
|
return undefined;
|
|
156
152
|
};
|
|
157
|
-
return visit(
|
|
153
|
+
return visit(presentation.slides[slideIndex], ["slides", String(slideIndex)], 0);
|
|
158
154
|
}
|
|
159
155
|
|
|
160
|
-
function socialsPatches(
|
|
156
|
+
function socialsPatches(presentation, value, options) {
|
|
161
157
|
const owner = options.owner ?? "speaker";
|
|
162
158
|
if (!["speaker", "organization"].includes(owner)) throw fail("invalid-switch-value", "Socials owner must be 'speaker' or 'organization'.", { owner });
|
|
163
159
|
if (!value || typeof value !== "object" || typeof value.platform !== "string" || typeof value.handle !== "string" || !value.handle)
|
|
164
160
|
throw fail("invalid-switch-value", "Switch socials to { platform, handle } with a non-empty handle.", { value });
|
|
165
|
-
requireCatalogId(
|
|
166
|
-
const host =
|
|
161
|
+
requireCatalogId(presentation, "socials", value.platform, options);
|
|
162
|
+
const host = presentation[owner];
|
|
167
163
|
const index = Array.isArray(host) ? (options.index ?? 0) : undefined;
|
|
168
164
|
const target = Array.isArray(host) ? host[index] : host;
|
|
169
165
|
if (!target || typeof target !== "object")
|
|
170
166
|
throw fail("missing-owner", `Add the ${owner} to the document before setting their socials.`, { owner });
|
|
171
167
|
const base = Array.isArray(host) ? [owner, String(index)] : [owner];
|
|
172
168
|
const socials = target.socials;
|
|
173
|
-
const patches = catalogRecordPatches(
|
|
169
|
+
const patches = catalogRecordPatches(presentation, "socials", options);
|
|
174
170
|
if (!socials || typeof socials !== "object") return [...patches, { op: "add", path: opfPathToJsonPointer([...base, "socials"]), value: { [value.platform]: value.handle } }];
|
|
175
171
|
if (socials[value.platform] === value.handle) return patches;
|
|
176
172
|
return [...patches, { op: Object.hasOwn(socials, value.platform) ? "replace" : "add", path: opfPathToJsonPointer([...base, "socials", value.platform]), value: value.handle }];
|
|
@@ -189,16 +185,16 @@ function blockValue(value, options) {
|
|
|
189
185
|
* Compute the patch that switches one dimension, without touching any session. The candidate
|
|
190
186
|
* document is validated; an invalid result throws unless the input was already invalid.
|
|
191
187
|
*/
|
|
192
|
-
export function prepareDimensionSwitch(
|
|
188
|
+
export function prepareDimensionSwitch(presentation, dimension, value, options = {}) {
|
|
193
189
|
if (!SWITCH_DIMENSIONS.includes(dimension))
|
|
194
190
|
throw fail("unknown-dimension", `Unknown dimension: ${dimension}. Use one of ${SWITCH_DIMENSIONS.join(", ")}.`, { dimension });
|
|
195
|
-
if (!
|
|
191
|
+
if (!presentation || typeof presentation !== "object") throw fail("invalid-input", "Switch requires an OPF document object.");
|
|
196
192
|
if (options.record) {
|
|
197
193
|
const ids = dimension === "audiences" ? [value].flat() : dimension === "socials" ? [value?.platform] : [value];
|
|
198
194
|
if (!CATALOG_KIND[dimension] || !ids.includes(options.record.id))
|
|
199
195
|
throw fail("record-id-mismatch", `The supplied record id must equal a switched ${dimension} id.`, { dimension, id: options.record.id });
|
|
200
196
|
}
|
|
201
|
-
const before =
|
|
197
|
+
const before = checkFormat(presentation);
|
|
202
198
|
const scopeIndex = options.slideIndex;
|
|
203
199
|
let patches = [];
|
|
204
200
|
let scope = "deck";
|
|
@@ -210,28 +206,24 @@ export function prepareDimensionSwitch(document, dimension, value, options = {})
|
|
|
210
206
|
// A preset string; a custom size (design.dimensions as an object with inches) is replaced by it.
|
|
211
207
|
if (!SLIDE_SIZE_PRESETS.includes(value)) throw fail("invalid-switch-value", `Switch slide-sizes to one of ${SLIDE_SIZE_PRESETS.join(", ")}.`, { value });
|
|
212
208
|
if (scopeIndex !== undefined) throw fail("deck-scope-only", "A presentation has one slide size: switch slide-sizes for the deck, without slideIndex.", { slideIndex: scopeIndex });
|
|
213
|
-
const own =
|
|
209
|
+
const own = presentation.design?.dimensions;
|
|
214
210
|
// {preset} alone is the same size as the bare preset string.
|
|
215
211
|
const sameSize = own && typeof own === "object" && !Array.isArray(own) && own.preset === value && Object.keys(own).length === 1;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (options.clearSlideOverrides) {
|
|
219
|
-
for (const index of shadowed) patches.push({ op: "remove", path: opfPathToJsonPointer(["slides", String(index), "design", "dimensions"]) });
|
|
220
|
-
shadowed = [];
|
|
221
|
-
}
|
|
212
|
+
// A slide's design cannot set dimensions (FA-07), so no slide can shadow the deck's size.
|
|
213
|
+
patches = sameSize ? [] : designPatches(presentation, [], { dimensions: value });
|
|
222
214
|
} else if (dimension === "purposes") {
|
|
223
215
|
// A catalog id, free-form goal text (no id check: any goal is valid) or an inline Purpose object; the schema validates it.
|
|
224
216
|
if (typeof value !== "string" && !(value && typeof value === "object" && !Array.isArray(value)))
|
|
225
217
|
throw fail("invalid-switch-value", "Switch purposes to a catalog id, a goal string or a purpose object.", { value });
|
|
226
218
|
if (typeof value === "string" && value.length === 0) throw fail("invalid-switch-value", "Switch purposes to a non-empty goal.", { value });
|
|
227
|
-
patches = [...catalogRecordPatches(
|
|
219
|
+
patches = [...catalogRecordPatches(presentation, dimension, options), ...rootPatch(presentation, "purpose", value)];
|
|
228
220
|
} else if (dimension === "layouts") {
|
|
229
221
|
slideIndex = scopeIndex;
|
|
230
|
-
slideAt(
|
|
222
|
+
slideAt(presentation, slideIndex, "a layout switch");
|
|
231
223
|
scope = "slide";
|
|
232
|
-
const record = requireCatalogId(
|
|
233
|
-
patches = catalogRecordPatches(
|
|
234
|
-
const withRecord = patches.length ? applyJsonPatch(
|
|
224
|
+
const record = requireCatalogId(presentation, dimension, value, options);
|
|
225
|
+
patches = catalogRecordPatches(presentation, dimension, options);
|
|
226
|
+
const withRecord = patches.length ? applyJsonPatch(presentation, patches) : presentation;
|
|
235
227
|
if (withRecord.slides[slideIndex].layout !== value) {
|
|
236
228
|
const layoutOps = createValuePatch(withRecord, ["slides", String(slideIndex), "layout"], value);
|
|
237
229
|
const swapped = applyJsonPatch(withRecord, layoutOps);
|
|
@@ -244,64 +236,65 @@ export function prepareDimensionSwitch(document, dimension, value, options = {})
|
|
|
244
236
|
}
|
|
245
237
|
} else if (dimension === "charts") {
|
|
246
238
|
slideIndex = scopeIndex;
|
|
247
|
-
slideAt(
|
|
239
|
+
slideAt(presentation, slideIndex, "a chart switch");
|
|
248
240
|
scope = "slide";
|
|
249
|
-
requireCatalogId(
|
|
250
|
-
const owner = options.path ? splitOpfPath(options.path) : findChartOwner(
|
|
241
|
+
requireCatalogId(presentation, dimension, value, options);
|
|
242
|
+
const owner = options.path ? splitOpfPath(options.path) : findChartOwner(presentation, slideIndex);
|
|
251
243
|
if (options.path && (owner[0] !== "slides" || owner[1] !== String(slideIndex)))
|
|
252
244
|
throw fail("path-slide-mismatch", "options.path is not on the slide named by options.slideIndex.", { slideIndex, path: options.path });
|
|
253
|
-
const chart = owner && getValueAtPath(
|
|
245
|
+
const chart = owner && getValueAtPath(presentation, [...owner, "chart"]);
|
|
254
246
|
if (!chart || typeof chart !== "object") throw fail("chart-not-found", "This slide has no chart to switch. Insert a chart block first.", { slideIndex, path: options.path });
|
|
255
|
-
patches = [...catalogRecordPatches(
|
|
247
|
+
patches = [...catalogRecordPatches(presentation, dimension, options), ...(chart.type === value ? [] : [...createValuePatch(presentation, [...owner, "chart", "type"], value), ...staleMappingPatches(presentation, owner, chart, value), ...staleComboPatches(presentation, owner, chart, value)])];
|
|
256
248
|
} else if (dimension === "blocks") {
|
|
257
249
|
if (options.path === undefined) throw fail("missing-path", "Choose the block to replace with options.path.");
|
|
258
250
|
// convert: true moves the block's own text into the new kind (block-convert.js) instead of replacing it.
|
|
259
251
|
if (options.convert) {
|
|
260
252
|
if (typeof value !== "string") throw fail("invalid-switch-value", "Convert a block to a content kind name.", { value });
|
|
261
|
-
const conversion = prepareBlockConversion(
|
|
253
|
+
const conversion = prepareBlockConversion(presentation, options.path, value, options.conversion);
|
|
262
254
|
patches = conversion.changed ? conversion.patches : [];
|
|
263
255
|
conversionLoss = conversion.loss;
|
|
264
256
|
} else {
|
|
265
257
|
const block = blockValue(value, options);
|
|
266
|
-
const change = prepareBlockReplace(
|
|
258
|
+
const change = prepareBlockReplace(presentation, options.path, block);
|
|
267
259
|
patches = change.changed ? change.patches : [];
|
|
268
260
|
}
|
|
269
261
|
const parts = splitOpfPath(options.path);
|
|
270
262
|
if (parts[0] === "slides") slideIndex = Number(parts[1]);
|
|
271
263
|
scope = "block";
|
|
272
264
|
} else if (dimension === "socials") {
|
|
273
|
-
patches = socialsPatches(
|
|
265
|
+
patches = socialsPatches(presentation, value, options);
|
|
274
266
|
} else if (ROOT_FIELD[dimension]) {
|
|
275
267
|
const field = ROOT_FIELD[dimension];
|
|
276
268
|
let next = value;
|
|
277
269
|
if (dimension === "audiences") {
|
|
278
270
|
next = Array.isArray(value) ? value : [value];
|
|
279
271
|
if (!next.length) throw fail("invalid-catalog-id", "Switch audiences to at least one catalog id.", { value });
|
|
280
|
-
for (const id of next) requireCatalogId(
|
|
281
|
-
} else requireCatalogId(
|
|
282
|
-
patches = [...catalogRecordPatches(
|
|
272
|
+
for (const id of next) requireCatalogId(presentation, dimension, id, options);
|
|
273
|
+
} else requireCatalogId(presentation, dimension, value, options);
|
|
274
|
+
patches = [...catalogRecordPatches(presentation, dimension, options), ...rootPatch(presentation, field, next)];
|
|
283
275
|
} else {
|
|
284
276
|
// Design dimensions: deck scope by default, or one slide with options.slideIndex.
|
|
285
277
|
let entries;
|
|
286
278
|
if (dimension === "themes") {
|
|
287
|
-
const record = requireCatalogId(
|
|
279
|
+
const record = requireCatalogId(presentation, dimension, value, options);
|
|
288
280
|
entries = { theme: value };
|
|
289
281
|
if (options.bundle !== false) {
|
|
290
282
|
// The gallery's theme snippet writes the whole bundle, so an explicit deck choice
|
|
291
283
|
// does not keep the previous theme's fonts, colors or background.
|
|
292
|
-
|
|
284
|
+
// A slide's design cannot set dimensions (a PPTX has one slide size), so only a deck-scope switch writes the theme's.
|
|
285
|
+
for (const key of ["colorScheme", "fontScheme", "background", ...(scopeIndex === undefined ? ["dimensions"] : [])]) if (record[key] !== undefined) entries[key] = record[key];
|
|
293
286
|
}
|
|
294
|
-
patches = catalogRecordPatches(
|
|
287
|
+
patches = catalogRecordPatches(presentation, dimension, options);
|
|
295
288
|
} else if (dimension === "color-schemes" || dimension === "font-schemes") {
|
|
296
|
-
requireCatalogId(
|
|
289
|
+
requireCatalogId(presentation, dimension, value, options);
|
|
297
290
|
entries = { [DESIGN_KEYS[dimension][0]]: value };
|
|
298
291
|
if (dimension === "font-schemes") {
|
|
299
292
|
// An accent font is its own choice, not part of the scheme being left: it stays (in the object form).
|
|
300
|
-
const scopeBase = scopeIndex !== undefined &&
|
|
301
|
-
const own = getValueAtPath(
|
|
293
|
+
const scopeBase = scopeIndex !== undefined && presentation.slides?.[scopeIndex] ? ["slides", String(scopeIndex)] : [];
|
|
294
|
+
const own = getValueAtPath(presentation, [...scopeBase, "design", "fontScheme"]);
|
|
302
295
|
if (own && typeof own === "object" && own.accent !== undefined) entries.fontScheme = { id: value, accent: structuredClone(own.accent) };
|
|
303
296
|
}
|
|
304
|
-
patches = catalogRecordPatches(
|
|
297
|
+
patches = catalogRecordPatches(presentation, dimension, options);
|
|
305
298
|
} else {
|
|
306
299
|
// A background is an object or a shorthand string (theme slot or hex color); the schema
|
|
307
300
|
// validates the candidate document, so a string that is neither is rejected below.
|
|
@@ -318,29 +311,29 @@ export function prepareDimensionSwitch(document, dimension, value, options = {})
|
|
|
318
311
|
}
|
|
319
312
|
let base = [];
|
|
320
313
|
if (scopeIndex !== undefined) {
|
|
321
|
-
slideAt(
|
|
314
|
+
slideAt(presentation, scopeIndex, `a ${dimension} switch`);
|
|
322
315
|
base = ["slides", String(scopeIndex)];
|
|
323
316
|
scope = "slide";
|
|
324
317
|
slideIndex = scopeIndex;
|
|
325
318
|
}
|
|
326
|
-
patches = [...patches, ...designPatches(
|
|
319
|
+
patches = [...patches, ...designPatches(presentation, base, entries)];
|
|
327
320
|
if (scope === "deck") {
|
|
328
321
|
const keys = Object.entries(entries).filter(([, entry]) => entry !== undefined).map(([key]) => key);
|
|
329
|
-
shadowed = shadowedSlides(
|
|
322
|
+
shadowed = shadowedSlides(presentation, keys);
|
|
330
323
|
if (options.clearSlideOverrides)
|
|
331
324
|
for (const index of shadowed)
|
|
332
325
|
for (const key of keys)
|
|
333
|
-
if (
|
|
326
|
+
if (presentation.slides[index].design?.[key] !== undefined) patches.push({ op: "remove", path: opfPathToJsonPointer(["slides", String(index), "design", key]) });
|
|
334
327
|
if (options.clearSlideOverrides) shadowed = [];
|
|
335
328
|
}
|
|
336
329
|
}
|
|
337
330
|
|
|
338
|
-
const next = checkedDocument(
|
|
331
|
+
const next = checkedDocument(presentation, patches, before);
|
|
339
332
|
return {
|
|
340
333
|
dimension,
|
|
341
334
|
scope,
|
|
342
335
|
...(slideIndex !== undefined ? { slideIndex } : {}),
|
|
343
|
-
|
|
336
|
+
presentation: structuredClone(next),
|
|
344
337
|
patches,
|
|
345
338
|
changed: patches.length > 0,
|
|
346
339
|
shadowed,
|
|
@@ -357,17 +350,17 @@ export function switchDimension(editor, dimension, value, options = {}) {
|
|
|
357
350
|
if (!editor || typeof editor.applyPatch !== "function" || typeof editor.subscribe !== "function")
|
|
358
351
|
throw fail("invalid-editor", "Expected an editor session created by createEditorSession.");
|
|
359
352
|
const { meta, ...switchOptions } = options;
|
|
360
|
-
const prepared = prepareDimensionSwitch(editor.
|
|
361
|
-
const {
|
|
362
|
-
void
|
|
363
|
-
if (!prepared.changed) return { ...summary,
|
|
353
|
+
const prepared = prepareDimensionSwitch(editor.presentation, dimension, value, switchOptions);
|
|
354
|
+
const { presentation, patches, ...summary } = prepared;
|
|
355
|
+
void presentation;
|
|
356
|
+
if (!prepared.changed) return { ...summary, presentation: editor.presentation, patches: [], inversePatches: [], validation: editor.validation };
|
|
364
357
|
const change = editor.applyPatch(patches, { ...meta, source: meta?.source ?? "dimension-switch", dimension, scope: prepared.scope });
|
|
365
358
|
return { ...change, ...summary };
|
|
366
359
|
}
|
|
367
360
|
|
|
368
361
|
// --- options for pickers and current values ---------------------------------------------------
|
|
369
362
|
|
|
370
|
-
const labelOf = (record) => record.
|
|
363
|
+
const labelOf = (record) => record.name ?? record.id;
|
|
371
364
|
|
|
372
365
|
/**
|
|
373
366
|
* The values a picker can offer for a catalog-backed dimension, in document order: the document's
|
|
@@ -375,14 +368,14 @@ const labelOf = (record) => record.label ?? record.name ?? record.id;
|
|
|
375
368
|
* without duplicates. `blocks` lists the content kinds. `charts` lists every chart type; use
|
|
376
369
|
* `compatibleChartTypes` to narrow it to the types the chart's data can use.
|
|
377
370
|
*/
|
|
378
|
-
export function listSwitchOptions(
|
|
371
|
+
export function listSwitchOptions(presentation, dimension, options = {}) {
|
|
379
372
|
if (dimension === "blocks") return BLOCK_KINDS.map((kind) => ({ id: kind, label: kind[0].toUpperCase() + kind.slice(1) }));
|
|
380
373
|
if (dimension === "slide-sizes") return SLIDE_SIZE_PRESETS.map((preset) => ({ id: preset, label: SLIDE_SIZE_LABELS[preset] }));
|
|
381
374
|
const kind = CATALOG_KIND[dimension];
|
|
382
375
|
if (!kind) return [];
|
|
383
376
|
const seen = new Set();
|
|
384
377
|
const out = [];
|
|
385
|
-
for (const source of [
|
|
378
|
+
for (const source of [presentation?.catalogs?.[kind], options.catalogs?.[kind] ?? options.catalogSources?.[kind], bundledCatalogs[kind]])
|
|
386
379
|
for (const record of recordList(source)) {
|
|
387
380
|
if (seen.has(record.id)) continue;
|
|
388
381
|
seen.add(record.id);
|
|
@@ -393,27 +386,38 @@ export function listSwitchOptions(document, dimension, options = {}) {
|
|
|
393
386
|
|
|
394
387
|
const SINGLE_SERIES_ONLY = new Set(["pieChart", "doughnutChart", "funnelChart", "treemapChart", "waterfallChart"]);
|
|
395
388
|
const MULTI_SERIES_CAPABLE = new Set(["barChart", "lineChart", "areaChart", "radarChart"]);
|
|
389
|
+
const STACKED_GROUPINGS = new Set(["stacked", "percentStacked"]);
|
|
396
390
|
const DISTRIBUTION_ELEMENTS = new Set(["histogramChart", "boxWhiskerChart", "mapChart"]);
|
|
397
391
|
|
|
398
392
|
// RR-54: `mapping.x` names the X column of an XY chart; a type without an X axis ignores it (core warns chart-mapping-adapted). Switching to such a
|
|
399
393
|
// type removes it, and the whole mapping when nothing else is left, so the document does not keep a warning it cannot act on.
|
|
400
|
-
function staleMappingPatches(
|
|
394
|
+
function staleMappingPatches(presentation, owner, chart, type) {
|
|
401
395
|
const mapping = chart.mapping;
|
|
402
396
|
if (!mapping || typeof mapping !== "object" || Array.isArray(mapping) || mapping.x === undefined) return [];
|
|
403
|
-
if (
|
|
397
|
+
if (isXYChartType(type)) return [];
|
|
404
398
|
const { x: _x, ...rest } = mapping;
|
|
405
399
|
const parts = [...owner, "chart", "mapping"];
|
|
406
|
-
return Object.keys(rest).length ? createValuePatch(
|
|
400
|
+
return Object.keys(rest).length ? createValuePatch(presentation, parts, rest) : [{ op: "remove", path: opfPathToJsonPointer(parts) }];
|
|
407
401
|
}
|
|
408
402
|
|
|
409
|
-
|
|
403
|
+
// FA-15: `line`, `secondaryAxis` and the secondary axis title belong to combo charts; any other type ignores them (core warns
|
|
404
|
+
// chart-option-adapted). Switching away from combo removes them, and an `axisTitles` left empty.
|
|
405
|
+
function staleComboPatches(presentation, owner, chart, type) {
|
|
406
|
+
if (chartOptionTarget(type)?.kind === "combo") return [];
|
|
407
|
+
const parts = [...owner, "chart"];
|
|
408
|
+
const patches = ["line", "secondaryAxis"].filter((key) => chart[key] !== undefined).map((key) => ({ op: "remove", path: opfPathToJsonPointer([...parts, key]) }));
|
|
409
|
+
const titles = chart.axisTitles;
|
|
410
|
+
if (titles && typeof titles === "object" && !Array.isArray(titles) && titles.secondary !== undefined) {
|
|
411
|
+
const { secondary: _secondary, ...rest } = titles;
|
|
412
|
+
patches.push(Object.keys(rest).length ? { op: "remove", path: opfPathToJsonPointer([...parts, "axisTitles", "secondary"]) } : { op: "remove", path: opfPathToJsonPointer([...parts, "axisTitles"]) });
|
|
413
|
+
}
|
|
414
|
+
return patches;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function chartDataShape(chart, presentation) {
|
|
410
418
|
// RR-54: inline data, a dataset reference and a series mapping all resolve to the columns the renderers read: the first column labels
|
|
411
419
|
// the categories and every further column is a series.
|
|
412
|
-
|
|
413
|
-
const data = chart?.data;
|
|
414
|
-
return data && Array.isArray(data.columns) && Array.isArray(data.rows) ? { series: Math.max(0, data.columns.length - 1), categories: data.rows.length } : undefined;
|
|
415
|
-
}
|
|
416
|
-
const resolved = core.resolveChartData(chart, document);
|
|
420
|
+
const resolved = resolveChartData(chart, presentation);
|
|
417
421
|
if (!resolved.ok) return undefined;
|
|
418
422
|
return { series: Math.max(0, resolved.columns.length - 1), categories: resolved.rows.length };
|
|
419
423
|
}
|
|
@@ -422,25 +426,34 @@ function chartDataShape(chart, document) {
|
|
|
422
426
|
* Chart types the chart's inline data can use as it is, from the chartTypes catalog: simple,
|
|
423
427
|
* non-geographic, non-distribution types whose series count fits the data (the first column labels
|
|
424
428
|
* the categories and each further column is a series; a type with N series needs exactly N value
|
|
425
|
-
* columns
|
|
429
|
+
* columns, except that a stacked or percent-stacked type and a combination such as combo take N or more;
|
|
430
|
+
* column, bar, line, area and radar take any number). Data that is read from
|
|
426
431
|
* an external source returns every simple type. This is data-shape compatibility, not a claim that
|
|
427
432
|
* an engine draws the type. `path` or `slideIndex` picks the chart (default: the slide's first).
|
|
428
433
|
* Each entry has `current: true` for the chart's present type, which is always listed.
|
|
429
434
|
*/
|
|
430
|
-
export function compatibleChartTypes(
|
|
435
|
+
export function compatibleChartTypes(presentation, options = {}) {
|
|
431
436
|
const slideIndex = options.slideIndex ?? 0;
|
|
432
|
-
const owner = options.path ? splitOpfPath(options.path) :
|
|
433
|
-
const chart = owner && getValueAtPath(
|
|
437
|
+
const owner = options.path ? splitOpfPath(options.path) : presentation.slides?.[slideIndex] ? findChartOwner(presentation, slideIndex) : undefined;
|
|
438
|
+
const chart = owner && getValueAtPath(presentation, [...owner, "chart"]);
|
|
434
439
|
if (!chart || typeof chart !== "object") return [];
|
|
435
|
-
const shape = chartDataShape(chart,
|
|
440
|
+
const shape = chartDataShape(chart, presentation);
|
|
436
441
|
const result = [];
|
|
437
|
-
for (const option of listSwitchOptions(
|
|
442
|
+
for (const option of listSwitchOptions(presentation, "charts", options)) {
|
|
438
443
|
const record = option.record;
|
|
439
444
|
const element = record.mappings?.openxml?.element;
|
|
440
445
|
const current = option.id === chart.type;
|
|
441
|
-
const simple = record.complexity === "simple" &&
|
|
446
|
+
const simple = record.complexity === "simple" && !DISTRIBUTION_ELEMENTS.has(element);
|
|
447
|
+
// A combination (composition "mixed", the FA-15 combo chart) needs at least its series count, like a stacked type.
|
|
448
|
+
const atLeast = STACKED_GROUPINGS.has(record.mappings?.openxml?.grouping) || record.mappings?.openxml?.composition === "mixed";
|
|
442
449
|
const seriesOk =
|
|
443
|
-
!shape ||
|
|
450
|
+
!shape ||
|
|
451
|
+
!record.series ||
|
|
452
|
+
(record.series > 1
|
|
453
|
+
? atLeast
|
|
454
|
+
? shape.series >= record.series
|
|
455
|
+
: record.series === shape.series
|
|
456
|
+
: shape.series === 1 || (MULTI_SERIES_CAPABLE.has(element) && !SINGLE_SERIES_ONLY.has(element)));
|
|
444
457
|
if (current || (simple && seriesOk)) result.push({ id: option.id, label: option.label, current, record });
|
|
445
458
|
}
|
|
446
459
|
return result;
|
|
@@ -452,43 +465,40 @@ export function compatibleChartTypes(document, options = {}) {
|
|
|
452
465
|
* dimension is unset. Catalog dimensions return the catalog id even when the document holds an
|
|
453
466
|
* inline object.
|
|
454
467
|
*/
|
|
455
|
-
export function currentSwitchValue(
|
|
468
|
+
export function currentSwitchValue(presentation, dimension, options = {}) {
|
|
456
469
|
const idOf = (reference) => (reference && typeof reference === "object" && !Array.isArray(reference) ? reference.id : reference);
|
|
457
470
|
const design = (key) => {
|
|
458
|
-
const slide = options.slideIndex !== undefined ?
|
|
471
|
+
const slide = options.slideIndex !== undefined ? presentation.slides?.[options.slideIndex]?.design?.[key] : undefined;
|
|
459
472
|
if (slide !== undefined) return { value: slide, scope: "slide" };
|
|
460
|
-
return
|
|
473
|
+
return presentation.design?.[key] === undefined ? { scope: "deck" } : { value: presentation.design[key], scope: "deck" };
|
|
461
474
|
};
|
|
462
|
-
if (dimension === "layouts") return { value:
|
|
475
|
+
if (dimension === "layouts") return { value: presentation.slides?.[options.slideIndex]?.layout, scope: "slide" };
|
|
463
476
|
if (dimension === "charts") {
|
|
464
|
-
const owner = options.path ? splitOpfPath(options.path) : findChartOwner(
|
|
465
|
-
return { value: owner ? getValueAtPath(
|
|
477
|
+
const owner = options.path ? splitOpfPath(options.path) : findChartOwner(presentation, options.slideIndex ?? 0);
|
|
478
|
+
return { value: owner ? getValueAtPath(presentation, [...owner, "chart", "type"]) : undefined, scope: "slide" };
|
|
466
479
|
}
|
|
467
480
|
if (dimension === "blocks") return { value: undefined, scope: "block" };
|
|
468
481
|
if (dimension === "slide-sizes") {
|
|
469
482
|
// The size the deck composes at: its own design.dimensions, else the theme's; a {preset} object reads as its preset.
|
|
470
483
|
// A custom size (inches without a preset) reads as the object itself. Unset reads as undefined (composed as widescreen).
|
|
471
|
-
|
|
472
|
-
let size = own.value;
|
|
473
|
-
let scope = own.scope;
|
|
484
|
+
let size = presentation.design?.dimensions;
|
|
474
485
|
if (size === undefined) {
|
|
475
|
-
const themeId = idOf(design
|
|
476
|
-
size = themeId ? findCatalogRecord(
|
|
477
|
-
scope = "deck";
|
|
486
|
+
const themeId = idOf(presentation.design?.theme);
|
|
487
|
+
size = themeId ? findCatalogRecord(presentation, "themes", themeId, {})?.dimensions : undefined;
|
|
478
488
|
}
|
|
479
|
-
return { value: size && typeof size === "object" && !Array.isArray(size) && Object.keys(size).length === 1 && size.preset ? size.preset : size, scope };
|
|
489
|
+
return { value: size && typeof size === "object" && !Array.isArray(size) && Object.keys(size).length === 1 && size.preset ? size.preset : size, scope: "deck" };
|
|
480
490
|
}
|
|
481
491
|
if (dimension === "purposes") {
|
|
482
492
|
// A catalog id or goal text reads as itself, a Purpose object as its id (else the object).
|
|
483
|
-
const purpose =
|
|
493
|
+
const purpose = presentation.purpose;
|
|
484
494
|
return { value: purpose && typeof purpose === "object" && !Array.isArray(purpose) ? (purpose.id ?? purpose) : purpose, scope: "deck" };
|
|
485
495
|
}
|
|
486
496
|
if (dimension === "socials") {
|
|
487
|
-
const host =
|
|
497
|
+
const host = presentation[options.owner ?? "speaker"];
|
|
488
498
|
const target = Array.isArray(host) ? host[options.index ?? 0] : host;
|
|
489
499
|
return { value: target?.socials, scope: "deck" };
|
|
490
500
|
}
|
|
491
|
-
if (ROOT_FIELD[dimension]) return { value:
|
|
501
|
+
if (ROOT_FIELD[dimension]) return { value: presentation[ROOT_FIELD[dimension]], scope: "deck" };
|
|
492
502
|
if (dimension === "color-schemes") return { ...design("colorScheme"), value: idOf(design("colorScheme").value) };
|
|
493
503
|
if (dimension === "font-schemes") return { ...design("fontScheme"), value: idOf(design("fontScheme").value) };
|
|
494
504
|
if (dimension === "themes") return { ...design("theme"), value: idOf(design("theme").value) };
|