Skip to content

fix(runtime): preserve frame delivery and add the optional Obsidian theme - #146

Merged
guyverino merged 19 commits into
masterfrom
codex/render-audit-roots-2026-11-06
Nov 6, 2026
Merged

guyverino merged 19 commits into
masterfrom
codex/render-audit-roots-2026-11-06

Conversation

@Moonbot-Tech

@Moonbot-Tech Moonbot-Tech commented Nov 6, 2026 •

Copy link
Copy Markdown
Owner

Rendering could lose a requested follow-up frame, discard a pending presentation, or keep unchanged controls requesting work. This series fixes those shared runtime paths and adds the optional Obsidian theme through MoonUI's existing component engines.

The companion MoonTerminal PR #1267 consumes these fixes and adds native regression coverage. Each commit describes one change and its reason; please preserve the semantic commit history when merging this series.

Changes

  • Preserve invalidations raised during drawing and presentation debt until the native backend accepts a frame. Let unchanged Input/Resizable state settle instead of self-notifying indefinitely.
  • Submit retained canvas bakes before growing scene buffers; preserve the first wgpu validation error. Add debug hooks for native frame delivery and shared-device replacement, plus actual view-render counters.
  • Separate Wayland timer polling from compositor callbacks; deliver X11 events buffered during setup. Share user-close policy with client-side window controls.
  • Keep input context menus working inside popovers and across windows; dismiss the owning dropdown before opening a dialog; honor explicit single-line field height. Expose the existing text-area size argument through the public facade.
  • Synchronize side-dock activity when visibility changes, without disturbing the zoomed chart. Keep sixth-week calendar dates in their own month.
  • Add Obsidian palette/Facet styling while retaining the Flat path and existing themes. Reuse the existing Progress animation engine, preserve colored control variants, and keep snapshot identities separate for Dark, Light, Obsidian and LightFacet.
  • Document why raw wgpu handles require the same Cargo package identity in producer and consumer; the raw API remains intact.

Validation

  • Component guardrails pass: formatting, gallery check, 732 component tests, 13 gallery tests, bundled fonts, component audit/API/donor mirror checks.
  • GPUI runtime: 222 tests plus one integration test pass.
  • Integrated MoonTerminal Windows Clippy passes. Native dense FireTest passes with two independent charts, 100,000 generated trades, actual gutter pixels, two exact-pixel GPU recovery rounds, native F1 delivery, background-save failure handling and scoped panel updates.
  • Nineteen of twenty current pre-theme versus integrated Dark/Light gallery pages are byte-identical. The remaining difference is the hover frame of one segmented control. Ten Obsidian pages, affected overlay/control cases, and native Terminal transitions through Light/Graphite/Dark/Obsidian were inspected.
  • Independent Sol and Astra reviews completed; findings were repaired and rechecked.

Review boundaries

  • This is an opt-in theme; existing selections and stored colors are preserved. Gallery's historical PNG baseline predates the current gallery and fails its old comparison; it was not overwritten to conceal the mismatch. The comparison above uses separately preserved captures immediately before theme integration.
  • Linux X11 completes functional FireTest stages on llvmpipe, but fails the unchanged idle CPU limit. Wayland has completed functional runs, but also intermittently disconnects during the 5,000 Hz mouse storm on the nested Weston test host; that limitation remains open. Linux pixel readback is not covered by the DX11 oracle. Native Metal rendering and hardware Linux performance are not visually qualified by the Windows results.
  • MoonDockArea::set_dock_open / toggle_dock now receive the Window needed to deliver the existing panel activity callbacks; the intentional API change is recorded in the API baseline.
  • Merge MoonUI before the companion Terminal PR. The latter uses this public review branch so a clean checkout can build the pair before merge; return its dependencies to rolling master before merging Terminal. No release is published by these PRs.

Calendar selection lost the last days of months that require six weeks.
Flattening multiple months into five-row chunks also assigned later rows to
the wrong month. Generate complete Sunday-first weeks, then render each
month as its own column while preserving the existing five-row minimum.

Keep the Longbridge calendar lifecycle and record this intentional Mirror
to TrackedFork correction in the existing component migration ledger.
Move the existing tests to sibling modules and add real rendered selection
coverage for August 30/31 beside September in both Dark and Light themes.

