@openpresentation/opf-editor 0.11.3 → 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.
Files changed (95) hide show
  1. package/README.md +82 -58
  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 +15 -15
  6. package/dist/background-options.d.ts +2 -2
  7. package/dist/background-options.js +6 -6
  8. package/dist/block-controls.js +7 -7
  9. package/dist/block-convert.d.ts +7 -7
  10. package/dist/block-convert.js +23 -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 +22 -30
  15. package/dist/canvas.js +57 -64
  16. package/dist/chart-data.d.ts +12 -10
  17. package/dist/chart-data.js +22 -21
  18. package/dist/chart-options-panel.d.ts +2 -2
  19. package/dist/chart-options-panel.js +82 -8
  20. package/dist/chart-options.d.ts +35 -9
  21. package/dist/chart-options.js +133 -40
  22. package/dist/checks.js +21 -0
  23. package/dist/color-authoring.js +1 -1
  24. package/dist/content-actions.d.ts +15 -15
  25. package/dist/content-actions.js +71 -70
  26. package/dist/content-controls.js +12 -12
  27. package/dist/data-grid.js +179 -10
  28. package/dist/data.d.ts +23 -2
  29. package/dist/data.js +40 -1
  30. package/dist/dataset-refs.d.ts +17 -0
  31. package/dist/dataset-refs.js +45 -0
  32. package/dist/design-controls.js +66 -47
  33. package/dist/design-options.d.ts +16 -14
  34. package/dist/design-options.js +86 -74
  35. package/dist/edit-helpers.js +8 -7
  36. package/dist/exact-source.js +5 -5
  37. package/dist/export.d.ts +5 -7
  38. package/dist/export.js +40 -14
  39. package/dist/find-panel.js +5 -5
  40. package/dist/find-replace.d.ts +3 -3
  41. package/dist/find-replace.js +35 -20
  42. package/dist/font-gate.js +49 -58
  43. package/dist/galleries.js +36 -52
  44. package/dist/grid-model.d.ts +99 -24
  45. package/dist/grid-model.js +577 -101
  46. package/dist/image-crop.d.ts +4 -4
  47. package/dist/image-crop.js +20 -20
  48. package/dist/image-cropper.js +3 -3
  49. package/dist/index.d.ts +28 -28
  50. package/dist/index.js +74 -90
  51. package/dist/json-options.js +17 -16
  52. package/dist/layout-handles.js +5 -5
  53. package/dist/layout-placeholders.js +4 -4
  54. package/dist/layout.d.ts +2 -2
  55. package/dist/layout.js +8 -7
  56. package/dist/numbering-panel.js +3 -5
  57. package/dist/numbering.d.ts +3 -5
  58. package/dist/numbering.js +14 -26
  59. package/dist/outline-view.js +24 -24
  60. package/dist/outline.d.ts +12 -12
  61. package/dist/outline.js +52 -50
  62. package/dist/persistence.d.ts +1 -1
  63. package/dist/persistence.js +19 -18
  64. package/dist/presentation-ids.js +2 -2
  65. package/dist/react.js +3 -3
  66. package/dist/review-panel.d.ts +30 -13
  67. package/dist/review-panel.js +154 -53
  68. package/dist/review.d.ts +36 -43
  69. package/dist/review.js +92 -53
  70. package/dist/rich-text-input.js +4 -3
  71. package/dist/rich-text-toolbar.js +5 -3
  72. package/dist/rich-text.js +5 -2
  73. package/dist/schema-inspector.d.ts +2 -2
  74. package/dist/schema-inspector.js +16 -11
  75. package/dist/schema.d.ts +1 -1
  76. package/dist/schema.js +25 -5
  77. package/dist/slide-manager.d.ts +2 -2
  78. package/dist/slide-manager.js +18 -18
  79. package/dist/slides.d.ts +20 -20
  80. package/dist/slides.js +99 -97
  81. package/dist/svelte.js +1 -1
  82. package/dist/switches.d.ts +10 -10
  83. package/dist/switches.js +136 -109
  84. package/dist/table-options.d.ts +8 -8
  85. package/dist/table-options.js +41 -30
  86. package/dist/table-structure.d.ts +10 -10
  87. package/dist/table-structure.js +21 -21
  88. package/dist/template-panel.d.ts +3 -3
  89. package/dist/template-panel.js +37 -13
  90. package/dist/templates.d.ts +22 -11
  91. package/dist/templates.js +48 -55
  92. package/dist/transfer.d.ts +4 -4
  93. package/dist/transfer.js +74 -54
  94. package/package.json +10 -6
  95. package/dist/font-defaults.js +0 -25
package/dist/slides.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { EditorChange, EditorSession, JsonPatchOperation } from "./index.js
2
2
 
3
3
  /** One section of the deck: a maximal run of consecutive slides sharing a `section` label (or none). */
