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
Once the core components are done (units 1–10), add a Sizable trait so applications can customize component size, following gpui-kit's shape: a Size enum (XSmall / Small / Medium default / Large, plus a custom Pixels variant) and with_size(impl Into<Size>) with xsmall() / small() / large() conveniences.
Our divergence from gpui-kit: the per-size metrics (control heights, paddings, text sizes, knob/indicator diameters) resolve from the theme, not from constants in the component layer — they are native-metric values (§5.5) and every component already reads its metrics through tokens. Implementation shape: the theme gains a size-class table (Size → control height/padding/text metrics), and each control implements Sizable by reading that table at paint time. Retrofits land one component per unit (Button first, as the API template) with the trait living in the ungated common module.
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).