@openpresentation/opf-editor 0.12.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +122 -80
  2. package/dist/annotations.d.ts +16 -16
  3. package/dist/annotations.js +71 -79
  4. package/dist/assets.d.ts +7 -7
  5. package/dist/assets.js +16 -16
  6. package/dist/background-options.d.ts +12 -5
  7. package/dist/background-options.js +44 -19
  8. package/dist/block-controls.js +7 -7
  9. package/dist/block-convert.d.ts +7 -7
  10. package/dist/block-convert.js +22 -21
  11. package/dist/blocks.d.ts +8 -8
  12. package/dist/blocks.js +41 -40
  13. package/dist/canvas-fields.js +35 -12
  14. package/dist/canvas.d.ts +30 -31
  15. package/dist/canvas.js +64 -68
  16. package/dist/catalogs.d.ts +48 -0
  17. package/dist/catalogs.js +217 -0
  18. package/dist/chart-data.d.ts +10 -10
  19. package/dist/chart-data.js +21 -21
  20. package/dist/chart-options-panel.d.ts +2 -2
  21. package/dist/chart-options-panel.js +82 -8
  22. package/dist/chart-options.d.ts +35 -9
  23. package/dist/chart-options.js +133 -40
  24. package/dist/checks.js +23 -0
  25. package/dist/color-authoring.js +1 -1
  26. package/dist/content-actions.d.ts +17 -17
  27. package/dist/content-actions.js +74 -73
  28. package/dist/content-controls.js +21 -21
  29. package/dist/data-grid.js +7 -8
  30. package/dist/data.d.ts +4 -4
  31. package/dist/data.js +5 -5
  32. package/dist/dataset-refs.d.ts +2 -2
  33. package/dist/dataset-refs.js +5 -5
  34. package/dist/design-controls.d.ts +12 -5
  35. package/dist/design-controls.js +312 -111
  36. package/dist/design-options.d.ts +20 -18
  37. package/dist/design-options.js +91 -99
  38. package/dist/edit-helpers.js +8 -7
  39. package/dist/exact-source.js +5 -5
  40. package/dist/export.d.ts +7 -7
  41. package/dist/export.js +48 -17
  42. package/dist/find-panel.js +5 -5
  43. package/dist/find-replace.d.ts +3 -3
  44. package/dist/find-replace.js +23 -20
  45. package/dist/font-gate.js +49 -58
  46. package/dist/galleries.js +50 -101
  47. package/dist/grid-model.d.ts +25 -27
  48. package/dist/grid-model.js +92 -102
  49. package/dist/image-crop.d.ts +4 -4
  50. package/dist/image-crop.js +28 -29
  51. package/dist/image-cropper.js +3 -3
  52. package/dist/image-options.d.ts +45 -0
  53. package/dist/image-options.js +195 -0
  54. package/dist/index.d.ts +48 -31
  55. package/dist/index.js +154 -126
  56. package/dist/json-editor.js +2 -2
  57. package/dist/json-menu.js +2 -2
  58. package/dist/json-options.d.ts +3 -3
  59. package/dist/json-options.js +44 -54
  60. package/dist/layout-handles.js +5 -5
  61. package/dist/layout-placeholders.js +4 -4
  62. package/dist/layout.d.ts +2 -2
  63. package/dist/layout.js +8 -7
  64. package/dist/numbering-panel.js +3 -5
  65. package/dist/numbering.d.ts +3 -5
  66. package/dist/numbering.js +14 -26
  67. package/dist/outline-view.js +24 -24
  68. package/dist/outline.d.ts +12 -12
  69. package/dist/outline.js +52 -50
  70. package/dist/persistence.d.ts +1 -1
  71. package/dist/persistence.js +19 -18
  72. package/dist/presentation-ids.js +2 -2
  73. package/dist/react.js +3 -3
  74. package/dist/review-panel.d.ts +30 -13
  75. package/dist/review-panel.js +160 -53
  76. package/dist/review.d.ts +36 -43
  77. package/dist/review.js +92 -53
  78. package/dist/rich-text-input.js +4 -3
  79. package/dist/rich-text-toolbar.js +5 -3
  80. package/dist/rich-text.js +5 -2
  81. package/dist/schema-inspector.d.ts +2 -2
  82. package/dist/schema-inspector.js +16 -11
  83. package/dist/schema.d.ts +1 -1
  84. package/dist/schema.js +26 -6
  85. package/dist/slide-manager.d.ts +2 -2
  86. package/dist/slide-manager.js +19 -19
  87. package/dist/slides.d.ts +21 -21
  88. package/dist/slides.js +104 -101
  89. package/dist/svelte.js +2 -3
  90. package/dist/switches.d.ts +37 -22
  91. package/dist/switches.js +282 -171
  92. package/dist/table-options.d.ts +8 -8
  93. package/dist/table-options.js +28 -27
  94. package/dist/table-structure.d.ts +10 -10
  95. package/dist/table-structure.js +21 -21
  96. package/dist/template-panel.d.ts +3 -3
  97. package/dist/template-panel.js +37 -13
  98. package/dist/templates.d.ts +22 -11
  99. package/dist/templates.js +48 -55
  100. package/dist/transfer.d.ts +15 -4
  101. package/dist/transfer.js +107 -161
  102. package/package.json +12 -6
  103. package/dist/font-defaults.js +0 -25
@@ -1,8 +1,8 @@
1
- // Content actions (RR-26): list levels, grouping, regions, images between content and design, and slide
1
+ // Content actions (RR-26): list levels, grouping, regions, images between content and design (background or watermark), and slide
2
2
  // split and merge, as editor transactions. The pure transforms live in core (`@openpresentation/opf/convert`);
3
3
  // each action here finds its target in the document, calls the transform, turns the result into a guarded patch
4
4
  // (`test` of what was read, then `replace`, or per-slide `remove` and `add`), validates the document and, in the
5
- // apply form, applies it as ONE undoable step. Every prepare function returns `{ document, patches, changed,
5
+ // apply form, applies it as ONE undoable step. Every prepare function returns `{ presentation, patches, changed,
6
6
  // lossless, loss, path }` without touching a session; a refusal throws `content-action-refused`.
