Long decks
Open long presentations progressively, track pending slides, and handle damaged content.
A long deck shows its first slide and takes input before the rest of the deck is ready. The other slides open in the background, in short tasks that give way to typing and clicks.
What opens first
editor.load(bytes) resolves when the first slide is ready to edit. To show another slide first, pass startSlide:
import type { Editor } from '@pptx-editor.dev/core/contracts';
export async function openAtSlide42(editor: Editor, bytes: Uint8Array) {
const result = await editor.load(bytes, { startSlide: 41 });
if (!result.ok) throw new Error(result.reason);
}startSlide is zero-based, so 41 opens slide 42. Adapter refs accept the same load options.
A start slide that is not a whole number rounds down, and a start slide outside the deck goes to the nearest slide. A value that is not a finite number opens the first slide.
The slides after the start slide open next, then the slides before it. The slide rail mounts its first screen with the first slide and the other items over the next frames. A thumbnail shows an empty placeholder until its slide is ready. The slides in view in the rail open before the others. Each rail step mounts what fits in about 8 ms, so a key press waits for one short task. In a rail with sections, a section header never shows without its slides. While the rail mounts, each item carries aria-setsize and aria-posinset, so a screen reader still hears the slide count.
A deck whose other slides hold less than 512 KiB of slide content opens whole.
Follow the progress
snapshot().isOpening is true until the first slide shows. Show an opening screen while it holds.
snapshot().pendingSlides is the number of slides still opening, and 0 when the deck is ready. Each SlideSummary in snapshot().slides has pending: true until its slide is ready. The count moves at most every 300 ms, and selectionChange fires when it moves. A slide that finishes opening does not fire change, because the document does not change: a host that saves on change does not save while the deck opens.
Use a subscription to update your loading indicator:
import { onEditorEvent } from '@pptx-editor.dev/core/editor';
import type { Editor } from '@pptx-editor.dev/core/contracts';
export function watchPendingSlides(editor: Editor, setProgress: (count: number) => void) {
setProgress(editor.snapshot().pendingSlides ?? 0);
return onEditorEvent(editor, 'selectionChange', (snapshot) => {
setProgress(snapshot.pendingSlides ?? 0);
});
}Call the returned function when you remove the indicator.
What works before the deck is ready
The editor opens a required slide immediately: a slide you go to, a slide that Find searches, and a slide that a command changes. The first Find in a long deck searches every slide, so it opens the rest of the deck in one step. Edits, undo, and redo work on every slide. save() opens the rest of the deck first, so the saved file holds every slide, and the parts that you did not edit save as they do after a full open.
A slide or a notes slide whose XML is damaged refuses the deck when it is read, in the background or for a command.
- With no edits, the editor holds no deck.
snapshot().openFailurereports it, and theopenFailureevent fires. - After edits, the deck stays open in view mode, so the edits stay on screen.
snapshot().readOnlyReasonis'damaged-slide', andopenFailurestaysnull, because the file did open. The status bar says: "A slide is damaged. You can view this presentation, but you cannot save changes." Thechangeevent fires once for the new state. The other slides go on opening. Every command, undo,save(), and automation write or export is refused, andsave()returnsnull. An automation read of the damaged slide is refused withdocument-unavailable. The next file that you open uses your mode again.
A save or an export before the damage is read opens the rest of the deck first, and finds the damage too. A damaged slide that you delete before it opens does not refuse the deck, unless an undo brings it back.
Typing in a long deck
A keystroke costs about the same in a deck of 5,000 slides as in a deck of 50.
- An edit keeps every part that it does not change, and records only its changes. It copies no list of all the parts of the deck.
- The slide summaries, the font list, and the font notice update from the parts that the edit changed.
- The slide rail renders again only the items whose slide changed. The other items keep their elements and their names.
- A thumbnail out of view in the rail takes no layout or paint (
content-visibility: auto). The rail can paint thumbnails in idle time without delaying the next key. - The rail and the slide sorter keep at most 200 thumbnails painted. Past that, the thumbnails painted longest ago that are out of view become empty again, and paint again when they scroll into view. Memory stays level when you scroll a long rail from end to end.
- The text measurements that the editor keeps are limited in count and in size. Typing in a long paragraph does not keep a copy of each version of the paragraph.
- The canvas reads its fit zoom again only when the slide size or the canvas size changes.
bun run bench:edit and bun run bench:open:browser measure these costs on synthetic decks of up to 5,000 slides. bun run bench:memory:browser measures memory across repeated opens, editing, and long decks.
Collaboration and fonts
- An editor with a collaboration module opens the whole deck before its first slide shows, because the room needs every slide.
- The fonts load for the families of the slides that are open. When the deck is ready, the fonts of families that only later slides name load too, and those slides lay out again with them.