Stage 1 of #47 (native macOS GUI), split out because it is the only stage this fleet can build and verify. #47 stays open as the tracking issue; stages 2–4 (the macOS wrapper itself, MacDriver) need an Apple SDK / objc2 toolchain that no machine in this fleet has.
Blocked on quadraui#699 (Backend Rc-handle symmetry) and on the pin bump in #767 — both must land first.
What
Move struct App, its impl App blocks and impl quadraui::ShellApp for App out of src/gtk/mod.rs into a backend-neutral src/app.rs, leaving src/gtk/mod.rs with only run(), build_shell_config() and genuinely GTK-only helpers.
This is the concrete next step of the north star in GOALS.md: the backends shrink to thin event-to-engine wiring. src/gtk/mod.rs is 7,684 production lines — 38% of both backends' remaining mass — and the 2026-09-03 audit found it is already mostly portable.
Why it is mostly mechanical
From the #47 re-audit (44882e9, full text in PLAN.md → "#47 re-audit findings"):
- Of the ~6,700 lines spanning
struct App → impl ShellApp for App, only ~40 lines touch gtk4:: / gio:: / pangocairo:: / glib:: directly.
map_gtk_key_name, gtk_key_name_to_quadraui, map_gtk_key_with_unicode look GTK-specific but take and return plain &str / quadraui::UiEvent — zero gtk4 dependency. They move verbatim.
setup_gtk_clipboard is already #[cfg]-branched for target_os = "macos" internally and never touches gtk4. Moves verbatim.
The four platform-typed fields
| Field |
Type |
Disposition |
settings_monitor |
Option<gio::FileMonitor> |
#[cfg(feature = "gui")], ~1 call site |
window |
Option<gtk4::Window> |
#[cfg(feature = "gui")], ~15 call sites, all window-chrome (resize/maximize/CSD) |
css_provider |
Option<gtk4::CssProvider> |
#[cfg(feature = "gui")], ~3 call sites, colorscheme reload |
backend |
Rc<RefCell<GtkBackend>> |
Switch to whatever shape quadraui#699 lands on — this is the blocker |
Re-derive all of these at pickup; src/gtk/mod.rs churns heavily and the counts above were measured at 44882e9.
Files
src/gtk/mod.rs
src/app.rs (new)
src/lib.rs
Acceptance
cargo build --no-default-features and cargo build --features gui both green.
cargo test --features gui,test-support green — in particular the GtkDriver black-box suite in src/gtk/testing.rs, which exercises the moved dispatch paths through production code.
src/gtk/mod.rs production line count drops materially; state the before/after using python3 scripts/prod_lines.py src/gtk src/tui_main src/render.rs in the PR.
- No new
#[cfg(feature = "gui")] inside src/app.rs beyond the three fields named above — if more is needed, that is a finding to report, not to route around (Platform-Neutrality Rule).
Not in scope
src/macos/, MacDriver, anything requiring an Apple toolchain. Pure refactor: no behaviour change, so no new black-box test is required — say so explicitly in the PR per CLAUDE.md's testing bar.
Stage 1 of #47 (native macOS GUI), split out because it is the only stage this fleet can build and verify. #47 stays open as the tracking issue; stages 2–4 (the macOS wrapper itself,
MacDriver) need an Apple SDK /objc2toolchain that no machine in this fleet has.Blocked on quadraui#699 (Backend Rc-handle symmetry) and on the pin bump in #767 — both must land first.
What
Move
struct App, itsimpl Appblocks andimpl quadraui::ShellApp for Appout ofsrc/gtk/mod.rsinto a backend-neutralsrc/app.rs, leavingsrc/gtk/mod.rswith onlyrun(),build_shell_config()and genuinely GTK-only helpers.This is the concrete next step of the north star in
GOALS.md: the backends shrink to thin event-to-engine wiring.src/gtk/mod.rsis 7,684 production lines — 38% of both backends' remaining mass — and the 2026-09-03 audit found it is already mostly portable.Why it is mostly mechanical
From the #47 re-audit (
44882e9, full text inPLAN.md→ "#47 re-audit findings"):struct App→impl ShellApp for App, only ~40 lines touchgtk4::/gio::/pangocairo::/glib::directly.map_gtk_key_name,gtk_key_name_to_quadraui,map_gtk_key_with_unicodelook GTK-specific but take and return plain&str/quadraui::UiEvent— zerogtk4dependency. They move verbatim.setup_gtk_clipboardis already#[cfg]-branched fortarget_os = "macos"internally and never touchesgtk4. Moves verbatim.The four platform-typed fields
settings_monitorOption<gio::FileMonitor>#[cfg(feature = "gui")], ~1 call sitewindowOption<gtk4::Window>#[cfg(feature = "gui")], ~15 call sites, all window-chrome (resize/maximize/CSD)css_providerOption<gtk4::CssProvider>#[cfg(feature = "gui")], ~3 call sites, colorscheme reloadbackendRc<RefCell<GtkBackend>>Re-derive all of these at pickup;
src/gtk/mod.rschurns heavily and the counts above were measured at44882e9.Files
src/gtk/mod.rssrc/app.rs(new)src/lib.rsAcceptance
cargo build --no-default-featuresandcargo build --features guiboth green.cargo test --features gui,test-supportgreen — in particular theGtkDriverblack-box suite insrc/gtk/testing.rs, which exercises the moved dispatch paths through production code.src/gtk/mod.rsproduction line count drops materially; state the before/after usingpython3 scripts/prod_lines.py src/gtk src/tui_main src/render.rsin the PR.#[cfg(feature = "gui")]insidesrc/app.rsbeyond the three fields named above — if more is needed, that is a finding to report, not to route around (Platform-Neutrality Rule).Not in scope
src/macos/,MacDriver, anything requiring an Apple toolchain. Pure refactor: no behaviour change, so no new black-box test is required — say so explicitly in the PR perCLAUDE.md's testing bar.