README.md (6599B)
1 # NoteDeck UI Developer Documentation 2 3 This document provides an in-depth overview of the `notedeck_ui` architecture, components, and guidelines for development. 4 5 For a guide on some of our components, check out the [NoteDeck UI Component Guide](./components.md) 6 7 ## Architecture 8 9 The `notedeck_ui` crate is organized into modules that handle different aspects of the Nostr client UI: 10 11 ``` 12 notedeck_ui 13 ├── anim.rs - Animation utilities and helpers 14 ├── colors.rs - Color constants and theme definitions 15 ├── constants.rs - UI constants (margins, sizes, etc.) 16 ├── gif.rs - GIF rendering and playback 17 ├── icons.rs - Icon rendering helpers 18 ├── images.rs - Image loading, caching, and display 19 ├── lib.rs - Main export and shared utilities 20 ├── mention.rs - Nostr mention component (@username) 21 ├── note/ - Note display components 22 │ ├── contents.rs - Note content rendering 23 │ ├── context.rs - Note context menu 24 │ ├── mod.rs - Note view component 25 │ ├── options.rs - Note display options 26 │ └── reply_description.rs - Reply metadata display 27 ├── profile/ - Profile components 28 │ ├── mod.rs - Shared profile utilities 29 │ ├── name.rs - Profile name display 30 │ ├── picture.rs - Profile picture component 31 │ └── preview.rs - Profile hover preview 32 ├── username.rs - Username display component 33 └── widgets.rs - Generic widget helpers 34 ``` 35 36 ## Core Components 37 38 ### NoteView 39 40 The `NoteView` component is the primary way to display Nostr notes. It handles rendering the note content, profile information, media, and interactive elements like replies and zaps. 41 42 Key design aspects: 43 - Stateful widget that maintains rendering state through EGUI's widget system 44 - Configurable display options via `NoteOptions` bitflags 45 - Support for different layouts (normal and wide) 46 - Handles nested content (note previews, mentions, hashtags) 47 48 ```rust 49 // NoteView creation and display 50 let mut note_view = NoteView::new(note_context, cur_acc, ¬e, options); 51 note_view.show(ui); // Returns NoteResponse with action 52 ``` 53 54 ### Note Actions 55 56 The note components use a pattern where user interactions produce `NoteAction` enum values: 57 58 ```rust 59 pub enum NoteAction { 60 Note(NoteId), // Note was clicked 61 Profile(Pubkey), // Profile was clicked 62 Reply(NoteId), // Reply button clicked 63 Quote(NoteId), // Quote button clicked 64 Hashtag(String), // Hashtag was clicked 65 Zap(ZapAction), // Zap interaction 66 Context(ContextSelection), // Context menu selection 67 } 68 ``` 69 70 Actions are propagated up from inner components to the parent UI, which can handle navigation and state changes. 71 72 ### Media Handling 73 74 The media system uses a cache and promise-based loading system: 75 76 1. `MediaCache` stores loaded images and animations 77 2. `fetch_img` retrieves images from disk or network 78 3. `render_images` handles the loading states and display 79 80 For GIFs, the system: 81 1. Decodes frames using a background thread 82 2. Sends frames via channels to the UI thread 83 3. Manages animation timing for playback 84 85 ## Design Patterns 86 87 ### Widget Pattern 88 89 Components implement the `egui::Widget` trait for integration with EGUI: 90 91 ```rust 92 impl egui::Widget for ProfilePic<'_, '_> { 93 fn ui(self, ui: &mut egui::Ui) -> egui::Response { 94 render_pfp(ui, self.cache, self.url, self.size, self.border) 95 } 96 } 97 ``` 98 99 ### Builder Pattern 100 101 Components often use a builder pattern for configuration: 102 103 ```rust 104 let view = NoteView::new(context, account, ¬e, options) 105 .small_pfp(true) 106 .wide(true) 107 .actionbar(false); 108 ``` 109 110 ### Animation Helper 111 112 For interactive elements, the `AnimationHelper` provides standardized hover animations: 113 114 ```rust 115 let helper = AnimationHelper::new(ui, "animation_id", max_size); 116 let current_size = helper.scale_1d_pos(min_size); 117 ``` 118 119 ## Working with Images 120 121 Images are handled through different types depending on their purpose: 122 123 1. `ImageType::Profile` - For profile pictures (with automatic cropping and rounding) 124 2. `ImageType::Content` - For general content images 125 126 ```rust 127 // Loading and displaying an image 128 render_images( 129 ui, 130 img_cache, 131 url, 132 ImageType::Profile(128), // Size hint 133 MediaCacheType::Image, // Static image or GIF 134 |ui| { /* show while loading */ }, 135 |ui, err| { /* show on error */ }, 136 |ui, url, img, gifs| { /* show successful load */ }, 137 ); 138 ``` 139 140 ## Performance Considerations 141 142 1. **Image Caching**: Images are cached both in memory and on disk 143 2. **Animation Optimization**: GIF frames are decoded in background threads 144 3. **Render Profiling**: Critical paths use `#[profiling::function]` for tracing 145 4. **Layout Reuse**: Components cache layout data to prevent recalculation 146 147 ## Theming 148 149 The UI adapts to light/dark mode through EGUI's visuals system: 150 151 ```rust 152 // Access current theme 153 let color = ui.visuals().hyperlink_color; 154 155 // Check theme mode 156 if ui.visuals().dark_mode { 157 // Use dark mode resources 158 } else { 159 // Use light mode resources 160 } 161 ``` 162 163 ## Debugging Tips 164 165 1. **EGUI Inspector**: Use `ctx.debug_painter()` to visualize layout bounds 166 2. **Trace Logging**: Enable trace logs to debug image loading and caching 167 3. **Animation Debugging**: Set `ANIM_SPEED` to a lower value to slow animations for visual debugging 168 4. **ID Collisions**: Use unique IDs for animations and state to prevent interaction bugs 169 170 ## Common Patterns 171 172 ### Hover Previews 173 174 ```rust 175 // For elements with hover previews 176 let resp = ui.add(/* widget */); 177 resp.on_hover_ui_at_pointer(|ui| { 178 ui.set_max_width(300.0); 179 ui.add(ProfilePreview::new(profile, img_cache)); 180 }); 181 ``` 182 183 ### Context Menus 184 185 ```rust 186 // For elements with context menus 187 let resp = ui.add(/* widget */); 188 resp.context_menu(|ui| { 189 if ui.button("Menu Option").clicked() { 190 // Handle selection 191 ui.close_menu(); 192 } 193 }); 194 ``` 195 196 ## Contributing Guidelines 197 198 When contributing to `notedeck_ui`: 199 200 1. **Widget Consistency**: Follow established patterns for new widgets 201 2. **Option Naming**: Keep option names consistent (has_X/set_X pairs) 202 3. **Performance**: Add profiling annotations to expensive operations 203 4. **Error Handling**: Propagate errors up rather than handling them directly in UI components 204 5. **Documentation**: Document public APIs and components with examples 205 6. **Theme Support**: Ensure components work in both light and dark mode