commit b8e2a16e3b74c9b6ba33bf36a074c7856eae9681
parent 627c3ba9b386ad7fcb50d8bd1cf1ea8f04593f96
Author: William Casarin <jb55@jb55.com>
Date: Wed, 26 Mar 2025 16:30:37 -0700
dave: give dave a new home in the sidebar
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
5 files changed, 118 insertions(+), 54 deletions(-)
diff --git a/crates/notedeck_chrome/src/app.rs b/crates/notedeck_chrome/src/app.rs
@@ -0,0 +1,19 @@
+use notedeck::AppContext;
+use notedeck_columns::Damus;
+use notedeck_dave::Dave;
+
+pub enum NotedeckApp {
+ Dave(Dave),
+ Columns(Damus),
+ Other(Box<dyn notedeck::App>),
+}
+
+impl notedeck::App for NotedeckApp {
+ fn update(&mut self, ctx: &mut AppContext, ui: &mut egui::Ui) {
+ match self {
+ NotedeckApp::Dave(dave) => dave.update(ctx, ui),
+ NotedeckApp::Columns(columns) => columns.update(ctx, ui),
+ NotedeckApp::Other(other) => other.update(ctx, ui),
+ }
+ }
+}
diff --git a/crates/notedeck_chrome/src/chrome.rs b/crates/notedeck_chrome/src/chrome.rs
@@ -1,10 +1,12 @@
// Entry point for wasm
//#[cfg(target_arch = "wasm32")]
//use wasm_bindgen::prelude::*;
-use egui::{Button, Label, Layout, RichText, ThemePreference, Widget};
+use crate::app::NotedeckApp;
+use egui::{vec2, Button, Label, Layout, RichText, ThemePreference, Widget};
use egui_extras::{Size, StripBuilder};
use nostrdb::{ProfileRecord, Transaction};
-use notedeck::{AppContext, NotedeckTextStyle, UserAccount};
+use notedeck::{App, AppContext, NotedeckTextStyle, UserAccount};
+use notedeck_dave::{Dave, DaveAvatar};
use notedeck_ui::{profile::get_profile_url, AnimationHelper, ProfilePic};
static ICON_WIDTH: f32 = 40.0;
@@ -13,7 +15,7 @@ pub static ICON_EXPANSION_MULTIPLE: f32 = 1.2;
#[derive(Default)]
pub struct Chrome {
active: i32,
- apps: Vec<Box<dyn notedeck::App>>,
+ apps: Vec<NotedeckApp>,
}
pub enum ChromePanelAction {
@@ -28,8 +30,18 @@ impl Chrome {
Chrome::default()
}
- pub fn add_app(&mut self, app: impl notedeck::App + 'static) {
- self.apps.push(Box::new(app));
+ pub fn add_app(&mut self, app: NotedeckApp) {
+ self.apps.push(app);
+ }
+
+ fn get_dave(&mut self) -> Option<&mut Dave> {
+ for app in &mut self.apps {
+ if let NotedeckApp::Dave(dave) = app {
+ return Some(dave);
+ }
+ }
+
+ None
}
pub fn set_active(&mut self, app: i32) {
@@ -44,7 +56,7 @@ impl Chrome {
fn show(&mut self, ctx: &mut AppContext, ui: &mut egui::Ui) {
ui.spacing_mut().item_spacing.x = 0.0;
- let side_panel_width: f32 = 68.0;
+ let side_panel_width: f32 = 70.0;
StripBuilder::new(ui)
.size(Size::exact(side_panel_width)) // collapsible sidebar
.size(Size::remainder()) // the main app contents
@@ -103,9 +115,10 @@ impl Chrome {
ctx: &mut AppContext,
ui: &mut egui::Ui,
) -> Option<ChromePanelAction> {
- let dark_mode = ui.ctx().style().visuals.dark_mode;
+ ui.add_space(8.0);
+
let pfp_resp = self.pfp_button(ctx, ui);
- let settings_resp = ui.add(settings_button(dark_mode));
+ let settings_resp = settings_button(ui);
let theme_action = match ui.ctx().theme() {
egui::Theme::Dark => {
@@ -130,7 +143,7 @@ impl Chrome {
}
};
- if ui.add(support_button()).clicked() {
+ if support_button(ui).clicked() {
return Some(ChromePanelAction::Support);
}
@@ -179,7 +192,15 @@ impl Chrome {
ui.add(milestone_name());
ui.add_space(16.0);
//let dark_mode = ui.ctx().style().visuals.dark_mode;
- //ui.add(add_column_button(dark_mode))
+ if columns_button(ui).clicked() {
+ self.active = 0;
+ }
+ ui.add_space(32.0);
+ if let Some(dave) = self.get_dave() {
+ if dave_button(dave.avatar_mut(), ui).clicked() {
+ self.active = 1;
+ }
+ }
}
}
@@ -222,54 +243,69 @@ fn expand_side_panel_button() -> impl Widget {
}
}
-fn support_button() -> impl Widget {
- |ui: &mut egui::Ui| -> egui::Response {
- let img_size = 16.0;
-
- let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
- let img_data = if ui.visuals().dark_mode {
- egui::include_image!("../../../assets/icons/help_icon_dark_4x.png")
- } else {
- egui::include_image!("../../../assets/icons/help_icon_inverted_4x.png")
- };
- let img = egui::Image::new(img_data).max_width(img_size);
+fn expanding_button(
+ name: &'static str,
+ img_size: f32,
+ light_img: &egui::ImageSource,
+ dark_img: &egui::ImageSource,
+ ui: &mut egui::Ui,
+) -> egui::Response {
+ let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
+ let img_data = if ui.visuals().dark_mode {
+ dark_img
+ } else {
+ light_img
+ };
+ let img = egui::Image::new(img_data.clone()).max_width(img_size);
- let helper = AnimationHelper::new(ui, "help-button", egui::vec2(max_size, max_size));
+ let helper = AnimationHelper::new(ui, name, egui::vec2(max_size, max_size));
- let cur_img_size = helper.scale_1d_pos(img_size);
- img.paint_at(
- ui,
- helper
- .get_animation_rect()
- .shrink((max_size - cur_img_size) / 2.0),
- );
+ let cur_img_size = helper.scale_1d_pos(img_size);
+ img.paint_at(
+ ui,
+ helper
+ .get_animation_rect()
+ .shrink((max_size - cur_img_size) / 2.0),
+ );
- helper.take_animation_response()
- }
+ helper.take_animation_response()
}
-fn settings_button(dark_mode: bool) -> impl Widget {
- move |ui: &mut egui::Ui| {
- let img_size = 24.0;
- let max_size = ICON_WIDTH * ICON_EXPANSION_MULTIPLE; // max size of the widget
- let img_data = if dark_mode {
- egui::include_image!("../../../assets/icons/settings_dark_4x.png")
- } else {
- egui::include_image!("../../../assets/icons/settings_light_4x.png")
- };
- let img = egui::Image::new(img_data).max_width(img_size);
+fn support_button(ui: &mut egui::Ui) -> egui::Response {
+ expanding_button(
+ "help-button",
+ 16.0,
+ &egui::include_image!("../../../assets/icons/help_icon_inverted_4x.png"),
+ &egui::include_image!("../../../assets/icons/help_icon_dark_4x.png"),
+ ui,
+ )
+}
- let helper = AnimationHelper::new(ui, "settings-button", egui::vec2(max_size, max_size));
+fn settings_button(ui: &mut egui::Ui) -> egui::Response {
+ expanding_button(
+ "settings-button",
+ 32.0,
+ &egui::include_image!("../../../assets/icons/settings_light_4x.png"),
+ &egui::include_image!("../../../assets/icons/settings_dark_4x.png"),
+ ui,
+ )
+}
- let cur_img_size = helper.scale_1d_pos(img_size);
- img.paint_at(
- ui,
- helper
- .get_animation_rect()
- .shrink((max_size - cur_img_size) / 2.0),
- );
+fn columns_button(ui: &mut egui::Ui) -> egui::Response {
+ let btn = egui::include_image!("../../../assets/icons/columns_80.png");
+ expanding_button("columns-button", 40.0, &btn, &btn, ui)
+}
- helper.take_animation_response()
+fn dave_button(avatar: Option<&mut DaveAvatar>, ui: &mut egui::Ui) -> egui::Response {
+ if let Some(avatar) = avatar {
+ let size = vec2(60.0, 60.0);
+ let available = ui.available_rect_before_wrap();
+ let center_x = available.center().x;
+ let rect = egui::Rect::from_center_size(egui::pos2(center_x, available.top()), size);
+ avatar.render(rect, ui)
+ } else {
+ // plain icon if wgpu device not available??
+ ui.label("fixme")
}
}
diff --git a/crates/notedeck_chrome/src/lib.rs b/crates/notedeck_chrome/src/lib.rs
@@ -5,6 +5,8 @@ pub mod theme;
#[cfg(target_os = "android")]
mod android;
+mod app;
mod chrome;
+pub use app::NotedeckApp;
pub use chrome::Chrome;
diff --git a/crates/notedeck_chrome/src/notedeck.rs b/crates/notedeck_chrome/src/notedeck.rs
@@ -4,7 +4,7 @@
use notedeck::{DataPath, DataPathType, Notedeck};
use notedeck_chrome::{
setup::{generate_native_options, setup_chrome},
- Chrome,
+ Chrome, NotedeckApp,
};
use notedeck_columns::Damus;
use notedeck_dave::Dave;
@@ -99,8 +99,8 @@ async fn main() {
completely_unrecognized
);
- chrome.add_app(columns);
- chrome.add_app(dave);
+ chrome.add_app(NotedeckApp::Columns(columns));
+ chrome.add_app(NotedeckApp::Dave(dave));
// test dav
chrome.set_active(1);
diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs
@@ -21,7 +21,7 @@ use std::sync::mpsc::{self, Receiver};
use std::sync::Arc;
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
-use avatar::DaveAvatar;
+pub use avatar::DaveAvatar;
use egui::{Rect, Vec2};
use egui_wgpu::RenderState;
@@ -306,6 +306,10 @@ pub struct Dave {
}
impl Dave {
+ pub fn avatar_mut(&mut self) -> Option<&mut DaveAvatar> {
+ self.avatar.as_mut()
+ }
+
pub fn new(render_state: Option<&RenderState>) -> Self {
let mut config = OpenAIConfig::new(); //.with_api_base("http://ollama.jb55.com/v1");
if let Ok(api_key) = std::env::var("OPENAI_API_KEY") {
@@ -393,12 +397,15 @@ impl Dave {
});
});
+ /*
+ // he lives in the sidebar now
if let Some(avatar) = &mut self.avatar {
let avatar_size = Vec2::splat(300.0);
let pos = Vec2::splat(100.0).to_pos2();
let pos = Rect::from_min_max(pos, pos + avatar_size);
avatar.render(pos, ui);
}
+ */
// send again
if should_send {