Slides: decks, slide layouts and exports
The add-on @skaile/forge-editor-slides adds presentations to the editor. A deck is a Typed block of the Block type deck. Its body is slides, Typed blocks of the Block type slide. A slide has a title, a description (the subtitle of a title slide), a body, and three Block properties: layout, notes and status.
| Piece | What it does | Where |
|---|---|---|
slideBlockTypes(locale) | the Block types deck (a container that accepts slide) and slide | editor and server |
renderSlide, renderDeck | pure HTML and CSS of a slide or a deck | anywhere |
slidesTargets() | the render targets reveal-html, pptx, marp-md | server |
slidesTransforms() | the transfer transforms slides-from-blocks and slides-from-blocks-ai | editor |
slidesFeature() | the block actions „Folien-Layout“ and „Folie anhängen“, the slide preview in the inspector | editor |
The add-on imports the library through its public entries only. The entry @skaile/forge-editor-slides/server reaches no editor code: use it in a server program.
Install and wire
import { defaultBlockTypes } from '@skaile/forge-editor/composites'
import { copyTransform } from '@skaile/forge-editor/transfer'
import { bulletsTransform, summaryTransform } from '@skaile/forge-editor/integrations/composite-ai'
import { slideBlockTypes, slidesTransforms } from '@skaile/forge-editor-slides'
import { slidesFeature } from '@skaile/forge-editor-slides/vue' // or '/react'
const editor = composeEditor({
content: 'rich',
interaction: [...INTERACTION.full, 'transfer'],
// The Block types of the editor: the built-in ones and the two of this add-on.
blockTypes: [...defaultBlockTypes(t), ...slideBlockTypes('de')],
add: [slidesFeature()],
// A `transfer.transforms` list replaces the default list, so name the built-in transforms too.
transfer: { transforms: [copyTransform, summaryTransform, bulletsTransform, ...slidesTransforms({ locale: 'de' })] },
})
import { createRenderer, markdownTarget } from '@skaile/forge-editor/adapters/render'
import { slideBlockTypes, slidesTargets } from '@skaile/forge-editor-slides/server'
// installEditorServer({ ports: { render }, blockTypes })
export const render = createRenderer([markdownTarget(), ...slidesTargets()])
export const blockTypes = [...serverBlockTypes, ...slideBlockTypes('de')]
The Block types go to the server too: the AI chat then knows what a deck is, and the server builds the schema from the same list (Block types).
The slide model
| Part of a slide | Source |
|---|---|
| title | the title row of the Typed block (hidden on a blank slide) |
| subtitle | the description row (shown on a title slide) |
| body | the blocks after the chrome rows: paragraphs, headings, lists, quotes, code, images, rules |
layout | Block property, a select: title, bullets (default), two-column, image, blank |
notes | Block property, a textarea: speaker notes |
status | Block property, a status: draft (todo), ready (doing), final (done) |
Layouts decide the arrangement only. two-column splits the body at the first horizontal rule, or after the first half of its blocks. image shows the first image above the other blocks. A deck ignores body blocks that are not slides.
The render contract
renderSlide(slide, layout?) returns one slide in a 16:9 frame. renderDeck(deck, { css? }) returns all slides, one frame each, in front of a <style> element with SLIDE_CSS. Both are pure: the same JSON gives the same string, and every text is escaped (links keep only http, https, mailto and relative targets; an image source must be web, relative or a data:image/ address).
import { renderDeck } from '@skaile/forge-editor-slides/server'
const html = renderDeck(deckJson, { css: false })
// <div class="sk-deck" data-title="Quartalsbericht">
// <div class="sk-frame"><section class="sk-slide sk-slide--title" data-layout="title"><h1>Willkommen</h1>…
Nothing in the contract knows reveal.js, PowerPoint or Marp. Every target reads the same slide blocks (blocksOf) and builds its own format.
Render targets
slidesTargets(options) gives three targets for a Block of the type deck. A render of any other block fails with a message the client shows.
| Target | File | What it holds |
|---|---|---|
reveal-html | .html | a reveal.js 6 page: one <section> per slide, the speaker notes as <aside class="notes"> (speaker view with S). reveal.js loads from REVEAL_CDN (jsDelivr); options.reveal.cdn points it at your own copy, options.reveal.theme picks the theme |
pptx | .pptx | PowerPoint through PptxGenJS: the title as one text box, every block of the body as one text box, two stacks for two-column, pictures for data: images, speaker notes on the notes page |
marp-md | .md | Marp Markdown: front matter, --- between slides, the layout as a Marp class, speaker notes as HTML comments |
A pptx text box has a height from an estimate of the wrapped lines; PowerPoint shrinks text that does not fit when a person edits it. An image with a source other than a data: address shows its alt text, because the render cannot fetch it.
Make slides from blocks
Drag a block into a deck and pick a transform, or drop it in the document and pick Als Folien einfügen. Both transforms insert a new deck Typed block after the drop position, titled „Folien:
- A Typed block with nested Typed blocks gives one slide per nested block, depth first. Its own content comes first as a slide titled after it.
- Any other block is split at its headings: a heading starts a slide.
- A block without headings is one slide.
| Transform | Derivation method | Needs |
|---|---|---|
slides-from-blocks | link: the structure is copied | nothing |
slides-from-blocks-ai | ai:slides: a model writes the slides from the block's Markdown | the ai capability |
The new deck records a Derivation that names the source block. inputHash is the hash of the source's body Markdown at that moment (the snapshot) and outputHash is the hash of the deck's body, so the Derivation badge shows the deck as stale when the source changes and as edited when the deck body changes. The AI transform also records activity (a request id and the model). A drag of a block that has no block id inserts the deck without a Derivation.
The AI transform asks for Markdown: every slide starts with ## Title, bullets follow, optional lines Layout: … and Notizen: … set the layout and the speaker notes. The answer is parsed by slideDraftsFromMarkdown; an answer without a slide shows a notice and inserts nothing.
In the editor
slidesFeature() of /vue or /react adds:
- the block action Folien-Layout on a slide (a menu of the layouts;
layoutsin the feature options narrows it) and Folie anhängen on a deck, - the section Folienvorschau in the Typed block inspector and details card: a slide shows its 16:9 preview, layout buttons and notes; a deck shows all slides. The component is
SlideView.
The framework-free slidesFeature() of the package entry has the block actions without the preview.
Strings
The add-on keeps its own small catalog in German and English (SLIDES_MESSAGES, createSlidesTranslator(locale, overrides)), because the library's message keys are closed over the library's own strings. slideBlockTypes(locale) and slidesTransforms({ locale }) take the language; the feature and the preview read it from the editor context.
Audio: read aloud, mixdown and ElevenLabs
The add-on @skaile/forge-editor-audio: an ElevenLabs SpeechProvider, the derive methods audio-tts and audio-mixdown that make recording blocks with named Derivation inputs, and the render target audio-mixdown that mixes takes through ffmpeg.
Knowledge graph: sources and concepts beside the Documents
The read-only KnowledgeGraph port with its search and neighbours routes, the Wissensgraph tab of the Explore panel, the memory adapter for tests and the @skaile/forge-editor-kgs add-on over the KGS SDK.