From 435dad71fb807244c3deaa56e4bacbde59f652de Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Mon, 30 Jun 2025 15:14:40 -0400 Subject: [PATCH] Start scaffolding ui --- crates/onboarding_ui/src/onboarding_ui.rs | 309 +++++++++++++++++++++- 1 file changed, 305 insertions(+), 4 deletions(-) diff --git a/crates/onboarding_ui/src/onboarding_ui.rs b/crates/onboarding_ui/src/onboarding_ui.rs index 6aae6d7093889fd565b75a17674073f77fb2a117..5f0b5b9345a79d2af996c3a783c5e4e7dade616c 100644 --- a/crates/onboarding_ui/src/onboarding_ui.rs +++ b/crates/onboarding_ui/src/onboarding_ui.rs @@ -1,17 +1,318 @@ +#![allow(unused, dead_code)] use command_palette_hooks::CommandPaletteFilter; use feature_flags::FeatureFlagAppExt as _; -use gpui::App; +use gpui::{Entity, EventEmitter, FocusHandle, Focusable, WeakEntity, actions, prelude::*}; use settings_ui::SettingsUiFeatureFlag; +use ui::prelude::*; use workspace::Workspace; -use gpui::actions; +actions!( + onboarding, + [ + ShowOnboarding, + JumpToBasics, + JumpToEditing, + JumpToAiSetup, + JumpToWelcome, + NextPage, + PreviousPage, + ToggleFocus, + ResetOnboarding, + ] +); -actions!(onboarding, [ShowOnboarding]); +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OnboardingPage { + Basics, + Editing, + AiSetup, + Welcome, +} + +impl OnboardingPage { + fn next(&self) -> Option { + match self { + Self::Basics => Some(Self::Editing), + Self::Editing => Some(Self::AiSetup), + Self::AiSetup => Some(Self::Welcome), + Self::Welcome => None, + } + } + + fn previous(&self) -> Option { + match self { + Self::Basics => None, + Self::Editing => Some(Self::Basics), + Self::AiSetup => Some(Self::Editing), + Self::Welcome => Some(Self::AiSetup), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OnboardingFocus { + Navigation, + Page, +} + +pub struct OnboardingUI { + focus_handle: FocusHandle, + current_page: OnboardingPage, + current_focus: OnboardingFocus, + completed_pages: [bool; 4], + basics_page: Entity, + editing_page: Entity, + ai_setup_page: Entity, + welcome_page: Entity, +} + +pub struct BasicsPage { + focus_handle: FocusHandle, + parent: WeakEntity, +} + +pub struct EditingPage { + focus_handle: FocusHandle, + parent: WeakEntity, +} + +pub struct AiSetupPage { + focus_handle: FocusHandle, + parent: WeakEntity, +} + +pub struct WelcomePage { + focus_handle: FocusHandle, + parent: WeakEntity, +} + +// Event types for communication between pages and main UI +#[derive(Clone)] +pub enum OnboardingEvent { + PageCompleted(OnboardingPage), +} + +// Implement EventEmitter for all entities +impl EventEmitter for OnboardingUI {} +impl EventEmitter for BasicsPage {} +impl EventEmitter for EditingPage {} +impl EventEmitter for AiSetupPage {} +impl EventEmitter for WelcomePage {} + +// Implement Focusable for all entities +impl Focusable for OnboardingUI { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl Focusable for BasicsPage { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl Focusable for EditingPage { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl Focusable for AiSetupPage { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +impl Focusable for WelcomePage { + fn focus_handle(&self, _cx: &App) -> FocusHandle { + self.focus_handle.clone() + } +} + +// Placeholder Render implementations +impl Render for OnboardingUI { + fn render( + &mut self, + _window: &mut gpui::Window, + _cx: &mut Context, + ) -> impl gpui::IntoElement { + h_flex() + .w(px(904.)) + .h(px(500.)) + .gap(px(48.)) + .child(v_flex().h_full().w(px(256.)).child("nav")) + } +} + +impl Render for BasicsPage { + fn render( + &mut self, + _window: &mut gpui::Window, + _cx: &mut Context, + ) -> impl gpui::IntoElement { + gpui::div() + } +} + +impl Render for EditingPage { + fn render( + &mut self, + _window: &mut gpui::Window, + _cx: &mut Context, + ) -> impl gpui::IntoElement { + gpui::div() + } +} + +impl Render for AiSetupPage { + fn render( + &mut self, + _window: &mut gpui::Window, + _cx: &mut Context, + ) -> impl gpui::IntoElement { + gpui::div() + } +} + +impl Render for WelcomePage { + fn render( + &mut self, + _window: &mut gpui::Window, + _cx: &mut Context, + ) -> impl gpui::IntoElement { + gpui::div() + } +} + +impl OnboardingUI { + pub fn new(cx: &mut Context) -> Self { + let parent_handle = cx.entity().downgrade(); + + let basics_page = cx.new(|cx| BasicsPage { + focus_handle: cx.focus_handle(), + parent: parent_handle.clone(), + }); + + let editing_page = cx.new(|cx| EditingPage { + focus_handle: cx.focus_handle(), + parent: parent_handle.clone(), + }); + + let ai_setup_page = cx.new(|cx| AiSetupPage { + focus_handle: cx.focus_handle(), + parent: parent_handle.clone(), + }); + + let welcome_page = cx.new(|cx| WelcomePage { + focus_handle: cx.focus_handle(), + parent: parent_handle.clone(), + }); + + Self { + focus_handle: cx.focus_handle(), + current_page: OnboardingPage::Basics, + current_focus: OnboardingFocus::Page, + completed_pages: [false; 4], + basics_page, + editing_page, + ai_setup_page, + welcome_page, + } + } + + fn jump_to_page( + &mut self, + page: OnboardingPage, + _window: &mut gpui::Window, + cx: &mut Context, + ) { + self.current_page = page; + cx.notify(); + } + + fn next_page(&mut self, _window: &mut gpui::Window, cx: &mut Context) { + if let Some(next) = self.current_page.next() { + self.current_page = next; + cx.notify(); + } + } + + fn previous_page(&mut self, _window: &mut gpui::Window, cx: &mut Context) { + if let Some(prev) = self.current_page.previous() { + self.current_page = prev; + cx.notify(); + } + } + + fn toggle_focus(&mut self, _window: &mut gpui::Window, cx: &mut Context) { + self.current_focus = match self.current_focus { + OnboardingFocus::Navigation => OnboardingFocus::Page, + OnboardingFocus::Page => OnboardingFocus::Navigation, + }; + cx.notify(); + } + + fn reset(&mut self, _window: &mut gpui::Window, cx: &mut Context) { + self.current_page = OnboardingPage::Basics; + self.current_focus = OnboardingFocus::Page; + self.completed_pages = [false; 4]; + cx.notify(); + } + + fn mark_page_completed( + &mut self, + page: OnboardingPage, + _window: &mut gpui::Window, + cx: &mut Context, + ) { + let index = match page { + OnboardingPage::Basics => 0, + OnboardingPage::Editing => 1, + OnboardingPage::AiSetup => 2, + OnboardingPage::Welcome => 3, + }; + self.completed_pages[index] = true; + cx.notify(); + } +} pub fn init(cx: &mut App) { cx.observe_new(|workspace: &mut Workspace, _, _cx| { workspace.register_action(|_workspace, _: &ShowOnboarding, _window, _cx| { - // Onboarding implementation will go here + // Show onboarding implementation will go here + }); + + workspace.register_action(|_workspace, _: &JumpToBasics, _window, _cx| { + // Jump to basics implementation + }); + + workspace.register_action(|_workspace, _: &JumpToEditing, _window, _cx| { + // Jump to editing implementation + }); + + workspace.register_action(|_workspace, _: &JumpToAiSetup, _window, _cx| { + // Jump to AI setup implementation + }); + + workspace.register_action(|_workspace, _: &JumpToWelcome, _window, _cx| { + // Jump to welcome implementation + }); + + workspace.register_action(|_workspace, _: &NextPage, _window, _cx| { + // Next page implementation + }); + + workspace.register_action(|_workspace, _: &PreviousPage, _window, _cx| { + // Previous page implementation + }); + + workspace.register_action(|_workspace, _: &ToggleFocus, _window, _cx| { + // Toggle focus implementation + }); + + workspace.register_action(|_workspace, _: &ResetOnboarding, _window, _cx| { + // Reset onboarding implementation }); }) .detach();