From 1c4922a1b43b1af1cb970b645aab276e35be72f9 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 22 Sep 2026 14:09:34 +0200 Subject: [PATCH 01/16] feat(studio): boot the shell on gpui-kit with a light/dark/system theme Boot: gpui_kit::application().run(...) + gpui_kit::init(cx) inside it - there is no Application::new() in this kit. The window opens from cx.spawn(async move |cx| { cx.open_window(...) }) so the state entity and the theme can be set up with a live window and App context in hand before the first paint. Root wrapper: gpui_component::Root::render mounts the app's view but NOT the dialog/sheet/notification layers - the app has to composite those itself via Root::render_dialog_layer/_sheet_layer/ _notification_layer. Isolated all three calls in app/overlays.rs and nowhere else: gpui-kit 0.7.0 removes them with no documented replacement, so when that migration lands it is a one-file patch instead of a hunt across every screen. StudioRoot (app/root.rs) is currently a placeholder - it renders "Library" or "Editor" depending on StudioState.view, since library::view and editor::view (WS-3/WS-4) are not ported yet. What is live and testable ahead of them: the window, the theme, and the overlay layer stack. Theme: gpui-component's ThemeMode only has Light/Dark, and theme::init (run once by gpui_kit::init) hard-sets Light with nothing observing the OS. ThemePref::System is built on top in theme/mod.rs: - apply() resolves System via Theme::sync_system_appearance, which calls Theme::change internally - so it already re-projects onto the gpui-base layer (scrollbars, resize handles); no separate Theme::sync_base call is needed on this path. - watch_system_appearance() re-checks state.theme_pref on every window.observe_window_appearance callback (not a value captured at subscribe time), so it only re-resolves against the OS while the live preference is still System - an explicit Light/Dark choice made after subscribing is never clobbered by a later OS flip. - theme::set() is the pref-switching entry point the topbar's theme button (WS-4) will call once that screen exists; nothing calls it yet, hence the #[allow(dead_code)]. Persistence (theme/persist.rs): same shape as library::data::load_recents/push_recent - a JSON value under dirs::config_dir()/rustmotion/, here theme.json. Fixes the theme resetting to System on every launch. Window sizing (app/window.rs): 75% of the primary display, centered, same target as the old Dioxus shell's one-shot use_effect resize - but set on WindowOptions.window_bounds before the window opens via Bounds::centered(None, size, cx), so there is no first-frame resize flash. Falls back to a fixed 1280x800 when no display can be found, still centered, rather than the old silent no-op. Token mapping for screens styling off cx.theme() (gpui_component:: ActiveTheme) in place of the old --rm-* CSS custom properties (formerly THEME_CSS in app/root.rs): --rm-bg -> background --rm-surface -> popover (raised panel / card fill) --rm-surface-2 -> secondary (a step below popover) --rm-surface-3 -> muted (hover / selected fill) --rm-border -> border --rm-border-2 -> ring (stronger, focus-adjacent edge - no exact equivalent, this is guidance not a contract) --rm-text -> foreground --rm-text-strong -> popover_foreground / primary --rm-text-muted -> muted_foreground --rm-accent -> accent / primary --rm-on-accent -> accent_foreground / primary_foreground --rm-error -> danger (NOT destructive - that name does not exist on ThemeColor) --rm-overlay-hover -> muted (or accent at reduced opacity) --rm-overlay-border -> ring Nothing here re-brands the kit's default palette (no custom ThemeColor/ThemeConfig) - that is a separate, larger decision left to whichever workstream first needs brand-accurate colors. Also: gpui-component added as a direct Cargo.toml dependency (reported separately, not included in this diff) - ActiveTheme/Theme/ThemeMode/ Root are used by their real crate path throughout this code, which needs gpui-component in the extern prelude; gpui-kit only re-exports it as gpui_kit::component. --- .../assets/dx-components-theme.css | 70 -- crates/rustmotion-studio/src/app/overlays.rs | 9 + crates/rustmotion-studio/src/app/root.rs | 217 +---- crates/rustmotion-studio/src/app/window.rs | 55 ++ .../src/components/button/component.rs | 107 --- .../src/components/button/mod.rs | 2 - .../src/components/button/style.css | 202 ----- .../src/components/color_picker/component.rs | 567 ------------ .../src/components/color_picker/mod.rs | 2 - .../src/components/color_picker/style.css | 267 ------ .../src/components/input/component.rs | 54 -- .../src/components/input/mod.rs | 2 - .../src/components/input/style.css | 33 - .../rustmotion-studio/src/components/mod.rs | 14 - .../src/components/popover/component.rs | 59 -- .../src/components/popover/mod.rs | 1 - .../src/components/popover/style.css | 238 ----- .../src/components/select/component.rs | 120 --- .../src/components/select/mod.rs | 2 - .../src/components/select/style.css | 131 --- .../src/components/separator/component.rs | 23 - .../src/components/separator/mod.rs | 2 - .../src/components/separator/style.css | 13 - .../src/components/sheet/component.rs | 147 --- .../src/components/sheet/mod.rs | 2 - .../src/components/sheet/style.css | 253 ------ .../src/components/sidebar/component.rs | 839 ----------------- .../src/components/sidebar/mod.rs | 1 - .../src/components/sidebar/style.css | 855 ------------------ .../src/components/skeleton/component.rs | 17 - .../src/components/skeleton/mod.rs | 2 - .../src/components/skeleton/style.css | 16 - .../src/components/slider/component.rs | 51 -- .../src/components/slider/mod.rs | 2 - .../src/components/slider/style.css | 80 -- .../src/components/switch/component.rs | 20 - .../src/components/switch/mod.rs | 2 - .../src/components/switch/style.css | 48 - .../src/components/textarea/component.rs | 84 -- .../src/components/textarea/mod.rs | 2 - .../src/components/textarea/style.css | 84 -- .../src/components/tooltip/component.rs | 61 -- .../src/components/tooltip/mod.rs | 2 - .../src/components/tooltip/style.css | 150 --- crates/rustmotion-studio/src/theme/mod.rs | 34 + crates/rustmotion-studio/src/theme/persist.rs | 67 ++ 46 files changed, 195 insertions(+), 4814 deletions(-) delete mode 100644 crates/rustmotion-studio/assets/dx-components-theme.css create mode 100644 crates/rustmotion-studio/src/app/overlays.rs create mode 100644 crates/rustmotion-studio/src/app/window.rs delete mode 100644 crates/rustmotion-studio/src/components/button/component.rs delete mode 100644 crates/rustmotion-studio/src/components/button/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/button/style.css delete mode 100644 crates/rustmotion-studio/src/components/color_picker/component.rs delete mode 100644 crates/rustmotion-studio/src/components/color_picker/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/color_picker/style.css delete mode 100644 crates/rustmotion-studio/src/components/input/component.rs delete mode 100644 crates/rustmotion-studio/src/components/input/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/input/style.css delete mode 100644 crates/rustmotion-studio/src/components/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/popover/component.rs delete mode 100644 crates/rustmotion-studio/src/components/popover/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/popover/style.css delete mode 100644 crates/rustmotion-studio/src/components/select/component.rs delete mode 100644 crates/rustmotion-studio/src/components/select/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/select/style.css delete mode 100644 crates/rustmotion-studio/src/components/separator/component.rs delete mode 100644 crates/rustmotion-studio/src/components/separator/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/separator/style.css delete mode 100644 crates/rustmotion-studio/src/components/sheet/component.rs delete mode 100644 crates/rustmotion-studio/src/components/sheet/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/sheet/style.css delete mode 100644 crates/rustmotion-studio/src/components/sidebar/component.rs delete mode 100644 crates/rustmotion-studio/src/components/sidebar/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/sidebar/style.css delete mode 100644 crates/rustmotion-studio/src/components/skeleton/component.rs delete mode 100644 crates/rustmotion-studio/src/components/skeleton/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/skeleton/style.css delete mode 100644 crates/rustmotion-studio/src/components/slider/component.rs delete mode 100644 crates/rustmotion-studio/src/components/slider/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/slider/style.css delete mode 100644 crates/rustmotion-studio/src/components/switch/component.rs delete mode 100644 crates/rustmotion-studio/src/components/switch/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/switch/style.css delete mode 100644 crates/rustmotion-studio/src/components/textarea/component.rs delete mode 100644 crates/rustmotion-studio/src/components/textarea/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/textarea/style.css delete mode 100644 crates/rustmotion-studio/src/components/tooltip/component.rs delete mode 100644 crates/rustmotion-studio/src/components/tooltip/mod.rs delete mode 100644 crates/rustmotion-studio/src/components/tooltip/style.css create mode 100644 crates/rustmotion-studio/src/theme/mod.rs create mode 100644 crates/rustmotion-studio/src/theme/persist.rs diff --git a/crates/rustmotion-studio/assets/dx-components-theme.css b/crates/rustmotion-studio/assets/dx-components-theme.css deleted file mode 100644 index 46b1aefe..00000000 --- a/crates/rustmotion-studio/assets/dx-components-theme.css +++ /dev/null @@ -1,70 +0,0 @@ -/* This file contains the theme variables for the styled dioxus-components components. You only - * need to import this file once in your project root. - */ - -html[data-theme="dark"] { - --dark: initial; - --light: ; -} - -html[data-theme="light"] { - --dark: ; - --light: initial; -} - -html[data-theme="dark"] [class~="dxc-system"] { - --dxc-light-on: ; - --dxc-dark-on: initial; -} - -html[data-theme="light"] [class~="dxc-system"] { - --dxc-light-on: initial; - --dxc-dark-on: ; -} - -@media (prefers-color-scheme: dark) { - :root { - --dark: initial; - --light: ; - } -} - -@media (prefers-color-scheme: light) { - :root { - --dark: ; - --light: initial; - } -} - -:root { - /* Primary colors */ - --primary-color: var(--dark, #000) var(--light, #fff); - --primary-color-1: var(--dark, #0e0e0e) var(--light, #fbfbfb); - --primary-color-2: var(--dark, #0a0a0a) var(--light, #fff); - --primary-color-3: var(--dark, #141313) var(--light, #f8f8f8); - --primary-color-4: var(--dark, #1a1a1a) var(--light, #f8f8f8); - --primary-color-5: var(--dark, #262626) var(--light, #f5f5f5); - --primary-color-6: var(--dark, #232323) var(--light, #e5e5e5); - --primary-color-7: var(--dark, #3e3e3e) var(--light, #b0b0b0); - - /* Secondary colors */ - --secondary-color: var(--dark, #fff) var(--light, #000); - --secondary-color-1: var(--dark, #fafafa) var(--light, #000); - --secondary-color-2: var(--dark, #e6e6e6) var(--light, #0d0d0d); - --secondary-color-3: var(--dark, #dcdcdc) var(--light, #2b2b2b); - --secondary-color-4: var(--dark, #d4d4d4) var(--light, #111); - --secondary-color-5: var(--dark, #a1a1a1) var(--light, #848484); - --secondary-color-6: var(--dark, #5d5d5d) var(--light, #d0d0d0); - - /* Highlight colors */ - --focused-border-color: var(--dark, #2b7fff) var(--light, #2b7fff); - --primary-success-color: var(--dark, #02271c) var(--light, #ecfdf5); - --secondary-success-color: var(--dark, #b6fae3) var(--light, #10b981); - --primary-warning-color: var(--dark, #342203) var(--light, #fffbeb); - --secondary-warning-color: var(--dark, #feeac7) var(--light, #f59e0b); - --primary-error-color: var(--dark, #a22e2e) var(--light, #dc2626); - --secondary-error-color: var(--dark, #9b1c1c) var(--light, #ef4444); - --contrast-error-color: var(--dark, var(--secondary-color-3)) var(--light, var(--primary-color)); - --primary-info-color: var(--dark, var(--primary-color-5)) var(--light, var(--primary-color)); - --secondary-info-color: var(--dark, var(--primary-color-7)) var(--light, var(--secondary-color-3)); -} diff --git a/crates/rustmotion-studio/src/app/overlays.rs b/crates/rustmotion-studio/src/app/overlays.rs new file mode 100644 index 00000000..78427291 --- /dev/null +++ b/crates/rustmotion-studio/src/app/overlays.rs @@ -0,0 +1,9 @@ +use gpui_component::Root; +use gpui_kit::*; + +pub fn render(window: &mut Window, cx: &mut App) -> impl IntoElement { + div() + .children(Root::render_sheet_layer(window, cx)) + .children(Root::render_dialog_layer(window, cx)) + .children(Root::render_notification_layer(window, cx)) +} diff --git a/crates/rustmotion-studio/src/app/root.rs b/crates/rustmotion-studio/src/app/root.rs index 918704a8..c2f41c8f 100644 --- a/crates/rustmotion-studio/src/app/root.rs +++ b/crates/rustmotion-studio/src/app/root.rs @@ -1,194 +1,37 @@ -use std::sync::Arc; +use gpui_component::ActiveTheme; +use gpui_kit::base::v_flex; +use gpui_kit::*; -use dioxus::desktop::{ - use_asset_handler, wry::http::Response, AssetRequest, RequestAsyncResponder, -}; -use dioxus::prelude::*; +use crate::app::overlays; +use crate::app::state::StudioState; +use crate::scenario::View; -use crate::editor::StudioApp; -use crate::library::{render_thumbnail, Library, SharedLibrary}; -use crate::scenario::{Shared, Theme, View}; - -/// dx-component CSS, injected globally. `#[css_module]` only injects its compiled -/// stylesheet through the `dx` asset pipeline, which our plain `cargo`-built -/// `rustmotion studio` binary doesn't run — so we bundle the (unscoped) CSS at -/// compile time and inject it as a `