Validation: all 624 component tests, gallery/font and component API/mirror
guardrails pass. The new grid coverage fails when five fixed rows return.
…ls settle

A notification from prepaint or paint could leave dirty views queued while
the common frame-entry predicate reported a clean window. A separate layout
model also lost its observer notification, so waking a frame alone could
not invalidate the cached view that consumed the measurement.

Treat queued views as draw debt everywhere and publish model notifications
through the existing deferred effect queue in every draw phase. This makes
previously hidden no-op notification loops observable; remove Input's
paint-time notify and make scroll, placeholder, loading, list search mode,
and resizable measured geometry notify only when their visible state changes.
No additional timer or parallel scheduling state is introduced.

Validation: mid-prepaint/paint and cached-model regressions pass; real Input
and Resizable forms settle after edits in Dark and Light. Native Windows
FireTest applies text/width without a rescue event, then observes two quiet
native callbacks with zero renders. Restoring the old dirty predicate or
Input paint notification independently makes that native probe fail.
…s it

Window cleared needs_present even when the backend skipped a recovering,
occluded, or unavailable surface. Without further input the valid scene
could then remain unpresented. Return acceptance from PlatformWindow::draw
and keep that existing debt armed until the native frame clock succeeds.

Carry the contract through DirectX, Metal, X11, Wayland, Web and the test
platform. Do not prepare canvas text while a window is unpresentable; the
restored frame prepares it once presentation becomes possible. Record input
latency only after an accepted present.

Validation: real frame-callback tests cover deferred draws, wake requests,
hidden canvas text and recovery without rescue input; the full 220-test
runtime suite and integration test pass. Windows FireTest covers native
clocks and recovery, and Hel1 covers X11/Wayland software surfaces. Metal
and Web have source parity here; native Metal validation remains a separate
platform check and is not claimed by the Windows run.
A canvas can mark its CPU bake clean after recording GPU commands. When a
later scene batch overflowed the instance buffer, dropping that encoder
lost the bake permanently: the larger retry saw clean CPU state and never
rebuilt the texture. Submit the already recorded commands before retrying.
The retry still clears the partial surface and only the final scene presents.

Apply the same ordering to wgpu and Metal, including CPU-to-GPU instance
buffer synchronization on non-unified Metal memory. DirectX submits its
immediate-context work already and needs no corresponding change.

Validation: an explicit native-surface regression forces an actual buffer
growth after a one-shot retained bake, then reads the final GPU pixel.
Hel1 produced the expected RGBA value; restoring encoder discard makes
that test fail. Metal has source parity; its native probe is still separate.
The fallback timer called frame(), which registered a new wl_surface frame
callback every tick. These requests remain pending until the compositor
answers, so a hidden or stalled surface accumulated callbacks indefinitely.

Keep one compositor chain: first configure starts it and each completed
callback replaces itself. Timer polls only drive GPUI and canvas decisions,
preserving the fallback wake that lets Skip become Present without input.
No new callback counter or parallel timer is introduced.

Validation: Linux build and full functional FireTest stages ran on the
Hel1 Weston session, including idle and device recovery. Software-renderer
CPU limits and intermittent diagnostic-client disconnects remain reported
separately; this commit does not claim to fix either unrelated observation.
Synchronous X11 setup can consume socket data into the client event buffer.
Calloop then sees no readable socket, so a newly mapped window can stay
blank until unrelated input. Schedule one idle drain after GPUI installs
the window callbacks and explicitly wake calloop so that drain runs.

This uses the existing event processor, not another polling loop.
Hel1 mutation qualification reproduced a blank startup and missing chart
probe when this block was removed; restoring it delivered the initial
frame and passed the functional X11 FireTest stages without rescue input.
…oint

FireTest needs to distinguish an unchanged view that really rebuilt from
a notification or reuse of its cached scene. Add opt-in per-App counting
where the concrete Render implementation is invoked, so application code
does not need a manually maintained list of instrumented panels.

The default path performs only an Option check: no clock, allocation, hash
or synchronization. Sampling drains the interval; disabling drops it so
old samples cannot leak across diagnostic runs. Independent apps do not
share the counters.

