Skip to content

backend: plain dvui app windows made of the window material - #565

Merged
foxnne merged 4 commits into
app/glass-materialfrom
backend/plain-material
Oct 18, 2026
Merged

foxnne merged 4 commits into
app/glass-materialfrom
backend/plain-material

Conversation

@foxnne

@foxnne foxnne commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Part of the window material (fizzyedit/plans#27, "backend: apply the effective material to every OS window") and the multi-window plan (fizzyedit/plans#26). Stacked on #549.

One decision, one applier (agreed with fizzy-1d):

  • core.material decides, and fizzy's Entry publishes the effective value.
  • The backend applies it. Entry hands the same value to the backend each frame, right after the publish (setWindowMaterial).
  • backend/ doesn't depend on core, so the backend has a plain-typed twin: MaterialKind, field for field, and window_material.

What the backend does, for the windows it dresses itself (dressPlainWindows: a plain dvui app's main window and its OS-framed floats):

  • Falls back from the kind asked to what it can show (effectiveMaterial, same order as core.material).
  • Keeps the theme's window colour as the backdrop, at the opacity asked (opaque for none).
  • Under it, the OS's vibrancy: the window's frosted material for frost, the full-screen-UI one for blur.
  • Liquid is fizzy's own window glass, not Apple's bare glass: the same parts fizzy's windows stand on (windowGlassInstall: lens, frost body, plain blur, colour) and the same look each frame (glass_look.window at the material's opacity, the clearing bevel by the window's size), over a clear backdrop. Choosing another material removes the parts. The first version, one NSGlassEffectView clipped to the corners (masksToBounds), looked unlike fizzy and cost a lot.
  • The material follows the backdrop exactly, including through the full-screen animation (glass_follow).
  • A float's window opened later takes the same material.

glass_look moves into the backend so it can use it: core/gfx/glass_look.zig → backend/src/glass_look.zig, a std-only module of its own.

  • The backend's build makes one instance per build (glassLookModule). core takes that same instance where it is linked beside the backend (core_module.addImports' new argument), since a file may belong to only one module.
  • Web, the tests and the plugin SDK make their own instance from the file. repoPath moves to core_module for that.
  • scripts/pack-sdk.sh vendors the file into the SDK tarball beside core/.

Not yet:

  • Fizzy's own windows (it dresses them itself: app_dresses_windows) don't take the material yet. That's the next step on plans#27.
  • In-app floating windows in the matching material: the next step after this one.
  • Windows and Linux: platform.window.plainMaterial is macOS only.

SDK impact

  • None. The plugin boundary is unchanged. The SDK tarball now carries backend/src/glass_look.zig (checked: scripts/pack-sdk.sh output lists it in .paths).

Verified

  • macOS (arm64, Zig 0.17.0): scripts/check-fmt.sh, zig build, zig build test, zig build check-tests-reached, zig build check-web, zig build test-sdk-version, zig build test-integration all pass.
  • plugins/image builds against sdk/ (a sandbox FIZZY_PROFILE).
  • By hand on run-dvui (dvui: the demo opens under the app's windows, the main window themed example-app#7): each material and the transparency slider, on the main window and an OS window.
  • By hand: Liquid Glass's look and cost against the first version, side by side.

🤖 Generated with Claude Code

foxnne and others added 2 commits October 18, 2026 12:09
The backend owns window_material (a kind, field for field with
core.material's, and an opacity) and applies it to the windows it dresses
itself (dressPlainWindows: a plain dvui app's main window and its
OS-framed floats), falling back through the same order to what it can
show. The theme's window colour stays the backdrop, at the opacity asked;
under it the OS's material: vibrancy for frost and blur, an
NSGlassEffectView for liquid. It follows the backdrop through full
screen (glass_follow). Fizzy's Entry hands it core.material's effective
value each frame after the publish: one decision, one applier
(setWindowMaterial). Fizzy's own windows, which it dresses itself, are
not made of it yet: the next step of plans#27.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the backend/plain-material branch from 458058c to 1e86eb9 Compare October 18, 2026 17:10
A plain dvui app's window made of Liquid Glass stood on one NSGlassEffectView
clipped to the window's corners (masksToBounds), which looked unlike fizzy's
windows (no clearing bevel, no frost body) and cost a lot to composite. It now
gets fizzy's own window glass: the same parts (`windowGlassInstall`) and the
same look each frame (`glass_look.window` at the material's opacity, the
bevel by the window's size), over a clear backdrop. Switching to another
material takes the parts away again.

`glass_look` moves from `core/gfx/` into the backend (`backend/src/`) as a
std-only module of its own, so the backend can apply it: the backend's build
makes one instance per build (`glassLookModule`), and `core` takes that one
where it is linked beside the backend (`core_module.addImports`), or makes
its own from the file (web, tests, the plugin SDK). The SDK tarball vendors
the file beside `core/`.
@foxnne
foxnne marked this pull request as ready for review October 18, 2026 18:39
@foxnne
foxnne merged commit ae5e719 into app/glass-material Oct 18, 2026
12 checks passed
@foxnne
foxnne deleted the backend/plain-material branch October 18, 2026 19:01
foxnne added a commit that referenced this pull request Oct 18, 2026
Part of the window material (fizzyedit/plans#27). Stacked on #549
(`app/glass-material`), where #565 put fizzy's window glass on a plain
dvui app's windows.

**The bug** (the user, on `run-dvui` with Liquid Glass): a blurred patch
in the window's top-left corner, with a diagonal edge, that appears and
disappears while the window is resized.

**Why:** the frost and colour of a window of Liquid Glass are masked by
the clearing bevel, a mask layer that each frame's look sizes
(`windowFeatherMask`). A live resize grows the parts straight away
(autoresizing), but the mask stayed at its old size until the next
frame. AppKit's layers are anchored at the bottom-left, so as the window
grew, the frost along its top fell outside the old mask for that moment,
and the old mask's rounded corner showed as a diagonal.

**The fix:** each masked part (frost and colour) sizes its mask as
AppKit resizes it. One shared observer listens for each part's frame
changes (`FizzyGlassMaskFollower`), and `windowGlassRemove` unregisters
a part as the glass is taken away. Fizzy's own windows install the same
parts, so they get it too.

## SDK impact

- [x] None

## Verified

- [x] macOS (arm64, Zig 0.17.0): `scripts/check-fmt.sh`, `zig build`.
- [ ] By hand: live-resize a `run-dvui` window on Liquid Glass (and a
fizzy window): no patch in the corner.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
foxnne added a commit that referenced this pull request Oct 18, 2026
Part of fizzyedit/plans#27 (step 2). Now on `main`: #546 landed with
`zig/0.17` (#575).

**This PR also carries #565**, merged into its branch: plain dvui apps'
windows take the window material in the backend, and `glass_look.zig`
moves to `backend/src/` as a shared module. So the diff is this PR's
three files (`Entry`, `Editor`, `Popout`) plus #565's.

## What changes

Every glass surface in the app and in plugins (dialogs, menus, the
command palette, tooltips, glass buttons, the dragged view) now draws in
one window material. That material is #546's `core.material`: whatever
the backend can show of what the app asks for.

| Material | In-app glass |
|---|---|
| `liquid` | as today: the glass program, with the bent edge and Apple's
lens on macOS; the OS's Liquid Glass for the drag where `native_glass`
is on |
| `frost` | the frosted pane: blur, tint, no bend, no glass program |
| `blur` | the same at half the blur |
| `transparent` | no blur: the window's colour at its opacity over what
is behind (`dialogFill`) |
| `none` | that fill, opaque |

- **Few new calls.** Core and every plugin dylib already read the values
the host publishes each frame: the dialog style, refraction, and whether
the glass program is on (`LiquidField.publishEnabled`). `Editor.tick`
maps the material onto those. `frost` takes the same code path fizzy
took with the glass shader switched off, so it is a path that has
already been exercised.
- **What it asks for.** `src/Entry.zig` asks for `liquid` until there is
a setting (plans#27, step 6), and publishes it before
`Popout.beginFrame`, so the drag's OS glass sees this frame's value.
`FIZZY_GLASS=frost|blur|transparent|none` asks for less, for testing.
- **Per-platform effect.** Per the rule agreed in plans#27 (one look per
OS, matching what its windows can show), this already changes Windows
and Linux, since they offer no liquid:
  - Windows frosts.
- Linux blurs where the compositor has a blur, and is transparent where
it doesn't.
  - macOS before 26 frosts.
  - macOS 26 and the web are unchanged.

## SDK impact

- [x] None. No new boundary: plugins read the published style as before.
- [ ] 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`
- [ ] SDK release: bumps `sdk_version`, lists the `sdk` PRs since the
last `sdk-v*` tag

## Verified

- [x] macOS 26:
- `scripts/check-fmt.sh`, `zig build`, `zig build check-web` and `zig
build test-sdk-version` all pass.
  - `zig build test`: 93/93 steps.
  - `zig build test-integration`: 77/77 steps, 523/523 tests.
- On screen: a sandbox fizzy (`--profile`) launched with each
`FIZZY_GLASS`, the command palette opened with ⇧⌘P, and the window
captured. `liquid` shows the bent edge; `frost` the same blur
flat-edged; `transparent` the welcome logo sharp behind the tint; `none`
opaque. `blur` was launched and captured, but its screenshot wasn't
compared side by side with `frost`.
- [ ] Windows: not run. Its default changes (frost); that needs seeing.
- [ ] Linux: not run. Its default changes (blur or transparent).
- [ ] Web: `check-web` only.

## Follow-ups

- The setting itself (plans#27, step 6).
- The OS windows taking the same material: #546's author, next.
- `BlurBackdrop` reads only `render_cmds`, so glass inside a
front-to-back block (a place's corner button) blurs nothing of that
block (plans#27).
- `glass_look`'s forDrops/forText split goes in its own PR.
- `transparent` over busy content is hard to read (the palette over the
welcome screen). It is what the kind is; a later look at a minimum
opacity under `transparent` may be worth it.

🤖 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant