git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit1dde836c4da697d082ed5e663098fd0494cbebe8
parent2a160b9f0f
authorLucas Galante <[email protected]>
date2026-08-25 12:49
rfc: Phase 7 (PROPOSED) — plate unification, backplate becomes a role

Backplate survives 6as/6at only as vocabulary (the style.surface.backplate.*
namespace + getters) and as a role: the plate that meets the window edge.
Phase 7 proposes finishing the merge — PlateSpec with window-corner and
drag-background role flags, the pane_plate_radii window-corner math moved
toolkit-side, one paint path for root and nested plates, and the designer's
detach/dock machinery generalized over it. Three stages (config alias, spec
+ engine paint, detach lift), with the silhouette-stays-shared and
blur-regime invariants called out. Not started.

 docs/rfc-core-rebuild.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/docs/rfc-core-rebuild.md b/docs/rfc-core-rebuild.md
index 9c5316b..762db63 100644
--- a/docs/rfc-core-rebuild.md
+++ b/docs/rfc-core-rebuild.md
@@ -2347,6 +2347,69 @@ Constraint respected: **each crate still builds standalone** — the new core is
     phase-4 flip reaches them first; no standalone pointer-to-id
     signature sweep.
 
+- **Phase 7 — Plate unification: backplate becomes a ROLE of Plate (PROPOSED, not started).**
+  Finish what 6as/6at began. The `Backplate` and `Plate` container widgets are deleted and the
+  `is_backplate`/`is_movable_backplate`/`is_plate` flags are folded, but "backplate" survives as
+  a second vocabulary for what is now one concept — a lit base surface (`Prim::Plate`). What
+  remains under the old name: the `style.surface.backplate.*` config namespace and its getters
+  (`backplate_{padding,gap,color,blur,corner_radius}`, the menubar/statusbar sub-styles); a
+  partial merge already in the tree (`layout::plate_corner_radius()` falls back to
+  `backplate_corner_radius`); and — the real content — a ROLE: "the plate that meets the window
+  edge" (window-background drag via `blocks_backplate_drag`/`drag_allowed_at`, MenuBar/StatusBar
+  carving into it, the compositor clipping every window at the span-widened backplate radius).
+  Each app also hand-rolls its root-surface painting from the backplate getters (`DemoApp`
+  in `src/main.rs` is the reference copy; every client repeats a variant).
+
+  **Motivation.** One surface concept instead of two makes plates fully compositional: a plate
+  can be the base surface of a window OR a child of another surface, with nothing but role data
+  distinguishing them. The concrete driver is detachable plates — cce-designer's detached panes
+  already behave exactly like this (a pane plate becomes a new window's root plate; its interior
+  corners become window corners), but the geometry lives app-side in
+  `cce-designer/src/render.rs::pane_plate_radii` and the detach machinery is designer-only.
+
+  **Design.** No widget returns (6as stays won). The unification lives in the paint/geometry
+  layer: a `PlateSpec` — rect, per-corner radii, color, blur, plus role flags:
+  `window_corners: (bool, bool, bool, bool)` (which corners lie on the window silhouette) and
+  `drag_background: bool` (whether uncovered area is a window-drag region). The toolkit computes
+  per-corner radii from the flags (a window corner wears
+  `window_corner_radius() * corner_span_factor()`, an interior corner wears
+  `plate_corner_radius()` — the `pane_plate_radii` math, moved in from the designer), and the
+  engine paints any plate root-or-nested through the one path, absorbing the per-app hand-rolled
+  root painting. A window's base surface is just a plate whose four corners are all window
+  corners.
+
+  **Invariants.**
+  - *The window silhouette stays a shared cross-process contract.* The compositor clips windows
+    from the SHARED corner value; per-plate radius freedom must never leak into a
+    `window_corners=true` corner (the designer's config.kdl radius override already documents
+    this trap at its `pane_plate_radii` call site). The role flags are where the constraint
+    lives: flagged corners read the shared value, period.
+  - *Blur regime follows the role.* A root plate frosts against the compositor's blur-behind
+    (the negative-alpha marker convention); a nested plate blurs against app content. Detaching
+    moves a plate between regimes; the marker choice keys off the role flags, and this is the
+    subtlest part of the phase — it gets its own design note before code.
+  - *No root container widget.* `PlateSpec` is data consumed by the paint path, not a node that
+    owns the window.
+
+  **Stages.**
+  - **7a — Vocabulary.** `style.surface.plate.*` becomes the canonical config namespace;
+    `backplate.*` keys stay as silent read-aliases (the existing `plate_corner_radius`
+    fallback is the pattern — extend it to every getter, then rename getters to `plate_*` with
+    deprecated `backplate_*` wrappers). User configs keep working unchanged. A/B: every client
+    AE=0.
+  - **7b — `PlateSpec` + window-corner math toolkit-side.** Introduce the spec, port
+    `pane_plate_radii` in, and give the engine a root-plate paint path fed by a spec instead of
+    each app's hand-rolled quads (DemoApp first, then the clients). The designer's per-pane
+    plates convert to specs with computed role flags. A/B: AE=0 per app.
+  - **7c — Detach/dock generalization.** Lift the designer's plate-corner control, collapse,
+    and dock-drag onto `PlateSpec` so any app can offer them. The detached-window PROCESS model
+    and sync channel (`default_project.json` polling) remain app policy — the toolkit provides
+    the plate-role flip (interior→window corners, blur regime swap, CSD hookup via the existing
+    `standard_csd`/`take_window_action` hooks), not the process management.
+
+  Not started; no code moves before 7a's alias sweep is reviewed, and stages land in order —
+  7b is the value (one paint path, plates compositional), 7c is the payoff feature.
+
 Order rationale: each phase is independently valuable and reversible, and no phase requires the
 next to compile. Phase 0 can land immediately regardless of the rest.