initial commit
This commit is contained in:
1
src/api.rs
Normal file
1
src/api.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub(crate) mod repo;
|
||||
17
src/api/repo.rs
Normal file
17
src/api/repo.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use crate::query;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct List;
|
||||
|
||||
impl query::QueryFn for List {
|
||||
type Data = ();
|
||||
type Error = ();
|
||||
|
||||
fn key(&self) -> &'static str {
|
||||
"repo.list"
|
||||
}
|
||||
|
||||
async fn run(&self) -> Result<Self::Data, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
58
src/app.rs
Normal file
58
src/app.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use gpui::{div, prelude::*};
|
||||
|
||||
use crate::app;
|
||||
use crate::query;
|
||||
use crate::dashboard;
|
||||
use crate::theme;
|
||||
use crate::titlebar;
|
||||
|
||||
pub struct Global {
|
||||
pub safe_area: gpui::Bounds<gpui::Pixels>,
|
||||
pub current_theme: theme::Theme,
|
||||
pub query_store: query::Store
|
||||
}
|
||||
|
||||
pub struct Chrome {}
|
||||
|
||||
impl Chrome {
|
||||
pub fn new(window: &mut gpui::Window, cx: &mut gpui::Context<Self>) -> Self {
|
||||
cx.observe_window_appearance(window, |_, window, cx| {
|
||||
cx.update_global::<app::Global, ()>(|global, _cx| {
|
||||
global.current_theme = window.appearance().into();
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl gpui::Render for Chrome {
|
||||
fn render(
|
||||
&mut self,
|
||||
_window: &mut gpui::Window,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) -> impl gpui::IntoElement {
|
||||
let title_bar = cx.new(|_| titlebar::TitleBar {});
|
||||
|
||||
let dashboard = cx.new(|_| dashboard::Screen {
|
||||
text: "World".into(),
|
||||
});
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.flex_col()
|
||||
.child(title_bar)
|
||||
.child(dashboard)
|
||||
}
|
||||
}
|
||||
|
||||
impl gpui::Global for Global {}
|
||||
|
||||
pub fn current_theme<'a, E>(cx: &'a gpui::Context<E>) -> &'a theme::Theme {
|
||||
&cx.global::<Global>().current_theme
|
||||
}
|
||||
|
||||
pub fn query_store<'a, E>(cx: &'a gpui::Context<E>) -> &'a query::Store {
|
||||
&cx.global::<Global>().query_store
|
||||
}
|
||||
13
src/asset.rs
Normal file
13
src/asset.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
include!(concat!(env!("OUT_DIR"), "/asset.rs"));
|
||||
|
||||
pub struct Asset;
|
||||
|
||||
impl gpui::AssetSource for Asset {
|
||||
fn load(&self, path: &str) -> gpui::Result<Option<std::borrow::Cow<'static, [u8]>>> {
|
||||
load_asset(path)
|
||||
}
|
||||
|
||||
fn list(&self, path: &str) -> gpui::Result<Vec<gpui::SharedString>> {
|
||||
list_assets(path)
|
||||
}
|
||||
}
|
||||
1
src/asset/font_icon/chevron_down.svg
Normal file
1
src/asset/font_icon/chevron_down.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down-icon lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
|
||||
|
After Width: | Height: | Size: 272 B |
1
src/asset/font_icon/folder_git.svg
Normal file
1
src/asset/font_icon/folder_git.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-folder-git2-icon lucide-folder-git-2"><path d="M18 19a5 5 0 0 1-5-5v8"/><path d="M9 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v5"/><circle cx="13" cy="12" r="2"/><circle cx="20" cy="19" r="2"/></svg>
|
||||
|
After Width: | Height: | Size: 457 B |
80
src/colors.rs
Normal file
80
src/colors.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use gpui::Rgba;
|
||||
|
||||
pub const fn hex(hex: u32) -> Rgba {
|
||||
let [_, r, g, b] = hex.to_be_bytes();
|
||||
|
||||
Rgba {
|
||||
r: r as f32 / 255.0,
|
||||
g: g as f32 / 255.0,
|
||||
b: b as f32 / 255.0,
|
||||
a: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn neutral(shade: u16) -> Rgba {
|
||||
match shade {
|
||||
50 => hex(0xfafafa),
|
||||
100 => hex(0xf5f5f5),
|
||||
200 => hex(0xe5e5e5),
|
||||
300 => hex(0xd4d4d4),
|
||||
400 => hex(0xa3a3a3),
|
||||
500 => hex(0x737373),
|
||||
600 => hex(0x525252),
|
||||
700 => hex(0x404040),
|
||||
800 => hex(0x262626),
|
||||
900 => hex(0x171717),
|
||||
950 => hex(0x0a0a0a),
|
||||
_ => panic!("unsupported Tailwind neutral shade"),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn violet(shade: u16) -> Rgba {
|
||||
match shade {
|
||||
50 => hex(0xf5f3ff),
|
||||
100 => hex(0xede9fe),
|
||||
200 => hex(0xddd6fe),
|
||||
300 => hex(0xc4b5fd),
|
||||
400 => hex(0xa78bfa),
|
||||
500 => hex(0x8b5cf6),
|
||||
600 => hex(0x7c3aed),
|
||||
700 => hex(0x6d28d9),
|
||||
800 => hex(0x5b21b6),
|
||||
900 => hex(0x4c1d95),
|
||||
950 => hex(0x2e1065),
|
||||
_ => panic!("unsupported Tailwind violet shade"),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn amber(shade: u16) -> Rgba {
|
||||
match shade {
|
||||
50 => hex(0xfffbeb),
|
||||
100 => hex(0xfef3c7),
|
||||
200 => hex(0xfde68a),
|
||||
300 => hex(0xfcd34d),
|
||||
400 => hex(0xfbbf24),
|
||||
500 => hex(0xf59e0b),
|
||||
600 => hex(0xd97706),
|
||||
700 => hex(0xb45309),
|
||||
800 => hex(0x92400e),
|
||||
900 => hex(0x78350f),
|
||||
950 => hex(0x451a03),
|
||||
_ => panic!("unsupported Tailwind amber shade"),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn red(shade: u16) -> Rgba {
|
||||
match shade {
|
||||
50 => hex(0xfef2f2),
|
||||
100 => hex(0xfee2e2),
|
||||
200 => hex(0xfecaca),
|
||||
300 => hex(0xfca5a5),
|
||||
400 => hex(0xf87171),
|
||||
500 => hex(0xef4444),
|
||||
600 => hex(0xdc2626),
|
||||
700 => hex(0xb91c1c),
|
||||
800 => hex(0x991b1b),
|
||||
900 => hex(0x7f1d1d),
|
||||
950 => hex(0x450a0a),
|
||||
_ => panic!("unsupported Tailwind red shade"),
|
||||
}
|
||||
}
|
||||
2
src/component.rs
Normal file
2
src/component.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod font_icon;
|
||||
pub mod text;
|
||||
34
src/component/font_icon.rs
Normal file
34
src/component/font_icon.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use gpui::{Styled, svg};
|
||||
use paste::paste;
|
||||
|
||||
use crate::app;
|
||||
|
||||
macro_rules! define_font_icons {
|
||||
($($name:ident),+ $(,)?) => {
|
||||
paste! {
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum FontIcon {
|
||||
$($name),+
|
||||
}
|
||||
|
||||
pub const fn icon_path(icon: FontIcon) -> &'static str {
|
||||
match icon {
|
||||
$(
|
||||
FontIcon::$name => concat!(
|
||||
"asset/font_icon/",
|
||||
stringify!([<$name:snake>]),
|
||||
".svg"
|
||||
),
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_font_icons!(ChevronDown, FolderGit);
|
||||
|
||||
pub fn font_icon<T>(icon: FontIcon, cx: &gpui::Context<T>) -> gpui::Svg {
|
||||
let theme = cx.global::<app::Global>().current_theme;
|
||||
svg().path(icon_path(icon)).text_color(theme.colors.text)
|
||||
}
|
||||
13
src/component/text.rs
Normal file
13
src/component/text.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use crate::app;
|
||||
use gpui::{ParentElement, Styled, div};
|
||||
|
||||
pub trait Text: gpui::IntoElement {}
|
||||
|
||||
impl Text for &'static str {}
|
||||
impl Text for String {}
|
||||
impl Text for gpui::SharedString {}
|
||||
|
||||
pub fn text<'a, Content: Text, T>(s: Content, cx: &gpui::Context<T>) -> gpui::Div {
|
||||
let theme = cx.global::<app::Global>().current_theme;
|
||||
div().text_color(theme.colors.text).child(s)
|
||||
}
|
||||
48
src/dashboard.rs
Normal file
48
src/dashboard.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use gpui::{div, prelude::*};
|
||||
|
||||
use crate::theme::Variant;
|
||||
|
||||
pub struct Screen {
|
||||
pub text: gpui::SharedString,
|
||||
}
|
||||
|
||||
impl Render for Screen {
|
||||
fn render(
|
||||
&mut self,
|
||||
_window: &mut gpui::Window,
|
||||
_cx: &mut gpui::Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let builtin = Variant::VioletDark;
|
||||
let theme = builtin.theme();
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.size_full()
|
||||
.gap_3()
|
||||
.bg(theme.colors.background)
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.shadow_lg()
|
||||
.text_xl()
|
||||
.text_color(theme.colors.text)
|
||||
.child(format!("Hello, {}!", &self.text))
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(theme.colors.text_muted)
|
||||
.child(format!("Built-in theme: {}", builtin.label())),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
.child(div().size_8().bg(theme.colors.surface))
|
||||
.child(div().size_8().bg(theme.colors.surface_elevated))
|
||||
.child(div().size_8().bg(theme.colors.accent))
|
||||
.child(div().size_8().bg(theme.colors.success))
|
||||
.child(div().size_8().bg(theme.colors.warning))
|
||||
.child(div().size_8().bg(theme.colors.danger)),
|
||||
)
|
||||
}
|
||||
}
|
||||
45
src/main.rs
Normal file
45
src/main.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use gpui::{bounds, point, prelude::*, px, size};
|
||||
|
||||
mod app;
|
||||
mod asset;
|
||||
mod colors;
|
||||
mod component;
|
||||
mod dashboard;
|
||||
mod query;
|
||||
mod theme;
|
||||
mod titlebar;
|
||||
mod api;
|
||||
|
||||
fn main() {
|
||||
gpui::Application::new()
|
||||
.with_assets(asset::Asset)
|
||||
.run(setup_application);
|
||||
}
|
||||
|
||||
fn setup_application(cx: &mut gpui::App) {
|
||||
let window_bounds = gpui::Bounds::centered(None, size(px(800.), px(600.0)), cx);
|
||||
|
||||
let global = app::Global {
|
||||
safe_area: bounds(point(px(0.), px(0.)), size(px(72.), px(12.))),
|
||||
current_theme: cx.window_appearance().into(),
|
||||
query_store: query::Store::new(),
|
||||
};
|
||||
|
||||
let top_left = global.safe_area.origin;
|
||||
|
||||
cx.set_global(global);
|
||||
|
||||
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(8.), px(8.))),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
|window, cx| cx.new(|cx| app::Chrome::new(window, cx)),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
123
src/query.rs
Normal file
123
src/query.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use crate::app;
|
||||
use gpui::{AppContext, BorrowAppContext};
|
||||
use std::any::Any;
|
||||
|
||||
pub trait QueryFn: Clone + 'static {
|
||||
type Data: 'static;
|
||||
type Error: 'static;
|
||||
|
||||
fn key(&self) -> &'static str;
|
||||
|
||||
async fn run(&self) -> Result<Self::Data, Self::Error>;
|
||||
}
|
||||
|
||||
pub enum QueryStatus<'a, Data, Error> {
|
||||
Loading,
|
||||
Loaded(&'a Data),
|
||||
Err(&'a Error),
|
||||
}
|
||||
|
||||
pub fn use_query<F, T>(query_fn: F, cx: &mut gpui::Context<T>)
|
||||
where
|
||||
F: QueryFn,
|
||||
T: 'static,
|
||||
{
|
||||
let ent = cx.update_global::<app::Global, _>(|global, cx| {
|
||||
let entity = global.query_store.ensure_query(&query_fn, cx);
|
||||
|
||||
if entity.read_with(cx, |state, _| matches!(state.data, QueryData::Pending)) {
|
||||
global.query_store.execute_query(query_fn, entity.clone(), cx).detach();
|
||||
}
|
||||
|
||||
entity
|
||||
});
|
||||
|
||||
cx.observe(&ent, |_, _, cx| {
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
// ================= Store ==================
|
||||
|
||||
pub(crate) struct QueryState {
|
||||
data: QueryData,
|
||||
}
|
||||
|
||||
pub(crate) enum QueryData {
|
||||
Pending,
|
||||
Loading,
|
||||
Some(Box<dyn Any>),
|
||||
Err(Box<dyn Any>),
|
||||
}
|
||||
|
||||
pub(crate) type Entity = gpui::Entity<QueryState>;
|
||||
|
||||
pub struct Store {
|
||||
query_data: std::collections::HashMap<String, Entity>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
query_data: std::collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_query<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>) -> Entity
|
||||
where
|
||||
Q: QueryFn,
|
||||
T: 'static,
|
||||
{
|
||||
self.query_data
|
||||
.entry(query.key().into())
|
||||
.or_insert_with(|| {
|
||||
cx.new(|_| QueryState {
|
||||
data: QueryData::Pending,
|
||||
})
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn execute_query<Q, T>(
|
||||
&mut self,
|
||||
query: Q,
|
||||
entity: Entity,
|
||||
cx: &mut gpui::Context<T>,
|
||||
) -> gpui::Task<anyhow::Result<()>>
|
||||
where
|
||||
Q: QueryFn,
|
||||
T: 'static,
|
||||
{
|
||||
entity.update(cx, |state, cx| {
|
||||
state.data = QueryData::Loading;
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
cx.spawn(async move |_, cx| {
|
||||
let result = query.run().await;
|
||||
|
||||
entity.update(cx, |state, cx| {
|
||||
state.data = match result {
|
||||
Ok(data) => QueryData::Some(Box::new(data)),
|
||||
Err(err) => QueryData::Err(Box::new(err)),
|
||||
};
|
||||
cx.notify();
|
||||
})?;
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn read_query<'a, Q, E>(&self, query_fn: Q, cx: &'a gpui::Context<E>) -> QueryStatus<'a, Q::Data, Q::Error> where Q: QueryFn {
|
||||
let Some(ent) = self.query_data.get(query_fn.key()) else {
|
||||
return QueryStatus::Loading;
|
||||
};
|
||||
let QueryState { data } = ent.read(cx);
|
||||
match data {
|
||||
QueryData::Loading | QueryData::Pending => QueryStatus::Loading,
|
||||
QueryData::Some(data) => QueryStatus::Loaded(data.downcast_ref::<Q::Data>().unwrap()),
|
||||
QueryData::Err(error) => QueryStatus::Err(error.downcast_ref::<Q::Error>().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/theme.rs
Normal file
117
src/theme.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use gpui::Rgba;
|
||||
|
||||
use crate::colors::{amber, hex, neutral, red, violet};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum ThemeMode {
|
||||
Light,
|
||||
#[default]
|
||||
Dark,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Theme {
|
||||
pub id: &'static str,
|
||||
pub name: &'static str,
|
||||
pub mode: ThemeMode,
|
||||
pub colors: ThemeColors,
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
fn default() -> Self {
|
||||
Variant::default().theme()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ThemeColors {
|
||||
pub background: Rgba,
|
||||
pub surface: Rgba,
|
||||
pub surface_elevated: Rgba,
|
||||
pub border: Rgba,
|
||||
pub text: Rgba,
|
||||
pub text_muted: Rgba,
|
||||
pub accent: Rgba,
|
||||
pub accent_hover: Rgba,
|
||||
pub accent_text: Rgba,
|
||||
pub success: Rgba,
|
||||
pub warning: Rgba,
|
||||
pub danger: Rgba,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Variant {
|
||||
VioletLight,
|
||||
#[default]
|
||||
VioletDark,
|
||||
}
|
||||
|
||||
impl Variant {
|
||||
#[allow(dead_code)]
|
||||
pub const ALL: [Self; 2] = [Self::VioletLight, Self::VioletDark];
|
||||
|
||||
pub const fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::VioletLight => "Violet Light",
|
||||
Self::VioletDark => "Violet Dark",
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn theme(self) -> Theme {
|
||||
match self {
|
||||
Self::VioletLight => Theme {
|
||||
id: "violet-light",
|
||||
name: "Violet Light",
|
||||
mode: ThemeMode::Light,
|
||||
colors: ThemeColors {
|
||||
background: neutral(50),
|
||||
surface: neutral(100),
|
||||
surface_elevated: neutral(200),
|
||||
border: neutral(200),
|
||||
text: neutral(900),
|
||||
text_muted: neutral(600),
|
||||
accent: violet(600),
|
||||
accent_hover: violet(500),
|
||||
accent_text: neutral(100),
|
||||
success: hex(0x16a34a),
|
||||
warning: amber(600),
|
||||
danger: red(600),
|
||||
},
|
||||
},
|
||||
Self::VioletDark => Theme {
|
||||
id: "violet-dark",
|
||||
name: "Violet Dark",
|
||||
mode: ThemeMode::Dark,
|
||||
colors: ThemeColors {
|
||||
background: neutral(950),
|
||||
surface: neutral(900),
|
||||
surface_elevated: neutral(800),
|
||||
border: neutral(800),
|
||||
text: neutral(50),
|
||||
text_muted: neutral(400),
|
||||
accent: violet(400),
|
||||
accent_hover: violet(300),
|
||||
accent_text: neutral(100),
|
||||
success: hex(0x22c55e),
|
||||
warning: amber(500),
|
||||
danger: red(500),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gpui::WindowAppearance> for Theme {
|
||||
fn from(value: gpui::WindowAppearance) -> Self {
|
||||
let variant = match value {
|
||||
gpui::WindowAppearance::Light | gpui::WindowAppearance::VibrantLight => {
|
||||
Variant::VioletLight
|
||||
}
|
||||
gpui::WindowAppearance::Dark | gpui::WindowAppearance::VibrantDark => {
|
||||
Variant::VioletDark
|
||||
}
|
||||
};
|
||||
variant.theme()
|
||||
}
|
||||
}
|
||||
59
src/titlebar.rs
Normal file
59
src/titlebar.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use gpui::{ParentElement, Styled, div, AppContext};
|
||||
|
||||
use crate::{api, app, component::{
|
||||
font_icon::{FontIcon, font_icon},
|
||||
text::text,
|
||||
}, query};
|
||||
use crate::app::query_store;
|
||||
use crate::query::use_query;
|
||||
|
||||
pub struct TitleBar {}
|
||||
|
||||
pub struct RepoSelector {}
|
||||
|
||||
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>();
|
||||
|
||||
div()
|
||||
.w_full()
|
||||
.h_8()
|
||||
.flex()
|
||||
.px(g.safe_area.size.width)
|
||||
.py_2()
|
||||
.flex_row()
|
||||
.justify_center()
|
||||
.items_center()
|
||||
.border_b_1()
|
||||
.border_color(g.current_theme.colors.border)
|
||||
.bg(g.current_theme.colors.surface)
|
||||
.text_color(g.current_theme.colors.text)
|
||||
.child(repo_selector(cx))
|
||||
}
|
||||
}
|
||||
|
||||
impl RepoSelector {
|
||||
pub fn new(cx: &mut gpui::Context<Self>) -> Self {
|
||||
use_query(api::repo::List, cx);
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
fn repo_selector<T>(cx: &gpui::Context<T>) -> gpui::Div {
|
||||
let store = app::query_store(cx);
|
||||
let repo = store.read_query(api::repo::List, cx);
|
||||
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.text_xs()
|
||||
.child(font_icon(FontIcon::FolderGit, cx).size_3())
|
||||
.child(text("test/repo", cx))
|
||||
.child(font_icon(FontIcon::ChevronDown, cx).size_3())
|
||||
}
|
||||
Reference in New Issue
Block a user