From 0d4acf5b659f3cc8fc579d75e4e3bf9dfebd96d7 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Wed, 21 Oct 2026 08:49:01 +0700 Subject: [PATCH] update plan --- docs/native-theme-migration.md | 78 ++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/docs/native-theme-migration.md b/docs/native-theme-migration.md index 336a0042..d6233d35 100644 --- a/docs/native-theme-migration.md +++ b/docs/native-theme-migration.md @@ -10,6 +10,13 @@ through its official `native-theme-gpui` connector. > mapping). Going through GPUI-Kit first makes that mapping unnecessary: the > connector already projects native themes onto gpui-component's `Theme`, and > every call site reads `cx.theme()` from gpui-component directly. +> +> **Revision 2.** Full replacement, no hybrids: no custom UI element is kept +> or maintained after Phase A. Earlier hybrid positions in this file (keep +> `ui::message` where it fits, possibly keep nav composites, keep an icon +> mapping shim) are dropped — kit elements are adopted outright, chat-specific +> composition moves into `chat_ui`, and the only coop-owned visuals left are +> brand SVG assets rendered through the kit `Icon`. ## 1. What the git history shows @@ -42,6 +49,8 @@ graph LR components and moves all color reads onto gpui-component's `Theme` (`ThemeColor`, 139 semantic fields). The app runs on gpui-component's built-in light/dark palettes at the end of Phase A; `crates/theme` is gone. +At the end of Phase A `crates/ui` contains no UI elements: every element +comes from the kit, and only app utilities survive there (§4.4). **Phase B** adds `native-theme-gpui`, which installs native OS themes (or any of 16 bundled presets) into that same `Theme`. No call site changes — the @@ -69,15 +78,15 @@ crates. |---|---|---| | `menu/` (popup, dropdown, item) | 983 | `menu::{PopupMenu, PopupMenuItem, DropdownMenu, ContextMenu}` | | `dock/` (mod, panel) | 1,032 | `dock` (entity-backed DockArea / panels) | -| `motion.rs` | 668 | keep — app-level animation, no theme dependency | +| `motion.rs` | 668 | keep — animation utility, not an element; no theme dependency | | `notification.rs` | 594 | `notification::Notification` + `WindowExt::push_notification`; placement via `Theme.notification` | | `button.rs` | 581 | `button::{Button, ButtonGroup, Toggle, DropdownButton}` | -| `message.rs` | 553 | evaluate `message::{Message, Bubble, Attachment, MessageScroller}`; keep as app composite where it fits better | +| `message.rs` | 553 | `message::{Message, Bubble, MessageGroup, MessageScroller}` (avatar/header/content/footer slots); author grouping, date separators and receipts move into `chat_ui` as app code | | `avatar.rs` | 445 | `avatar::Avatar` | | `root.rs` | 430 | framework `Root` via `gpui_kit::open_window` (owns dialogs, notifications, tooltip/menu layers, focus traps) | | `dialog.rs` | 394 | `WindowExt::open_dialog` / `AlertDialog`; custom footer buttons → Dialog's footer API | | `input/` | ~290 | `input::{Input, InputState, Textarea, NumberInput}`; delete the custom styling (its `input_bg`/`selection`/`caret` reads die with it) | -| `icon.rs` | 274 | `Icon` + `IconName` (Lucide); keep `ui::icon` as a thin mapping shim (see §4.3) | +| `icon.rs` | 274 | kit `Icon` + `IconName` (Lucide) at every call site; the module and the coop enum are deleted (see §4.3) | | `kbd.rs` | 270 | `Kbd` | | `tab/` | 385 | `tab::{Tab, TabBar, Tabs}` | | `title_bar.rs` | 218 | `TitleBar` (keep the client-side-decoration constants) | @@ -85,12 +94,12 @@ crates. | `styled.rs` | 161 | `h_flex`/`v_flex`/`StyledExt` from the kit; drop coop-specific helpers as their users migrate | | `switch.rs` | 157 | `switch::Switch` | | `markdown.rs` | 146 | `text::TextView::markdown` (code styling via `Theme.highlight_theme`) | -| `nav.rs` + `nav_item.rs` | 268 | `sidebar::{Sidebar, SidebarMenu, …}`; if the coop layout doesn't fit, keep as an app composite on top of kit primitives — decide during the slice | +| `nav.rs` + `nav_item.rs` | 268 | `sidebar::{Sidebar, SidebarHeader, SidebarGroup, SidebarMenu, SidebarMenuItem, SidebarFooter}`; the nav layout is adapted to the kit structure, not preserved | | `group_box.rs` | 126 | `group_box::GroupBox` | | `scroll/` | ~124 | `scroll::{Scrollbar, Scrollable}` | | `resizable/` | 31 | `resizable::{h_resizable, v_resizable, resizable_panel}` | | `tooltip.rs` | 34 | `.tooltip()` | -| `indicator.rs` | 59 | `Marker`/`Badge`, or keep if neither fits | +| `indicator.rs` | 59 | `marker::Marker` / `badge::Badge`; the custom module is deleted either way | | `window_ext.rs` | 90 | `gpui_kit::component::WindowExt` | ## 4. Phase A — GPUI-Kit migration @@ -102,6 +111,10 @@ crates. installs the framework `Root` (dialog, sheet, notification and tooltip layers). Delete `ui::Root` and its layers; `workspace::init` stays as the window's content view. +- Register a combined `AssetSource` at app construction: coop's existing + per-platform source for `assets/brand` and images, with + `gpui_kit::assets::Assets` as the fallback for the component icon bundle + (embedded on desktop; CDN-on-demand on wasm). - `TitlebarOptions` / client-side decoration setup stays coop's (`CLIENT_SIDE_DECORATION_*`, `TABBAR_HEIGHT` move to `ui`). - Keep `bind_keys` **before** `set_menus` (guide rule); coop already does. @@ -112,10 +125,12 @@ Replace one family at a time; consumers update in the same PR: 1. **Deps + bootstrap** (above). Custom components still work alongside the framework Root. -2. **Icons** — introduce the `ui::icon` shim so later slices see kit icons - through the old call sites (see §4.3). +2. **Icons** — rewrite every icon call site onto kit `Icon`/`IconName` and + delete `ui::icon` in the same PR (see §4.3). Mechanical churn: one Lucide + target per coop variant, `Icon::path` for brand marks; doing it early + settles the imports for all later slices. 3. **Small controls** — Button, Switch, Kbd, GroupBox, Avatar, Tooltip, - Indicator, Popover. + Popover; Indicator → `Marker`/`Badge`. 4. **Menu** — PopupMenu/DropdownMenu/ContextMenu (+ the popup-menu actions already imported from `gpui_base::actions`). 5. **Input** — Input/InputState/Textarea; port the chat composer, dialog @@ -124,26 +139,30 @@ Replace one family at a time; consumers update in the same PR: `push_notification`; port the settings, relays, profile, new-chat, import… dialogs; the custom footers move to the Dialog footer API. 7. **Scroll + Resizable + Tab** — panels and the workspace tab strip. -8. **Sidebar/Nav** — Sidebar family (or keep composites, per the table). +8. **Sidebar/Nav** — port the nav onto `sidebar::{Sidebar, SidebarGroup, + SidebarMenu, …}` and delete `nav.rs`/`nav_item.rs` in the same PR. 9. **Title bar** — kit `TitleBar` + traffic-light handling. 10. **Dock** — the biggest single item (805 + 205 lines, `dock::add` registry, `DockPlacement`). Port panels onto `DockArea`; do it last so the panels it hosts are already kit-based. -11. **Message + Markdown** — TextView::markdown; Message/Bubble where they - fit, otherwise keep `ui::message` as an app composite (it is domain - layout, allowed by the guides' app-component layer). -12. **Cleanup** — delete the replaced modules; decide what `crates/ui` still - is (see §4.4). +11. **Message + Markdown** — TextView::markdown; port chat rows onto + `Message`/`Bubble`/`MessageGroup` + `MessageScroller`. Chat-specific + composition (author grouping, date separators, receipts) is app code in + `chat_ui`, built from kit elements — `crates/ui` keeps no message + component. +12. **Cleanup** — delete the replaced modules; `crates/ui` ends as a + utilities-only crate (see §4.4). ### 4.3 Icons -coop's `IconName` has 128 variants backed by `assets/icons/*.svg`; the kit's -`IconName` (Lucide) has 106. Keep `ui::icon` as a mapping module: a -coop-`IconName` enum (same variant names) that resolves to the kit `IconName` -where a Lucide equivalent exists, and to a custom asset path (coop's bundled -SVGs) where it doesn't (brand marks, a few specials). Most `Icon::new(...)` -call sites then keep compiling with import changes only. Delete -`assets/icons` entries that get Lucide equivalents; keep `assets/brand`. +No mapping shim and no coop `IconName` enum: every call site uses the kit's +`Icon` + `IconName` (Lucide) directly. During slice 2, give each of coop's +128 variants a Lucide target — exact equivalent where one exists, nearest +match otherwise, accepting the visual difference. The only coop-owned SVGs +that survive are true brand/product marks under `assets/brand`, rendered +through the kit element with `Icon::path("brand/….svg")` (the kit `Icon` +accepts any asset path). Delete `assets/icons` entirely once the last call +site is mapped. ### 4.4 Theme removal @@ -188,11 +207,14 @@ call sites then keep compiling with import changes only. Delete `colors.rs`, `palette.rs`, `brand.rs`, `appearance.rs`, `paint.rs`, `color.rs`, `syntax.rs`, `notification.rs`, `scrollbar_mode.rs`, `platform_kind.rs`; - - move to `crates/ui` unchanged: `typography.rs` (`TextStyle`/`Typeset`, + move to `crates/ui` unchanged: `typography.rs` (`TextStyle`/`Typeset`, ~200 call sites — retained deliberately as coop's type ladder), `layout.rs` (spacing constants, radius accessors), `geometry.rs` (`Edges`/`Side`/`AxisExt`), and the three `CLIENT_SIDE_DECORATION_*` constants; + - end state: `crates/ui` is utilities-only — `motion.rs`, `typography.rs`, + `layout.rs`, `geometry.rs`, the CSD constants and the combined asset + source. Every element module is deleted; nothing custom is maintained; - `AppearanceMode` (System/Light/Dark, serde-persisted) moves into `crates/settings`. - The gpui-base projection coop did in `sync_base` is now the framework's job @@ -262,7 +284,11 @@ to native targets; on web, apply the fallback preset (or extend - dock panels move, split and zoom; sidebar resizes; scrollbars behave in Hover/Always/Scrolling modes; - markdown code blocks render through `TextView` with syntax colors from - `Theme.highlight_theme`. + `Theme.highlight_theme`; + - every icon renders from the kit Lucide bundle, and brand marks render + via `Icon::path` on desktop and web (on wasm the kit bundle is fetched + from CDN on demand; coop brand assets still come from coop's own + source). - End of Phase B: - macOS: OS light/dark follows; pinned modes; accent changes picked up at next launch (documented limitation — no `watch` feature); @@ -276,11 +302,11 @@ to native targets; on web, apply the fallback preset (or extend | Risk | Mitigation | |---|---| -| Scale: ~8.7k lines of custom UI, ~140 `ui::` imports, ~340 token reads | Slices in §4.2 each compile; `crates/ui` acts as the facade during the migration so consumer churn is incremental | +| Scale: ~8.7k lines of custom UI, ~140 `ui::` imports, ~340 token reads | Slices in §4.2 each compile; `crates/ui` acts as the facade during the migration so consumer churn is incremental, then shrinks to utilities (§4.4) | | Dock port (largest, most stateful) | Scheduled last; panels are kit-based by then; DockArea owns layout state coop currently hand-rolls | | gpui-component on wasm | Spike in §6 before committing; fallback would be staying on 0.7.1 desktop-only and parking the web target | -| Visual identity changes (shadcn-style stock palette in Phase A) | Accepted — requirement says the current palette need not survive; Phase B replaces it with native themes anyway | -| Icon gaps (coop 128 vs Lucide 106) | Mapping shim keeps coop names; unmatched variants fall back to bundled SVGs | +| Visual identity changes (shadcn-style stock palette in Phase A; kit Message/Sidebar look replacing coop's chat/nav styling) | Accepted — requirement says the current palette need not survive; Phase B replaces it with native themes anyway | +| Icon gaps (coop 128 vs Lucide 106) | Nearest-Lucide target per variant in slice 2; only true brand marks stay as coop assets via `Icon::path` | | API drift between coop's wrappers and the kit (e.g. `Sizable`, `Disableable`, callback shapes) | Per the coding guides: never invent APIs; verify each component page / source signature during its slice | | `TextStyle` ladder is a deliberate holdout from rem-based text | Documented exception; sizes are absolute points and don't claim to zoom. Revisit only if the kit's text scale is adopted later | | native-theme-gpui version coupling | Pin `gpui-kit = "=0.7.1"`-era set (0.7.1, gpui-pre =0.3.8) matching the connector's verified table; bump all together |