notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 494e0019afbb926ff619c65c9de53edff030074d
parent 7f6dd15f769d6d64b69583e45fffd8e2fd95cedd
Author: William Casarin <jb55@jb55.com>
Date:   Mon,  5 Jan 2026 12:23:47 -0800

Merge AGENTS.md from elsat

alltheseas (22):
      Add AGENTS.md agent overview
      Add guidelines for inspecting notedeck code
      AGENTS.md: added global variables as anti-pattern
      AGENTS.md: added requirement for human readable and reviewable code
      AGENTS.md: add nevernesting requirement
      docs: add coding pattern for cherry-picking commits
      docs: add coding pattern for frame-aware animations
      docs: add coding pattern for Mutex avoidance
      docs: add coding pattern for per-frame UI constraints
      docs: add Concurrency & Thread Safety section
      docs: add profiling annotation guidance
      docs: remove outdated job pool references
      docs: update Async & Background Work section
      Update AGENTS.md
      Updated AGENTS.md with coding patterns section
      Updated coding requirements in AGENTS.MD

Diffstat:
AAGENTS.md | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+), 0 deletions(-)

diff --git a/AGENTS.md b/AGENTS.md @@ -0,0 +1,126 @@ +# Notedeck Agent Development Overview + +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. + +## Repository Topology + +- **`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. + +## Core Abstractions & Patterns + +### Application Host + +- **`App` trait** (`crates/notedeck/src/app.rs`): Apps implement `update(&mut self, &mut AppContext, &mut egui::Ui) -> AppResponse` to drive egui rendering and signal high-level actions (`AppAction` for route changes, chrome toggles, etc.). +- **`Notedeck` struct** (`crates/notedeck/src/app.rs`) owns global resources—NostrDB connection, caches, relay pool, accounts, zaps, localization, clipboard, frame history—and injects them through `AppContext`. +- **`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. +- **`AppResponse`** carries optional actions and drag targets; chrome inspects it to react to app-level intent. + +### UI Container & Navigation + +- **Chrome shell** (`crates/notedeck_chrome/src/chrome.rs`): wraps multiple `App` instances, draws sidebar navigation, and forwards egui `update` passes to the active app. +- **`NotedeckApp` enum** (`crates/notedeck_chrome/src/app.rs`) defines the shipping app roster (Columns/Damus, Dave, others) and provides constructors for wiring new apps. +- **Preview system & theming** (`crates/notedeck_chrome/src/preview.rs`, `crates/notedeck_chrome/src/theme.rs`) centralize look-and-feel, font loading, and debug previews. + +### Concurrency & Thread Safety + +- **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**: When truly needed, prefer `Arc<tokio::sync::RwLock<>>` over `Arc<Mutex<>>`. The codebase has only 3 Mutex instances total (image cache size tracking, JobPool internals, test-only code). +- **Promise pattern**: Wrap async work in `poll_promise::Promise`, check with `promise.ready()` or `promise.ready_mut()` each frame—no blocking. + +### Nostr Data & Networking + +- **Database**: `nostrdb::Ndb` is 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::RelayPool` is shared in `AppContext`. Apps enqueue filters, process `RelayEvent`s, and update timelines. +- **Subscriptions**: Columns crate layers `Subscriptions`, `MultiSubscriber`, and `TimelineCache` to fan out relay queries per column. Unknown IDs are resolved lazily and retried until satisfied. +- **Debouncing & persistence**: `TimedSerializer` + `Debouncer` persist settings/state without hammering the filesystem (`crates/notedeck/src/timed_serializer.rs`). + +### UI Composition + +- **Immediate-mode UI**: All apps render with `egui`, respecting the host’s `Context` for theming and input. +- **Shared components** (`crates/notedeck_ui`): + - `NoteView` bundles author header, body, media, and action bar with configurable `NoteOptions`. + - Profile widgets (`ProfilePic`, `ProfilePreview`), media viewers, mention chips, and timeline helpers keep rendering consistent. +- **Columns-specific layout**: `Damus` app (`crates/notedeck_columns/src/app.rs`) manages decks, per-column routers, timeline hydration, and keyboard navigation. It uses `StripBuilder` and custom panels for multi-column flows. +- **Chrome** handles responsive breakpoints (e.g., `ui::is_narrow`) to switch layouts for mobile widths. + +### Async & Background Work + +- **Promise-based async** (`poll_promise::Promise`): The dominant pattern for async work. Promises are polled via `promise.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 via `tokio::sync::oneshot` wrapped in Promises. +- **Tokio tasks**: Network I/O, wallet operations, and relay sync use `tokio::spawn()`. Use `tokio::task::JoinSet` when managing multiple concurrent tasks. +- **Dave async**: Streams AI tokens through channels, spawns tasks with `tokio::spawn`, and updates the UI as chunks arrive—see `crates/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, Styling, Persistence + +- **Localization**: `tr!`/`tr_plural!` macros (documented in `crates/notedeck/DEVELOPER.md`) normalize strings into Fluent keys. `LocalizationManager` caches translations; locale is saved via `SettingsHandler`. +- **Themes & fonts**: `ColorTheme`, `NamedFontFamily`, and theme builders ensure consistent typography and support OLED dark mode. +- **Settings & tokens**: `SettingsHandler` stores theme, zoom, locale, and textual toggles; `TokenHandler` persists auth tokens safely. + +### Dave Agent Patterns (Template for Future Agents) + +- **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 `mpsc` channels 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. + +## Coding Conventions & Practices + +- Rust 2021, edition-lints are strict; clippy `disallowed_methods` is 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 `tracing` macros for structured logging and `profiling` scopes 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`/`horizontal` for layout. +- Persist state via `TimedSerializer::try_save` to avoid blocking the frame; batch mutations with `SettingsHandler::update_batch`. +- Tests live alongside modules (e.g., `JobPool`), often using `#[tokio::test]` when async behavior is involved. +- Localization updates: run `python3 scripts/export_source_strings.py` after changing user-facing strings; translators rely on the generated Fluent files. + +## Integrating New Agents + +1. **Prototype as an App**: Implement the `App` trait, using `AppContext` to read from `Ndb`, inspect accounts, and access localization. +2. **Register in Chrome**: Add a variant to `NotedeckApp`, supply icon/label metadata, and hook it into the sidebar. +3. **Leverage shared UI**: Reuse `notedeck_ui` components (note previews, media viewers) for consistency. Compose with `NoteContext` when rendering Nostr events. +4. **Relay access pattern**: Subscribe to the relevant Nostr kinds through `RelayPool`, mirroring Columns’ subscription helpers or Dave’s targeted queries. +5. **State & persistence**: Store lightweight view state in your app struct; use `TimedSerializer` only if persisting user preferences. +6. **Localization & theming**: Wrap strings with `tr!`, respect `ctx.style()` for colors/fonts, and support narrow layouts. + +## Reference Material + +- `README.md` for project overview and crate map. +- `crates/notedeck/DEVELOPER.md` for core architecture, localization, caching. +- `crates/notedeck_chrome/DEVELOPER.md` for container lifecycle and theming. +- `crates/notedeck_columns/DEVELOPER.md` for timeline/deck architecture. +- `crates/notedeck_dave/docs/*.md` for agent-style tooling and streaming patterns. +- `crates/notedeck_ui/docs/components.md` for reusable widgets. + +## Notedeck Coding Patterns + +1. Please make all **commits logically distinct**. +2. 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). +3. Related to logically distinct code, and standalone commits care must be taken for all **code to be human readable, and reviewable by human developers**. +4. Please set up code for **performance profiling utilizing puffin** (e.g. `cargo run --release --features puffin`). +5. 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. +6. **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. +7. **Inspect notedeck code for reusable components, elements, patterns** etc. before creating new code for both A) notedeck updates, and B) apps built on notedeck. +8. **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 `if` statements. +9. **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. +10. Before proposing changes, please **review and analyze if a change or upgrade to nostrdb** is beneficial to the change at hand. +11. **Ensure docstring coverage** for any code added, or modified. +12. Run **cargo fmt, cargo clippy, cargo test**. +13. **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. +14. **Avoid Mutexes** — prefer `poll_promise::Promise` for async results, `Rc<RefCell<>>` for single-threaded interior mutability, or `tokio::sync::RwLock` when cross-thread sharing is truly necessary. Mutexes can cause UI stalls if held across frames. +15. **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 to `JobPool` or `tokio::spawn()`, returning results via channels or Promises. +16. **Cherry-pick commits** — when incorporating work from other branches or contributors, use `git cherry-pick` to preserve original authorship rather than copying code manually. +17. **Frame-aware animations** — for animations (GIFs, video), track `repaint_at` timestamps 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.