mirror of
https://github.com/aljazceru/notedeck.git
synced 2026-02-20 16:04:21 +01:00
Merge notebook (feature gated)
This merges the experimental notebook app, which can be enabled with --notebook. We also switch to bitflags for notedeck options
This commit is contained in:
160
Cargo.lock
generated
160
Cargo.lock
generated
@@ -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",
|
||||
@@ -3434,6 +3483,7 @@ dependencies = [
|
||||
"base32",
|
||||
"bech32",
|
||||
"bincode",
|
||||
"bitflags 2.9.1",
|
||||
"blurhash",
|
||||
"dirs",
|
||||
"eframe",
|
||||
@@ -3445,7 +3495,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 +3540,7 @@ dependencies = [
|
||||
"notedeck",
|
||||
"notedeck_columns",
|
||||
"notedeck_dave",
|
||||
"notedeck_notebook",
|
||||
"notedeck_ui",
|
||||
"profiling",
|
||||
"puffin",
|
||||
@@ -3523,11 +3574,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 +3635,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 +3655,7 @@ dependencies = [
|
||||
"egui_extras",
|
||||
"ehttp",
|
||||
"enostr",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.4",
|
||||
"image",
|
||||
"nostrdb",
|
||||
"notedeck",
|
||||
@@ -4424,7 +4484,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 +4812,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 +5193,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 +5370,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 +5409,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 +6041,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 +6824,7 @@ dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"cfg_aliases",
|
||||
"document-features",
|
||||
"indexmap",
|
||||
"indexmap 2.9.0",
|
||||
"log",
|
||||
"naga",
|
||||
"once_cell",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -47,6 +47,7 @@ fluent-langneg = { workspace = true }
|
||||
unic-langid = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
md5 = { workspace = true }
|
||||
bitflags = { workspace = true }
|
||||
regex = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::persist::{AppSizeHandler, SettingsHandler};
|
||||
use crate::wallet::GlobalWallet;
|
||||
use crate::zaps::Zaps;
|
||||
use crate::JobPool;
|
||||
use crate::NotedeckOptions;
|
||||
use crate::{
|
||||
frame_history::FrameHistory, AccountStorage, Accounts, AppContext, Args, DataPath,
|
||||
DataPathType, Directory, Images, NoteAction, NoteCache, RelayDebugView, UnknownIds,
|
||||
@@ -109,7 +110,7 @@ impl eframe::App for Notedeck {
|
||||
});
|
||||
self.app_size.try_save_app_size(ctx);
|
||||
|
||||
if self.args.relay_debug {
|
||||
if self.args.options.contains(NotedeckOptions::RelayDebug) {
|
||||
if self.pool.debug.is_none() {
|
||||
self.pool.use_debug();
|
||||
}
|
||||
@@ -170,7 +171,7 @@ impl Notedeck {
|
||||
|
||||
let config = Config::new().set_ingester_threads(2).set_mapsize(map_size);
|
||||
|
||||
let keystore = if parsed_args.use_keystore {
|
||||
let keystore = if parsed_args.options.contains(NotedeckOptions::UseKeystore) {
|
||||
let keys_path = path.path(DataPathType::Keys);
|
||||
let selected_key_path = path.path(DataPathType::SelectedKey);
|
||||
Some(AccountStorage::new(
|
||||
@@ -276,6 +277,10 @@ impl Notedeck {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn options(&self) -> NotedeckOptions {
|
||||
self.args.options
|
||||
}
|
||||
|
||||
pub fn app<A: App + 'static>(mut self, app: A) -> Self {
|
||||
self.set_app(app);
|
||||
self
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::NotedeckOptions;
|
||||
use enostr::{Keypair, Pubkey, SecretKey};
|
||||
use tracing::error;
|
||||
use unic_langid::{LanguageIdentifier, LanguageIdentifierError};
|
||||
|
||||
pub struct Args {
|
||||
pub relays: Vec<String>,
|
||||
pub is_mobile: Option<bool>,
|
||||
pub locale: Option<LanguageIdentifier>,
|
||||
pub show_note_client: bool,
|
||||
pub keys: Vec<Keypair>,
|
||||
pub light: bool,
|
||||
pub debug: bool,
|
||||
pub relay_debug: bool,
|
||||
|
||||
/// Enable when running tests so we don't panic on app startup
|
||||
pub tests: bool,
|
||||
|
||||
pub use_keystore: bool,
|
||||
pub options: NotedeckOptions,
|
||||
pub dbpath: Option<String>,
|
||||
pub datapath: Option<String>,
|
||||
}
|
||||
@@ -28,14 +20,8 @@ impl Args {
|
||||
let mut unrecognized_args = BTreeSet::new();
|
||||
let mut res = Args {
|
||||
relays: vec![],
|
||||
is_mobile: None,
|
||||
keys: vec![],
|
||||
light: false,
|
||||
show_note_client: false,
|
||||
debug: false,
|
||||
relay_debug: false,
|
||||
tests: false,
|
||||
use_keystore: true,
|
||||
options: NotedeckOptions::default(),
|
||||
dbpath: None,
|
||||
datapath: None,
|
||||
locale: None,
|
||||
@@ -47,9 +33,9 @@ impl Args {
|
||||
let arg = &args[i];
|
||||
|
||||
if arg == "--mobile" {
|
||||
res.is_mobile = Some(true);
|
||||
res.options.set(NotedeckOptions::Mobile, true);
|
||||
} else if arg == "--light" {
|
||||
res.light = true;
|
||||
res.options.set(NotedeckOptions::LightTheme, true);
|
||||
} else if arg == "--locale" {
|
||||
i += 1;
|
||||
let Some(locale) = args.get(i) else {
|
||||
@@ -68,11 +54,11 @@ impl Args {
|
||||
}
|
||||
}
|
||||
} else if arg == "--dark" {
|
||||
res.light = false;
|
||||
res.options.set(NotedeckOptions::LightTheme, false);
|
||||
} else if arg == "--debug" {
|
||||
res.debug = true;
|
||||
res.options.set(NotedeckOptions::Debug, true);
|
||||
} else if arg == "--testrunner" {
|
||||
res.tests = true;
|
||||
res.options.set(NotedeckOptions::Tests, true);
|
||||
} else if arg == "--pub" || arg == "--npub" {
|
||||
i += 1;
|
||||
let pubstr = if let Some(next_arg) = args.get(i) {
|
||||
@@ -135,11 +121,13 @@ impl Args {
|
||||
};
|
||||
res.relays.push(relay.clone());
|
||||
} else if arg == "--no-keystore" {
|
||||
res.use_keystore = false;
|
||||
res.options.set(NotedeckOptions::UseKeystore, true);
|
||||
} else if arg == "--relay-debug" {
|
||||
res.relay_debug = true;
|
||||
} else if arg == "--show-note-client" {
|
||||
res.show_note_client = true;
|
||||
res.options.set(NotedeckOptions::RelayDebug, true);
|
||||
} else if arg == "--show-client" {
|
||||
res.options.set(NotedeckOptions::ShowClient, true);
|
||||
} else if arg == "--notebook" {
|
||||
res.options.set(NotedeckOptions::FeatureNotebook, true);
|
||||
} else {
|
||||
unrecognized_args.insert(arg.clone());
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ mod muted;
|
||||
pub mod name;
|
||||
pub mod note;
|
||||
mod notecache;
|
||||
mod options;
|
||||
mod persist;
|
||||
pub mod platform;
|
||||
pub mod profile;
|
||||
@@ -68,6 +69,7 @@ pub use note::{
|
||||
RootIdError, RootNoteId, RootNoteIdBuf, ScrollInfo, ZapAction,
|
||||
};
|
||||
pub use notecache::{CachedNote, NoteCache};
|
||||
pub use options::NotedeckOptions;
|
||||
pub use persist::*;
|
||||
pub use profile::get_profile_url;
|
||||
pub use relay_debug::RelayDebugView;
|
||||
|
||||
39
crates/notedeck/src/options.rs
Normal file
39
crates/notedeck/src/options.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct NotedeckOptions: u64 {
|
||||
// ===== Settings ======
|
||||
/// Are we on light theme?
|
||||
const LightTheme = 1 << 0;
|
||||
|
||||
/// Debug controls, fps stats
|
||||
const Debug = 1 << 1;
|
||||
|
||||
/// Show relay debug window?
|
||||
const RelayDebug = 1 << 2;
|
||||
|
||||
/// Are we running as tests?
|
||||
const Tests = 1 << 3;
|
||||
|
||||
/// Use keystore?
|
||||
const UseKeystore = 1 << 4;
|
||||
|
||||
/// Show client on notes?
|
||||
const ShowClient = 1 << 5;
|
||||
|
||||
/// Simulate is_compiled_as_mobile ?
|
||||
const Mobile = 1 << 6;
|
||||
|
||||
// ===== Feature Flags ======
|
||||
/// Is notebook enabled?
|
||||
const FeatureNotebook = 1 << 32;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NotedeckOptions {
|
||||
fn default() -> Self {
|
||||
NotedeckOptions::UseKeystore
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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,15 @@ 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)));
|
||||
|
||||
if notedeck
|
||||
.options()
|
||||
.contains(NotedeckOptions::FeaturesNotebook)
|
||||
{
|
||||
chrome.add_app(NotedeckApp::Notebook(Box::default()));
|
||||
}
|
||||
|
||||
// test dav
|
||||
chrome.set_active(0);
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ use egui::{vec2, Button, Color32, Label, Layout, Rect, RichText, ThemePreference
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
use nostrdb::{ProfileRecord, Transaction};
|
||||
use notedeck::{
|
||||
tr, App, AppAction, AppContext, Localization, NotedeckTextStyle, UserAccount, WalletType,
|
||||
tr, App, AppAction, AppContext, Localization, NotedeckOptions, NotedeckTextStyle, UserAccount,
|
||||
WalletType,
|
||||
};
|
||||
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 +201,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 +219,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,13 +448,11 @@ 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()
|
||||
{
|
||||
if columns_button(ui)
|
||||
.on_hover_cursor(egui::CursorIcon::PointingHand)
|
||||
.clicked()
|
||||
{
|
||||
self.active = 0;
|
||||
}
|
||||
self.active = 0;
|
||||
}
|
||||
ui.add_space(32.0);
|
||||
|
||||
@@ -446,6 +464,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 +611,16 @@ fn accounts_button(ui: &mut egui::Ui) -> egui::Response {
|
||||
)
|
||||
}
|
||||
|
||||
fn notebook_button(ui: &mut egui::Ui) -> egui::Response {
|
||||
expanding_button(
|
||||
"notebook-button",
|
||||
40.0,
|
||||
app_images::algo_image(),
|
||||
app_images::algo_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();
|
||||
@@ -861,7 +899,7 @@ fn bottomup_sidebar(
|
||||
.add(wallet_button())
|
||||
.on_hover_cursor(egui::CursorIcon::PointingHand);
|
||||
|
||||
if ctx.args.debug {
|
||||
if ctx.args.options.contains(NotedeckOptions::Debug) {
|
||||
ui.weak(format!("{}", ctx.frame_history.fps() as i32));
|
||||
ui.weak(format!(
|
||||
"{:10.1}",
|
||||
|
||||
@@ -10,7 +10,7 @@ static GLOBAL: AccountingAllocator<std::alloc::System> =
|
||||
AccountingAllocator::new(std::alloc::System);
|
||||
|
||||
use notedeck::enostr::Error;
|
||||
use notedeck::{DataPath, DataPathType, Notedeck};
|
||||
use notedeck::{DataPath, DataPathType, Notedeck, NotedeckOptions};
|
||||
use notedeck_chrome::{
|
||||
setup::{generate_native_options, setup_chrome},
|
||||
Chrome, NotedeckApp,
|
||||
@@ -117,8 +117,15 @@ 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)));
|
||||
|
||||
if notedeck
|
||||
.options()
|
||||
.contains(NotedeckOptions::FeatureNotebook)
|
||||
{
|
||||
chrome.add_app(NotedeckApp::Notebook(Box::default()));
|
||||
}
|
||||
|
||||
chrome.set_active(0);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{fonts, theme};
|
||||
|
||||
use eframe::NativeOptions;
|
||||
use egui::{FontId, ThemePreference};
|
||||
use notedeck::{AppSizeHandler, DataPath, NotedeckTextStyle};
|
||||
use notedeck::{AppSizeHandler, DataPath, NotedeckOptions, NotedeckTextStyle};
|
||||
use notedeck_ui::app_images;
|
||||
use tracing::info;
|
||||
|
||||
@@ -13,16 +13,20 @@ pub fn setup_chrome(
|
||||
note_body_font_size: f32,
|
||||
zoom_factor: f32,
|
||||
) {
|
||||
let is_mobile = args
|
||||
.is_mobile
|
||||
.unwrap_or(notedeck::ui::is_compiled_as_mobile());
|
||||
let is_mobile =
|
||||
args.options.contains(NotedeckOptions::Mobile) || notedeck::ui::is_compiled_as_mobile();
|
||||
|
||||
let is_oled = notedeck::ui::is_oled();
|
||||
|
||||
// Some people have been running notedeck in debug, let's catch that!
|
||||
if !args.tests && cfg!(debug_assertions) && !args.debug {
|
||||
if !args.options.contains(NotedeckOptions::Tests)
|
||||
&& cfg!(debug_assertions)
|
||||
&& !args.options.contains(NotedeckOptions::Debug)
|
||||
{
|
||||
println!("--- WELCOME TO DAMUS NOTEDECK! ---");
|
||||
println!("It looks like are running notedeck in debug mode, unless you are a developer, this is not likely what you want.");
|
||||
println!(
|
||||
"It looks like are running notedeck in debug mode, unless you are a developer, this is not likely what you want."
|
||||
);
|
||||
println!("If you are a developer, run `cargo run -- --debug` to skip this message.");
|
||||
println!("For everyone else, try again with `cargo run --release`. Enjoy!");
|
||||
println!("---------------------------------");
|
||||
|
||||
@@ -19,7 +19,7 @@ use enostr::{ClientMessage, PoolRelay, Pubkey, RelayEvent, RelayMessage, RelayPo
|
||||
use nostrdb::Transaction;
|
||||
use notedeck::{
|
||||
tr, ui::is_narrow, Accounts, AppAction, AppContext, DataPath, DataPathType, FilterState,
|
||||
Images, JobsCache, Localization, SettingsHandler, UnknownIds,
|
||||
Images, JobsCache, Localization, NotedeckOptions, SettingsHandler, UnknownIds,
|
||||
};
|
||||
use notedeck_ui::{
|
||||
media::{MediaViewer, MediaViewerFlags, MediaViewerState},
|
||||
@@ -442,7 +442,10 @@ impl Damus {
|
||||
let mut options = AppOptions::default();
|
||||
let tmp_columns = !parsed_args.columns.is_empty();
|
||||
options.set(AppOptions::TmpColumns, tmp_columns);
|
||||
options.set(AppOptions::Debug, app_context.args.debug);
|
||||
options.set(
|
||||
AppOptions::Debug,
|
||||
app_context.args.options.contains(NotedeckOptions::Debug),
|
||||
);
|
||||
options.set(
|
||||
AppOptions::SinceOptimize,
|
||||
parsed_args.is_flag_set(ColumnsFlag::SinceOptimize),
|
||||
|
||||
9
crates/notedeck_notebook/Cargo.toml
Normal file
9
crates/notedeck_notebook/Cargo.toml
Normal 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 }
|
||||
232
crates/notedeck_notebook/demo.canvas
Normal file
232
crates/notedeck_notebook/demo.canvas
Normal file
File diff suppressed because one or more lines are too long
26
crates/notedeck_notebook/src/debug.rs
Normal file
26
crates/notedeck_notebook/src/debug.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
/*
|
||||
fn debug_slider(
|
||||
ui: &mut egui::Ui,
|
||||
id: egui::Id,
|
||||
point: Pos2,
|
||||
initial: f32,
|
||||
range: std::ops::RangeInclusive<f32>,
|
||||
) -> f32 {
|
||||
let mut val = ui.data_mut(|d| *d.get_temp_mut_or::<f32>(id, initial));
|
||||
let nudge = vec2(10.0, 10.0);
|
||||
let slider = Rect::from_min_max(point - nudge, point + nudge);
|
||||
let label = Rect::from_min_max(point + nudge * 2.0, point - nudge * 2.0);
|
||||
|
||||
let old_val = val;
|
||||
ui.put(slider, egui::Slider::new(&mut val, range));
|
||||
ui.put(label, egui::Label::new(format!("{val}")));
|
||||
|
||||
if val != old_val {
|
||||
ui.data_mut(|d| d.insert_temp(id, val))
|
||||
}
|
||||
|
||||
val
|
||||
}
|
||||
*/
|
||||
|
||||
60
crates/notedeck_notebook/src/lib.rs
Normal file
60
crates/notedeck_notebook/src/lib.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use crate::ui::{edge_ui, node_ui};
|
||||
use egui::{Pos2, Rect};
|
||||
use jsoncanvas::JsonCanvas;
|
||||
use notedeck::{AppAction, AppContext};
|
||||
|
||||
mod ui;
|
||||
|
||||
pub struct Notebook {
|
||||
canvas: JsonCanvas,
|
||||
scene_rect: Rect,
|
||||
loaded: bool,
|
||||
}
|
||||
|
||||
impl Notebook {
|
||||
pub fn new() -> Self {
|
||||
Notebook::default()
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
// render nodes
|
||||
for (_node_id, node) in self.canvas.get_nodes().iter() {
|
||||
let _resp = node_ui(ui, node);
|
||||
}
|
||||
|
||||
// render edges
|
||||
for (_edge_id, edge) in self.canvas.get_edges().iter() {
|
||||
let _resp = edge_ui(ui, self.canvas.get_nodes(), edge);
|
||||
}
|
||||
});
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
184
crates/notedeck_notebook/src/ui.rs
Normal file
184
crates/notedeck_notebook/src/ui.rs
Normal file
@@ -0,0 +1,184 @@
|
||||
use egui::{Align, Label, Pos2, Rect, Shape, Stroke, TextWrapMode, epaint::CubicBezierShape, vec2};
|
||||
use jsoncanvas::{
|
||||
FileNode, GroupNode, LinkNode, Node, NodeId, TextNode,
|
||||
edge::{Edge, Side},
|
||||
node::GenericNode,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Neg;
|
||||
|
||||
fn node_rect(node: &GenericNode) -> Rect {
|
||||
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);
|
||||
|
||||
Rect::from_min_max(min, max)
|
||||
}
|
||||
|
||||
fn side_point(side: &Side, node: &GenericNode) -> Pos2 {
|
||||
let rect = node_rect(node);
|
||||
|
||||
match side {
|
||||
Side::Top => rect.center_top(),
|
||||
Side::Left => rect.left_center(),
|
||||
Side::Right => rect.right_center(),
|
||||
Side::Bottom => rect.center_bottom(),
|
||||
}
|
||||
}
|
||||
|
||||
/// a unit vector pointing outward from the given side
|
||||
fn side_tangent(side: &Side) -> egui::Vec2 {
|
||||
match side {
|
||||
Side::Top => vec2(0.0, -1.0),
|
||||
Side::Bottom => vec2(0.0, 1.0),
|
||||
Side::Left => vec2(-1.0, 0.0),
|
||||
Side::Right => vec2(1.0, 0.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edge_ui(
|
||||
ui: &mut egui::Ui,
|
||||
nodes: &HashMap<NodeId, Node>,
|
||||
edge: &Edge,
|
||||
) -> Option<egui::Response> {
|
||||
let from_node = nodes.get(edge.from_node())?;
|
||||
let to_node = nodes.get(edge.to_node())?;
|
||||
let to_side = edge.to_side()?;
|
||||
let from_side = edge.from_side()?;
|
||||
|
||||
// anchor from-side
|
||||
let p0 = side_point(from_side, from_node.node());
|
||||
|
||||
// anchor b
|
||||
let to_anchor = side_point(to_side, to_node.node());
|
||||
|
||||
// to-point is slightly offset to accomidate arrow
|
||||
let p3 = to_anchor + side_tangent(to_side) * 2.0;
|
||||
|
||||
// bend debug
|
||||
//let bend = debug_slider(ui, ui.id().with("bend"), p3, 0.25, 0.0..=1.0);
|
||||
let bend = 0.28;
|
||||
|
||||
// How far to pull the tangents.
|
||||
// ¼ of the distance between anchors feels very “Obsidian”.
|
||||
let d = (p3 - p0).length() * bend;
|
||||
|
||||
// c1 = anchor A + (outward tangent) * d
|
||||
let c1 = p0 + side_tangent(from_side) * d;
|
||||
|
||||
// c2 = anchor B + (inward tangent) * d
|
||||
let c2 = p3 - side_tangent(to_side).neg() * d;
|
||||
|
||||
let color = ui.visuals().noninteractive().bg_stroke.color;
|
||||
let stroke = egui::Stroke::new(4.0, color);
|
||||
let bezier = CubicBezierShape::from_points_stroke([p0, c1, c2, p3], false, color, stroke);
|
||||
|
||||
ui.painter().add(Shape::CubicBezier(bezier));
|
||||
arrow_ui(ui, to_side, to_anchor, color);
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Paint a tiny triangular “arrow”.
|
||||
///
|
||||
/// * `ui` – the egui `Ui` you’re painting in
|
||||
/// * `side` – which edge of the box we’re attaching to
|
||||
/// * `point` – the exact spot on that edge the arrow’s tip should touch
|
||||
/// * `fill` – colour to fill the arrow with (usually your popup’s background)
|
||||
pub fn arrow_ui(ui: &mut egui::Ui, side: &Side, point: Pos2, fill: egui::Color32) {
|
||||
let len: f32 = 12.0; // distance from tip to base
|
||||
let width: f32 = 16.0; // length of the base
|
||||
let stroke: f32 = 1.0; // length of the base
|
||||
|
||||
let verts = match side {
|
||||
Side::Top => [
|
||||
point, // tip
|
||||
Pos2::new(point.x - width * 0.5, point.y - len), // base‑left (above)
|
||||
Pos2::new(point.x + width * 0.5, point.y - len), // base‑right (above)
|
||||
],
|
||||
Side::Bottom => [
|
||||
point,
|
||||
Pos2::new(point.x + width * 0.5, point.y + len), // below
|
||||
Pos2::new(point.x - width * 0.5, point.y + len),
|
||||
],
|
||||
Side::Left => [
|
||||
point,
|
||||
Pos2::new(point.x - len, point.y + width * 0.5), // left
|
||||
Pos2::new(point.x - len, point.y - width * 0.5),
|
||||
],
|
||||
Side::Right => [
|
||||
point,
|
||||
Pos2::new(point.x + len, point.y - width * 0.5), // right
|
||||
Pos2::new(point.x + len, point.y + width * 0.5),
|
||||
],
|
||||
};
|
||||
|
||||
ui.painter().add(egui::Shape::convex_polygon(
|
||||
verts.to_vec(),
|
||||
fill,
|
||||
Stroke::new(stroke, fill), // add a stroke here if you want an outline
|
||||
));
|
||||
}
|
||||
|
||||
pub 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| {
|
||||
egui::ScrollArea::vertical()
|
||||
.show(ui, |ui| {
|
||||
ui.with_layout(egui::Layout::left_to_right(Align::Min), |ui| {
|
||||
ui.add(Label::new(node.text()).wrap_mode(TextWrapMode::Wrap))
|
||||
})
|
||||
})
|
||||
.inner
|
||||
.response
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
) -> egui::Response {
|
||||
let pos = node_rect(node);
|
||||
|
||||
ui.put(pos, |ui: &mut egui::Ui| {
|
||||
egui::Frame::default()
|
||||
.fill(ui.visuals().noninteractive().weak_bg_fill)
|
||||
.inner_margin(egui::Margin::same(16))
|
||||
.corner_radius(egui::CornerRadius::same(10))
|
||||
.stroke(egui::Stroke::new(
|
||||
2.0,
|
||||
ui.visuals().noninteractive().bg_stroke.color,
|
||||
))
|
||||
.show(ui, |ui| {
|
||||
let rect = ui.available_rect_before_wrap();
|
||||
ui.allocate_at_least(ui.available_size(), egui::Sense::click());
|
||||
ui.put(rect, contents);
|
||||
})
|
||||
.response
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user