@liebstoeckel/engine
v0.3.6
Published
The compiler, React runtime, and single-file build pipeline at the core of liebstoeckel.
Maintainers
Readme
@liebstoeckel/engine
The compiler, React runtime, and single-file build pipeline at the core of liebstoeckel.
Part of liebstoeckel, a code-first presentation framework. You write decks in MDX and TSX and build them into a single self-contained HTML file with no server or runtime dependencies. The same file works offline, and when you host it the deck runs a live session between the presenter and the audience. Built on Bun, React 19, Motion, and Tailwind v4.
Status: experimental, pre-1.0. liebstoeckel is an evolving experiment, not yet production-ready. Before 1.0, breaking changes can land in any release without a major-version bump, so pin an exact version if you depend on it.
engine compiles your MDX and TSX slides into a deck, renders them with Motion transitions and keyboard and touch navigation, and bundles the markup, styles, and client runtime into a single HTML file. It also carries the live-mode client that syncs the presenter and audience over Yjs, plus a build-time macro for animated code. Most people drive it through @liebstoeckel/cli. Install it directly when you want to run the build yourself.
Install
bun add @liebstoeckel/engine
bun add react react-dom yjs # peers you provideBun-native. It ships raw TypeScript and builds under Bun ≥ 1.3. The
./codeexport is a Bun macro, so the build has to run under Bun.
Usage
// main.tsx — the deck entry, mounted by index.html
import { createRoot } from "react-dom/client";
import { Present } from "@liebstoeckel/engine";
import "@liebstoeckel/theme/styles.css";
import Intro from "./slides/intro.mdx";
import Hello from "./slides/hello.tsx";
createRoot(document.getElementById("root")!).render(
<Present slides={[Intro, Hello]} brands={["acme"]} />,
);Each slide is its own MDX or TSX module; Present orders them, renders Motion
transitions, and (when hosted) runs the live presenter↔audience session.
// build.ts: compile the deck (entry is the index.html) to one self-contained file
import { bundleDeck } from "@liebstoeckel/engine/build";
await bundleDeck({ entry: "./index.html", outdir: "./dist" });
// writes dist/index.htmlFor thumbnails-in-the-build, use the batteries-included buildDeck from
@liebstoeckel/thumbnails/build
instead — it wraps bundleDeck and embeds per-slide screenshots.
Exports
| Entry | What |
|---|---|
| @liebstoeckel/engine | Runtime: Present, Deck, PresenterView, ScaledStage / SlideFrame, Step / StepsProvider / useRevealState, CodeMagic, Magic / Atmosphere, useDeckSync / useDeckNav, and the live re-exports (Plugin, LiveProvider, useLive, …) |
| @liebstoeckel/engine/build | bundleDeck(options) writes self-contained HTML. Options: entry (an index.html), outdir, outfile, minify, pkgJson, inlinePackage, inlineLicenses |
| @liebstoeckel/engine/live | Live-mode client runtime (Yjs sync) |
| @liebstoeckel/engine/code | The Bun macro for build-time syntax highlighting (animated code) |
| @liebstoeckel/engine/mdx-plugin | The MDX build plugin |
| @liebstoeckel/engine/visx-esm-plugin | The visx ESM-interop build plugin |
Architecture
Three concerns live here: a build pipeline (src/build/), a React runtime (the top-level src/*.tsx), and a live and plugin layer (src/live/). A build-time code macro (src/code/) sits alongside them.
build/buildDeck.tsdrivesBun.build({ target: "browser", compile: true })with thetailwindandmdxplugins, so the JS and CSS inline into one HTML file. It then post-processes the output.escapeInlineModuleScriptneutralizes any</script>inside the inlined bundle, andembedManifestinjects the base64 plugin manifest (server bundles come frombuildServerBundle).build/mdx-plugin.tsis aBunPluginthat compiles.mdxwith the automatic React runtime and the@mdx-js/reactprovider. Fenced code is highlighted by Shiki at build time using a css-variables theme (var(--shiki-token-*)), so no highlighter ships to the browser.code/macro.ts(the./codeBun macro) andcode/tokenize.tshandle animated code.codeStoryandcodetokenize source at build time intoTokenizedStepliterals for<CodeMagic>, which keeps Shiki and its grammars out of the runtime.Present.tsxis the single entry point. It detects live mode (live/detect), connects the shared Yjs doc, provides theLiveProvidercontext, and branches toDeck,PresenterView, orCaptureView(the build-time thumbnail capture, gated bybuild/capture-protocol).Deck.tsx,Stage.tsx, andsteps.tsxrender slides.Deckdraws the active slide inside a fixedSTAGE_WbySTAGE_HScaledStage(letterboxed), animates transitions with MotionAnimatePresence, and wires navigation throughnav.ts.StepsProvider,Step, anduseRevealStatedrive progressive reveals. Index and step state come fromuseDeckSync(a BroadcastChannel, standalone) orlive/deckIndex(Yjs, live).live/holds the live client.connect.tsdoes auto-reconnecting WebSocket Yjs sync with a half-open watchdog,Plugin.tsxrenders a plugin's Slide or fallback against the shared doc, andbreakout.tsxturns a touch glow-tap into a sheet.
The engine re-exports Magic and Atmosphere from @liebstoeckel/components, reads brand tokens through useTheme in @liebstoeckel/plugin-ui, and uses the --shiki-* and --brand-* variables from @liebstoeckel/theme for on-palette code and atmosphere. Plugin discovery and manifest types come from @liebstoeckel/plugin-sdk.
Links
Licensed under MPL-2.0.
