pptx-vanilla-viewer 0.2.0 → 0.3.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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { n, o, m, P, a, e, f, X, p, q, r, s, t, u, v, w, x, y, z, A, B, D, E, F, G, H, J, K, L, i, j, l, k, g, h } from './presentation-BfnrtJV1.js';
1
+ import { n, o, m, P, a, e, f, X, p, q, r, s, t, u, v, w, x, y, z, A, B, D, E, F, G, H, J, K, L, i, j, l, k, g, h, a0 } from './presentation-BfnrtJV1.js';
2
2
  export { a as PptxElement, P as PptxSlide } from './presentation-BfnrtJV1.js';
3
3
  import { ViewerTheme } from './theme/index.js';
4
4
  export { ViewerTheme, ViewerThemeColors, defaultCssVars, defaultRadius, defaultThemeColors, themeToCssVars, vermilionDarkTheme, vermilionLightTheme } from './theme/index.js';
@@ -1431,6 +1431,35 @@ interface CollaborationConfig {
1431
1431
  /** A neutral CSS map (framework `CSSProperties` are structurally compatible). */
1432
1432
  type CssStyleMap = Record<string, string | number>;
1433
1433
 
1434
+ /**
1435
+ * Pure geometry helpers for the align / distribute editor operations.
1436
+ *
1437
+ * These functions operate over a list of slide elements (anything carrying the
1438
+ * `{ id, x, y, width, height }` bounding-box fields of {@link PptxElement}) and
1439
+ * return a `Map` keyed by element `id` describing the *new* position(s) for the
1440
+ * elements that need to move. Elements that already sit on the target edge (or
1441
+ * the two outer-most elements during distribution) are still included with
1442
+ * their unchanged coordinate so callers can apply the whole map uniformly — the
1443
+ * map only ever contains the axis that the operation touches.
1444
+ *
1445
+ * The helpers are deliberately framework-agnostic: no DOM, no Vue reactivity,
1446
+ * no side effects. The host wires them into the editor by feeding the current
1447
+ * selection in and applying the returned `Map` via its element-transform
1448
+ * operation (one batched history entry per call).
1449
+ */
1450
+ /** Edge / centre that {@link alignElements} can snap a selection to. */
1451
+ type AlignEdge = 'left' | 'centerH' | 'right' | 'top' | 'middle' | 'bottom';
1452
+
1453
+ /**
1454
+ * "Change Case" text mutation (PowerPoint's Aa dropdown: Sentence case, lower,
1455
+ * UPPER, Capitalize Each Word, tOGGLE cASE). Unlike `textCaps` (a purely
1456
+ * visual `text-transform`-style render hint), these modes rewrite the actual
1457
+ * characters, matching PowerPoint's own behaviour. Framework-agnostic; no
1458
+ * framework imports.
1459
+ */
1460
+
1461
+ type ChangeCaseMode = 'sentence' | 'lower' | 'upper' | 'capitalize' | 'toggle';
1462
+
1434
1463
  /**
1435
1464
  * collaboration-presence.ts: Pure, framework-agnostic logic for the real-time
1436
1465
  * collaboration subsystem (Yjs-backed presence + remote cursors).
@@ -1455,6 +1484,29 @@ type CssStyleMap = Record<string, string | number>;
1455
1484
 
1456
1485
  /** Connection lifecycle states for the Yjs WebSocket provider. */
1457
1486
  type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error';
1487
+ /** In-memory clipboard payload stored when an element is copied or cut. */
1488
+ interface ElementClipboardPayload {
1489
+ element: a;
1490
+ isTemplate: boolean;
1491
+ }
1492
+
1493
+ /**
1494
+ * shape-preset-catalog.ts: the Insert > Shape picker catalogue shared by every
1495
+ * binding's toolbar/inspector.
1496
+ *
1497
+ * Pure data: each entry carries the preset geometry `type` (OOXML `a:prstGeom`
1498
+ * value the editor can insert), an English fallback `label`, the shared-i18n
1499
+ * `i18nKey`, and a framework-neutral icon descriptor ({@link ShapePresetGlyph}
1500
+ * name + optional utility-class modifier for rotation/skew). Each binding maps
1501
+ * the glyph name onto its own icon component/SVG.
1502
+ *
1503
+ * Order matters: bindings surface the first 12 entries as the quick "top
1504
+ * shapes" row, so new presets should be appended, not inserted.
1505
+ *
1506
+ * @module render/shape-preset-catalog
1507
+ */
1508
+ /** Shape preset geometry types offered by the insert picker. */
1509
+ type ShapePresetType = 'rect' | 'roundRect' | 'ellipse' | 'cylinder' | 'rtArrow' | 'leftArrow' | 'upArrow' | 'downArrow' | 'triangle' | 'rtTriangle' | 'diamond' | 'parallelogram' | 'trapezoid' | 'pentagon' | 'hexagon' | 'octagon' | 'chevron' | 'star5' | 'star6' | 'star8' | 'plus' | 'heart' | 'cloud' | 'sun' | 'moon' | 'pie' | 'plaque' | 'teardrop' | 'line' | 'connector';
1458
1510
  interface AutosaveRecord {
1459
1511
  key: string;
1460
1512
  data: Uint8Array;
@@ -1506,29 +1558,6 @@ interface PrintSettings {
1506
1558
  customRangeTo: number;
1507
1559
  }
1508
1560
 
1509
- /**
1510
- * Minimal i18n for the vanilla binding.
1511
- *
1512
- * The other bindings delegate to their framework's i18n runtime (react-i18next,
1513
- * vue-i18n, ngx-translate) fed by the shared `pptx.*` dictionary. The vanilla
1514
- * binding has no framework, so this module provides the one missing piece: a
1515
- * `t(key, params)` lookup with `{{param}}` interpolation over the same shared
1516
- * English dictionary, plus per-locale overrides supplied by the host.
1517
- *
1518
- * Resolution order: host dictionary for the active locale, then the built-in
1519
- * English dictionary, then a humanised fallback derived from the key (shared
1520
- * `keyToLabel`), so a missing key never renders as a raw `pptx.*` string.
1521
- */
1522
- /** Translate a dotted `pptx.*` key with optional `{{param}}` interpolation. */
1523
- type Translator = (key: string, params?: Record<string, string | number>) => string;
1524
- /** Locale to flat `key: message` dictionary map supplied by the host. */
1525
- type TranslationMessages = Record<string, Record<string, string>>;
1526
- /**
1527
- * Build a {@link Translator} for a locale. `messages[locale]` (when provided)
1528
- * wins over the built-in English dictionary; English is always the fallback.
1529
- */
1530
- declare function createTranslator(locale?: string, messages?: TranslationMessages): Translator;
1531
-
1532
1561
  /**
1533
1562
  * A tiny framework-free reactive store: `get` / `set(patch)` / `subscribe`.
1534
1563
  *
@@ -1587,6 +1616,51 @@ interface ViewerState {
1587
1616
  * navigation for the life of the viewer instance (in-memory only).
1588
1617
  */
1589
1618
  notesExpanded: boolean;
1619
+ /** In-memory clipboard payload from the last copy/cut, or null. */
1620
+ clipboardPayload: ElementClipboardPayload | null;
1621
+ }
1622
+
1623
+ /**
1624
+ * Z-order, align, flip, and group/ungroup actions for the ribbon's
1625
+ * Home > Arrange group.
1626
+ *
1627
+ * Align/distribute: the vanilla editor's selection model is single-element
1628
+ * only (see `state/viewer-state.ts`), so `alignElements` reaches the
1629
+ * single-selection "align to slide" mode ({@link alignToCanvas}), matching
1630
+ * PowerPoint's own behaviour when nothing else is selected. Multi-selection
1631
+ * "align to selection bounding box" / distribute (needing >= 2 / >= 3
1632
+ * elements, see `editor-arrange-mutations.ts`) is unreachable until a
1633
+ * multi-select model lands; `canDistribute` is therefore always `false` today
1634
+ * (see `editing-chrome-sync.ts`), a documented limitation rather than a bug.
1635
+ *
1636
+ * Group: needs >= 2 selected elements to be meaningful, so it too is
1637
+ * unreachable under single selection; `groupSelected` is a deliberate no-op
1638
+ * kept only so the ribbon button (permanently disabled, with an explanatory
1639
+ * title) has something to call. Ungroup works today: it applies to whichever
1640
+ * single `group` element is selected.
1641
+ */
1642
+ interface ArrangeActions {
1643
+ bringForward(): void;
1644
+ sendBackward(): void;
1645
+ bringToFront(): void;
1646
+ sendToBack(): void;
1647
+ alignElements(edge: AlignEdge): void;
1648
+ flipHorizontal(): void;
1649
+ flipVertical(): void;
1650
+ groupSelected(): void;
1651
+ ungroupSelected(): void;
1652
+ }
1653
+
1654
+ /**
1655
+ * Cut/copy/paste actions for the ribbon's Home > Clipboard group, backed by
1656
+ * the shared `element-clipboard.ts` codec. The in-memory clipboard payload
1657
+ * lives on `ViewerState.clipboardPayload` (not a module-level variable) so
1658
+ * the ribbon's selection sync can reactively enable/disable the Paste button.
1659
+ */
1660
+ interface ClipboardActions {
1661
+ copy(): void;
1662
+ cut(): void;
1663
+ paste(): void;
1590
1664
  }
1591
1665
 
1592
1666
  /**
@@ -1599,29 +1673,49 @@ interface ViewerState {
1599
1673
  * side effects needed to turn a chosen image file into a data-URL image
1600
1674
  * element; it too returns a centred, ready-to-insert element.
1601
1675
  */
1602
- /** The shape/text/table element kinds the insert menu can create directly. */
1603
- type InsertKind = 'text' | 'rect' | 'ellipse' | 'line' | 'table';
1676
+ /** The element kinds the Insert ribbon tab can create directly. */
1677
+ type InsertKind = 'text' | 'table' | 'shape';
1604
1678
 
1605
- /** The selection-derived state the toolbar reflects (enable/disable + values). */
1606
- interface FormatSelectionState {
1607
- hasSelection: boolean;
1608
- canText: boolean;
1609
- canShape: boolean;
1610
- bold: boolean;
1611
- italic: boolean;
1612
- underline: boolean;
1613
- fontSize: number;
1614
- textColor: string | undefined;
1615
- highlightColor: string | undefined;
1616
- fillColor: string | undefined;
1617
- strokeColor: string | undefined;
1679
+ /**
1680
+ * New/duplicate/delete-slide actions for the ribbon's Home > Slides group,
1681
+ * backed by the shared `slide-operations.ts` factory. Every mutation is
1682
+ * history-integrated (matches the element-level actions in `editor-edit-ops`)
1683
+ * and renumbers `slideNumber` across the whole deck so it always matches the
1684
+ * array index (mirrors the Vue `useSlideOperations` contract).
1685
+ */
1686
+ interface SlideActions {
1687
+ addSlide(): void;
1688
+ duplicateSlide(): void;
1689
+ deleteSlide(): void;
1618
1690
  }
1619
- interface FormatToolbar {
1620
- el: HTMLElement;
1621
- /** Reflect the current selection (enable/disable controls + seed values). */
1622
- update(state: FormatSelectionState): void;
1623
- /** Show/hide the whole row (editing mode on/off). */
1624
- setEditable(editable: boolean): void;
1691
+
1692
+ /**
1693
+ * Character + paragraph formatting actions for the ribbon's Home > Font and
1694
+ * Home > Paragraph groups. Every method is a thin `applyToSelected` wrapper
1695
+ * around the pure builders in `editor-format-mutations.ts` /
1696
+ * `editor-paragraph-mutations.ts`; this file owns none of the mutation logic
1697
+ * itself, only the selection/history wiring shared by the whole action set.
1698
+ */
1699
+ interface TextActions {
1700
+ toggleBold(): void;
1701
+ toggleItalic(): void;
1702
+ toggleUnderline(): void;
1703
+ toggleStrikethrough(): void;
1704
+ toggleTextShadow(): void;
1705
+ changeFontSize(delta: number): void;
1706
+ setFontSize(size: number): void;
1707
+ setFontFamily(family: string): void;
1708
+ setTextColor(color: string): void;
1709
+ setHighlightColor(color: string): void;
1710
+ setCharacterSpacing(value: number): void;
1711
+ changeCase(mode: ChangeCaseMode): void;
1712
+ clearFormatting(): void;
1713
+ toggleBulletList(): void;
1714
+ toggleNumberedList(): void;
1715
+ increaseIndent(): void;
1716
+ decreaseIndent(): void;
1717
+ setTextAlign(align: a0['align']): void;
1718
+ setLineSpacing(value: number): void;
1625
1719
  }
1626
1720
 
1627
1721
  /** A geometry patch from the inspector (all fields optional). */
@@ -1633,31 +1727,67 @@ interface GeometryPatch {
1633
1727
  rotation?: number;
1634
1728
  }
1635
1729
  /**
1636
- * The formatting / insert / arrange actions exposed to the editing chrome
1637
- * (format toolbar, inspector, insert menu). Every mutating action is
1638
- * history-integrated (push -> mutate -> commit) via the shared {@link EditorOps}.
1730
+ * The full set of formatting / insert / arrange / clipboard / slide actions
1731
+ * exposed to the editing chrome (ribbon, inspector). Composed from the
1732
+ * focused per-concern action files (`editor-text-actions.ts`,
1733
+ * `editor-arrange-actions.ts`, `editor-clipboard-actions.ts`,
1734
+ * `editor-slide-actions.ts`) plus the shape/geometry/insert actions owned
1735
+ * directly here. Every mutating action is history-integrated (push -> mutate
1736
+ * -> commit) via the shared {@link EditorOps}.
1639
1737
  */
1640
- interface EditActions {
1641
- toggleBold(): void;
1642
- toggleItalic(): void;
1643
- toggleUnderline(): void;
1644
- changeFontSize(delta: number): void;
1645
- setFontSize(size: number): void;
1646
- setTextColor(color: string): void;
1647
- setHighlightColor(color: string): void;
1738
+ interface EditActions extends TextActions, ArrangeActions, ClipboardActions, SlideActions {
1648
1739
  setShapeFill(color: string): void;
1649
1740
  setShapeStroke(color: string): void;
1650
1741
  setShapeStrokeWidth(width: number): void;
1651
1742
  /** Commit an inspector geometry edit (X/Y/W/H/rotation). */
1652
1743
  setGeometry(patch: GeometryPatch): void;
1653
- insert(kind: InsertKind): void;
1744
+ insert(kind: InsertKind, shapeType?: ShapePresetType): void;
1654
1745
  insertImage(): Promise<void>;
1655
- bringForward(): void;
1656
- sendBackward(): void;
1657
- bringToFront(): void;
1658
- sendToBack(): void;
1746
+ duplicateSelected(): void;
1747
+ deleteSelected(): void;
1748
+ }
1749
+
1750
+ /**
1751
+ * Find & Replace actions for the ribbon's Home > Editing group, backed by the
1752
+ * shared `find-replace.ts` helpers. A lean, docked-panel implementation:
1753
+ * `search` reports a match count for the query, `replaceCurrent` replaces the
1754
+ * first match, and `replaceAll` replaces every match; there is no in-canvas
1755
+ * match highlighting or "next/previous match" cursor (that needs per-match
1756
+ * selection/scroll wiring into the renderer, out of scope for this wave).
1757
+ * Every replace is history-integrated like every other editor action.
1758
+ */
1759
+ interface FindReplaceActions {
1760
+ /** Count occurrences of `query` across all slides (does not mutate). */
1761
+ search(query: string, matchCase: boolean): number;
1762
+ /** Replace the first match of `query`; returns the number replaced (0 or 1). */
1763
+ replaceCurrent(query: string, replacement: string, matchCase: boolean): number;
1764
+ /** Replace every match of `query`; returns the number replaced. */
1765
+ replaceAll(query: string, replacement: string, matchCase: boolean): number;
1659
1766
  }
1660
1767
 
1768
+ /**
1769
+ * Minimal i18n for the vanilla binding.
1770
+ *
1771
+ * The other bindings delegate to their framework's i18n runtime (react-i18next,
1772
+ * vue-i18n, ngx-translate) fed by the shared `pptx.*` dictionary. The vanilla
1773
+ * binding has no framework, so this module provides the one missing piece: a
1774
+ * `t(key, params)` lookup with `{{param}}` interpolation over the same shared
1775
+ * English dictionary, plus per-locale overrides supplied by the host.
1776
+ *
1777
+ * Resolution order: host dictionary for the active locale, then the built-in
1778
+ * English dictionary, then a humanised fallback derived from the key (shared
1779
+ * `keyToLabel`), so a missing key never renders as a raw `pptx.*` string.
1780
+ */
1781
+ /** Translate a dotted `pptx.*` key with optional `{{param}}` interpolation. */
1782
+ type Translator = (key: string, params?: Record<string, string | number>) => string;
1783
+ /** Locale to flat `key: message` dictionary map supplied by the host. */
1784
+ type TranslationMessages = Record<string, Record<string, string>>;
1785
+ /**
1786
+ * Build a {@link Translator} for a locale. `messages[locale]` (when provided)
1787
+ * wins over the built-in English dictionary; English is always the fallback.
1788
+ */
1789
+ declare function createTranslator(locale?: string, messages?: TranslationMessages): Translator;
1790
+
1661
1791
  /** Selection-derived state the inspector reflects. */
1662
1792
  interface InspectorState {
1663
1793
  hasSelection: boolean;
@@ -1694,52 +1824,52 @@ interface NotesPanel {
1694
1824
  setExpanded(expanded: boolean): void;
1695
1825
  }
1696
1826
 
1697
- interface ThumbnailRail {
1698
- el: HTMLElement;
1699
- /** Rebuild the rail for a new slide list (uses `renderStage` per slide). */
1700
- render(slides: P[], canvasSize: CanvasSize, renderStage: (slide: P, scale: number) => HTMLElement): void;
1701
- /** Highlight the active slide and scroll it into view. */
1702
- setActive(index: number): void;
1703
- /** Show or hide the rail. */
1704
- setVisible(visible: boolean): void;
1705
- }
1706
-
1707
- interface ToolbarUpdate {
1708
- /** Zero-based current slide index. */
1827
+ /** Nav-row state (prev/next/counter/zoom label). */
1828
+ interface RibbonNavState {
1709
1829
  current: number;
1710
- /** Total slide count. */
1711
1830
  total: number;
1712
- /** Effective zoom percentage (100 = 1:1). */
1713
1831
  zoomPercent: number;
1714
1832
  }
1715
- /** Editing-cluster state (Save / Undo / Redo buttons). */
1716
- interface ToolbarEditState {
1717
- /** Shows/hides the whole editing cluster. */
1833
+ /** Primary-row + tab-bar visibility state. */
1834
+ interface RibbonEditState {
1718
1835
  editable: boolean;
1719
1836
  canUndo: boolean;
1720
1837
  canRedo: boolean;
1721
1838
  }
1722
- interface Toolbar {
1839
+ /** Selection-derived state the Home tab's Font/Paragraph/Arrange groups reflect. */
1840
+ interface RibbonSelectionState {
1841
+ hasClipboard: boolean;
1842
+ slideCount: number;
1843
+ }
1844
+
1845
+ interface Ribbon {
1723
1846
  el: HTMLElement;
1724
- update(state: ToolbarUpdate): void;
1725
- setEditState(state: ToolbarEditState): void;
1726
- /** Reflect the notes panel's expanded/collapsed state on the Notes button. */
1847
+ update(state: RibbonNavState): void;
1848
+ setEditState(state: RibbonEditState): void;
1727
1849
  setNotesExpanded(expanded: boolean): void;
1728
- /**
1729
- * Show the autosave status pill. `label` is the (already localized) text;
1730
- * `kind` drives the styling hook (`is-saving` / `is-error`). An empty label
1731
- * hides the pill.
1732
- */
1733
1850
  setAutosaveStatus(label: string, kind: 'idle' | 'saving' | 'saved' | 'error'): void;
1851
+ /** Show/hide the whole editing surface (Home/Insert tab content + find/replace). */
1852
+ setEditable(editable: boolean): void;
1853
+ /** Reflect the current selection across the Home tab's Font/Paragraph/Arrange groups. */
1854
+ updateSelection(selectedElement: a | undefined, extra: RibbonSelectionState): void;
1855
+ }
1856
+
1857
+ interface ThumbnailRail {
1858
+ el: HTMLElement;
1859
+ /** Rebuild the rail for a new slide list (uses `renderStage` per slide). */
1860
+ render(slides: P[], canvasSize: CanvasSize, renderStage: (slide: P, scale: number) => HTMLElement): void;
1861
+ /** Highlight the active slide and scroll it into view. */
1862
+ setActive(index: number): void;
1863
+ /** Show or hide the rail. */
1864
+ setVisible(visible: boolean): void;
1734
1865
  }
1735
1866
 
1736
1867
  /** The viewer's static DOM skeleton plus the mutable overlay controls. */
1737
1868
  interface ViewerChrome {
1738
1869
  /** `.pptxv` root (focusable; keyboard navigation attaches here). */
1739
1870
  root: HTMLElement;
1740
- toolbar: Toolbar | null;
1741
- /** Editing format toolbar (bold/fill/insert/z-order); null when disabled. */
1742
- formatToolbar: FormatToolbar | null;
1871
+ /** The tabbed ribbon (primary row, nav row, tab bar + File/Home/Insert/View content); null when disabled. */
1872
+ ribbon: Ribbon | null;
1743
1873
  /** Property inspector panel; null when disabled. */
1744
1874
  inspector: Inspector | null;
1745
1875
  thumbnails: ThumbnailRail | null;
@@ -1786,6 +1916,8 @@ interface EditorController {
1786
1916
  getSelectedElementId(): string | null;
1787
1917
  /** The formatting / insert / arrange actions for the editing chrome. */
1788
1918
  getEditActions(): EditActions;
1919
+ /** The Find & Replace actions for the ribbon's docked panel. */
1920
+ getFindReplaceActions(): FindReplaceActions;
1789
1921
  /** Commit the speaker-notes textarea's plain text onto the current slide. */
1790
1922
  commitNotes(notes: string): void;
1791
1923
  save(): Promise<Uint8Array>;
@@ -2367,6 +2499,7 @@ interface ChromeHost {
2367
2499
  editor: {
2368
2500
  commitNotes(notes: string): void;
2369
2501
  getEditActions(): EditActions;
2502
+ getFindReplaceActions(): FindReplaceActions;
2370
2503
  };
2371
2504
  prev(): void;
2372
2505
  next(): void;
@@ -2381,6 +2514,11 @@ interface ChromeHost {
2381
2514
  getSlideCount(): number;
2382
2515
  enterPresentation(): Promise<void>;
2383
2516
  exitPresentation(): Promise<void>;
2517
+ exportSlidePng(): Promise<void>;
2518
+ exportPdf(): Promise<void>;
2519
+ exportGif(): Promise<void>;
2520
+ exportVideo(): Promise<void>;
2521
+ print(): Promise<boolean>;
2384
2522
  }
2385
2523
 
2386
2524
  /** The export slice of the public viewer API (see `PptxViewerInstance`). */