Repository navigation
sdk: plugins register demos, and the demos service lists and plays them - #608
Merged
Merged
Conversation
A plugin registers a demo with `host.registerDemo(.{ .name, .title, .tape })`: the tape as ZON
or binary bytes, copied by the host and dropped when the plugin goes. The new `demos` service
(`sdk.services.demos`, fizzy's in src/editor/Demos.zig) lists every demo fizzy can play, its own
first, each with its length, chapters and origin, and plays one by name, from a chapter if
asked; `current` names the one playing. A plugin's demo that would not play as one (a live
tape, a step nothing applies, a name fizzy has) is left out of the list with a warning.
`fizzy.demo.play` plays any of them by name.
The boundary's fingerprint moves; `sdk_version` does not.
Part of fizzyedit/plans#30 (step 1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
foxnne
marked this pull request as ready for review
October 19, 2026 01:26
foxnne
enabled auto-merge (squash)
October 19, 2026 03:24
3 of 5 tasks
foxnne
added a commit
that referenced
this pull request
Oct 19, 2026
Part of fizzyedit/plans#30 (step 2: the guides plugin). ## What changes A new bundled plugin, **`guides`**, adds a view on the rail (a graduation cap, under Files). - The view lists what the `demos` service (#608) lists: fizzy's tour first, then each plugin's own demos. Each guide is a row with its length, and each chapter is an indented row with where it starts. - Clicking a guide plays it from the start. Clicking a chapter plays the guide from that chapter. - The guide playing is drawn as selected. - The service already refuses a demo over unsaved work or while a live tape drives, and says why in a toast, so the view adds nothing for that. - With no `demos` service, the view says the app plays no demos. With an empty list, it says there are no guides yet. - `guides.show` reveals the view wherever it lives. It reaches nothing but `sdk.services.demos`, so an app on fizzy links it or leaves it out like any other plugin. It's static-only, like `devtools` and `archive`: no bundled dylib. Fixed while looking at it: the time is laid out before the title. A dvui box gives its children room in order, so a long title ("Markdown, Previewed as You Type") had squeezed the length to "0…". Now the title is cut short instead. ## SDK impact - [x] None ## Verified - [x] Web, in the browser pane, on `zig build web`: - The view lists both guides: the tour at 1:09 with its five chapters past the first, and the markdown guide at 0:26 with three. - Clicking "Editing" (0:23) cut the tour straight into its Editing chapter, which then typed on. - Reopening the view during the demo shows the tour drawn as selected. - No console errors. - [x] macOS: `zig build`, `zig build test`, `zig build test-integration` (81/81 steps), `zig build check-web`, `zig build check-tests-reached`, `zig build test-sdk-version`, `scripts/check-fmt.sh`. The standalone plugin build (`cd plugins/guides && zig build`, into a sandbox `FIZZY_PROFILE`) builds the dylib, and its `zig-pkg` holds no Velopack. - [ ] Windows: - [ ] Linux: No automated test drives the view. What it shows and plays comes from the `demos` service, which #608's tests cover. ## Follow-ups - Step 3: the web's hosted guides (`demos/index.json`) join the list. - Step 4: `?view=guides` opens with the view showing, for the site. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne
added a commit
that referenced
this pull request
Oct 19, 2026
## What changes `sdk_version` goes from 0.2.31 to 0.2.32. Merging this tags `sdk-v0.2.32` and publishes its tarball. `sdk-repin` then asks each store plugin to repin. **Why now:** the SDK's shape has moved three times since 0.2.31, so store plugins built for 0.2.31 don't load in a fizzy built from `main`. Pixi's history trim (reading fizzy's history limit) needs #620's call. ## SDK impact - [ ] None - [ ] Core-only or additive: reaches plugins at the next SDK release - [ ] Fingerprint moved: recorded in `sdk/src/version.zig`, `sdk_version` untouched, PR labelled `sdk` - [x] SDK release: bumps `sdk_version`, lists the `sdk` PRs since the last `sdk-v*` tag **`sdk` PRs since `sdk-v0.2.31`:** - **#608: plugins ship demos.** - `host.registerDemo(.{ .name, .title, .tape })` in `register`; the host copies them and drops them on unregister. - The `demos` service (`sdk.services.demos`, version 1) lists every demo fizzy can play and plays one by name. - `sdk.Demo` is new. Nothing existing changed shape. - **#606: a document's bytes without a save.** - New optional `Plugin.VTable.documentBytes(state, doc, path, allocator)`: the bytes a save would write, with none of a save's bookkeeping (no format on save, no undo stop). Fizzy uses them for crash checkpoints, a restart's carried work, a tape's file and `fizzy.document.changed` listeners. - An owner without it falls back to `documentSnapshot` + `encodeSnapshot`, as before. - **#620: the history limit is fizzy's.** `host.historyLimit()` (a new `EditorAPI` member) is how many steps a document's undo history keeps: fizzy's "Undo steps per document" setting, 0 for no limit. Each plugin that owns a history trims its own. **Also reaching plugins at this release** (no boundary change): - **#616:** `Host.Activity` gains an optional `agent`, the author index of an agent's edit. - **#589:** the dvui pin moves to the zig-0.17 branch's floating-window background hook (`docs/DEPENDENCIES.md`, patch 14). - **#614:** glass panels in `core/dialogs.zig` get a drop's rim. (`app/` changes since 0.2.31 are not compiled into plugins.) ## Verified - [x] macOS: `scripts/check-fmt.sh` and `zig build test-sdk-version` pass. The fingerprint is main's recorded value (`0x3d7fca8d3d211e73`); only `sdk_version` changed. - [x] Store plugins built against this `sdk/` (`--fork`, Zig 0.17, macOS, sandbox profile): pixi, zig, atlas, drive and ghostty, each from its `main`, all build. - [ ] Linux: CI ## Follow-ups - Repin and release the store plugins (pixi, zig, ghostty, drive, atlas; then agent and chat, by hand) when `sdk-repin` opens their PRs. - Make 0.2.32 live on fizzyed.it/app once the repinned plugins are in the store: `gh workflow run web.yml --ref main -f go_live=0.2.32`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
3 of 5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of fizzyedit/plans#30 (step 1: a demo registry).
What changes
A plugin can ship demos, and anything can list and play the demos the app has. This is the seam the
guidesplugin (step 2) is built on, and that pixi's own guides come in through later.host.registerDemo(.{ .name, .title, .tape }), called inregister. The tape is ZON or binary bytes built with the SDK'stapemodule. The host copies the name, title and tape, so nothing points into the plugin's image, and drops them when the plugin unregisters. A name already registered is refused (error.DemoExists).Host.demos_generationmoves whenever the list changes.demosservice (sdk.services.demos), provided by fizzy insrc/editor/Demos.zig:listreturns every demo fizzy can play, its own first, each with its length, chapters and origin (bundled,plugin, orhostedfor step 3).play(name, at_ms)plays one, from a chapter's start if asked. It returnsunknown,busy(a live tape is driving) orunsaved(the same refusals a demo already had, now with a reason).currentnames the demo playing. A tape loaded some other way since isn't mistaken for it.generationis cheap to ask every frame.fizzy.demo.play("Demo: Play by Name…") plays any listed demo byname, fromat. The bundled demos keep their own palette rows. A plugin's demo gets none, because aCommandcarries only its owner's state, so one row per runtime demo would need a context it doesn't have. The guides view (step 2) is where those are browsed.Demo.startablesays why a demo can't start, andDemo.loadDemoTapeis the demo checkplayTapealready used.Changed from the plan: the plan had the
automationservice growingdemos()andplayDemo().automationis about live tapes on whatever is open. A demo is fizzy's player setting the session aside, so it got a service of its own, andautomation's version is untouched.SDK impact
sdk/src/version.zig,sdk_versionuntouched, PR labelledsdksdk_version, lists thesdkPRs since the lastsdk-v*tagFor plugin authors:
sdk.DemoandHost.registerDemoare new;sdk.services.demosis a new service at version 1. Nothing existing changed shape.Verified
zig build test: a newHosttest checks thatregisterDemokeeps its own copy (the caller's buffers are overwritten after it returns), refuses a name twice, and drops a plugin's demos atunregisterPlugin, with the generation moving each time.zig build test-integration(81/81 steps, after mergingmainwith app: a demo gives back a folder opened from a zip's bytes #596 in): the demo test lists the bundled demos through the service, then registers three demos. A plugin demo is listed with two chapters; one reusing the nametourand a live tape are left out, with exactly those two warnings. The plugin demo plays by name to its end, named bycurrent. Started at its second chapter, it's at that chapter. A tape loaded another way isn'tcurrent, and nothing is once unloaded.zig build,zig build check-web,zig build test-sdk-version,zig build check-tests-reached,scripts/check-fmt.sh.check-web); not run in a browser.Follow-ups
guidesplugin, lists the service and plays what's clicked.hostedreturnsunknownuntil the web'sdemos/index.jsonis read into the list.🤖 Generated with Claude Code