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 Footer(props: { children?: NormalizedChild[] }): VNode

Render repeated slide footer content using the theme's footer placement.

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.

Properties

Notes
Head
Header
Footer
Steps
pre