30 KiB
gpui-native-kit — Project Plan
Rev 6 (2026-10-28). Changes from rev 5: components are built one element at a time behind a review gate — each unit is designed, implemented, tested, shown in the gallery, and reviewed before the next starts (batch waves are gone). gpui-kit base is re-admitted as the API-design reference: its unstyled-control conventions (controlled state, one activation path for pointer + Enter + Space, semantic states, a11y labels) shape our component APIs, and small utilities may be adapted where they help. Everything else (empty-project reset, theme engine as the product, crate split, per-component features) stands.
1. Vision
GPUI components that feel native to the OS they run on — macOS, Windows 11 (WinUI), GNOME, KDE, Omarchy — with a designed neutral fallback for unknown systems. Every color, radius, spacing, metric, and icon comes from engines that read the running system or a user-supplied theme. The theme and icon engines are the product; components are their proof.
Consumption is flexible: small focused crates, per-component cargo features, and a thin optional facade. A hello-world app can depend on gpui-native-core with two features; a full app can depend on the gpui-native-kit facade with --all-features.
2. Evaluated foundations (2026-10-28)
| Crate | What it is | Verdict | What we take |
|---|---|---|---|
| gpui-kit (longbridge) | Unstyled base behavior layer (controlled state, one activation path, semantic states, a11y roles, focus utilities) + styled component library |
Not a wholesale foundation: the full base is larger than we need and pins gpui-pre snapshots. But base is the API-design reference for our components (D20), and its small utilities are adapted where they help |
API shapes: Button::new("save") unstyled + .on_click as the single path for pointer/Enter/Space; controlled checked + on_change(next); disabled/selected semantic states resolved after the builder chain, disabled last; accessibility_label; composable parts (CheckboxIndicator, SwitchTrack/SwitchThumb) where a component needs them. Utilities worth adapting: h_flex/v_flex, StyledExt, StateStyle, focus-ring helper |
| gpuikit (iamnbutler) | Web/SwiftUI-style toolkit (editor, input, keymap, markdown, selection, a11y modules) on gpui-unofficial |
Not a foundation: builds on the gpui-unofficial crates.io distribution (conflicts with D3); generic theme system with no per-OS native look |
Component API ergonomics; showcase/gallery pattern |
| bezel (crabtalk) | SwiftUI-lean library; style flows through the environment — Theme::of(cx) + catalog traits, zero style parameters, Brand/Tint hue re-theming; own gpui fork (bezel-gpui) |
Not a foundation: gpui fork (conflicts with D3); one visual language (macOS-leaning HIG), not per-OS native | Environment-first styling: components read the theme global at paint time and take no color/size parameters; Brand-style hue overrides inform custom themes |
| zeron (zeronsh) | Production gpui app (coding-agent control plane; crates: ui, theme, text, syntax, term, …) | Not a foundation — an application, not a library | Existence proof that gpui scales; app-level patterns for data-heavy screens |
Decision D15: we depend on no UI toolkit and port no wholesale subsystems. These crates (plus native-theme, D1) are read for API and design ideas — and always simplified, never mirrored 1:1. When in doubt, ship the smaller API.
3. Background facts (verified)
- GPUI install shape — the only framework dependency:
gpui = { git = "https://github.com/zed-industries/zed", rev = "ec86047d67acd6fddd81392f44ed6ebcc6e7f531" } gpui_platform = { git = "https://github.com/zed-industries/zed", rev = "ec86047d67acd6fddd81392f44ed6ebcc6e7f531", features = ["font-kit"] }font-kitis required on macOS for glyph rasterization; Linux needswaylandand/orx11; Windows needs nothing. Nogpui-pre, no forks, nogpui-unofficial. Toolchain 1.98.1 viarust-toolchain.toml(matches zed at the pinned rev; verified building on this machine). Bumping the pin is a deliberate act: re-verify, fix drift, note it in the bump commit message. - native-theme design (the blueprint we adapt, not a dependency): sparse TOML-shaped
Theme(Option fields) resolves into a fully populatedResolvedTheme; bundled presets;AccessibilityPreferences; thin per-toolkit connectors. Its GPUI connector targets gpui-component — a case study only. - xdg-desktop-portal
Settings(verified against the spec):org.freedesktop.appearancestandardizescolor-scheme(u),accent-color((ddd) RGB 0–1),contrast(u),reduced-motion(u), withSettingChanged— one DBus interface covering mode, accent, contrast, and reduce-motion across GNOME, KDE, and generic Linux. - Omarchy theme contract (verified against the manual and the shipped
tokyo-nighttheme): active theme is$HOME/.local/state/omarchy/current/theme/colors.tomlwiththeme.namebeside it (legacy~/.config/omarchy/current;current/themeis a symlink into the theme set).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_*; the ANSIcolor0..15format is an accepted alternative. - Platform icon systems: macOS SF Symbols — thousands of template glyphs, runtime lookup via
NSImage(systemSymbolName:)rendered at a requested size/weight; Apple's license forbids redistributing the set, so it is used at runtime only, never bundled. Windows 11 Segoe Fluent Icons — a system font addressed by codepoint. Linux (and Omarchy) — the freedesktop icon-theme spec: lookup across the active theme (Breeze, Adwaita) and hicolor. Lucide (ISC) is the bundled offline fallback everywhere else. - Prior art — gpui-omarchy (huacnlee, MIT): validates the Omarchy adapter design: read
colors.toml+theme.name, watch filesystem events for palette edits and theme-symlink replacement,follow_system/ explicitapplyto pause/resume following, whole-palette fallback to Tokyo Night when files are missing or invalid.
4. Architecture
4.1 Workspace layout (Phase A)
gpui-native-kit/ # repo root = workspace root
├── Cargo.toml # [workspace]; root package = facade
├── rust-toolchain.toml
├── AGENTS.md
├── docs/PLAN.md
├── src/lib.rs # gpui-native-kit: thin facade (re-exports, feature-driven)
├── crates/
│ ├── theme/ # gpui-native-theme: the native theme engine
│ ├── icons/ # gpui-native-icons: system icons + bundled fallback
│ ├── core/ # gpui-native-core: components every app needs
│ │ # (one cargo feature per component)
│ └── editor/ # gpui-native-editor: created when wave 2 needs it (D12)
└── examples/
└── gallery/ # showcase; a page per component + theme/icon inspector
Advanced crates (data for Table/Tree/List, charts, pickers) are created on demand when their wave starts — not speculative scaffolding.
graph TD
FAC[gpui-native-kit facade — optional] --> CORE[gpui-native-core]
FAC --> FUT[editor / data / …]
CORE --> T[gpui-native-theme]
CORE --> I[gpui-native-icons]
T --> P[gpui + gpui_platform — zed, pinned rev]
I --> P
One-way, no cycles. Nothing depends on gpui-component, gpui-kit, gpuikit, bezel, or native-theme. Applications may depend on gpui-native-core directly and skip the facade.
4.2 Feature model
- One feature per component in
core(button,checkbox,switch,radio,toggle,link,slider,progress,avatar,input,popover,tooltip,menu,dialog,toast,list,tabs, …).default = ["full"]; lean apps usedefault-features = false+ the features they want. - Features gate whole modules only (
#[cfg(feature = "…")] pub mod button;) and are additive. A component that composes another declares the dependency in Cargo.toml (dialog = ["button"]). No cfg logic may cross modules. - Facade features map to crates:
core(default),editor,data, … - Shared helpers promoted per D16 live in an ungated
commonmodule so every feature combination sees them. - CI checks three points, not a full matrix: default,
--no-default-features,--all-features, plus a compile check of each feature alone.
4.3 Component design
- One element at a time, review-gated (D20). Each component is one unit: API sketch → implementation → tests → gallery page → review. The next unit starts only after the previous one is approved. No batch waves.
- API shape follows gpui-kit
baseconventions (adapted, simplified where we can):- Controlled components: the owner passes the current value, the callback reports the next value, the owner updates state and calls
cx.notify().Checkbox::new(id).checked(v).on_change(move |next, window, cx| …). - One activation path: pointer, Enter, and Space all fire the same
on_click(gpui synthesizes keyboard clicks for tracked focus elements). - Semantic states as builders —
disabled(bool),selected(bool),checked(bool),pressed(bool)— resolved after the builder chain and painted from theme state colors; disabled resolves last and always wins. accessibility_label(...)on every control; correct accesskit role per control (Button,CheckBox,Switch,RadioButton,Link, …).- Composable parts (
CheckboxIndicator,SwitchTrack/Thumb) only where a component genuinely needs independently painted pieces — we paint from tokens, so most parts collapse into the component itself; decide per review.
- Controlled components: the owner passes the current value, the callback reports the next value, the owner updates state and calls
- Value-like controls are
#[derive(IntoElement)]+RenderOnce; stateful ones areEntity<XxxState>the application owns. - No style parameters (bezel's model): a component reads the theme global at paint time and takes zero color/size arguments. Overrides are gpui style modifiers chained by the caller.
- Behavior is inline. Activation, focus, keyboard, dragging, and a11y are written per component with gpui primitives (
Interactivity,FocusHandle+tab_stop(true),actions!, a11y builders). A helper is promoted to sharedcommononly when a third component needs it — no pre-built behavior layer. - Deliberate divergence from base: our
Linkopenshrefthrough the platform by default (cx.open_url) — native feel is the product — withon_clickas an override for apps that route internally. Base never opens URLs; we do unless told otherwise. - Each component has a named native reference per platform (NSButton / WinUI Button / Adwaita / Breeze) recorded on its gallery page; "native feel" is reviewed against those references.
- Naming seams per AGENTS.md:
is_/has_readers,with_builders,set_mutation,on_callbacks,State/Delegatesuffixes, stable domain-derivedElementIds.
4.4 Icons (crates/icons)
IconRole— a semantic enum of the glyphs UI actually needs (~25 to start): chevrons, check, close, plus/minus, search, folder, warning/info/success, copy, arrow-left/right, calendar, clock, settings, dots-horizontal, … Components and apps reference roles, never glyph names.- Resolver cascade per platform, behind one
Iconelement:- macOS: SF Symbols via
NSImage(systemSymbolName:), rasterized through AppKit at (size, weight, appearance, tint) and cached; never bundled. - Windows: Segoe Fluent Icons codepoints rendered as text (system font).
- Linux/Omarchy: freedesktop icon-theme lookup (active theme → hicolor), SVG painted via gpui's
svg(). - Fallback everywhere: a bundled Lucide subset (ISC; attribution in NOTICE) — ships first so components work before platform resolvers land.
- macOS: SF Symbols via
- Public API stays tiny:
icon(role)→ element with.size(...); tint defaults to theme tokens. A missing glyph logs once and falls back — never panics. - Gallery page: icon browser showing every role resolved on the current platform.
4.5 Theme pipeline
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 --> TK[token projection onto the Theme global]
TK --> WIN[all windows repaint]
EVT[runtime change events] -->|re-read, re-resolve, re-install| TK
- Read — one adapter per platform produces a normalized
SystemTheme(mode, accent, semantic palette, accessibility flags, style hint). - Detect — capability cascade: Omarchy marker → KDE (
kdeglobalspresent) → portal reachable (GNOME/generic Linux) → fallback. macOS/Windows by compile target. - Resolve — overlay chain: platform reader → bundled preset → user theme → runtime overrides → accessibility adjustments. Pure function, unit-testable without a GUI.
- Project —
ResolvedTheme→ the token structs components consume, installed on the themeGlobal. - Observe —
ThemeWatcherforwards real change events (distributed notifications, WinRTColorValuesChanged, portalSettingChanged, inotify on KDE/Omarchy files); each event re-runs read → resolve → project → repaint.
5. Theme engine (crates/theme)
5.1 Architecture
ThemeSourcetrait — one adapter per platform:snapshot() -> SystemThemepluswatch(callback). Adapters are thin over parseable payloads (ini text, registry dumps, portal replies) so parsing is fixture-testable on any OS.SystemTheme— normalized snapshot: mode, accent, semantic palette, high-contrast, reduce-motion, reduce-transparency, text-scaling, style hint, platform identity.ThemeDefinition— sparse TOML-serializableOption-field struct; bundled presets and user themes share one schema.ResolvedTheme— fully populated;resolve(system, definitions…, prefs)is pure.AccessibilityPreferences— text scaling multiplies font sizes (through rem, all rem-relative sizing); reduce-motion forwards to GPUI; reduce-transparency drops overlay backdrops; high-contrast sharpens borders and removes subtle fills.ThemeWatcher— owns adapter watches, drives the observe step.
5.2 Role mapping
| Token role | Source |
|---|---|
background / foreground |
window background / default text |
surface / surface_foreground |
cards / secondary areas |
primary, secondary, accent |
button + selection colors (macOS: accent-derived) |
muted |
secondary text and hover shades |
destructive |
danger role |
border / input |
control border line / field background |
ring |
focus ring |
selection |
text selection background |
Omarchy colors.toml maps almost 1:1: lighter_background → surface, dark_background/darker_background → elevation depth, dark_foreground → muted foreground, accent → primary/accent/ring, red → destructive, green/yellow/orange → success/warning. Every mapping lives in one module; never literals. Tests assert finite, distinct, semantically sane tokens for every preset × mode.
The set stays role-minimal and grows one concept at a time, driven by the unit that needs it. Known planned additions:
- Overlay scrim + shadow/elevation tokens — with the overlay units (Popover/Dialog/Menu, unit 11+); §5.5's popup elevation promise. No current role covers the modality backdrop.
success/warningroles — with the first severity-bearing component (Toast, badges, validation); the Omarchy mapping above already anticipates them.knob/ control-fill role — only if Slider (unit 7) confirms the Switch's borrowed roles (primary_foregroundthumb,borderoff-track) as a second consumer; until then no expansion.
Controls through unit 10 paint from existing roles and ramps.
5.3 Platform matrix
| Target | Detection | Sources read | Live changes |
|---|---|---|---|
| macOS | compile target | NSColor semantic palette (windowBackgroundColor, labelColor, controlColor, controlAccentColor, selectedTextBackgroundColor, keyboardFocusIndicatorColor, …) in the effective appearance via objc2-app-kit; NSWorkspace accessibility flags |
distributed notifications + GPUI appearance events |
| Windows 11 (WinUI) | compile target | WinRT UISettings (AccentColor, AccentLight1–3, AccentDark1–2, Background, Foreground), TextScaleFactor, AccessibilitySettings high-contrast; Personalize registry fallback |
UISettings.ColorValuesChanged |
| GNOME / generic Linux | portal reachable | portal org.freedesktop.appearance (color-scheme, accent-color, contrast, reduced-motion); gsettings fallback |
portal SettingChanged |
| KDE | kdeglobals present |
kdeglobals color groups + scheme hash + accent; portal for mode/accent |
inotify on kdeglobals |
| 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) |
| Fallback / unknown | none of the above, or reader failure | bundled neutral light/dark theme | manual only |
5.4 Accent ramp engine
Every target supplies one accent color (or a stated ramp). Where the platform states shades (WinUI AccentLight1–3/AccentDark1–2, KDE scheme ramps) use them as-is; elsewhere derive the state shades components need (hover, pressed, selected, disabled, subtle fills) in OKLCH with fixed lightness/chroma offsets toward the background or foreground, hue preserved. Derived colors compute once per resolve, never per frame.
5.5 Native metrics
Native feel is half geometry. Per-platform tables for: control heights and paddings, corner radii (WinUI control vs card rounding; macOS control radii), font fallback lists resolved through .SystemUIFont (SF Pro / Segoe UI Variable / Adwaita Sans / Noto Sans — no fonts bundled), focus-ring policy (macOS keyboard ring, WinUI two-layer ring, GTK outline), scrollbar style (overlay vs inline), popup elevation and menu metrics. Values are transcribed from each toolkit's reference metrics and verified with side-by-side screenshots (macOS in Phase B, the rest in Phase F).
5.6 Custom themes
ThemeDefinitionTOML: presets and user files share one schema; may override any role or metric, or just the accent (accent = "#7c3aed"→ full derived ramp).- Merge priority: platform reader → preset → user file(s) → runtime overrides → accessibility adjustments.
mode = "system" | "light" | "dark"forces a mode. - Sources:
--theme <preset|path>flag, a config directory scanned at startup, and anapplyAPI for in-app settings UIs. - Omarchy interop: the loader accepts Omarchy
colors.toml(semantic or ANSI) directly, so community themes drop in unchanged. - Live reload via
notify; parse errors surface as a notification, never a panic. - Follow-mode API:
follow_system(cx)keeps adapting to system changes; explicitapplydetaches;system_or_default()takes a one-time snapshot.
5.7 Implementation vehicles
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.
5.8 What we do differently from native-theme
Real semantic colors per platform (NSColor / UISettings / portal) instead of hardcoded approximations; OKLCH accent-ramp derivation; native metrics as a first-class deliverable; Omarchy as a first-class target; a designed neutral fallback; custom themes with overlay merge priority and live reload; a role-minimal token set instead of a 139-field toolkit mapping.
6. Component roadmap (review units, in order)
Each row is one review unit: build it, show it in the gallery, review, then move on.
| Unit | Crate | Component | Notes |
|---|---|---|---|
| 1 | core | Button | Variants (primary/secondary/ghost/destructive) painted from token ramps; single activation path; disabled + a11y role. The API template for everything after. |
| 2 | core | Checkbox | Controlled checked + on_change(next); Check icon from the icons crate. |
| 3 | core | Switch | Controlled pill + thumb; thumb slides once motion exists. |
| 4 | core | Radio + RadioGroup | Group owns the selected value; radios never self-uncheck. |
| 5 | core | Toggle | Controlled pressed (aria-pressed). |
| 6 | core | Link | href opens via platform by default; on_click overrides. |
| 7 | core | Slider | Click-to-set + in-track drag; controlled value. |
| 8 | core | Progress | Determinate + indeterminate. |
| 9 | core | Avatar | Initials; image slot later. |
| 10 | core | Input (single-line) then multiline, NumberInput | String buffer + unicode-segmentation word movement; the field owns its own selection. May seed crates/editor. |
| 11+ | core | Popover, Tooltip, Menu, Dialog, AlertDialog, Sheet, Toast | One unit each, same gate. |
| then | data (new) | List, Table, Tree, Tabs, Sidebar | Created on demand, same gate. |
| then | pickers or data | Calendar/DatePicker, TimeField, ColorPicker | Decide at unit time. |
| editor | editor (new) | Text/code editor, syntax highlighting (D12) | Same gate. |
Deferred until a real need: Markdown/TextView, Dock, VirtualList, window-wide text selection, a motion system (use gpui's built-in animations first), charts.
6.1 After the core units: sizing API — ✅ landed
Landed (2026-10-29): the Size enum (XSmall / Small / Medium default / Large / Custom(Pixels)), the Sizable trait (with_size(impl Into<Size>) + xsmall() / small() / large()), and the SizeTable of per-class control metrics (height, padding-x, text size, icon/indicator size, avatar size) now live where they belong — Size/SizeTable/ControlMetrics in the theme (they are native-metric values, §5.5), the trait in core's ungated common.
Our divergence from gpui-kit held: the per-size metrics resolve from the theme's size table at paint time, not from constants in the component layer. All ten controls implement Sizable; Size::Custom(side) derives the other metrics from the side length. Still open, when a real need arrives: TOML overrides for the size table in ThemeDefinition, and per-platform size tables from the Phase F native-metrics work.
7. Milestones
| Phase | Scope | Acceptance |
|---|---|---|
| A — scaffold (next) | Workspace per §4.1: theme stub, icons crate (IconRole + Lucide fallback), core crate with feature scaffolding (zero components), facade, gallery shell, CI incl. feature checks |
gallery opens a window reading the theme global; core builds at default / no-default / all / each-feature-alone |
| B — theme engine core | Token model + resolve + presets (neutral fallback, tokyo-night) + macOS source + ThemeWatcher; gallery theme inspector. Done: resolve overlay chain (base → system → preset → mode override → high-contrast) is pure and tested; OKLCH accent-ramp engine; NSColor/NSWorkspace adapter; distributed-notification watchers; inspector with live mode/preset switching. (The "Button + Checkbox painted from live tokens" proof moves to Phase C — those components land there; the gallery inspector proves the live pipeline meanwhile.) |
|
| C — controls | One review unit at a time (§6, units 1–9), each: API per §4.3 + tests + gallery page → review gate. macOS SF Symbols resolver behind the icon seam lands with the first unit that shows it matters | Per unit: activation, controlled round-trip, and disabled inertness tested; gallery page shows the live control against the theme; your approval closes the unit |
| D — input | Wave 2 | Single-line input with cursor, selection, clipboard; NumberInput |
| E — overlays | Wave 3 | Focus trapping and escape/dismiss semantics per overlay |
| F — platforms + data | crates/data + Windows/GNOME/KDE/Omarchy theme sources + Segoe Fluent/freedesktop icon resolvers |
Side-by-side gallery screenshots per platform |
| G — pickers + polish | Wave 5 → 0.1; start crates/editor if wanted |
— |
Gate for every phase: cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test, gallery smoke run.
8. Testing
Pure tests for resolve/overlay chain, payload parsing (fixtures), icon resolution tables, and metrics; context tests for the theme global lifecycle; headless UI tests for activation, controlled round-trips, focus, disabled; gallery smoke run. GPUI executor timers in tests. Only meaningful tests — state semantics, a11y mapping, invariants (AGENTS.md Tests rule).
9. Licensing
- All code in this repo is original: Apache-2.0 (D6).
- gpui: Apache-2.0 dependency, nothing copied.
- gpui-kit
base: API-design reference (D20); where a small utility is adapted rather than reinvented, keep Apache-2.0 compliance — note the origin in a file-header comment or NOTICE. - Lucide icons: ISC — bundled subset with attribution in NOTICE.
- SF Symbols and Segoe Fluent Icons: used through the operating system at runtime only; never extracted or redistributed.
- native-theme, gpui-omarchy, gpuikit, bezel: inspiration only; no code copied. If that ever changes, respect their licenses.
10. Risks
| Risk | Mitigation |
|---|---|
zed main churns gpui APIs |
Pinned rev; bumps are deliberate (re-verify, fix, note in commit message); CI catches drift early |
| Writing our own input/editor is the biggest lift | Single-line first; no LSP/highlighting/composition until needed; selection stays private to the field |
| Per-component features multiply configurations | Features gate whole modules, additive only; CI checks default / none / all / each-alone, not the full matrix |
| Platform icon lookups vary (missing fonts, themes) | Resolver cascade always ends in the bundled Lucide fallback; missing glyph logs once and falls back |
| Per-OS readers are a maintenance burden | Thin readers over parseable payloads + fixture tests; graceful fallback chain (system → preset → fallback theme) |
| "Native feel" is judgment | Named native reference per component; per-platform screenshot review each phase |
Omarchy colors.toml key set evolves |
Parse tolerantly (unknown keys ignored, missing keys fall back); track the theme-set script 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 |
11. Decisions
| # | Decision |
|---|---|
| D1 | Custom theme engine inspired by native-theme; no native-theme dependency |
| D3 | gpui from zed's repository only, pinned rev; no gpui-pre, no forks, no gpui-unofficial |
| D4 | Desktop only; no WASM |
| D6 | Apache-2.0 for this repo's code |
| D7 | gpui-native-kit stays the thin facade package name; it only re-exports crates behind features. Applications may depend on gpui-native-core directly |
| D8 | macOS first, then Windows 11 (WinUI), then GNOME → KDE → Omarchy; fallback theme from day one |
| D11 | Bundled presets: neutral fallback, tokyo-night, per-platform presets for testing readers; Omarchy files load as presets too |
| D12 | Editor deferred; crates/editor is created when wave 2's multiline editing or highlighting demands it |
| D13 | Accent shades hybrid: stated platform ramps where available (WinUI, KDE), OKLCH derivation elsewhere |
| D14 | Custom-theme live reload via notify |
| D15 | No wholesale porting, no UI-toolkit dependencies. Component APIs follow gpui-kit base conventions where they fit (D20), always simplified — never mirrored 1:1; small utilities may be adapted with attribution |
| D16 | No separate behavior crate; behavior lives in each component; shared helpers promoted at the third use |
| D17 | UPSTREAM.md removed; provenance = §3 pins + git history |
| D18 | Crate split for consumption: core (every-app components) → editor, data, … (advanced) → thin gpui-native-kit facade; one cargo feature per component, additive, module-gated |
| D19 | System icons via semantic IconRoles with per-platform resolvers (SF Symbols / Segoe Fluent Icons / freedesktop) and a bundled Lucide fallback; platform glyph sets are used at runtime only, never bundled |
| D20 | Components land one element at a time behind a review gate: API sketch → implementation → tests → gallery page → review → next. gpui-kit base is the API-design reference for these units; its conventions (controlled state, one activation path, semantic states, a11y labels) and selected utilities are adopted, simplified |
Dropped from rev 3: D2, D5, D9 (port-era decisions); the review gate returns in a lighter form as D20. D10 (no icon bundling) is superseded by D19.
12. Immediate next steps
Phase A scaffold✅ — workspace, toolchain pin, CI, theme seed, icons crate, core feature scaffolding, facade, gallery shell.Phase B theme engine core✅ — resolve pipeline, OKLCH ramps, presets, macOS source + watchers, gallery theme inspector.- Unit 1: Button (next) — API per §4.3 modeled on gpui-kit
base's Button (single activation path,disabled, a11y role + label), painted from theme ramps; tests for activation / disabled inertness / keyboard activation; gallery page. → your review, then unit 2 (Checkbox).