← Design · c140

handon-qnative.md

Hand-on to qnative: the QGUIkit cleanup (c140 D14)

2026-11-12. For: the qnative cycle that carries c140's code rulings, opened after the merge thread is done. Before starting, check that qnative main has the c3, c6 and c7 branches merged (git branch -a, git log). Reads first: c140's review-1.md § Ruled, findings.md, and the evidence on Two Surfaces, Scored. Read at: qnative e609f42. Re-read any line number below at the merged head.

What it is. The design rulings of 2026-11-12, in Mark's order (D14). There are five steps, with a review stop after step 2 and at the end. Design is the driver; the rulings are not reopened here.

1 · The crate takes its name (D1, D2)

  • crates/layer → crates/qguikit. Update the workspace Cargo.toml and every use layer:: in app, stories and devmode.
  • The crate's doc comment (lib.rs) says QGUIkit is our component library: GPUI Kit draws the shell, GPUI is the framework, and Vue is the other surface (design brand/glossary.md, Surface).
  • CLAUDE.md, the README and /gpui-story's paths follow.
  • The app's name stays provisional (c0). QGUIkit is the library's name, not the app's.

2 · The four small items (D13)

  1. The specs. Copy ../design/cycles/c140.surface-evaluation/rescued-specs/spec-*.md (ten files, about 400 KB) into cycles/c2.expressive-range/specs/ and commit them. Point c2's CYCLE.md "A spec first" lines at them. Leave design's copy until c140 closes.
  2. The bridge fixture. Copy qnative c4's it_contact (crates/fixtures/bridge/pilot.json, crates/types/src/bridge.rs:62) into design's app/data/worlds/bridge/pilot.json and its types.ts, so sync-bridge.sh cannot delete it. Then decide which repo is the bridge's source and say it in both READMEs.
  3. Six flaws, one commit:
    • app/src/pages/harness.rs:280: the Skills table's .qp("mcp.tool").sticky_top(…) is a copy from the table above; keep one .qp("harness.skill").
    • layer/src/table.rs:734: the empty box's rgb(0xE8E8E8) → a role, so it themes in dark mode.
    • app/src/pages/account.rs:119: GRAPE_500 on hover → the role, as dark mode wants GRAPE_400.
    • app/src/pages/inbox.rs:53–55: unwrap_or_default() on the store reads draws an SQLite error as "Nothing yet". Draw the failed state instead.
    • app/src/pages/sensor.rs (around 160–170): cycle ids ("design c132 D10", "c131 D17") in UI copy. Say what the person needs; keep the ids in comments.
    • layer/src/scorecard.rs:82, 84: the two stale rows (the sticky header and the expand row's keyboard, both built by D19). Add a row for DataHistoryGraph (c5).
  4. The token drift check. Add --check to ../design/scripts/tokens-to-kit.mjs: build both outputs in memory and fail on a diff, against the layer at the tag stamped in tokens.rs. Add a CI step in .github/workflows/build.yml that checks out q-nuxt-layer at that tag. The read-only comparison c140's token reader wrote is a starting point. It was in a session scratchpad, so write it again if it's gone.

Review stop 1 after step 2: the commits, CI on three systems, and anything step 1 broke.

3 · What leaves the crate, what comes in (D9)

  • Out:
    • The tour engine (tour.rs: the registry, the overlay, the glide) moves to its own crate or an app module. .qp_id() stays a thin trait the components call.
    • The 17-entry harness registry and its alias resolver (ux.rs:235–269) and workspace_mark's git-remote parser (data/surface.rs:81) move into the app's data.
  • The doc pages: pub mod demos (2,364 lines) moves to a docs crate, which devmode and stories depend on. The component crate ships no docs (design component_philosophy.md § Documentation architecture).
  • In, from the pages: section() and failed() (pages/mod.rs), absence_box, severity_pill (inbox), outcome_tag (tools), the lede (copied into seven detail pages), the primary text link (three copies). Each gets a doc page and a scorecard row.
  • Helpers: pct, short, plural, short_date and skill.rs's bytes move into qguikit::data::format. There should be one of each.

4 · The web's names (D8)

  • Every QGUIkit component takes its Vue name, with the Data and Ux prefixes. For example:
    • RankedBars → DataRankedBars
    • SegmentBar → DataSegmentBar
    • DonutChart → DataDonutChart
    • SurfacePanel → DataSurfacePanel
    • MetricValue → DataMetricValue
    • stroke_button() → UxButton with its stroke variant
    • tab_group() → UxTabGroup

    The full list comes from the scorecard's web column.
  • Keep a #[deprecated] alias per old name for one release.
  • The scorecard gets a rust column so the map is a lookup.

5 · Tokens everywhere, motion added, a lint (D5)

  • Type:
    • 433 text_size(px(literal)) and 156 line_height(px(literal)) become the text::T* pairs, as devmode already does.
    • The off-scale values (px 10, 12.8; line heights 22.75, 24.375, 47.5) are decided one by one: fold each to a step, or ask design for one.
  • Radius: 52 rounded(px(literal)) become radius::R*.
  • Colour leftovers:
    • the 14 hex literals (six of them off the palette: give the tour's 0x0099FF a role);
    • the 14 raw palette constants that do not theme;
    • the 51 .opacity() calls → qp_alpha where it is meant.
  • Motion (all three repos):
    • Add tokens to the layer's tokens.json: fast, base, slow and the one easing.
    • The bridge emits them to tokens.rs.
    • Replace the table's BORN_FADE and GROW_MS and the duplicate ease_in_out_quint.
    • Design rewrites component_philosophy.md's 200 / 500 ms rule to what the web does (it uses 300 ms most).
  • The lint (a script in CI) refuses:
    • hex outside tokens.rs;
    • text_size(px(<literal>));
    • rounded(px(<literal>));
    • cycle ids in UI strings;
    • unwrap_or_default() on store reads.

Review stop 2 at the end: the budget with strobe, against c2's readings (the rename and moves should cost nothing); captures per world compared with the atlas; the calls.

Not in this cycle

  • The slot equivalents (D6): named in the docs now; the APIs change when a component is next touched.
  • Keyboard and focus (D7): after V1 ships.
  • The parity register and the Surfaces panels (D3, D10): design c141.
  • /native-port and spec-measure (D11): design's skill and script. If they land first, use them here.

Qpoint Brand Style Guide