Validation: runtime regressions distinguish cached child reuse from parent
renders and cover enable, repeated enable, drain, disable and re-enable.
The terminal native idle and form probes consume these actual counts.
Keep the borrowed raw callback API: it intentionally lets the terminal
render inside MoonUI's native pass without another abstraction or copy.
Document its real safety boundary instead. These are pointers to Rust
objects, not a stable C ABI, so producer and consumer must resolve the
same wgpu Cargo package source and version.

The companion terminal change replaces its separate git-sourced wgpu with
the registry package already used by MoonUI. Matching version text alone
would not make two distinct Cargo package identities interchangeable.
Later draw submissions can replace a useful shader validation diagnostic
with a generic invalid-pipeline error. Keep the first uncaptured error
until the existing consumer drains it, retaining the actual failure cause
without adding a separate error queue or changing recovery behavior.

This was exercised while diagnosing the terminal's Linux candle shader;
the original validation reason now survives the later invalid submissions.
FireTest must prove that retained charts return after their GPU resources
are destroyed, without depending on a driver crash or a system-wide TDR.
Expose a diagnostic request through Window and PlatformWindow. Windows
uses its existing VSync device owner; X11 and Wayland destroy the shared
wgpu device and let the ordinary native draw path recover it. Refresh all
windows sharing the device. Unsupported platforms explicitly return false.

Preserve the existing adapter policy during recovery: a session already
using a CPU adapter can recreate that class, while a hardware session
still refuses a silent fallback to software. This makes recovery testable
on the Hel1 software lab without weakening hardware-session behavior.

Validation: the terminal FireTest performs two controlled losses with no
new market data, checks retained history and generations on Windows and
Linux, and reads exact restored own-pass image layers on DirectX. This is
not a driver-hang/TDR or a native Metal recovery test.
The app-global set of open deferred popovers disabled every Input context
menu whenever any popover was open. The renderer already supports nested
deferred overlays, so that guard was both obsolete and broader than the
window or control it was meant to protect.

Remove the guard and its registry, including Select/Combobox/Popover writes.
Visibility remains with each existing owner; Root keeps overlay placement
and dismissal. No replacement global state or native popup window is added.

Validation: real right-click events reach the Input menu inside a popover
and in another window, and the nested Root menu survives actual prepaint
with non-empty bounds in both Dark and Light themes. The full component
suite and final guardrails pass with the companion focus changes.
A row callback could open a modal while the menu still owned focus. The
later menu close then either stole the modal's focus or left its return
target on a removed menu. Route selection through the actual PopoverState
and dismiss it before invoking the row action or selection callback.

Popover close snapshots its descendant/tracked focus before content leaves
the dispatch tree. Deferred restoration runs only while that exact focus
is still owned; it never steals focus already transferred elsewhere.
This removes the duplicate dropdown close plan and parallel open-state
updates rather than adding another focus-restoration mechanism.

Validation: real Root dialog/menu flows in Dark and Light cover opening a
modal, a menu inside that modal, return to the original trigger, tracked
and descendant focus, no previous focus, and deliberate transfer elsewhere.
Existing selection behavior tests remain; removed policy-only assertions
are replaced by lifecycle tests that fail when close ordering is reversed.
Update the existing fork reasons and byte/hash ledger for the component
fixes in this series: convergent Input/List/Resizable invalidation and local
popover ownership supporting nested input menus. Keep the original donor
references and audit baseline; do not broaden drift budgets or conceal
unrelated upstream differences.

The calendar class migration is recorded with its own behavior commit.
These records describe the final component source already covered by the
runtime and component regressions, so future donor updates can distinguish
intentional fixes from accidental drift. Full component API, mirror and
audit guardrails pass against this exact snapshot.
MoonInput Custom metrics supplied a height, but the shared Input applied it only in multiline mode. A 19px field became 22px with the terminal font delta, breaking the wrapper geometry contract.

Apply the existing optional height after the multiline auto-height choice. Preserve default sizing and update only the Input/TextArea donor-drift baseline.

Regression: measure unconstrained real GPUI layout for Compact, Small, Normal, Custom and minimum-height clamping in Dark and Light. The old code fails at 22px versus 19px; the fix passes on Windows and Linux. MoonUI guardrails and terminal workspace checks pass.
MoonTextArea::size accepted MoonTextAreaSize from a private module, so downstream users could not name the supported variants. Re-export the existing enum through the Moon facade and record the exact public signature.

