init notebook

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2025-07-18 12:25:46 -07:00
parent a8c6baeacb
commit 1ab4eeb48c
10 changed files with 556 additions and 24 deletions

159
Cargo.lock generated
View File

@@ -1008,6 +1008,7 @@ dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
@@ -1263,6 +1264,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
dependencies = [
"powerfmt",
"serde",
]
[[package]]
@@ -1408,6 +1410,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
[[package]]
name = "dyn-clone"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]]
name = "ecolor"
version = "0.31.1"
@@ -1626,7 +1634,7 @@ version = "0.3.0"
dependencies = [
"bech32",
"ewebsock",
"hashbrown",
"hashbrown 0.15.4",
"hex",
"mio",
"nostr 0.37.0",
@@ -2300,7 +2308,7 @@ checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca"
dependencies = [
"bitflags 2.9.1",
"gpu-descriptor-types",
"hashbrown",
"hashbrown 0.15.4",
]
[[package]]
@@ -2322,6 +2330,12 @@ dependencies = [
"crunchy",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.15.4"
@@ -2366,6 +2380,17 @@ dependencies = [
"arrayvec",
]
[[package]]
name = "hex_color"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0"
dependencies = [
"arrayvec",
"rand 0.8.5",
"serde",
]
[[package]]
name = "hex_lit"
version = "0.1.1"
@@ -2695,6 +2720,17 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408"
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"serde",
]
[[package]]
name = "indexmap"
version = "2.9.0"
@@ -2702,7 +2738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
"hashbrown 0.15.4",
"serde",
]
@@ -2890,6 +2926,19 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsoncanvas"
version = "0.1.6"
source = "git+https://github.com/jb55/jsoncanvas?rev=ae60f96e4d022cf037e086b793cacc3225bc14e5#ae60f96e4d022cf037e086b793cacc3225bc14e5"
dependencies = [
"hex_color",
"serde",
"serde_json",
"serde_with",
"thiserror 1.0.69",
"url",
]
[[package]]
name = "khronos-egl"
version = "6.0.0"
@@ -3212,7 +3261,7 @@ dependencies = [
"cfg_aliases",
"codespan-reporting",
"hexf-parse",
"indexmap",
"indexmap 2.9.0",
"log",
"rustc-hash 1.1.0",
"spirv",
@@ -3445,7 +3494,7 @@ dependencies = [
"fluent",
"fluent-langneg",
"fluent-resmgr",
"hashbrown",
"hashbrown 0.15.4",
"hex",
"image",
"jni 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3490,6 +3539,7 @@ dependencies = [
"notedeck",
"notedeck_columns",
"notedeck_dave",
"notedeck_notebook",
"notedeck_ui",
"profiling",
"puffin",
@@ -3523,11 +3573,11 @@ dependencies = [
"egui_virtual_list",
"ehttp",
"enostr",
"hashbrown",
"hashbrown 0.15.4",
"hex",
"human_format",
"image",
"indexmap",
"indexmap 2.9.0",
"nostrdb",
"notedeck",
"notedeck_ui",
@@ -3584,6 +3634,15 @@ dependencies = [
"tracing",
]
[[package]]
name = "notedeck_notebook"
version = "0.5.9"
dependencies = [
"egui",
"jsoncanvas",
"notedeck",
]
[[package]]
name = "notedeck_ui"
version = "0.5.9"
@@ -3595,7 +3654,7 @@ dependencies = [
"egui_extras",
"ehttp",
"enostr",
"hashbrown",
"hashbrown 0.15.4",
"image",
"nostrdb",
"notedeck",
@@ -4424,7 +4483,7 @@ source = "git+https://github.com/jb55/puffin?rev=c6a6242adaf90b6292c0f462d2acd34
dependencies = [
"egui",
"egui_extras",
"indexmap",
"indexmap 2.9.0",
"natord",
"once_cell",
"parking_lot",
@@ -4752,6 +4811,26 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "ref-cast"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "regex"
version = "1.11.1"
@@ -5113,6 +5192,30 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "schemars"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
dependencies = [
"dyn-clone",
"ref-cast",
"serde",
"serde_json",
]
[[package]]
name = "schemars"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
dependencies = [
"dyn-clone",
"ref-cast",
"serde",
"serde_json",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
@@ -5266,7 +5369,7 @@ version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"indexmap",
"indexmap 2.9.0",
"itoa",
"memchr",
"ryu",
@@ -5305,6 +5408,38 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_with"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5"
dependencies = [
"base64 0.22.1",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.9.0",
"schemars 0.9.0",
"schemars 1.0.4",
"serde",
"serde_derive",
"serde_json",
"serde_with_macros",
"time",
]
[[package]]
name = "serde_with_macros"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "sha1"
version = "0.10.6"
@@ -5905,7 +6040,7 @@ version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"indexmap 2.9.0",
"serde",
"serde_spanned",
"toml_datetime",
@@ -6688,7 +6823,7 @@ dependencies = [
"bitflags 2.9.1",
"cfg_aliases",
"document-features",
"indexmap",
"indexmap 2.9.0",
"log",
"naga",
"once_cell",

View File

@@ -6,6 +6,7 @@ members = [
"crates/notedeck_chrome",
"crates/notedeck_columns",
"crates/notedeck_dave",
"crates/notedeck_notebook",
"crates/notedeck_ui",
"crates/enostr", "crates/tokenator", "crates/notedeck_dave", "crates/notedeck_ui",
@@ -48,6 +49,7 @@ notedeck = { path = "crates/notedeck" }
notedeck_chrome = { path = "crates/notedeck_chrome" }
notedeck_columns = { path = "crates/notedeck_columns" }
notedeck_dave = { path = "crates/notedeck_dave" }
notedeck_notebook = { path = "crates/notedeck_notebook" }
notedeck_ui = { path = "crates/notedeck_ui" }
tokenator = { path = "crates/tokenator" }
once_cell = "1.19.0"

View File

@@ -16,6 +16,7 @@ egui = { workspace = true }
notedeck_columns = { workspace = true }
notedeck_ui = { workspace = true }
notedeck_dave = { workspace = true }
notedeck_notebook = { workspace = true }
notedeck = { workspace = true }
nostrdb = { workspace = true }
puffin = { workspace = true, optional = true }

View File

@@ -5,6 +5,7 @@ use egui_winit::winit::platform::android::activity::AndroidApp;
use notedeck::enostr::Error;
use notedeck_columns::Damus;
use notedeck_dave::Dave;
use notedeck_notebook::Notebook;
use crate::{app::NotedeckApp, chrome::Chrome, setup::setup_chrome};
use notedeck::Notedeck;
@@ -80,6 +81,7 @@ pub async fn android_main(app: AndroidApp) {
let context = &mut notedeck.app_context();
let dave = Dave::new(cc.wgpu_render_state.as_ref());
let columns = Damus::new(context, &app_args);
let notebook = Notebook::new();
let mut chrome = Chrome::new();
// ensure we recognized all the arguments
@@ -93,8 +95,9 @@ pub async fn android_main(app: AndroidApp) {
return Err(Error::Empty.into());
}
chrome.add_app(NotedeckApp::Columns(columns));
chrome.add_app(NotedeckApp::Dave(dave));
chrome.add_app(NotedeckApp::Columns(Box::new(columns)));
chrome.add_app(NotedeckApp::Dave(Box::new(dave)));
chrome.add_app(NotedeckApp::Notebook(Box::new(notebook)));
// test dav
chrome.set_active(0);

View File

@@ -1,11 +1,13 @@
use notedeck::{AppAction, AppContext};
use notedeck_columns::Damus;
use notedeck_dave::Dave;
use notedeck_notebook::Notebook;
#[allow(clippy::large_enum_variant)]
pub enum NotedeckApp {
Dave(Dave),
Columns(Damus),
Dave(Box<Dave>),
Columns(Box<Damus>),
Notebook(Box<Notebook>),
Other(Box<dyn notedeck::App>),
}
@@ -14,6 +16,7 @@ impl notedeck::App for NotedeckApp {
match self {
NotedeckApp::Dave(dave) => dave.update(ctx, ui),
NotedeckApp::Columns(columns) => columns.update(ctx, ui),
NotedeckApp::Notebook(notebook) => notebook.update(ctx, ui),
NotedeckApp::Other(other) => other.update(ctx, ui),
}
}

View File

@@ -12,6 +12,7 @@ use notedeck_columns::{
column::SelectionResult, timeline::kind::ListKind, timeline::TimelineKind, Damus,
};
use notedeck_dave::{Dave, DaveAvatar};
use notedeck_notebook::Notebook;
use notedeck_ui::{app_images, AnimationHelper, ProfilePic};
static ICON_WIDTH: f32 = 40.0;
@@ -199,6 +200,16 @@ impl Chrome {
None
}
fn get_notebook(&mut self) -> Option<&mut Notebook> {
for app in &mut self.apps {
if let NotedeckApp::Notebook(notebook) = app {
return Some(notebook);
}
}
None
}
fn switch_to_dave(&mut self) {
for (i, app) in self.apps.iter().enumerate() {
if let NotedeckApp::Dave(_) = app {
@@ -207,6 +218,14 @@ impl Chrome {
}
}
fn switch_to_notebook(&mut self) {
for (i, app) in self.apps.iter().enumerate() {
if let NotedeckApp::Notebook(_) = app {
self.active = i as i32;
}
}
}
fn switch_to_columns(&mut self) {
for (i, app) in self.apps.iter().enumerate() {
if let NotedeckApp::Columns(_) = app {
@@ -428,14 +447,12 @@ impl Chrome {
ui.add(milestone_name(i18n));
ui.add_space(16.0);
//let dark_mode = ui.ctx().style().visuals.dark_mode;
{
if columns_button(ui)
.on_hover_cursor(egui::CursorIcon::PointingHand)
.clicked()
{
self.active = 0;
}
}
ui.add_space(32.0);
if let Some(dave) = self.get_dave() {
@@ -446,6 +463,16 @@ impl Chrome {
self.switch_to_dave();
}
}
//ui.add_space(32.0);
if let Some(_notebook) = self.get_notebook() {
if notebook_button(ui)
.on_hover_cursor(egui::CursorIcon::PointingHand)
.clicked()
{
self.switch_to_notebook();
}
}
}
}
@@ -583,6 +610,16 @@ fn accounts_button(ui: &mut egui::Ui) -> egui::Response {
)
}
fn notebook_button(ui: &mut egui::Ui) -> egui::Response {
expanding_button(
"columns-button",
40.0,
app_images::new_message_image(),
app_images::new_message_image(),
ui,
)
}
fn dave_sidebar_rect(ui: &mut egui::Ui) -> Rect {
let size = vec2(60.0, 60.0);
let available = ui.available_rect_before_wrap();

View File

@@ -17,6 +17,7 @@ use notedeck_chrome::{
};
use notedeck_columns::Damus;
use notedeck_dave::Dave;
use notedeck_notebook::Notebook;
use tracing::error;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::EnvFilter;
@@ -97,6 +98,7 @@ async fn main() {
let mut chrome = Chrome::new();
let columns = Damus::new(&mut notedeck.app_context(), &args);
let dave = Dave::new(cc.wgpu_render_state.as_ref());
let notebook = Notebook::default();
setup_chrome(
ctx,
@@ -117,8 +119,9 @@ async fn main() {
return Err(Error::Empty.into());
}
chrome.add_app(NotedeckApp::Columns(columns));
chrome.add_app(NotedeckApp::Dave(dave));
chrome.add_app(NotedeckApp::Columns(Box::new(columns)));
chrome.add_app(NotedeckApp::Dave(Box::new(dave)));
chrome.add_app(NotedeckApp::Notebook(Box::new(notebook)));
chrome.set_active(0);

View File

@@ -0,0 +1,9 @@
[package]
name = "notedeck_notebook"
edition = "2024"
version.workspace = true
[dependencies]
jsoncanvas = { git = "https://github.com/jb55/jsoncanvas", rev = "ae60f96e4d022cf037e086b793cacc3225bc14e5" }
notedeck = { workspace = true }
egui = { workspace = true }

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,107 @@
use egui::{Align, Label, Pos2, Rect, TextWrapMode};
use jsoncanvas::{FileNode, GroupNode, JsonCanvas, LinkNode, Node, TextNode, node::*};
use notedeck::{AppAction, AppContext};
pub struct Notebook {
canvas: JsonCanvas,
scene_rect: Rect,
loaded: bool,
}
impl Default for Notebook {
fn default() -> Self {
Notebook {
canvas: demo_canvas(),
scene_rect: Rect::from_min_max(Pos2::ZERO, Pos2::ZERO),
loaded: false,
}
}
}
impl notedeck::App for Notebook {
fn update(&mut self, _ctx: &mut AppContext<'_>, ui: &mut egui::Ui) -> Option<AppAction> {
//let app_action: Option<AppAction> = None;
if !self.loaded {
self.scene_rect = ui.available_rect_before_wrap();
self.loaded = true;
}
egui::Scene::new().show(ui, &mut self.scene_rect, |ui| {
for (_node_id, node) in self.canvas.get_nodes().iter() {
let _resp = node_ui(ui, node);
}
});
None
}
}
fn node_ui(ui: &mut egui::Ui, node: &Node) -> egui::Response {
match node {
Node::Text(text_node) => text_node_ui(ui, text_node),
Node::File(file_node) => file_node_ui(ui, file_node),
Node::Link(link_node) => link_node_ui(ui, link_node),
Node::Group(group_node) => group_node_ui(ui, group_node),
}
}
fn text_node_ui(ui: &mut egui::Ui, node: &TextNode) -> egui::Response {
node_box_ui(ui, node.node(), |ui| {
ui.with_layout(egui::Layout::left_to_right(Align::Min), |ui| {
ui.add(Label::new(node.text()).wrap_mode(TextWrapMode::Wrap))
});
})
}
fn file_node_ui(ui: &mut egui::Ui, node: &FileNode) -> egui::Response {
node_box_ui(ui, node.node(), |ui| {
ui.label("file node");
})
}
fn link_node_ui(ui: &mut egui::Ui, node: &LinkNode) -> egui::Response {
node_box_ui(ui, node.node(), |ui| {
ui.label("link node");
})
}
fn group_node_ui(ui: &mut egui::Ui, node: &GroupNode) -> egui::Response {
node_box_ui(ui, node.node(), |ui| {
ui.label("group node");
})
}
fn node_box_ui(
ui: &mut egui::Ui,
node: &GenericNode,
contents: impl FnOnce(&mut egui::Ui),
) -> egui::Response {
let x = node.x as f32;
let y = node.y as f32;
let width = node.width as f32;
let height = node.height as f32;
let min = Pos2::new(x, y);
let max = Pos2::new(x + width, y + height);
ui.put(Rect::from_min_max(min, max), |ui: &mut egui::Ui| {
egui::Frame::default()
.fill(ui.visuals().noninteractive().weak_bg_fill)
.inner_margin(egui::Margin::same(4))
.corner_radius(egui::CornerRadius::same(10))
.stroke(egui::Stroke::new(
1.0,
ui.visuals().noninteractive().bg_stroke.color,
))
.show(ui, contents)
.response
})
}
fn demo_canvas() -> JsonCanvas {
let demo_json: String = include_str!("../demo.canvas").to_string();
let canvas: JsonCanvas = demo_json.parse().unwrap_or_else(|_| JsonCanvas::default());
canvas
}