* E37: migrate GridCraft to egui 0.37 Pin the reviewed fork and shared dependencies, adapt input and rendering to the new frame lifecycle, and preserve bounded capture handling and font notices. Add representative accessibility checks and meaningful control labels. Validation: full repository CI passed all ten stages, with 2,249 tests passed, zero failed and one existing timing test ignored. Native workflows and paired performance qualification remain documented separately. * egui037: record final published pins and local qualification * M0.37: fix RTL clipping and preserve screenshot budgets * test: share migrated GridCraft UI harness setup * egui037: document migrated UI and regression contracts * egui-037: align popup test fonts and avoid redundant tooltip probes
17 KiB
egui 0.37 local port — follow-ups and gates
Status: local implementation on GridCraft b8771d51fb4da60bd48c13cf313d6d6014dc5e66 with canonical
immutable dependency pins. Final managed full CI
egui037-gridcraft-candidate-20261024-full-ci-queue-07 succeeded at 2026-10-24 05:21 UTC after both
accessibility repairs: all ten stages passed, with 2,249 tests passed, none failed and one existing
release timing experiment ignored. The representative explicit accessibility/capture checks
passed. The required local precommit CI gate is complete for the formatted runtime source;
this final refresh changes only the two migration documents. Native/platform/performance
acceptance remains separate. No code
push or PR publication is authorized; each requires fresh human approval of contents and validation.
Current integration inputs
- Root Cargo patches select the egui family at
173cc7375d35dc5fbc5b5d0994fdf787f547c5d1and winit atae48c756a3b81125eb796c0cc6dccf60990d1181. Selected egui packages are 0.37.0; there is no machine-local path override or second egui 0.36 source. craft-uiandcraft-printboth pinc488caa9fbc2fb1a86e4674887e9b80b598ebbfa. The shared capture slot is consumed through that revision. Canonical Cargo.lock SHA256 is98209e84412b8ce08bf290ab706c20909389ca6d1e6668aca929f717ad55ea2f.- CI, release and FreeBSD select craft-fonts
b076417b5880bd4154bd22f00695b1d28c6bea83. Local validation uses command-scoped private Git mirrors for unpublished immutable inputs; clean remote fetching after approved publication remains a separate gate. - The shared capture contract fits GridCraft's requirements without changes: one slot per request,
take()at most once,cancel()/drop makes late callbacks inert, a per-image byte bound, and a viewport waker that runs after the slot lock is released. GridCraft adds only its admission budget, request records, geometry epoch and output routing around it. - The
CaptureSlotcallback holds only a weak reference and never encodes, writes a file or sends a control reply; GridCraft drains the slot fromlogicand writes outside the slot.
Capture lifecycle repair (2026-10-23)
The previous managed run of this checkout (egui037-gridcraft-candidate-20261023-tests-01)
finished with 154 passed and 8 failed: five screenshot/lifecycle tests dropped an unapplied
TexturesDelta, two more lifecycle tests failed on the same atlas delta, and
formula_bar::spill_tests::formula_bar_click_and_theme_presentation reported the Name Box
TextInput without an accessible name. Those failures are historical; final full CI07 passed the repaired lifecycle and
accessibility tests.
The local source now addresses the fresh review's capture findings:
- A completion that arrives after the request deadline is refused with the timeout error before
any encoding or file write; timeouts, geometry mismatches, oversize images and removed
requests all release their reservation. Every screenshot reply carries its
requesttoken. - Screenshot deadlines and AutoSave pacing use a monotonic clock (
std::time::Instantnatively,performance.now()on wasm).now_ms()stays the wall clock for user-visible timestamps such as Backstage's "last changed". CaptureGeometry::currentreadsViewportInfo(refreshed byrun_logic) for the viewportinner_rectandnative_pixels_per_point * zoom, falling back to the last screen rect; a hidden resize is therefore seen without a UI pass. Issuance and drain update the geometry and its epoch through one helper, and the slot is created at issuance with a per-image bound equal to the aggregate reservation (which includes the accepted one-pixel rounding).- A request admitted while the window has no size (minimized or not laid out) reserves
UNKNOWN_CAPTURE_BYTESand waits for a real size; it times out with a size-specific message. Drain runs before admission, and settling counts one non-discarded egui pass per request. - The geometry identity intentionally includes
zoom,visibleandoccludedeven when the expected physical size is unchanged. A capture is bound to one layout generation; the two-retry budget followed by an explicit error is the accepted bounded failure, not a reason to compare only the expected size. - A synthetic step consumed by a font-gated
uiearly return is offered again on the next pass (the hook tracks thecumulative_pass_nrat injection), and font-set changes wait while an input step is pending. Re-offering the step is safe: the early-returning pass handled no input, so egui's state changes from those events (keys down, pointer down) have not applied a step. - The Name Box and the formula-bar/in-cell editors carry explicit accessible names, so the strict kittest accessibility check passes without disabling it or the missing-glyph policy.
Shared eframe web follow-up: RequestPaintWhileHidden
Native egui-winit handles ActionRequested::wants_paint() for RequestPaintWhileHidden and
paints the hidden viewport. The pinned eframe web runner does not: a hidden tab runs only
run_logic, and handle_viewport_commands handles Screenshot/SetTheme while every other
command — including RequestPaintWhileHidden — falls into its unhandled-command log. A queued
ui.screenshot or a queued ui.key/ui.text step in a hidden browser tab therefore cannot reach
its ui pass until the tab is visible; the screenshot request reports its timeout instead. The
fix belongs in the shared eframe web runner (paint a hidden tab on request, with the same
queued-work trigger GridCraft sends from logic on native), not in a GridCraft-local channel.
GridCraft therefore sends RequestPaintWhileHidden only off wasm (a visible web tab still gets
request_repaint). The control protocol documents this web limitation instead of claiming hidden
web screenshots work.
E21b — application qualification remains open
The final canonical egui fork includes IMEOutput::surrounding_text, owner/context generation
and the Wayland outgoing adapter; the earlier statement that no shared producer/adapter exists is
superseded. GridCraft must qualify its actual formula-bar, in-cell and text-box editing paths,
including available-empty versus unavailable, stale epoch/serial deletion and commit, deletion-only
batches, composition ownership and two-surface isolation. Full CI07 is not a native IME or complete
E21b/W27 acceptance result. No application-local API stub is introduced.
Review repair (2026-10-24)
RequestPaintWhileHiddenis sent only for a queued/settling request that has a capturable size; an issued capture relies on theScreenshotcommand'swants_paint, and a minimized request no longer forces full hidden frames while it waits.- A slot-level per-image refusal (an image larger than the reservation) now spends the same bounded retry as a smaller image, instead of failing only when the window grew.
- The shared deterministic harness, the
Text Boxaccessible-name translations, the explicitPasteImage/MiddleClickPasteno-op choice, the lifecycle once-per-host-frame tests (autosave, title, print poll, discard after issuance) and wheel unit/source/phase coverage are in the working tree. Final full CI07 exercised these repairs and the explicit accessibility assertions below successfully.
Remaining acceptance gates
- Required local CI is complete: full CI07 passed i18n-report, fmt, strict workspace all-target
clippy, workspace tests, doctest, assets (34 attributed files), layers, WASM, parity and keytips.
The existing timing-sensitive
recalc_with_range_formulas_scales_linearlytest remains ignored by the normal CI command; it is not a performance result. No additional CI execution is claimed for this documentation-only refresh. - Strict
egui_kittestharness qualification completed for the selected end states:tests/common/mod.rsnow installstheme::pinned_font_definitions()(bundled faces plus the pinned craft-fonts input, no host system scan),SheetApp::use_prepared_fonts()keeps that set through the firstlogicpass, andMissingGlyphPolicy::Panicstays on. Sincerun_stepsdoes not invoke kittest's accessibility checker, selected ribbon, locale, pane/menu, dialog, active-editor, backstage, text-box and capture/repaint end states now callcheck_accessibility()explicitly; these assertions passed in CI07, without claiming coverage of every UI state.CRAFT_FONTS_DIR(andCRAFT_FONTS_REQUIRED=1for acceptance) is the CI contract; without it the deterministic bundled set is used and the all-catalog language test records theJa/Ko/Zhgap. - End-to-end hidden/occluded
ui.screenshotwith a capturable current size and a minimized request that waits for restore (or times out), through a real wgpu/kittest or native window; the per-image/aggregate budget stress cases, resize-back/DPI/zoom epoch retries, and native clipboard/PRIMARY/IME evidence. No native hidden/minimize/restore run has been performed for this source. - Native Linux, Windows clone, browser and macOS workflows; performance, text-tessellation, Windows renderer-construction and print measurements against the recorded baseline.
- Accessibility roles/names beyond the mechanical
Roleport (G3) and native screen-reader smoke tests.
Font provisioning (2026-10-23, font-coverage integration)
The font gap is addressed in the font set itself. Test harnesses keep
MissingGlyphPolicy::Panic and the enabled accessibility-check policy; the representative explicit
checks above are needed to execute that policy on run_steps fixtures. No test calls allow_missing_glyphs()
or disables the accessibility check, and production still renders Tofu for a character no face
has.
gridcraft-ui-eguienablesegui/default_fontsandegui/monochrome_emoji_fonts. egui 0.37 moved Noto Emoji and emoji-icon-font behind the monochrome feature, and withdefault_fontsalone no installed face has✕or📋(Hack, which the theme adds as a fallback, has the arrows and triangles but neither of those).theme::font_definitions_withappends egui's bundledHackface (arrows, triangles,↳) behind the UI/cell and emoji faces of every family, and appends the optional craft-fonts faces at the very end. The order is: app/bundled faces, Hack/emoji, system script fallbacks, permitted craft faces last. A craft face never displaces a system face that already has the glyph.crates/ui-egui/build.rsembeds the faces named byCRAFT_FONTS_DIRthroughcrates/ui-egui/src/craft_fonts_policy.rs, the single policy shared by the build script and the tests. Nothing reads a fixture path from Cargo configuration: the checked.cargo/config.tomlholds only thextaskalias, so every local and managed run sets the variable explicitly. With it unset the generated table is empty and the app builds, tests and runs; a script that no installed system face and no bundled face covers stays the platform gap it was before (Tofu in production, and the strict kittest harness still panics on it by design).- Required faces, matched by exact family and style and by their exact manifest script tags:
BIZ UDPGothicRegularandBold(Jpan,Latn),NanumGothicRegular(Hang,Kore),Noto Sans SymbolsRegular(Zsym) andNoto Sans Symbols 2Regular(Zsym).NanumGothichas no space, andNoto Sans Symbolsis notNoto Sans Symbols 2. CRAFT_FONTS_REQUIRED=1turns a missingCRAFT_FONTS_DIR, a missingfonts/manifest.txt, a malformed manifest row, a missing font file, an unexpected style of a required family or any missing required face into a build error.- Barred families never enter the font set, although the shared repository keeps them for other
consumers:
Adobe*,Source Han*,Source Sans*,Source Serif*,Noto Sans CJK*andNoto Serif CJK*. There is deliberately no broadNoto Sansprefix, because it would also reject the permittedNoto Sans Symbolsfaces. - Web (
wasm32) embeds only the explicit allowlist:BIZ UDPGothicRegularandBold,NanumGothicRegular,Noto Sans SymbolsRegularandNoto Sans Symbols 2Regular; no CJK collection and no document-only face. Those five files are 12,970,840 bytes raw. Raw font bytes, raw wasm and compressed wasm are not measured yet: the size gate is a parent acceptance item, not a claim made here. - Native builds opt into the OFL
LXGW WenKai Liteface from craft-fonts'fonts/simplified-chinese-manifest.txt(the same face DeckCraft uses). The barredNoto Sans CJKcollection still never enters; system Han faces still lead when installed.CRAFT_FONTS_REQUIRED=1native builds require the Chinese manifest, ordinary builds tolerate an older checkout without it, andWEB_ALLOWEDexcludes it. The Chinese samples join the strict script test only when the face is embedded. - Native also embeds every other non-barred manifest row (for example
BIZ UDMincho,Shippori Mincho,Noto Sans Arabic/Georgian/Hebrew/Thai). They are OFL and asset-policy safe, but the actual set is wider than the five required faces; the size gate must measure it rather than citing only the web allowlist. - No-font/default mode: the bundled faces supply
↘ ◀ ↑ 📋and the rest of the interface's symbols, whichtests/symbol_glyphs.rsasserts strictly.✕,✓and⌃have no bundled egui face, so chrome draws them instead of painting them: the Symbol gallery and the close/check controls paint the code-drawnIcon::Check/Icon::Close(icons::fallback_for_symbol,widgets::glyph_button), and a tooltip whose symbol is missing falls back to^(theme::symbol_fallbackthroughtheme::available_text). Only workbook text still needs a real glyph, because its characters are the user's data. The test documents✓/⌃as optional-input gaps rather than claiming them. - Extended acceptance mode (
CRAFT_FONTS_DIRset, andCRAFT_FONTS_REQUIRED=1for release):tests/symbol_glyphs.rsrequires the exact union↘ ✕ ◀ ↑ 📋 ✓ ⌃in bothProportionalandCELLplus the Korean interface labels, and fails instead of skipping when a loaded or required set lacks a face.⌃comes fromNoto Sans Symbols,✕ ◀ ✓fromNoto Sans Symbols 2,↘/↑from either Symbols face (NanumGothic also has them), and📋from egui's emoji faces. - The required faces live in the pinned
craft-fontscheckout atb076417b5880bd4154bd22f00695b1d28c6bea83. PointCRAFT_FONTS_DIRat that checkout: the olderfixtures/craft-fontssnapshot has no Korean or symbol rows, and a loaded set without the required faces fails the extended checks by design. theme::fallback_covers_its_scriptis strict whenever a system or craft-fonts face provides the script, now including Arabic and Georgian; its skip prints the script name and sample.- The production positive-rectangle fix in
formula_bar.rs, theRole::DisclosureTrianglequeries, the linear per-line editor-height assertions and the explicit accessible names for the Name Box and both editors are unchanged.
Explicit G1 accessibility repair — 2026-10-24
Seven existing integration tests now check rendered ribbon/light-dark, each supported locale, pivot pane and open menu, fifteen dialogs, active cell/formula edits, backstage pages and active text-box/committed states. The existing capture callback test now uses a full SheetApp Harness, asserts the Name Box tree is present, and checks accessibility before issuance and after callback drain/repaint. Its manually supplied pixels still test callback lifecycle rather than GPU readback; the explicit no-GPU renderer prevents kittest from silently turning it into a platform test. One-callback, request-token, one-reply and released-reservation assertions remain.
All eight affected tests (seven integration tests and the capture unit test) passed in full CI07. The four CI06 failures exposed unnamed Options/ribbon ComboBoxes, custom format input and pivot layout control; visible localized labels or precise semantic names now identify them. The fresh source review found no blocker; CI07's formatting changed only wrapping/trailing commas in those four reviewed production files. Original roles, input sequences and behavior assertions remain. Full CI05 is historical pre-repair evidence and CI06 remains a failed run.
Actual native screenshots/hidden/minimize/restore, screen-reader output, application E21b IME and
platform/performance acceptance remain open. Full CI07 uses the canonical lock, required craft-fonts
input, command-scoped exact-source mirrors and the private Vulkan loader; it is not host-loader or
clean public-fetch qualification. Exact job/log hashes, review union, complete final source inventory
and documentation-only delta are preserved in local task evidence under
egui037-migration-20261022/final-landing/gridcraft-final-qualification.
Revision: 2026-10-24 — replaced pending accessibility/CI statements with actual full CI07 results, corrected the shared outgoing IME inventory, and retained native/performance/publication gates.