feat: dashboard skeleton

This commit is contained in:
2026-04-26 16:06:49 +01:00
parent 1cecfaf167
commit bef3a0b9ed
13 changed files with 166 additions and 138 deletions

View File

@@ -0,0 +1,43 @@
mod screen;
mod titlebar;
use gpui::{AppContext, BorrowAppContext, point, px, size};
pub(crate) use screen::new;
use crate::{app, screen::dashboard::screen::Screen};
pub fn open_window(screen: Screen, cx: &mut gpui::App) -> anyhow::Result<()> {
let (top_left, window_bounds) = cx.read_global::<app::Global, _>(|global, cx| {
(
global.safe_area.origin,
gpui::Bounds::centered(None, size(px(800.), px(600.0)), cx),
)
});
cx.open_window(
gpui::WindowOptions {
window_bounds: Some(gpui::WindowBounds::Windowed(window_bounds)),
titlebar: Some(gpui::TitlebarOptions {
appears_transparent: true,
traffic_light_position: Some(top_left + point(px(12.), px(12.))),
..Default::default()
}),
..Default::default()
},
|window, cx| {
cx.new(|cx| {
cx.observe_window_appearance(window, |_, window, cx| {
cx.update_global::<app::Global, ()>(|global, cx| {
global.current_theme = global
.theme_family
.theme_for_appearance(window.appearance());
cx.notify();
});
})
.detach();
screen
})
},
)
.map(|_| ())
}

View File

@@ -0,0 +1,41 @@
use gpui::{AppContext, ParentElement, Styled, div};
use crate::{app, screen::dashboard::titlebar};
pub(crate) struct Screen {
titlebar: gpui::Entity<titlebar::TitleBar>,
}
pub(crate) fn new(cx: &mut gpui::App) -> Screen {
Screen {
titlebar: cx.new(|cx| titlebar::new(cx)),
}
}
impl gpui::Render for Screen {
fn render(
&mut self,
_window: &mut gpui::Window,
cx: &mut gpui::Context<Self>,
) -> impl gpui::IntoElement {
let theme = app::current_theme(cx);
div()
.flex()
.flex_col()
.bg(theme.colors.background)
.size_full()
.child(self.titlebar.clone())
.child(
div()
.flex()
.flex_row()
.flex_1()
.w_full()
.gap_2()
.px_3()
.pb_3()
.child(div().w_1_4().h_full().rounded_lg().bg(theme.colors.surface))
.child(div().w_3_4().h_full().rounded_lg().bg(theme.colors.surface)),
)
}
}

View File

@@ -0,0 +1,80 @@
use gpui::{ParentElement, Styled, TitlebarOptions, div};
use crate::component::button::button;
use crate::query::{self, QueryStatus, read_query, use_query};
use crate::{
api, app,
component::{
font_icon::{FontIcon, font_icon},
text::text,
},
};
pub struct TitleBar {
fetch_user_query: query::Entity<api::user::Fetch>,
}
pub struct RepoSelector {}
pub fn new(cx: &mut gpui::Context<TitleBar>) -> TitleBar {
TitleBar {
fetch_user_query: use_query(api::user::Fetch, cx),
}
}
impl gpui::Render for TitleBar {
fn render(
&mut self,
_window: &mut gpui::Window,
cx: &mut gpui::Context<Self>,
) -> impl gpui::IntoElement {
let g = cx.global::<app::Global>();
let user = read_query(&self.fetch_user_query, cx);
let user_avatar = match user {
QueryStatus::Err(api::Error::Unauthenticated) => div().absolute().right_2p5().child(
button("login-btn")
.leading(font_icon(FontIcon::Github))
.label("Login"),
),
_ => div(),
};
div()
.flex_row()
.justify_center()
.items_center()
.w_full()
.h_10()
.flex()
.px(g.safe_area.size.width)
.py_2()
.bg(g.current_theme.colors.background)
.text_color(g.current_theme.colors.text)
.relative()
.child(repo_selector(cx))
.child(user_avatar)
}
}
impl RepoSelector {
pub fn new(cx: &mut gpui::Context<Self>) -> Self {
use_query(api::repo::List, cx);
use_query(api::user::Fetch, cx);
Self {}
}
}
fn repo_selector<T: 'static>(_cx: &gpui::Context<T>) -> gpui::Div {
div()
.flex()
.flex_row()
.items_center()
.gap_1()
.text_xs()
.child(font_icon(FontIcon::FolderGit).size_3())
.child(text("test/repo"))
.child(font_icon(FontIcon::ChevronDown).size_3())
}

