Plugins overview
A plugin adds live, shared behavior to a deck: a poll, audience Q&A, emoji reactions, or anything you can model as shared state. Plugins are ordinary Bun packages; a deck depends on them like any dependency.
Anatomy
Section titled “Anatomy”A plugin is a definePlugin({ … }) value with four fields:
| Field | What it is |
|---|---|
id | A short string. Used to place the plugin (<Plugin id="poll" />) and to namespace its shared state (plugin:poll). |
state | A typed schema describing the shared CRDT state (the SDK’s schema/t). |
client | React surfaces: Slide (in-deck), an optional presenter console (a tab in the presenter view), and a fallback (offline/thumbnail). Runs in every browser. |
server | Optional host-side logic. Runs once, on the machine presenting, never in the audience’s browsers. |
Built-in plugins
Section titled “Built-in plugins”@liebstoeckel/plugin-poll: live poll; everyone votes, results update in real time.@liebstoeckel/plugin-qa: audience asks + upvotes questions from any slide (a 💬 chrome button, no Q&A slide required); presenter moderates from the presenter console; the queue re-ranks live.@liebstoeckel/plugin-reactions: ephemeral floating emoji over the deck (rate-limited, self-pruning).
All three are pure-CRDT (no server part) and make good templates.
Using a plugin
Section titled “Using a plugin”Register it with <Present> and place it on a slide:
import poll from "@liebstoeckel/plugin-poll";
<Present plugins={[poll]} slides={[…]} />import { Plugin } from "@liebstoeckel/engine";
<Plugin id="poll" props={{ question: "Ship it?", options: ["Yes", "Also yes"] }} /><Plugin> renders the plugin’s Slide when a live server is connected, else its fallback. On touch screens it provides the tap-to-interact breakout automatically.
The id you place must be in <Present plugins={[…]}>. A <Plugin> whose id isn’t registered renders nothing (the build still succeeds) — a blank spot on a slide almost always means a missing entry in plugins. Dev builds log a console warning to flag it.
Discovery & bundling
Section titled “Discovery & bundling”A package is recognized as a plugin by a marker in its package.json:
{ "keywords": ["liebstoeckel-plugin"], "liebstoeckel": { "client": "./src/client.tsx", "server": "./src/server.ts" }}At build time the deck’s dependencies are scanned for this marker; matching plugins go into a manifest embedded in the deck. Any server entry is bundled (target bun) and base64-encoded into that manifest. It is decoded and run only by a live server, never in the browser.
Code-first presentations your agent can author. One file out, no server.
Comments