No new wrapper or sizing behavior is introduced. Validated with MoonUI component/gallery/API guardrails and the full terminal workspace.
The Linux MoonWindowFrame close button removed its window directly, bypassing the native should-close callback. Applications could neither veto that close nor distinguish it from owner teardown.

Retain one replaceable policy on Window and expose request_close for client-drawn controls. Native close uses the same policy; remove_window remains an unconditional teardown operation. Preserve the existing callback registration API and release its borrow before invoking user code.

Real GPUI test windows verify veto, callback replacement, accepted/default close, forced removal and the FrameButton path on Windows and Linux. Native compositor click/lifecycle qualification remains separate; no macOS or Linux visual proof is claimed by these tests.
A downstream retained chart must exercise prepare_text inside the real window frame callback to detect an already-served frame being requested again. Expose the existing test-window callback through VisualTestContext and the test-support feature instead of constructing an artificial public text context.

Production behavior is unchanged. Validated by the terminal readout RED/GREEN regression and the full MoonUI runtime/component suites.
Closing a side root stopped rendering its subtree but left its selected panel active. That hidden subtree could no longer deliver its own deactivation. This is a library API defect; MoonTerminal currently does not call these public side-toggle methods.

Pass Window through set_dock_open and toggle_dock so the existing Panel lifecycle can run at the visibility transition. Synchronize only the changed side subtree and preserve its selected-tab runtime. A currently valid zoom keeps sole visibility and receives no activity callbacks when an unrelated side flag changes. No new activity registry, timer or retained state is introduced.

API change: both methods now require the host Window, matching the lifecycle operations they perform. Update their two recorded public signatures; no in-repository production caller requires migration.

Validation: a real GPUI window/probe regression fails with the old active panel after close. An independently reviewed broader-sync variant also fails with four extra zoom callbacks. The final implementation passes close/reopen, no-op, selected-index and zoom-preservation checks. MoonUI full guardrails pass (647 component tests, 12 gallery tests); terminal workspace Clippy passes. Astra reviewed the final correction.
Add a selectable dark Obsidian palette and a palette-driven Facet skin for Moon
components. Keep existing Flat themes, their default values, layout geometry,
interaction owners and persistence identity unchanged. Adapt the existing
button/input/select/progress/dock engines instead of duplicating their lifecycle.

Preserve colored dropdown variants and use one donor Progress animation engine
with track/fill paint hooks. Keep snapshot theme identity distinct from dark/light
mode, so Obsidian cannot overwrite Dark captures. Preserve the custom single-line
input-height fix and its existing regression when integrating the author's tests.

Validation: 732 component tests, 13 gallery tests, bundled fonts, formatting and
component audit/API/donor mirror checks pass. Nineteen of twenty native Dark/Light
pages match pre-integration PNGs byte-for-byte; the remaining difference is one
hovered segment. Ten Obsidian pages and native Obsidian -> Light -> Dark switching
were inspected. Historical repository PNGs predate the current gallery and were
not rewritten to hide their mismatch. Independent Sol/Astra reviews completed.
@guyverino
guyverino merged commit 15871b8 into master Nov 6, 2026
4 checks passed
guyverino added a commit to Moonbot-Tech/MoonTerminal that referenced this pull request Nov 6, 2026
Moonbot-Tech/MoonUI#146 is on master, so the review branch reference goes.
The lock's MoonUI rows stay stale by design: CI and the release refresh them
to master. The only lock change left is dropping the zed wgpu fork.
guyverino pushed a commit to Moonbot-Tech/MoonTerminal that referenced this pull request Nov 6, 2026
…saves and Obsidian (#1267)

Keep chart geometry off the price and time gutters, replay retained layers after a GPU replacement, present the final throttled book update, move recurring saves to one background writer, preserve unreadable config originals, give status, market and catalog changes their own notifications, pin COIN-M knowledge to each money batch, and add the selectable Obsidian theme.

Pairs with Moonbot-Tech/MoonUI#146. Refs #1190.
@guyverino
guyverino deleted the codex/render-audit-roots-2026-11-06 branch November 6, 2026 21:33
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.

2 participants