@openpresentation/opf-editor 0.29.1 → 0.31.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.
@@ -12,8 +12,22 @@ import type { ReviewFinding, ReviewHook } from "./review.js";
12
12
  import type { ReviewPanel, ReviewPanelOptions } from "./review-panel.js";
13
13
  import type { SlideManager } from "./slide-manager.js";
14
14
  import type { SourceCursor, SourceFormat, SourceFile, SourcePane, SourcePaneOptions } from "./source-pane.js";
15
+ import type { Toolbar, ToolbarItemName, ToolbarItemSpec } from "./toolbar.js";
16
+ import type { DeckTitle } from "./deck-title.js";
17
+ import type { OpfIcons } from "./icons.js";
18
+ import type { DesignMenu, DesignMenuLabels } from "./design-menu.js";
19
+ import type { ReviewPill, ReviewPillCounts, ReviewPillLabels } from "./review-pill.js";
20
+ import type { HelpButton, HelpButtonLabels, HelpShortcut } from "./shortcuts-sheet.js";
21
+ import type { ToolbarAction, ToolbarActions, ToolbarActionsLabels } from "./toolbar-actions.js";
15
22
 
16
- export type OpfEditorLayout = "full" | "canvas" | "source" | "studio" | "playground";
23
+ /** CE-28: `"embed"` is the slide alone under a compact toolbar (title, history, counter, expand, Present), for a page that shows a deck. */
24
+ export type OpfEditorLayout = "full" | "canvas" | "embed" | "viewer" | "source" | "studio" | "playground";
25
+ export type { ToolbarAction, HelpShortcut };
26
+ /** CE-28: a toolbar item by name, or `{ id, priority, collapsible }` (see `toolbar.d.ts`). */
27
+ export type OpfToolbarItem = ToolbarItemSpec;
28
+ /** CE-29: the names of the `icons` option. */
29
+ export type OpfEditorIconName = "more" | "expand" | "collapse" | "valid" | "error" | "warning" | "info" | "checking" | "unavailable" | "help" | "close" | "trigger" | "check" | "previous" | "next" | "add" | "present";
30
+ export type { Toolbar, ToolbarItemName };
17
31
  export type OpfEditorView = "slide" | "sorter" | "outline" | "layouts";
18
32
  export type OpfEditorDensity = "comfortable" | "touch";
19
33
 
@@ -48,6 +62,17 @@ export declare const THEME_TOKENS: Readonly<{
48
62
  controlHeight: "--opf-ui-control-h";
49
63
  shadow: "--opf-ui-shadow-pop";
50
64
  motion: "--opf-ui-ease";
65
+ barBackground: "--opf-ui-bar-bg";
66
+ barText: "--opf-ui-bar-fg";
67
+ barBorder: "--opf-ui-bar-border";
68
+ barGap: "--opf-ui-bar-gap";
69
+ barPadding: "--opf-ui-bar-padding";
70
+ barHeight: "--opf-ui-bar-height";
71
+ titleFont: "--opf-ui-title-font";
72
+ titleSize: "--opf-ui-title-size";
73
+ titleWeight: "--opf-ui-title-weight";
74
+ titleText: "--opf-ui-title-fg";
75
+ overlayZ: "--opf-ui-z-overlay";
51
76
  }>;
52
77
  export type ThemeTokenName = keyof typeof THEME_TOKENS;
53
78
  /** Token values by short name or by full custom-property name: `{ accent: "#0a7", "--opf-ui-border": "#ddd" }`. */
@@ -145,6 +170,23 @@ export interface OpfEditorSlots {
145
170
  overlay?: HTMLElement;
146
171
  /** CE-20, the Studio: an element of the host placed at the end of the Studio's bar (Share, avatars). */
147
172
  topbarEnd?: HTMLElement;
173
+ /**
174
+ * CE-28, the toolbar's items in the host's own elements (mode B): the deck title, the Undo and Redo buttons, the slide counter (a second
175
+ * navigator on the one slide index), the view switch and the expand button. An item given here is left out of the bar. Slots that name only
176
+ * these (and `toolbar`) keep the built layout; `export`, `present` and `autosave` above are their items' slots too.
177
+ */
178
+ title?: HTMLElement;
179
+ history?: HTMLElement;
180
+ counter?: HTMLElement;
181
+ views?: HTMLElement;
182
+ expand?: HTMLElement;
183
+ /** CE-28 wave 2: the Design quick menu (the `design` item; `design` is the side panel's slot), the review pill (`review` item), the help button and the host's actions. */
184
+ designMenu?: HTMLElement;
185
+ reviewPill?: HTMLElement;
186
+ help?: HTMLElement;
187
+ actions?: HTMLElement;
188
+ /** CE-28: the element the toolbar is built in, instead of the default layout's bar (with your own `slots` arrangement, the only place it goes). */
189
+ toolbar?: HTMLElement;
148
190
  }