7
7
  import {
8
8
  OPFConversionError,
@@ -19,9 +19,10 @@ import {
19
19
  unwrapGroup,
20
20
  wrapBlocks,
21
21
  } from "@openpresentation/opf/convert";
22
- import { applyJsonPatch, getValueAtPath, opfPathToJsonPointer, splitOpfPath, validateOpfDocument } from "./index.js";
22
+ import { applyJsonPatch, getValueAtPath, opfPathToJsonPointer, splitOpfPath } from "./index.js";
23
23
  import { readBlockContent } from "./block-convert.js";
24
24
  import { checkedDocument, fail } from "./edit-helpers.js";
25
+ import { checkFormat } from "./checks.js";
25
26
 
26
27
  const refuse = (message, details) => fail("content-action-refused", message, details);
27
28
 
@@ -39,14 +40,14 @@ function requireEditor(editor) {
39
40
  if (!editor || typeof editor.applyPatch !== "function") throw fail("invalid-editor", "Expected an editor session created by createEditorSession.");
40
41
  }
41
42
 
42
- function slideParts(document, path, what) {
43
+ function slideParts(presentation, path, what) {
43
44
  let parts;
44
45
  try {
45
46
  parts = splitOpfPath(path);
46
47
  } catch {
47
48
  parts = undefined;
48
49
  }
49
- if (!parts || parts[0] !== "slides" || !/^(0|[1-9][0-9]*)$/.test(parts[1] ?? "") || !document?.slides?.[Number(parts[1])]) throw refuse(`Choose ${what} on a slide.`, { path });
50
+ if (!parts || parts[0] !== "slides" || !/^(0|[1-9][0-9]*)$/.test(parts[1] ?? "") || !presentation?.slides?.[Number(parts[1])]) throw refuse(`Choose ${what} on a slide.`, { path });
50
51
  return { slideIndex: Number(parts[1]), relative: parts.slice(2), parts };
51
52
  }
52
53
 
@@ -54,27 +55,27 @@ const asNumbers = (path) => path.map((part) => (/^(0|[1-9][0-9]*)$/.test(String(
54
55
  const toDocumentPath = (slideIndex, relative) => ["slides", String(slideIndex), ...(relative ?? []).map(String)].join(".");
55
56
 
56
57
  /** Replace one value (a slide, a block, a list) as `test` + `replace`, validated like every other edit. */
57
- function replacePatches(document, parts, next) {
58
+ function replacePatches(presentation, parts, next) {
58
59
  const pointer = opfPathToJsonPointer(parts);
59
60
  return [
60
- { op: "test", path: pointer, value: structuredClone(getValueAtPath(document, parts)) },
61
+ { op: "test", path: pointer, value: structuredClone(getValueAtPath(presentation, parts)) },
61
62
  { op: "replace", path: pointer, value: next },
62
63
  ];
63
64
  }
64
65
  // `validate: false` skips the whole-document check, for a dry run that only needs the loss report (core has already
65
66
  // validated the changed slide); applying always validates.
66
- function finish(document, patches, extra, validate = true) {
67
- if (!validate) return { document: applyJsonPatch(document, patches), patches, ...extra };
68
- const before = validateOpfDocument(document);
69
- return { document: checkedDocument(document, patches, before), patches, ...extra };
67
+ function finish(presentation, patches, extra, validate = true) {
68
+ if (!validate) return { presentation: applyJsonPatch(presentation, patches), patches, ...extra };
69
+ const before = checkFormat(presentation);
70
+ return { presentation: checkedDocument(presentation, patches, before), patches, ...extra };
70
71
  }
71
72
  function apply(editor, prepared, meta, source) {
72
- if (!prepared.changed) return { ...prepared, document: editor.document, patches: [], inversePatches: [], validation: editor.validation };
73
+ if (!prepared.changed) return { ...prepared, presentation: editor.presentation, patches: [], inversePatches: [], validation: editor.validation };
73
74
  const change = editor.applyPatch(prepared.patches, { ...meta, source: meta.source ?? source, path: prepared.path });
74
75
  const extra = Object.fromEntries(["levels", "range", "slideCount", "pages"].filter((key) => prepared[key] !== undefined).map((key) => [key, prepared[key]]));
75
76
  return { ...change, changed: true, lossless: prepared.lossless, loss: prepared.loss, path: prepared.path, ...extra };
76
77
  }
77
- const unchanged = (document, path, reason) => ({ document: structuredClone(document), patches: [], path, changed: false, lossless: true, loss: [], ...(reason ? { reason } : {}) });
78
+ const unchanged = (presentation, path, reason) => ({ presentation: structuredClone(presentation), patches: [], path, changed: false, lossless: true, loss: [], ...(reason ? { reason } : {}) });
78
79
 
79
80
  // --- list levels -------------------------------------------------------------------------------
80
81
 
@@ -91,75 +92,75 @@ export function listItemIndexForSelection(selectedPath, blockPath) {
91
92
  * item cannot be nested, and items under a moved item move with it (`options.withChildren`, default true).
92
93
  * `changed` is false, with a `reason`, when nothing can move.
93
94
  */
94
- export function prepareListShift(document, blockPath, indices, delta, options = {}) {
95
- const found = readBlockContent(document, blockPath);
95
+ export function prepareListShift(presentation, blockPath, indices, delta, options = {}) {
96
+ const found = readBlockContent(presentation, blockPath);
96
97
  if (!found || found.kind !== "list") throw refuse("Choose a list block.", { path: blockPath });
97
98
  const result = core(() => shiftListLevels(found.owner, indices, delta, options));
98
99
  const path = found.path.join(".");
99
- if (!result.changed) return unchanged(document, path, result.reason);
100
- return finish(document, replacePatches(document, found.path, result.payload), { path, changed: true, lossless: result.lossless, loss: result.loss, levels: result.levels }, options.validate !== false);
100
+ if (!result.changed) return unchanged(presentation, path, result.reason);
101
+ return finish(presentation, replacePatches(presentation, found.path, result.payload), { path, changed: true, lossless: result.lossless, loss: result.loss, levels: result.levels }, options.validate !== false);
101
102
  }
102
103
  /** Nest or un-nest list items as one undoable step. Returns the session change plus `changed`, `reason`, `levels`, `lossless` and `loss`. */
103
104
  export function shiftListItems(editor, blockPath, indices, delta, options = {}, meta = {}) {
104
105
  requireEditor(editor);
105
- return apply(editor, prepareListShift(editor.document, blockPath, indices, delta, options), meta, "list-level");
106
+ return apply(editor, prepareListShift(editor.presentation, blockPath, indices, delta, options), meta, "list-level");
106
107
  }
107
108
 
108
109
  // --- structure inside one slide ----------------------------------------------------------------
109
110
 
110
111
  /** One slide-level transform as a guarded replace of that slide. */
111
- function slideAction(document, slideIndex, transform, targetPath, validate = true) {
112
- const slide = document.slides[slideIndex];
112
+ function slideAction(presentation, slideIndex, transform, targetPath, validate = true) {
113
+ const slide = presentation.slides[slideIndex];
113
114
  const result = core(() => transform(slide));
114
115
  const path = result.path ? toDocumentPath(slideIndex, result.path) : (targetPath ?? `slides.${slideIndex}`);
115
- if (!result.changed) return unchanged(document, path);
116
- const patches = replacePatches(document, ["slides", String(slideIndex)], result.slide);
117
- return finish(document, patches, { path, changed: true, lossless: result.lossless, loss: result.loss }, validate);
116
+ if (!result.changed) return unchanged(presentation, path);
117
+ const patches = replacePatches(presentation, ["slides", String(slideIndex)], result.slide);
118
+ return finish(presentation, patches, { path, changed: true, lossless: result.lossless, loss: result.loss }, validate);
118
119
  }
119
120
 
120
121
  /** Wrap the blocks at `indices` of the container at `containerPath` (a slide `slides.0`, a group or a region group) in a new group. */
121
- export function prepareGroupBlocks(document, containerPath, indices, options = {}) {
122
- const { slideIndex, relative } = slideParts(document, containerPath, "a container");
123
- return slideAction(document, slideIndex, (slide) => wrapBlocks(slide, indices, { container: asNumbers(relative), ...(options.composition ? { composition: options.composition } : {}) }), undefined, options.validate !== false);
122
+ export function prepareGroupBlocks(presentation, containerPath, indices, options = {}) {
123
+ const { slideIndex, relative } = slideParts(presentation, containerPath, "a container");
124
+ return slideAction(presentation, slideIndex, (slide) => wrapBlocks(slide, indices, { container: asNumbers(relative), ...(options.composition ? { composition: options.composition } : {}) }), undefined, options.validate !== false);
124
125
  }
125
126
  /** Replace the group at `groupPath` by its blocks. What the group carried (composition, id) is reported in `loss`. */
126
- export function prepareUngroupBlock(document, groupPath, options = {}) {
127
- const { slideIndex, relative } = slideParts(document, groupPath, "a group");
128
- return slideAction(document, slideIndex, (slide) => unwrapGroup(slide, asNumbers(relative)), undefined, options.validate !== false);
127
+ export function prepareUngroupBlock(presentation, groupPath, options = {}) {
128
+ const { slideIndex, relative } = slideParts(presentation, groupPath, "a group");
129
+ return slideAction(presentation, slideIndex, (slide) => unwrapGroup(slide, asNumbers(relative)), undefined, options.validate !== false);
129
130
  }
130
131
  /** Place every block of a slide in its own named region: `regions[i]` is block i's region (`left`, `right`, `top:left`, ...). */
131
- export function prepareBlocksToRegions(document, slideIndex, regions, options = {}) {
132
- slideParts(document, `slides.${slideIndex}`, "a slide");
133
- return slideAction(document, slideIndex, (slide) => blocksToRegions(slide, regions), undefined, options.validate !== false);
132
+ export function prepareBlocksToRegions(presentation, slideIndex, regions, options = {}) {
133
+ slideParts(presentation, `slides.${slideIndex}`, "a slide");
134
+ return slideAction(presentation, slideIndex, (slide) => blocksToRegions(slide, regions), undefined, options.validate !== false);
134
135
  }
135
136
  /** Turn the named regions of a slide into ordinary blocks in reading order. The regions' placement is reported in `loss`. */
136
- export function prepareRegionsToBlocks(document, slideIndex, options = {}) {
137
- slideParts(document, `slides.${slideIndex}`, "a slide");
138
- return slideAction(document, slideIndex, (slide) => regionsToBlocks(slide), undefined, options.validate !== false);
137
+ export function prepareRegionsToBlocks(presentation, slideIndex, options = {}) {
138
+ slideParts(presentation, `slides.${slideIndex}`, "a slide");
139
+ return slideAction(presentation, slideIndex, (slide) => regionsToBlocks(slide), undefined, options.validate !== false);
139
140
  }
140
141
  /** Move the content of region `from` to region `to`, or swap them when `to` is taken and `options.swap` is true. */
141
- export function prepareMoveRegion(document, slideIndex, from, to, options = {}) {
142
- slideParts(document, `slides.${slideIndex}`, "a slide");
143
- return slideAction(document, slideIndex, (slide) => moveRegion(slide, from, to, options), undefined, options.validate !== false);
142
+ export function prepareMoveRegion(presentation, slideIndex, from, to, options = {}) {
143
+ slideParts(presentation, `slides.${slideIndex}`, "a slide");
144
+ return slideAction(presentation, slideIndex, (slide) => moveRegion(slide, from, to, options), undefined, options.validate !== false);
144
145
  }
145
- /** Move the image block at `blockPath` into the slide's design as `slideImage`, `background` or `watermark`. */
146
- export function prepareImageToDesign(document, blockPath, target, options = {}) {
147
- const { slideIndex, relative } = slideParts(document, blockPath, "an image block");
148
- return slideAction(document, slideIndex, (slide) => promoteImage(slide, asNumbers(relative), target, options), `slides.${slideIndex}`, options.validate !== false);
146
+ /** Move the image block at `blockPath` into the slide's design as its `background` or `watermark` (core's `promoteImage`). */
147
+ export function prepareImageToDesign(presentation, blockPath, target, options = {}) {
148
+ const { slideIndex, relative } = slideParts(presentation, blockPath, "an image block");
149
+ return slideAction(presentation, slideIndex, (slide) => promoteImage(slide, asNumbers(relative), target, options), `slides.${slideIndex}`, options.validate !== false);
149
150
  }
150
- /** Move a slide's own `slideImage`, `background` image or `watermark` back into its content as an image block. */
151
- export function prepareImageToContent(document, slideIndex, source, options = {}) {
152
- slideParts(document, `slides.${slideIndex}`, "a slide");
153
- return slideAction(document, slideIndex, (slide) => demoteImage(slide, source, options), undefined, options.validate !== false);
151
+ /** Move a slide's own `background` image or `watermark` back into its content as an image block (core's `demoteImage`). */
152
+ export function prepareImageToContent(presentation, slideIndex, source, options = {}) {
153
+ slideParts(presentation, `slides.${slideIndex}`, "a slide");
154
+ return slideAction(presentation, slideIndex, (slide) => demoteImage(slide, source, options), undefined, options.validate !== false);
154
155
  }
155
156
 
156
- export const groupBlocks = (editor, containerPath, indices, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareGroupBlocks(editor.document, containerPath, indices, options), meta, "group-blocks"));
157
- export const ungroupBlock = (editor, groupPath, meta = {}) => (requireEditor(editor), apply(editor, prepareUngroupBlock(editor.document, groupPath), meta, "ungroup-block"));
158
- export const placeBlocksInRegions = (editor, slideIndex, regions, meta = {}) => (requireEditor(editor), apply(editor, prepareBlocksToRegions(editor.document, slideIndex, regions), meta, "blocks-to-regions"));
159
- export const regionsAsBlocks = (editor, slideIndex, meta = {}) => (requireEditor(editor), apply(editor, prepareRegionsToBlocks(editor.document, slideIndex), meta, "regions-to-blocks"));
160
- export const moveSlideRegion = (editor, slideIndex, from, to, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareMoveRegion(editor.document, slideIndex, from, to, options), meta, "move-region"));
161
- export const moveImageToDesign = (editor, blockPath, target, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToDesign(editor.document, blockPath, target, options), meta, "image-to-design"));
162
- export const moveImageToContent = (editor, slideIndex, source, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToContent(editor.document, slideIndex, source, options), meta, "image-to-content"));
157
+ export const groupBlocks = (editor, containerPath, indices, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareGroupBlocks(editor.presentation, containerPath, indices, options), meta, "group-blocks"));
158
+ export const ungroupBlock = (editor, groupPath, meta = {}) => (requireEditor(editor), apply(editor, prepareUngroupBlock(editor.presentation, groupPath), meta, "ungroup-block"));
159
+ export const placeBlocksInRegions = (editor, slideIndex, regions, meta = {}) => (requireEditor(editor), apply(editor, prepareBlocksToRegions(editor.presentation, slideIndex, regions), meta, "blocks-to-regions"));
160
+ export const regionsAsBlocks = (editor, slideIndex, meta = {}) => (requireEditor(editor), apply(editor, prepareRegionsToBlocks(editor.presentation, slideIndex), meta, "regions-to-blocks"));
161
+ export const moveSlideRegion = (editor, slideIndex, from, to, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareMoveRegion(editor.presentation, slideIndex, from, to, options), meta, "move-region"));
162
+ export const moveImageToDesign = (editor, blockPath, target, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToDesign(editor.presentation, blockPath, target, options), meta, "image-to-design"));
163
+ export const moveImageToContent = (editor, slideIndex, source, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareImageToContent(editor.presentation, slideIndex, source, options), meta, "image-to-content"));
163
164
 
164
165
  // --- slides ------------------------------------------------------------------------------------
165
166
 
@@ -168,8 +169,8 @@ export const moveImageToContent = (editor, slideIndex, source, options = {}, met
168
169
  * replaced slide, a `replace` for each one that changed, `remove` for the surplus old slides and `add` for the
169
170
  * surplus new ones, so a split or merge stays one guarded, undoable transaction without rewriting the other slides.
170
171
  */
171
- function slidePatches(document, range, slides) {
172
- const old = document.slides.slice(range.start, range.start + range.deleteCount);
172
+ function slidePatches(presentation, range, slides) {
173
+ const old = presentation.slides.slice(range.start, range.start + range.deleteCount);
173
174
  const patches = old.map((slide, offset) => ({ op: "test", path: `/slides/${range.start + offset}`, value: structuredClone(slide) }));
174
175
  const common = Math.min(old.length, slides.length);
175
176
  for (let offset = 0; offset < common; offset++) if (JSON.stringify(old[offset]) !== JSON.stringify(slides[offset])) patches.push({ op: "replace", path: `/slides/${range.start + offset}`, value: structuredClone(slides[offset]) });
@@ -177,31 +178,31 @@ function slidePatches(document, range, slides) {
177
178
  for (let offset = common; offset < slides.length; offset++) patches.push({ op: "add", path: `/slides/${range.start + offset}`, value: structuredClone(slides[offset]) });
178
179
  return patches;
179
180
  }
180
- function slidesAction(document, result, extra = {}) {
181
+ function slidesAction(presentation, result, extra = {}) {
181
182
  const path = `slides.${result.range.start}`;
182
- if (!result.changed) return unchanged(document, path, "Nothing to change.");
183
- return finish(document, slidePatches(document, result.range, result.slides), { path, changed: true, lossless: result.lossless, loss: result.loss, range: result.range, slideCount: result.slides.length, ...extra });
183
+ if (!result.changed) return unchanged(presentation, path, "Nothing to change.");
184
+ return finish(presentation, slidePatches(presentation, result.range, result.slides), { path, changed: true, lossless: result.lossless, loss: result.loss, range: result.range, slideCount: result.slides.length, ...extra });
184
185
  }
185
186
 
186
187
  /** Split a slide by its blocks (`options.at`: block indexes where a new slide starts, or `options.each`). See core's `splitSlide`. */
187
- export function prepareSplitSlide(document, slideIndex, options = {}) {
188
- return slidesAction(document, core(() => splitSlide(document, slideIndex, options)));
188
+ export function prepareSplitSlide(presentation, slideIndex, options = {}) {
189
+ return slidesAction(presentation, core(() => splitSlide(presentation, slideIndex, options)));
189
190
  }
190
- /** Split a slide that overflows into pages with the existing pagination (`options`: text measurement, fonts, as for `paginatePresentation`). `pages` maps the new slides back for `prepareUnpaginate`. */
191
- export function prepareSplitSlideOnOverflow(document, slideIndex, options = {}) {
192
- const result = core(() => splitSlideOnOverflowCore(document, slideIndex, options));
193
- return slidesAction(document, result, { pages: result.pages });
191
+ /** Split a slide that overflows into pages with the existing pagination (`options`: text measurement, fonts, as for core `paginate`). `pages` maps the new slides back for `prepareUnpaginate`. */
192
+ export function prepareSplitSlideOnOverflow(presentation, slideIndex, options = {}) {
193
+ const result = core(() => splitSlideOnOverflowCore(presentation, slideIndex, options));
194
+ return slidesAction(presentation, result, { pages: result.pages });
194
195
  }
195
196
  /** Merge `count` consecutive slides from `start`. The first slide's id, headings and design win; what is dropped is reported in `loss`. */
196
- export function prepareMergeSlides(document, start, count = 2) {
197
- return slidesAction(document, core(() => mergeSlidesCore(document, start, count)));
197
+ export function prepareMergeSlides(presentation, start, count = 2) {
198
+ return slidesAction(presentation, core(() => mergeSlidesCore(presentation, start, count)));
198
199
  }
199
- /** Put the continuation slides of a pagination back into one slide each. `pages` is the mapping `splitSlideOnOverflow` or `paginatePresentation` returned. */
200
- export function prepareUnpaginate(document, pages, options = {}) {
201
- return slidesAction(document, core(() => unpaginate(document, pages, options)));
200
+ /** Put the continuation slides of a pagination back into one slide each. `pages` is the mapping `splitSlideOnOverflow` or core `paginate` returned. */
201
+ export function prepareUnpaginate(presentation, pages, options = {}) {
202
+ return slidesAction(presentation, core(() => unpaginate(presentation, pages, options)));
202
203
  }
203
204
 
204
- export const splitSlideByBlocks = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlide(editor.document, slideIndex, options), meta, "split-slide"));
205
- export const splitSlideOnOverflow = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlideOnOverflow(editor.document, slideIndex, options), meta, "split-slide-overflow"));
206
- export const mergeSlides = (editor, start, count = 2, meta = {}) => (requireEditor(editor), apply(editor, prepareMergeSlides(editor.document, start, count), meta, "merge-slides"));
207
- export const unpaginateSlides = (editor, pages, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareUnpaginate(editor.document, pages, options), meta, "unpaginate-slides"));
205
+ export const splitSlideByBlocks = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlide(editor.presentation, slideIndex, options), meta, "split-slide"));
206
+ export const splitSlideOnOverflow = (editor, slideIndex, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareSplitSlideOnOverflow(editor.presentation, slideIndex, options), meta, "split-slide-overflow"));
207
+ export const mergeSlides = (editor, start, count = 2, meta = {}) => (requireEditor(editor), apply(editor, prepareMergeSlides(editor.presentation, start, count), meta, "merge-slides"));
208
+ export const unpaginateSlides = (editor, pages, options = {}, meta = {}) => (requireEditor(editor), apply(editor, prepareUnpaginate(editor.presentation, pages, options), meta, "unpaginate-slides"));
@@ -36,20 +36,20 @@ export const REGION_LAYOUTS = Object.freeze({
36
36
  4: [{ id: "grid", label: "Two by two", regions: ["top:left", "top:right", "bottom:left", "bottom:right"] }],
37
37
  });
38
38
 
39
- /** Where an image block can go in the slide's design. The value is `<target>` or `<target>:<position>`. */
39
+ /**
40
+ * Where an image block can go in the slide's design (OPF 0.15): its background (a full-bleed photo behind the content) or its
41
+ * watermark. Bleeding an image to one edge beside the content is not a move: it is the block's `placement` (image-options.js).
42
+ */
40
43
  export const IMAGE_DESTINATIONS = Object.freeze([
41
- { value: "slideImage:right", label: "Slide image, on the right", target: "slideImage", options: { position: "right" } },
42
- { value: "slideImage:left", label: "Slide image, on the left", target: "slideImage", options: { position: "left" } },
43
- { value: "slideImage:top", label: "Slide image, along the top", target: "slideImage", options: { position: "top" } },
44
- { value: "slideImage:bottom", label: "Slide image, along the bottom", target: "slideImage", options: { position: "bottom" } },
45
- { value: "slideImage:background", label: "Slide image, full slide behind the content", target: "slideImage", options: { position: "background" } },
46
- { value: "background", label: "Slide background", target: "background", options: {} },
44
+ { value: "background", label: "Slide background, full slide behind the content", target: "background", options: {} },
47
45
  { value: "watermark", label: "Watermark", target: "watermark", options: {} },
48
46
  ]);
49
47
 
50
48
  const REGION_KEY = /^(?:(?:top|middle|bottom)(?:\+(?:top|middle|bottom))*(?::(?:left|center|right)(?:\+(?:left|center|right))*)?|(?:left|center|right)(?:\+(?:left|center|right))*)$/;
51
49
  const regionKeysOfSlide = (slide) => Object.keys(slide).filter((key) => REGION_KEY.test(key));
52
- const DESIGN_IMAGE_LABELS = { slideImage: "slide image", background: "background image", watermark: "watermark" };
50
+ const DESIGN_IMAGE_LABELS = { background: "background image", watermark: "watermark" };
51
+ // An image block's own framing fields (FA-22) do not stop it being "a block that holds only an image".
52
+ const IMAGE_BLOCK_FIELDS = new Set(["id", "extensions", "type", "fit", "focus", "aspectRatio", "shape", "cornerRadius", "border", "opacity", "recolor", "overlay", "placement"]);
53
53
  const messageOf = (error) => error?.issues?.[0]?.message ?? error?.message ?? String(error);
54
54
  const lossText = (prepared) => (prepared.loss.length ? `Not carried over: ${prepared.loss.join(", ")}.` : "Nothing is lost.");
55
55
 
@@ -104,7 +104,7 @@ export function createContentControls(ctx) {
104
104
  const prepare = (delta) => (state) => {
105
105
  const item = index(state);
106
106
  if (item === undefined) return { changed: false, loss: [], reason: "Select a list item to change its level." };
107
- return prepareListShift(editor.document, state.blockPath, [item], delta, { validate: false });
107
+ return prepareListShift(editor.presentation, state.blockPath, [item], delta, { validate: false });
108
108
  };
109
109
  const apply = (delta) => () => {
110
110
  const state = ctxState.current;
@@ -140,7 +140,7 @@ export function createContentControls(ctx) {
140
140
  updaters.push((state) => {
141
141
  metrics.wrap.hidden = !state.metricGroupPath;
142
142
  if (!state.metricGroupPath) return;
143
- const targets = blockConversionTargets(editor.document, state.metricGroupPath);
143
+ const targets = blockConversionTargets(editor.presentation, state.metricGroupPath);
144
144
  metrics.setOptions(
145
145
  targets.map((target) => ({
146
146
  value: target.kind,
@@ -172,7 +172,7 @@ export function createContentControls(ctx) {
172
172
  prepare: (state) => {
173
173
  const indices = next(state);
174
174
  if (!indices) return { changed: false, loss: [], reason: "There is no block after this one in its container." };
175
- return prepareGroupBlocks(editor.document, containerPath(state), indices, { validate: false });
175
+ return prepareGroupBlocks(editor.presentation, containerPath(state), indices, { validate: false });
176
176
  },
177
177
  apply: () => {
178
178
  const state = ctxState.current;
@@ -190,7 +190,7 @@ export function createContentControls(ctx) {
190
190
  prepare: (state) => {
191
191
  const path = parentGroup(state);
192
192
  if (!path) return { changed: false, loss: [], reason: "This block is not inside a group." };
193
- return prepareUngroupBlock(editor.document, path, { validate: false });
193
+ return prepareUngroupBlock(editor.presentation, path, { validate: false });
194
194
  },
195
195
  apply: () => ungroupBlock(editor, parentGroup(ctxState.current)),
196
196
  selectAfter: (change) => change.path,
@@ -222,7 +222,7 @@ export function createContentControls(ctx) {
222
222
  image.setOptions(
223
223
  IMAGE_DESTINATIONS.map((entry) => {
224
224
  try {
225
- const prepared = prepareImageToDesign(editor.document, state.blockPath, entry.target, { ...entry.options, validate: false });
225
+ const prepared = prepareImageToDesign(editor.presentation, state.blockPath, entry.target, { ...entry.options, validate: false });
226
226
  return { value: entry.value, label: `${entry.label}${prepared.loss.length ? ` (loses ${prepared.loss.join(", ")})` : ""}` };
227
227
  } catch (error) {
228
228
  return { value: entry.value, label: `${entry.label} (unavailable)`, disabled: true, title: messageOf(error) };
@@ -251,21 +251,21 @@ export function createContentControls(ctx) {
251
251
  });
252
252
  const toBlocks = action("Turn regions into blocks", {
253
253
  role: "regions-to-blocks",
254
- prepare: () => prepareRegionsToBlocks(editor.document, getSlide(), { validate: false }),
254
+ prepare: () => prepareRegionsToBlocks(editor.presentation, getSlide(), { validate: false }),
255
255
  apply: () => regionsAsBlocks(editor, getSlide()),
256
256
  done: "Regions are now blocks, in reading order.",
257
257
  });
258
258
  const designImages = Object.keys(DESIGN_IMAGE_LABELS).map((source) =>
259
259
  action(`Move the ${DESIGN_IMAGE_LABELS[source]} into the content`, {
260
260
  role: `back-${source}`,
261
- prepare: () => prepareImageToContent(editor.document, getSlide(), source, { validate: false }),
261
+ prepare: () => prepareImageToContent(editor.presentation, getSlide(), source, { validate: false }),
262
262
  apply: () => moveImageToContent(editor, getSlide(), source),
263
263
  done: `The ${DESIGN_IMAGE_LABELS[source]} is now an image block.`,
264
264
  }),
265
265
  );
266
266
  slideBox.append(layouts.wrap, toBlocks.wrap, ...designImages.map((entry) => entry.wrap));
267
267
  updaters.push((state) => {
268
- const slide = editor.document.slides?.[state.slideIndex];
268
+ const slide = editor.presentation.slides?.[state.slideIndex];
269
269
  if (!slide) return;
270
270
  const regions = regionKeysOfSlide(slide);
271
271
  const count = blockCount(state.slideIndex);
@@ -275,7 +275,7 @@ export function createContentControls(ctx) {
275
275
  layouts.setOptions(
276
276
  choices.map((choice) => {
277
277
  try {
278
- const prepared = prepareBlocksToRegions(editor.document, state.slideIndex, choice.regions, { validate: false });
278
+ const prepared = prepareBlocksToRegions(editor.presentation, state.slideIndex, choice.regions, { validate: false });
279
279
  return { value: choice.id, label: `${choice.label}${prepared.loss.length ? ` (loses ${prepared.loss.join(", ")})` : ""}` };
280
280
  } catch (error) {
281
281
  return { value: choice.id, label: `${choice.label} (unavailable)`, disabled: true, title: messageOf(error) };
@@ -296,7 +296,7 @@ export function createContentControls(ctx) {
296
296
 
297
297
  /** The number of blocks a slide can place in regions: its `blocks`, or the content fields it holds inline. */
298
298
  function blockCount(slideIndex) {
299
- const slide = editor.document.slides?.[slideIndex];
299
+ const slide = editor.presentation.slides?.[slideIndex];
300
300
  if (!slide) return 0;
301
301
  if (Array.isArray(slide.blocks)) return slide.blocks.length;
302
302
  return ["text", "items", "bullets", "image", "video", "chart", "table", "code", "metric", "quote", "timeline"].filter((key) => slide[key] !== undefined).length;
@@ -309,9 +309,9 @@ export function createContentControls(ctx) {
309
309
  slideNode: slideBox,
310
310
  /** Update every control from the session and the current selection. */
311
311
  sync(state) {
312
- const slide = editor.document.slides?.[state.slideIndex];
312
+ const slide = editor.presentation.slides?.[state.slideIndex];
313
313
  const imageBlock = state.blockPath ? isImageBlock(editor, state.blockPath) : false;
314
- const full = { ...state, imageBlock, metricGroupPath: state.selectedPath ? metricGroupForSelection(editor.document, state.selectedPath) : undefined, slide };
314
+ const full = { ...state, imageBlock, metricGroupPath: state.selectedPath ? metricGroupForSelection(editor.presentation, state.selectedPath) : undefined, slide };
315
315
  ctxState.current = full;
316
316
  for (const update of updaters) update(full);
317
317
  },
@@ -321,6 +321,6 @@ export function createContentControls(ctx) {
321
321
  function isImageBlock(editor, blockPath) {
322
322
  const block = editor.get(blockPath);
323
323
  if (!block || typeof block !== "object" || Array.isArray(block)) return false;
324
- const keys = Object.keys(block).filter((key) => key !== "id" && key !== "extensions" && key !== "type");
324
+ const keys = Object.keys(block).filter((key) => !IMAGE_BLOCK_FIELDS.has(key));
325
325
  return keys.length === 1 && keys[0] === "image";
326
326
  }
package/dist/data-grid.js CHANGED
@@ -33,7 +33,6 @@ import {
33
33
  setGridColumnFormat,
34
34
  setGridHeader,
35
35
  sortGridRows,
36
- supportsChartTableData,
37
36
  transposeGridData,
38
37
  } from "./grid-model.js";
39
38
  import { NUMBER_FORMATS, parseDelimited, resolveNumberFormat, toDelimited } from "./grid-text.js";
@@ -456,7 +455,7 @@ export function createDataGrid(container, options = {}) {
456
455
  function resolveTarget() {
457
456
  const path = getPath();
458
457
  if (!path) return undefined;
459
- return resolveDataGridTarget(editor.document, path);
458
+ return resolveDataGridTarget(editor.presentation, path);
460
459
  }
461
460
 
462
461
  function rebuild() {
@@ -486,7 +485,7 @@ export function createDataGrid(container, options = {}) {
486
485
  grid = undefined;
487
486
  return;
488
487
  }
489
- grid = describeDataGrid(editor.document, next.path, opts());
488
+ grid = describeDataGrid(editor.presentation, next.path, opts());
490
489
  buildTable();
491
490
  buildDatasetRow();
492
491
  buildMapping();
@@ -505,7 +504,7 @@ export function createDataGrid(container, options = {}) {
505
504
  function dataSignature() {
506
505
  if (!target || target.editable === false) return "";
507
506
  try {
508
- return JSON.stringify([target.path, editor.get(target.path), target.dataset ? editor.document.datasets?.[target.dataset.id] : undefined, numberFormat]);
507
+ return JSON.stringify([target.path, editor.get(target.path), target.dataset ? editor.presentation.datasets?.[target.dataset.id] : undefined, numberFormat]);
509
508
  } catch {
510
509
  return "";
511
510
  }
@@ -551,7 +550,7 @@ export function createDataGrid(container, options = {}) {
551
550
  // moves the selection before the field's change event fires, and the format typed for one column must not land on the next.
552
551
  let formatBound = 0;
553
552
  function updateFormatRow() {
554
- const visible = Boolean(grid?.hasHeader) && target?.editable !== false && supportsChartTableData;
553
+ const visible = Boolean(grid?.hasHeader) && target?.editable !== false;
555
554
  formatRow.hidden = !visible;
556
555
  if (!visible) return;
557
556
  if (doc.activeElement !== formatInput) {
@@ -613,11 +612,11 @@ export function createDataGrid(container, options = {}) {
613
612
 
614
613
  function buildMapping() {
615
614
  mappingBody.replaceChildren();
616
- mappingBox.hidden = !grid || target?.kind !== "chart" || !supportsChartTableData;
615
+ mappingBox.hidden = !grid || target?.kind !== "chart";
617
616
  if (mappingBox.hidden) return;
618
617
  let view;
619
618
  try {
620
- view = describeChartMapping(editor.document, target.path);
619
+ view = describeChartMapping(editor.presentation, target.path);
621
620
  } catch {
622
621
  mappingBox.hidden = true;
623
622
  return;
@@ -694,7 +693,7 @@ export function createDataGrid(container, options = {}) {
694
693
  }
695
694
 
696
695
  function checkLive(area, u, c) {
697
- const issues = gridCellIssues(editor.document, target.path, [{ ...addr(u, c), text: area.value }], opts());
696
+ const issues = gridCellIssues(editor.presentation, target.path, [{ ...addr(u, c), text: area.value }], opts());
698
697
  if (issues.length) {
699
698
  area.setAttribute("aria-invalid", "true");
700
699
  complain(issues[0].message);
package/dist/data.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export {parseTabularData, createDataContent, OPFDataImportError} from "@openpresentation/opf/data";
2
- export type {TabularData, DataImportOptions, DataContentOptions} from "@openpresentation/opf/data";
1
+ export {parseTabularData, importData, OPFDataImportError} from "@openpresentation/opf/data";
2
+ export type {TabularData, DataImportOptions, ImportDataOptions} from "@openpresentation/opf/data";
3
3
  import type { JsonPatchOperation } from "./index.js";
4
4
 
5
5
  /** The id pattern of a top-level dataset (the assets id pattern). */
@@ -19,5 +19,5 @@ export interface DatasetImport {
19
19
  /** True when a dataset of that id existed and its columns and rows were replaced. */
20
20
  replaced: boolean;
21
21
  }
22
- /** Store imported data (the result of `createDataContent`) as a shared dataset, as `opf import-data --dataset <id>` does. Throws `dataset-id-invalid`. */
23
- export declare function prepareDatasetImport(document: unknown, content: { table: unknown } | { chart: { type: string; data: unknown } }, options: DatasetImportOptions): DatasetImport;
22
+ /** Store imported data (the result of `importData`) as a shared dataset, as `opf import-data --dataset <id>` does. Throws `dataset-id-invalid`. */
23
+ export declare function prepareDatasetImport(presentation: unknown, content: { table: unknown } | { chart: { type: string; data: unknown } }, options: DatasetImportOptions): DatasetImport;
package/dist/data.js CHANGED
@@ -1,4 +1,4 @@
1
- export {parseTabularData, createDataContent, OPFDataImportError} from "@openpresentation/opf/data";
1
+ export {parseTabularData, importData, OPFDataImportError} from "@openpresentation/opf/data";
2
2
  import { opfPathToJsonPointer } from "./index.js";
3
3
  import { fail } from "./edit-helpers.js";
4
4
 
@@ -8,19 +8,19 @@ export const DATASET_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
8
8
  const isObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
9
9
 
10
10
  /**
11
- * Store imported data as a shared dataset (RR-54), as `opf import-data --dataset <id>` does. `content` is what `createDataContent` returns
11
+ * Store imported data as a shared dataset (RR-54), as `opf import-data --dataset <id>` does. `content` is what `importData` returns
12
12
  * (`{ table }` or `{ chart: { type, data } }`). Returns `{ id, content, patches, replaced }`: `content` is the same table or chart holding
13
13
  * `{ dataset: id }` instead of the rows, and `patches` write `datasets.<id>` (the imported columns and rows replace those of an existing
14
14
  * dataset of that id, which keeps its title, description and source; `source` is merged into its `source`). Apply `patches` and the patch that
15
15
  * places `content` in one `applyPatch` call so the import is one undoable edit. Throws `dataset-id-invalid`.
16
16
  */
17
- export function prepareDatasetImport(document, content, options = {}) {
17
+ export function prepareDatasetImport(presentation, content, options = {}) {
18
18
  const id = options.id;
19
19
  if (typeof id !== "string" || !DATASET_ID_PATTERN.test(id)) throw fail("dataset-id-invalid", 'A dataset id starts with a letter or digit and holds letters, digits, ".", "_" or "-".', { id });
20
20
  const isTable = isObject(content) && isObject(content.table);
21
- if (!isTable && !(isObject(content) && isObject(content.chart) && isObject(content.chart.data))) throw fail("dataset-content-invalid", "Give the table or chart that createDataContent returned.", {});
21
+ if (!isTable && !(isObject(content) && isObject(content.chart) && isObject(content.chart.data))) throw fail("dataset-content-invalid", "Give the table or chart that importData returned.", {});
22
22
  const data = isTable ? content.table : content.chart.data;
23
- const datasets = isObject(document) && isObject(document.datasets) ? document.datasets : undefined;
23
+ const datasets = isObject(presentation) && isObject(presentation.datasets) ? presentation.datasets : undefined;
24
24
  const previous = datasets && Object.hasOwn(datasets, id) && isObject(datasets[id]) ? datasets[id] : undefined;
25
25
  const origin = isObject(options.source) && typeof options.source.src === "string" ? options.source : undefined;
26
26
  const entry = {
@@ -12,6 +12,6 @@ export interface DatasetItem {
12
12
  /** The dataset reference test (core's `isDatasetRef`). */
13
13
  export declare function isDatasetRef(value: unknown): boolean;
14
14
  /** Visit every chart and table that references a dataset (slides, nested blocks and promoted regions). */
15
- export declare function walkDatasetItems(document: unknown, visit: (entry: DatasetItem) => void): void;
15
+ export declare function walkDatasetItems(presentation: unknown, visit: (entry: DatasetItem) => void): void;
16
16
  /** The charts and tables that use dataset `id`, in document order. */
17
- export declare function datasetUsage(document: unknown, id: string): Array<{ kind: "chart" | "table"; path: string; parts: string[] }>;
17
+ export declare function datasetUsage(presentation: unknown, id: string): Array<{ kind: "chart" | "table"; path: string; parts: string[] }>;
@@ -14,8 +14,8 @@ export const isDatasetRef = (value) => isObject(value) && typeof value.dataset =
14
14
  * `item` is the chart or table object, `parts` its path, and `ref` the object that holds `dataset` and `fields`
15
15
  * (`chart.data` for a chart, the table itself for a table) at `refParts`.
16
16
  */
17
- export function walkDatasetItems(document, visit) {
18
- if (!isObject(document) || !Array.isArray(document.slides)) return;
17
+ export function walkDatasetItems(presentation, visit) {
18
+ if (!isObject(presentation) || !Array.isArray(presentation.slides)) return;
19
19
  const walk = (payload, base, depth, ancestors) => {
20
20
  if (isObject(payload.chart) && isDatasetRef(payload.chart.data))
21
21
  visit({ kind: "chart", id: payload.chart.data.dataset, parts: [...base, "chart"], item: payload.chart, refParts: [...base, "chart", "data"], ref: payload.chart.data });
@@ -27,7 +27,7 @@ export function walkDatasetItems(document, visit) {
27
27
  if (isObject(block)) walk(block, [...base, "blocks", String(index)], depth + 1, nested);
28
28
  });
29
29
  };
30
- document.slides.forEach((slide, index) => {
30
+ presentation.slides.forEach((slide, index) => {
31
31
  if (!isObject(slide)) return;
32
32
  const base = ["slides", String(index)];
33
33
  walk(slide, base, 0, []);
@@ -36,9 +36,9 @@ export function walkDatasetItems(document, visit) {
36
36
  }
37
37
 
38
38
  /** The charts and tables that use dataset `id`: `[{ kind, path, parts }]` in document order. */
39
- export function datasetUsage(document, id) {
39
+ export function datasetUsage(presentation, id) {
40
40
  const items = [];
41
- walkDatasetItems(document, (entry) => {
41
+ walkDatasetItems(presentation, (entry) => {
42
42
  if (entry.id === id) items.push({ kind: entry.kind, path: entry.parts.join("."), parts: entry.parts });
43
43
  });
44
44
  return items;
@@ -1,7 +1,13 @@
1
+ import type { Catalog } from "@openpresentation/opf";
1
2
  import type { EditorSession } from "./index.js";
3
+ import type { SwitchVocabularies } from "./switches.js";
2
4
 
3
- export type DesignControlSection = "look" | "background" | "slide-image" | "header-footer" | "brand" | "layout-options" | "info" | "selection" | "table" | "slide-content";
4
- /** Every section, in panel order. `selection` and `table` follow the host's current selection; `slide-content` (RR-26: blocks and regions, images between content and design) follows the current slide. */
5
+ export type DesignControlSection = "look" | "background" | "header-footer" | "brand" | "layout-options" | "info" | "selection" | "image" | "table" | "slide-content" | "catalog";
6
+ /**
7
+ * Every section, in panel order. `selection`, `image` (the selected image block's fit, focus, treatments and placement) and `table`
8
+ * follow the host's current selection; `slide-content` (RR-26: blocks and regions, images between content and design) follows the
9
+ * current slide; `catalog` lists the catalog reference findings and runs the explicit "Update from catalog".
10
+ */
5
11
  export declare const DESIGN_CONTROL_SECTIONS: readonly DesignControlSection[];
6
12
 
7
13
  export interface DesignControlsOptions {
@@ -14,9 +20,10 @@ export interface DesignControlsOptions {
14
20
  sections?: readonly DesignControlSection[];
15
21
  /** Initial "Applies to" choice. */
16
22
  scope?: "deck" | "slide";
17
- /** Caller-loaded catalog records by kind (layouts, font schemes, themes, ...) for options the bundled catalogs do not have. */
18
- catalogs?: Record<string, unknown>;
19
- catalogSources?: Record<string, unknown>;
23
+ /** Host catalogs for the pickers, merged after the session's registered ones. */
24
+ catalogs?: readonly Catalog[];
25
+ /** Display metadata for the engine vocabularies (languages, chart types, social platforms): `catalogDisplay` from `@openpresentation/opf/catalog`. */
26
+ vocabularies?: SwitchVocabularies;
20
27
  /** Called after every committed change (never for a refused one). */
21
28
  onChange?: (change: unknown) => void;
22
29
  /** Called after a content conversion or replacement with the path to keep selected (the block, or the inline payload field), since the old selection path may no longer exist. */