diff --git a/CHANGELOG.md b/CHANGELOG.md index c68172d..96783e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### Added -- Follow the operating system's native theme on macOS, Windows and Linux (KDE, GNOME) via `native-theme`, applying the OS colors, fonts, radii and accessibility preferences; the bundled themes remain as the fallback when the OS theme cannot be read +- Follow the operating system's native theme on macOS, Windows and Linux (KDE, GNOME), applying the OS colors, fonts, radii and accessibility preferences. - Add an identity import dialog, importing an existing identity by pasting its `nsec` secret key (encrypted with a new passphrase), NIP-49 `ncryptsec` (unlocked with its passphrase), or `bunker://` NIP-46 URI - Add a dropdown menu on the signed-in user button in the sidebar - Add sign-out feature, confirmed by an alert dialog before deleting the stored session credential @@ -18,6 +18,7 @@ ### Changed +- Use the built-in light/dark syntax highlighting palette instead of the Signed palette bundled in `signed.json` when the native theme is active - Increase the default theme corner radii from 2 to 4 and large radii from 6 to 8 - Surface backend errors as error notifications in the workspace instead of silently dropping them - Break ties in repository activity lists by event id, so same-second events order deterministically diff --git a/docs/NATIVE-THEME-MIGRATION.md b/docs/NATIVE-THEME-MIGRATION.md deleted file mode 100644 index 5d2e1e6..0000000 --- a/docs/NATIVE-THEME-MIGRATION.md +++ /dev/null @@ -1,285 +0,0 @@ -# Native theme migration plan - -Integrate [`native-theme`](https://github.com/tiborgats/native-theme) (via the -`native-theme-gpui` connector) so the app follows the OS native theme on -macOS, Windows and Linux (KDE/GNOME), instead of only the bundled -`signed.json` themes. - -Principles for this migration: plug the connector in, migrate what it -replaces, do not wrap it, and do not adopt its optional subsystems (icons, -theme-drawn widgets, presets UI) in this pass. - ---- - -## 1. Current state - -| Piece | Location | Notes | -| --- | --- | --- | -| Startup theme setup | `desktop/src/main.rs` | `gpui_component::init` + `theme::init`, loads `signed.json` into `ThemeRegistry`, sets `Theme::global_mut` fields (`radius`, `radius_lg`, `focus_ring`, `shadow`, `font_size`, `mono_font_size`, `light_theme`, `dark_theme`), then `Theme::change` / `Theme::sync_system_appearance` | -| Runtime theme application | `crates/workspace/src/views/sidebar/settings_dialog.rs` | `apply_theme()` re-reads settings into the global `Theme`; `apply_appearance()` maps `AppearanceMode` to `Theme::change` / `sync_system_appearance` | -| System appearance follow | `crates/workspace/src/workspace.rs` | `cx.observe_window_appearance` → `Theme::sync_system_appearance` when appearance is `System` | -| Theme settings | `crates/settings/src/settings.rs` (`ThemeSettings`) | `light_theme`, `dark_theme`, `font_size`, `mono_font_size`, `radius`, `radius_lg`, `focus_ring`, `shadow` | -| Bundled themes | `crates/assets/assets/themes/signed.json` | `Signed Light` / `Signed Dark` colors + a `highlight` section (editor/syntax colors) | -| Theme consumers | `dock`, `signed_ui`, `workspace` | ~50 `cx.theme()` call sites via `gpui_component::ActiveTheme` | - -Dependencies today (branch `feat/native-theme`, commit `eb4fbb0`): -`gpui-pre` 0.3.9, `gpui-pre-platform` 0.3.9, `gpui-component` 0.8, -`gpui-base` 0.8, toolchain 1.96. - -## 2. What the connector does - -`native-theme-gpui` turns a `native_theme::ResolvedTheme` (read from the OS, -or from a bundled preset) into a fully configured `gpui_component::Theme` and -installs it: - -- all 139 `ThemeColor` fields, fonts, `radius`, `shadow`, `focus_ring`, - scrollbar and base-layer values; -- a `ThemeConfig` per mode, so the app's existing - `Theme::change(mode, None, cx)` / `Theme::sync_system_appearance(..)` - switch between the native light and dark palettes unchanged; -- an internal observer that restores the native values gpui-component - overwrites on each rebuild; -- accessibility: the OS text-scaling factor scales fonts (through the window - rem), reduce-motion and reduce-transparency are forwarded. - -Because the connector produces a regular gpui-component `Theme`, every -existing `cx.theme()` call site keeps working without modification. That is -the core reason this integration is small. - -Key APIs: `SystemTheme::from_system()`, `apply_system_theme(&system, cx)`, -`apply(theme, &resolved, &prefs, cx)`, `from_preset(name, is_dark, &prefs)`, -`AccessibilityPreferences::from_system()`. - -## 3. Gate 0 — version compatibility (blocking) - -Verified against crates.io and the upstream repo on 2026-10-21: - -| Fact | Value | -| --- | --- | -| Latest connector | `native-theme-gpui` 0.6.1 (2026-10-09); upstream `main` is the same | -| Connector requires | `gpui-component ^0.7.1`, `gpui-base ^0.7.1`, `gpui-pre ^0.3.8`, Rust ≥ 1.95 | -| `gpui-component`/`gpui-base` 0.7.1 pin | `gpui-pre =0.3.8` | -| `gpui-component`/`gpui-base` 0.7.3 pin | `gpui-pre =0.3.9` | -| `gpui-component` 0.8.0 pins | `gpui-pre =0.3.9` (0.8.0 published 2026-10-19) | -| This branch | moved 0.7.1 / 0.3.8 → 0.8 / 0.3.9 in `eb4fbb0` ("chore: update deps") | - -The pins are mutually exclusive: with the app on `gpui-component` 0.8, cargo -cannot unify the graph — the connector would pull a second `gpui-pre` 0.3.8 -and a second `gpui-component` 0.7.x, and `apply(.., cx)` would not even -type-check against the app's `App`. **The connector cannot be added on top of -gpui-component 0.8 today.** - -### Decision - -Revert the GPUI-side dependency bump on this branch and align with the -connector's supported closure (recommended): - -```toml -# [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"] } -reqwest_client = { package = "gpui-pre-reqwest-client", version = "0.3.8" } - -gpui-component = { version = "=0.7.1", features = ["tree-sitter-languages"] } -gpui-base = "=0.7.1" - -native-theme-gpui = { version = "0.6.1", default-features = false, features = ["system-fonts"] } -``` - -- This is the state of `master` before `eb4fbb0`, the version set the - connector was verified against (2026-10-08 stamp). The exact `=0.7.1` - pins are required, not a style choice: a caret `"0.7.1"` resolves 0.7.3, - whose `gpui-pre =0.3.9` pin drags the graph onto 0.3.9, outside the - closure the connector is verified against. The three `gpui-pre-*` family - crates are published in lockstep; all three move back to 0.3.8 together. -- The connector re-exports the `native-theme` types that appear in its - public signatures (`SystemTheme`, `AccessibilityPreferences`, …), so the - app does **not** need `native-theme` as a direct dependency. -- The cost of the revert is the two one-line tweaks `eb4fbb0` made for 0.8 - (the `Deref` removal in `crates/dock/src/dock_area.rs` is restored; the - 0.7.1 `ResizeHandle::on_drag` hands the closure an `Rc` and requires - `E: Render`). The nostr changes in that commit are unrelated and stay. -- When `native-theme-gpui` releases gpui-component 0.8 support (upstream has - bumped within days of each gpui-component release so far), bump both sides - together in one commit. - -Alternative, rejected for now: staying on gpui-component 0.8 and waiting for -upstream 0.8 support. Nothing in this migration depends on 0.8-only APIs, and -0.8.0 shipped two days before this plan, so the revert is the cheaper path. - -Note: the connector's README quick-start shows the `gpui-kit` facade crate; -this project depends on `gpui-component` + `gpui-base` directly, which is -equivalent — no `gpui-kit` dependency is needed. - -## 4. Target design - -### 4.1 Theme source - -The native OS theme becomes the primary source. The bundled `signed.json` -registry stays as the fallback when the OS theme cannot be read (unsupported -Linux desktop, headless, etc.). This keeps the existing startup code as the -fallback branch instead of deleting a working safety net. - -### 4.2 Startup flow (`desktop/src/main.rs`) - -Order is load-bearing: - -1. `gpui_component::init(cx)` and `theme::init(cx)` — must run before - `apply` (connector requirement). -2. `SettingsStore` load — already before any theme work. -3. `ThemeRegistry` load from `Assets.themes()` — kept, feeds the fallback. -4. Apply the native theme: - -```rust -// native_theme_gpui re-exports SystemTheme from native-theme. -match native_theme_gpui::SystemTheme::from_system() { - Ok(system) => native_theme_gpui::apply_system_theme(&system, cx), - Err(error) => { - tracing::warn!("native theme unavailable ({error}); using bundled themes"); - // existing path: registry lookups + Theme::global_mut field setup - } -} -// shared: the existing `match settings.appearance { .. }` block stays, -// now driving native palettes per mode on the native path. -``` - -`apply_system_theme` installs both the light and dark variants plus their -configs, so the subsequent `Theme::change` / `sync_system_appearance` for the -user's `AppearanceMode` picks the right native palette. - -Sizing note: gpui-component derives the window rem from `theme.font_size`, so -every rem-relative element scales with it. The OS-reported UI font (13 on -macOS) is smaller than the 16 this app is designed around, so the native path -multiplies `system.accessibility.text_scaling_factor` by -`settings.theme.font_size / native_size` before applying — the connector's -supported scaling knob, which also preserves any OS accessibility scaling. -The same factor scales `mono_font_size`, so code text follows the UI size -(the pre-migration app used 13 for mono while the UI was 16). - -On the native path, do **not** set `radius` / `font_size` / `light_theme` / -`dark_theme` etc. on `Theme::global_mut` — the connector owns those fields, -and overwriting them fights its observer. - -### 4.3 Appearance switching (no changes) - -- `AppearanceMode::System | Light | Dark` and `apply_appearance()` in - `settings_dialog.rs` work as-is. -- `workspace.rs`'s `observe_window_appearance` → `sync_system_appearance` - keeps following the OS while in `System` mode. -- `Theme::change(mode, None, cx)` reproduces the native palette for the mode - because the connector installed a `ThemeConfig` per variant. - -Known gap (addressed since): gpui-component's system-appearance follow is -driven by window-activation events. The app now enables native-theme's -`watch` feature and re-applies the theme on its signal (`start_theme_watcher` -in `desktop/src/main.rs`), so live OS changes apply without re-focusing or -restarting. macOS reports appearance changes only (`AppleInterfaceTheme- -ChangedNotification`); an accent-only change posts a different notification -the watcher does not observe yet — Windows (`UISettings::ColorValuesChanged`), -KDE (inotify on `kdeglobals`) and GNOME (portal `SettingChanged`) cover accent. - -### 4.4 Settings dialog (`settings_dialog.rs`) - -Keep: - -- the **Appearance** row (`System` / `Light` / `Dark`) and its handler. - -Remove (the native theme now owns all of these): - -- **Light Theme** / **Dark Theme** pickers and `theme_options()` — colors are - chosen by the OS. This is also the only way to avoid the connector's - registry trap: selecting a registry theme would overwrite the connector's - per-mode configs on the next `Theme::change` and silently drop native - colors until restart. -- **Font size**, **Mono font size**, **Radius**, **Large radius**, - **Focus ring**, **Shadows** rows and their handlers — the resolved theme - provides geometry, and the connector already scales fonts by the OS text - size. Keeping manual overrides would require re-applying them after every - upstream rebuild, a permanent fight with the connector. - -`apply_theme()` and `theme_options()` are deleted outright: with the pickers -and number fields gone they have no remaining callers. The fallback logic -lives only in `main.rs`'s `Err` branch; the Appearance handler keeps working -through `apply_appearance()` alone (`Theme::change` picks up whatever configs -are installed, bundled or native). - -`ThemeSettings` keeps its fields and serde shape — the fallback branch still -reads them, existing settings files keep parsing, and the tests in -`settings.rs` stay valid. They become fallback-only tuning (editable via -`settings.json`). Deleting the fields is a possible follow-up cleanup if the -fallback ever moves to native-theme presets. - -### 4.5 Syntax highlighting (known trade-off) - -`signed.json` carries a `highlight` section (editor and syntax colors) used by -code and markdown rendering (`TextView` in the files view and elsewhere). The -connector's installed per-mode configs carry upstream's default highlighter -styles, so on the native path the active syntax palette is upstream's, not -Signed's. - -Decision for this pass: accept upstream's defaults — zero code. If the Signed -syntax palette must be kept later, investigate whether gpui-component 0.7 -exposes a syntax-theme setter that survives `Theme::change`; do not build an -override mechanism now. - -### 4.6 Features - -Use the connector with narrowed features; the app uses its own SVG icons and -gpui-component widgets: - -```toml -native-theme-gpui = { version = "0.6.1", default-features = false, features = ["system-fonts"] } -``` - -`system-fonts` keeps a theme font family the system lacks drawing in the -platform's substitute. `widgets` (theme-drawn checkbox/switch/etc.), -`material-icons`, `lucide-icons`, `system-icons`, `svg-rasterize` are all -unused here and each carries build weight (the icon bundles are ~2 MB). -Adopting any of them is a separate, deliberate follow-up. - -## 5. Change list - -| File | Change | -| --- | --- | -| `Cargo.toml` | Revert gpui-side pins to 0.3.8 / `=0.7.1` (Gate 0); add the `native-theme-gpui` workspace dep | -| `desktop/Cargo.toml` | Add `native-theme-gpui` | -| `desktop/src/main.rs` | Native apply with bundled fallback (§4.2); move the settings-field setup into the fallback branch | -| `crates/workspace/src/views/sidebar/settings_dialog.rs` | Remove theme picker + font/radius/focus/shadow rows; shrink `apply_theme` to fallback-only (§4.4) | -| `CHANGELOG.md` | Added: native OS theme via `native-theme`; Removed: manual theme/font/radius settings | - -Unchanged: - -- every `cx.theme()` consumer in `dock`, `signed_ui`, `workspace` — including - the dock/tab skins and window controls, which keep reading tokens; -- `crates/workspace/src/workspace.rs` appearance observer; -- `crates/settings` (fields stay), `crates/assets` (`signed.json` stays as - the fallback), `crates/dock`. - -## 6. Validation - -1. `cargo check`, `cargo test --workspace`, `cargo clippy` across the workspace - (toolchain 1.96 satisfies the connector's 1.95 MSRV). -2. Manual matrix, per platform available (macOS first; KDE and GNOME Linux; - Windows): - - cold start matches the OS theme and accent; radius, fonts and controls - read as native; - - switching Appearance `System ↔ Light ↔ Dark` in settings switches native - palettes; - - with `System`, changing the OS theme and re-focusing the window updates - the app; - - settings dialog, dock tabs, diffs, files view, markdown rendering look - correct; the syntax palette difference from §4.5 is reviewed and - accepted; - - `cargo run` once with native detection forced to fail (e.g. temporarily - return early in the match) to prove the bundled fallback still works. -3. `cargo tree -i gpui-pre` and `cargo tree -i gpui-component` must each show - exactly one version — the whole point of Gate 0. - -## 7. Explicitly out of scope - -- native-theme icon sets / `CustomIconName` mapping — the app keeps its own - SVG assets. -- theme-drawn widgets (`widgets` feature). -- a preset-picker UI (`from_preset`) in settings. -- per-field user overrides layered on the native theme. -- migrating `signed.json` into a native-theme `Theme` definition.