Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Fizzy (Editor) ←── Host registries + EditorAPI ──→ Plugin (register(
7. Memory: `host.allocator` vs `host.arena()`; never touch `dvui.currentWindow().gpa` directly.
8. ABI: structural fingerprint at `dlopen` (`fizzy_plugin_abi_fingerprint`). A change that moves the boundary's shape records the new `recorded_sdk_shape_fingerprint` and leaves `sdk_version` alone; only an SDK release PR bumps the version (`CONTRIBUTING.md`, "Changing the SDK").

Full contract: **[`docs/PLUGINS.md`](docs/PLUGINS.md)**. Living reshape plan: **[`docs/PLUGIN_MANIFEST_PLAN.md`](docs/PLUGIN_MANIFEST_PLAN.md)**.
Full contract: **[`docs/PLUGINS.md`](docs/PLUGINS.md)**. Living reshape plan: **[`plans/PLUGIN_MANIFEST_PLAN.md`](https://github.com/fizzyedit/plans/blob/main/plans/PLUGIN_MANIFEST_PLAN.md)**.

## Bundling plugins into an app

Expand Down Expand Up @@ -175,24 +175,30 @@ CI builds plugins for all 6 host targets by cross-compiling with `-Dtarget=` (se

## When you need more than this file

- **The library/framework work (merged from bookmark `fizzy-lib` in #194)** → [`docs/LIB_CHECKPOINT.md`](docs/LIB_CHECKPOINT.md):
**Plans live in a private repo, [`fizzyedit/plans`](https://github.com/fizzyedit/plans)**, open to
the `fizzyedit` organization. A path written `plans/X.md` — here, in the code's comments, in
PRs — means `plans/X.md` in that repo. Clone it beside the others (`~/dev/fizzyedit/plans`,
`gh repo clone fizzyedit/plans`) and read the plan before working on its step; a session without
access works from what the code and its public docs say.

- **The library/framework work (merged from bookmark `fizzy-lib` in #194)** → [`plans/LIB_CHECKPOINT.md`](https://github.com/fizzyedit/plans/blob/main/plans/LIB_CHECKPOINT.md):
what was done and the agreed next steps. Its ground rules predate `CONTRIBUTING.md`, which wins
where they differ.

- Demos that play the real app (tapes, the player, rewind, writing a demo, the anchors widgets
publish) → [`docs/AUTOMATION.md`](docs/AUTOMATION.md); where it is going (recording, seeking,
libraries) → [`docs/AUTOMATION_PLAN.md`](docs/AUTOMATION_PLAN.md). The tape, sequencer and
libraries) → [`plans/AUTOMATION_PLAN.md`](https://github.com/fizzyedit/plans/blob/main/plans/AUTOMATION_PLAN.md). The tape, sequencer and
script are the `tape` library in `sdk/tape/` (std-only); the players, their input and anchors
the `replay` library in `sdk/replay/` (dvui and `tape` only, for any dvui app); fizzy's overlay
and plugin service in `app/automation/`; fizzy's stage and bundled demos in
`src/editor/Demo.zig` + `src/editor/demos/`.
- Floating views (a view dropped on its own place's middle floats into a glass window: the rule,
occlusion, close-home, persistence) → `app/layout/SPLITS.md` "Floating a view"; taking floats out
into OS windows (ImGui-style viewports, per-OS dressing, the dvui-dev replay hook) →
[`docs/POPOUT_WINDOWS_PLAN.md`](docs/POPOUT_WINDOWS_PLAN.md).
[`plans/POPOUT_WINDOWS_PLAN.md`](https://github.com/fizzyedit/plans/blob/main/plans/POPOUT_WINDOWS_PLAN.md).
- Full plugin contract + lifecycle/hook tables → `docs/PLUGINS.md`
- The forks fizzy builds on (dvui-dev, SDL, sdl_zig): what each patches, where each is pinned, how to bump → [`docs/DEPENDENCIES.md`](docs/DEPENDENCIES.md)
- Native windows and glass: what Liquid Glass can do (measured), the drag's glass as one native
overlay on macOS 26, and fizzy's backend owning its windows while taking SDL's releases →
[`docs/NATIVE_WINDOWS_PLAN.md`](docs/NATIVE_WINDOWS_PLAN.md)
- Living reshape plan (identity `plugin.zig.zon`, comptime `settings.Schema`, ZON user config, no sidecars) → [`docs/PLUGIN_MANIFEST_PLAN.md`](docs/PLUGIN_MANIFEST_PLAN.md)
[`plans/NATIVE_WINDOWS_PLAN.md`](https://github.com/fizzyedit/plans/blob/main/plans/NATIVE_WINDOWS_PLAN.md)
- Living reshape plan (identity `plugin.zig.zon`, comptime `settings.Schema`, ZON user config, no sidecars) → [`plans/PLUGIN_MANIFEST_PLAN.md`](https://github.com/fizzyedit/plans/blob/main/plans/PLUGIN_MANIFEST_PLAN.md)
7 changes: 5 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ preferences, findings still in flight.
## One plan step, one branch, one PR

- **A plan is an issue** with a checklist of steps, or a plan doc merged on its own and linked
from one. A plan is never the PR its implementation lands in.
from one, both in the private [`fizzyedit/plans`](https://github.com/fizzyedit/plans) repo. A
plan is never the PR its implementation lands in. A PR here says `Part of fizzyedit/plans#N`,
or names the plan file as `plans/<NAME>_PLAN.md`; what a reviewer without access needs to judge
the change goes in the PR itself.
- **Each step is its own PR**, saying `Part of #N`. A PR is one change a reviewer can hold in
their head: aim for **800 lines or fewer** of diff outside tests and docs. Bigger work goes in
a stack of PRs, or lands dark behind a flag (`FIZZY_POPOUT` is the pattern) and is switched on
Expand Down Expand Up @@ -125,7 +128,7 @@ tarball is pinned by hash, so it is never replaced.
| What fizzy is, and its architecture rules | `CLAUDE.md` |
| How we work | this file |
| The plugin contract | `docs/PLUGINS.md` |
| Plans | issues; a long design as `docs/<NAME>_PLAN.md`, opening with a status line (`Status: proposed`, `in progress, #N`, or `done`) |
| Plans | private, in [`fizzyedit/plans`](https://github.com/fizzyedit/plans): issues, and a long design as `plans/<NAME>_PLAN.md` opening with a status line (`Status: proposed`, `in progress, fizzyedit/fizzy#N`, or `done`) |
| The forks fizzy builds on | `docs/DEPENDENCIES.md` |
| Releases | `RELEASING.md` |

Expand Down
4 changes: 2 additions & 2 deletions app/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pending_app_close: bool = false,
/// Hash of the last serialized settings.zon text written or captured at startup; avoids
/// redundant writes without keeping a full duplicate copy of the text around. Doubles as the
/// "was that change ours" filter for `settings_watcher` (see R11 in
/// docs/PLUGIN_MANIFEST_PLAN.md) — a change to disk whose hash matches this is our own last
/// plans/PLUGIN_MANIFEST_PLAN.md) — a change to disk whose hash matches this is our own last
/// write, not an external edit worth reconciling.
settings_last_saved_hash: ?u64 = null,

Expand Down Expand Up @@ -788,7 +788,7 @@ pub fn closeProjectFolder(app: *App) void {
}

/// One-shot: moves any pre-R10 flat `{plugins_dir}/{id}.{ext}` into its own
/// `{plugins_dir}/{id}/{id}.{ext}` directory (see docs/PLUGIN_MANIFEST_PLAN.md R10). Collects the
/// `{plugins_dir}/{id}/{id}.{ext}` directory (see plans/PLUGIN_MANIFEST_PLAN.md R10). Collects the
/// list of flat files first, then renames in a second pass, so mutating the directory never races
/// the iterator that's still walking it. Best-effort: a single failed rename is logged and
/// skipped rather than aborting the rest.
Expand Down
2 changes: 1 addition & 1 deletion app/RecentsMigration.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! One-shot migration of the legacy `recents.json` into `recents.zon` (see
//! docs/PLUGIN_MANIFEST_PLAN.md R3). All `std.json` usage on the recents path is isolated
//! plans/PLUGIN_MANIFEST_PLAN.md R3). All `std.json` usage on the recents path is isolated
//! to this file so it can be deleted once every install has migrated.
const std = @import("std");
const core = @import("core");
Expand Down
2 changes: 1 addition & 1 deletion app/layout/Floats.zig
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub const Float = struct {
/// the drop was cancelled and the photograph is still a true picture of it; gone, it landed
/// elsewhere, and the float comes back as it is now.
aside_view: []const u8 = "",
/// Out of the main window, in an OS window of its own (`docs/POPOUT_WINDOWS_PLAN.md`); null
/// Out of the main window, in an OS window of its own (`plans/POPOUT_WINDOWS_PLAN.md`); null
/// while it is in the main window. The application that owns the OS windows sets and clears
/// it, replays the float's drawing into its window and routes that window's pointer back.
viewport: ?Viewport = null,
Expand Down
2 changes: 1 addition & 1 deletion app/layout/SPLITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ by again, the drop zones, splits, the picker. A place of several views keeps the
its floats held on screen, and gives them back their places when it grows.

A float is a window inside fizzy's own window for now. Taking one out into an OS window of its
own is the plan in `docs/POPOUT_WINDOWS_PLAN.md`.
own is the plan in `plans/POPOUT_WINDOWS_PLAN.md`.

## Edges and bands

Expand Down
2 changes: 1 addition & 1 deletion app/settings/Settings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fn fieldEqual(comptime FT: type, a: FT, b: FT) bool {

/// Serialize fizzy's own fields as `.{ ...fizzy fields... }`, emitting **only the fields that
/// differ from the declared defaults above** — the same non-default-only rule plugin settings
/// follow (`sdk.settings.Schema(T).diffSerialize`, R12 in docs/PLUGIN_MANIFEST_PLAN.md), so both
/// follow (`sdk.settings.Schema(T).diffSerialize`, R12 in plans/PLUGIN_MANIFEST_PLAN.md), so both
/// halves of `settings.zon` record just what the user actually changed. An untouched fizzy
/// serializes to `.{}`; a field hand-written back at its default drops out on the next write.
/// Reading back is unaffected: `load`/`parseOnly` fill every missing field from these same
Expand Down
2 changes: 1 addition & 1 deletion app/settings/SettingsMigration.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! One-shot migrations for `settings.zon` layout changes — see docs/PLUGIN_MANIFEST_PLAN.md.
//! One-shot migrations for `settings.zon` layout changes — see plans/PLUGIN_MANIFEST_PLAN.md.
//!
//! 1. `mergeLegacyPerPluginFiles` — pre-R10 one-file-per-plugin (`<plugins_dir>/<id>.settings.zon`)
//! into the merged `.plugins.<id>` field (R10).
Expand Down
2 changes: 1 addition & 1 deletion app/settings/SettingsPluginsZon.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Reads/writes the `.plugins = .{ .<id> = .{...}, ... }` sub-tree of the merged
//! `settings.zon` — see docs/PLUGIN_MANIFEST_PLAN.md R10/R12.
//! `settings.zon` — see plans/PLUGIN_MANIFEST_PLAN.md R10/R12.
//!
//! Every plugin's block lives as a real nested ZON struct literal keyed by plugin id, not an
//! escaped-string blob. On-disk shape after R12:
Expand Down
2 changes: 1 addition & 1 deletion app/store/PluginLoader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub fn pluginExtension() []const u8 {
}

/// Bare `{name}.{ext}` filename (no directory) — the basename inside a plugin's own
/// `{plugins_dir}/{name}/` directory (see docs/PLUGIN_MANIFEST_PLAN.md R10; every plugin, built-in
/// `{plugins_dir}/{name}/` directory (see plans/PLUGIN_MANIFEST_PLAN.md R10; every plugin, built-in
/// or third-party, gets its own directory rather than sitting flat in `plugins/`).
pub fn pluginFilename(name: []const u8, allocator: std.mem.Allocator) ![]const u8 {
return std.fmt.allocPrint(allocator, "{s}.{s}", .{ name, pluginExtension() });
Expand Down
2 changes: 1 addition & 1 deletion app/store/PluginLoader_web.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Runtime plugin loading on the web: a plugin built as a wasm *side module* (see
//! `sdk/plugin_sdk.zig`'s `create` for wasm32 and `docs/REVIEW_2026-09.md` §2), fetched and
//! `sdk/plugin_sdk.zig`'s `create` for wasm32 and `plans/REVIEW_2026-09.md` §2), fetched and
//! linked into the page by `web/index.html`'s `loadPlugin`, which hands back one function-table
//! index per entry point. On wasm32 a function pointer *is* a table index, so from there this
//! runs the same sequence the desktop loader runs (`PluginLoader.loadAndRegister`): fingerprint,
Expand Down
2 changes: 1 addition & 1 deletion app/store/PluginStore.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1847,7 +1847,7 @@ fn scoreEntry(entry: StoreEntry, query: *const fuzzy.Query) ?f64 {

// `descriptionFor`/`tagsFor` already fall back past the registry (built-in manifest, then a
// dylib probe), so a plugin with no registry entry yet still scores on its own prose — only
// `author` has no such fallback (it's registry-only, see `docs/PLUGIN_MANIFEST_PLAN.md`).
// `author` has no such fallback (it's registry-only, see `plans/PLUGIN_MANIFEST_PLAN.md`).
const author = if (entry.registry) |r| r.author else "";
var prose = fuzzy.scoreBest(&.{ descriptionFor(entry) orelse "", author }, query, .{ .plain = true });
for (tagsFor(entry)) |tag| {
Expand Down
2 changes: 1 addition & 1 deletion app/watch/SettingsWatcher.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Watches `<config>/` (recursive) for external changes while fizzy is running — see
//! docs/PLUGIN_MANIFEST_PLAN.md R11/R12.
//! plans/PLUGIN_MANIFEST_PLAN.md R11/R12.
//!
//! Thin adapter over [`neurocyte/nightwatch`](https://github.com/neurocyte/nightwatch): one
//! recursive `watch(config_folder)` covers `settings.zon` reconciliation, discovery of
Expand Down
6 changes: 3 additions & 3 deletions build/app.zig
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ pub fn construct(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.

// `replay` (`sdk/replay/`) on its own: dvui and `tape`, nothing of fizzy's. A reach into
// `core` or `app` fails to compile here, which is what keeps it a library any dvui app can
// take (`docs/AUTOMATION_PLAN.md`, "The libraries").
// take (`plans/AUTOMATION_PLAN.md`, "The libraries").
{
const replay_only = b.createModule(.{
.target = target,
Expand Down Expand Up @@ -914,7 +914,7 @@ pub fn construct(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
integration_module.addAnonymousImport("markdown_sample", .{ .root_source_file = b.path("docs/PLUGINS.md") });
// The document with the 45KB table — the case table-row culling exists for, and the one it
// could get wrong.
integration_module.addAnonymousImport("markdown_sample_tables", .{ .root_source_file = b.path("docs/PLUGIN_MANIFEST_PLAN.md") });
integration_module.addAnonymousImport("markdown_sample_tables", .{ .root_source_file = b.path("tests/data/markdown_sample_tables.md") });
// The image-heavy fixture. Both docs above are prose and tables, so without this no test ever
// laid out an image block — the one block kind whose height nothing in the source predicts
// and which rescales with the pane right up until the pane is wider than the image.
Expand Down Expand Up @@ -1022,7 +1022,7 @@ pub fn construct(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.
// reasoning as `bench-text`'s samples. `PLUGINS.md` is the document that prompted the
// benchmark.
bench_module.addAnonymousImport("sample_huge", .{ .root_source_file = b.path("docs/PLUGINS.md") });
bench_module.addAnonymousImport("sample_prose", .{ .root_source_file = b.path("docs/PLUGIN_MANIFEST_PLAN.md") });
bench_module.addAnonymousImport("sample_prose", .{ .root_source_file = b.path("tests/data/markdown_sample_tables.md") });
bench_module.addAnonymousImport("sample_medium", .{ .root_source_file = b.path("CLAUDE.md") });
bench_module.addAnonymousImport("sample_small", .{ .root_source_file = b.path("docs/MODULARIZATION_RELEASE_NOTES.md") });

Expand Down
2 changes: 1 addition & 1 deletion core/core.zig
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ pub const fuzzy = @import("fuzzy.zig");
pub const FileTable = @import("FileTable.zig");
/// The mountable-filesystem contract (`vfs.Fs`): path-addressed, completion-based, wasm-safe.
/// A cloud plugin mounts one on the host's `FileTable`; the local disk is `LocalFs` behind the
/// same interface. See `docs/CLOUD_FS_PLAN.md`.
/// same interface. See `plans/CLOUD_FS_PLAN.md`.
pub const vfs = @import("vfs/vfs.zig");
pub const LocalFs = @import("LocalFs.zig");
/// Long-running work as a stepped `Task`, run on a thread natively or from the frame on the
Expand Down
2 changes: 1 addition & 1 deletion core/gfx/glass_look.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! One material on two sliders, the window's (`docs/NATIVE_WINDOWS_PLAN.md`): what every glass
//! One material on two sliders, the window's (`plans/NATIVE_WINDOWS_PLAN.md`): what every glass
//! surface is at the window's opacity and roughness.
//!
//! - **Roughness** is the glass. At 0 it is clear, shiny glass, a lens bending what is behind it
Expand Down
2 changes: 1 addition & 1 deletion core/screens.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! The screens a floating thing — a menu, a tooltip, a popover, a dialog — is placed on and kept
//! within. The main window's, and, while a float is out of it in an OS window of its own, that
//! window's part of the frame (`docs/POPOUT_WINDOWS_PLAN.md`): far past the main window's edge,
//! window's part of the frame (`plans/POPOUT_WINDOWS_PLAN.md`): far past the main window's edge,
//! where anything opened from the float is drawn and from where it is copied into that window.
//! Placed against the main window there, a menu opened in a float that is out was pulled all the
//! way back onto the main window, and drawn there.
Expand Down
2 changes: 1 addition & 1 deletion core/vfs/vfs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//! and implements `Fs` over `http.Transport`; nothing here knows any provider's name.
//!
//! See `Fs.zig` for the two decisions that shape the API (paths, not ids; completions from
//! `pump`, never a blocking call) and `docs/CLOUD_FS_PLAN.md` for the whole design.
//! `pump`, never a blocking call) and `plans/CLOUD_FS_PLAN.md` for the whole design.
const FsFile = @import("Fs.zig");

pub const Error = FsFile.Error;
Expand Down
2 changes: 1 addition & 1 deletion core/widgets/FloatingWindowWidget.zig
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub const InitOptions = struct {
/// else places and sizes — a layout's float, which lands exactly where a drop put it.
placed: bool = false,
/// Drawn for an OS window of its own rather than the main one (a float popped out,
/// `docs/POPOUT_WINDOWS_PLAN.md`): its rect lies wherever that window's part of the frame is,
/// `plans/POPOUT_WINDOWS_PLAN.md`): its rect lies wherever that window's part of the frame is,
/// usually past the main window's edge, so it is not held on the main window and is clipped
/// to itself rather than to it.
detached: bool = false,
Expand Down
Loading
Loading