34 KiB
gpui-native-kit — Project Plan
Status: rev 3 · 2026-10-27
Changes in rev 3 (theme engine upgrade)
- Per-platform adapters read real semantic system colors (macOS
NSColor, Windows WinRTUISettings, Linux portal/KDE/GNOME, Omarchy theme files) instead of approximating platform palettes by hand. - Accent-ramp engine: one accent color in → hover/pressed/selected/subtle shades out (stated platform ramps where available, OKLCH derivation elsewhere).
- Native metrics promoted to a first-class deliverable (control heights, radii, fonts, focus rings, scrollbars) — color parity alone does not make native feel.
- Platform targets fixed: macOS, Windows 11 (WinUI), GNOME, KDE, Omarchy, plus a neutral fallback theme for unknown systems.
- Custom themes: sparse TOML definitions with overlay merge priority, config directory + CLI flag, live reload, single-accent override.
Changes from rev 1
- Component-by-component porting. No wholesale vendoring of
gpui-kitbase. Components are copied one at a time, each behind a review gate; the next port starts only after the previous is approved. - Custom native theme engine. No dependency on the
native-themecrate. We build our own engine inspired by its design (sparse theme definition → resolved theme → toolkit projection, presets, accessibility preferences) with our own per-platform readers. - Upstream GPUI only. The only GPUI is
gpui/gpui_platformfromzed-industries/zed(git, pinned revision). Nogpui-pre, nogpui-component, nogpui-kitfacade dependency.
1. Vision
gpui-native-kit is a Rust component library for GPUI whose components look native to the user's operating system.
| Source | Role in this project |
|---|---|
GPUI Kit base |
The source we port components from, one per review unit. Its primitives own interaction behavior, controlled state, focus, accessibility semantics — and no visuals. |
| Upstream GPUI | The UI framework, from Zed's repository at a pinned revision. |
| native-theme | Design inspiration only. Its layered model (sparse Theme → ResolvedTheme → connector, presets, accessibility preferences, per-platform readers) is the blueprint for our own engine. We depend on none of its code. |
The core idea: base primitives own behavior but no visuals. We add the missing layers — a theme engine that reflects the user's system, a projection from resolved theme to base's semantic tokens and native control metrics, and styled components that paint the ported primitives with them.
What "native" means here. Components are rendered by GPUI to approximate the platform toolkit's appearance (colors, control heights, radii, focus rings, fonts, light/dark, accent color) — they are not OS-native controls.
2. Background facts (verified)
- GPUI install shape — the only framework dependency:
(
gpui = { git = "https://github.com/zed-industries/zed", rev = "<pin>" } gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "<pin>", features = ["font-kit"] }font-kitrequired on macOS for glyph rasterization; Linux needswaylandand/orx11; Windows needs nothing.) Nogpui-pre: that package is gpui-component's republishing mechanism for Zed snapshots and plays no role here. Consequence we must manage: source ported from upstreamgpui-basewas written against a particular GPUI snapshot and may differ slightly from our pinnedmainrevision — each port unit applies minimal adaptation patches and records them. gpui-basefacts (from the official docs):initmust run once before opening windows;open_window-equivalent plumbing wraps the root view in aRoot(overlay hosting, window state, rem size). Theme tokens:ColorTokens::light()/dark()provide semantic palettes —background,foreground,surface,surface_foreground,primary,secondary,muted,accent,destructive,border,input,ring,selection+ foreground roles — installed asSemanticThemeTokens(which also carries radius, spacing, typography, shadow scales) on a themeGlobal. Base paints its own chrome (scrollbars, resize handles) from these tokens.- Ported-component catalog (37 components): Accordion, Alert Dialog, Avatar, Button, Calendar, Checkbox, Collapsible, Color Picker, Combobox, Date Picker, Dialog, Editor, Hover Card, Input, Link, Nav Stack, Number Input, OTP Input, Pagination, Popover, Popup, Progress, Radio, Radio Group, Resizable, Scrollbar, Select, Sheet, Slider, Switch, Table, Tabs, Textarea, Time Field, Toast, Toggle, Toggle Group, Tooltip. Plus infrastructure systems ported lazily when first needed: Motion, Virtual List, Dock, History, Plot, TextView, Text Selection. Upstream examples live in
crates/base/examplesand are the per-component reference behavior we bring along. - native-theme design (the blueprint we adapt):
SystemTheme::from_system()reads macOS / Windows / KDE / GNOME; a sparse, TOML-shapedTheme(Option fields) resolves into a fully populatedResolvedTheme; bundled presets;AccessibilityPreferences(text scaling, reduce motion, reduce transparency); a thin per-toolkit "connector" maps the resolved theme onto the framework's theming system. Its existing GPUI connector targets gpui-component (139ThemeColorfields, geometry builders, theme-drawn widgets) — useful as a case study, not a dependency. - xdg-desktop-portal
Settings(verified against the spec):org.freedesktop.appearancestandardizescolor-scheme(u),accent-color((ddd) RGB 0–1),contrast(u), andreduced-motion(u), with aSettingChangedsignal — one DBus interface covers mode, accent, contrast, and reduce-motion across GNOME, KDE, and generic Linux. - Omarchy theme contract (verified against the manual and Omarchy's shipped
tokyo-nighttheme): the active theme is$HOME/.local/state/omarchy/current/theme/colors.tomlwith the theme name intheme.namebeside it (legacy location~/.config/omarchy/current;current/themeis a symlink into~/.config/omarchy/themesor/usr/share/omarchy/themes).colors.tomlstatesmode = "dark" | "light"plus semantic keys —accent,selection,muted,background/dark_background/darker_background/lighter_background,foreground/dark_foreground/light_foreground/bright_foreground, andred/yellow/orange/green/cyan/blue/magenta/brownwithbright_*variants; the ANSIcolor0..15format is an accepted alternative. - Prior art — gpui-omarchy (MIT): a gpui-kit component library styled exclusively for Omarchy. It validates this project's shape (base + system-style layer) and its theme handling is the reference for our Omarchy adapter: read
colors.toml+theme.name, watch filesystem events for palette edits and theme-symlink replacement,follow_system(cx)/ explicitapplyto pause/resume following, whole-palette fallback to Tokyo Night when files are missing or invalid. We differ deliberately: upstream zed GPUI only (it uses the publishedgpui-kitfacade), per-component porting, and five other platform targets.
3. Architecture
3.1 Workspace layout
The repo currently holds a single empty package (gpui-native-kit/, no dependencies). Phase 0 restructures the repo root into a cargo workspace:
gpui-native-kit/ # repo root = workspace root
├── Cargo.toml # [workspace] members = ["crates/*"]
├── rust-toolchain.toml # latest stable (gpui requires recent stable)
├── AGENTS.md # adapted coding guidelines (§7)
├── UPSTREAM.md # zed rev pin, per-port-unit provenance & divergence log
├── docs/PLAN.md
├── crates/
│ ├── base/ # gpui-native-base: foundation seed + ported components
│ │ # (grows one component per approved review unit)
│ ├── theme/ # gpui-native-theme: custom native theme engine + projection
│ └── kit/ # gpui-native-kit: styled native components (facade)
│ # (grows alongside each base port unit)
└── examples/
└── gallery/ # showcase application; gains a page per port unit
- The existing empty package becomes
crates/kit(keeps thegpui-native-kitpackage name). baseandkitare separate crates so the "no presentation in base, no behavior in kit" boundary is compiler-enforced. (Decision point D5 covers keeping them separate vs one crate.)
3.2 Dependency direction
examples/gallery ──▶ gpui-native-kit ──▶ gpui-native-theme ──▶ gpui-native-base ──▶ gpui, gpui_platform (zed, pinned rev)
│ │
└─────────────────────┴──▶ gpui
basedepends only on GPUI.themedepends on GPUI +base(it writes base's token global).kitdepends on all three. One-way, no cycles.- Nothing depends on
gpui-component,gpui-kit, ornative-theme.
3.3 Theme pipeline (custom engine, native-theme-inspired)
graph TD
OS[Adapters: macOS / Windows 11 / GNOME / KDE / Omarchy / fallback] --> ST[SystemTheme normalized snapshot]
PR[ThemeDefinition presets + user overlays] --> RS[resolve]
ST --> RS
RS --> RT[ResolvedTheme + AccessibilityPreferences]
RT --> BR[projection: bridge + metrics]
BR --> TK[SemanticThemeTokens onto base Theme global]
BR --> GM[per-widget native metrics for kit components]
TK --> WIN[windows]
GM --> WIN
EVT[runtime change: gpui appearance events, portal signals, pref changes] -->|re-read, re-resolve, re-install, repaint| BR
- Read — one adapter per target platform produces a normalized
SystemTheme(mode, accent, semantic palette, accessibility flags, style hint):- macOS: real
NSColorsemantic colors (windowBackgroundColor,labelColor,secondaryLabelColor,controlColor,controlBackgroundColor,separatorColor,controlAccentColor,selectedTextBackgroundColor,keyboardFocusIndicatorColor, …) read in the current effective appearance viaobjc2-app-kit; contrast/reduce-motion/reduce-transparency fromNSWorkspaceflags; changes viaNSDistributedNotificationCenter+ GPUI appearance events. - Windows 11 (WinUI): documented WinRT
UISettings.GetColorValue(AccentColor,AccentLight1–3,AccentDark1–3,Background,Foreground, …) via thewindowscrate, plusTextScaleFactorandAccessibilitySettingshigh-contrast;Personalizeregistry as fallback; runtime viaUISettings.ColorValuesChanged. - GNOME / generic Linux:
xdg-desktop-portalorg.freedesktop.appearance— standardizedcolor-scheme,accent-color(RGB 0–1),contrast,reduced-motionwithSettingChanged(verified against the portal spec) — viazbus;gsettings org.gnome.desktop.interfaceas fallback. - KDE:
kdeglobalsini ([Colors:Window/Button/Selection/…], scheme hash, accent) parsed with fixture tests; portal keys still honored for mode/accent. - Omarchy: read
$HOME/.local/state/omarchy/current/theme/colors.toml+theme.name(legacy~/.config/omarchy/current), accepting both the semantic key set and ANSIcolor0..15; watch thecurrentdir for palette edits and theme-symlink replacement; invalid or missing palette falls back to Tokyo Night (Omarchy's default); portal keys supply contrast/reduced-motion. - Fallback / unknown system: no adapter applies, or it fails → bundled neutral light/dark theme; also the per-field fallback for values a reader could not supply.
- macOS: real
- Detect — capability cascade picks the adapter: Omarchy marker (
~/.local/state/omarchyor~/.config/omarchy) → KDE (kdeglobalspresent) → portal reachable (GNOME/generic) → fallback. macOS/Windows by compile target. - Resolve — overlay chain: platform defaults → preset (
ThemeDefinition) → user overlay → accessibility adjustments. Output:ResolvedTheme(role palette + control metrics) andAccessibilityPreferences. - Project —
ResolvedTheme→ base'sSemanticThemeTokens(installed on the themeGlobal, covering base-drawn chrome) + native metrics consumed bykitcomponents. - Observe — a
ThemeWatcherper adapter forwards real change events (distributed notifications, WinRTColorValuesChanged, portalSettingChanged, inotify on KDE/Omarchy files) into the app; GPUI appearance events remain the light/dark baseline everywhere. Each event triggers re-read → re-resolve → re-install → repaint all windows. We are the only writer of base's theme global, which keeps re-application simple.
3.4 Component design rule
Each kit component wraps a ported base primitive and owns only presentation:
- The base primitive provides activation, focus, keyboard, dragging, controlled state, AccessKit role.
- The kit component paints track/thumb/indicator/panel etc. from
ResolvedThemeleaves, with geometry from native metrics. - No behavior is reimplemented; no color/radius literal appears outside
theme's projection and metrics modules.
4. Porting process & component order
4.1 The review gate
Work proceeds in port units. One unit = one component (or one infrastructure system, or the foundation seed). The next unit starts only after you approve the previous one.
Each unit delivers:
- the ported module(s) in
crates/base(behavior, unstyled), copied from upstreamlongbridge/gpui-kitcrates/baseat a recorded upstream commit; - the styled counterpart in
crates/kit(from Phase 3 onward — themed at birth; see D9); - its example wired into the gallery (ported from upstream's example for that primitive, adapted);
- tests (ported and/or new);
- a
UPSTREAM.mdentry: upstream commit, files touched, every adaptation patch made to compile against our zed pin, and any intentional divergence.
Unit checklist before requesting review:
- compiles against the pinned zed rev;
cargo clippyclean - zero visual literals in
base; all styling reads theme/metrics (kit side) - stable domain-derived
ElementIds; controlled state round-trips through the owner - pointer + keyboard + disabled behavior verified by test
- gallery page renders in light and dark
UPSTREAM.mdprovenance recorded
4.2 Port order (dependency-aware)
Unit 0 — foundation seed (prerequisite, not a "component"): init, Root/window plumbing, theme global with ColorTokens/SemanticThemeTokens, prelude, small shared utils, gallery shell.
Then, in order — leaf controls first, popup infrastructure before anchored controls, overlays together, data and pickers last:
| # | Unit | Notes |
|---|---|---|
| 1 | Button | First full unit; proves the whole pipeline |
| 2 | Checkbox | |
| 3 | Switch | |
| 4 | Radio + Radio Group | One unit |
| 5 | Toggle + Toggle Group | One unit |
| 6 | Link | |
| 7 | Slider | First drag interaction |
| 8 | Progress | |
| 9 | Avatar | |
| 10 | Input | Brings input state machinery |
| 11 | Number Input | Reuses Input |
| 12 | Textarea | |
| 13 | OTP Input | |
| 14 | Popup | Low-level anchored-content host; needed by 15–19 |
| 15 | Popover | |
| 16 | Select | |
| 17 | Combobox | Input + popup |
| 18 | Tooltip | |
| 19 | Hover Card | |
| 20 | Dialog | Overlay focus trap/restore |
| 21 | Alert Dialog | |
| 22 | Sheet | |
| 23 | Toast | May pull in Motion as a lazy infra unit |
| 24 | Tabs | |
| 25 | Accordion | |
| 26 | Collapsible | |
| 27 | Pagination | |
| 28 | Scrollbar | |
| 29 | Resizable | |
| 30 | Table | May pull Virtual List |
| 31 | Tree | May pull Virtual List |
| 32 | Calendar | |
| 33 | Date Picker | Input + popup + Calendar |
| 34 | Time Field | |
| 35 | Color Picker | |
| 36 | Nav Stack | May pull History |
Infrastructure systems (Motion, Virtual List, Dock, History, Plot, TextView, Text Selection) are ported lazily — each as its own review unit — when the first component that needs it comes up (suggestions in the table). Editor is deliberately out of the initial 36; schedule it after Phase 5 if wanted (D12).
5. Milestones
Phase 0 — Workspace scaffold
- Restructure repo root into a cargo workspace (§3.1); move the existing package to
crates/kit. - Dependencies:
gpui+gpui_platformfrom zed at a pinned rev; record the rev inUPSTREAM.md. - Toolchain file,
rustfmt,clippy(deny warnings in CI), license check. - Write
AGENTS.md(§7) and theUPSTREAM.mdskeleton. - Minimal
main.rs: open one window with adiv. - Done when:
cargo run -p gpui-native-kitshows a window on macOS; CI skeleton (build + clippy + fmt, macOS/Windows/Linux matrix) passes.
Phase 1 — Port unit 0: foundation seed
- Port init/Root/theme-global/tokens/prelude into
crates/base; gallery shell inexamples/gallery. - Default
ColorTokenspalettes only; no system reading yet. - Done when: gallery runs; tokens switchable between light/dark manually; you approve unit 0.
Phase 2 — Custom native theme engine (macOS first)
crates/theme:ThemeSourcetrait +SystemTheme/ThemeDefinition/ResolvedTheme/AccessibilityPreferencesmodel; macOS adapter (realNSColorsemantic reads + accessibility flags); accent-ramp engine (OKLCH); resolve overlay chain; projection to base tokens + the macOS metrics table; install + watcher (distributed notifications + GPUI appearance events); neutral fallback theme; CLI override flag for theme/preset/mode.- Reader fixture tests (parse canned payloads); resolve/projection unit tests for presets × modes.
- Done when: the gallery reflects the live macOS theme; toggling system appearance switches at runtime; a preset can be forced via CLI flag; you approve the engine.
Phase 3 — Port units 1–13 (controls + text)
- Units 1–13 per §4.2, each styled from
ResolvedThemeat birth, with metrics (control heights, paddings, radii, focus ring) from the engine. - Per-component UI integration tests; gallery pages.
- Done when: gallery shows these controls native-styled in light/dark; all units approved.
Phase 4 — Port units 14–27 + platform expansion
- Popup/overlay/navigation units (14–27), including overlay focus-trap/restore and stacked dismissal verification.
- Parallel track: Windows adapter (WinRT
UISettings+ColorValuesChanged); Linux adapters — portal viazbus(baseline), KDEkdeglobals, GNOMEgsettings, Omarchy theme-dir reader (schema verified on a real install); per-platform metrics tables; runtime-change observation on each platform; side-by-side screenshot verification against each toolkit. - Per-platform CI screenshots for the gallery (best-effort visual regression).
- Done when: gallery runs native-themed on macOS, Windows 11, GNOME, KDE, and Omarchy, and falls back cleanly on unknown systems; units approved.
Phase 5 — Port units 28–36, lazy infra, polish
- Data + picker units (28–36); lazy infrastructure units as surfaced (Virtual List, Motion, History).
- Showcase polish (live theme switching UI), README, per-component doc comments with compilable examples, CHANGELOG, test sweep.
- Decide on publishing (crates.io vs git tag).
- Done when: all units approved; a fresh contributor can clone, build, run the gallery, and find the docs they need.
6. Theme engine design (custom, crates/theme)
6.1 Architecture
ThemeSourcetrait — one adapter per target platform:snapshot() -> SystemThemepluswatch(callback)for live changes. Adapters are thin over parseable payloads (registry dumps, ini text, portal replies, theme JSON/CSS) so parsing is fixture-testable on any OS; platform calls live in a small seam.SystemTheme— normalized snapshot: mode (light/dark), accent color, semantic palette (the roles the adapter can supply), high-contrast flag, reduce-motion, reduce-transparency, text-scaling factor, style hint (e.g."Breeze"), platform identity.- Detection cascade — Omarchy marker (
~/.local/state/omarchyor~/.config/omarchy) → KDE (kdeglobalspresent) → portal reachable (GNOME/generic Linux) → fallback theme. macOS/Windows chosen by compile target. ThemeDefinition— sparse, TOML-serializable struct ofOptionfields; used for bundled presets and user themes. May omit almost anything.ResolvedTheme— fully populated: every color role has a value; metrics have platform defaults where the definition is silent.resolve(system, definitions…, prefs)is a pure function — unit-testable without a GUI.AccessibilityPreferences— text-scaling factor (≥ 1, clamped finite), reduce-motion, reduce-transparency, high-contrast. Forwarded: text scaling multiplies font sizes (and, through rem, all rem-relative sizing); reduce-motion forwarded to GPUI; reduce-transparency removes overlay backdrops; high-contrast sharpens borders and removes subtle fills.- Projection —
ResolvedTheme→ baseSemanticThemeTokens+ metrics structs consumed by kit components. ThemeWatcher— owns adapter watches, forwards change events to an app entity, drives re-resolve → re-project → repaint (pipeline step 5).
6.2 Initial role mapping (refined during Phase 2)
Base role (ColorTokens) |
ResolvedTheme source |
|---|---|
background / foreground |
window background / default text |
surface / surface_foreground |
card / secondary areas |
primary, secondary, accent |
button + selection colors (macOS: accent-derived) |
muted |
muted/secondary text and hover shades |
destructive |
danger/error role |
border |
button/input border line |
input |
text field background/border |
ring |
focus ring color |
selection |
text selection background (macOS: selected-text color) |
Rules: every mapping lives in one module; derivations from neighboring roles are documented where the definition is silent; never literals. Tests assert finite, distinct, semantically sane tokens for every preset × mode.
Omarchy colors.toml maps almost 1:1 onto our roles: background → background, lighter_background → surface, dark_background/darker_background → elevated/overlay depth, foreground → foreground, dark_foreground → muted foreground, selection → selection, accent → primary/accent/ring, red → destructive, green/yellow/orange → success/warning, and the remaining named colors → secondary accents.
6.3 Platform support matrix
| Target | Detection | Sources read | Live changes | Notes |
|---|---|---|---|---|
| macOS | compile target | NSColor semantic palette in the effective appearance; NSWorkspace accessibility flags |
distributed notifications + GPUI appearance events | Real system colors, including custom accent and increased-contrast variants |
| Windows 11 (WinUI) | compile target | WinRT UISettings colors (Accent, AccentLight1–3, AccentDark1–3, Background, Foreground…), TextScaleFactor, AccessibilitySettings high-contrast; Personalize registry fallback |
UISettings.ColorValuesChanged |
Documented WinRT APIs only; gives the exact WinUI token set |
| GNOME | portal reachable + GNOME hint | portal org.freedesktop.appearance (color-scheme, accent-color, contrast, reduced-motion); gsettings fallback |
portal SettingChanged |
Portal keys are the generic-Linux baseline too |
| KDE | kdeglobals present |
kdeglobals color groups + scheme hash + accent; portal for mode/accent |
inotify on kdeglobals |
Ini parser fixture-tested |
| Omarchy | ~/.local/state/omarchy or ~/.config/omarchy marker |
current/theme/colors.toml + theme.name (semantic keys or ANSI color0..15); portal for contrast/reduced-motion |
inotify on the current dir (palette edits + symlink replacement) |
Invalid/missing palette → Tokyo Night fallback; role mapping in §6.2 |
| Fallback / unknown | none of the above, or reader failure | bundled neutral light/dark theme | manual only | Also the per-field fallback for values a reader could not supply |
6.4 Accent ramp engine
Every target except the fallback provides one accent color (or a stated ramp). The engine turns an accent into the state shades components need — hover, pressed, selected, disabled, subtle fills:
- Where the platform states shades (WinUI
AccentLight1–3/AccentDark1–2, KDE scheme ramps), use them as-is. - Where it does not (portal
accent-color, macOS custom accent beyond the base color), derive shades in OKLCH: fixed lightness/chroma offsets toward the background (hover/subtle) or foreground (pressed/strong), hue preserved. - All derived colors are computed once per resolve, never per frame.
6.5 Native metrics (beyond color)
Native feel is half geometry. crates/theme ships per-platform metric tables consumed by kit components: control heights and paddings, corner radii (WinUI control vs card rounding; macOS control radii), font families and base size (SF Pro / Segoe UI Variable / Adwaita Sans / Noto Sans; rem base), focus-ring policy per platform (macOS keyboard ring, WinUI two-layer ring, GTK outline), scrollbar style (overlay vs inline, widths), popup shadow/elevation, and menu/popup metrics. Values are transcribed from each toolkit's reference metrics and verified with side-by-side screenshots in Phase 2 (macOS) and Phase 4 (Windows/Linux). Prefer GPUI's .SystemUIFont resolution with per-platform fallback lists, as gpui-omarchy does, so no fonts need bundling.
6.6 Custom themes
ThemeDefinitionTOML files: bundled presets and user files share one schema. A definition may override any role, any metric, or just the accent (accent = "#7c3aed"→ full ramp derived).- Merge priority: platform reader → bundled preset → user file(s) → explicit runtime overrides → accessibility adjustments.
mode = "system" | "light" | "dark"forces a mode. - Sources:
--theme <preset|path>CLI flag; a config directory (~/.config/gpui-native-kit/themes/*.tomlor the platform equivalent) scanned at startup; anapplyAPI for applications that build their own settings UI. - Omarchy interop: the loader also accepts Omarchy
colors.tomltheme files (semantic keys or ANSIcolor0..15) directly, so any community Omarchy theme can be dropped into the config dir unchanged. - Live reload: file watcher (
notify) re-resolves on save; parse errors surface as a notification, never a panic (per AGENTS.md error rules). - Follow-mode API (pattern from gpui-omarchy):
follow_system(cx)keeps adapting to system changes; an explicitapplydetaches;system_or_default()takes a one-time snapshot.
6.7 Implementation vehicles
Prefer maintained crates over hand-rolled FFI: objc2 + objc2-app-kit (macOS), windows (WinRT UISettings), zbus (portal DBus), notify (file watches), serde + toml (definitions). All behind the ThemeSource seam so the engine core stays platform-free and testable.
6.8 What we do differently from native-theme
- Role-minimal palette aligned 1:1 with base's semantic roles — no 139-field toolkit mapping.
- Real semantic colors per platform (
NSColor/UISettings/ portal), not hardcoded approximations of platform palettes. - Accent-ramp derivation (OKLCH) instead of hand-mapped hover/active shades.
- Native metrics as a first-class deliverable, not an afterthought.
- Omarchy as a first-class target; a designed neutral fallback for unknown systems.
- Custom themes with overlay merge priority and live reload.
7. Coding standards — AGENTS.md adaptation
Create AGENTS.md at the repo root in Phase 0, adapted from the reya/signed AGENTS.md:
From reya/signed (kept nearly verbatim):
- Correctness and clarity first; speed secondary unless specified.
- No organizational/summarizing comments; comments explain non-obvious why only; doc comments one line, simple.
- Prefer existing files over new small ones; extend existing
implblocks. - No wrapper structs around dependency APIs; call them directly.
- Bind global handles to a local before use; no inline chained
::global(cx)calls. - No
unwrap()/panicking indexing; propagate with?; never silently discard errors withlet _ =— propagate,.log_err(), or handle explicitly. - Async errors surface to the UI layer with meaningful feedback.
- Full-word names; shadowing to scope clones in async closures.
- GPUI section: context conventions (
cx/window),Entity/WeakEntityrules,cx.spawn/background_spawn/Tasklifetime rules,Render/RenderOnce,cx.listener,actions!,cx.notify()/emit/subscribewith storedSubscriptions, GPUI executor timers in tests instead ofsmol::Timer. - PR hygiene: imperative title, optional crate prefix,
Release Notes:section.
Added for this project (from the GPUI Kit coding guides, adopted as normative):
- Never invent APIs — search the ported source for real signatures before writing code.
- Behavior/presentation boundary:
crates/basekeeps zero presentation;crates/kitkeeps zero behavior. RenderOncefor value-like controls;Entity<_State>for retained behavior; stable domain-derivedElementIds (never list indexes).SharedStringfor retained UI text; private fields + builders/readers for behavioral seams; naming vocabulary (is_/has_readers,with_builders,on_callbacks,State/Event/Delegatesuffixes).- No raw colors/radii/px outside
crates/theme. - Test at the lowest layer that proves the behavior; regression test before bug fixes.
- Porting discipline: port upstream structure and naming as-is; do not "improve" while porting — improvements are separate commits after review; record every divergence in
UPSTREAM.md.
8. Testing strategy
- Pure tests — theme resolve overlay chain, projection mapping (presets × modes), reader payload parsing (fixtures), metrics derivations, geometry math (popup clamping, scrollbar ranges).
- Context tests — theme global lifecycle, appearance-change handling, token re-install.
- UI integration tests — headless windows: pointer/keyboard activation, controlled value round-trips, focus movement and traps, disabled/readonly, event order.
- Example smoke — gallery launches, switches theme/preset/mode without panic.
Conventions: GPUI executor timers in tests; regression test first for bugs; screenshots only for facts the semantic tree cannot express.
9. Licensing & attribution
- Ported base code: Apache-2.0 — preserve upstream file headers, keep a per-file provenance table in
UPSTREAM.md(upstream commit → our path → changes). - GPUI: Apache-2.0 (dependency, not copied).
- native-theme: inspiration only; no code copied. If that ever changes, respect its Apache/MIT/0BSD terms.
- Docs prose: write our own. Any prose adapted from gpui-kit.com must follow CC BY 4.0 attribution — default to avoiding it.
- This repo's own code: Apache-2.0 recommended (D6).
10. Risks & mitigations
| Risk | Mitigation |
|---|---|
Zed main (pre-1.0) churns; ported upstream source may not match our pin |
Pin zed rev; per-unit adaptation patches recorded in UPSTREAM.md; CI catches drift early |
| Custom platform readers are a maintenance burden (OS updates, portal API changes) | Thin readers over parseable payloads + fixture tests; graceful fallback chain (system → preset → fallback theme) so a failed read degrades to a sane default |
Omarchy colors.toml key set may evolve across releases |
Schema now documented and verified against a shipped theme; parse tolerantly (unknown keys ignored, missing keys fall back); track upstream theme-set changes each release cycle |
Portal accent-color absent on older desktops |
Treat as unset (spec-defined); fall back to gsettings/KDE accent, then ramp from a neutral accent |
| Undocumented Windows APIs drift | Use documented WinRT UISettings only; registry reads as fallback |
| Accent-color changes may not surface via GPUI events on all platforms | Re-read on window focus/appearance events; verify per platform during Phase 2/4 |
| Review-gated porting is slower than bulk copying | Accepted by design; batches in §4.2/§5 keep the sequence predictable; unit 0 makes everything after it mechanical |
| 36 components + lazy infra is scope creep | Strict one-unit review gate; Editor and Dock deferred unless requested |
| WASM support (upstream base examples run on wasm) is lost | Desktop is the product; theme reading is desktop-only; keep ColorTokens fallbacks (D4) |
11. Decision points
| # | Decision | Status / recommendation |
|---|---|---|
| D1 | Theme source | Decided (your feedback): custom engine inspired by native-theme; no native-theme dependency |
| D2 | Copy strategy | Decided (your feedback): one component per review-gated port unit; no wholesale vendoring |
| D3 | GPUI source | Decided (your feedback): upstream zed git only, no gpui-pre — pinned rev (recommended) |
| D4 | WASM target | Not initially; desktop only |
| D5 | Separate base + kit crates vs one crate |
Separate (compiler-enforced behavior/presentation boundary) |
| D6 | License for our code | Apache-2.0 |
| D7 | Facade keeps package name gpui-native-kit |
Yes |
| D8 | Platform order | macOS first (dev machine); then Windows 11 (WinUI), then GNOME → KDE → Omarchy (portal baseline lands with GNOME); fallback theme from day one |
| D9 | Styled counterpart in the same unit as the behavior port | Yes from unit 1 (theme engine lands in Phase 2 before any component unit) |
| D10 | Icon sets (Material/Lucide bundling) | Out of scope initially; revisit after Phase 5 |
| D11 | Preset breadth | Bundled: neutral fallback light/dark (unknown systems), tokyo-night (Omarchy's default and its Omarchy fallback), plus per-platform presets for testing readers; Omarchy colors.toml files load as presets too |
| D12 | Editor primitive | Deferred; schedule after Phase 5 if wanted |
| D13 | Accent state shades | Hybrid: stated platform ramps where available (WinUI, KDE), OKLCH derivation elsewhere |
| D14 | Custom-theme live reload | Yes, via notify; the dep is justified by the custom-theme requirement |
12. Immediate next steps
- Confirm the remaining recommendations (D4–D12) or adjust.
- Phase 0: workspace restructure, zed pin,
AGENTS.md,UPSTREAM.mdskeleton, CI, window smoke test. - Port unit 0 (foundation seed) → your review.
- Phase 2 theme engine (model + macOS adapter + accent ramp + fallback theme) → your review.
- Port unit 1 (Button, styled) → your review → continue down the §4.2 list.