@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
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
 
@@ -48,7 +48,7 @@ export type MoveSectionMode = "adopt" | "keep" | string | null;
48
48
  export interface AddSlideOptions {
49
49
  /** The index the slide takes (default: the end). */
50
50
  at?: number;
51
- /** A layouts catalog id; the layout's placeholders are added as empty slots. */
51
+ /** A layout reference (`id` or `name:id`); the layout's placeholders are added as empty slots. */
52
52
  layout?: string;
53
53
  title?: string;
54
54
  text?: string;
@@ -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,11 @@
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 { mergeCatalogs } from "./catalogs.js";
15
+ import { checkFormat } from "./checks.js";
15
16
 
16
17
  const isIndex = (value) => Number.isInteger(value) && value >= 0;
17
18
  const clone = (value) => structuredClone(value);
@@ -19,13 +20,13 @@ const clone = (value) => structuredClone(value);
19
20
  function requireEditor(editor) {
20
21
  if (!editor || typeof editor.applyPatch !== "function") throw fail("invalid-editor", "Expected an editor session created by createEditorSession.");
21
22
  }
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;
23
+ function requireSlides(presentation) {
24
+ if (!presentation || typeof presentation !== "object" || !Array.isArray(presentation.slides)) throw fail("invalid-input", "Slide management needs an OPF document with a slides array.");
25
+ return presentation.slides;
25
26
  }
26
27
  /** 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);
28
+ function selectionOf(presentation, indices, what = "Choose at least one slide.") {
29
+ const slides = requireSlides(presentation);
29
30
  const list = [...new Set(Array.isArray(indices) ? indices : [indices])];
30
31
  if (!list.length) throw fail("no-slides-chosen", what);
31
32
  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 +34,21 @@ function selectionOf(document, indices, what = "Choose at least one slide.") {
33
34
  }
34
35
  const sectionOf = (slide) => (typeof slide?.section === "string" && slide.section.trim() !== "" ? slide.section : undefined);
35
36
 
36
- function unchanged(document, extra = {}) {
37
- return { document: clone(document), patches: [], changed: false, selection: [], ...extra };
37
+ function unchanged(presentation, extra = {}) {
38
+ return { presentation: clone(presentation), patches: [], changed: false, selection: [], ...extra };
38
39
  }
39
40
  /** 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 };
41
+ function finish(presentation, patches, extra) {
42
+ if (!patches.length) return unchanged(presentation, extra);
43
+ const before = checkFormat(presentation);
44
+ const next = checkedDocument(presentation, patches, before);
45
+ return { presentation: next, patches, changed: true, ...extra };
45
46
  }
46
47
  function apply(editor, prepared, meta, action) {
47
48
  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 };
49
+ const { presentation, patches, ...summary } = prepared;
50
+ void presentation;
51
+ if (!prepared.changed) return { ...summary, presentation: editor.presentation, patches: [], inversePatches: [], validation: editor.validation };
51
52
  const change = editor.applyPatch(patches, { ...meta, source: meta?.source ?? "slides", action });
52
53
  return { ...change, ...summary };
53
54
  }
@@ -59,8 +60,8 @@ function apply(editor, prepared, meta, action) {
59
60
  * of slides with no label is reported with `name: undefined` (`unnamed: true`). `index` is the position in
60
61
  * the returned list, the id every section operation takes.
61
62
  */
62
- export function listSections(document) {
63
- const slides = requireSlides(document);
63
+ export function listSections(presentation) {
64
+ const slides = requireSlides(presentation);
64
65
  const sections = [];
65
66
  slides.forEach((slide, slideIndex) => {
66
67
  const name = sectionOf(slide);
@@ -71,20 +72,22 @@ export function listSections(document) {
71
72
  return sections;
72
73
  }
73
74
  /** 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);
75
+ export function hasSections(presentation) {
76
+ return requireSlides(presentation).some((slide) => sectionOf(slide) !== undefined);
76
77
  }
77
78
  /** 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);
79
+ export function sectionForSlide(presentation, slideIndex) {
80
+ return listSections(presentation).find((section) => slideIndex >= section.start && slideIndex < section.start + section.count);
80
81
  }
81
82
  /** The title shown for a slide in navigators and outlines. */
82
83
  export function slideTitle(slide) {
83
- return typeof slide?.title === "string" && slide.title.trim() ? slide.title : "";
84
+ // A title is a string or TextRun[] (FA-10): either shows as its plain text.
85
+ const title = typeof slide?.title === "string" ? slide.title : Array.isArray(slide?.title) ? slide.title.map((run) => (typeof run === "string" ? run : (run?.text ?? ""))).join("") : "";
86
+ return title.trim() ? title : "";
84
87
  }
85
88
  /** 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 }));
89
+ export function slideSummaries(presentation) {
90
+ return requireSlides(presentation).map((slide, index) => ({ index, id: slide.id, title: slideTitle(slide), hidden: slide.hidden === true, section: sectionOf(slide), layout: slide.layout }));
88
91
  }
89
92
 
90
93
  // --- patches ---------------------------------------------------------------------------------------
@@ -120,24 +123,24 @@ function permutationPatches(moved, order, values) {
120
123
 
121
124
  // --- add -------------------------------------------------------------------------------------------
122
125
 
123
- function newSlideId(document) {
124
- const used = new Set(collectReservedPresentationIds(document));
125
- let number = document.slides.length + 1;
126
+ function newSlideId(presentation) {
127
+ const used = new Set(collectReservedPresentationIds(presentation));
128
+ let number = presentation.slides.length + 1;
126
129
  while (used.has(`slide-${number}`)) number += 1;
127
130
  return `slide-${number}`;
128
131
  }
129
132
 
130
133
  /**
131
- * Compute adding a slide. Options: `at` (the index the slide takes, default the end), `layout` (a layouts
132
- * catalog id; the layout's placeholders are added as empty slots, exactly like switching a slide's layout),
134
+ * Compute adding a slide. Options: `at` (the index the slide takes, default the end), `layout` (a layout
135
+ * reference, `id` or `name:id`; the layout's placeholders are added as empty slots, exactly like switching a slide's layout),
133
136
  * `title`, `text`, `id`, `section` (default: the section of the slide before it), `slide` (a ready slide to
134
- * insert, for hosts that build their own), `catalogs`/`record` for catalog lookup (as `prepareDimensionSwitch`).
137
+ * insert, for hosts that build their own), `catalogs`/`record`/`recordSource` for catalog lookup (as `prepareDimensionSwitch`).
135
138
  */
136
- export function prepareAddSlide(document, options = {}) {
137
- const slides = requireSlides(document);
139
+ export function prepareAddSlide(presentation, options = {}) {
140
+ const slides = requireSlides(presentation);
138
141
  const at = options.at === undefined ? slides.length : options.at;
139
142
  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);
143
+ const id = options.id ?? options.slide?.id ?? newSlideId(presentation);
141
144
  let slide = options.slide ? clone(options.slide) : { id };
142
145
  if (!options.slide) {
143
146
  if (options.title !== undefined) slide.title = String(options.title);
@@ -149,20 +152,20 @@ export function prepareAddSlide(document, options = {}) {
149
152
  slide = withSection(slide, inherited);
150
153
  if (options.layout) {
151
154
  // Switch the layout on a scratch copy to reuse the placeholder rules and the catalog lookup.
152
- const scratch = { ...clone(document), slides: [...clone(slides)] };
155
+ const scratch = { ...clone(presentation), slides: [...clone(slides)] };
153
156
  scratch.slides.splice(at, 0, slide);
154
- const switched = prepareDimensionSwitch(scratch, "layouts", options.layout, { slideIndex: at, catalogs: options.catalogs, record: options.record });
157
+ const switched = prepareDimensionSwitch(scratch, "layouts", options.layout, { slideIndex: at, catalogs: options.catalogs, record: options.record, recordSource: options.recordSource });
155
158
  const catalogOps = switched.patches.filter((patch) => !patch.path.startsWith("/slides/"));
156
- slide = switched.document.slides[at];
159
+ slide = switched.presentation.slides[at];
157
160
  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 });
161
+ return finish(presentation, patches, { action: "add", index: at, id: slide.id, selection: [at], layout: options.layout });
159
162
  }
160
- return finish(document, [{ op: "add", path: slidePointer(at), value: slide }], { action: "add", index: at, id: slide.id, selection: [at] });
163
+ return finish(presentation, [{ op: "add", path: slidePointer(at), value: slide }], { action: "add", index: at, id: slide.id, selection: [at] });
161
164
  }
162
165
  /** Add a slide (see `prepareAddSlide`) as one undoable step. The change reports `index`, `id` and `selection`. */
163
166
  export function addSlide(editor, options = {}, meta) {
164
167
  requireEditor(editor);
165
- return apply(editor, prepareAddSlide(editor.document, options), meta, "add");
168
+ return apply(editor, prepareAddSlide(editor.presentation, { ...options, catalogs: mergeCatalogs(editor.catalogs, options.catalogs) }), meta, "add");
166
169
  }
167
170
 
168
171
  // --- duplicate -------------------------------------------------------------------------------------
@@ -172,11 +175,11 @@ export function addSlide(editor, options = {}, meta) {
172
175
  * the slide and any content it identifies (`slide-2` becomes `slide-2-2`, as when pagination copies slides).
173
176
  * Copies join the section of the slide they follow unless `options.section` is `"keep"`.
174
177
  */
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;
178
+ export function prepareDuplicateSlides(presentation, indices, options = {}) {
179
+ const list = selectionOf(presentation, indices, "Choose at least one slide to duplicate.");
180
+ const slides = presentation.slides;
178
181
  const at = list.at(-1) + 1;
179
- const ids = new Set(collectReservedPresentationIds(document));
182
+ const ids = new Set(collectReservedPresentationIds(presentation));
180
183
  const section = options.section === "keep" ? undefined : sectionOf(slides[at - 1]);
181
184
  const copies = list.map((index) => {
182
185
  let copy = clone(slides[index]);
@@ -185,11 +188,11 @@ export function prepareDuplicateSlides(document, indices, options = {}) {
185
188
  return copy;
186
189
  });
187
190
  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) });
191
+ 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
192
  }
190
193
  export function duplicateSlides(editor, indices, options = {}, meta) {
191
194
  requireEditor(editor);
192
- return apply(editor, prepareDuplicateSlides(editor.document, indices, options), meta, "duplicate");
195
+ return apply(editor, prepareDuplicateSlides(editor.presentation, indices, options), meta, "duplicate");
193
196
  }
194
197
 
195
198
  // --- remove ----------------------------------------------------------------------------------------
@@ -199,17 +202,17 @@ export function duplicateSlides(editor, indices, options = {}, meta) {
199
202
  * `cannot-remove-all-slides`. `selection` is the slide to show next (the one that moves into the first
200
203
  * removed position, or the last slide).
201
204
  */
202
- export function prepareRemoveSlides(document, indices) {
203
- const list = selectionOf(document, indices, "Choose at least one slide to delete.");
204
- const slides = document.slides;
205
+ export function prepareRemoveSlides(presentation, indices) {
206
+ const list = selectionOf(presentation, indices, "Choose at least one slide to delete.");
207
+ const slides = presentation.slides;
205
208
  if (list.length >= slides.length) throw fail("cannot-remove-all-slides", "A presentation needs at least one slide.", { count: slides.length });
206
209
  const patches = [...list].reverse().map((index) => ({ op: "remove", path: slidePointer(index) }));
207
210
  const remaining = slides.length - list.length;
208
- return finish(document, patches, { action: "remove", removed: list, selection: [Math.min(list[0], remaining - 1)] });
211
+ return finish(presentation, patches, { action: "remove", removed: list, selection: [Math.min(list[0], remaining - 1)] });
209
212
  }
210
213
  export function removeSlides(editor, indices, meta) {
211
214
  requireEditor(editor);
212
- return apply(editor, prepareRemoveSlides(editor.document, indices), meta, "remove");
215
+ return apply(editor, prepareRemoveSlides(editor.presentation, indices), meta, "remove");
213
216
  }
214
217
 
215
218
  // --- move ------------------------------------------------------------------------------------------
@@ -221,9 +224,9 @@ export function removeSlides(editor, indices, meta) {
221
224
  * first slide's when moving to the start), `"keep"` leaves labels alone, a string sets that section and
222
225
  * `null` clears it.
223
226
  */
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;
227
+ export function prepareMoveSlides(presentation, indices, to, options = {}) {
228
+ const list = selectionOf(presentation, indices, "Choose at least one slide to move.");
229
+ const slides = presentation.slides;
227
230
  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
231
  const chosen = new Set(list);
229
232
  const rest = slides.map((_, index) => index).filter((index) => !chosen.has(index));
@@ -237,18 +240,18 @@ export function prepareMoveSlides(document, indices, to, options = {}) {
237
240
  const values = new Map(list.map((index) => [index, target ? withSection(slides[index], target.name) : slides[index]]));
238
241
  const sameOrder = order.every((old, at) => old === at);
239
242
  const sameValues = list.every((index) => same(values.get(index), slides[index]));
240
- if (sameOrder && sameValues) return unchanged(document, { action: "move", selection: list });
243
+ if (sameOrder && sameValues) return unchanged(presentation, { action: "move", selection: list });
241
244
  // A pure relabel keeps the order: patch the labels in place instead of remove and add.
242
245
  const moved = new Set(list);
243
246
  const patches = sameOrder
244
247
  ? list.flatMap((index) => fieldPatch(slides[index], index, "section", sectionOf(values.get(index))))
245
248
  : permutationPatches(moved, order, values);
246
249
  const selection = list.map((old) => order.indexOf(old));
247
- return finish(document, patches, { action: "move", selection, order });
250
+ return finish(presentation, patches, { action: "move", selection, order });
248
251
  }
249
252
  export function moveSlides(editor, indices, to, options = {}, meta) {
250
253
  requireEditor(editor);
251
- return apply(editor, prepareMoveSlides(editor.document, indices, to, options), meta, "move");
254
+ return apply(editor, prepareMoveSlides(editor.presentation, indices, to, options), meta, "move");
252
255
  }
253
256
 
254
257
  /**
@@ -256,12 +259,12 @@ export function moveSlides(editor, indices, to, options = {}, meta) {
256
259
  * scattered one moves each slide past its neighbour, like Alt+Arrow in a list. Moving past the first or last
257
260
  * slide changes nothing (`changed` is false).
258
261
  */
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 });
262
+ export function prepareMoveSlidesBy(presentation, indices, delta, options = {}) {
263
+ const list = selectionOf(presentation, indices, "Choose at least one slide to move.");
264
+ const count = presentation.slides.length;
265
+ if (!Number.isInteger(delta) || delta === 0) return unchanged(presentation, { action: "move", selection: list });
263
266
  const step = Math.sign(delta);
264
- const order = document.slides.map((_, index) => index);
267
+ const order = presentation.slides.map((_, index) => index);
265
268
  const selected = new Set(list);
266
269
  for (let turn = 0; turn < Math.abs(delta); turn += 1) {
267
270
  const sequence = step < 0 ? [...order.keys()] : [...order.keys()].reverse();
@@ -274,8 +277,8 @@ export function prepareMoveSlidesBy(document, indices, delta, options = {}) {
274
277
  }
275
278
  if (!moved) break;
276
279
  }
277
- if (order.every((old, at) => old === at)) return unchanged(document, { action: "move", selection: list });
278
- const slides = document.slides;
280
+ if (order.every((old, at) => old === at)) return unchanged(presentation, { action: "move", selection: list });
281
+ const slides = presentation.slides;
279
282
  const mode = options.section === undefined ? "adopt" : options.section;
280
283
  // A moved slide adopts the section of the nearest slide that did not move: the one before it in the new order,
281
284
  // else the one after it.
@@ -288,11 +291,11 @@ export function prepareMoveSlidesBy(document, indices, delta, options = {}) {
288
291
  const name = mode === "keep" ? sectionOf(slides[index]) : mode === "adopt" ? adopted(order.indexOf(index)) : mode === null ? undefined : String(mode);
289
292
  return [index, withSection(slides[index], name)];
290
293
  }));
291
- return finish(document, permutationPatches(selected, order, values), { action: "move", selection: list.map((old) => order.indexOf(old)), order });
294
+ return finish(presentation, permutationPatches(selected, order, values), { action: "move", selection: list.map((old) => order.indexOf(old)), order });
292
295
  }
293
296
  export function moveSlidesBy(editor, indices, delta, options = {}, meta) {
294
297
  requireEditor(editor);
295
- return apply(editor, prepareMoveSlidesBy(editor.document, indices, delta, options), meta, "move");
298
+ return apply(editor, prepareMoveSlidesBy(editor.presentation, indices, delta, options), meta, "move");
296
299
  }
297
300
 
298
301
  // --- hide ------------------------------------------------------------------------------------------
@@ -302,24 +305,24 @@ export function moveSlidesBy(editor, indices, delta, options = {}, meta) {
302
305
  * navigator but is skipped when presenting; showing removes the `hidden` field instead of writing `false`.
303
306
  * Without `hidden`, the slides toggle together: all hidden become shown, otherwise all become hidden.
304
307
  */
305
- export function prepareSetHidden(document, indices, hidden) {
306
- const list = selectionOf(document, indices, "Choose at least one slide.");
307
- const slides = document.slides;
308
+ export function prepareSetHidden(presentation, indices, hidden) {
309
+ const list = selectionOf(presentation, indices, "Choose at least one slide.");
310
+ const slides = presentation.slides;
308
311
  const value = hidden === undefined ? !list.every((index) => slides[index].hidden === true) : Boolean(hidden);
309
312
  const patches = list.flatMap((index) => fieldPatch(slides[index], index, "hidden", value ? true : undefined));
310
313
  // 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 });
314
+ return finish(presentation, patches, { action: value ? "hide" : "show", hidden: value, selection: list });
312
315
  }
313
316
  export function setHidden(editor, indices, hidden, meta) {
314
317
  requireEditor(editor);
315
- const prepared = prepareSetHidden(editor.document, indices, hidden);
318
+ const prepared = prepareSetHidden(editor.presentation, indices, hidden);
316
319
  return apply(editor, prepared, meta, prepared.action);
317
320
  }
318
321
 
319
322
  // --- sections --------------------------------------------------------------------------------------
320
323
 
321
- function sectionAt(document, sectionIndex) {
322
- const section = listSections(document)[sectionIndex];
324
+ function sectionAt(presentation, sectionIndex) {
325
+ const section = listSections(presentation)[sectionIndex];
323
326
  if (!section) throw fail("section-index-out-of-range", `Section ${sectionIndex} does not exist.`, { sectionIndex });
324
327
  return section;
325
328
  }
@@ -329,24 +332,24 @@ const cleanName = (name) => (typeof name === "string" ? name.trim() : name);
329
332
  * Compute giving slides a section label (`name`), or clearing it (`null`). The slides keep their order, so
330
333
  * labelling a middle slide splits its section in two runs; use `prepareAddSection` to start a section.
331
334
  */
332
- export function prepareSetSection(document, indices, name) {
333
- const list = selectionOf(document, indices, "Choose at least one slide.");
335
+ export function prepareSetSection(presentation, indices, name) {
336
+ const list = selectionOf(presentation, indices, "Choose at least one slide.");
334
337
  const label = name === null || name === undefined ? undefined : cleanName(name);
335
338
  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 });
339
+ const patches = list.flatMap((index) => fieldPatch(presentation.slides[index], index, "section", label));
340
+ return finish(presentation, patches, { action: "section", section: label, selection: list });
338
341
  }
339
342
  export function setSection(editor, indices, name, meta) {
340
343
  requireEditor(editor);
341
- return apply(editor, prepareSetSection(editor.document, indices, name), meta, "section");
344
+ return apply(editor, prepareSetSection(editor.presentation, indices, name), meta, "section");
342
345
  }
343
346
 
344
347
  /**
345
348
  * Compute starting a section at a slide: that slide and the slides after it up to the end of its current
346
349
  * section take `name` (PowerPoint's "Add section", whose header sits above the chosen slide).
347
350
  */
348
- export function prepareAddSection(document, slideIndex, name) {
349
- const slides = requireSlides(document);
351
+ export function prepareAddSection(presentation, slideIndex, name) {
352
+ const slides = requireSlides(presentation);
350
353
  if (!isIndex(slideIndex) || slideIndex >= slides.length) throw fail("slide-index-out-of-range", `Slide ${slideIndex} does not exist.`, { slideIndex });
351
354
  const label = cleanName(name);
352
355
  if (typeof label !== "string" || label === "") throw fail("invalid-section-name", "Give the section a name.");
@@ -358,25 +361,25 @@ export function prepareAddSection(document, slideIndex, name) {
358
361
  })();
359
362
  const indices = Array.from({ length: end - slideIndex + 1 }, (_, offset) => slideIndex + offset);
360
363
  const patches = indices.flatMap((index) => fieldPatch(slides[index], index, "section", label));
361
- return finish(document, patches, { action: "add-section", section: label, selection: indices });
364
+ return finish(presentation, patches, { action: "add-section", section: label, selection: indices });
362
365
  }
363
366
  export function addSection(editor, slideIndex, name, meta) {
364
367
  requireEditor(editor);
365
- return apply(editor, prepareAddSection(editor.document, slideIndex, name), meta, "add-section");
368
+ return apply(editor, prepareAddSection(editor.presentation, slideIndex, name), meta, "add-section");
366
369
  }
367
370
 
368
371
  /** 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);
372
+ export function prepareRenameSection(presentation, sectionIndex, name) {
373
+ const section = sectionAt(presentation, sectionIndex);
371
374
  const label = cleanName(name);
372
375
  if (typeof label !== "string" || label === "") throw fail("invalid-section-name", "Give the section a name.");
373
376
  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 });
377
+ const patches = indices.flatMap((index) => fieldPatch(presentation.slides[index], index, "section", label));
378
+ return finish(presentation, patches, { action: "rename-section", section: label, selection: indices });
376
379
  }
377
380
  export function renameSection(editor, sectionIndex, name, meta) {
378
381
  requireEditor(editor);
379
- return apply(editor, prepareRenameSection(editor.document, sectionIndex, name), meta, "rename-section");
382
+ return apply(editor, prepareRenameSection(editor.presentation, sectionIndex, name), meta, "rename-section");
380
383
  }
381
384
 
382
385
  /**
@@ -384,37 +387,37 @@ export function renameSection(editor, sectionIndex, name, meta) {
384
387
  * lose their label). With `deleteSlides: true` the slides are deleted too (not allowed when they are every
385
388
  * slide of the deck).
386
389
  */
387
- export function prepareRemoveSection(document, sectionIndex, options = {}) {
388
- const sections = listSections(document);
389
- const section = sectionAt(document, sectionIndex);
390
+ export function prepareRemoveSection(presentation, sectionIndex, options = {}) {
391
+ const sections = listSections(presentation);
392
+ const section = sectionAt(presentation, sectionIndex);
390
393
  const indices = Array.from({ length: section.count }, (_, offset) => section.start + offset);
391
- if (options.deleteSlides) return { ...prepareRemoveSlides(document, indices), action: "remove-section" };
394
+ if (options.deleteSlides) return { ...prepareRemoveSlides(presentation, indices), action: "remove-section" };
392
395
  const previous = sections[sectionIndex - 1];
393
396
  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 });
397
+ const patches = indices.flatMap((index) => fieldPatch(presentation.slides[index], index, "section", label));
398
+ return finish(presentation, patches, { action: "remove-section", section: label, selection: indices });
396
399
  }
397
400
  export function removeSection(editor, sectionIndex, options = {}, meta) {
398
401
  requireEditor(editor);
399
- return apply(editor, prepareRemoveSection(editor.document, sectionIndex, options), meta, "remove-section");
402
+ return apply(editor, prepareRemoveSection(editor.presentation, sectionIndex, options), meta, "remove-section");
400
403
  }
401
404
 
402
405
  /**
403
406
  * Compute moving a whole section to the gap `to` among the sections (0 to the section count, in the
404
407
  * original order of `listSections`). The slides keep their labels.
405
408
  */
406
- export function prepareMoveSection(document, sectionIndex, to) {
407
- const sections = listSections(document);
408
- const section = sectionAt(document, sectionIndex);
409
+ export function prepareMoveSection(presentation, sectionIndex, to) {
410
+ const sections = listSections(presentation);
411
+ const section = sectionAt(presentation, sectionIndex);
409
412
  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;
413
+ const gap = to === sections.length ? presentation.slides.length : sections[to].start;
411
414
  const indices = Array.from({ length: section.count }, (_, offset) => section.start + offset);
412
- const prepared = prepareMoveSlides(document, indices, gap, { section: "keep" });
415
+ const prepared = prepareMoveSlides(presentation, indices, gap, { section: "keep" });
413
416
  return { ...prepared, action: "move-section", section: section.name };
414
417
  }
415
418
  export function moveSection(editor, sectionIndex, to, meta) {
416
419
  requireEditor(editor);
417
- return apply(editor, prepareMoveSection(editor.document, sectionIndex, to), meta, "move-section");
420
+ return apply(editor, prepareMoveSection(editor.presentation, sectionIndex, to), meta, "move-section");
418
421
  }
419
422
 
420
423
  /** 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
@@ -50,9 +50,9 @@ export function opfCatalogSelect(node, params) {
50
50
  node.setAttribute?.("data-opf-catalog-kind", catalogKind);
51
51
  if (label) node.setAttribute?.("aria-label", label);
52
52
  replaceOptions(node, getCatalogOptions(catalogKind, {
53
+ editor,
53
54
  catalogs: nextParams.catalogs,
54
- catalogSources: nextParams.catalogSources,
55
- presentation: nextParams.presentation ?? editor.document
55
+ presentation: nextParams.presentation ?? editor.presentation
56
56
  }));
57
57
 
58
58
  const sync = () => {
@@ -61,7 +61,6 @@ export function opfCatalogSelect(node, params) {
61
61
  };
62
62
  const onChange = () => setCatalogId(editor, path, catalogKind, node.value, {
63
63
  catalogs: nextParams.catalogs,
64
- catalogSources: nextParams.catalogSources,
65
64
  presentation: nextParams.presentation,
66
65
  source: "svelte-catalog-select"
67
66
  });