Skip to content

workbench: a home surface replaces the built-in home page - #599

Merged
foxnne merged 2 commits into
mainfrom
workbench/home-surface
Oct 19, 2026
Merged

foxnne merged 2 commits into
mainfrom
workbench/home-surface

Conversation

@foxnne

@foxnne foxnne commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator

Part of fizzyedit/work#33

What changes

A plugin can replace the workbench's home page (logo, New File, Open Folder) by registering a surface with the keyword home (sdk.keywords.ide.home):

try host.registerSurface(.{ .id = "graphl.start", .title = "Start",
    .keywords = sdk.keywords.ide.home, .draw = drawStart });

While its one document pane is empty, the workbench declares a region named Home inside it (Workspace.drawHome: keywords = {"home"}, shows = .one, hide_when_empty). It draws the surface that region accepts, or its own page when there isn't one. Because the place exists only while nothing is open, a document opening puts the home away. So does a restored session landing a few frames after startup, and the last document closing brings it back. Graphl no longer has to retry revealSurface until the regions exist, or watch for the restore.

Design note

(a) A home keyword, and a Home place the workbench declares (chosen). This reuses the existing seams. Keywords say what a surface is. A plugin region decides where it goes and when it exists. Assignments choose between two: an app can call host.assignSurfaces("Home", &.{"graphl.start"}), the same list session restore and the picker write, and a user can choose in Settings › Layout. None of the shape's regions accept home (Fit.none against main), so a home surface never turns up as a tab in Main. The web build runs the same statically linked workbench.

(b) An app setting or AppInfo field naming the home surface id. The workbench would read it through a new EditorAPI member, which moves the fingerprint. It would also be a second way to say where a surface goes, beside keywords and assignments, and only an app could use it: a plugin couldn't offer a home page, and a user couldn't choose between two.

(a) is narrower and declarative. The boundary's shape doesn't move (test-sdk-version passes unchanged); the only addition is one keyword preset. The note is also on fizzyedit/work#33.

Also: Editor.fizzy_api_vtable is now pub, so the test can drive the workbench's own drawHome through a real Host.

SDK impact

  • None
  • Core-only or additive: reaches plugins at the next SDK release. It adds sdk.keywords.ide.home; a plugin on an older SDK can write &.{"home"}. The fingerprint did not move.
  • Fingerprint moved: recorded in sdk/src/version.zig, sdk_version untouched, PR labelled sdk
  • SDK release: bumps sdk_version, lists the sdk PRs since the last sdk-v* tag

Verified

  • macOS: the new integration test workbench: a home surface shows while nothing is open, and a document puts it away drives the workbench's Workspace.drawHome inside a pane declared as Workspace.draw declares it. It checks that: with no home surface the place declines and the built-in page draws; a home surface draws once, in Home and not in Main; a restored pane listing a document that hasn't landed keeps the home, and the landing puts it away (drawn nowhere); the last document closing brings it back; and with two home surfaces the first shows until assignSurfaces("Home", …) picks the other. I broke one expectation once to confirm the test runs. Gates: check-fmt (only the existing backend/src/platform/win32_titlebar.zig, untouched here), zig build, test, test-integration (80/80 steps), check-web, test-sdk-version, check-tests-reached, and check-examples (example-app zig-0.17 + example-plugin main via EXAMPLES_DIR; the default zig-0.17 ref no longer exists for example-plugin, which ci: the example repos are checked at main again #598 fixes). I didn't run it in the live app: fizzy registers no home surface, so its page goes down the unchanged path.
  • Windows:
  • Linux: CI
  • Web: check-web only

Follow-ups

  • Shapes without the workbench (example-app's minimal, studio, endless) have no Home place. A shape that wants one declares a region that takes home itself.
  • Graphl's start page: switch its keywords to sdk.keywords.ide.home and drop the startup retry and restore watch, once this reaches an SDK release.

🤖 Generated with Claude Code

A surface keyworded `home` (`sdk.keywords.ide.home`) is shown in a `Home`
place the workbench declares inside its sole document pane while it is
empty, instead of the workbench's logo and buttons. The place exists only
while nothing is open, so a document opening, or a session restore landing,
puts it away; two home pages are chosen between by assigning the place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne foxnne added the sdk Moves the plugin boundary's fingerprint; ships at the next SDK release label Oct 18, 2026
@foxnne
foxnne marked this pull request as ready for review October 18, 2026 23:38
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne enabled auto-merge (squash) October 19, 2026 01:31
@foxnne
foxnne merged commit f6d4f92 into main Oct 19, 2026
11 checks passed
@foxnne
foxnne deleted the workbench/home-surface branch October 19, 2026 01:43
@foxnne foxnne mentioned this pull request Oct 19, 2026
2 of 8 tasks
foxnne added a commit that referenced this pull request Oct 19, 2026
## What changes

`sdk_version` goes from 0.2.30 to 0.2.31. Merging this tags
`sdk-v0.2.31` and publishes its tarball. `sdk-repin` then asks each
store plugin to repin.

**Why now:** the SDK's shape has moved several times since 0.2.30. Store
plugins and apps built on fizzy, Graphl among them, only get the new
calls once they're released. Most of them come from Graphl's report.

## 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.30`:**
- **#560: a plugin's own data folder.** `host.pluginDataFolder(plugin)`
gives `<config>/plugin-data/<id>`, made on first ask. It's the plugin's
own from `register` through `deinit`, and stays on disk after uninstall.
- **#587: the data folder goes with the plugin.** `Host.retirePlugin`
unregisters a plugin, runs its `deinit`, then tells the app, so a plugin
can still write out state in `deinit`. On `EditorAPI`,
`pluginUnregistered` became `pluginRetired`; that vtable is implemented
by the app, not by plugins.
- **#593: put a view away.** `host.hideSurface(id)` shuts the tray whose
shown view is `id`, or closes a float that holds only `id`. Anything
else returns false. Mid-frame it acts after the frame has drawn. A
toggle is `if (!host.hideSurface(id)) _ = host.revealSurface(id);`.
- **#603: times on recent folders.** `host.recentFolderOpened(i)`
returns when recent folder `i` was opened, in Unix seconds, or null if
unknown. `recentFolderAt` is unchanged.
- **#599: a home page.** `sdk.keywords.ide.home` is new. While the
workbench's pane is empty, it shows a surface keyworded `home` in place
of its own page. The fingerprint didn't move; a plugin on an older SDK
can write `&.{"home"}`.
- **#581: undo is announced.** Undoing someone else's step is announced
as `fizzy.history.undone`, with a payload type `events.StepTaken`.
Additive; the fingerprint didn't move.
- **#609: history restore.** An optional `restoreHistory` hook and
`sdk.Plugin.RestoredHistory`. Plugins without the hook are unaffected.

**Core change plugins pick up at this release** (no boundary change):
#550, a replayed backdrop captures inside a front-to-back block. (`app/`
changes since 0.2.30, such as #570 and #602, 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; only `sdk_version`
changed.
- [ ] Windows:
- [ ] Linux: CI
- [ ] Web:

## Follow-ups

- Repin and release the store plugins (pixi, zig, ghostty, drive, atlas;
then agent and chat) when `sdk-repin` opens their PRs.
- Make 0.2.31 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.31`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sdk Moves the plugin boundary's fingerprint; ships at the next SDK release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant