slides
js/ui/slides.ts
fino:ui/slides — shared, server-driven presentations from MDX and Fino VNodes.
A Presentation owns one default in-memory navigation state shared by its
presenter and following viewers, plus isolated viewer sessions requested
with ?follow=false. It exposes mountable Router collections instead of
choosing application paths: viewer() and presenter() can be mounted
independently, while router() provides a convenience composition with the
presenter at the relative /_presenter route. The presenter is intentionally
unauthenticated at this layer so applications can attach their own middleware
before mount.
Navigation is serialized on the server. By default, each accepted presenter
command renders one revision and fans the same { id, mode, html } SSE patch
out to every connected viewer. Opening a viewer with ?follow=false creates
an isolated server-side navigation session instead: arrow keys command that
session and its own SSE stream without changing presenter state. All session
state is process-local and resets with the application.
MDX and component modules are trusted executable code. File-backed decks may
export theme variables, a trusted styles string, and Markdown components
overrides. The default mappings render fenced code through the shared
syntax-highlighting component. Components must be synchronous server-rendered
Fino UI components; browser hydration and arbitrary client action registration
are outside this module.
import { App } from 'fino:net/http/app';
import { Presentation } from 'fino:ui/slides';
const slides = new Presentation('./talk.mdx');
const app = new App();
app.route('/talk').mount(slides.viewer());
app.route('/talk-control').use(requireUser).mount(slides.presenter());
// `/talk` follows the presenter; `/talk?follow=false` navigates independently.Interfaces
interface PresentationMeta {
Metadata optionally exported by a slide module.
Properties
title?: string
Browser and presenter title.
author?: string
Presenter or organization label.
lang?: string
Document language. Defaults to en.
interface PresentationModule {
Executable module consumed by Presentation.
Properties
default: (props?: { components?: Record<string, string | Component<any>> }) => VNode
Render the complete deck as slide <section> VNodes.
meta?: PresentationMeta
Optional document metadata.
theme?: PresentationTheme
Optional CSS variables applied to viewer and presenter pages.
styles?: string
Optional trusted CSS appended to the viewer and presenter page styles.
components?: Record<string, string | Component<any>>
Optional Markdown element and deck primitive overrides.
interface PresentationOptions {
Construction options for a shared presentation.
Properties
id?: string
Stable identifier used to isolate the presentation broadcast topic.
interface PresentationState {
Immutable snapshot of shared navigation state.
Properties
slide: number
Zero-based active slide index.
step: number
Zero-based revealed step within the active slide.
revision: number
Monotonically increasing accepted-command revision.
Types
type PresentationTheme = Record<string, string | number>
CSS-variable theme optionally exported by a slide module.
Classes
class Presentation {
Shared server-side presentation and route factory.
source may be a module object or an importable .mdx/.tsx path. Loading
begins immediately and handlers await it before rendering. Call close() to
end active event streams and release the presentation.
Constructors
constructor(source: string | PresentationModule, options: PresentationOptions = {})
Begin loading source and create the default shared presentation session.
Getters
get state(): PresentationState
Current navigation snapshot. A fresh object is returned on every read.
Methods
next(): Promise<void>
Reveal the next step, or advance one slide when all steps are visible.
previous(): Promise<void>
Move to the previous step, or to the preceding slide's first step.
goTo(index: number, step = 0): Promise<void>
Move to a zero-based slide and optional step, clamped to deck bounds.
reset(): Promise<void>
Return to the first slide and restart the presenter timer.
viewer(): Router
Create the audience router and its SSE endpoints.
The default page follows presenter state. A request with ?follow=false
allocates an isolated server-side session, starts it at the first slide,
and emits arrow-key commands over POST /_command. Independent sessions
still receive rendered patches over SSE and are capped at 128 per
Presentation; creating another closes the oldest session.
presenter(): Router
Create a router serving the presenter console, command endpoint, and SSE stream.
router(): Router
Create the convenience router: viewer at /, presenter at the relative
/_presenter branch. Use split routers when authentication or unrelated
public paths are required.
async close(): Promise<void>
End active streams and reject future navigation commands. Idempotent.
Functions
function Head(props: { children?: NormalizedChild[] }): VNode
Mark document-level head content that is not rendered inside a slide.
function Header(props: { children?: NormalizedChild[] }): VNode
Render repeated slide header content using the theme's header placement.
function Notes(props: { children?: NormalizedChild[] }): VNode
Mark presenter-only speaker notes that are removed from audience output.
function Steps(props: { children?: NormalizedChild[] }): VNode
Reveal direct children sequentially before navigation advances the slide.
Constants
const slideComponents: Readonly<Record<string, Component<any>>>
Default MDX and presentation primitive mappings used by slide decks.