One-page hand-verification list for the Chinese IME gate: preedit anchoring, candidate window tracking, commit/cancel/backspace paths, scrolling during composition, TUI anchoring, focus/tab/DPI interactions. Any failing item vetoes G2.
Final form pinned per product decision: one product and one codebase (nebula.exe / nebula_app) with GPUI as its in-process UI layer; nebula_gpui is a validation lab whose proven modules get integrated into nebula_app, never a second product. Grid rendering stays first-party via the render contract; powerline/fastfetch/CJK fidelity are acceptance gates. Mega-file split (display/mod.rs 10k+ lines etc.) is scheduled as a dedicated refactor pass after GPUI integration.
Port nebula_app's builtin_font coverage (box drawing, block elements, legacy sextants/blocks, Powerline separators) from pixel rasterization to renderer-agnostic fill primitives: axis-aligned rects plus convex polygons, snapped to device pixels so strokes stay crisp and join seamlessly across cells. RenderSnapshot::capture now routes these cells to box_glyphs instead of text segments - the typography engine never touches them, which roots out the box-line misalignment CJK fonts cause. The GPUI element consumes the primitives via paint_quad/paint_path (cursor inversion rules unchanged). Known deltas vs the legacy rasterizer: narrow-cell Powerline tips clip instead of falling back to the font, and shade blocks use alpha 64/128/192. Engine tests: 202 passing (11 new).
Per product decision: once GPUI genuinely beats the hand-drawn UI for a surface, it becomes that surface's code path outright - no runtime switches or long-lived feature flags keeping two UI paths alive. Rollback = the winit shell and existing modules staying buildable in-tree, plus per-step commits to revert. The gpui-shell cargo feature is spike scaffolding slated for removal, not a rollback mechanism.
nebula_gpui now exposes run_shell() as a library entry so nebula.exe can
host the GPUI UI layer on a dedicated thread (route C: one program, GPUI
provides the component layer). The standalone bin remains for development.
Off by default; enabled via the gpui-shell feature + NEBULA_GPUI_SHELL=1.
Layering: Term (model) -> render contract -> frontend backend.
- CellMetrics + TerminalViewport: floor division, minimum grid, exact
text-area pixels (cols x cell_width) for PTY/app reporting.
- ViewportTracker: resize coalescing, monotonic revisions (a stale resize
can never override a newer one), and pixel-only changes still reported
to the PTY even when rows/cols are unchanged.
- RenderSnapshot: single-lock plain-data capture (bg runs, selection runs,
wide-aware text segments, cursor, OSC color-override copy). Colors stay
as vte values; frontends resolve them outside the lock.
- gpui element/view now consume the contract: grid math single-sourced in
the tracker, cursor-cell isolation for block-cursor inversion. Swapping
the paint backend later touches element.rs only.
- 6 contract tests (segmentation, cursor isolation, viewport rules).
- crossfont 0.8.1 -> 0.9.0: upstream bumped yeslogic-fontconfig-sys to ^6.0,
aligning with gpui's zed-font-kit so a single 6.0.1 owns links=fontconfig.
- wayland CSD switches to the ab_glyph flavor so sctk-adwaita 0.10 stops
pulling crossfont ^0.8 (and yeslogic ^5) back into the graph. Linux-only
decoration text; Windows/macOS unaffected.
- Drop the workspace exclude and nebula_gpui's member-level lockfile; one
lockfile now holds gpui 0.2.2 + crossfont 0.9.0. Both bins compile.
- ARCHITECTURE.md: the "unresolvable conflict" section now records the fix.
TerminalView now emits TitleChanged/Exited and owns session teardown
(Msg::Shutdown on removal, Drop as backstop). TerminalPanel adapts the
terminal to the dock Panel contract with a title_suffix close button,
since a root-level TabPanel never renders its own. NebulaWorkspace
wires TitleBar + tabs with ctrl-shift-t/w hotkeys (passed through the
terminal key handler), closes tabs on shell exit, and quits when the
last tab closes. The component gallery moves to a secondary window.
ConPTY + EventLoop wired through a futures channel into a GPUI entity;
custom Element paints the grid from a single-lock snapshot (bg runs,
force_width text segments for CJK alignment, cursor, selection); view
handles keyboard encoding, mouse selection, wheel scroll, clipboard,
and IME via EntityInputHandler. No fork of either side; boundary stays
symmetric (no GPUI types in nebula_terminal, no terminal types outside
src/terminal).