This document captures the current architecture, coding conventions, and design patterns across the Notedeck repository to help new agent-driven experiences slot cleanly into the existing codebase.
crates/notedeck– Core framework: application host (Notedeck), shared services (AppContext,Accounts, caches, persistence, localization).crates/notedeck_chrome– Container UI that boots Notedeck, manages the application switcher/sidebar, and wires apps into the main window.crates/notedeck_columns– Primary “Damus” client: timelines, decks/columns, routing, multi-relay subscription management.crates/notedeck_ui– Reusable egui widgets (NoteView, media renderers, profile components) and UI utilities.crates/notedeck_dave– Dave AI assistant showcasing agent-style tooling, streaming responses, and custom rendering.crates/tokenator– Text token utility library used by other crates.
Apptrait (crates/notedeck/src/app.rs): Apps implementupdate(&mut self, &mut AppContext, &mut egui::Ui) -> AppResponseto drive egui rendering and signal high-level actions (AppActionfor route changes, chrome toggles, etc.).Notedeckstruct (crates/notedeck/src/app.rs) owns global resources—NostrDB connection, caches, relay pool, accounts, zaps, localization, clipboard, frame history—and injects them throughAppContext.AppContext(crates/notedeck/src/context.rs) is the dependency hub handed to every app update. It exposes mutable handles to services (database, caches, relay pool, account state, localization, settings, wallet) so apps stay decoupled from the host.AppResponsecarries optional actions and drag targets; chrome inspects it to react to app-level intent.
- Chrome shell (
crates/notedeck_chrome/src/chrome.rs): wraps multipleAppinstances, draws sidebar navigation, and forwards eguiupdatepasses to the active app. NotedeckAppenum (crates/notedeck_chrome/src/app.rs) defines the shipping app roster (Columns/Damus, Dave, others) and provides constructors for wiring new apps.
- No Mutexes in UI paths: The render loop must never block. All UI code operates on owned data or uses
Rc<RefCell<>>for single-threaded interior mutability. - Cross-thread sharing: Prefer share-nothing channels over Mutex-based shared state when building concurrent application features.
- Database:
nostrdb::Ndbis the primary storage/query engine. Transactions are short-lived (Transaction::new) and most reads flow through caches. - Caches:
NoteCache(NIP-10/thread metadata),Images(image/GIF cache),UnknownIds(tracks pubkeys/notes discovered via tags).
- Relay management:
enostr::RelayPoolis shared inAppContext. Apps enqueue filters, processRelayEvents, and update timelines. - Subscriptions: Columns crate layers
Subscriptions,MultiSubscriber, andTimelineCacheto fan out relay queries per column. Unknown IDs are resolved lazily and retried until satisfied. - Debouncing & persistence:
TimedSerializer+Debouncerpersist settings/state without hammering the filesystem (crates/notedeck/src/timed_serializer.rs).
- Immediate-mode UI: All apps render with
egui, respecting the host’sContextfor theming and input. - Shared components (
crates/notedeck_ui):NoteViewbundles author header, body, media, and action bar with configurableNoteOptions.- Profile widgets (
ProfilePic,ProfilePreview), media viewers, mention chips, and timeline helpers keep rendering consistent.
- Columns-specific layout:
Damusapp (crates/notedeck_columns/src/app.rs) manages decks, per-column routers, timeline hydration, and keyboard navigation. It usesStripBuilderand custom panels for multi-column flows. - Chrome handles responsive breakpoints (e.g.,
ui::is_narrow) to switch layouts for mobile widths.
- Promise-based async (
poll_promise::Promise): The dominant pattern for async work. Promises are polled viapromise.ready()in the render loop—never blocking. Results are consumed when available. JobPool(crates/notedeck/src/job_pool.rs): A 2-thread pool for CPU-bound work (e.g., blurhash computation). Returns results viatokio::sync::oneshotwrapped in Promises.- Tokio tasks: Network I/O, wallet operations, and relay sync use
tokio::spawn(). Usetokio::task::JoinSetwhen managing multiple concurrent tasks. - Dave async: Streams AI tokens through channels, spawns tasks with
tokio::spawn, and updates the UI as chunks arrive—seecrates/notedeck_dave/src/lib.rs. - Relay events: Columns polls
RelayPool::try_recv()inside the egui loop, translates network activity into timeline mutations, and schedules follow-up fetches (e.g.,timeline::poll_notes_into_view).
- Localization:
tr!/tr_plural!macros (documented incrates/notedeck/DEVELOPER.md) normalize strings into Fluent keys.LocalizationManagercaches translations; locale is saved viaSettingsHandler. - Themes & fonts:
ColorTheme,NamedFontFamily, and theme builders ensure consistent typography and support OLED dark mode. - Settings & tokens:
SettingsHandlerstores theme, zoom, locale, and textual toggles;TokenHandlerpersists auth tokens safely.
- Structured tool system (
crates/notedeck_dave/src/tools.rs): Defines tool metadata, JSON argument parsing, and execution into typed responses. Great reference for agent capabilities (search, present notes). - Streaming UI: Uses
mpscchannels to surface streaming AI output while continuing to render frames (crates/notedeck_dave/docs/developer-guide.md). - Custom rendering: Demonstrates embedding WebGPU callbacks for 3D avatars while remaining within egui’s lifecycle.
- Run
cargo fmt --all && cargo clippywhen you're done your work, ensure that all lints are fixed before committing. - Rust 2021, edition-lints are strict; clippy
disallowed_methodsis denied at crate root to enforce API hygiene (crates/notedeck/src/lib.rs). - Prefer module-level organization over monolithic files; each feature (accounts, decks, timelines, media) lives in its own module tree.
- Use
tracingmacros for structured logging andprofilingscopes where hot paths exist (Columns' relay/event loop). - Mark performance-critical functions with
#[profiling::function]for visibility in the puffin profiler. - UI code embraces egui idioms: builder chains, closures returning
Response,ui.vertical/horizontalfor layout. - Tests live alongside modules (e.g.,
JobPool), often using#[tokio::test]when async behavior is involved. - Localization updates: run
python3 scripts/export_source_strings.pyafter changing user-facing strings; translators rely on the generated Fluent files.
- Prototype as an App: Implement the
Apptrait, usingAppContextto read fromNdb, inspect accounts, and access localization. - Register in Chrome: Add a variant to
NotedeckApp, supply icon/label metadata, and hook it into the sidebar. - Leverage shared UI: Reuse
notedeck_uicomponents (note previews, media viewers) for consistency. Compose withNoteContextwhen rendering Nostr events. - Relay access pattern: Subscribe to the relevant Nostr kinds through
RelayPool, mirroring Columns’ subscription helpers or Dave’s targeted queries. - State & persistence: Store lightweight view state in your app struct; use
TimedSerializeronly if persisting user preferences. - Localization & theming: Wrap strings with
tr!, respectctx.style()for colors/fonts, and support narrow layouts.
README.mdfor project overview and crate map.crates/notedeck/DEVELOPER.mdfor core architecture, localization, caching.crates/notedeck_chrome/DEVELOPER.mdfor container lifecycle and theming.crates/notedeck_columns/DEVELOPER.mdfor timeline/deck architecture.crates/notedeck_dave/docs/*.mdfor agent-style tooling and streaming patterns.crates/notedeck_ui/docs/components.mdfor reusable widgets.
- Please make all commits logically distinct.
- Please make all commits standalone (i.e. so that they can be readily removed tens of commits later without impact the rest of the code).
- Related to logically distinct code, and standalone commits care must be taken for all code to be human readable, and reviewable by human developers.
- Please set up code for performance profiling utilizing puffin (e.g.
cargo run --release --features puffin). - Related to Puffin & performance profiling, for code suspected of impacting performance, carefully consider adding performance profiling attributes such as e.g. profiling::function in order to see functions performance in the profiler.
- Global variables are not allowed in this codebase, even if they are thread local. State should be managed in an struct that is passed in as reference.
- Inspect notedeck code for reusable components, elements, patterns etc. before creating new code for both A) notedeck updates, and B) apps built on notedeck.
- Nevernesting — favor early returns and guard clauses over deeply nested conditionals; simplify control flow by exiting early instead of wrapping logic in multiple layers of
ifstatements. - Do not fudge CI tests, in order to get a commit or PR to pass. Instead identify the underlying root cause of CI failure, and address that.
- Before proposing changes, please review and analyze if a change or upgrade to nostrdb is beneficial to the change at hand.
- Ensure docstring coverage for any code added, or modified.
- Run cargo fmt, cargo clippy, cargo test.
- Do not vendor code. In cargo.toml replace the existing url with the fork that includes the new code. If vendoring is absolutely necessary you must present the case why no other options are feasible.
- Avoid Mutexes — prefer
poll_promise::Promisefor async results,Rc<RefCell<>>for single-threaded interior mutability, ortokio::sync::RwLockwhen cross-thread sharing is truly necessary. Mutexes can cause UI stalls if held across frames. - Per-frame UI constraints — the UI runs every frame; never block the render loop. Use
Promise::ready()for non-blocking result checks. Offload CPU-heavy work toJobPoolortokio::spawn(), returning results via channels or Promises. - Cherry-pick commits — when incorporating work from other branches or contributors, use
git cherry-pickto preserve original authorship rather than copying code manually. - Frame-aware animations — for animations (GIFs, video), track
repaint_attimestamps and only request repaints when necessary; avoid spinning every frame.
Use this guide as a launchpad when extending Notedeck with new agents or protocol features. It highlights where to attach new functionality without duplicating existing infrastructure.