View File

@@ -1 +1,2 @@
pub(crate) mod dashboard;
pub(crate) mod setup_wizard;

View File

@@ -2,8 +2,8 @@ use std::time::Duration;
use futures_lite::StreamExt;
use gpui::{
BorrowAppContext, InteractiveElement, ParentElement, Styled, Timer, div, img,
prelude::FluentBuilder,
BorrowAppContext, InteractiveElement, ParentElement, StatefulInteractiveElement, Styled, Timer,
div, img, prelude::FluentBuilder,
};
use rand::RngExt;
@@ -62,28 +62,28 @@ impl GithubStepView {
fn on_create(&mut self, cx: &mut gpui::Context<Self>) {
cx.observe(&self.create_device_code_query, |this, _, cx| {
let code = {
let codes = {
let data = read_query(&this.create_device_code_query, cx);
if let QueryStatus::Loaded(data) = data {
Some(data.device_code.clone())
Some((data.device_code.clone(), data.user_code.clone()))
} else {
None
}
};
if let Some(ref code) = code
if let Some((ref device_code, ref user_code)) = codes
&& !this.has_opened_link
&& !this.is_opening_link
{
this.is_opening_link = true;
this.copy_device_code(code, cx);
this.copy_user_code(user_code, cx);
let code = code.clone();
let device_code = device_code.clone();
set_timeout(
move |weak, cx| {
_ = weak.update(cx, |this, cx| {
this.has_opened_link = true;
this.is_opening_link = false;
this.begin_auth_flow(&code, cx);
this.begin_auth_flow(&device_code, cx);
cx.notify();
});
},
@@ -137,7 +137,7 @@ impl GithubStepView {
.collect()
}
fn copy_device_code(&mut self, code: &str, cx: &mut gpui::Context<Self>) {
fn copy_user_code(&mut self, code: &str, cx: &mut gpui::Context<Self>) {
cx.write_to_clipboard(gpui::ClipboardItem::new_string(code.to_owned()));
self.has_copied_code = true;
@@ -262,9 +262,9 @@ impl GithubStepView {
let theme = app::current_theme(cx);
let displayed_code = match create_device_code_query {
QueryStatus::Loaded(data) => &data.user_code,
_ => &self.placeholder_code,
let (displayed_code, copyable_code) = match create_device_code_query {
QueryStatus::Loaded(data) => (data.user_code.as_str(), Some(data.user_code.clone())),
_ => (self.placeholder_code.as_str(), None),
};
let border_color = theme.colors.border.clone();
@@ -304,6 +304,12 @@ impl GithubStepView {
.items_center()
.justify_center()
.gap_1p5()
.when_some(copyable_code, |it, code| {
it.cursor_pointer()
.on_click(cx.listener(move |this, _, _, cx| {
this.copy_user_code(&code, cx);
}))
})
.children(letter_boxes),
)
.child(

View File

@@ -1,4 +1,4 @@
use gpui::{AppContext, IntoElement, ParentElement, Styled, div, prelude::FluentBuilder};
use gpui::{AppContext, IntoElement, ParentElement, Styled, div, prelude::FluentBuilder, rems};
use crate::{
api, app,
@@ -120,7 +120,8 @@ impl Screen {
.flex_col()
.items_start()
.w_full()
.px_8()
.pl_6()
.pr_8()
.justify_center()
.gap_3()
.text_sm()
@@ -165,9 +166,17 @@ impl gpui::Render for Screen {
.bg(theme.colors.surface)
.relative()
.child(
text("Novem")
.bold()
.styled(|it| it.absolute().top_20().left_8()),
div()
.flex()
.flex_row()
.justify_center()
.items_center()
.gap_2p5()
.absolute()
.top_20()
.left_6()
.child(font_icon(FontIcon::Cat).size_4())
.child(text("Novem").bold()),
)
.child(self.step_list(cx)),
)