add motion to switch
This commit is contained in:
+17
-11
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, BoxShadow, ElementId, InteractiveElement as _, IntoElement, ParentElement as _,
|
AnimationExt as _, App, BoxShadow, ElementId, InteractiveElement as _, IntoElement,
|
||||||
RenderOnce, Role, SharedString, StatefulInteractiveElement as _, Styled as _, Toggled, Window,
|
ParentElement as _, RenderOnce, Role, SharedString, SpringAnimation, SpringConfig,
|
||||||
div, px,
|
StatefulInteractiveElement as _, Styled as _, Toggled, Window, div, px,
|
||||||
};
|
};
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
|
|
||||||
@@ -100,11 +100,15 @@ impl RenderOnce for Switch {
|
|||||||
|
|
||||||
let knob = metrics.icon_size;
|
let knob = metrics.icon_size;
|
||||||
let inset = metrics.icon_size * 0.125;
|
let inset = metrics.icon_size * 0.125;
|
||||||
|
let travel = track_width - knob - inset * 2.;
|
||||||
|
|
||||||
let next = !self.checked;
|
let next = !self.checked;
|
||||||
let on_change = self.on_change;
|
let on_change = self.on_change;
|
||||||
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
let a11y_label = self.a11y_label.or_else(|| self.label.clone());
|
||||||
|
|
||||||
|
let anim_config = SpringConfig::new(500., 35., 1.);
|
||||||
|
let anim = SpringAnimation::new(anim_config).to(if self.checked { travel } else { px(0.) });
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id(self.id)
|
.id(self.id)
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
@@ -128,29 +132,31 @@ impl RenderOnce for Switch {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
div()
|
||||||
.flex_none()
|
.flex_none()
|
||||||
|
.relative()
|
||||||
.w(track_width)
|
.w(track_width)
|
||||||
.h(track_height)
|
.h(track_height)
|
||||||
.p(inset)
|
|
||||||
.rounded(theme.radius.full)
|
.rounded(theme.radius.full)
|
||||||
.when(self.checked, |track| {
|
.when(self.checked, |track| {
|
||||||
track
|
track
|
||||||
.bg(ramps.primary.base)
|
.bg(ramps.primary.base)
|
||||||
.justify_end()
|
|
||||||
.hover(|style| style.bg(ramps.primary.hover))
|
.hover(|style| style.bg(ramps.primary.hover))
|
||||||
})
|
})
|
||||||
.when(!self.checked, |track| {
|
.when(!self.checked, |track| track.bg(colors.border))
|
||||||
track.bg(colors.border).justify_start()
|
|
||||||
})
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
.absolute()
|
||||||
|
.top(inset)
|
||||||
|
.left(inset)
|
||||||
.size(knob)
|
.size(knob)
|
||||||
.flex_none()
|
|
||||||
.rounded(theme.radius.full)
|
.rounded(theme.radius.full)
|
||||||
.bg(colors.primary_foreground)
|
.bg(colors.primary_foreground)
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(colors.border),
|
.border_color(colors.border)
|
||||||
|
.with_spring("switch-thumb", anim, move |thumb, offset| {
|
||||||
|
thumb.left(inset + offset)
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.when_some(self.label, |switch, label| switch.child(label))
|
.when_some(self.label, |switch, label| switch.child(label))
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ gpui = { workspace = true }
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
gpui = { workspace = true, features = ["test-support"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
block2 = "0.6"
|
block2 = "0.6"
|
||||||
objc2 = "0.6"
|
objc2 = "0.6"
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ pub(crate) fn reapply(cx: &mut App) {
|
|||||||
let system = cx.global::<ThemeEngine>().source.snapshot(variant);
|
let system = cx.global::<ThemeEngine>().source.snapshot(variant);
|
||||||
let resolved = resolve::resolve(&system, preset.as_ref(), mode_override);
|
let resolved = resolve::resolve(&system, preset.as_ref(), mode_override);
|
||||||
|
|
||||||
|
cx.set_reduce_motion(resolved.accessibility.reduce_motion);
|
||||||
cx.set_global(Theme::from_resolved(resolved));
|
cx.set_global(Theme::from_resolved(resolved));
|
||||||
cx.refresh_windows();
|
cx.refresh_windows();
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -91,6 +91,7 @@ One-way, no cycles. Nothing depends on `gpui-component`, `gpui-kit`, `gpuikit`,
|
|||||||
- **Deliberate divergence from base:** our `Link` opens `href` through the platform by default (`cx.open_url`) — native feel is the product — with `on_click` as an override for apps that route internally. Base never opens URLs; we do unless told otherwise.
|
- **Deliberate divergence from base:** our `Link` opens `href` through the platform by default (`cx.open_url`) — native feel is the product — with `on_click` as 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.
|
- 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`/`Delegate` suffixes, stable domain-derived `ElementId`s.
|
- Naming seams per AGENTS.md: `is_`/`has_` readers, `with_` builders, `set_` mutation, `on_` callbacks, `State`/`Delegate` suffixes, stable domain-derived `ElementId`s.
|
||||||
|
- **Motion is inline and per component (D21).** Each component animates with gpui's own primitives (`with_animation`, `with_spring`, the gpui easings, `with_max_fps`) directly in its own file, a few lines at most, using theme durations where they're shared (`Motion.overlay`). No shared motion module, no ghost panels, no hover-wash plumbing; overlay exit animations and hover washes are dropped until a genuinely simple approach exists.
|
||||||
|
|
||||||
### 4.4 Icons (`crates/icons`)
|
### 4.4 Icons (`crates/icons`)
|
||||||
|
|
||||||
@@ -153,7 +154,7 @@ The set stays role-minimal and grows one concept at a time, driven by the unit t
|
|||||||
- ~~**Overlay scrim + shadow/elevation tokens**~~ — ✅ landed with unit 11 (2026-10-29): `colors.overlay` (translucent-black scrim, denser in dark; TOML-overridable; no platform states one), `Radius.overlay` (10 pt), `Elevations.popover`/`dialog` (transcribed NSPopover-class shadows, builder → `Vec<BoxShadow>`), and `Motion.overlay` (200 ms enter transition). Popover consumes elevation + motion; the scrim waits for Dialog.
|
- ~~**Overlay scrim + shadow/elevation tokens**~~ — ✅ landed with unit 11 (2026-10-29): `colors.overlay` (translucent-black scrim, denser in dark; TOML-overridable; no platform states one), `Radius.overlay` (10 pt), `Elevations.popover`/`dialog` (transcribed NSPopover-class shadows, builder → `Vec<BoxShadow>`), and `Motion.overlay` (200 ms enter transition). Popover consumes elevation + motion; the scrim waits for Dialog.
|
||||||
- ~~**Scrollbar thumb token + metrics**~~ — ✅ landed with the ScrollArea unit (2026-10-29): `colors.scrollbar_thumb` (translucent over content: black in light, light gray in dark; TOML-overridable) and `Theme.scrollbar` (`width` 8 pt, `inset` 2 pt, `min_thumb` 24 pt).
|
- ~~**Scrollbar thumb token + metrics**~~ — ✅ landed with the ScrollArea unit (2026-10-29): `colors.scrollbar_thumb` (translucent over content: black in light, light gray in dark; TOML-overridable) and `Theme.scrollbar` (`width` 8 pt, `inset` 2 pt, `min_thumb` 24 pt).
|
||||||
- ~~**`success` / `warning` roles**~~ — ✅ landed with Toast (2026-10-29): `colors.success`/`warning` (+ foregrounds; light and dark variants; TOML-overridable; no platform states them) and derived `Ramps.success`/`warning` via the OKLCH engine, matching the destructive treatment.
|
- ~~**`success` / `warning` roles**~~ — ✅ landed with Toast (2026-10-29): `colors.success`/`warning` (+ foregrounds; light and dark variants; TOML-overridable; no platform states them) and derived `Ramps.success`/`warning` via the OKLCH engine, matching the destructive treatment.
|
||||||
- ~~**Motion tokens**~~ — attempted (unit 14, 2026-10-30) and reverted the same day: the working set (`Curve` + CSS cubic-bezier, `Springs` presets, `[motion]` TOML overrides, an ungated `motion` module with an exit-lifecycle state machine and spring-blended hover washes, exit ghosts on all three overlays, a spring Switch thumb, a store-held toast exiting list) judged too complex for the value, and the motion itself not good enough. The motion system goes back to the drawing board — see the unit 14 row (§6) for what was tried. Two cheap pieces remain worth taking regardless of the rethink: forwarding `reduce_motion` to `cx.set_reduce_motion` in `reapply` (the OS flag is still read but ignored — every animation shipped so far disobeys Reduce Motion), and a 30 fps cap on Progress's indeterminate repeat.
|
- **Motion tokens** — after the reverted framework attempt (2026-10-30), motion stays minimal: `Motion.overlay` (200 ms) is the only token. Components reach for gpui's own easings and `SpringConfig` inline (D21); a token is added only when a third component shares the same value (D16). TOML `[motion]` overrides and per-platform motion presets wait for a real need. ~~The reduce-motion forwarding fix~~ ✅ landed (pass a, 2026-10-30): `reapply` calls `cx.set_reduce_motion(resolved.accessibility.reduce_motion)`, so gpui's animation elements snap under the OS setting.
|
||||||
- **`knob` / control-fill role** — only if Slider (unit 7) confirms the Switch's borrowed roles (`primary_foreground` thumb, `border` off-track) as a second consumer; until then no expansion.
|
- **`knob` / control-fill role** — only if Slider (unit 7) confirms the Switch's borrowed roles (`primary_foreground` thumb, `border` off-track) as a second consumer; until then no expansion.
|
||||||
|
|
||||||
Controls through unit 10 paint from existing roles and ramps.
|
Controls through unit 10 paint from existing roles and ramps.
|
||||||
@@ -175,7 +176,7 @@ Every target supplies one accent color (or a stated ramp). Where the platform st
|
|||||||
|
|
||||||
### 5.5 Native metrics
|
### 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. Motion presets are metrics too: each platform states its curves and durations (macOS tuned to NSPopover/NSMenu transitions, Windows the Fluent decelerate cubic-bezier, GNOME/KDE shell timings) — the token shape is open again after the unit 14 revert, and Phase F sources fill it once the rethink lands. 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).
|
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. Motion stays per-component timing on gpui's own primitives; when a platform source needs to state it (macOS NSPopover-class timing, Fluent's decelerate curve, GNOME/KDE shell timings), it states `Motion.overlay` first and further tokens arrive per the role-minimal rule. 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
|
### 5.6 Custom themes
|
||||||
|
|
||||||
@@ -214,12 +215,12 @@ Each row is one review unit: build it, show it in the gallery, review, then move
|
|||||||
| 12 | core | Menu | ✅ Landed (2026-10-29). Controlled `open`/`on_change` + `trigger`/`items` (MenuItem: label, icon, shortcut hint, disabled, destructive, checked, separator). Popover shell patterns duplicated per D16 (promote at Dialog); panel = surface + overlay radius + popover elevation, role Menu; claims focus while open; keyboard nav — arrows/home/end over selectable items (wrapping, skipping separators + disabled), enter/space activates (closes menu before the action), escape dismisses; hover selects, click activates; trigger clicks toggle (popover-owned); selection resets on each open; long menus scroll via the ScrollArea once a height is styled. Deferred: submenus, type-ahead, scroll-selected-item-into-view, real shortcut handling |
|
| 12 | core | Menu | ✅ Landed (2026-10-29). Controlled `open`/`on_change` + `trigger`/`items` (MenuItem: label, icon, shortcut hint, disabled, destructive, checked, separator). Popover shell patterns duplicated per D16 (promote at Dialog); panel = surface + overlay radius + popover elevation, role Menu; claims focus while open; keyboard nav — arrows/home/end over selectable items (wrapping, skipping separators + disabled), enter/space activates (closes menu before the action), escape dismisses; hover selects, click activates; trigger clicks toggle (popover-owned); selection resets on each open; long menus scroll via the ScrollArea once a height is styled. Deferred: submenus, type-ahead, scroll-selected-item-into-view, real shortcut handling |
|
||||||
| 11 | core | Popover | ✅ Landed (2026-10-29). First overlay. Controlled `open` + `on_change` (Rc-shared handler); `trigger`/`content` element closures; panel = surface + `Radius.overlay` + `Elevations.popover` + border, role Dialog with label; `anchored()` + `deferred()` (window-overflow flip for free); enter fade via gpui `with_animation` at `Motion.overlay` (respects reduce-motion); claims keyboard focus while open; escape and click-outside dismiss (outside clicks swallowed). No scrim — native macOS popover model; the overlay token waits for Dialog. Known v1 gaps: no exit animation, trigger aria-expanded left to the app |
|
| 11 | core | Popover | ✅ Landed (2026-10-29). First overlay. Controlled `open` + `on_change` (Rc-shared handler); `trigger`/`content` element closures; panel = surface + `Radius.overlay` + `Elevations.popover` + border, role Dialog with label; `anchored()` + `deferred()` (window-overflow flip for free); enter fade via gpui `with_animation` at `Motion.overlay` (respects reduce-motion); claims keyboard focus while open; escape and click-outside dismiss (outside clicks swallowed). No scrim — native macOS popover model; the overlay token waits for Dialog. Known v1 gaps: no exit animation, trigger aria-expanded left to the app |
|
||||||
| 11+ | core | Tooltip ✅ (2026-10-29), Dialog + AlertDialog ✅ (2026-10-29), Toast ✅ (2026-10-29) | One unit each, same gate. Tooltip rides gpui's own `.tooltip()` lifecycle (hover delay, anchoring, dismissal — all framework-owned); ours is the styled view (`surface` + border + `Radius.sm`, small text) plus a `Tooltip::text` closure and a Button passthrough. Dialog + AlertDialog (one component, `alert(true)` switches semantics): scrim (`colors.overlay`, occluding) + centered panel (surface, `Radius.overlay`, `Elevations.dialog`), role Dialog/AlertDialog, focus claimed while open; modal kind closes on escape + scrim click, alert kind is inert to both (the user answers through the actions); actions footer is caller-built elements closing through owner state. Toast: `Toasts` store entity (push/dismiss/clear + auto-dismiss timers on the GPUI executor, `Toast::DURATION` 5 s, zero = sticky) + `ToastsView` deferred stack (top-right, `Radius.overlay` + popover elevation, severity icon from the new success/warning ramps, close button, `on_dismiss` windowless handler). Toast landed the deferred `success`/`warning` tokens (§5.2). Sheet dropped from the roadmap |
|
| 11+ | core | Tooltip ✅ (2026-10-29), Dialog + AlertDialog ✅ (2026-10-29), Toast ✅ (2026-10-29) | One unit each, same gate. Tooltip rides gpui's own `.tooltip()` lifecycle (hover delay, anchoring, dismissal — all framework-owned); ours is the styled view (`surface` + border + `Radius.sm`, small text) plus a `Tooltip::text` closure and a Button passthrough. Dialog + AlertDialog (one component, `alert(true)` switches semantics): scrim (`colors.overlay`, occluding) + centered panel (surface, `Radius.overlay`, `Elevations.dialog`), role Dialog/AlertDialog, focus claimed while open; modal kind closes on escape + scrim click, alert kind is inert to both (the user answers through the actions); actions footer is caller-built elements closing through owner state. Toast: `Toasts` store entity (push/dismiss/clear + auto-dismiss timers on the GPUI executor, `Toast::DURATION` 5 s, zero = sticky) + `ToastsView` deferred stack (top-right, `Radius.overlay` + popover elevation, severity icon from the new success/warning ramps, close button, `on_dismiss` windowless handler). Toast landed the deferred `success`/`warning` tokens (§5.2). Sheet dropped from the roadmap |
|
||||||
| 14 | core + theme | Motion | ⏪ Attempted and reverted (2026-10-30). What was tried: theme `Motion` tokens (overlay duration + `Curve` with a CSS cubic-bezier + spring presets, `[motion]` TOML); an ungated core `motion` module (curve→easing, `ExitLifecycle` exit ghosts via keyed window state, spring hover washes); consumers — exit ghosts on Popover/Menu/Dialog (scrim fading both ways), Switch thumb on `with_spring`, Toast fade/slide + a store-held exiting list, Button + toast-close hover washes, Progress capped at 30 fps. All reverted — judged too complex for the value and not good enough to watch. The rethink starts from what the attempt proved: gpui's `with_spring`/`with_animation` are the right substrate (D21) and keyed window state CAN drive overlay exits; the cost centered on the exit-lifecycle plumbing and per-control wash machinery. Keep the plan open, redesign before reimplementing |
|
| 14 | core + theme | Motion (per component) | Replaces the reverted 2026-10-30 framework attempt — no token tower, no `motion` module, no ghost panels; just gpui's own primitives used inline, one separately reviewed pass per component, each a few lines: (a) theme: ✅ landed — `reapply` forwards `reduce_motion` via `cx.set_reduce_motion`, so gpui snaps every animation under the OS setting; (b) Switch: ✅ landed — the thumb absolute-positions in the track and slides on `with_spring` (inline `SpringConfig::new(500, 35, 1)`, `Pixels` target, mounts at its target so the initial render never animates; velocity survives mid-flight retargets); (c) Toast: enter fade via `with_animation`; (d) Dialog: the scrim enters with the panel instead of snapping; (e) Progress: cap the indeterminate sweep at 30 fps. Overlay enter fades already exist from units 11/12. Exits and hover washes stay out — they were the complexity sink (§ deferred list). Each pass reviewed in the gallery on its own |
|
||||||
| then | data (new) | List, Table, Tree, Tabs, Sidebar | Created on demand, 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. |
|
| then | pickers or data | Calendar/DatePicker, TimeField, ColorPicker | Decide at unit time. |
|
||||||
| editor | editor (new) | Text/code editor, syntax highlighting (D12) | Same gate. |
|
| 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 (unit 14 reverted; rethink before reimplementing), charts.
|
Deferred until a real need: Markdown/TextView, Dock, VirtualList, window-wide text selection, overlay exit animations and hover washes (dropped with the 2026-10-30 revert; only return if a genuinely simple approach appears), charts.
|
||||||
|
|
||||||
### 6.1 After the core units: sizing API — ✅ landed
|
### 6.1 After the core units: sizing API — ✅ landed
|
||||||
|
|
||||||
@@ -255,7 +256,7 @@ Deferred until a real need: bidi/RTL; prefix/suffix adornments and a form field
|
|||||||
| 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 |
|
| 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 |
|
| D — input | Wave 2 | Single-line input with cursor, selection, clipboard; NumberInput |
|
||||||
| E — overlays | Wave 3 | Focus trapping and escape/dismiss semantics per overlay |
|
| E — overlays | Wave 3 | Focus trapping and escape/dismiss semantics per overlay |
|
||||||
| E+ — motion | Unit 14 (§6): Motion tokens + reduce-motion forwarding + overlay exits + Switch thumb + hover washes | ⏪ Attempted and reverted (2026-10-30); rethink pending. The reduce-motion forwarding fix and the Progress 30 fps cap stay on the table as standalone wins |
|
| E+ — motion | Unit 14 (§6): per-component passes, each a few lines on gpui's own primitives — forwarding, Switch thumb, Toast enter, Dialog scrim, Progress cap | Each pass reviewed in the gallery separately; reduce-motion snaps gpui-side once the flag is forwarded |
|
||||||
| F — platforms + data | `crates/data` + Windows/GNOME/KDE/Omarchy theme sources + Segoe Fluent/freedesktop icon resolvers | Side-by-side gallery screenshots per platform |
|
| 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 | — |
|
| G — pickers + polish | Wave 5 → 0.1; start `crates/editor` if wanted | — |
|
||||||
|
|
||||||
@@ -307,7 +308,7 @@ Pure tests for resolve/overlay chain, payload parsing (fixtures), icon resolutio
|
|||||||
| 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 |
|
| 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 `IconRole`s 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 |
|
| D19 | System icons via semantic `IconRole`s 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 |
|
| 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 |
|
||||||
| D21 | Motion is a thin layer over gpui's pinned-rev animation/spring primitives (`with_animation`, `with_spring` with velocity retention, built-in reduce-motion snapping, `max_fps`) — no parallel motion framework (bezel's Painter/Fade store and pulse clock rejected); the theme owns durations, curves (`ease_out_quint` default + CSS-spec `CubicBezier`), and spring presets; hover washes ride settled-spring idleness on controls only, data rows snap. ⏪ Under re-evaluation after the unit 14 revert (2026-10-30): the substrate still holds, the execution complexity does not — the rethink may narrow scope to overlay enters/exits only |
|
| D21 | Motion is per component and inline: each component uses gpui's own animation/spring primitives (`with_animation`, `with_spring`, the gpui easings, `with_max_fps`) directly in its file; the theme owns only the durations components actually share (today `Motion.overlay`), growing per the role-minimal rule. No shared motion module, no exit ghosts, no hover-wash machinery — exits and washes stay out until a genuinely simple approach exists. Reduce motion is gpui-side: forward the OS flag once in `reapply` and every primitive snaps. Supersedes the reverted 2026-10-30 framework attempt (which proved the substrate but over-built the plumbing) |
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@@ -318,7 +319,8 @@ Dropped from rev 3: D2, D5, D9 (port-era decisions); the review gate returns in
|
|||||||
3. ~~Phase C controls~~ ✅ — units 1–9 (Button → Avatar), each through the review gate; sizing API (§6.1).
|
3. ~~Phase C controls~~ ✅ — units 1–9 (Button → Avatar), each through the review gate; sizing API (§6.1).
|
||||||
4. ~~Phase D input~~ ✅ — single-line Input (unit 10) + the §6.2 completion pass (modifiers, undo/redo, read-only, actions + key context, caret blink, paint polish). NumberInput and multiline remain open as deferred units.
|
4. ~~Phase D input~~ ✅ — single-line Input (unit 10) + the §6.2 completion pass (modifiers, undo/redo, read-only, actions + key context, caret blink, paint polish). NumberInput and multiline remain open as deferred units.
|
||||||
5. ~~Phase E overlays~~ ✅ — Popover (unit 11), ScrollArea (unit 11.5), Menu (unit 12), Tooltip + Dialog/AlertDialog + Toast (unit 11+ row; Sheet dropped). Phase gate held: escape/dismiss semantics and focus behavior per overlay.
|
5. ~~Phase E overlays~~ ✅ — Popover (unit 11), ScrollArea (unit 11.5), Menu (unit 12), Tooltip + Dialog/AlertDialog + Toast (unit 11+ row; Sheet dropped). Phase gate held: escape/dismiss semantics and focus behavior per overlay.
|
||||||
6. ~~Unit 14 — Motion~~ ⏪ Attempted and reverted (2026-10-30) — judged too complex, and the motion not good enough; the plan and the lessons stay (§6 row 14, D21). Next: **Phase F** — `crates/data` (List, Table, Tree, Tabs, Sidebar) + Windows/GNOME/KDE/Omarchy theme sources (each states its Motion preset, §5.5, once the rethink lands) + Segoe Fluent/freedesktop icon resolvers, with side-by-side gallery screenshots per platform. NumberInput/multiline return as deferred units when needed. Cheap standalone wins to fold in anywhere: `cx.set_reduce_motion` forwarding in `reapply` (§5.2), Progress 30 fps cap.
|
6. **Next: unit 14 — motion, per component (§6 row 14, D21)** — ~~(a) theme `reduce_motion` → `cx.set_reduce_motion` in `reapply`~~ ✅ landed; ~~(b) Switch thumb `with_spring`~~ ✅ landed. Remaining passes, each small and separately reviewed: (c) Toast enter fade; (d) Dialog scrim enters with the panel; (e) Progress 30 fps cap. No shared machinery; exits and hover washes stay deferred (§6 deferred list).
|
||||||
7. Then — Phase G: pickers, polish, → 0.1; start `crates/editor` if wanted.
|
7. Then — Phase F: `crates/data` (List, Table, Tree, Tabs, Sidebar) + Windows/GNOME/KDE/Omarchy theme sources (a source may state `Motion.overlay` when it needs platform timing, §5.5) + Segoe Fluent/freedesktop icon resolvers, with side-by-side gallery screenshots per platform. NumberInput/multiline return as deferred units when needed.
|
||||||
|
8. Then — Phase G: pickers, polish, → 0.1; start `crates/editor` if wanted.
|
||||||
|
|
||||||
Batched polish candidates between units: scrollbar auto-hide + horizontal thumb, `Undo`/`Redo` menu events.
|
Batched polish candidates between units: scrollbar auto-hide + horizontal thumb, `Undo`/`Redo` menu events.
|
||||||
|
|||||||
Reference in New Issue
Block a user