pptx-vanilla-viewer 0.1.3 → 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/CHANGELOG.md +12 -0
- package/dist/index.cjs +921 -82
- package/dist/index.d.cts +762 -132
- package/dist/index.d.ts +762 -132
- package/dist/index.js +921 -82
- package/package.json +10 -2
package/dist/index.d.cts
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';
|
|
@@ -1368,9 +1368,403 @@ interface CanvasSize {
|
|
|
1368
1368
|
width: number;
|
|
1369
1369
|
height: number;
|
|
1370
1370
|
}
|
|
1371
|
+
/** Collaboration role within a session. */
|
|
1372
|
+
type CollaborationRole = 'owner' | 'collaborator' | 'viewer';
|
|
1373
|
+
/**
|
|
1374
|
+
* Collaboration transport.
|
|
1375
|
+
*
|
|
1376
|
+
* - `'websocket'` (default): y-websocket against `serverUrl`.
|
|
1377
|
+
* - `'webrtc'`: y-webrtc peer-to-peer; needs no document server. Peers meet
|
|
1378
|
+
* through the `signaling` servers (WebRTC signaling only, no document data)
|
|
1379
|
+
* and same-browser tabs additionally sync via BroadcastChannel even without
|
|
1380
|
+
* any signaling server, which makes this mode usable from static hosting.
|
|
1381
|
+
*/
|
|
1382
|
+
type CollaborationTransport = 'websocket' | 'webrtc';
|
|
1383
|
+
/**
|
|
1384
|
+
* Real-time collaboration configuration.
|
|
1385
|
+
*
|
|
1386
|
+
* The same shape is accepted by the React, Vue, and Angular bindings.
|
|
1387
|
+
*/
|
|
1388
|
+
interface CollaborationConfig {
|
|
1389
|
+
/** Unique identifier for the collaboration room (alphanumeric, hyphens, underscores). */
|
|
1390
|
+
roomId: string;
|
|
1391
|
+
/**
|
|
1392
|
+
* WebSocket server URL for the Yjs provider (e.g. "wss://collab.example.com").
|
|
1393
|
+
* Ignored (may be empty) when `transport` is `'webrtc'`.
|
|
1394
|
+
*/
|
|
1395
|
+
serverUrl: string;
|
|
1396
|
+
/** Transport to use. Defaults to `'websocket'`. */
|
|
1397
|
+
transport?: CollaborationTransport;
|
|
1398
|
+
/**
|
|
1399
|
+
* WebRTC signaling server URLs (only used when `transport` is `'webrtc'`).
|
|
1400
|
+
* Defaults to y-webrtc's built-in public signaling list. Same-browser tabs
|
|
1401
|
+
* sync via BroadcastChannel regardless of signaling availability.
|
|
1402
|
+
*/
|
|
1403
|
+
signaling?: string[];
|
|
1404
|
+
/** Display name for the local user. */
|
|
1405
|
+
userName: string;
|
|
1406
|
+
/** Avatar URL for the local user (optional). */
|
|
1407
|
+
userAvatar?: string;
|
|
1408
|
+
/** Hex colour for the local user's cursor/presence indicator. */
|
|
1409
|
+
userColor?: string;
|
|
1410
|
+
/** Optional authentication token sent with the WebSocket handshake. */
|
|
1411
|
+
authToken?: string;
|
|
1412
|
+
/** Role in the session; defaults to `'collaborator'`. */
|
|
1413
|
+
role?: CollaborationRole;
|
|
1414
|
+
/**
|
|
1415
|
+
* Elected-writer write-back callback (Area 3 of the C3 hardening plan).
|
|
1416
|
+
*
|
|
1417
|
+
* When the local user has `role: 'owner'`, the binding debounces changes and
|
|
1418
|
+
* serializes the current Y.Doc state to a PPTX byte array, then calls this
|
|
1419
|
+
* callback so the host can persist the snapshot. Only one writer (the owner)
|
|
1420
|
+
* does this; other collaborators never trigger write-back, eliminating the
|
|
1421
|
+
* last-save-wins problem.
|
|
1422
|
+
*/
|
|
1423
|
+
onWriteBack?: (bytes: Uint8Array) => void;
|
|
1424
|
+
/**
|
|
1425
|
+
* Debounce delay (ms) between the last Y.Doc change and the write-back
|
|
1426
|
+
* invocation. Defaults to 5000 ms. Set to 0 to write back on every change
|
|
1427
|
+
* (not recommended for large documents).
|
|
1428
|
+
*/
|
|
1429
|
+
writeBackDebounceMs?: number;
|
|
1430
|
+
}
|
|
1371
1431
|
/** A neutral CSS map (framework `CSSProperties` are structurally compatible). */
|
|
1372
1432
|
type CssStyleMap = Record<string, string | number>;
|
|
1373
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
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* collaboration-presence.ts: Pure, framework-agnostic logic for the real-time
|
|
1465
|
+
* collaboration subsystem (Yjs-backed presence + remote cursors).
|
|
1466
|
+
*
|
|
1467
|
+
* Everything here is a plain function with no framework / Yjs dependency, so it
|
|
1468
|
+
* can be unit-tested in isolation and shared across the React, Vue, and Angular
|
|
1469
|
+
* bindings. The bindings own the stateful provider lifecycle (creating the
|
|
1470
|
+
* Y.Doc / WebsocketProvider, wiring awareness listeners) and call into these
|
|
1471
|
+
* helpers to validate config, sanitise inbound awareness data, and project it
|
|
1472
|
+
* into render-ready view-models.
|
|
1473
|
+
*
|
|
1474
|
+
* Responsibilities:
|
|
1475
|
+
* - Validate the room id and detect mixed-content (ws:// from https) up front.
|
|
1476
|
+
* - Sanitise inbound awareness data (XSS, bounds, colour, avatar, room id).
|
|
1477
|
+
* - Map awareness state into a `RemoteCursor` view-model for rendering.
|
|
1478
|
+
* - Derive the presence list (remote users only, stale entries dropped).
|
|
1479
|
+
* - Deterministic per-user colour assignment + cursor label formatting.
|
|
1480
|
+
*
|
|
1481
|
+
* This mirrors the React `sanitize.ts` / `usePresenceTracking.ts` helpers and
|
|
1482
|
+
* the Angular `collaboration-helpers.ts` (which predates this shared copy).
|
|
1483
|
+
*/
|
|
1484
|
+
|
|
1485
|
+
/** Connection lifecycle states for the Yjs WebSocket provider. */
|
|
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';
|
|
1510
|
+
interface AutosaveRecord {
|
|
1511
|
+
key: string;
|
|
1512
|
+
data: Uint8Array;
|
|
1513
|
+
timestamp: number;
|
|
1514
|
+
size: number;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
/**
|
|
1518
|
+
* Pure handout layout calculations, shared by every binding's print path.
|
|
1519
|
+
*
|
|
1520
|
+
* Handles distributing slides across pages, computing grid dimensions, and
|
|
1521
|
+
* positioning cells within A4 page space. No DOM/framework dependency: callers
|
|
1522
|
+
* render the resulting rectangles however their view layer prefers.
|
|
1523
|
+
*/
|
|
1524
|
+
/** Supported slides-per-page values. */
|
|
1525
|
+
type HandoutSlidesPerPage = 1 | 2 | 3 | 4 | 6 | 9;
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* Pure print helpers shared by every binding's print path: settings validation,
|
|
1529
|
+
* slide-range / colour-filter resolution, page-count estimation, HTML markup
|
|
1530
|
+
* builders, escaping, and the full print-document assembler.
|
|
1531
|
+
*
|
|
1532
|
+
* No DOM side effects and no `window.print()`: everything is deterministic and
|
|
1533
|
+
* the binding writes the returned HTML string into a print window. The handout
|
|
1534
|
+
* grid geometry lives in `handout-layout`; this module reuses it.
|
|
1535
|
+
*
|
|
1536
|
+
* ng-packagr constraint honoured here (the Angular binding inlines this source
|
|
1537
|
+
* and compiles it through ng-packagr): NO `String.prototype.replaceAll`
|
|
1538
|
+
* (`escapeHtml` uses `.split(x).join(y)` instead).
|
|
1539
|
+
*/
|
|
1540
|
+
|
|
1541
|
+
/** What to print. */
|
|
1542
|
+
type PrintWhat = 'slides' | 'handouts' | 'notes' | 'outline';
|
|
1543
|
+
/** Page orientation for the printed output. */
|
|
1544
|
+
type PrintOrientation = 'portrait' | 'landscape';
|
|
1545
|
+
/** Colour mode for the printed output. */
|
|
1546
|
+
type PrintColorMode = 'color' | 'grayscale' | 'blackAndWhite';
|
|
1547
|
+
/** Slide range mode. */
|
|
1548
|
+
type PrintSlideRange = 'all' | 'current' | 'custom';
|
|
1549
|
+
/** Resolved print settings emitted on confirm. */
|
|
1550
|
+
interface PrintSettings {
|
|
1551
|
+
printWhat: PrintWhat;
|
|
1552
|
+
orientation: PrintOrientation;
|
|
1553
|
+
colorMode: PrintColorMode;
|
|
1554
|
+
frameSlides: boolean;
|
|
1555
|
+
slidesPerPage: HandoutSlidesPerPage;
|
|
1556
|
+
slideRange: PrintSlideRange;
|
|
1557
|
+
customRangeFrom: number;
|
|
1558
|
+
customRangeTo: number;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/**
|
|
1562
|
+
* A tiny framework-free reactive store: `get` / `set(patch)` / `subscribe`.
|
|
1563
|
+
*
|
|
1564
|
+
* This is deliberately minimal (a few dozen lines, no external deps). It only
|
|
1565
|
+
* carries the vanilla binding's *view state*; all domain logic (parsing,
|
|
1566
|
+
* styles, geometry, text building) stays in `pptx-viewer-core` and
|
|
1567
|
+
* `pptx-viewer-shared`.
|
|
1568
|
+
*/
|
|
1569
|
+
/** Listener invoked after every state change with the next and previous state. */
|
|
1570
|
+
type StoreListener<T> = (state: T, previous: T) => void;
|
|
1571
|
+
interface Store<T extends object> {
|
|
1572
|
+
/** Current state snapshot (do not mutate). */
|
|
1573
|
+
get(): T;
|
|
1574
|
+
/** Shallow-merge a partial patch into the state and notify subscribers. */
|
|
1575
|
+
set(patch: Partial<T>): void;
|
|
1576
|
+
/** Subscribe to state changes; returns an unsubscribe function. */
|
|
1577
|
+
subscribe(listener: StoreListener<T>): () => void;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
/** `zoom` is either an explicit scale factor (1 = 100%) or fit-to-viewport. */
|
|
1581
|
+
type ZoomLevel = number | 'fit';
|
|
1582
|
+
/**
|
|
1583
|
+
* The vanilla viewer's reactive view state. Kept intentionally flat and small;
|
|
1584
|
+
* everything here is what the DOM render layer consumes.
|
|
1585
|
+
*/
|
|
1586
|
+
interface ViewerState {
|
|
1587
|
+
/** Parsed slides (image/media URLs already patched in by the load pipeline). */
|
|
1588
|
+
slides: P[];
|
|
1589
|
+
/** Slide canvas size in CSS pixels. */
|
|
1590
|
+
canvasSize: CanvasSize;
|
|
1591
|
+
/** Archive-path to displayable URL map for media + poster frames. */
|
|
1592
|
+
mediaDataUrls: Map<string, string>;
|
|
1593
|
+
/** Zero-based index of the visible slide. */
|
|
1594
|
+
currentSlide: number;
|
|
1595
|
+
/** Requested zoom (explicit factor or fit-to-viewport). */
|
|
1596
|
+
zoom: ZoomLevel;
|
|
1597
|
+
/** True while a load is in flight. */
|
|
1598
|
+
loading: boolean;
|
|
1599
|
+
/** Error message from the last failed load, or null. */
|
|
1600
|
+
error: string | null;
|
|
1601
|
+
/** True while presentation (fullscreen) mode is active. */
|
|
1602
|
+
presenting: boolean;
|
|
1603
|
+
/** True when editing interactions (select/move/resize/...) are enabled. */
|
|
1604
|
+
editable: boolean;
|
|
1605
|
+
/** Id of the selected element on the current slide, or null. */
|
|
1606
|
+
selectedElementId: string | null;
|
|
1607
|
+
/** True when the document has unsaved edits (cleared by a save). */
|
|
1608
|
+
dirty: boolean;
|
|
1609
|
+
/**
|
|
1610
|
+
* True while a pointer gesture (drag/resize/rotate) is in flight. Thumbnail
|
|
1611
|
+
* re-renders are deferred until the gesture ends.
|
|
1612
|
+
*/
|
|
1613
|
+
interactionActive: boolean;
|
|
1614
|
+
/**
|
|
1615
|
+
* True when the speaker-notes panel body is expanded. Persists across slide
|
|
1616
|
+
* navigation for the life of the viewer instance (in-memory only).
|
|
1617
|
+
*/
|
|
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;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/**
|
|
1667
|
+
* Insert-element factories for the vanilla editor.
|
|
1668
|
+
*
|
|
1669
|
+
* The pure builders wrap the framework-agnostic core/shared factories
|
|
1670
|
+
* (`createTextElement`, `createShapeElement`, `createConnectorElement`,
|
|
1671
|
+
* `newTableElement`) and centre the new element on the slide canvas. The one
|
|
1672
|
+
* async helper (`pickImageElement`) owns the file-picker + `FileReader` DOM
|
|
1673
|
+
* side effects needed to turn a chosen image file into a data-URL image
|
|
1674
|
+
* element; it too returns a centred, ready-to-insert element.
|
|
1675
|
+
*/
|
|
1676
|
+
/** The element kinds the Insert ribbon tab can create directly. */
|
|
1677
|
+
type InsertKind = 'text' | 'table' | 'shape';
|
|
1678
|
+
|
|
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;
|
|
1690
|
+
}
|
|
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;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
/** A geometry patch from the inspector (all fields optional). */
|
|
1722
|
+
interface GeometryPatch {
|
|
1723
|
+
x?: number;
|
|
1724
|
+
y?: number;
|
|
1725
|
+
width?: number;
|
|
1726
|
+
height?: number;
|
|
1727
|
+
rotation?: number;
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
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}.
|
|
1737
|
+
*/
|
|
1738
|
+
interface EditActions extends TextActions, ArrangeActions, ClipboardActions, SlideActions {
|
|
1739
|
+
setShapeFill(color: string): void;
|
|
1740
|
+
setShapeStroke(color: string): void;
|
|
1741
|
+
setShapeStrokeWidth(width: number): void;
|
|
1742
|
+
/** Commit an inspector geometry edit (X/Y/W/H/rotation). */
|
|
1743
|
+
setGeometry(patch: GeometryPatch): void;
|
|
1744
|
+
insert(kind: InsertKind, shapeType?: ShapePresetType): void;
|
|
1745
|
+
insertImage(): Promise<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;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1374
1768
|
/**
|
|
1375
1769
|
* Minimal i18n for the vanilla binding.
|
|
1376
1770
|
*
|
|
@@ -1394,6 +1788,25 @@ type TranslationMessages = Record<string, Record<string, string>>;
|
|
|
1394
1788
|
*/
|
|
1395
1789
|
declare function createTranslator(locale?: string, messages?: TranslationMessages): Translator;
|
|
1396
1790
|
|
|
1791
|
+
/** Selection-derived state the inspector reflects. */
|
|
1792
|
+
interface InspectorState {
|
|
1793
|
+
hasSelection: boolean;
|
|
1794
|
+
canShape: boolean;
|
|
1795
|
+
x: number;
|
|
1796
|
+
y: number;
|
|
1797
|
+
width: number;
|
|
1798
|
+
height: number;
|
|
1799
|
+
rotation: number;
|
|
1800
|
+
fillColor: string | undefined;
|
|
1801
|
+
strokeColor: string | undefined;
|
|
1802
|
+
strokeWidth: number;
|
|
1803
|
+
}
|
|
1804
|
+
interface Inspector {
|
|
1805
|
+
el: HTMLElement;
|
|
1806
|
+
update(state: InspectorState): void;
|
|
1807
|
+
setEditable(editable: boolean): void;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1397
1810
|
/** State the panel needs to reflect: the slide to read notes from, and whether edits are allowed. */
|
|
1398
1811
|
interface NotesPanelUpdate {
|
|
1399
1812
|
slide: P | undefined;
|
|
@@ -1411,44 +1824,54 @@ interface NotesPanel {
|
|
|
1411
1824
|
setExpanded(expanded: boolean): void;
|
|
1412
1825
|
}
|
|
1413
1826
|
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
/** Rebuild the rail for a new slide list (uses `renderStage` per slide). */
|
|
1417
|
-
render(slides: P[], canvasSize: CanvasSize, renderStage: (slide: P, scale: number) => HTMLElement): void;
|
|
1418
|
-
/** Highlight the active slide and scroll it into view. */
|
|
1419
|
-
setActive(index: number): void;
|
|
1420
|
-
/** Show or hide the rail. */
|
|
1421
|
-
setVisible(visible: boolean): void;
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
interface ToolbarUpdate {
|
|
1425
|
-
/** Zero-based current slide index. */
|
|
1827
|
+
/** Nav-row state (prev/next/counter/zoom label). */
|
|
1828
|
+
interface RibbonNavState {
|
|
1426
1829
|
current: number;
|
|
1427
|
-
/** Total slide count. */
|
|
1428
1830
|
total: number;
|
|
1429
|
-
/** Effective zoom percentage (100 = 1:1). */
|
|
1430
1831
|
zoomPercent: number;
|
|
1431
1832
|
}
|
|
1432
|
-
/**
|
|
1433
|
-
interface
|
|
1434
|
-
/** Shows/hides the whole editing cluster. */
|
|
1833
|
+
/** Primary-row + tab-bar visibility state. */
|
|
1834
|
+
interface RibbonEditState {
|
|
1435
1835
|
editable: boolean;
|
|
1436
1836
|
canUndo: boolean;
|
|
1437
1837
|
canRedo: boolean;
|
|
1438
1838
|
}
|
|
1439
|
-
|
|
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 {
|
|
1440
1846
|
el: HTMLElement;
|
|
1441
|
-
update(state:
|
|
1442
|
-
setEditState(state:
|
|
1443
|
-
/** Reflect the notes panel's expanded/collapsed state on the Notes button. */
|
|
1847
|
+
update(state: RibbonNavState): void;
|
|
1848
|
+
setEditState(state: RibbonEditState): void;
|
|
1444
1849
|
setNotesExpanded(expanded: boolean): void;
|
|
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;
|
|
1445
1865
|
}
|
|
1446
1866
|
|
|
1447
1867
|
/** The viewer's static DOM skeleton plus the mutable overlay controls. */
|
|
1448
1868
|
interface ViewerChrome {
|
|
1449
1869
|
/** `.pptxv` root (focusable; keyboard navigation attaches here). */
|
|
1450
1870
|
root: HTMLElement;
|
|
1451
|
-
|
|
1871
|
+
/** The tabbed ribbon (primary row, nav row, tab bar + File/Home/Insert/View content); null when disabled. */
|
|
1872
|
+
ribbon: Ribbon | null;
|
|
1873
|
+
/** Property inspector panel; null when disabled. */
|
|
1874
|
+
inspector: Inspector | null;
|
|
1452
1875
|
thumbnails: ThumbnailRail | null;
|
|
1453
1876
|
/** Scrollable centring viewport around the stage. */
|
|
1454
1877
|
viewport: HTMLElement;
|
|
@@ -1473,6 +1896,80 @@ interface PresentationController {
|
|
|
1473
1896
|
dispose(): void;
|
|
1474
1897
|
}
|
|
1475
1898
|
|
|
1899
|
+
interface EditorController {
|
|
1900
|
+
/** (Re)wire listeners + overlay into the current chrome (after mount). */
|
|
1901
|
+
attachChrome(): void;
|
|
1902
|
+
detachChrome(): void;
|
|
1903
|
+
/** Called by the render controller after every stage render. */
|
|
1904
|
+
onStageRendered(): void;
|
|
1905
|
+
/** True while editing owns the keyboard (selection or inline editing). */
|
|
1906
|
+
capturesKeyboard(): boolean;
|
|
1907
|
+
/** Drop history/selection/dirty state (new content loaded). */
|
|
1908
|
+
reset(): void;
|
|
1909
|
+
setEditable(editable: boolean): void;
|
|
1910
|
+
undo(): void;
|
|
1911
|
+
redo(): void;
|
|
1912
|
+
canUndo(): boolean;
|
|
1913
|
+
canRedo(): boolean;
|
|
1914
|
+
deleteSelected(): void;
|
|
1915
|
+
duplicateSelected(): string | null;
|
|
1916
|
+
getSelectedElementId(): string | null;
|
|
1917
|
+
/** The formatting / insert / arrange actions for the editing chrome. */
|
|
1918
|
+
getEditActions(): EditActions;
|
|
1919
|
+
/** The Find & Replace actions for the ribbon's docked panel. */
|
|
1920
|
+
getFindReplaceActions(): FindReplaceActions;
|
|
1921
|
+
/** Commit the speaker-notes textarea's plain text onto the current slide. */
|
|
1922
|
+
commitNotes(notes: string): void;
|
|
1923
|
+
save(): Promise<Uint8Array>;
|
|
1924
|
+
downloadPptx(fileName?: string): Promise<void>;
|
|
1925
|
+
destroy(): void;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
/** Everything the controller needs after a stage (re)render. */
|
|
1929
|
+
interface SyncStageParams {
|
|
1930
|
+
doc: Document;
|
|
1931
|
+
/** The stage host (position: relative), for layering a transition overlay. */
|
|
1932
|
+
stageWrap: HTMLElement;
|
|
1933
|
+
/** The freshly rendered, fully-visible main stage node. */
|
|
1934
|
+
stage: HTMLElement;
|
|
1935
|
+
/** The slide the stage renders, or `undefined` when empty. */
|
|
1936
|
+
slide: P | undefined;
|
|
1937
|
+
/** Zero-based index of the rendered slide. */
|
|
1938
|
+
slideIndex: number;
|
|
1939
|
+
/** True only when the live (fullscreen) presentation stage is active. */
|
|
1940
|
+
presenting: boolean;
|
|
1941
|
+
}
|
|
1942
|
+
/**
|
|
1943
|
+
* The presentation-mode playback state machine for the vanilla binding.
|
|
1944
|
+
*
|
|
1945
|
+
* It owns the click-stepped animation cursor and the slide-transition overlay,
|
|
1946
|
+
* both driven by the shared framework-agnostic helpers. It is consulted from
|
|
1947
|
+
* two seams in the rebuild-per-change render flow:
|
|
1948
|
+
*
|
|
1949
|
+
* - {@link PresentationPlayback.syncStage} runs after every stage render:
|
|
1950
|
+
* on a slide entry it rebuilds the click groups, resets the step, hides
|
|
1951
|
+
* pending entrances, and (when the slide changed mid-show) plays the
|
|
1952
|
+
* incoming slide's transition over a snapshot of the outgoing stage.
|
|
1953
|
+
* - {@link PresentationPlayback.advance} runs on each forward navigation
|
|
1954
|
+
* key/tap while presenting: it reveals the next animation build in place
|
|
1955
|
+
* (no rebuild) and reports whether a build remained, so the caller only
|
|
1956
|
+
* advances the slide once the timeline is exhausted.
|
|
1957
|
+
*/
|
|
1958
|
+
interface PresentationPlayback {
|
|
1959
|
+
/**
|
|
1960
|
+
* Reveal the next on-click animation build for the current slide. Returns
|
|
1961
|
+
* `true` if a build was revealed (stay on the slide); `false` when the
|
|
1962
|
+
* slide's builds are exhausted (the caller should advance to the next slide).
|
|
1963
|
+
*/
|
|
1964
|
+
advance(): boolean;
|
|
1965
|
+
/** True when every click group on the current slide has been revealed. */
|
|
1966
|
+
isComplete(): boolean;
|
|
1967
|
+
/** Sync playback + transitions after a stage (re)render. */
|
|
1968
|
+
syncStage(params: SyncStageParams): void;
|
|
1969
|
+
/** Cancel any running transition and forget all per-slide state. */
|
|
1970
|
+
reset(): void;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1476
1973
|
/** Discriminant values of the {@link PptxElement} union (`'text'`, `'chart'`, ...). */
|
|
1477
1974
|
type PptxElementType = a['type'];
|
|
1478
1975
|
/**
|
|
@@ -1620,79 +2117,137 @@ declare function renderSlideStage(options: SlideStageOptions): HTMLElement;
|
|
|
1620
2117
|
*/
|
|
1621
2118
|
declare function createDefaultRegistry(): ElementRendererRegistry;
|
|
1622
2119
|
|
|
2120
|
+
interface RenderController {
|
|
2121
|
+
/** Re-render everything (used after a chrome rebuild). */
|
|
2122
|
+
renderAll(): void;
|
|
2123
|
+
/** Re-render the main stage + toolbar counters at the current state. */
|
|
2124
|
+
renderStage(): void;
|
|
2125
|
+
/** Rebuild the thumbnail rail from the current slide list. */
|
|
2126
|
+
renderThumbnails(): void;
|
|
2127
|
+
/** Resolve the requested zoom into a concrete scale factor. */
|
|
2128
|
+
effectiveScale(): number;
|
|
2129
|
+
/**
|
|
2130
|
+
* Presentation-mode animation/transition playback, driven by the stage
|
|
2131
|
+
* rebuild flow. Navigation (`viewer-controls`) consults `advance()` so a
|
|
2132
|
+
* "next" first steps the on-click animation timeline before advancing slides.
|
|
2133
|
+
*/
|
|
2134
|
+
readonly presentationPlayback: PresentationPlayback;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
1623
2137
|
/**
|
|
1624
|
-
*
|
|
2138
|
+
* Debounced autosave for the vanilla viewer, layered on the shared IndexedDB
|
|
2139
|
+
* recovery store (`pptx-viewer-shared/autosave-store`).
|
|
1625
2140
|
*
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
*
|
|
1629
|
-
* `
|
|
2141
|
+
* Semantics mirror the React/Vue bindings: a local edit marks the document
|
|
2142
|
+
* dirty; a debounce timer then re-serializes the deck through
|
|
2143
|
+
* `PptxHandler.save` and persists it under `filePath` via
|
|
2144
|
+
* `saveAutosaveSnapshot` (evicting the oldest record on quota exhaustion). The
|
|
2145
|
+
* recovery blob is a crash-safety net only; it never clears the editor's dirty
|
|
2146
|
+
* flag or stands in for the user's real Save.
|
|
2147
|
+
*
|
|
2148
|
+
* On construction it probes `getAutosaveSnapshot(filePath)` and, when a snapshot
|
|
2149
|
+
* from a previous session exists, offers it back through `onRecovery` so the
|
|
2150
|
+
* host can decide whether to restore it (e.g. via `loadFile`).
|
|
1630
2151
|
*/
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
get(): T;
|
|
1636
|
-
/** Shallow-merge a partial patch into the state and notify subscribers. */
|
|
1637
|
-
set(patch: Partial<T>): void;
|
|
1638
|
-
/** Subscribe to state changes; returns an unsubscribe function. */
|
|
1639
|
-
subscribe(listener: StoreListener<T>): () => void;
|
|
1640
|
-
}
|
|
2152
|
+
type AutosaveStatus = 'idle' | 'saving' | 'saved' | 'error';
|
|
2153
|
+
|
|
2154
|
+
/** Per-slide progress callback: `(currentSlideIndex, totalSlides)`. */
|
|
2155
|
+
type ExportProgress = (current: number, total: number) => void;
|
|
1641
2156
|
|
|
1642
|
-
/** `zoom` is either an explicit scale factor (1 = 100%) or fit-to-viewport. */
|
|
1643
|
-
type ZoomLevel = number | 'fit';
|
|
1644
2157
|
/**
|
|
1645
|
-
*
|
|
1646
|
-
*
|
|
2158
|
+
* Animated-GIF export for the vanilla binding. All slides are captured (one
|
|
2159
|
+
* frame per slide) via the injected `rasterizeSlide`, then encoded with the
|
|
2160
|
+
* shared pure-JS GIF89a encoder (`pptx-viewer-shared` `gif-encoder`: median-cut
|
|
2161
|
+
* quantisation + LZW). Frame timing comes from the shared `planGifFrames`
|
|
2162
|
+
* planner and oversized captures are downscaled via `clampGifDimensions`; only
|
|
2163
|
+
* the DOM capture / canvas scaling / Blob download driver lives here.
|
|
1647
2164
|
*/
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
slides: P[];
|
|
1651
|
-
/** Slide canvas size in CSS pixels. */
|
|
1652
|
-
canvasSize: CanvasSize;
|
|
1653
|
-
/** Archive-path to displayable URL map for media + poster frames. */
|
|
1654
|
-
mediaDataUrls: Map<string, string>;
|
|
1655
|
-
/** Zero-based index of the visible slide. */
|
|
1656
|
-
currentSlide: number;
|
|
1657
|
-
/** Requested zoom (explicit factor or fit-to-viewport). */
|
|
1658
|
-
zoom: ZoomLevel;
|
|
1659
|
-
/** True while a load is in flight. */
|
|
1660
|
-
loading: boolean;
|
|
1661
|
-
/** Error message from the last failed load, or null. */
|
|
1662
|
-
error: string | null;
|
|
1663
|
-
/** True while presentation (fullscreen) mode is active. */
|
|
1664
|
-
presenting: boolean;
|
|
1665
|
-
/** True when editing interactions (select/move/resize/...) are enabled. */
|
|
1666
|
-
editable: boolean;
|
|
1667
|
-
/** Id of the selected element on the current slide, or null. */
|
|
1668
|
-
selectedElementId: string | null;
|
|
1669
|
-
/** True when the document has unsaved edits (cleared by a save). */
|
|
1670
|
-
dirty: boolean;
|
|
2165
|
+
/** Options for the animated-GIF export (all slides, one frame per slide). */
|
|
2166
|
+
interface ExportGifOptions {
|
|
1671
2167
|
/**
|
|
1672
|
-
*
|
|
1673
|
-
*
|
|
2168
|
+
* Duration each slide is shown, in milliseconds (default 2000). Per-slide
|
|
2169
|
+
* overrides can be supplied via {@link ExportGifOptions.slideTimingsMs}.
|
|
1674
2170
|
*/
|
|
1675
|
-
|
|
2171
|
+
slideDurationMs?: number;
|
|
1676
2172
|
/**
|
|
1677
|
-
*
|
|
1678
|
-
*
|
|
2173
|
+
* Per-slide duration overrides in milliseconds (index maps to slide index,
|
|
2174
|
+
* e.g. rehearsed timings). Flows through the shared `planGifFrames` plan
|
|
2175
|
+
* into per-frame GIF delays.
|
|
1679
2176
|
*/
|
|
1680
|
-
|
|
2177
|
+
slideTimingsMs?: number[];
|
|
2178
|
+
/**
|
|
2179
|
+
* Cap on the longer side of the encoded frames, in pixels (default 1920).
|
|
2180
|
+
* Captured canvases larger than this are downscaled before quantisation,
|
|
2181
|
+
* keeping encode time and file size manageable.
|
|
2182
|
+
*/
|
|
2183
|
+
maxDimension?: number;
|
|
2184
|
+
/** Capture-phase progress callback: `(currentSlide, totalSlides)`. */
|
|
2185
|
+
onProgress?: ExportProgress;
|
|
2186
|
+
/** Abort the export early; the capture loop checks this between slides. */
|
|
2187
|
+
signal?: AbortSignal;
|
|
1681
2188
|
}
|
|
1682
2189
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
2190
|
+
/**
|
|
2191
|
+
* Print for the vanilla binding, assembled entirely from the shared print
|
|
2192
|
+
* module (`pptx-viewer-shared` `print-document`): `validatePrintSettings`,
|
|
2193
|
+
* `computeSlideIndices` / `computeColorFilter`, the `build*Html` body markup
|
|
2194
|
+
* builders, and the DOMPurify-hardened `buildPrintHtmlDocument` assembler.
|
|
2195
|
+
* Only the drivers live here: rasterising the selected slides to data URLs and
|
|
2196
|
+
* writing the document into a print window. Vanilla port of Vue's `usePrint`
|
|
2197
|
+
* raster path (slides / notes / handouts / outline).
|
|
2198
|
+
*/
|
|
2199
|
+
/**
|
|
2200
|
+
* Open a print window for a complete HTML document and trigger printing.
|
|
2201
|
+
* Returns `false` when the window could not be opened (popup blocker).
|
|
2202
|
+
*/
|
|
2203
|
+
type OpenPrintWindow = (htmlDocument: string) => boolean;
|
|
2204
|
+
/**
|
|
2205
|
+
* Options for `print`: any subset of the shared `PrintSettings` (unspecified
|
|
2206
|
+
* fields fall back to `DEFAULT_PRINT_SETTINGS`, i.e. all slides, landscape,
|
|
2207
|
+
* full colour) plus progress/abort and a print-window override.
|
|
2208
|
+
*/
|
|
2209
|
+
interface PrintOptions extends Partial<PrintSettings> {
|
|
2210
|
+
/** Rasterisation progress callback: `(currentSlide, totalSlidesToPrint)`. */
|
|
2211
|
+
onProgress?: ExportProgress;
|
|
2212
|
+
/** Abort before the window opens; checked between slide captures. */
|
|
2213
|
+
signal?: AbortSignal;
|
|
2214
|
+
/**
|
|
2215
|
+
* Override how the assembled document is opened (e.g. write it into a
|
|
2216
|
+
* hidden iframe). Popup-blocker caveat: the default opener uses
|
|
2217
|
+
* `window.open`, which browsers typically only allow inside a user
|
|
2218
|
+
* gesture, so call `print()` from a click handler; when the popup is
|
|
2219
|
+
* blocked the returned promise resolves `false`.
|
|
2220
|
+
*/
|
|
2221
|
+
openPrintWindow?: OpenPrintWindow;
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
/**
|
|
2225
|
+
* WebM video export for the vanilla binding, driven by the shared `video-plan`
|
|
2226
|
+
* module: `planVideoSegments` (per-slide segment timing + frame counts),
|
|
2227
|
+
* `fpsToFrameIntervalMs` (draw-loop pacing), and `pickSupportedMimeType` over
|
|
2228
|
+
* `WEBM_MIME_CANDIDATES` (MediaRecorder codec selection). Only the browser
|
|
2229
|
+
* driver lives here: capture each slide to a canvas, replay the canvases onto
|
|
2230
|
+
* a recording canvas fed to `captureStream()` + `MediaRecorder`, and download
|
|
2231
|
+
* the resulting Blob. Vanilla port of React's `exportAllSlidesAsVideo`.
|
|
2232
|
+
*/
|
|
2233
|
+
/** Options for the WebM video export (all slides). */
|
|
2234
|
+
interface ExportVideoOptions {
|
|
2235
|
+
/** Duration each slide is held, in milliseconds (default 3000). */
|
|
2236
|
+
slideDurationMs?: number;
|
|
2237
|
+
/** Per-slide duration overrides in milliseconds (index maps to slide index). */
|
|
2238
|
+
slideTimingsMs?: number[];
|
|
2239
|
+
/** Recording frame rate in frames per second (default 30). */
|
|
2240
|
+
fps?: number;
|
|
2241
|
+
/** MediaRecorder video bitrate in bits per second (default 5,000,000). */
|
|
2242
|
+
videoBitsPerSecond?: number;
|
|
2243
|
+
/** Capture-phase progress callback: `(currentSlide, totalSlides)`. */
|
|
2244
|
+
onProgress?: ExportProgress;
|
|
2245
|
+
/** Recording-phase progress callback: `(currentSlide, totalSlides)`. */
|
|
2246
|
+
onRecordProgress?: ExportProgress;
|
|
2247
|
+
/** Abort the export early; checked between slides and between frames. */
|
|
2248
|
+
signal?: AbortSignal;
|
|
1692
2249
|
}
|
|
1693
2250
|
|
|
1694
|
-
/** Per-slide progress callback: `(currentSlideIndex, totalSlides)`. */
|
|
1695
|
-
type ExportProgress = (current: number, total: number) => void;
|
|
1696
2251
|
/** Options for the multi-slide PDF export (progress + cooperative cancel). */
|
|
1697
2252
|
interface ExportPdfOptions {
|
|
1698
2253
|
/** Capture-phase progress callback: `(currentSlide, totalSlides)`. */
|
|
@@ -1707,13 +2262,6 @@ interface ExportPdfOptions {
|
|
|
1707
2262
|
*/
|
|
1708
2263
|
type PptxViewerSource = ArrayBuffer | Uint8Array | Blob | string;
|
|
1709
2264
|
|
|
1710
|
-
/**
|
|
1711
|
-
* Public API types for the vanilla (zero-framework) PowerPoint viewer.
|
|
1712
|
-
*
|
|
1713
|
-
* Derived from the Vue binding's `PowerPointViewerProps` / emits, translated
|
|
1714
|
-
* to plain options + callback functions (there is no framework event system
|
|
1715
|
-
* to emit through).
|
|
1716
|
-
*/
|
|
1717
2265
|
/** Callbacks mirroring the Vue component's emits. */
|
|
1718
2266
|
interface PptxViewerCallbacks {
|
|
1719
2267
|
/** Fired after a presentation loads successfully. */
|
|
@@ -1735,6 +2283,16 @@ interface PptxViewerCallbacks {
|
|
|
1735
2283
|
onDirtyChange?: (dirty: boolean) => void;
|
|
1736
2284
|
/** Fired when the selected element changes (`null` = no selection). */
|
|
1737
2285
|
onSelectionChange?: (elementId: string | null) => void;
|
|
2286
|
+
/** Fired on every autosave lifecycle transition (`saving`/`saved`/`error`). */
|
|
2287
|
+
onAutosaveStatus?: (status: AutosaveStatus) => void;
|
|
2288
|
+
/**
|
|
2289
|
+
* Offered a recovery snapshot found in the shared IndexedDB store on start
|
|
2290
|
+
* (a previous session's autosave for the same `autosaveFilePath`). The host
|
|
2291
|
+
* decides whether to restore it, e.g. `viewer.loadFile(record.data)`.
|
|
2292
|
+
*/
|
|
2293
|
+
onAutosaveRecovery?: (record: AutosaveRecord) => void;
|
|
2294
|
+
/** Fired on every collaboration connection-status transition. */
|
|
2295
|
+
onCollaborationStatus?: (status: ConnectionStatus) => void;
|
|
1738
2296
|
}
|
|
1739
2297
|
interface PptxViewerOptions extends PptxViewerCallbacks {
|
|
1740
2298
|
/**
|
|
@@ -1769,6 +2327,16 @@ interface PptxViewerOptions extends PptxViewerCallbacks {
|
|
|
1769
2327
|
showToolbar?: boolean;
|
|
1770
2328
|
/** Show the thumbnail sidebar (default `true`). */
|
|
1771
2329
|
showThumbnails?: boolean;
|
|
2330
|
+
/**
|
|
2331
|
+
* Build the editing format toolbar row (bold/fill/insert/z-order); default
|
|
2332
|
+
* `true`. The row is only *visible* while editing is enabled.
|
|
2333
|
+
*/
|
|
2334
|
+
showFormatToolbar?: boolean;
|
|
2335
|
+
/**
|
|
2336
|
+
* Build the property inspector panel (position/size/fill/line); default
|
|
2337
|
+
* `true`. Only *visible* while editing is enabled.
|
|
2338
|
+
*/
|
|
2339
|
+
showInspector?: boolean;
|
|
1772
2340
|
/**
|
|
1773
2341
|
* Custom element-renderer registry. Defaults to `createDefaultRegistry()`;
|
|
1774
2342
|
* pass your own (or mutate the default via `getRegistry()`) to add or
|
|
@@ -1784,6 +2352,30 @@ interface PptxViewerOptions extends PptxViewerCallbacks {
|
|
|
1784
2352
|
* mirroring the Vue/React/Angular bindings).
|
|
1785
2353
|
*/
|
|
1786
2354
|
smartArt3D?: boolean;
|
|
2355
|
+
/**
|
|
2356
|
+
* Enable debounced autosave (default `false`): after each local edit the deck
|
|
2357
|
+
* is re-serialized and stashed in the shared IndexedDB recovery store as a
|
|
2358
|
+
* crash-safety net (it never replaces the user's real Save). The toolbar shows
|
|
2359
|
+
* a small status pill; a snapshot from a prior session is offered through
|
|
2360
|
+
* {@link PptxViewerCallbacks.onAutosaveRecovery}.
|
|
2361
|
+
*/
|
|
2362
|
+
autosave?: boolean;
|
|
2363
|
+
/** Debounce window (ms) between an edit and the persisted snapshot (default 2000). */
|
|
2364
|
+
autosaveIntervalMs?: number;
|
|
2365
|
+
/** IndexedDB recovery key for autosave (default `'presentation.pptx'`). */
|
|
2366
|
+
autosaveFilePath?: string;
|
|
2367
|
+
/**
|
|
2368
|
+
* Start a real-time collaboration session immediately (Yjs over y-websocket
|
|
2369
|
+
* or serverless y-webrtc). Local edits publish to peers and remote edits
|
|
2370
|
+
* merge in granularly; a `role: 'viewer'` config forces read-only. Start or
|
|
2371
|
+
* stop a session later with {@link PptxViewerInstance.startCollaboration} /
|
|
2372
|
+
* {@link PptxViewerInstance.stopCollaboration}.
|
|
2373
|
+
*
|
|
2374
|
+
* Note: media/OLE/3D/ink binary payloads are not carried over the wire (a
|
|
2375
|
+
* shared codec limitation), and a remote update replaces the whole local
|
|
2376
|
+
* slide array, so a joiner's host-provided media can degrade.
|
|
2377
|
+
*/
|
|
2378
|
+
collaboration?: CollaborationConfig;
|
|
1787
2379
|
}
|
|
1788
2380
|
/** The viewer handle returned by `createPptxViewer`. */
|
|
1789
2381
|
interface PptxViewerInstance {
|
|
@@ -1843,6 +2435,26 @@ interface PptxViewerInstance {
|
|
|
1843
2435
|
* `jspdf` is dynamically imported on first use.
|
|
1844
2436
|
*/
|
|
1845
2437
|
exportPdf(options?: ExportPdfOptions): Promise<void>;
|
|
2438
|
+
/**
|
|
2439
|
+
* Export every slide as an animated GIF download (one frame per slide,
|
|
2440
|
+
* `slideDurationMs` per frame). Slides are captured off-screen like
|
|
2441
|
+
* `exportSlidePng` and encoded with the shared pure-JS GIF89a encoder.
|
|
2442
|
+
*/
|
|
2443
|
+
exportGif(options?: ExportGifOptions): Promise<void>;
|
|
2444
|
+
/**
|
|
2445
|
+
* Export every slide as a WebM video download: each captured slide is held
|
|
2446
|
+
* for its configured duration on a canvas stream recorded by
|
|
2447
|
+
* `MediaRecorder` (codec picked from the shared WebM candidates).
|
|
2448
|
+
*/
|
|
2449
|
+
exportVideo(options?: ExportVideoOptions): Promise<void>;
|
|
2450
|
+
/**
|
|
2451
|
+
* Assemble the printable document (slides / notes / handouts / outline)
|
|
2452
|
+
* and open it in a new print window. Resolves `false` when the popup was
|
|
2453
|
+
* blocked: browsers typically only allow `window.open` inside a user
|
|
2454
|
+
* gesture, so call this from a click handler (or pass a custom
|
|
2455
|
+
* `openPrintWindow` that writes into an iframe you own).
|
|
2456
|
+
*/
|
|
2457
|
+
print(options?: PrintOptions): Promise<boolean>;
|
|
1846
2458
|
/** The element-renderer registry in effect (extension point). */
|
|
1847
2459
|
getRegistry(): ElementRendererRegistry;
|
|
1848
2460
|
/**
|
|
@@ -1851,6 +2463,18 @@ interface PptxViewerInstance {
|
|
|
1851
2463
|
* archive access) without extra APIs here.
|
|
1852
2464
|
*/
|
|
1853
2465
|
getHandler(): PptxHandler | null;
|
|
2466
|
+
/**
|
|
2467
|
+
* Start (or restart) a real-time collaboration session. Resolves once the
|
|
2468
|
+
* transport is created; connection status arrives via
|
|
2469
|
+
* {@link PptxViewerCallbacks.onCollaborationStatus}.
|
|
2470
|
+
*/
|
|
2471
|
+
startCollaboration(config: CollaborationConfig): Promise<void>;
|
|
2472
|
+
/** Stop the active collaboration session (no-op when none is running). */
|
|
2473
|
+
stopCollaboration(): void;
|
|
2474
|
+
/** Current collaboration connection status (`'disconnected'` when inactive). */
|
|
2475
|
+
getCollaborationStatus(): ConnectionStatus;
|
|
2476
|
+
/** Force an immediate autosave snapshot (no-op when autosave is disabled). */
|
|
2477
|
+
autosaveNow(): Promise<void>;
|
|
1854
2478
|
/** Tear down DOM, listeners, Blob URLs, and the core handler. */
|
|
1855
2479
|
destroy(): void;
|
|
1856
2480
|
}
|
|
@@ -1874,6 +2498,8 @@ interface ChromeHost {
|
|
|
1874
2498
|
lifecycle: ChromeLifecycle;
|
|
1875
2499
|
editor: {
|
|
1876
2500
|
commitNotes(notes: string): void;
|
|
2501
|
+
getEditActions(): EditActions;
|
|
2502
|
+
getFindReplaceActions(): FindReplaceActions;
|
|
1877
2503
|
};
|
|
1878
2504
|
prev(): void;
|
|
1879
2505
|
next(): void;
|
|
@@ -1888,41 +2514,51 @@ interface ChromeHost {
|
|
|
1888
2514
|
getSlideCount(): number;
|
|
1889
2515
|
enterPresentation(): Promise<void>;
|
|
1890
2516
|
exitPresentation(): Promise<void>;
|
|
2517
|
+
exportSlidePng(): Promise<void>;
|
|
2518
|
+
exportPdf(): Promise<void>;
|
|
2519
|
+
exportGif(): Promise<void>;
|
|
2520
|
+
exportVideo(): Promise<void>;
|
|
2521
|
+
print(): Promise<boolean>;
|
|
1891
2522
|
}
|
|
1892
2523
|
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
/**
|
|
1898
|
-
|
|
1899
|
-
/**
|
|
1900
|
-
|
|
1901
|
-
/**
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
deleteSelected(): void;
|
|
1909
|
-
duplicateSelected(): string | null;
|
|
1910
|
-
getSelectedElementId(): string | null;
|
|
1911
|
-
/** Commit the speaker-notes textarea's plain text onto the current slide. */
|
|
1912
|
-
commitNotes(notes: string): void;
|
|
1913
|
-
save(): Promise<Uint8Array>;
|
|
1914
|
-
downloadPptx(fileName?: string): Promise<void>;
|
|
2524
|
+
/** The export slice of the public viewer API (see `PptxViewerInstance`). */
|
|
2525
|
+
interface ViewerExportApi {
|
|
2526
|
+
/** Export a single slide as a PNG download. Defaults to the current slide. */
|
|
2527
|
+
exportSlidePng(index?: number): Promise<void>;
|
|
2528
|
+
/** Export every slide as a multi-page PDF download (one slide per page). */
|
|
2529
|
+
exportPdf(options?: ExportPdfOptions): Promise<void>;
|
|
2530
|
+
/** Export every slide as an animated GIF download (one frame per slide). */
|
|
2531
|
+
exportGif(options?: ExportGifOptions): Promise<void>;
|
|
2532
|
+
/** Export every slide as a WebM video download (MediaRecorder). */
|
|
2533
|
+
exportVideo(options?: ExportVideoOptions): Promise<void>;
|
|
2534
|
+
/** Open the assembled print document in a print window (`false` = blocked). */
|
|
2535
|
+
print(options?: PrintOptions): Promise<boolean>;
|
|
2536
|
+
}
|
|
2537
|
+
interface ExportLifecycle extends ViewerExportApi {
|
|
2538
|
+
/** Remove the off-screen capture stage from the DOM. */
|
|
1915
2539
|
destroy(): void;
|
|
1916
2540
|
}
|
|
2541
|
+
/**
|
|
2542
|
+
* Base class implementing the export slice of `PptxViewerInstance` by
|
|
2543
|
+
* delegating to the instance's {@link ExportLifecycle}. `PptxViewer` extends
|
|
2544
|
+
* this, so new export formats are wired here (and in the export controller)
|
|
2545
|
+
* without growing the size-capped `PptxViewer.ts`.
|
|
2546
|
+
*/
|
|
2547
|
+
declare abstract class ViewerExportHost implements ViewerExportApi {
|
|
2548
|
+
protected abstract readonly exporter: ExportLifecycle;
|
|
2549
|
+
exportSlidePng(index?: number): Promise<void>;
|
|
2550
|
+
exportPdf(options?: ExportPdfOptions): Promise<void>;
|
|
2551
|
+
exportGif(options?: ExportGifOptions): Promise<void>;
|
|
2552
|
+
exportVideo(options?: ExportVideoOptions): Promise<void>;
|
|
2553
|
+
print(options?: PrintOptions): Promise<boolean>;
|
|
2554
|
+
}
|
|
1917
2555
|
|
|
1918
2556
|
/**
|
|
1919
|
-
* The zero-framework PowerPoint viewer. Construct via {@link createPptxViewer}
|
|
1920
|
-
*
|
|
1921
|
-
*
|
|
1922
|
-
* tiny reactive store. All parsing lives in `pptx-viewer-core`; all pure
|
|
1923
|
-
* render logic in `pptx-viewer-shared`.
|
|
2557
|
+
* The zero-framework PowerPoint viewer. Construct via {@link createPptxViewer}:
|
|
2558
|
+
* builds chrome inside `container`, loads `options.source` when given, and
|
|
2559
|
+
* re-renders through a tiny reactive store.
|
|
1924
2560
|
*/
|
|
1925
|
-
declare class PptxViewer implements PptxViewerInstance, ChromeHost {
|
|
2561
|
+
declare class PptxViewer extends ViewerExportHost implements PptxViewerInstance, ChromeHost {
|
|
1926
2562
|
readonly container: HTMLElement;
|
|
1927
2563
|
readonly doc: Document;
|
|
1928
2564
|
readonly options: PptxViewerOptions;
|
|
@@ -1931,9 +2567,11 @@ declare class PptxViewer implements PptxViewerInstance, ChromeHost {
|
|
|
1931
2567
|
t: Translator;
|
|
1932
2568
|
lifecycle: ChromeLifecycle;
|
|
1933
2569
|
editor: EditorController;
|
|
2570
|
+
protected readonly exporter: ExportLifecycle;
|
|
1934
2571
|
private readonly loading;
|
|
1935
|
-
private readonly exportLifecycle;
|
|
1936
2572
|
private readonly registry;
|
|
2573
|
+
private readonly sessions;
|
|
2574
|
+
private readonly controls;
|
|
1937
2575
|
private destroyed;
|
|
1938
2576
|
constructor(container: HTMLElement, options?: PptxViewerOptions);
|
|
1939
2577
|
loadFile(file: Blob | ArrayBuffer | Uint8Array): Promise<void>;
|
|
@@ -1960,30 +2598,22 @@ declare class PptxViewer implements PptxViewerInstance, ChromeHost {
|
|
|
1960
2598
|
save(): Promise<Uint8Array>;
|
|
1961
2599
|
downloadPptx(fileName?: string): Promise<void>;
|
|
1962
2600
|
deleteSelected(): void;
|
|
1963
|
-
exportSlidePng(index?: number): Promise<void>;
|
|
1964
|
-
exportPdf(options?: ExportPdfOptions): Promise<void>;
|
|
1965
2601
|
getSelectedElementId(): string | null;
|
|
1966
2602
|
enterPresentation(): Promise<void>;
|
|
1967
2603
|
exitPresentation(): Promise<void>;
|
|
1968
2604
|
getRegistry(): ElementRendererRegistry;
|
|
1969
2605
|
getHandler(): PptxHandler | null;
|
|
2606
|
+
startCollaboration(config: CollaborationConfig): Promise<void>;
|
|
2607
|
+
stopCollaboration(): void;
|
|
2608
|
+
getCollaborationStatus(): ConnectionStatus;
|
|
2609
|
+
autosaveNow(): Promise<void>;
|
|
1970
2610
|
destroy(): void;
|
|
1971
2611
|
private remountChrome;
|
|
1972
2612
|
}
|
|
1973
|
-
/**
|
|
1974
|
-
* Create a PowerPoint viewer inside `container`.
|
|
1975
|
-
*
|
|
1976
|
-
* ```ts
|
|
1977
|
-
* import { createPptxViewer } from 'pptx-vanilla-viewer';
|
|
1978
|
-
* const viewer = createPptxViewer(document.querySelector('#host')!, {
|
|
1979
|
-
* source: '/deck.pptx',
|
|
1980
|
-
* onSlideChange: (i) => console.log('slide', i + 1),
|
|
1981
|
-
* });
|
|
1982
|
-
* ```
|
|
1983
|
-
*/
|
|
2613
|
+
/** Create a PowerPoint viewer inside `container` (see {@link PptxViewerOptions}). */
|
|
1984
2614
|
declare function createPptxViewer(container: HTMLElement, options?: PptxViewerOptions): PptxViewerInstance;
|
|
1985
2615
|
|
|
1986
2616
|
/** The complete viewer stylesheet text (for hosts that self-manage CSS). */
|
|
1987
2617
|
declare function getViewerCss(): string;
|
|
1988
2618
|
|
|
1989
|
-
export { type CssStyleMap, type ElementRenderContext, type ElementRenderer, type ElementRendererRegistry, type ExportPdfOptions, type ExportProgress, type PptxElementType, PptxHandler, PptxViewer, type PptxViewerCallbacks, type PptxViewerInstance, type PptxViewerOptions, type PptxViewerSource, type SlideStageOptions, type TranslationMessages, type Translator, type ViewerState, type ZoomLevel, applyStyleMap, createDefaultRegistry, createEl, createElementRendererRegistry, createPptxViewer, createSvgEl, createTranslator, getViewerCss, renderSlideStage };
|
|
2619
|
+
export { type AutosaveRecord, type AutosaveStatus, type CollaborationConfig, type CollaborationRole, type CollaborationTransport, type ConnectionStatus, type CssStyleMap, type ElementRenderContext, type ElementRenderer, type ElementRendererRegistry, type ExportGifOptions, type ExportPdfOptions, type ExportProgress, type ExportVideoOptions, type OpenPrintWindow, type PptxElementType, PptxHandler, PptxViewer, type PptxViewerCallbacks, type PptxViewerInstance, type PptxViewerOptions, type PptxViewerSource, type PrintOptions, type SlideStageOptions, type TranslationMessages, type Translator, type ViewerState, type ZoomLevel, applyStyleMap, createDefaultRegistry, createEl, createElementRendererRegistry, createPptxViewer, createSvgEl, createTranslator, getViewerCss, renderSlideStage };
|