record phase 4 in the migration plan
This commit is contained in:
+81
-15
@@ -26,6 +26,12 @@ repository at `main`, and this workspace's `Cargo.lock` (zed at `4b47ceb`,
|
||||
were 1,592, and nothing outside `crates/ui` changed. The behavioural differences
|
||||
are named in phase 3 below; the largest is that base's toast stack replaces the
|
||||
fork's notification list.
|
||||
- **Phase 4: landed.** `switch`, `button`, `scroll/`, and `resizable/` run on base's
|
||||
controls; `avatar` was checked and deliberately left alone. `crates/ui/src` went
|
||||
from 12,922 lines to 10,830, and again nothing outside `crates/ui` changed. The
|
||||
only files inside `crates/ui` outside the migrated modules are the two dock files
|
||||
that consume `resizable`. The differences are named in phase 4 below; the one that
|
||||
wants an eye on it is a button's inherited line height.
|
||||
- One pre-existing, unrelated breakage was found; see
|
||||
[A pre-existing wasm blocker](#a-pre-existing-wasm-blocker).
|
||||
|
||||
@@ -88,7 +94,7 @@ which is the case when `ui::init` runs ahead of `theme::init`; `Theme::change` i
|
||||
hook that actually keeps the projection current.
|
||||
|
||||
It projects the color roles base can act on — the focus ring, the wash under selected
|
||||
text, scrollbars, and overlay backdrops — and nothing else:
|
||||
text, and overlay backdrops — and nothing else:
|
||||
|
||||
| `gpui_base::ColorTokens` | coop `ThemeColors` |
|
||||
| --- | --- |
|
||||
@@ -105,11 +111,14 @@ text, scrollbars, and overlay backdrops — and nothing else:
|
||||
|
||||
It also sets `ThemeAppearance` from coop's mode, `ScrollbarTheme`'s mode from coop's
|
||||
`scrollbar_mode`, and `TypographyTokens::sans` from coop's `font_family`. Radius,
|
||||
spacing, typography sizes, shadows, and scrollbar geometry keep their base defaults:
|
||||
coop has a single `radius`/`radius_lg`/`font_size` where base has six-point scales, so
|
||||
any mapping would be invented rather than derived. `ResizableTheme` needs nothing —
|
||||
base's documented `None` fallback already resolves to `border` at rest and `ring` while
|
||||
dragging, both of which are projected.
|
||||
spacing, typography sizes, shadows, and scrollbar geometry keep their base defaults
|
||||
here: coop has a single `radius`/`radius_lg`/`font_size` where base has six-point
|
||||
scales, so any mapping would be invented rather than derived. Two things base would
|
||||
otherwise paint from its own defaults are instead supplied by `crates/ui` at the call
|
||||
site, because they are per-element rather than global: `scroll/` passes coop's rail and
|
||||
thumb geometry through `ScrollbarStyles`, and `resizable/` paints the divider with a
|
||||
`ResizeHandleRenderer`. `ResizableTheme` and `ScrollbarStyles` are therefore never
|
||||
projected.
|
||||
|
||||
## What each module becomes
|
||||
|
||||
@@ -121,15 +130,15 @@ LOC is the count before the work; a module whose phase has landed reads
|
||||
| `input/` (input, clear_button) | 6,573 | Replace; keep `ui::input::{Input, InputEvent, InputState}` as the import path. 321 lines remain, and the engine paints itself through `InputEditorStyle` | `InputState`/`TextareaState` (`InputBaseState` in two modes) plus the `InputBase` frame |
|
||||
| `list/` | 1,477 | Delete | GPUI's own `list` (already in use) |
|
||||
| `checkbox.rs` | 312 | Delete | `Checkbox` |
|
||||
| `scroll/` (scrollbar, scrollable, scrollable_mask) | 1,332 | Replace; keep the `ScrollableElement` and `Scrollbar` names | `Scrollbar`, `ScrollableMask` |
|
||||
| `resizable/` | 927 | Replace; base exports the same names (`h_resizable`, `v_resizable`, `resizable_panel`, `PANEL_MIN_SIZE`, `resize_handle`) | `Resizable` + `ResizeHandleRenderer` for the coop hairline |
|
||||
| `scroll/` (scrollbar, scrollable, scrollable_mask) | 1,332 → 232 | Replace; keep the `ScrollableElement` and `Scrollbar` names. The two scrollbar files are gone and `scrollable.rs` is a thin layer over base | `Scrollbar` (`ScrollableMask` had no callers and is dropped) |
|
||||
| `resizable/` | 927 → 37 | Replace with a re-export of base's identically named API plus a `ResizeHandleRenderer` for the coop hairline | `Resizable*`, `resize_handle`, `PANEL_MIN_SIZE` |
|
||||
| `modal.rs` | 540 → 500 | Port onto base parts; `Modal`, `ModalButtonProps`, and `window.open_modal` unchanged. `Root` still owns the stack | `Dialog` — focus trap, Escape/Enter/backdrop dispatch, layer priority, deferred host |
|
||||
| `notification.rs` | 584 → 663 | Port; `Notification`, `NotificationKind`, and `window.push_notification` unchanged | `ToastManager` (storage, ids, timers, exit), `ToastStack` (geometry, motion), `Toast` (`Role::Alert`) |
|
||||
| `popover.rs` | 432 → 234 | Coop's builder over base's element; `PopoverState` is base's, re-exported | `Popover`, `Popup`, `Positioner` |
|
||||
| `tooltip.rs` | 36 → 37 | Coop's view rooted at base's element | `Tooltip` (`Role::Tooltip`) |
|
||||
| `button.rs` | 626 | Skin: base behavior plus coop's existing variant tables | `Button`, `StateStyle` |
|
||||
| `switch.rs` | 287 | Skin | `Switch`, `SwitchTrack`, `SwitchThumb` |
|
||||
| `avatar.rs` | 141 | Skin | `Avatar`, `AvatarImage`, `AvatarFallback` |
|
||||
| `button.rs` | 626 → 610 | Skin: base behavior plus coop's existing variant tables | `Button`, `StateStyle` |
|
||||
| `switch.rs` | 287 → 188 | Skin: base owns the toggle and its semantics; coop keeps the geometry, the label and the description | `Switch`, `SwitchTrack`, `SwitchThumb`, `spring` |
|
||||
| `avatar.rs` | 141 | Keep. Base's `Avatar` is an unstyled `Div` holding an image slot *or* a fallback slot, and its `AvatarImage` exposes neither `grayscale` nor `Img::with_fallback`; the fork needs a runtime load failure to swap in `brand/avatar.png`, which is a different thing from having no image. Nothing base offers is reachable from this module | `Avatar` (unusable here) |
|
||||
| `history.rs` | 184 | Delete. Base's input keeps its own `UndoManager`, and `UndoHistory` is a separate public utility the input never touches, so nothing has to be re-based. Its only consumer was `input/state.rs` | — |
|
||||
| `index_path.rs`, `element_ext.rs`, `event.rs`, `focusable.rs` | 156 | Delete | `IndexPath`, `ElementExt`, `InteractiveElementExt`. `FocusableCycle` has no counterpart — base's `FocusableExt` is a different concept (whether a component draws a focus ring) — so it is dropped rather than re-based |
|
||||
| `styled.rs`, `actions.rs`, `animation.rs` | 305 | Keep `ui::StyledExt`, `Size`, and `Sizable` as the app's import. `Selectable`, `Disableable`, and `Collapsible` now come from `gpui_base::component_traits`; the local three-line `h_flex`/`v_flex` wrappers stay rather than delegating to base's identical ones | `styled`, `StateStyle` |
|
||||
@@ -138,7 +147,9 @@ LOC is the count before the work; a module whose phase has landed reads
|
||||
| `dock/` + `tab/` | 3,356 | Keep for now; see phase 5 | base dock (different contract) |
|
||||
| `root.rs`, `window_ext.rs`, `title_bar.rs` | 965 | Keep; app shell. `Root` continues to host the dialog and toast layers. Its `focused_input` field and the two `WindowExtension` methods that read it are gone — the only thing that ever set them was the deleted input paint hook, and no crate consumed them | — |
|
||||
|
||||
Roughly 10k lines are removed, 3k are re-expressed as thin skins, and 8k are kept.
|
||||
Once phases 1–4 have landed, `crates/ui/src` is 39 files and 10,830 lines where it
|
||||
began at 71 and 21,876: roughly 11k lines removed, 2k re-expressed as thin skins,
|
||||
and the rest kept as the design system.
|
||||
|
||||
## Dependency change
|
||||
|
||||
@@ -394,7 +405,7 @@ import, restore and screening modals (a modal with a textarea, and one with
|
||||
`push_notification` site — sending an empty message, a failed upload with its retry
|
||||
action, and the device-approval notification that never auto-hides.
|
||||
|
||||
### Phase 4 — leaf controls, scroll, and resizable (one module per pull request)
|
||||
### Phase 4 — leaf controls, scroll, and resizable (one module per pull request) — landed
|
||||
|
||||
Order: `avatar`, `switch`, `button`, `scroll/`, `resizable/`. `button` is the
|
||||
largest skin: the `ButtonVariants` and `ButtonCustomVariant` tables, the `compact`,
|
||||
@@ -407,6 +418,52 @@ for the coop hairline.
|
||||
Each of these is independently shippable. Acceptance for each: no change outside
|
||||
`crates/ui`, and the surfaces that use the module are pixel-identical before and after.
|
||||
|
||||
What each module turned into:
|
||||
|
||||
| Module | LOC | Base now owns | Stayed in `ui` |
|
||||
| --- | --- | --- | --- |
|
||||
| `switch.rs` | 287 → 188 | `Role::Switch`, `aria_toggled`, focus tracking, Enter/Space and pointer activation, disabled inertness, the thumb's travel | both slots of the control, the label and description, the sizes and radii |
|
||||
| `button.rs` | 626 → 610 | `Role::Button`, focus tracking, Enter/Space and pointer activation, the disabled and selected precedence, the disabled `mouse_down` veto | every variant colour, the size and padding table, icon-only mode, `loading`, `caret`, `indicator`, `compact`, `rounded`, the tooltip |
|
||||
| `scroll/` | 1,332 → 232 | the whole scrollbar: geometry, fade, drag, hover and active states, and the handle traits for `ScrollHandle`, `UniformListScrollHandle` and `ListState` | `ScrollableElement`/`Scrollable` (base has no trait for attaching a scrollbar to an arbitrary element) and the projection of coop's rails and colours |
|
||||
| `resizable/` | 927 → 37 | the group and panel elements, the state, the drag arithmetic, the minimum-size clamp, the handle's hit area and cursor | the hover-only 1px hairline and its two colours |
|
||||
|
||||
`crates/ui` changed in three places outside those modules: `dock/dock.rs` and
|
||||
`dock/stack_panel.rs`, the two consumers of `resizable` (`resizable` is consumed
|
||||
nowhere else — it is `dock`'s machinery), and nowhere else. No call site outside
|
||||
`crates/ui` changed.
|
||||
|
||||
Behavioural differences, all of them base's:
|
||||
|
||||
- **`switch`**: activation moves from the fork's mouse-down on the whole row to
|
||||
base's click, Enter and Space on the switch. The label is inside the switch, so
|
||||
clicking it still toggles, and the control is now a tab stop that announces
|
||||
itself and its toggled state. It also no longer stops the press from reaching a
|
||||
parent; base stops it only while disabled. The thumb's 150 ms slide becomes base's
|
||||
critically damped 0.15 s spring, so the easing differs slightly, and reduced-motion
|
||||
is now honoured.
|
||||
- **`button`**: Enter and Space now activate the button and `Role::Button` is set
|
||||
with the label as its accessible name. Nothing draws a focus ring, which is
|
||||
unchanged. Base's root carries `line_height(relative(1.))` where the fork inherited
|
||||
GPUI's default `phi()`: the label already pins `relative(1.)` so it does not move,
|
||||
but text handed to `.child(..)` now sits tighter. This is the one difference a
|
||||
screenshot review should look for.
|
||||
- **`scroll`**: coop's rail and thumb are projected explicitly — a 10px rail, a 6px
|
||||
thumb inset by 1px with a 3px radius (8/1/4 whenever the mode is not `Scrolling`),
|
||||
a 48px minimum length, and the thumb's two palette colours — so the resting
|
||||
appearance does not move. Base's own fade timings replace the fork's
|
||||
`FADE_OUT_DURATION`/`FADE_OUT_DELAY`. `ui::scroll::ScrollbarState`, `PrepaintState`
|
||||
and `AxisPrepaintState` are gone; nothing outside `crates/ui/src/scroll` ever named
|
||||
them. The `is_inspector_picking` guard went with them, as coop never enables the
|
||||
inspector.
|
||||
- **`resizable`**: base keeps `sync_panels_count`, `update_panel_size` and
|
||||
`replace_panel` private, so the dock uses `reset_panel` for the one it needed. Base's
|
||||
built-in divider is always painted and takes its colours from `ResizableTheme`,
|
||||
which `sync_base` leaves at the `border`/`ring` fallback, so the divider is drawn by
|
||||
a `ResizeHandleRenderer` instead: the fork paints nothing at rest and `border` on
|
||||
hover, `border_selected` while dragging. `ResizeHandle::placement` takes
|
||||
`gpui_base::Side` rather than `DockPlacement`; the dock maps it, and base only
|
||||
distinguishes `Left`.
|
||||
|
||||
### Phase 5 — dock, tab, and menu (deliberately later)
|
||||
|
||||
Base has a full dock, but its contract is "layout is data, and the application
|
||||
@@ -432,7 +489,12 @@ There is no UI test suite to lean on, so each phase gets the same treatment:
|
||||
- Launch the app and walk the surfaces the phase touched. The settings dialog is the
|
||||
densest single smoke surface (Button, GroupBox, Switch, Input, DropdownMenu,
|
||||
PopupMenuItem), followed by the chat panel and the sidebar.
|
||||
- For phase 4, record before/after screenshots per module.
|
||||
- For phase 4, record before/after screenshots per module. These are still owed: the
|
||||
module work is compiled and linted but has not been walked by eye. The settings
|
||||
dialog covers `switch` and `button`, the chat list and the sidebar cover `scroll`,
|
||||
and any docked panel divider covers `resizable`. For `button`, watch text passed
|
||||
through `.child(..)` rather than `.label(..)`, which is the one place the inherited
|
||||
line height changes.
|
||||
- Keep the call-site diff at zero where the phase claims it — phases 1 and 3–4 do; if a
|
||||
call site has to change because base has no equivalent, list it in the pull request.
|
||||
Phase 2 needed six, tabulated above, and the list is the record of what "no equivalent"
|
||||
@@ -483,7 +545,11 @@ compile for `wasm32-unknown-unknown`".
|
||||
| 2 | Phase 1: base wiring, `sync_base`, deletions | `crates/theme` | landed |
|
||||
| 3 | Phase 2: input, plus `history.rs` and the `ropey`/`sum_tree`/`lsp-types`/`regex`/`unicode-segmentation`/`tree-sitter` pruning | `crates/workspace`, `crates/chat_ui` (six call sites); no manifest outside `crates/ui` | landed |
|
||||
| 4 | Phase 3: popover, modal, notification, tooltip | none | landed |
|
||||
| 5–10 | Phase 4: one leaf module each | none | not started |
|
||||
| 5 | Phase 4: `switch` | none | landed |
|
||||
| 6 | Phase 4: `button` | none | landed |
|
||||
| 7 | Phase 4: `scroll/` | none | landed |
|
||||
| 8 | Phase 4: `resizable/`, plus its two `dock` consumers | none; `dock/dock.rs` and `dock/stack_panel.rs` are inside `crates/ui` | landed |
|
||||
| — | Phase 4: `avatar` | none | no change; see phase 4 |
|
||||
| later | Phase 5: dock, as its own plan | `crates/workspace`, `crates/chat_ui` | not started |
|
||||
|
||||
The end state: the application keeps its design system and its call sites, `crates/ui`
|
||||
|
||||
Reference in New Issue
Block a user