From 6f35c86afc24d383000ea93b91c6d56da5a1eddd Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Wed, 21 Oct 2026 08:33:34 +0700 Subject: [PATCH 01/14] add plan --- docs/native-theme-migration.md | 294 +++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 docs/native-theme-migration.md diff --git a/docs/native-theme-migration.md b/docs/native-theme-migration.md new file mode 100644 index 00000000..336a0042 --- /dev/null +++ b/docs/native-theme-migration.md @@ -0,0 +1,294 @@ +# Migrate the UI to GPUI-Kit, then native-theme + +Plan to move coop's custom UI elements onto [GPUI-Kit](https://gpui-kit.com) +(`gpui-kit` 0.7.1), delete the custom theme implementation (`crates/theme`), +and then integrate [`native-theme`](https://github.com/tiborgats/native-theme) +through its official `native-theme-gpui` connector. + +> **Revision.** This supersedes the earlier approach in this file (depend on +> the `native-theme` core crate and hand-write a `ResolvedTheme` → coop token +> 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. + +## 1. What the git history shows + +- `516eb0e` — *move gpui-components to ui crate*: the UI crate **was** the + upstream component library, vendored into `crates/ui`. Call sites used + library components and its theme vocabulary + (`cx.theme().elevated_surface_background`, `surface_background`, + `border_variant`, …). +- `0c1c23f` — *migrate to gpui-base (#51)*: the vendored library was deleted + and replaced by the `gpui-base` crate plus hand-written wrappers (Button, + Switch, Dialog, Input, Dock, Menu, Notification, …) and the custom + `crates/theme`. + +"Migrate like before" therefore means: go back to the upstream component +library — this time as a dependency (`gpui-kit`), not vendored — and let it +own the theme. + +## 2. Strategy + +```mermaid +graph LR + A[Phase A: GPUI-Kit] --> B[Phase B: native-theme-gpui] + A1[A1 Bootstrap
init + open_window + Root] --> A2[A2 Component swap
~20 custom modules] + A2 --> A3[A3 Theme removal
cx.theme = gpui-component] + A3 --> B1[B1 Connector
apply_system_theme] + B1 --> B2[B2 Appearance + presets
settings wiring] +``` + +**Phase A** replaces the custom elements with `gpui_kit::component` +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. + +**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 +connector is verified against exactly the versions Phase A pins. + +### Target dependency set (exact versions) + +| Crate | Version | Note | +|---|---|---| +| `gpui-kit` | 0.7.1 | facade: `gpui_kit::*` = GPUI (`gpui-pre`), `gpui_kit::component` (gpui-component 0.7.1), `gpui_kit::base` (gpui-base 0.7.1), `gpui_kit::assets` | +| `gpui-pre` | =0.3.8 | already pinned; gpui-kit 0.7.1 requires it | +| `native-theme-gpui` | 0.6 | Phase B; verified against gpui-kit 0.7.1 / gpui-pre 0.3.8; needs Rust ≥ 1.95 (toolchain is 1.98) | + +Do not jump to `gpui-kit` 0.8: `native-theme-gpui` 0.6.1 pins 0.7.1, and a +gpui-component patch release has broken the connector before. + +## 3. Current custom UI inventory + +`crates/ui` is ~8,700 lines across 34 files. Consumers import it at ~138 +sites (`workspace` 14 files, `chat_ui`, `nip29_ui`, `nip4e`, `settings`, +`desktop`, `web`); theme tokens are read at ~340 sites across the same +crates. + +| Custom module | Lines | GPUI-Kit replacement | +|---|---|---| +| `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 | +| `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 | +| `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) | +| `kbd.rs` | 270 | `Kbd` | +| `tab/` | 385 | `tab::{Tab, TabBar, Tabs}` | +| `title_bar.rs` | 218 | `TitleBar` (keep the client-side-decoration constants) | +| `popover.rs` | 170 | `popover::Popover` | +| `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 | +| `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 | +| `window_ext.rs` | 90 | `gpui_kit::component::WindowExt` | + +## 4. Phase A — GPUI-Kit migration + +### 4.1 Bootstrap + +- `desktop/main.rs` and `web/lib.rs`: `gpui_kit::init(cx)` first, then open + the window with `gpui_kit::open_window(opts, cx, |window, cx| …)`, which + 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. +- `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. + +### 4.2 Component slices (each PR compiles) + +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). +3. **Small controls** — Button, Switch, Kbd, GroupBox, Avatar, Tooltip, + Indicator, Popover. +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 + fields, relay editor; drop the clear-button and input-styling modules. +6. **Dialog + Notification** — `open_dialog`/`AlertDialog`/ + `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). +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). + +### 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`. + +### 4.4 Theme removal + +- **Call sites**: `theme::ActiveTheme` → `gpui_kit::component`'s `ActiveTheme` + (`cx.theme() -> &Theme`). Rewrite the ~340 token reads with the mapping + below (done per consumer crate, one slice each: `ui`, `workspace`, + `chat_ui`, `nip29_ui`, `settings` + `nip4e`). + +| coop token (`cx.theme().x`) | gpui-component `ThemeColor` | +|---|---| +| `bg` | `background` | +| `surface` | `sidebar` | +| `surface_raised` | `secondary` | +| `surface_card` | `muted` | +| `surface_dialog`, `surface_overlay` | `popover` | +| `element_hover` | `accent` (gpui-component's accent **is** the item highlight) | +| `element_active` | `list_active` | +| `border` | `border` | +| `border_faint`, `border_strong` | `border` (one border token; faintness loss accepted) | +| `ring` | `ring` | +| `text` | `foreground` | +| `text_muted`, `text_faint`, `text_dim` | `muted_foreground` | +| `cursor`, `caret` | `caret` | +| `selection` | `selection` | +| `solid` / `on_solid` | `primary` / `primary_foreground` | +| `accent`, `accent_strong` | `primary` (emphasis ink) | +| `danger` / `danger_muted` | `danger` | +| `danger_strong` | `button_danger` | +| `warning` / `warning_muted` | `warning` / `warning.alpha(≈0.15)` at the call site | +| `success` | `success` | +| `input_bg`, `selection`, `caret` inside the custom input | die with the custom Input (§3) | +| `code_text`, `code_wash` | die with `ui::markdown` → `TextView` | +| `drop_line` / `drop_target` | `drag_border` / `drop_target` | + +- **Non-color fields** move to `Theme` directly: `font_sans`/`font_mono` → + `theme.font_family`/`mono_font_family`; `shadow` → `theme.shadow`; + `scrollbar_mode` → `theme.scrollbar_mode`; notification placement → + `theme.notification` (gpui-component ships the same `NotificationSettings` + coop's was modeled on); `is_dark()` → `theme.mode`. +- **`crates/theme` disposition**: + - delete: `lib.rs` (Theme global, `AppExt`, `sync_base`, `PaletteBuilder`), + `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`, + ~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; + - `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 + (`Theme::update` / `Theme::change` keep colors, tokens and the base layer + in step per the coding guides). + +### 4.5 Appearance handling (end of Phase A) + +- `settings::apply_theme` → `Theme::change(mode, None, cx)` for a pinned + Light/Dark, `Theme::sync_system_appearance(None, cx)` for System. +- `workspace`'s `Theme::sync_system_appearance(Some(window), cx)` call stays, + now against the kit's `Theme`; window-appearance observation comes with the + framework Root/theme (verify; if not, keep the existing + `observe_window_appearance` wiring). + +## 5. Phase B — native-theme-gpui + +1. Add `native-theme-gpui = "0.6"` (pulls `native-theme`; keep default + features or trim icon features coop doesn't use). +2. Boot, after `gpui_kit::init` and before opening the window: + + ```rust,ignore + match native_theme::SystemTheme::from_system() { + Ok(sys) => native_theme_gpui::apply_system_theme(&sys, cx), + Err(error) => { + log::warn!("system theme unavailable: {error}"); + // per-platform fallback preset: macos-sonoma / kde-breeze / windows-11 + let prefs = native_theme_gpui::AccessibilityPreferences::default(); + if let Ok((theme, resolved)) = native_theme_gpui::from_preset("macos-sonoma", true, &prefs) { + native_theme_gpui::apply(theme, &resolved, &prefs, cx); + } + } + } + ``` + + `apply_system_theme` stores **both** variants and a `ThemeConfig` per + mode; afterwards the Phase A controls keep working unchanged: + `Theme::sync_system_appearance` / `Theme::change` switch between the + native palettes. +3. Optional preset picker (the same one proposed in the superseded plan): + `settings.theme_preset: Option`, a dropdown in Preferences → + Appearance; selecting one calls `from_preset(name, is_dark, &prefs)` + + `apply` for both variants. +4. Fonts, radius, scrollbar geometry and the base layer are written by the + connector (it also re-asserts them after each upstream rebuild via its + observer). Coop writes nothing theme-related itself. + +wasm: `SystemTheme::from_system` is unavailable on wasm32 — cfg-gate step 2 +to native targets; on web, apply the fallback preset (or extend +`native-theme` with a web story later; presets need no OS reader). + +## 6. Testing and QA + +- Per slice: `cargo check` workspace, `cargo clippy`, `cargo fmt --check`, + plus the affected crate's tests; UI behavior verified by hand in the + desktop app. +- **Early spike (before slice 2)**: `cargo check -p web --target + wasm32-unknown-unknown` with the `gpui-kit` dependency added. The vendored + library was wasm-capable; confirm the published crates are too before + committing to the migration. If they are not, stop and reassess — the web + target is a launch blocker. +- End of Phase A: + - all dialogs open/close, focus trap and restore works; nested + popover-in-dialog works (framework Root owns the layers); + - notifications stack and auto-hide; settings' appearance dropdown switches + Light/Dark/System with a full repaint; + - 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`. +- End of Phase B: + - macOS: OS light/dark follows; pinned modes; accent changes picked up at + next launch (documented limitation — no `watch` feature); + - Linux (KDE + GNOME/portal) and Windows: theme reads work or fall back to + the platform preset with a logged warning; + - preset switch renders in both modes; inputs/menus/dialogs/tooltips/ + scrollbars all paint from the native palette; + - startup shows no wrong-palette flash (apply before the first window). + +## 7. Risks + +| 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 | +| 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 | +| 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 | + +## 8. Out of scope + +- Migrating `motion.rs` or the panel-background image feature. +- Accessibility text scaling / reduce-motion plumbing (connector supports + it; adopt after Phase B if wanted). +- Replacing coop's composited panels (chat, screening, backup…) with kit + `Sheet`/`Settings` — revisit after the migration settles. -- 2.54.0 From 0d4acf5b659f3cc8fc579d75e4e3bf9dfebd96d7 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Wed, 21 Oct 2026 08:49:01 +0700 Subject: [PATCH 02/14] 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 | -- 2.54.0 From d0ad1a6f573a9ba324a0283652ea4d16df3119f5 Mon Sep 17 00:00:00 2001 From: Ren Amamiya Date: Wed, 21 Oct 2026 13:40:29 +0700 Subject: [PATCH 03/14] migrate ui components to gpui-kit --- Cargo.lock | 571 ++++++++++++- Cargo.toml | 141 ++-- assets/icons/arrow-down-circle.svg | 3 - assets/icons/arrow-left.svg | 16 - assets/icons/arrow-right.svg | 3 - assets/icons/ban.svg | 1 - assets/icons/book.svg | 1 - assets/icons/boom.svg | 3 - assets/icons/caret-down.svg | 3 - assets/icons/caret-right.svg | 3 - assets/icons/caret-up.svg | 3 - assets/icons/check-circle.svg | 3 - assets/icons/check.svg | 3 - assets/icons/chevron-down.svg | 3 - assets/icons/close-circle-fill.svg | 3 - assets/icons/close-circle.svg | 3 - assets/icons/close.svg | 3 - assets/icons/compass.svg | 3 - assets/icons/copy.svg | 3 - assets/icons/device.svg | 3 - assets/icons/door.svg | 3 - assets/icons/edit.svg | 1 - assets/icons/ellipsis.svg | 3 - assets/icons/emoji.svg | 3 - assets/icons/eye.svg | 3 - assets/icons/fistbump-fill.svg | 3 - assets/icons/fistbump.svg | 3 - assets/icons/folder.svg | 3 - assets/icons/group.svg | 3 - assets/icons/hashtag.svg | 3 - assets/icons/history.svg | 3 - assets/icons/inbox-fill.svg | 3 - assets/icons/inbox.svg | 3 - assets/icons/info.svg | 3 - assets/icons/input.svg | 3 - assets/icons/invite.svg | 3 - assets/icons/link.svg | 3 - assets/icons/loader.svg | 10 - assets/icons/lock.svg | 3 - assets/icons/login.svg | 1 - assets/icons/logout.svg | 1 - assets/icons/message.svg | 3 - assets/icons/moon.svg | 3 - assets/icons/panel-left-open.svg | 3 - assets/icons/panel-left.svg | 3 - assets/icons/panel-right-open.svg | 3 - assets/icons/panel-right.svg | 3 - assets/icons/paper-plane-fill.svg | 3 - assets/icons/plus-chat.svg | 1 - assets/icons/plus-circle.svg | 3 - assets/icons/plus.svg | 3 - assets/icons/profile.svg | 3 - assets/icons/refresh.svg | 3 - assets/icons/relay.svg | 3 - assets/icons/reply.svg | 3 - assets/icons/reset.svg | 3 - assets/icons/scan.svg | 3 - assets/icons/search.svg | 3 - assets/icons/settings.svg | 3 - assets/icons/settings2.svg | 3 - assets/icons/shield.svg | 3 - assets/icons/ship.svg | 3 - assets/icons/sun.svg | 3 - assets/icons/upload.svg | 1 - assets/icons/usb.svg | 3 - assets/icons/user-key.svg | 3 - assets/icons/warning.svg | 3 - assets/icons/window-close.svg | 9 - assets/icons/window-maximize.svg | 9 - assets/icons/window-minimize.svg | 9 - assets/icons/window-restore.svg | 10 - assets/icons/zoom.svg | 58 -- crates/assets/Cargo.toml | 1 + crates/assets/src/lib.rs | 19 + crates/chat_ui/Cargo.toml | 2 +- crates/chat_ui/src/lib.rs | 204 ++--- crates/{ui => chat_ui}/src/message.rs | 45 +- crates/chat_ui/src/text.rs | 122 ++- crates/nip29_ui/Cargo.toml | 2 +- crates/nip29_ui/src/details.rs | 40 +- crates/nip29_ui/src/invite.rs | 18 +- crates/nip29_ui/src/lib.rs | 180 ++--- crates/nip29_ui/src/message.rs | 71 +- crates/nip29_ui/src/metadata.rs | 101 ++- crates/nip29_ui/src/util.rs | 12 +- crates/nip4e/Cargo.toml | 2 +- crates/nip4e/src/lib.rs | 50 +- crates/settings/Cargo.toml | 44 +- crates/settings/src/lib.rs | 34 +- crates/theme/Cargo.toml | 17 - crates/theme/src/appearance.rs | 151 ---- crates/theme/src/brand.rs | 298 ------- crates/theme/src/color.rs | 215 ----- crates/theme/src/colors.rs | 118 --- crates/theme/src/geometry.rs | 63 -- crates/theme/src/lib.rs | 312 -------- crates/theme/src/notification.rs | 30 - crates/theme/src/paint.rs | 191 ----- crates/theme/src/palette.rs | 137 ---- crates/theme/src/platform_kind.rs | 21 - crates/theme/src/scrollbar_mode.rs | 16 - crates/theme/src/syntax.rs | 176 ---- crates/theme/src/theme.rs | 42 - crates/ui/Cargo.toml | 32 +- crates/ui/src/avatar.rs | 445 ----------- crates/ui/src/button.rs | 581 -------------- crates/ui/src/dialog.rs | 394 --------- crates/ui/src/dock/mod.rs | 805 ------------------- crates/ui/src/dock/panel.rs | 205 ----- crates/ui/src/group_box.rs | 126 --- crates/ui/src/icon.rs | 274 ------- crates/ui/src/indicator.rs | 59 -- crates/ui/src/input/clear_button.rs | 15 - crates/ui/src/input/input.rs | 277 ------- crates/ui/src/input/mod.rs | 7 - crates/ui/src/kbd.rs | 270 ------- crates/{theme => ui}/src/layout.rs | 34 +- crates/ui/src/lib.rs | 61 +- crates/ui/src/markdown.rs | 146 ---- crates/ui/src/menu/dropdown_menu.rs | 178 ----- crates/ui/src/menu/menu_item.rs | 121 --- crates/ui/src/menu/mod.rs | 12 - crates/ui/src/menu/popup_menu.rs | 839 -------------------- crates/ui/src/motion.rs | 2 +- crates/ui/src/nav_item.rs | 125 --- crates/ui/src/notification.rs | 594 -------------- crates/ui/src/popover.rs | 170 ---- crates/ui/src/resizable/mod.rs | 31 - crates/ui/src/root.rs | 430 ---------- crates/ui/src/scroll/mod.rs | 4 - crates/ui/src/scroll/scrollable.rs | 93 --- crates/ui/src/styled.rs | 154 +--- crates/ui/src/switch.rs | 157 ---- crates/ui/src/tab/mod.rs | 224 ------ crates/ui/src/tab/tab_bar.rs | 161 ---- crates/ui/src/title_bar.rs | 218 ----- crates/ui/src/tooltip.rs | 34 - crates/{theme => ui}/src/typography.rs | 0 crates/ui/src/window_drag.rs | 55 ++ crates/ui/src/window_ext.rs | 90 --- crates/workspace/Cargo.toml | 2 +- crates/workspace/src/dialogs/import.rs | 26 +- crates/workspace/src/dialogs/join_group.rs | 12 +- crates/workspace/src/dialogs/new_chat.rs | 46 +- crates/workspace/src/dialogs/new_group.rs | 153 ++-- crates/workspace/src/dialogs/profile.rs | 51 +- crates/workspace/src/dialogs/relays.rs | 33 +- crates/workspace/src/dialogs/restore.rs | 14 +- crates/workspace/src/dialogs/screening.rs | 61 +- crates/workspace/src/dialogs/settings.rs | 103 +-- crates/workspace/src/dock.rs | 78 ++ crates/workspace/src/lib.rs | 66 +- crates/{ui => workspace}/src/nav.rs | 15 +- crates/workspace/src/panels/backup.rs | 42 +- crates/workspace/src/panels/browse.rs | 76 +- crates/workspace/src/panels/contact_list.rs | 60 +- crates/workspace/src/panels/greeter.rs | 77 +- crates/workspace/src/panels/inbox.rs | 25 +- crates/workspace/src/sidebar/group_view.rs | 75 +- crates/workspace/src/sidebar/header.rs | 50 +- crates/workspace/src/sidebar/inbox.rs | 18 +- crates/workspace/src/sidebar/list.rs | 40 +- crates/workspace/src/sidebar/mod.rs | 753 +++++++++--------- crates/workspace/src/sidebar/onboarding.rs | 10 +- crates/workspace/src/sidebar/rows.rs | 161 ++-- crates/workspace/src/sidebar/search.rs | 13 +- desktop/Cargo.toml | 102 +-- desktop/src/main.rs | 20 +- docs/native-theme-migration.md | 12 + script/migration/fixup.py | 98 +++ script/migration/sweep.py | 189 +++++ web/Cargo.toml | 2 +- web/src/lib.rs | 12 +- 173 files changed, 2775 insertions(+), 10885 deletions(-) delete mode 100644 assets/icons/arrow-down-circle.svg delete mode 100644 assets/icons/arrow-left.svg delete mode 100644 assets/icons/arrow-right.svg delete mode 100644 assets/icons/ban.svg delete mode 100644 assets/icons/book.svg delete mode 100644 assets/icons/boom.svg delete mode 100644 assets/icons/caret-down.svg delete mode 100644 assets/icons/caret-right.svg delete mode 100644 assets/icons/caret-up.svg delete mode 100644 assets/icons/check-circle.svg delete mode 100644 assets/icons/check.svg delete mode 100644 assets/icons/chevron-down.svg delete mode 100644 assets/icons/close-circle-fill.svg delete mode 100644 assets/icons/close-circle.svg delete mode 100644 assets/icons/close.svg delete mode 100644 assets/icons/compass.svg delete mode 100644 assets/icons/copy.svg delete mode 100644 assets/icons/device.svg delete mode 100644 assets/icons/door.svg delete mode 100644 assets/icons/edit.svg delete mode 100644 assets/icons/ellipsis.svg delete mode 100644 assets/icons/emoji.svg delete mode 100644 assets/icons/eye.svg delete mode 100644 assets/icons/fistbump-fill.svg delete mode 100644 assets/icons/fistbump.svg delete mode 100644 assets/icons/folder.svg delete mode 100644 assets/icons/group.svg delete mode 100644 assets/icons/hashtag.svg delete mode 100644 assets/icons/history.svg delete mode 100644 assets/icons/inbox-fill.svg delete mode 100644 assets/icons/inbox.svg delete mode 100644 assets/icons/info.svg delete mode 100644 assets/icons/input.svg delete mode 100644 assets/icons/invite.svg delete mode 100644 assets/icons/link.svg delete mode 100644 assets/icons/loader.svg delete mode 100644 assets/icons/lock.svg delete mode 100644 assets/icons/login.svg delete mode 100644 assets/icons/logout.svg delete mode 100644 assets/icons/message.svg delete mode 100644 assets/icons/moon.svg delete mode 100644 assets/icons/panel-left-open.svg delete mode 100644 assets/icons/panel-left.svg delete mode 100644 assets/icons/panel-right-open.svg delete mode 100644 assets/icons/panel-right.svg delete mode 100644 assets/icons/paper-plane-fill.svg delete mode 100644 assets/icons/plus-chat.svg delete mode 100644 assets/icons/plus-circle.svg delete mode 100644 assets/icons/plus.svg delete mode 100644 assets/icons/profile.svg delete mode 100644 assets/icons/refresh.svg delete mode 100644 assets/icons/relay.svg delete mode 100644 assets/icons/reply.svg delete mode 100644 assets/icons/reset.svg delete mode 100644 assets/icons/scan.svg delete mode 100644 assets/icons/search.svg delete mode 100644 assets/icons/settings.svg delete mode 100644 assets/icons/settings2.svg delete mode 100644 assets/icons/shield.svg delete mode 100644 assets/icons/ship.svg delete mode 100644 assets/icons/sun.svg delete mode 100644 assets/icons/upload.svg delete mode 100644 assets/icons/usb.svg delete mode 100644 assets/icons/user-key.svg delete mode 100644 assets/icons/warning.svg delete mode 100644 assets/icons/window-close.svg delete mode 100644 assets/icons/window-maximize.svg delete mode 100644 assets/icons/window-minimize.svg delete mode 100644 assets/icons/window-restore.svg delete mode 100644 assets/icons/zoom.svg rename crates/{ui => chat_ui}/src/message.rs (91%) delete mode 100644 crates/theme/Cargo.toml delete mode 100644 crates/theme/src/appearance.rs delete mode 100644 crates/theme/src/brand.rs delete mode 100644 crates/theme/src/color.rs delete mode 100644 crates/theme/src/colors.rs delete mode 100644 crates/theme/src/geometry.rs delete mode 100644 crates/theme/src/lib.rs delete mode 100644 crates/theme/src/notification.rs delete mode 100644 crates/theme/src/paint.rs delete mode 100644 crates/theme/src/palette.rs delete mode 100644 crates/theme/src/platform_kind.rs delete mode 100644 crates/theme/src/scrollbar_mode.rs delete mode 100644 crates/theme/src/syntax.rs delete mode 100644 crates/theme/src/theme.rs delete mode 100644 crates/ui/src/avatar.rs delete mode 100644 crates/ui/src/button.rs delete mode 100644 crates/ui/src/dialog.rs delete mode 100644 crates/ui/src/dock/mod.rs delete mode 100644 crates/ui/src/dock/panel.rs delete mode 100644 crates/ui/src/group_box.rs delete mode 100644 crates/ui/src/icon.rs delete mode 100644 crates/ui/src/indicator.rs delete mode 100644 crates/ui/src/input/clear_button.rs delete mode 100644 crates/ui/src/input/input.rs delete mode 100644 crates/ui/src/input/mod.rs delete mode 100644 crates/ui/src/kbd.rs rename crates/{theme => ui}/src/layout.rs (74%) delete mode 100644 crates/ui/src/markdown.rs delete mode 100644 crates/ui/src/menu/dropdown_menu.rs delete mode 100644 crates/ui/src/menu/menu_item.rs delete mode 100644 crates/ui/src/menu/mod.rs delete mode 100644 crates/ui/src/menu/popup_menu.rs delete mode 100644 crates/ui/src/nav_item.rs delete mode 100644 crates/ui/src/notification.rs delete mode 100644 crates/ui/src/popover.rs delete mode 100644 crates/ui/src/resizable/mod.rs delete mode 100644 crates/ui/src/root.rs delete mode 100644 crates/ui/src/scroll/mod.rs delete mode 100644 crates/ui/src/scroll/scrollable.rs delete mode 100644 crates/ui/src/switch.rs delete mode 100644 crates/ui/src/tab/mod.rs delete mode 100644 crates/ui/src/tab/tab_bar.rs delete mode 100644 crates/ui/src/title_bar.rs delete mode 100644 crates/ui/src/tooltip.rs rename crates/{theme => ui}/src/typography.rs (100%) create mode 100644 crates/ui/src/window_drag.rs delete mode 100644 crates/ui/src/window_ext.rs create mode 100644 crates/workspace/src/dock.rs rename crates/{ui => workspace}/src/nav.rs (92%) create mode 100644 script/migration/fixup.py create mode 100644 script/migration/sweep.py diff --git a/Cargo.lock b/Cargo.lock index a1fcf204..ad6b1e68 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,23 @@ dependencies = [ "libc", ] +[[package]] +name = "annotate-snippets" +version = "0.12.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a5b0a5ab8561c603f16c3b78c24a386b1f70e7fc1b1b1e8c15738828e4a176" +dependencies = [ + "anstyle", + "memchr", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + [[package]] name = "anyhow" version = "1.0.104" @@ -200,6 +217,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bc62ac97cc33321f50863d514c3bc38a453947a8f9e781137e47c7401020aed" +[[package]] +name = "arc-swap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e" +dependencies = [ + "rustversion", +] + [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -211,6 +237,12 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "arrayref" version = "0.3.9" @@ -270,6 +302,7 @@ name = "assets" version = "2.0.0-alpha" dependencies = [ "anyhow", + "gpui-kit", "gpui-pre", "rust-embed", ] @@ -607,6 +640,12 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "base62" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a647dce9631f152a99ec16cfc09267ae55fe280d5dc288964027d458076323" + [[package]] name = "base64" version = "0.22.1" @@ -821,6 +860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", + "regex-automata", "serde_core", ] @@ -1020,6 +1060,7 @@ dependencies = [ "anyhow", "chat", "common", + "gpui-kit", "gpui-pre", "itertools 0.13.0", "nostr-sdk", @@ -1029,7 +1070,6 @@ dependencies = [ "settings", "smallvec", "state", - "theme", "ui", ] @@ -1285,6 +1325,7 @@ dependencies = [ "assets", "auto_update", "chat", + "gpui-kit", "gpui-pre", "gpui-pre-linux", "gpui-pre-macos", @@ -1298,7 +1339,6 @@ dependencies = [ "person", "settings", "state", - "theme", "tracing-subscriber", "ui", "workspace", @@ -1314,6 +1354,7 @@ dependencies = [ "getrandom 0.2.17", "getrandom 0.3.4", "getrandom 0.4.3", + "gpui-kit", "gpui-pre", "gpui-pre-platform", "gpui-pre-web", @@ -1324,7 +1365,6 @@ dependencies = [ "person", "settings", "state", - "theme", "tracing-wasm", "ui", "universal-time 0.3.1 (git+https://github.com/shadowylab/universal-time)", @@ -1834,12 +1874,41 @@ dependencies = [ "simdutf8", ] +[[package]] +name = "encoding_rs_io" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba3fe847045ecff794b9c138293a80db914678c453ad63fbf0c6a9eb6e00b22" +dependencies = [ + "encoding_rs", +] + [[package]] name = "endi" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enum-iterator" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4549325971814bda7a44061bf3fe7e487d447cba01e4220a4b454d630d7a016" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -2200,6 +2269,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futf" version = "0.1.5" @@ -2391,6 +2469,16 @@ dependencies = [ "polyval", ] +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gif" version = "0.14.2" @@ -2418,6 +2506,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + [[package]] name = "globset" version = "0.4.20" @@ -2431,6 +2525,17 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", +] + [[package]] name = "gloo-timers" version = "0.3.0" @@ -2539,6 +2644,95 @@ dependencies = [ "windows 0.58.0", ] +[[package]] +name = "gpui-component" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a363235cd4062ef5f0953fc096fc764ed6f53dfbd9f409f669f93ea3b1ff9" +dependencies = [ + "anyhow", + "chrono", + "core-text", + "enum-iterator", + "gpui-base", + "gpui-component-macros", + "gpui-kit-assets", + "gpui-pre", + "gpui-pre-macros", + "gpui-pre-sum-tree", + "instant", + "itertools 0.13.0", + "log", + "lsp-types", + "markdown", + "notify", + "num-traits", + "objc2 0.6.5", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", + "once_cell", + "paste", + "raw-window-handle", + "regex", + "resvg 0.45.1", + "ropey", + "rust-i18n", + "schemars", + "serde", + "serde_json", + "serde_repr", + "similar", + "smallvec", + "smol", + "tracing", + "unicode-segmentation", + "unicode-width", + "uuid", + "windows 0.58.0", +] + +[[package]] +name = "gpui-component-macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0c9398aaf99e595d450e38eb44ff0c500df8c332b4783d5b72022e69525359" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "gpui-kit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac401f6b149bf33794da0942510240afd29b7b18556574e4cf17196b647066" +dependencies = [ + "gpui-base", + "gpui-component", + "gpui-kit-assets", + "gpui-pre", + "gpui-pre-macros", + "gpui-pre-platform", + "gpui-pre-web", +] + +[[package]] +name = "gpui-kit-assets" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7abc281dcbfe51c16a7e73189521a3a771168c31e3ab0db99e21e481d1b1b9a" +dependencies = [ + "anyhow", + "gpui-pre", + "gpui-pre-reqwest", + "log", + "rust-embed", + "wasm-bindgen", + "wasm-bindgen-futures", +] + [[package]] name = "gpui-pre" version = "0.3.9" @@ -2584,7 +2778,7 @@ dependencies = [ "rand 0.9.5", "raw-window-handle", "regex", - "resvg", + "resvg 0.46.0", "schemars", "seahash", "serde", @@ -2597,7 +2791,7 @@ dependencies = [ "thiserror 2.0.21", "ttf-parser", "url", - "usvg", + "usvg 0.46.0", "uuid", "waker-fn", "windows 0.62.2", @@ -2829,6 +3023,7 @@ checksum = "05be23908e707966824f8c51a609904b7f30739e8d915e120a53c1b995ba964c" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-core", "futures-util", "h2", @@ -2842,6 +3037,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "once_cell", "percent-encoding", "pin-project-lite", @@ -3081,6 +3277,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "granit-parser" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e20f99e46474f56bd905c56e817ebddcf377a611f94c53ac4649e4d3fa3c0cd0" +dependencies = [ + "arraydeque", + "smallvec", +] + [[package]] name = "h2" version = "0.4.20" @@ -3547,6 +3753,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "image" version = "0.25.10" @@ -3557,7 +3779,7 @@ dependencies = [ "byteorder-lite", "color_quant", "exr", - "gif", + "gif 0.14.2", "image-webp", "moxcms", "num-traits", @@ -3566,8 +3788,8 @@ dependencies = [ "ravif", "rayon", "tiff", - "zune-core", - "zune-jpeg", + "zune-core 0.5.3", + "zune-jpeg 0.5.15", ] [[package]] @@ -3580,6 +3802,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + [[package]] name = "imagesize" version = "0.14.0" @@ -3604,6 +3832,26 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inotify" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cc00ea907cab49550b7da656f80ebb97be1b997d931fbcd28d39734e17ce592" +dependencies = [ + "bitflags 2.13.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" +dependencies = [ + "libc", +] + [[package]] name = "inout" version = "0.1.4" @@ -3680,6 +3928,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -3800,6 +4057,37 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kqueue" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d763e5b24120b4ddf50de6c92308156765aabfbbccebf401da7cff2d70a41ea" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" +dependencies = [ + "bitflags 2.13.2", + "libc", +] + +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + [[package]] name = "kurbo" version = "0.13.1" @@ -4195,6 +4483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] @@ -4294,6 +4583,7 @@ dependencies = [ "chat", "chat_ui", "common", + "gpui-kit", "gpui-pre", "log", "nip29", @@ -4303,7 +4593,6 @@ dependencies = [ "settings", "smallvec", "state", - "theme", "ui", ] @@ -4313,6 +4602,7 @@ version = "2.0.0-alpha" dependencies = [ "anyhow", "flume 0.11.1", + "gpui-kit", "gpui-pre", "log", "nostr-sdk", @@ -4321,7 +4611,6 @@ dependencies = [ "smallvec", "smol", "state", - "theme", "ui", ] @@ -4334,6 +4623,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "8.0.0" @@ -4349,6 +4644,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "normpath" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11ce00d2594068e8a27c9146fdc5cf9f3ac38eb42c7cd34d05fea618873ac9f" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "nostr" version = "0.45.5" @@ -4479,6 +4783,24 @@ dependencies = [ "universal-time 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.13.2", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + [[package]] name = "notify-rust" version = "4.18.2" @@ -4493,6 +4815,15 @@ dependencies = [ "zbus", ] +[[package]] +name = "notify-types" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" +dependencies = [ + "bitflags 2.13.2", +] + [[package]] name = "ntapi" version = "0.4.3" @@ -5947,19 +6278,36 @@ dependencies = [ [[package]] name = "resvg" -version = "0.46.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" dependencies = [ - "gif", + "gif 0.13.3", "image-webp", "log", "pico-args", "rgb", - "svgtypes", + "svgtypes 0.15.3", "tiny-skia", - "usvg", - "zune-jpeg", + "usvg 0.45.1", + "zune-jpeg 0.4.21", +] + +[[package]] +name = "resvg" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b563218631706d614e23059436526d005b50ab5f2d506b55a17eb65c5eb83419" +dependencies = [ + "gif 0.14.2", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes 0.16.1", + "tiny-skia", + "usvg 0.46.0", + "zune-jpeg 0.5.15", ] [[package]] @@ -6030,6 +6378,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", + "shellexpand", "syn 2.0.119", "walkdir", ] @@ -6045,6 +6394,54 @@ dependencies = [ "walkdir", ] +[[package]] +name = "rust-i18n" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8502e73e6461c738659455c5d2cf95bc132760a6f203c717e66ee72b78906d7f" +dependencies = [ + "globwalk", + "regex", + "rust-i18n-macro", + "rust-i18n-support", + "smallvec", +] + +[[package]] +name = "rust-i18n-macro" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54a28dd9d46108d56b9f0c85b220908844233e561c03087890325104f90f2c6" +dependencies = [ + "glob", + "proc-macro2", + "quote", + "rust-i18n-support", + "serde", + "serde_json", + "syn 2.0.119", +] + +[[package]] +name = "rust-i18n-support" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e806c2a9701cbd6043992cd935c30cb1fdb128fa15e7b506f3b46f97045737c0" +dependencies = [ + "arc-swap", + "base62", + "foldhash 0.2.0", + "globwalk", + "itertools 0.11.0", + "normpath", + "serde", + "serde-saphyr", + "serde_json", + "siphasher", + "toml 0.8.23", + "triomphe", +] + [[package]] name = "rustc-demangle" version = "0.1.28" @@ -6377,6 +6774,23 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-saphyr" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8050abb251097357e24aff63ba2c52a6309ecb7d23a5474023df960a02694d8" +dependencies = [ + "annotate-snippets", + "base64 0.23.1", + "encoding_rs_io", + "granit-parser", + "nohash-hasher", + "num-traits", + "serde_core", + "smallvec", + "zmij", +] + [[package]] name = "serde_bytes" version = "0.11.19" @@ -6480,6 +6894,7 @@ version = "2.0.0-alpha" dependencies = [ "anyhow", "common", + "gpui-kit", "gpui-pre", "log", "nostr-sdk", @@ -6488,7 +6903,6 @@ dependencies = [ "serde_json", "smallvec", "smol", - "theme", ] [[package]] @@ -6539,6 +6953,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" +dependencies = [ + "dirs", +] + [[package]] name = "shlex" version = "2.0.1" @@ -6586,6 +7009,16 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +dependencies = [ + "bstr", + "unicode-segmentation", +] + [[package]] name = "simplecss" version = "0.2.2" @@ -6819,13 +7252,23 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo 0.11.3", + "siphasher", +] + [[package]] name = "svgtypes" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "695b5790b3131dafa99b3bbfd25a216edb3d216dad9ca208d4657bfb8f2abc3d" dependencies = [ - "kurbo", + "kurbo 0.13.1", "siphasher", ] @@ -7003,20 +7446,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "theme" -version = "2.0.0-alpha" -dependencies = [ - "anyhow", - "gpui-base", - "gpui-pre", - "log", - "schemars", - "serde", - "serde_json", - "tempfile", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -7077,7 +7506,7 @@ dependencies = [ "half", "quick-error", "weezl", - "zune-jpeg", + "zune-jpeg 0.5.15", ] [[package]] @@ -7466,6 +7895,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "triomphe" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d15f4f5898bcfc5d8a8e45ed30b2f108d710c1a2e5489dbf80e078f707181bc" +dependencies = [ + "arc-swap", + "serde", + "stable_deref_trait", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -7522,12 +7962,12 @@ name = "ui" version = "2.0.0-alpha" dependencies = [ "gpui-base", + "gpui-kit", "gpui-pre", - "instant", "log", "smallvec", - "theme", "uuid", + "web-time", ] [[package]] @@ -7683,6 +8123,33 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize 0.13.0", + "kurbo 0.11.3", + "log", + "pico-args", + "roxmltree 0.20.0", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes 0.15.3", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "usvg" version = "0.46.0" @@ -7693,8 +8160,8 @@ dependencies = [ "data-url", "flate2", "fontdb", - "imagesize", - "kurbo", + "imagesize 0.14.0", + "kurbo 0.13.1", "log", "pico-args", "roxmltree 0.21.1", @@ -7702,7 +8169,7 @@ dependencies = [ "simplecss", "siphasher", "strict-num", - "svgtypes", + "svgtypes 0.16.1", "tiny-skia-path", "unicode-bidi", "unicode-script", @@ -8651,6 +9118,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -8926,6 +9402,7 @@ dependencies = [ "chat_ui", "common", "gpui-base", + "gpui-kit", "gpui-pre", "instant", "log", @@ -8939,7 +9416,6 @@ dependencies = [ "settings", "smallvec", "state", - "theme", "ui", ] @@ -9381,6 +9857,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-core" version = "0.5.3" @@ -9396,13 +9878,22 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core 0.4.12", +] + [[package]] name = "zune-jpeg" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ - "zune-core", + "zune-core 0.5.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 06c6ca2a..368fbcbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,70 +1,71 @@ -[workspace] -resolver = "2" -members = ["crates/*", "desktop", "web"] -default-members = ["desktop"] - -[workspace.package] -version = "2.0.0-alpha" -edition = "2024" -publish = false - -[workspace.dependencies] -gpui = { package = "gpui-pre", version = "0.3.8" } -gpui_platform = { package = "gpui-pre-platform", version = "0.3.8", features = ["font-kit", "x11", "wayland"] } -gpui_linux = { package = "gpui-pre-linux", version = "0.3.8" } -gpui_windows = { package = "gpui-pre-windows", version = "0.3.8" } -gpui_macos = { package = "gpui-pre-macos", version = "0.3.8" } -gpui_web = { package = "gpui-pre-web", version = "0.3.8" } -gpui_util = { package = "gpui-pre-util", version = "0.3.8" } -reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.8" } -gpui_tokio = { path = "crates/gpui_tokio" } -gpui-base = "0.7.1" - -# Nostr -nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" } -nostr-memory = { git = "https://github.com/rust-nostr/nostr" } -nostr-blossom = { git = "https://github.com/rust-nostr/nostr" } -nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" } -nostr-connect = { git = "https://github.com/rust-nostr/nostr" } -nostr-sdk = { git = "https://github.com/rust-nostr/nostr" } -nostr = { git = "https://github.com/rust-nostr/nostr", features = [ "nip59", "nip49", "nip44", "nip98" ] } - -# Crypto (NIP-17 encrypted file messages) -aes-gcm = "0.10" -base64 = "0.22" -sha2 = "0.10" -data-encoding = "2" -hkdf = "0.12" -# Pinned to the instances `nostr` already builds: the NIP-44 message-key disclosure -chacha20 = "0.9" -hmac = "0.12" -# Pinned to the instance `nostr-sdk` already builds, so NIP-44 nonces share it -rand = { version = "0.10", default-features = false, features = [ "std", "sys_rng", "thread_rng" ] } - -# Others -anyhow = "1.0.44" -chrono = { version = "0.4.38", features = ["wasmbind"] } -futures = "0.3" -itertools = "0.13.0" -log = "0.4" -flume = { version = "0.11.1", default-features = false, features = ["async", "select"] } -rust-embed = { version = "8.5", features = ["include-exclude"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -schemars = "1" -smallvec = "1.14.0" -smol = "2" -webbrowser = "1.0.4" -tracing-subscriber = { version = "0.3.18", features = ["fmt"] } -instant = "0.1" - -[profile.release] -strip = true -opt-level = "z" -lto = true -codegen-units = 1 -panic = "abort" - -[profile.profiling] -inherits = "release" -debug = true +[workspace] +resolver = "2" +members = ["crates/*", "desktop", "web"] +default-members = ["desktop"] + +[workspace.package] +version = "2.0.0-alpha" +edition = "2024" +publish = false + +[workspace.dependencies] +gpui = { package = "gpui-pre", version = "0.3.8" } +gpui_platform = { package = "gpui-pre-platform", version = "0.3.8", features = ["font-kit", "x11", "wayland"] } +gpui_linux = { package = "gpui-pre-linux", version = "0.3.8" } +gpui_windows = { package = "gpui-pre-windows", version = "0.3.8" } +gpui_macos = { package = "gpui-pre-macos", version = "0.3.8" } +gpui_web = { package = "gpui-pre-web", version = "0.3.8" } +gpui_util = { package = "gpui-pre-util", version = "0.3.8" } +reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.8" } +gpui_tokio = { path = "crates/gpui_tokio" } +gpui-base = "0.7.1" +gpui-kit = "0.7.3" + +# Nostr +nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" } +nostr-memory = { git = "https://github.com/rust-nostr/nostr" } +nostr-blossom = { git = "https://github.com/rust-nostr/nostr" } +nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" } +nostr-connect = { git = "https://github.com/rust-nostr/nostr" } +nostr-sdk = { git = "https://github.com/rust-nostr/nostr" } +nostr = { git = "https://github.com/rust-nostr/nostr", features = [ "nip59", "nip49", "nip44", "nip98" ] } + +# Crypto (NIP-17 encrypted file messages) +aes-gcm = "0.10" +base64 = "0.22" +sha2 = "0.10" +data-encoding = "2" +hkdf = "0.12" +# Pinned to the instances `nostr` already builds: the NIP-44 message-key disclosure +chacha20 = "0.9" +hmac = "0.12" +# Pinned to the instance `nostr-sdk` already builds, so NIP-44 nonces share it +rand = { version = "0.10", default-features = false, features = [ "std", "sys_rng", "thread_rng" ] } + +# Others +anyhow = "1.0.44" +chrono = { version = "0.4.38", features = ["wasmbind"] } +futures = "0.3" +itertools = "0.13.0" +log = "0.4" +flume = { version = "0.11.1", default-features = false, features = ["async", "select"] } +rust-embed = { version = "8.5", features = ["include-exclude"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +schemars = "1" +smallvec = "1.14.0" +smol = "2" +webbrowser = "1.0.4" +tracing-subscriber = { version = "0.3.18", features = ["fmt"] } +instant = "0.1" + +[profile.release] +strip = true +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" + +[profile.profiling] +inherits = "release" +debug = true diff --git a/assets/icons/arrow-down-circle.svg b/assets/icons/arrow-down-circle.svg deleted file mode 100644 index fea6250e..00000000 --- a/assets/icons/arrow-down-circle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/arrow-left.svg b/assets/icons/arrow-left.svg deleted file mode 100644 index ac3e659d..00000000 --- a/assets/icons/arrow-left.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/assets/icons/arrow-right.svg b/assets/icons/arrow-right.svg deleted file mode 100644 index 8aa7e0d4..00000000 --- a/assets/icons/arrow-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/ban.svg b/assets/icons/ban.svg deleted file mode 100644 index 0129765f..00000000 --- a/assets/icons/ban.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/book.svg b/assets/icons/book.svg deleted file mode 100644 index 9f439a6f..00000000 --- a/assets/icons/book.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/boom.svg b/assets/icons/boom.svg deleted file mode 100644 index dcaf9402..00000000 --- a/assets/icons/boom.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret-down.svg b/assets/icons/caret-down.svg deleted file mode 100644 index 36be93ed..00000000 --- a/assets/icons/caret-down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret-right.svg b/assets/icons/caret-right.svg deleted file mode 100644 index 1d0cc50f..00000000 --- a/assets/icons/caret-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/caret-up.svg b/assets/icons/caret-up.svg deleted file mode 100644 index 6e3a8a1b..00000000 --- a/assets/icons/caret-up.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/check-circle.svg b/assets/icons/check-circle.svg deleted file mode 100644 index 6b1b8d0f..00000000 --- a/assets/icons/check-circle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/check.svg b/assets/icons/check.svg deleted file mode 100644 index 299e0013..00000000 --- a/assets/icons/check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/chevron-down.svg b/assets/icons/chevron-down.svg deleted file mode 100644 index d8788af6..00000000 --- a/assets/icons/chevron-down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/close-circle-fill.svg b/assets/icons/close-circle-fill.svg deleted file mode 100644 index 079ddbbe..00000000 --- a/assets/icons/close-circle-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/close-circle.svg b/assets/icons/close-circle.svg deleted file mode 100644 index 201fc939..00000000 --- a/assets/icons/close-circle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/close.svg b/assets/icons/close.svg deleted file mode 100644 index 484ffce2..00000000 --- a/assets/icons/close.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/compass.svg b/assets/icons/compass.svg deleted file mode 100644 index 6cd227dc..00000000 --- a/assets/icons/compass.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/copy.svg b/assets/icons/copy.svg deleted file mode 100644 index 7286461e..00000000 --- a/assets/icons/copy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/device.svg b/assets/icons/device.svg deleted file mode 100644 index 8c54aef5..00000000 --- a/assets/icons/device.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/door.svg b/assets/icons/door.svg deleted file mode 100644 index b9cb5533..00000000 --- a/assets/icons/door.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/edit.svg b/assets/icons/edit.svg deleted file mode 100644 index 9387f08a..00000000 --- a/assets/icons/edit.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/ellipsis.svg b/assets/icons/ellipsis.svg deleted file mode 100644 index 411f5457..00000000 --- a/assets/icons/ellipsis.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/emoji.svg b/assets/icons/emoji.svg deleted file mode 100644 index 2df5b844..00000000 --- a/assets/icons/emoji.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/eye.svg b/assets/icons/eye.svg deleted file mode 100644 index 44d426ab..00000000 --- a/assets/icons/eye.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/fistbump-fill.svg b/assets/icons/fistbump-fill.svg deleted file mode 100644 index ad34489b..00000000 --- a/assets/icons/fistbump-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/fistbump.svg b/assets/icons/fistbump.svg deleted file mode 100644 index 4a3d0dc9..00000000 --- a/assets/icons/fistbump.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/folder.svg b/assets/icons/folder.svg deleted file mode 100644 index 65967a9b..00000000 --- a/assets/icons/folder.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/group.svg b/assets/icons/group.svg deleted file mode 100644 index c150fe35..00000000 --- a/assets/icons/group.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/hashtag.svg b/assets/icons/hashtag.svg deleted file mode 100644 index 2459408b..00000000 --- a/assets/icons/hashtag.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/history.svg b/assets/icons/history.svg deleted file mode 100644 index e7c76e68..00000000 --- a/assets/icons/history.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/inbox-fill.svg b/assets/icons/inbox-fill.svg deleted file mode 100644 index 719c0506..00000000 --- a/assets/icons/inbox-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/inbox.svg b/assets/icons/inbox.svg deleted file mode 100644 index 5569c57c..00000000 --- a/assets/icons/inbox.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/info.svg b/assets/icons/info.svg deleted file mode 100644 index 7c816dcd..00000000 --- a/assets/icons/info.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/input.svg b/assets/icons/input.svg deleted file mode 100644 index 86efa0e7..00000000 --- a/assets/icons/input.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/invite.svg b/assets/icons/invite.svg deleted file mode 100644 index 228373b0..00000000 --- a/assets/icons/invite.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/link.svg b/assets/icons/link.svg deleted file mode 100644 index f5217bfd..00000000 --- a/assets/icons/link.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/loader.svg b/assets/icons/loader.svg deleted file mode 100644 index f6ff93fa..00000000 --- a/assets/icons/loader.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/assets/icons/lock.svg b/assets/icons/lock.svg deleted file mode 100644 index ea03c36d..00000000 --- a/assets/icons/lock.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/login.svg b/assets/icons/login.svg deleted file mode 100644 index dc5ec458..00000000 --- a/assets/icons/login.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/logout.svg b/assets/icons/logout.svg deleted file mode 100644 index 05534526..00000000 --- a/assets/icons/logout.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/message.svg b/assets/icons/message.svg deleted file mode 100644 index 87e04016..00000000 --- a/assets/icons/message.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/moon.svg b/assets/icons/moon.svg deleted file mode 100644 index 68dd15ae..00000000 --- a/assets/icons/moon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/panel-left-open.svg b/assets/icons/panel-left-open.svg deleted file mode 100644 index 62bae092..00000000 --- a/assets/icons/panel-left-open.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/panel-left.svg b/assets/icons/panel-left.svg deleted file mode 100644 index 6cac95c5..00000000 --- a/assets/icons/panel-left.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/panel-right-open.svg b/assets/icons/panel-right-open.svg deleted file mode 100644 index 6387eaee..00000000 --- a/assets/icons/panel-right-open.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/panel-right.svg b/assets/icons/panel-right.svg deleted file mode 100644 index 636e01d2..00000000 --- a/assets/icons/panel-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/paper-plane-fill.svg b/assets/icons/paper-plane-fill.svg deleted file mode 100644 index 8231630f..00000000 --- a/assets/icons/paper-plane-fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/plus-chat.svg b/assets/icons/plus-chat.svg deleted file mode 100644 index e4649d28..00000000 --- a/assets/icons/plus-chat.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/plus-circle.svg b/assets/icons/plus-circle.svg deleted file mode 100644 index 96ac6546..00000000 --- a/assets/icons/plus-circle.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/plus.svg b/assets/icons/plus.svg deleted file mode 100644 index ed27febe..00000000 --- a/assets/icons/plus.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/profile.svg b/assets/icons/profile.svg deleted file mode 100644 index 0ed13004..00000000 --- a/assets/icons/profile.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/refresh.svg b/assets/icons/refresh.svg deleted file mode 100644 index b7ec11eb..00000000 --- a/assets/icons/refresh.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/relay.svg b/assets/icons/relay.svg deleted file mode 100644 index ea4e7b43..00000000 --- a/assets/icons/relay.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/reply.svg b/assets/icons/reply.svg deleted file mode 100644 index 499a40a8..00000000 --- a/assets/icons/reply.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/reset.svg b/assets/icons/reset.svg deleted file mode 100644 index 4bc319db..00000000 --- a/assets/icons/reset.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/scan.svg b/assets/icons/scan.svg deleted file mode 100644 index 489cb126..00000000 --- a/assets/icons/scan.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/search.svg b/assets/icons/search.svg deleted file mode 100644 index 01ed3cc0..00000000 --- a/assets/icons/search.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/settings.svg b/assets/icons/settings.svg deleted file mode 100644 index cb2a6acd..00000000 --- a/assets/icons/settings.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/settings2.svg b/assets/icons/settings2.svg deleted file mode 100644 index 07260972..00000000 --- a/assets/icons/settings2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/shield.svg b/assets/icons/shield.svg deleted file mode 100644 index dacf3b7d..00000000 --- a/assets/icons/shield.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/ship.svg b/assets/icons/ship.svg deleted file mode 100644 index 137c9778..00000000 --- a/assets/icons/ship.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/sun.svg b/assets/icons/sun.svg deleted file mode 100644 index b9d5dbdf..00000000 --- a/assets/icons/sun.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/upload.svg b/assets/icons/upload.svg deleted file mode 100644 index d92353e7..00000000 --- a/assets/icons/upload.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/assets/icons/usb.svg b/assets/icons/usb.svg deleted file mode 100644 index 1d89050e..00000000 --- a/assets/icons/usb.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/user-key.svg b/assets/icons/user-key.svg deleted file mode 100644 index 72d57dcc..00000000 --- a/assets/icons/user-key.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/warning.svg b/assets/icons/warning.svg deleted file mode 100644 index 07eeb040..00000000 --- a/assets/icons/warning.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/assets/icons/window-close.svg b/assets/icons/window-close.svg deleted file mode 100644 index 9fc48625..00000000 --- a/assets/icons/window-close.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/assets/icons/window-maximize.svg b/assets/icons/window-maximize.svg deleted file mode 100644 index 7fdbbd88..00000000 --- a/assets/icons/window-maximize.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/assets/icons/window-minimize.svg b/assets/icons/window-minimize.svg deleted file mode 100644 index d6b135d0..00000000 --- a/assets/icons/window-minimize.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/assets/icons/window-restore.svg b/assets/icons/window-restore.svg deleted file mode 100644 index 14016590..00000000 --- a/assets/icons/window-restore.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/icons/zoom.svg b/assets/icons/zoom.svg deleted file mode 100644 index 663232e2..00000000 --- a/assets/icons/zoom.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - diff --git a/crates/assets/Cargo.toml b/crates/assets/Cargo.toml index 2d89f8a8..21b3f8ad 100644 --- a/crates/assets/Cargo.toml +++ b/crates/assets/Cargo.toml @@ -8,3 +8,4 @@ publish.workspace = true gpui.workspace = true anyhow.workspace = true rust-embed.workspace = true +gpui-kit.workspace = true diff --git a/crates/assets/src/lib.rs b/crates/assets/src/lib.rs index a226cfa5..976c89f6 100644 --- a/crates/assets/src/lib.rs +++ b/crates/assets/src/lib.rs @@ -10,6 +10,25 @@ use rust_embed::RustEmbed; #[exclude = "*.DS_Store"] pub struct Assets; +/// Coop's bundled assets, with the GPUI Kit component icon bundle (Lucide) as +/// the fallback so `IconName` SVGs resolve alongside coop's own files. +pub struct CombinedAssets; + +impl AssetSource for CombinedAssets { + fn load(&self, path: &str) -> Result>> { + if let Some(data) = Assets.load(path)? { + return Ok(Some(data)); + } + gpui_kit::assets::Assets.load(path) + } + + fn list(&self, path: &str) -> Result> { + let mut paths = Assets.list(path)?; + paths.extend(gpui_kit::assets::Assets.list(path)?); + Ok(paths) + } +} + impl AssetSource for Assets { fn load(&self, path: &str) -> Result>> { Self::get(path) diff --git a/crates/chat_ui/Cargo.toml b/crates/chat_ui/Cargo.toml index 193b0aaa..810ba59e 100644 --- a/crates/chat_ui/Cargo.toml +++ b/crates/chat_ui/Cargo.toml @@ -7,7 +7,7 @@ publish.workspace = true [dependencies] state = { path = "../state" } ui = { path = "../ui" } -theme = { path = "../theme" } +gpui-kit.workspace = true common = { path = "../common" } person = { path = "../person" } chat = { path = "../chat" } diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index bf12d6e8..6d25e6e0 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -9,12 +9,22 @@ use common::{TimestampExt, emojis_from_tags}; use gpui::prelude::FluentBuilder; use gpui::{ AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, - Focusable, ImageSource, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, - MouseButton, ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, SharedUri, + Focusable, InteractiveElement, IntoElement, ListAlignment, ListOffset, ListState, MouseButton, + ObjectFit, ParentElement, PathPromptOptions, Render, SharedString, SharedUri, StatefulInteractiveElement, Styled, StyledImage, Subscription, SystemNotification, SystemNotificationAction, Task, WeakEntity, Window, div, img, list, px, red, relative, retain_all, svg, white, }; +use gpui_kit::assets::IconName; +use gpui_kit::component::avatar::Avatar; +use gpui_kit::component::button::{Button, ButtonVariants}; +use gpui_kit::component::dock::{Panel, PanelEvent}; +use gpui_kit::component::input::{Input, InputEvent, InputState, Textarea, TextareaState}; +use gpui_kit::component::menu::DropdownMenu; +use gpui_kit::component::notification::Notification; +use gpui_kit::component::scroll::Scrollbar; +use gpui_kit::component::tooltip::Tooltip; +use gpui_kit::component::{ActiveTheme, Disableable, Icon, Sizable, WindowExt, h_flex, v_flex}; use itertools::Itertools; use nostr_sdk::prelude::*; use person::{Directory, Person}; @@ -24,21 +34,12 @@ use smallvec::{SmallVec, smallvec}; use state::{ FileAttachment, NostrRegistry, download_and_decrypt_to_file, upload, upload_encrypted, }; -use theme::{ActiveTheme, TextStyle, Typeset as _}; -use ui::avatar::Avatar; -use ui::button::{Button, ButtonVariants}; -use ui::dock::{Panel, PanelEvent}; -use ui::input::{Input, InputEvent, InputState, Textarea, TextareaState}; -use ui::menu::DropdownMenu; -use ui::message::{ - MessageMedia, MessageRow, ReactionBar, ReplyPreview, WelcomeMessage, group_reactions, opens_run, -}; -use ui::notification::Notification; -use ui::scroll::Scrollbar; -use ui::tooltip::Tooltip; -use ui::{Disableable, Icon, IconName, Sizable, StyledExt, WindowExtension, h_flex, v_flex}; +use ui::{TextStyle, Typeset as _}; use crate::file::*; +use crate::message::{ + MessageMedia, MessageRow, ReactionBar, ReplyPreview, WelcomeMessage, group_reactions, opens_run, +}; use crate::text::RenderedText; const REACTION_EMOJIS: &[&str] = &["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀"]; @@ -59,6 +60,7 @@ static CUSTOM_EMOJI_RE: LazyLock = mod actions; mod file; +pub mod message; pub mod text; pub fn init(room: WeakEntity, window: &mut Window, cx: &mut App) -> Entity { @@ -126,6 +128,11 @@ pub struct ChatPanel { } impl ChatPanel { + /// The room this panel displays, used to dedupe open tabs. + pub fn room_id(&self) -> &SharedString { + &self.id + } + pub fn new(room: WeakEntity, window: &mut Window, cx: &mut Context) -> Self { // Define attachments and replies_to entities let attachments = cx.new(|_| vec![]); @@ -916,7 +923,7 @@ impl ChatPanel { let seen_on = chat.read(cx).rumor_seen_on(id); window.open_dialog(cx, move |this, _window, cx| { - this.title("Seen on").show_close(true).child( + this.title("Seen on").close_button(true).child( v_flex() .gap_1() .when_none(&seen_on, |this| { @@ -925,8 +932,8 @@ impl ChatPanel { .h_10() .justify_center() .text_style(TextStyle::Callout) - .bg(cx.theme().surface_raised) - .rounded(px(theme::panel_radius())) + .bg(cx.theme().secondary) + .rounded(px(ui::panel_radius())) .child("Message isn't traced yet"), ) }) @@ -939,9 +946,9 @@ impl ChatPanel { h_flex() .h_7() .px_2() - .gap(px(theme::SPACE)) - .bg(cx.theme().surface_raised) - .rounded(px(theme::control_radius())) + .gap(px(ui::SPACE)) + .bg(cx.theme().secondary) + .rounded(px(ui::control_radius())) .text_style(TextStyle::Callout) .child(div().size_1p5().rounded_full().bg(gpui::green())) .child(SharedString::from(url.to_string())), @@ -962,7 +969,7 @@ impl ChatPanel { let relays = profile.messaging_relays(); this.title("Messaging Relays") - .show_close(true) + .close_button(true) .child(v_flex().gap_1().children({ let mut items = vec![]; @@ -971,9 +978,9 @@ impl ChatPanel { h_flex() .h_7() .px_2() - .gap(px(theme::SPACE)) - .bg(cx.theme().surface_raised) - .rounded(px(theme::control_radius())) + .gap(px(ui::SPACE)) + .bg(cx.theme().secondary) + .rounded(px(ui::control_radius())) .text_style(TextStyle::Callout) .child(div().size_1p5().rounded_full().bg(gpui::green())) .child(SharedString::from(url.to_string())), @@ -996,7 +1003,7 @@ impl ChatPanel { svg() .path("brand/coop.svg") .size_12() - .text_color(cx.theme().surface_raised), + .text_color(cx.theme().secondary), ) .message(PRIVATE_NOTICE) .into_any_element() @@ -1012,13 +1019,13 @@ impl ChatPanel { .justify_center() .text_center() .text_style(TextStyle::Caption) - .text_color(cx.theme().text_faint) + .text_color(cx.theme().muted_foreground) .line_height(relative(1.3)) .child( svg() .path("brand/coop.svg") .size_12() - .text_color(cx.theme().element_active), + .text_color(cx.theme().list_active), ) .child(PRIVATE_NOTICE) .into_any_element() @@ -1041,9 +1048,9 @@ impl ChatPanel { .size_8() .justify_center() .rounded_full() - .bg(cx.theme().warning_muted) + .bg(cx.theme().warning.opacity(0.15)) .text_color(cx.theme().warning) - .child(Icon::new(IconName::Warning).small()), + .child(Icon::new(IconName::TriangleAlert).small()), ) .child( div() @@ -1146,10 +1153,14 @@ impl ChatPanel { .show_author(show_author) .hide_avatar(hide_avatar) .avatar( - Avatar::new(author.avatar()) - .seed(author.avatar_seed()) - .flex_shrink_0() + Button::new("message-avatar") + .ghost() .relative() + .flex_shrink_0() + .child(match author.avatar() { + Some(picture) => Avatar::new().src(picture).into_any_element(), + None => Avatar::new().into_any_element(), + }) .dropdown_menu(move |this, _window, _cx| { this.menu("Public Key", Box::new(Command::Copy(pk))) .menu("View Relays", Box::new(Command::Relays(pk))) @@ -1173,7 +1184,7 @@ impl ChatPanel { .when(has_reactions, |this| this.child(reactions)) .overlay( div() - .group_hover("", |this| this.bg(cx.theme().element_active)) + .group_hover("", |this| this.bg(cx.theme().list_active)) .absolute() .left_0() .top_0() @@ -1247,7 +1258,7 @@ impl ChatPanel { div() .id(SharedString::from(id.to_hex())) .child(label) - .when(failed, |this| this.text_color(cx.theme().danger_muted)) + .when(failed, |this| this.text_color(cx.theme().danger)) .when_some(reports, |this, reports| { this.when(!pending, |this| { this.on_click(move |_e, window, cx| { @@ -1265,7 +1276,7 @@ impl ChatPanel { .unwrap_or_default(); this.title(SharedString::from("Sent Reports")) - .show_close(true) + .close_button(true) .child(v_flex().gap_4().children(items)) }); }) @@ -1279,18 +1290,23 @@ impl ChatPanel { let avatar = profile.avatar(); v_flex() - .gap(px(theme::SPACE)) + .gap(px(ui::SPACE)) .w_full() .child( h_flex() - .gap(px(theme::SPACE)) + .gap(px(ui::SPACE)) .text_style(TextStyle::Callout) .child(SharedString::from("Sent to:")) .child( h_flex() .gap_1() - .font_semibold() - .child(Avatar::new(avatar).seed(profile.avatar_seed()).small()) + .font_weight(gpui::FontWeight::SEMIBOLD) + .child(match avatar { + Some(picture) => { + Avatar::new().src(picture).small().into_any_element() + } + None => Avatar::new().small().into_any_element(), + }) .child(name.clone()), ), ) @@ -1303,8 +1319,8 @@ impl ChatPanel { .h_16() .w_full() .text_style(TextStyle::Callout) - .rounded(px(theme::surface_radius())) - .bg(cx.theme().warning_muted) + .rounded(px(ui::surface_radius())) + .bg(cx.theme().warning.opacity(0.15)) .text_color(cx.theme().warning) .child(div().flex_1().w_full().text_center().child(error)), ) @@ -1312,7 +1328,7 @@ impl ChatPanel { .when_some(report.output().cloned(), |this, output| { this.child( v_flex() - .gap(px(theme::SPACE)) + .gap(px(ui::SPACE)) .w_full() .children({ let mut items = Vec::with_capacity(output.failed.len()); @@ -1323,19 +1339,19 @@ impl ChatPanel { .gap_0p5() .p_1() .w_full() - .rounded(px(theme::surface_radius())) + .rounded(px(ui::surface_radius())) .bg(cx.theme().danger.opacity(0.06)) .child( div() .text_style(TextStyle::Caption) - .font_semibold() + .font_weight(gpui::FontWeight::SEMIBOLD) .line_height(relative(1.25)) .child(SharedString::from(url.to_string())), ) .child( div() .text_style(TextStyle::Caption) - .text_color(cx.theme().danger_muted.opacity(0.9)) + .text_color(cx.theme().danger.opacity(0.9)) .line_height(relative(1.25)) .child(SharedString::from(msg.to_string())), ), @@ -1353,12 +1369,12 @@ impl ChatPanel { .gap_0p5() .p_1() .w_full() - .rounded(px(theme::surface_radius())) - .bg(cx.theme().surface_card) + .rounded(px(ui::surface_radius())) + .bg(cx.theme().muted) .child( div() .text_style(TextStyle::Caption) - .font_semibold() + .font_weight(gpui::FontWeight::SEMIBOLD) .line_height(relative(1.25)) .child(SharedString::from(url.0.to_string())), ) @@ -1392,10 +1408,10 @@ impl ChatPanel { .right_4() .top_neg_2() .when(cx.theme().shadow, |this| this.shadow_sm()) - .rounded(px(theme::surface_radius())) + .rounded(px(ui::surface_radius())) .border_1() .border_color(cx.theme().border) - .bg(cx.theme().bg) + .bg(cx.theme().background) .children({ let mut items = vec![]; @@ -1472,7 +1488,7 @@ impl ChatPanel { img(url.as_str()) .size_16() .when(cx.theme().shadow, |this| this.shadow_lg()) - .rounded(px(theme::control_radius())) + .rounded(px(ui::control_radius())) .object_fit(ObjectFit::Cover), ) .child( @@ -1486,7 +1502,7 @@ impl ChatPanel { .justify_center() .rounded_full() .bg(red()) - .child(Icon::new(IconName::Close).size_2().text_color(white())), + .child(Icon::new(IconName::X).size_2().text_color(white())), ) .on_click({ let url = url.clone(); @@ -1530,10 +1546,10 @@ impl ChatPanel { .child( img(path.clone()) .border_1() - .border_color(cx.theme().border_faint) + .border_color(cx.theme().border) .h(px(250.)) .object_fit(ObjectFit::Cover) - .rounded(px(theme::bubble_radius())), + .rounded(px(ui::bubble_radius())), ) .into_any_element(); } @@ -1561,12 +1577,12 @@ impl ChatPanel { .self_start() .items_start() .min_w_0() - .gap(px(theme::SPACE)) + .gap(px(ui::SPACE)) .p_2() .border_1() - .border_color(cx.theme().border_faint) - .rounded(px(theme::control_radius())) - .child(Icon::new(IconName::Lock).text_color(cx.theme().accent)) + .border_color(cx.theme().border) + .rounded(px(ui::control_radius())) + .child(Icon::new(IconName::Lock).text_color(cx.theme().primary)) .child( v_flex() .min_w_0() @@ -1576,7 +1592,7 @@ impl ChatPanel { .child( div() .text_style(TextStyle::Caption) - .text_color(cx.theme().text_faint) + .text_color(cx.theme().muted_foreground) .child(label), ), ) @@ -1600,14 +1616,14 @@ impl ChatPanel { .id(SharedString::from(file.url.to_string())) .relative() .w_16() - .tooltip(move |window, cx| Tooltip::new(label.clone(), window, cx).into()) + .tooltip(move |window, cx| Tooltip::new(label.clone()).build(window, cx)) .map(|this| { if file.is_image() { this.child( img(pending.path.clone()) .size_16() .when(cx.theme().shadow, |this| this.shadow_sm()) - .rounded(px(theme::control_radius())) + .rounded(px(ui::control_radius())) .object_fit(ObjectFit::Cover), ) } else { @@ -1617,10 +1633,10 @@ impl ChatPanel { .flex() .items_center() .justify_center() - .rounded(px(theme::control_radius())) + .rounded(px(ui::control_radius())) .border_1() - .border_color(cx.theme().border_faint) - .bg(cx.theme().surface) + .border_color(cx.theme().border) + .bg(cx.theme().sidebar) .text_style(TextStyle::Caption) .text_center() .child("Preview not available"), @@ -1637,7 +1653,7 @@ impl ChatPanel { .justify_center() .rounded_full() .border_1() - .border_color(cx.theme().border_faint) + .border_color(cx.theme().border) .bg(gpui::green()) .child(Icon::new(IconName::Lock).size_2().text_color(gpui::white())), ) @@ -1671,7 +1687,7 @@ impl ChatPanel { .w_full() .pl_2() .border_l_2() - .border_color(cx.theme().element_active) + .border_color(cx.theme().list_active) .child( h_flex() .justify_between() @@ -1681,13 +1697,13 @@ impl ChatPanel { .items_baseline() .gap_1() .text_style(TextStyle::Caption) - .text_color(cx.theme().text_muted) + .text_color(cx.theme().muted_foreground) .child("Replying to:") - .child(div().text_color(cx.theme().accent).child(profile.name())), + .child(div().text_color(cx.theme().primary).child(profile.name())), ) .child( Button::new("remove-reply") - .icon(IconName::Close) + .icon(IconName::X) .xsmall() .ghost() .on_click({ @@ -1784,15 +1800,13 @@ impl ChatPanel { .unwrap_or_default(); Button::new("emoji") - .icon(IconName::Emoji) + .icon(IconName::Sticker) .ghost() .large() .dropdown_menu_with_anchor(gpui::Anchor::BottomLeft, move |this, _window, _cx| { - let menu = REACTION_EMOJIS - .iter() - .fold(this.horizontal(), |this, emoji| { - this.menu(*emoji, Box::new(Command::Insert(emoji.to_string()))) - }); + let menu = REACTION_EMOJIS.iter().fold(this, |this, emoji| { + this.menu(*emoji, Box::new(Command::Insert(emoji.to_string()))) + }); emojis.iter().fold(menu, |this, (shortcode, _)| { let label = format!(":{shortcode}:"); @@ -1802,22 +1816,26 @@ impl ChatPanel { } } -impl Panel for ChatPanel { - fn panel_id(&self) -> SharedString { - self.id.clone() +impl gpui_kit::component::dock::BasePanel for ChatPanel { + fn panel_name(&self) -> &'static str { + "ChatPanel" } +} - fn title(&self, cx: &App) -> AnyElement { +impl Panel for ChatPanel { + fn title(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { self.room .read_with(cx, |this, cx| { let label = this.display_name(cx); let picture = this.display_image(cx); - let seed = this.display_image_seed(cx); h_flex() .gap_1() .text_style(TextStyle::Caption) - .child(Avatar::new(picture).seed(seed).xsmall()) + .child(match picture { + Some(picture) => Avatar::new().src(picture).xsmall().into_any_element(), + None => Avatar::new().xsmall().into_any_element(), + }) .child(label) .into_any_element() }) @@ -1829,12 +1847,16 @@ impl Panel for ChatPanel { ) } - fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec