EFX/IMG acceptance demos
Every feature in effects.md that a person can look at, and the command that shows it. Each one is runnable from a clean checkout inside nix develop, and each says what you should see — so a reviewer can accept or reject it without reading the diff.
Two conventions:
- A GUI demo runs under
xvfb-runso it needs no display, and writes a PNG. Drop thexvfb-runand the capture variable to watch it live instead. Theenv -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIRis not decoration: with a Wayland session in the environment, GLFW prefers it over the Xvfb displayxvfb-runjust started, and the window opens on the real desktop. - A terminal demo writes ANSI to stdout. Pipe it to
less -R, or run the binary without--renderfor the interactive version.
Build once: dub build :ui-gallery && dub build :hue.
Images (IMG1–IMG5)
| # | What it shows | Command | Expected |
|---|---|---|---|
| D1 | An image lays out as an ordinary box, and a cell grid degrades it visibly | dub run :ui-gallery -q -- --render-plain --page primitives | An image row whose specimen is the swatch rastered in sextants (🬭🬂🬭🬂…, GLY9) in the 8×2 cells the image's pixel size resolved to; with --profile baseline it reads [a colo] — the alt text, bracketed and truncated to the same cells |
| D2 | The same handle drawn as real pixels on the GPU | xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR UIG_SHOT=/tmp/img.png ./apps/ui-gallery/build/ui-gallery --gui --page primitives | /tmp/img.png shows a colour-swatch image where the terminal showed [a colo] |
| D3 | contain preserves aspect; an unregistered handle falls back | xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR IMGSHOT=/tmp/fit.png dub run --single docs/specs/ui/demos/imgshot.d | Three panels: intrinsic size, a 40×12 box letterboxed, and [not re] for a handle nothing registered |
The bracket and tier 0 (EFX1–EFX10, EFX24)
| # | What it shows | Command | Expected |
|---|---|---|---|
| D4 | Tier-0 effects running in a terminal — the claim EFX24 exists to make falsifiable | dub run :ui-gallery -q -- --render --page effects | Five panels of identical widgets: none plain, scanlines with alternating darkened rows, phosphor green, dim uniformly darker, spectrum a 24-bit hue sweep across the columns |
| D5 | Nesting composes with ancestors rather than replacing them (EFX2) | same as D4, scroll to "nesting · dim inside dim" | The inner panel is visibly darker than the outer one — it took both |
| D6 | A bracket never moves anything (EFX6) | dub test :ui -- -i "ui.displayList.effect" | Frames identical with and without the effect; the op stream is the plain one plus exactly two ops |
Tier 1, the registry and theming (EFX11–EFX19)
| # | What it shows | Command | Expected |
|---|---|---|---|
| D7 | The same page on the GPU: tier 0 and tier 1 | xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR UIG_SHOT=/tmp/fx.png ./apps/ui-gallery/build/ui-gallery --gui --page effects --window-width 60 --window-height 150 | The five tier-0 panels as in D4, plus a curvature panel whose text and borders visibly bow — rendered to a texture and warped |
| D8 | The tier boundary from the other side | D7's capture, "tier 1" section; then D4's terminal output | The window says "this target renders the bracket to a texture"; the terminal says it paints unaffected. Same widget, same id, two declared answers (EFX12) |
| D9 | A theme rebinding built-ins to nothing (EFX16) | UIG_EFFECTS=off dub run :ui-gallery -q -- --render --page effects | All five tier-0 panels identical to none. No view changed — the widgets still name the same ids |
| D10 | Every built-in ships both halves of its twin (EFX13) | dub test :ui -- -i "ui.effect" | builtinsCarryTheirGpuHalf passes: a D function and a GLSL artifact per built-in, each declaring the wrapper's signature |
| D11 | A missing effect cannot take a frame down (EFX17) | dub test :ui-tui -- -i "tier0EffectLandsInTheTerminal" | With no registry bound, the same op stream paints the subtree unaffected instead of failing |
| D17 | An effect over raster content — an image is an ordinary leaf inside the bracket | D7's capture, "over raster content" section; and D4's terminal output | In the window the swatch itself is tinted green under phosphor and hue-swept under spectrum; in the terminal the same brackets tint the swatch's cell raster (GLY9), because that is what the cells hold |
| D18 | A CRT assembled from the built-ins: tier 0 nested inside tier 1 | D7's capture, "a CRT · scanlines inside curvature" section | The swatch is barrel-warped and its scanlines bow with it — the raster is inside the bracket that warps it, so the composition order is visible, not asserted |
Tier 2 and the built-in ids (EFX11, EFX15)
| # | What it shows | Command | Expected |
|---|---|---|---|
| D19 | Tier 2 — a multi-pass effect declared as data — on the GPU, and its declared degradation in a terminal | xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR UIG_SHOT=/tmp/bloom.png ./apps/ui-gallery/build/ui-gallery --gui --page effects --window-width 90 --window-height 150; then dub run :ui-gallery -q -- --render --page effects | In the PNG, the "tier 2 · reads the layer" panel's text glows — a soft halo following each line, no dotted halftone. In the terminal the same panel is plain, with the note that a cell cannot see its neighbours |
| D20 | The built-ins are constants every registry resolves | dub test :ui -- -i "areConstantsInEveryRegistry" | Passes: a registry nobody touched resolves Builtin.scanlines … Builtin.bloom through a const pointer, and an app's first effect lands after the last built-in |
| # | What it shows | Command | Expected |
|---|---|---|---|
| D12 | The focus halo outlines what was actually painted (EFX23) | xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR HUE_GUI_SCREENSHOT=/tmp/halo.png HUE_GUI_PICKER= HUE_GUI_CRT=1 ./apps/hue/build/hue view README.md --gui | The halo sits tight on the file picker's border — from the rect the paint site recorded, with no second layout per frame |
| D13 | ui-app grew no post-process bracket (EFX22) | grep -rni -e postprocess -e postpass libs/ui-app/src | No matches |
| D21 | The CRT is an effect on the frame's root, toggled live (EFX21) | ./apps/hue/build/hue view README.md --gui, then press F12 twice | F12 curves the whole window into a tube — bloom, scanlines, the phosphor mask and the in-shader pointer — and F12 again returns the flat frame; nothing else in the window changes |
| D22 | A CRT capture is reproducible — the effect clock is pinned for it (DBG1) | for i in 1 2; do xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR HUE_GUI_CRT=1 HUE_GUI_SCREENSHOT=/tmp/crt$i.png ./apps/hue/build/hue view README.md --gui; done; cmp /tmp/crt1.png /tmp/crt2.png | cmp prints nothing: two captures of an animated effect are byte-identical |
| D23 | The CRT's UI context is harvested from what the frame painted (EFX23) | xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR HUE_GUI_CRT=1 HUE_GUI_SCREENSHOT=/tmp/split.png ./apps/hue/build/hue view . --gui | The divider's seam sits ON the hairline between the tree and the document, and the focused pane's halo hugs what that pane painted |
Regression guards worth running
| # | What it guards | Command | | --- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | D14 | The op stays inside its 64-byte budget (IMG1) | dub test :ui -- -i "ui.canvas" | | D15 | The scissor box clamps to its target, not to a sentinel that casts to int.min | dub test :ui-raylib -- -i "scissorBox" | | D16 | Every WidgetKind has a catalogue specimen | dub test :ui-gallery -- -i "primitivesCoversEveryWidgetKind" | | D24 | A component that does not compile says why (enforceAppFor) | dub build --single docs/specs/ui/demos/broken_app.d — the build fails ON PURPOSE: the first line is undefined identifier titel at the typo, then the trace to the assert. With isAppFor the same typo printed only the assert's sentence | | D25 | The gallery's capture hook, on the page that runs shells | xvfb-run -a env -u WAYLAND_DISPLAY -u XDG_RUNTIME_DIR UIG_SHOT=/tmp/term.png ./apps/ui-gallery/build/ui-gallery --gui --page terminal | Exits 0; the PNG shows three shell tabs and the scrolled-back output of seq 1 100 | | D26 | Multi-pass is data, and the CRT is one such record | dub test :ui -- -i "isAMultiPassTier2Effect" and dub test :ui-raylib -- -i "isATier2EffectOnTheRoot" | Both pass |