Upgrading to 0.4
0.4 brings the live audience work: the join card and stable join link, a phone-first audience view, clear audience states in English and German, and presenter links that keep their token out of the address bar. It builds decks with a new build core, and the CLI brings its own Bun, so you no longer need Bun installed. It also tightens the CLI’s machine output. This page lists what to do and what behaves differently from 0.3.12.
Upgrade a deck
Section titled “Upgrade a deck”-
In each deck, run:
Terminal window liebstoeckel updateIt moves every
@liebstoeckel/*package the deck declares to its latest release, updates a global CLI install, and refreshes the agent skill. It prints one line per package that moved. Then it brings the deck’s scaffold files along (see The build core): it removes the 0.3bunfig.toml,server.tsandbuild.tswhere you never changed them, and says what to do with one you did change. A plainbun updatekeeps a deck on 0.3: before 1.0,^0.3.10does not include 0.4. -
Rebuild the deck (
liebstoeckel build), and rebuild any.htmlyou present through a relay (see Presenting an older deck). -
If the deck defines plugins of its own and presents them through a relay, declare their audience fields (see Plugins defined inside a deck).
All @liebstoeckel/* packages in a deck have to be on the same release line. A deck is one
inlined .html, so two copies of a package (say a 0.3 and a 0.4 plugin-sdk) would both be
bundled, and the build refuses that. The error names the package that pulled in the older copy.
Exact pins stay exact. A dependency written as "0.3.10" becomes "0.4.0", not
"^0.4.0", so a deck that pins keeps pinning. Ranges move as bun moves them.
Plugin authors: a plugin package that depends on @liebstoeckel/plugin-sdk or
@liebstoeckel/plugin-ui with ^0.3.x pulls in a 0.3 copy next to the deck’s 0.4 one. Publish
a release that accepts ^0.4.0.
Presenting an older deck
Section titled “Presenting an older deck”A 0.4 relay moves a presenter link’s token from the address into a cookie on the first load, so a projected presenter tab shows nothing that grants control. A deck built with an engine before 0.4 builds its “drive from your phone” link from the address bar, so the relay leaves the token in the address for such a deck, as before, and the phone link keeps working. The relay tells the two apart by the engine in the deck’s generator stamp. Rebuild the deck with 0.4 to get the cookie.
The other way round, a 0.4 deck on a 0.3 relay works, with the token left in the address bar
(only the relay can move it), and a viewer’s address may gain a ?pid= (a 0.3 relay does not
keep the participant id in a cookie, so the page keeps it in its address).
Plugins defined inside a deck
Section titled “Plugins defined inside a deck”A relay lets the audience write only the state fields a plugin declares. A plugin package
declares them in its own package.json; a plugin defined with definePlugin inside the deck
had no way to, so on a relay every audience write to it was lost (0.3 relays showed the viewer
their own vote while the presenter saw none; 0.4 relays refuse it and say so). Declare them in
the deck’s package.json, by plugin id:
"liebstoeckel": { "plugins": { "mood": { "audienceWrites": ["votes"] } }}See a plugin inside the deck. Add
"plugins" to the deck’s files list too (decks made with liebstoeckel new from 0.4 on have
it), or the built deck’s embedded source, and with it eject, lacks
the plugin. The build warns when it is missing.
The build core replaces the scaffold’s tooling files
Section titled “The build core replaces the scaffold’s tooling files”0.4 builds and serves decks with its own build core on esbuild instead of Bun’s bundler and dev server. For a deck author that means:
- Three files fewer. A 0.3 deck carries a
bunfig.toml, aserver.tsand abuild.ts, withdevandbuildscripts that run them.liebstoeckel update(andliebstoeckel dev) removes the ones you never changed, points the scripts atliebstoeckel devandliebstoeckel build, and drops them fromfiles. One you changed stays, and the CLI says what to do with it: move what you added elsewhere, then delete it. Nothing breaks while it is there, the CLI never runs it. This is migration0003-scaffold-tooling-files; see scaffold migrations. - No Bun plugins, no other macros. The build runs nothing of the deck. A Bun plugin of
your own in
bunfig.tomlis reported bydev,buildanddoctor, with what to do instead (see Building); a macro import other thancodeStory, and Tailwind’s@pluginor@config, are errors. - Animated code moves to code files. Write the states of a
<CodeMagic>block as fenced blocks in a.code.mdfile and import it. ThecodeStorymacro still builds, with a warning that it is deprecated; it goes in a later release. See Animated code for how to move a slide. - The same bytes everywhere. The same source builds to the same file on every machine and from any folder, and hot reload keeps the slide, the step and component state across edits.
The CLI brings its own Bun
Section titled “The CLI brings its own Bun”From 0.4 the liebstoeckel command runs on its own Bun, one exact version (Bun 1.4.3)
installed from npm with the CLI, and never on the Bun you have installed. You no longer need Bun at all:
npm install -g @liebstoeckel/cli is enough. liebstoeckel new installs a new deck’s
dependencies itself, and liebstoeckel install replaces
bun install and bun add in a deck. liebstoeckel --version and doctor show which Bun and
esbuild run. If you installed the CLI with --omit=optional, or through a registry mirror that
does not proxy the @oven scope, the command says what is missing; see
Installing the CLI. On Alpine (musl)
install the musl build of Bun next to the CLI, as the message says.
A deck you installed with Bun 1.3 needs nothing: the CLI reads its bun.lock and keeps it in
that format. A deck the CLI installs gets a bun.lock in Bun 1.4’s format. If you then run
bun install in it yourself with Bun 1.3, that Bun prints “Unknown lockfile version” once,
ignores the lockfile and writes it again in its own format, which the CLI then keeps. To avoid
the extra change in your history, use liebstoeckel install in the deck, or move your own Bun
to 1.4 (bun upgrade).
A deck that still runs its own 0.3 server.ts or build.ts needs bun-plugin-tailwind in
the deck now (liebstoeckel install --dev bun-plugin-tailwind), or moves to liebstoeckel dev
and liebstoeckel build, which need nothing.
CLI changes for scripts and agents
Section titled “CLI changes for scripts and agents”The CLI’s machine output follows one set of rules on every command. What a 0.3.12 script may notice:
| Change | 0.3.12 | 0.4 |
|---|---|---|
| Unknown options | ignored (build --check --thumbs=false ran) | refused before the command does anything, exit 2, code unknown_option, with the closest option it takes |
| Unknown commands | the whole usage, exit 1 | one line with the closest command, exit 2, code unknown_command |
--version | accepted after a command; prints the version | only liebstoeckel --version; prints liebstoeckel <version>, then the Bun and esbuild lines |
Usage errors (a missing or invalid argument, e.g. add with nothing to add, registry view without a name, build --visual without --check, eject without a deck, push without a file, new without a name) | exit 1 | exit 2 |
| JSON when piped | build, licenses, add, registry, doctor | also pack, push, decks, orgs list, brand list, and the new pull, sync, folders |
--no-json in a pipe | had no effect on the JSON commands | prints the human output |
| Error output in JSON mode | { "error": "..." }, or prose on stderr for build, pack and cloud HTTP errors | one document on stdout: { "ok": false, "error", "code", "hint" } |
dev poll errors | on stderr, { "error": "poll_failed", "message": ... } for a refused host or token too | on stdout with a code of their own: unauthorized, forbidden_host, no_dev_server, poll_failed; a bad --reply or --data exits 2 |
orgs use, brand push, brand pull without credentials | JSON on stdout when piped | prose on stderr (they have no JSON mode) |
Everything a 0.3.12 command did successfully it still does; the codes are listed under error codes.
The agent skill follows the deck. The CLI refreshes an installed skill to its own version
on any command, except in a deck whose engine is an older minor (a deck still on 0.3 under a
0.4 CLI): the 0.4 skill tells agents to use features such as <JoinCard /> that a 0.3 engine
does not have, so there the skill stays as it is until liebstoeckel update moves the deck.
liebstoeckel doctor --json reports it as skill.held.
Other changes
Section titled “Other changes”- Default brand. A deck that names no
brandsnow uses theliebstoeckelhouse brand. 0.3 defaulted to a brand name with no theme behind it, so such a deck rendered without theme tokens. An unknown brand name now prints a warning in development. - Smaller files. Builds always use React’s production build (before, the builder’s
NODE_ENVdecided, and it was usually the development build): a typical deck is about 17 % smaller. - Generator stamp. The
engineversion in a built file is the engine the deck bundles, not the one the CLI builds with. - Eject gets the deck’s
bun.lockand.gitignoreback, soliebstoeckel installin the ejected project restores the versions the deck was built with. The lockfile travels inside the built.html; a build refuses a lockfile with a password in a URL and names any registry other than npm’s that it lists. - Live connections send a keepalive every 10 s (was 25 s), on the relay and on
liebstoeckel live, and a client gives up on a silent connection after 35 s (was 60 s), so a dropped phone reconnects sooner. - Relay sessions can be requested for up to 24 h (the default stays 6 h). The relay takes up
to 10 000 entries in a plugin field holding objects and 50 000 in one holding single values
(was 5 000 for both);
PRESENT_RELAY_AUDIENCE_ENTRY_CAPlowers both, for testing what a full session looks like.
Live, programmable presentations. Your deck is a program; run it with the room.
Comments