4
4
  export interface SlideSection {
5
- /** Position in `listSections(document)`, the id every section operation takes. */
5
+ /** Position in `listSections(presentation)`, the id every section operation takes. */
6
6
  index: number;
7
7
  /** The label; undefined for the unnamed run of slides with no label. */
8
8
  name: string | undefined;
@@ -15,7 +15,7 @@ export interface SlideSummary { index: number; id?: string; title: string; hidde
15
15
 
16
16
  /** What every `prepare*` function returns without touching a session. Empty `patches` and `changed: false` mean nothing to do. */
17
17
  export interface PreparedSlideChange {
18
- document: unknown;
18
+ presentation: unknown;
19
19
  patches: JsonPatchOperation[];
20
20
  changed: boolean;
21
21
  /** The slide indices to select after the change (the new, moved or remaining slides). */
@@ -38,8 +38,8 @@ export interface PreparedSlideChange {
38
38
  section?: string;
39
39
  }
40
40
  /** The session change of an applied operation, with the same report fields. One undo step; an unchanged operation commits nothing. */
41
- export interface SlideChange extends Omit<EditorChange, "document" | "patches">, Omit<PreparedSlideChange, "document" | "patches"> {
42
- document: unknown;
41
+ export interface SlideChange extends Omit<EditorChange, "presentation" | "patches">, Omit<PreparedSlideChange, "presentation" | "patches"> {
42
+ presentation: unknown;
43
43
  patches: JsonPatchOperation[];
44
44
  }
45
45
 
@@ -61,36 +61,36 @@ export interface AddSlideOptions {
61
61
  record?: Record<string, unknown>;
62
62
  }
63
63
 
64
- export declare function listSections(document: unknown): SlideSection[];
65
- export declare function hasSections(document: unknown): boolean;
66
- export declare function sectionForSlide(document: unknown, slideIndex: number): SlideSection | undefined;
64
+ export declare function listSections(presentation: unknown): SlideSection[];
65
+ export declare function hasSections(presentation: unknown): boolean;
66
+ export declare function sectionForSlide(presentation: unknown, slideIndex: number): SlideSection | undefined;
67
67
  export declare function slideTitle(slide: unknown): string;
68
- export declare function slideSummaries(document: unknown): SlideSummary[];
68
+ export declare function slideSummaries(presentation: unknown): SlideSummary[];
69
69
 
70
- export declare function prepareAddSlide(document: unknown, options?: AddSlideOptions): PreparedSlideChange;
70
+ export declare function prepareAddSlide(presentation: unknown, options?: AddSlideOptions): PreparedSlideChange;
71
71
  export declare function addSlide(editor: EditorSession, options?: AddSlideOptions, meta?: Record<string, unknown>): SlideChange;
72
- export declare function prepareDuplicateSlides(document: unknown, indices: number | number[], options?: { section?: "keep" }): PreparedSlideChange;
72
+ export declare function prepareDuplicateSlides(presentation: unknown, indices: number | number[], options?: { section?: "keep" }): PreparedSlideChange;
73
73
  export declare function duplicateSlides(editor: EditorSession, indices: number | number[], options?: { section?: "keep" }, meta?: Record<string, unknown>): SlideChange;
74
74
  /** Throws `cannot-remove-all-slides` when every slide is chosen. */
75
- export declare function prepareRemoveSlides(document: unknown, indices: number | number[]): PreparedSlideChange;
75
+ export declare function prepareRemoveSlides(presentation: unknown, indices: number | number[]): PreparedSlideChange;
76
76
  export declare function removeSlides(editor: EditorSession, indices: number | number[], meta?: Record<string, unknown>): SlideChange;
77
77
  /** `to` is the drop gap in the original order (0 to the slide count). */
78
- export declare function prepareMoveSlides(document: unknown, indices: number | number[], to: number, options?: { section?: MoveSectionMode }): PreparedSlideChange;
78
+ export declare function prepareMoveSlides(presentation: unknown, indices: number | number[], to: number, options?: { section?: MoveSectionMode }): PreparedSlideChange;
79
79
  export declare function moveSlides(editor: EditorSession, indices: number | number[], to: number, options?: { section?: MoveSectionMode }, meta?: Record<string, unknown>): SlideChange;
80
- export declare function prepareMoveSlidesBy(document: unknown, indices: number | number[], delta: number, options?: { section?: MoveSectionMode }): PreparedSlideChange;
80
+ export declare function prepareMoveSlidesBy(presentation: unknown, indices: number | number[], delta: number, options?: { section?: MoveSectionMode }): PreparedSlideChange;
81
81
  export declare function moveSlidesBy(editor: EditorSession, indices: number | number[], delta: number, options?: { section?: MoveSectionMode }, meta?: Record<string, unknown>): SlideChange;
82
82
  /** `hidden` omitted toggles: all hidden become shown, otherwise all become hidden. */
83
- export declare function prepareSetHidden(document: unknown, indices: number | number[], hidden?: boolean): PreparedSlideChange;
83
+ export declare function prepareSetHidden(presentation: unknown, indices: number | number[], hidden?: boolean): PreparedSlideChange;
84
84
  export declare function setHidden(editor: EditorSession, indices: number | number[], hidden?: boolean, meta?: Record<string, unknown>): SlideChange;
85
- export declare function prepareSetSection(document: unknown, indices: number | number[], name: string | null): PreparedSlideChange;
85
+ export declare function prepareSetSection(presentation: unknown, indices: number | number[], name: string | null): PreparedSlideChange;
86
86
  export declare function setSection(editor: EditorSession, indices: number | number[], name: string | null, meta?: Record<string, unknown>): SlideChange;
87
- export declare function prepareAddSection(document: unknown, slideIndex: number, name: string): PreparedSlideChange;
87
+ export declare function prepareAddSection(presentation: unknown, slideIndex: number, name: string): PreparedSlideChange;
88
88
  export declare function addSection(editor: EditorSession, slideIndex: number, name: string, meta?: Record<string, unknown>): SlideChange;
89
- export declare function prepareRenameSection(document: unknown, sectionIndex: number, name: string): PreparedSlideChange;
89
+ export declare function prepareRenameSection(presentation: unknown, sectionIndex: number, name: string): PreparedSlideChange;
90
90
  export declare function renameSection(editor: EditorSession, sectionIndex: number, name: string, meta?: Record<string, unknown>): SlideChange;
91
- export declare function prepareRemoveSection(document: unknown, sectionIndex: number, options?: { deleteSlides?: boolean }): PreparedSlideChange;
91
+ export declare function prepareRemoveSection(presentation: unknown, sectionIndex: number, options?: { deleteSlides?: boolean }): PreparedSlideChange;
92
92
  export declare function removeSection(editor: EditorSession, sectionIndex: number, options?: { deleteSlides?: boolean }, meta?: Record<string, unknown>): SlideChange;
93
93
  /** `to` is a gap among the sections (0 to the section count). */
94
- export declare function prepareMoveSection(document: unknown, sectionIndex: number, to: number): PreparedSlideChange;
94
+ export declare function prepareMoveSection(presentation: unknown, sectionIndex: number, to: number): PreparedSlideChange;
95
95
  export declare function moveSection(editor: EditorSession, sectionIndex: number, to: number, meta?: Record<string, unknown>): SlideChange;
96
- export declare const SLIDE_OPERATIONS: Readonly<Record<string, (document: unknown, ...args: any[]) => PreparedSlideChange>>;
96
+ export declare const SLIDE_OPERATIONS: Readonly<Record<string, (presentation: unknown, ...args: any[]) => PreparedSlideChange>>;
package/dist/slides.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Slide management (RR-21): add, duplicate, remove, move, hide and section slides as one validated, undoable
2
2
  // change each. Every operation has a `prepare*` form that computes the JSON Patch from a document without
3
- // touching a session (a dry run: `changed`, `patches` and the resulting `document`), and an apply form that
3
+ // touching a session (a dry run: `changed`, `patches` and the resulting `presentation`), and an apply form that
4
4
  // commits that patch through the session as ONE transaction, so a single Undo restores the deck exactly and
5
5
  // the preview, thumbnails and PPTX export follow from the document.
6
6
  //
@@ -8,10 +8,10 @@
8
8
  // the same label form a section (see `listSections`). Slides without a label form an unnamed run. `hidden`
9
9
  // is a boolean on the slide. Slide order is the `slides` array, so a move is a remove plus an add of the same
10
10
  // slide (removing and re-adding keeps every other slide's patch path stable).
11
- import { validateOpfDocument } from "./index.js";
12
11
  import { checkedDocument, fail, same } from "./edit-helpers.js";
13
12
  import { collectReservedPresentationIds, remapSlideTreeIds } from "./presentation-ids.js";
14
13
  import { prepareDimensionSwitch } from "./switches.js";
14
+ import { checkFormat } from "./checks.js";
15
15
 
16
16
  const isIndex = (value) => Number.isInteger(value) && value >= 0;
17
17
  const clone = (value) => structuredClone(value);
@@ -19,13 +19,13 @@ const clone = (value) => structuredClone(value);
19
19
  function requireEditor(editor) {
20
20
  if (!editor || typeof editor.applyPatch !== "function") throw fail("invalid-editor", "Expected an editor session created by createEditorSession.");
21
21
  }
22
- function requireSlides(document) {
23
- if (!document || typeof document !== "object" || !Array.isArray(document.slides)) throw fail("invalid-input", "Slide management needs an OPF document with a slides array.");
24
- return document.slides;
22
+ function requireSlides(presentation) {
23
+ if (!presentation || typeof presentation !== "object" || !Array.isArray(presentation.slides)) throw fail("invalid-input", "Slide management needs an OPF document with a slides array.");
24
+ return presentation.slides;
25
25
  }
26
26
  /** Validate and sort a list of slide indices; a single index is accepted too. */
27
- function selectionOf(document, indices, what = "Choose at least one slide.") {
28
- const slides = requireSlides(document);
27
+ function selectionOf(presentation, indices, what = "Choose at least one slide.") {
28
+ const slides = requireSlides(presentation);
29
29
  const list = [...new Set(Array.isArray(indices) ? indices : [indices])];
30
30
  if (!list.length) throw fail("no-slides-chosen", what);
31
31
  for (const index of list) if (!isIndex(index) || index >= slides.length) throw fail("slide-index-out-of-range", `Slide ${index} does not exist.`, { slideIndex: index });
@@ -33,21 +33,21 @@ function selectionOf(document, indices, what = "Choose at least one slide.") {
33
33
  }
34
34
  const sectionOf = (slide) => (typeof slide?.section === "string" && slide.section.trim() !== "" ? slide.section : undefined);
35
35
 
36
- function unchanged(document, extra = {}) {
37
- return { document: clone(document), patches: [], changed: false, selection: [], ...extra };
36
+ function unchanged(presentation, extra = {}) {
37
+ return { presentation: clone(presentation), patches: [], changed: false, selection: [], ...extra };
38
38
  }
39
39
  /** Validate the candidate the way every switch does (a valid deck must stay valid) and package the result. */
40
- function finish(document, patches, extra) {
41
- if (!patches.length) return unchanged(document, extra);
42
- const before = validateOpfDocument(document);
43
- const next = checkedDocument(document, patches, before);
44
- return { document: next, patches, changed: true, ...extra };
40
+ function finish(presentation, patches, extra) {
41
+ if (!patches.length) return unchanged(presentation, extra);
42
+ const before = checkFormat(presentation);
43
+ const next = checkedDocument(presentation, patches, before);
44
+ return { presentation: next, patches, changed: true, ...extra };
45
45
  }
46
46
  function apply(editor, prepared, meta, action) {
47
47
  requireEditor(editor);
48
- const { document, patches, ...summary } = prepared;
49
- void document;
50
- if (!prepared.changed) return { ...summary, document: editor.document, patches: [], inversePatches: [], validation: editor.validation };
48
+ const { presentation, patches, ...summary } = prepared;
49
+ void presentation;
50
+ if (!prepared.changed) return { ...summary, presentation: editor.presentation, patches: [], inversePatches: [], validation: editor.validation };
51
51
  const change = editor.applyPatch(patches, { ...meta, source: meta?.source ?? "slides", action });
52
52
  return { ...change, ...summary };
53
53
  }
@@ -59,8 +59,8 @@ function apply(editor, prepared, meta, action) {
59
59
  * of slides with no label is reported with `name: undefined` (`unnamed: true`). `index` is the position in
60
60
  * the returned list, the id every section operation takes.
61
61
  */
62
- export function listSections(document) {
63
- const slides = requireSlides(document);
62
+ export function listSections(presentation) {
63
+ const slides = requireSlides(presentation);
64
64
  const sections = [];
65
65
  slides.forEach((slide, slideIndex) => {
66
66
  const name = sectionOf(slide);
@@ -71,20 +71,22 @@ export function listSections(document) {
71
71
  return sections;
72
72
  }
73
73
  /** Whether any slide carries a section label (a deck with none shows no section headers). */
74
- export function hasSections(document) {
75
- return requireSlides(document).some((slide) => sectionOf(slide) !== undefined);
74
+ export function hasSections(presentation) {
75
+ return requireSlides(presentation).some((slide) => sectionOf(slide) !== undefined);
76
76
  }
77
77
  /** The section (from `listSections`) that holds a slide. */
78
- export function sectionForSlide(document, slideIndex) {
79
- return listSections(document).find((section) => slideIndex >= section.start && slideIndex < section.start + section.count);
78
+ export function sectionForSlide(presentation, slideIndex) {
79
+ return listSections(presentation).find((section) => slideIndex >= section.start && slideIndex < section.start + section.count);
80
80
  }
81
81
  /** The title shown for a slide in navigators and outlines. */
82
82
  export function slideTitle(slide) {
83
- return typeof slide?.title === "string" && slide.title.trim() ? slide.title : "";
83
+ // A title is a string or TextRun[] (FA-10): either shows as its plain text.
84
+ const title = typeof slide?.title === "string" ? slide.title : Array.isArray(slide?.title) ? slide.title.map((run) => (typeof run === "string" ? run : (run?.text ?? ""))).join("") : "";
85
+ return title.trim() ? title : "";
84
86
  }
85
87
  /** One row per slide for pickers and navigators: `{ index, id, title, hidden, section, layout }`. */
86
- export function slideSummaries(document) {
87
- return requireSlides(document).map((slide, index) => ({ index, id: slide.id, title: slideTitle(slide), hidden: slide.hidden === true, section: sectionOf(slide), layout: slide.layout }));
88
+ export function slideSummaries(presentation) {
89
+ return requireSlides(presentation).map((slide, index) => ({ index, id: slide.id, title: slideTitle(slide), hidden: slide.hidden === true, section: sectionOf(slide), layout: slide.layout }));
88
90
  }
89
91
 
90
92
  // --- patches ---------------------------------------------------------------------------------------
@@ -120,9 +122,9 @@ function permutationPatches(moved, order, values) {
120
122
 
121
123
  // --- add -------------------------------------------------------------------------------------------
122
124
 
123
- function newSlideId(document) {
124
- const used = new Set(collectReservedPresentationIds(document));
125
- let number = document.slides.length + 1;
125
+ function newSlideId(presentation) {
126
+ const used = new Set(collectReservedPresentationIds(presentation));
127
+ let number = presentation.slides.length + 1;
126
128
  while (used.has(`slide-${number}`)) number += 1;
127
129
  return `slide-${number}`;
128
130
  }
@@ -133,11 +135,11 @@ function newSlideId(document) {
133
135
  * `title`, `text`, `id`, `section` (default: the section of the slide before it), `slide` (a ready slide to
134
136
  * insert, for hosts that build their own), `catalogs`/`record` for catalog lookup (as `prepareDimensionSwitch`).
135
137
  */
136
- export function prepareAddSlide(document, options = {}) {
137
- const slides = requireSlides(document);
138
+ export function prepareAddSlide(presentation, options = {}) {
139
+ const slides = requireSlides(presentation);
138
140
  const at = options.at === undefined ? slides.length : options.at;
139
141
  if (!isIndex(at) || at > slides.length) throw fail("slide-index-out-of-range", "Add the slide at an index from 0 to the slide count.", { at });
140
- const id = options.id ?? options.slide?.id ?? newSlideId(document);
142
+ const id = options.id ?? options.slide?.id ?? newSlideId(presentation);
141
143
  let slide = options.slide ? clone(options.slide) : { id };
142
144
  if (!options.slide) {
143
145
  if (options.title !== undefined) slide.title = String(options.title);
@@ -149,20 +151,20 @@ export function prepareAddSlide(document, options = {}) {
149
151
  slide = withSection(slide, inherited);
150
152
  if (options.layout) {
151
153
  // Switch the layout on a scratch copy to reuse the placeholder rules and the catalog lookup.
152
- const scratch = { ...clone(document), slides: [...clone(slides)] };
154
+ const scratch = { ...clone(presentation), slides: [...clone(slides)] };
153
155
  scratch.slides.splice(at, 0, slide);
154
156
  const switched = prepareDimensionSwitch(scratch, "layouts", options.layout, { slideIndex: at, catalogs: options.catalogs, record: options.record });
155
157
  const catalogOps = switched.patches.filter((patch) => !patch.path.startsWith("/slides/"));
156
- slide = switched.document.slides[at];
158
+ slide = switched.presentation.slides[at];
157
159
  const patches = [...catalogOps, { op: "add", path: slidePointer(at), value: slide }];
158
- return finish(document, patches, { action: "add", index: at, id: slide.id, selection: [at], layout: options.layout });
160
+ return finish(presentation, patches, { action: "add", index: at, id: slide.id, selection: [at], layout: options.layout });
159
161
  }
160
- return finish(document, [{ op: "add", path: slidePointer(at), value: slide }], { action: "add", index: at, id: slide.id, selection: [at] });
162
+ return finish(presentation, [{ op: "add", path: slidePointer(at), value: slide }], { action: "add", index: at, id: slide.id, selection: [at] });
161
163
  }
162
164
  /** Add a slide (see `prepareAddSlide`) as one undoable step. The change reports `index`, `id` and `selection`. */
163
165
  export function addSlide(editor, options = {}, meta) {
164
166
  requireEditor(editor);
165
- return apply(editor, prepareAddSlide(editor.document, options), meta, "add");
167
+ return apply(editor, prepareAddSlide(editor.presentation, options), meta, "add");
166
168
  }
167
169
 
168
170
  // --- duplicate -------------------------------------------------------------------------------------
@@ -172,11 +174,11 @@ export function addSlide(editor, options = {}, meta) {
172
174
  * the slide and any content it identifies (`slide-2` becomes `slide-2-2`, as when pagination copies slides).
173
175
  * Copies join the section of the slide they follow unless `options.section` is `"keep"`.
174
176
  */
175
- export function prepareDuplicateSlides(document, indices, options = {}) {
176
- const list = selectionOf(document, indices, "Choose at least one slide to duplicate.");
177
- const slides = document.slides;
177
+ export function prepareDuplicateSlides(presentation, indices, options = {}) {
178
+ const list = selectionOf(presentation, indices, "Choose at least one slide to duplicate.");
179
+ const slides = presentation.slides;
178
180
  const at = list.at(-1) + 1;
179
- const ids = new Set(collectReservedPresentationIds(document));
181
+ const ids = new Set(collectReservedPresentationIds(presentation));
180
182
  const section = options.section === "keep" ? undefined : sectionOf(slides[at - 1]);
181
183
  const copies = list.map((index) => {
182
184
  let copy = clone(slides[index]);
@@ -185,11 +187,11 @@ export function prepareDuplicateSlides(document, indices, options = {}) {
185
187
  return copy;
186
188
  });
187
189
  const patches = copies.map((copy, offset) => ({ op: "add", path: slidePointer(at + offset), value: copy }));
188
- return finish(document, patches, { action: "duplicate", selection: copies.map((_, offset) => at + offset), range: { start: at, count: copies.length }, ids: copies.map((copy) => copy.id) });
190
+ return finish(presentation, patches, { action: "duplicate", selection: copies.map((_, offset) => at + offset), range: { start: at, count: copies.length }, ids: copies.map((copy) => copy.id) });
189
191
  }
190
192
  export function duplicateSlides(editor, indices, options = {}, meta) {
191
193
  requireEditor(editor);
192
- return apply(editor, prepareDuplicateSlides(editor.document, indices, options), meta, "duplicate");
194
+ return apply(editor, prepareDuplicateSlides(editor.presentation, indices, options), meta, "duplicate");
193
195
  }
194
196
 
195
197
  // --- remove ----------------------------------------------------------------------------------------
@@ -199,17 +201,17 @@ export function duplicateSlides(editor, indices, options = {}, meta) {
199
201
  * `cannot-remove-all-slides`. `selection` is the slide to show next (the one that moves into the first
200
202
  * removed position, or the last slide).
201
203
  */
202
- export function prepareRemoveSlides(document, indices) {
203
- const list = selectionOf(document, indices, "Choose at least one slide to delete.");
204
- const slides = document.slides;
204
+ export function prepareRemoveSlides(presentation, indices) {
205
+ const list = selectionOf(presentation, indices, "Choose at least one slide to delete.");
206
+ const slides = presentation.slides;
205
207
  if (list.length >= slides.length) throw fail("cannot-remove-all-slides", "A presentation needs at least one slide.", { count: slides.length });
206
208
  const patches = [...list].reverse().map((index) => ({ op: "remove", path: slidePointer(index) }));
207
209
  const remaining = slides.length - list.length;
208
- return finish(document, patches, { action: "remove", removed: list, selection: [Math.min(list[0], remaining - 1)] });
210
+ return finish(presentation, patches, { action: "remove", removed: list, selection: [Math.min(list[0], remaining - 1)] });
209
211
  }
210
212
  export function removeSlides(editor, indices, meta) {
211
213
  requireEditor(editor);
212
- return apply(editor, prepareRemoveSlides(editor.document, indices), meta, "remove");
214
+ return apply(editor, prepareRemoveSlides(editor.presentation, indices), meta, "remove");
213
215
  }
214
216
 
215
217
  // --- move ------------------------------------------------------------------------------------------
@@ -221,9 +223,9 @@ export function removeSlides(editor, indices, meta) {
221
223
  * first slide's when moving to the start), `"keep"` leaves labels alone, a string sets that section and
222
224
  * `null` clears it.
223
225
  */
224
- export function prepareMoveSlides(document, indices, to, options = {}) {
225
- const list = selectionOf(document, indices, "Choose at least one slide to move.");
226
- const slides = document.slides;
226
+ export function prepareMoveSlides(presentation, indices, to, options = {}) {
227
+ const list = selectionOf(presentation, indices, "Choose at least one slide to move.");
228
+ const slides = presentation.slides;
227
229
  if (!isIndex(to) || to > slides.length) throw fail("slide-index-out-of-range", "Move to a gap from 0 to the slide count.", { to });
228
230
  const chosen = new Set(list);
229
231
  const rest = slides.map((_, index) => index).filter((index) => !chosen.has(index));
@@ -237,18 +239,18 @@ export function prepareMoveSlides(document, indices, to, options = {}) {
237
239
  const values = new Map(list.map((index) => [index, target ? withSection(slides[index], target.name) : slides[index]]));
238
240
  const sameOrder = order.every((old, at) => old === at);
239
241
  const sameValues = list.every((index) => same(values.get(index), slides[index]));
240
- if (sameOrder && sameValues) return unchanged(document, { action: "move", selection: list });
242
+ if (sameOrder && sameValues) return unchanged(presentation, { action: "move", selection: list });
241
243
  // A pure relabel keeps the order: patch the labels in place instead of remove and add.
242
244
  const moved = new Set(list);
243
245
  const patches = sameOrder
244
246
  ? list.flatMap((index) => fieldPatch(slides[index], index, "section", sectionOf(values.get(index))))
245
247
  : permutationPatches(moved, order, values);
246
248
  const selection = list.map((old) => order.indexOf(old));
247
- return finish(document, patches, { action: "move", selection, order });
249
+ return finish(presentation, patches, { action: "move", selection, order });
248
250
  }
249
251
  export function moveSlides(editor, indices, to, options = {}, meta) {
250
252
  requireEditor(editor);
251
- return apply(editor, prepareMoveSlides(editor.document, indices, to, options), meta, "move");
253
+ return apply(editor, prepareMoveSlides(editor.presentation, indices, to, options), meta, "move");
252
254
  }
253
255
 
254
256
  /**
@@ -256,12 +258,12 @@ export function moveSlides(editor, indices, to, options = {}, meta) {
256
258
  * scattered one moves each slide past its neighbour, like Alt+Arrow in a list. Moving past the first or last
257
259
  * slide changes nothing (`changed` is false).
258
260
  */
259
- export function prepareMoveSlidesBy(document, indices, delta, options = {}) {
260
- const list = selectionOf(document, indices, "Choose at least one slide to move.");
261
- const count = document.slides.length;
262
- if (!Number.isInteger(delta) || delta === 0) return unchanged(document, { action: "move", selection: list });
261
+ export function prepareMoveSlidesBy(presentation, indices, delta, options = {}) {
262
+ const list = selectionOf(presentation, indices, "Choose at least one slide to move.");
263
+ const count = presentation.slides.length;
264
+ if (!Number.isInteger(delta) || delta === 0) return unchanged(presentation, { action: "move", selection: list });
263
265
  const step = Math.sign(delta);
264
- const order = document.slides.map((_, index) => index);
266
+ const order = presentation.slides.map((_, index) => index);
265
267
  const selected = new Set(list);
266
268
  for (let turn = 0; turn < Math.abs(delta); turn += 1) {
267
269
  const sequence = step < 0 ? [...order.keys()] : [...order.keys()].reverse();
@@ -274,8 +276,8 @@ export function prepareMoveSlidesBy(document, indices, delta, options = {}) {
274
276
  }
275
277
  if (!moved) break;
276
278
  }
277
- if (order.every((old, at) => old === at)) return unchanged(document, { action: "move", selection: list });
278
- const slides = document.slides;
279
+ if (order.every((old, at) => old === at)) return unchanged(presentation, { action: "move", selection: list });
280
+ const slides = presentation.slides;
279
281
  const mode = options.section === undefined ? "adopt" : options.section;
280
282
  // A moved slide adopts the section of the nearest slide that did not move: the one before it in the new order,
281
283
  // else the one after it.
@@ -288,11 +290,11 @@ export function prepareMoveSlidesBy(document, indices, delta, options = {}) {
288
290
  const name = mode === "keep" ? sectionOf(slides[index]) : mode === "adopt" ? adopted(order.indexOf(index)) : mode === null ? undefined : String(mode);
289
291
  return [index, withSection(slides[index], name)];
290
292
  }));
291
- return finish(document, permutationPatches(selected, order, values), { action: "move", selection: list.map((old) => order.indexOf(old)), order });
293
+ return finish(presentation, permutationPatches(selected, order, values), { action: "move", selection: list.map((old) => order.indexOf(old)), order });
292
294
  }
293
295
  export function moveSlidesBy(editor, indices, delta, options = {}, meta) {
294
296
  requireEditor(editor);
295
- return apply(editor, prepareMoveSlidesBy(editor.document, indices, delta, options), meta, "move");
297
+ return apply(editor, prepareMoveSlidesBy(editor.presentation, indices, delta, options), meta, "move");
296
298
  }
297
299
 
298
300
  // --- hide ------------------------------------------------------------------------------------------
@@ -302,24 +304,24 @@ export function moveSlidesBy(editor, indices, delta, options = {}, meta) {
302
304
  * navigator but is skipped when presenting; showing removes the `hidden` field instead of writing `false`.
303
305
  * Without `hidden`, the slides toggle together: all hidden become shown, otherwise all become hidden.
304
306
  */
305
- export function prepareSetHidden(document, indices, hidden) {
306
- const list = selectionOf(document, indices, "Choose at least one slide.");
307
- const slides = document.slides;
307
+ export function prepareSetHidden(presentation, indices, hidden) {
308
+ const list = selectionOf(presentation, indices, "Choose at least one slide.");
309
+ const slides = presentation.slides;
308
310
  const value = hidden === undefined ? !list.every((index) => slides[index].hidden === true) : Boolean(hidden);
309
311
  const patches = list.flatMap((index) => fieldPatch(slides[index], index, "hidden", value ? true : undefined));
310
312
  // A stored `hidden: false` counts as shown; showing it removes the field, hiding replaces it.
311
- return finish(document, patches, { action: value ? "hide" : "show", hidden: value, selection: list });
313
+ return finish(presentation, patches, { action: value ? "hide" : "show", hidden: value, selection: list });
312
314
  }
313
315
  export function setHidden(editor, indices, hidden, meta) {
314
316
  requireEditor(editor);
315
- const prepared = prepareSetHidden(editor.document, indices, hidden);
317
+ const prepared = prepareSetHidden(editor.presentation, indices, hidden);
316
318
  return apply(editor, prepared, meta, prepared.action);
317
319
  }
318
320
 
319
321
  // --- sections --------------------------------------------------------------------------------------
320
322
 
321
- function sectionAt(document, sectionIndex) {
322
- const section = listSections(document)[sectionIndex];
323
+ function sectionAt(presentation, sectionIndex) {
324
+ const section = listSections(presentation)[sectionIndex];
323
325
  if (!section) throw fail("section-index-out-of-range", `Section ${sectionIndex} does not exist.`, { sectionIndex });
324
326
  return section;
325
327
  }
@@ -329,24 +331,24 @@ const cleanName = (name) => (typeof name === "string" ? name.trim() : name);
329
331
  * Compute giving slides a section label (`name`), or clearing it (`null`). The slides keep their order, so
330
332
  * labelling a middle slide splits its section in two runs; use `prepareAddSection` to start a section.
331
333
  */
332
- export function prepareSetSection(document, indices, name) {
333
- const list = selectionOf(document, indices, "Choose at least one slide.");
334
+ export function prepareSetSection(presentation, indices, name) {
335
+ const list = selectionOf(presentation, indices, "Choose at least one slide.");
334
336
  const label = name === null || name === undefined ? undefined : cleanName(name);
335
337
  if (label === "") throw fail("invalid-section-name", "A section needs a name. Use null to remove the section from a slide.");
336
- const patches = list.flatMap((index) => fieldPatch(document.slides[index], index, "section", label));
337
- return finish(document, patches, { action: "section", section: label, selection: list });
338
+ const patches = list.flatMap((index) => fieldPatch(presentation.slides[index], index, "section", label));
339
+ return finish(presentation, patches, { action: "section", section: label, selection: list });
338
340
  }
339
341
  export function setSection(editor, indices, name, meta) {
340
342
  requireEditor(editor);
341
- return apply(editor, prepareSetSection(editor.document, indices, name), meta, "section");
343
+ return apply(editor, prepareSetSection(editor.presentation, indices, name), meta, "section");
342
344
  }
343
345
 
344
346
  /**
345
347
  * Compute starting a section at a slide: that slide and the slides after it up to the end of its current
346
348
  * section take `name` (PowerPoint's "Add section", whose header sits above the chosen slide).
347
349
  */
348
- export function prepareAddSection(document, slideIndex, name) {
349
- const slides = requireSlides(document);
350
+ export function prepareAddSection(presentation, slideIndex, name) {
351
+ const slides = requireSlides(presentation);
350
352
  if (!isIndex(slideIndex) || slideIndex >= slides.length) throw fail("slide-index-out-of-range", `Slide ${slideIndex} does not exist.`, { slideIndex });
351
353
  const label = cleanName(name);
352
354
  if (typeof label !== "string" || label === "") throw fail("invalid-section-name", "Give the section a name.");
@@ -358,25 +360,25 @@ export function prepareAddSection(document, slideIndex, name) {
358
360
  })();
359
361
  const indices = Array.from({ length: end - slideIndex + 1 }, (_, offset) => slideIndex + offset);
360
362
  const patches = indices.flatMap((index) => fieldPatch(slides[index], index, "section", label));
361
- return finish(document, patches, { action: "add-section", section: label, selection: indices });
363
+ return finish(presentation, patches, { action: "add-section", section: label, selection: indices });
362
364
  }
363
365
  export function addSection(editor, slideIndex, name, meta) {
364
366
  requireEditor(editor);
365
- return apply(editor, prepareAddSection(editor.document, slideIndex, name), meta, "add-section");
367
+ return apply(editor, prepareAddSection(editor.presentation, slideIndex, name), meta, "add-section");
366
368
  }
367
369
 
368
370
  /** Compute renaming a section (an index from `listSections`) for all of its slides. */
369
- export function prepareRenameSection(document, sectionIndex, name) {
370
- const section = sectionAt(document, sectionIndex);
371
+ export function prepareRenameSection(presentation, sectionIndex, name) {
372
+ const section = sectionAt(presentation, sectionIndex);
371
373
  const label = cleanName(name);
372
374
  if (typeof label !== "string" || label === "") throw fail("invalid-section-name", "Give the section a name.");
373
375
  const indices = Array.from({ length: section.count }, (_, offset) => section.start + offset);
374
- const patches = indices.flatMap((index) => fieldPatch(document.slides[index], index, "section", label));
375
- return finish(document, patches, { action: "rename-section", section: label, selection: indices });
376
+ const patches = indices.flatMap((index) => fieldPatch(presentation.slides[index], index, "section", label));
377
+ return finish(presentation, patches, { action: "rename-section", section: label, selection: indices });
376
378
  }
377
379
  export function renameSection(editor, sectionIndex, name, meta) {
378
380
  requireEditor(editor);
379
- return apply(editor, prepareRenameSection(editor.document, sectionIndex, name), meta, "rename-section");
381
+ return apply(editor, prepareRenameSection(editor.presentation, sectionIndex, name), meta, "rename-section");
380
382
  }
381
383
 
382
384
  /**
@@ -384,37 +386,37 @@ export function renameSection(editor, sectionIndex, name, meta) {
384
386
  * lose their label). With `deleteSlides: true` the slides are deleted too (not allowed when they are every
385
387
  * slide of the deck).
386
388
  */
387
- export function prepareRemoveSection(document, sectionIndex, options = {}) {
388
- const sections = listSections(document);
389
- const section = sectionAt(document, sectionIndex);
389
+ export function prepareRemoveSection(presentation, sectionIndex, options = {}) {
390
+ const sections = listSections(presentation);
391
+ const section = sectionAt(presentation, sectionIndex);
390
392
  const indices = Array.from({ length: section.count }, (_, offset) => section.start + offset);
391
- if (options.deleteSlides) return { ...prepareRemoveSlides(document, indices), action: "remove-section" };
393
+ if (options.deleteSlides) return { ...prepareRemoveSlides(presentation, indices), action: "remove-section" };
392
394
  const previous = sections[sectionIndex - 1];
393
395
  const label = previous?.name;
394
- const patches = indices.flatMap((index) => fieldPatch(document.slides[index], index, "section", label));
395
- return finish(document, patches, { action: "remove-section", section: label, selection: indices });
396
+ const patches = indices.flatMap((index) => fieldPatch(presentation.slides[index], index, "section", label));
397
+ return finish(presentation, patches, { action: "remove-section", section: label, selection: indices });
396
398
  }
397
399
  export function removeSection(editor, sectionIndex, options = {}, meta) {
398
400
  requireEditor(editor);
399
- return apply(editor, prepareRemoveSection(editor.document, sectionIndex, options), meta, "remove-section");
401
+ return apply(editor, prepareRemoveSection(editor.presentation, sectionIndex, options), meta, "remove-section");
400
402
  }
401
403
 
402
404
  /**
403
405
  * Compute moving a whole section to the gap `to` among the sections (0 to the section count, in the
404
406
  * original order of `listSections`). The slides keep their labels.
405
407
  */
406
- export function prepareMoveSection(document, sectionIndex, to) {
407
- const sections = listSections(document);
408
- const section = sectionAt(document, sectionIndex);
408
+ export function prepareMoveSection(presentation, sectionIndex, to) {
409
+ const sections = listSections(presentation);
410
+ const section = sectionAt(presentation, sectionIndex);
409
411
  if (!isIndex(to) || to > sections.length) throw fail("section-index-out-of-range", "Move to a gap from 0 to the section count.", { to });
410
- const gap = to === sections.length ? document.slides.length : sections[to].start;
412
+ const gap = to === sections.length ? presentation.slides.length : sections[to].start;
411
413
  const indices = Array.from({ length: section.count }, (_, offset) => section.start + offset);
412
- const prepared = prepareMoveSlides(document, indices, gap, { section: "keep" });
414
+ const prepared = prepareMoveSlides(presentation, indices, gap, { section: "keep" });
413
415
  return { ...prepared, action: "move-section", section: section.name };
414
416
  }
415
417
  export function moveSection(editor, sectionIndex, to, meta) {
416
418
  requireEditor(editor);
417
- return apply(editor, prepareMoveSection(editor.document, sectionIndex, to), meta, "move-section");
419
+ return apply(editor, prepareMoveSection(editor.presentation, sectionIndex, to), meta, "move-section");
418
420
  }
419
421
 
420
422
  /** Every slide operation as a name to its `prepare` function, for hosts that map a command id to a change. */
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.document
55
+ presentation: nextParams.presentation ?? editor.presentation
56
56
  }));
57
57
 
58
58
  const sync = () => {
@@ -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, background and dimensions to the deck design (default true). */
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 (and slide-sizes): remove slide-level values that would hide the switch. */
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
- document: unknown;
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, "document" | "patches"> {
79
- document: unknown;
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(document: unknown, dimension: SwitchDimension, value: DimensionSwitchValue, options?: DimensionSwitchOptions): PreparedDimensionSwitch;
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(document: unknown, dimension: SwitchDimension, options?: Pick<DimensionSwitchOptions, "catalogs" | "catalogSources">): SwitchOption[];
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(document: unknown, options?: Pick<DimensionSwitchOptions, "slideIndex" | "path" | "catalogs" | "catalogSources">): CompatibleChartType[];
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(document: unknown, dimension: SwitchDimension, options?: Pick<DimensionSwitchOptions, "slideIndex" | "path" | "owner" | "index">): { value: unknown; scope: "deck" | "slide" | "block" };
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";