149
191
 
150
192
  /** CE-20: one item of the Studio's selection. `key` is the scene key (the id path, `/slides/@intro/blocks/@kpi`; a layout region `<slide key>#<name>`). */
@@ -232,6 +274,15 @@ export interface OpfEditorSourceOptions {
232
274
  onDownload?(file: SourceFile): void | Promise<void>;
233
275
  }
234
276
 
277
+ /** CE-30: `variables` as an object: the canvas draws `{{variables}}` resolved, as with `"resolved"`. */
278
+ export interface OpfEditorVariablesOptions {
279
+ /**
280
+ * Called when a press on the slide lands on text that has no field of its own (a value the deck generates), in place of focusing the editor's
281
+ * source pane: focus your own code editor. A throw is reported as an `error` event.
282
+ */
283
+ focusSource?(): void;
284
+ }
285
+
235
286
  export interface OpfEditorOptions {
236
287
  /** The document to edit: an OPF object, or its JSON text (then the pane keeps its spelling). Creates the session. */
237
288
  document?: unknown;
@@ -247,11 +298,16 @@ export interface OpfEditorOptions {
247
298
  imageResolver?: ToSvgOptions["imageResolver"];
248
299
  /** Stores an uploaded image yourself and returns its reference (see `@openpresentation/opf-editor/assets`). Used by the design controls. */
249
300
  onAddAsset?: DesignControlsOptions["onAddAsset"];
250
- /** `"authored"` (default): the canvas shows `{{variables}}` as written. `"resolved"`: filled in, except in a template deck; a press on filled-in text opens its properties form. */
251
- variables?: "authored" | "resolved";
301
+ /**
302
+ * `"authored"` (default): the canvas shows `{{variables}}` as written. `"resolved"`: filled in, except in a template deck; a press on filled-in text
303
+ * opens its properties form, and a press on text without a field of its own (a generated value) focuses the source pane. CE-30: an object is
304
+ * `"resolved"` too, and its `focusSource` is called instead of focusing the source pane: for a host with its own code editor beside the slide.
305
+ */
306
+ variables?: "authored" | "resolved" | OpfEditorVariablesOptions;
252
307
 
253
308
  /**
254
- * `"full"` (default), `"canvas"` or `"source"`: the editor builds the arrangement (ignored when `slots` is given). CE-20: `"studio"` is the
309
+ * `"full"` (default), `"canvas"`, `"embed"` (CE-28) or `"source"`: the editor builds the arrangement (ignored when `slots` names a part; slots for the
310
+ * toolbar's items alone keep it). CE-20: `"studio"` is the
255
311
  * Presentation and Slide Studio and `"playground"` its first-contact preset (a host's `slots` then add parts, such as `topbarEnd`).
256
312
  */
257
313
  layout?: OpfEditorLayout;
@@ -269,6 +325,35 @@ export interface OpfEditorOptions {
269
325
  slideIndex?: number;
270
326
  /** `"contain"` sizes the slide to fit its frame (default in the built layouts); `false` leaves the size to the host. */
271
327
  fit?: "contain" | false;
328
+ /**
329
+ * CE-28: the toolbar's items in order (names, or `{ id, priority, collapsible }`); `false` builds no bar (items given in `slots` still mount).
330
+ * Default: the layout's preset (`full`: title, history, find, views, spacer, autosave, export, present, expand, more; `canvas`: title, history,
331
+ * spacer, expand, more; `embed`: title, history, counter, spacer, expand, present, more; none for `source` and `slots`). Items this editor has
332
+ * nothing for (Export without Export, the views with one view) are left out; `zoom`, `design`, `review`, `actions` and `help` are reserved and
333
+ * left out until their parts ship. The Studio has its own bar and ignores this.
334
+ */
335
+ toolbar?: false | readonly OpfToolbarItem[];
336
+ /**
337
+ * CE-28: the expanded editor (the `expand` item and `editor.expand()`). `target` is the element that fills the window (default the mount
338
+ * element; give a wrapper when your own header with the toolbar items sits outside it), `fullscreen` also asks the browser for full screen.
339
+ * `false` turns it off.
340
+ */
341
+ expand?: false | { target?: HTMLElement; fullscreen?: boolean };
342
+ /**
343
+ * CE-29: the icons of the chrome's parts, by name, in one flat map (see docs/styling.md): `more` (the ⋯ button), `expand` and `collapse`,
344
+ * `valid`, `error`, `warning`, `info`, `checking` and `unavailable` (the review pill), `help` and `close` (the help sheet; `close` is also
345
+ * the Design menu's), `trigger` and `check` (the Design menu), `previous` and `next` (both slide navigators), `add` (the strip's "+"), `present`.
346
+ * An icon is an SVG element, a function returning one, or 24x24 path data; an unknown name throws.
347
+ */
348
+ icons?: OpfIcons<OpfEditorIconName>;
349
+ /** CE-28 wave 2: the host's own buttons in the toolbar (the `actions` item); `editor.setActions` replaces them. Each `enabled()` and `pressed()` is read again after every change, selection and read-only switch. */
350
+ actions?: ToolbarAction[];
351
+ /** CE-28 wave 2: the Design quick menu (the `design` item). `onOpenPanel` adds its "More design options…" row for your own Design panel; the editor's own Design panel wins when it has one. */
352
+ designMenu?: { onOpenPanel?: () => void };
353
+ /** CE-28 wave 2: "+" and the context menu on the thumbnail strip (default true in `full` and `embed`, false elsewhere). */
354
+ slideActions?: boolean;
355
+ /** CE-28 wave 2: the browser asks before the page closes while the editor is `dirty` (`true`), or while the function says so (default false). */
356
+ guardUnload?: boolean | ((editor: OpfEditor) => boolean);
272
357
 
273
358
  source?: OpfEditorSourceOptions;
274
359
  canvas?: Omit<CanvasEditorOptions, "editor" | "fonts" | "renderOptions" | "slideIndex" | "presentation" | "catalogs" | "propertiesContainer">;
@@ -288,9 +373,18 @@ export interface OpfEditorOptions {
288
373
  theme?: OpfEditorTheme;
289
374
  /**
290
375
  * Text for the default layouts (`review`, `design`, `find`, `undo`, `redo`, `slideView`, `inspector`) and the accessible names of the slide row's
291
- * two landmarks (`navigator`, default "Slide navigation"; `thumbnails`, default "Slides").
376
+ * two landmarks (`navigator`, default "Slide navigation"; `thumbnails`, default "Slides"). CE-28, the toolbar: `title` (the name of the deck
377
+ * title field, default "Presentation name"), `untitled` (its placeholder, default "Untitled presentation"), `expand` and `collapse` (the expand
378
+ * button, default "Expand editor" and "Exit expanded editor"), `more` (the ⋯ button, default "More"), `moreMenu` (its menu, default "More
379
+ * actions") and `counter` (the toolbar counter's landmark, default "Slide navigation").
292
380
  */
293
- labels?: Partial<Record<"review" | "design" | "find" | "undo" | "redo" | "slideView" | "inspector" | "navigator" | "thumbnails", string>>;
381
+ labels?: Partial<Record<"review" | "design" | "find" | "undo" | "redo" | "slideView" | "inspector" | "navigator" | "thumbnails" | OpfEditorChromeLabel, string>> & {
382
+ /** CE-28 wave 2: the labels of the Design quick menu, the review pill, the help sheet and the actions group. */
383
+ designMenu?: DesignMenuLabels;
384
+ reviewPill?: ReviewPillLabels;
385
+ help?: HelpButtonLabels;
386
+ actions?: ToolbarActionsLabels;
387
+ };
294
388
  /**
295
389
  * Which slide stays on show when the document changes outside the canvas: `"id"` (default) follows the slide by its id (a reorder in the source
296
390
  * pane keeps it), `"position"` keeps the position and clamps it to the deck.
@@ -309,6 +403,9 @@ export interface OpfEditorOptions {
309
403
  on?: { [Name in keyof OpfEditorEvents]?: (detail: OpfEditorEvents[Name]) => void };
310
404
  }
311
405
 
406
+ /** CE-28: the labels of the toolbar's parts. */
407
+ export type OpfEditorChromeLabel = "title" | "untitled" | "expand" | "collapse" | "more" | "moreMenu" | "counter" | "exportItem";
408
+
312
409
  export interface OpfEditorSourceState {
313
410
  /** `valid`, `invalid` (the document has errors) or `syntax` (the text does not read as a document in `format`). */
314
411
  status: { kind: "valid"; warnings: number } | { kind: "invalid"; errors: number; warnings: number } | { kind: "syntax"; format: SourceFormat; message: string };
@@ -369,6 +466,16 @@ export interface OpfEditorEvents {
369
466
  "review-goto": { finding: ReviewFinding; target: unknown };
370
467
  "review-focus": { fix: unknown; target: unknown };
371
468
  panel: { panel: "review" | "design" };
469
+ /** CE-28: the document's `name` changed, from any source (the title field, the source pane, undo, the host). `""` is no name. */
470
+ deckName: { name: string; previous: string };
471
+ /** CE-28: the editor was expanded to fill the window, or returned (the button, `expand()`, Escape, the browser leaving full screen). */
472
+ expand: { expanded: boolean };
473
+ /** CE-28 wave 2: the document, or a source draft, now differs from what was last loaded or saved (`true`), or no longer does. */
474
+ dirty: { dirty: boolean };
475
+ /** CE-28 wave 2: Ctrl/Cmd+S inside the editor (or `editor.save()`): save this. `text` is the source pane's text, a draft too (`draft`). Call `markSaved()` once saved. */
476
+ save: { document: unknown; text: string; format: SourceFormat; draft: boolean };
477
+ /** CE-28 wave 2: the review pill was pressed in a layout without a Review panel (the embed): show the findings your own way. */
478
+ "review-open": { counts: ReviewPillCounts | null; findings: ReviewFinding[] };
372
479
  destroy: Record<string, never>;
373
480
  }
374
481
 
@@ -387,7 +494,7 @@ export interface OpfEditorSetDocumentOptions {
387
494
  * The handle of a mounted editor. It is returned at once, before the parts have mounted (they wait for the fonts and, in a layout that shows
388
495
  * slides, for the slide chunk), and every method works from the first moment: a setting (`setDensity`, `setExportMode`, `setView`, `setSlide`,
389
496
  * `setReadOnly`, `setTheme`, `setName`) is kept and read when the parts mount, and a call that acts on a part (`select`, `goTo`, `showPanel`,
390
- * `focus`, `openFind`, `present`, and the Studio's `setLayout`, `setZoom`, `fit`, `openLayout` and `commands.register`) is queued and runs, in
497
+ * `focus`, `openFind`, `present`, CE-28's `setDeckName` and `expand`, and the Studio's `setLayout`, `setZoom`, `fit`, `openLayout` and `commands.register`) is queued and runs, in
391
498
  * order, as soon as the editor is mounted, before `mounted` is emitted; a later call to the same method replaces an earlier one. A queued call
392
499
  * returns `true` (accepted) or, where it needs a part the editor does not have, `false`. Only `setDocument` and `setSourceText`, whose result
393
500
  * depends on the document, throw before `ready`.
@@ -417,6 +524,35 @@ export interface OpfEditor {
417
524
  readonly selection: { path: string; value: unknown } | OpfStudioSelectionItem[] | null;
418
525
  /** CE-20: the arrangement; `setLayout` switches the Studio between "studio" and "playground". */
419
526
  readonly layout: OpfEditorLayout | "slots";
527
+ /** CE-28: the toolbar the editor built (undefined without one: `toolbar: false`, `source`, `slots` without `slots.toolbar`, the Studio). */
528
+ readonly toolbar: Toolbar | undefined;
529
+ /** CE-28: the deck title part (the `title` item), once mounted. */
530
+ readonly deckTitle: DeckTitle | undefined;
531
+ /** CE-28 wave 2: the toolbar's other parts once mounted. A host that runs its own Review (the playground) feeds `reviewPill.update(counts)`. */
532
+ readonly designMenu: DesignMenu | undefined;
533
+ readonly reviewPill: ReviewPill | undefined;
534
+ readonly helpButton: HelpButton | undefined;
535
+ readonly toolbarActions: ToolbarActions | undefined;
536
+ /** CE-28: the deck's `name` (`""` without one). Not the source pane's file name (`getName`). */
537
+ getDeckName(): string;
538
+ /**
539
+ * CE-28: rename the deck: one undoable, validated edit through the session (the text is trimmed; an empty name removes `name`). An open canvas
540
+ * edit is committed first. False while read-only, while the source text is not a valid deck, or when the edit was refused; queued (true) before mount.
541
+ */
542
+ setDeckName(name: string): boolean;
543
+ /** CE-28: expand the editor to fill the window (`false` returns it). Returns the resulting state; queued (true) before mount; false with `expand: false` and in the Studio. */
544
+ expand(on?: boolean): boolean;
545
+ readonly expanded: boolean;
546
+ /** CE-28 wave 2: the document, or a source draft that is not a valid deck, differs from what was last loaded (`setDocument`, `setSourceText`) or saved. */
547
+ readonly dirty: boolean;
548
+ /** CE-28 wave 2: the host saved the document: it becomes the baseline of `dirty` (and autosave records it, with `persistence`). */
549
+ markSaved(): boolean;
550
+ /** CE-28 wave 2: emit `save` now, as Ctrl/Cmd+S does. False before mount. */
551
+ save(): boolean;
552
+ /** CE-28 wave 2: replace the host's toolbar actions. */
553
+ setActions(actions: ToolbarAction[]): void;
554
+ /** CE-28 wave 2: the keyboard shortcuts this editor binds now (what the help sheet lists; in the Studio, its commands and the chrome's keys). */
555
+ shortcuts(): HelpShortcut[];
420
556
  setLayout(layout: "studio" | "playground"): boolean;
421
557
  /** CE-20: the Studio's zoom (1 is the slide fitted), `setZoom` and `fit` ("slide" or "width"). */
422
558
  readonly zoom: number;