|
| 1 | +# Architecture |
| 2 | + |
| 3 | +**Analysis Date:** 2026-01-13 |
| 4 | + |
| 5 | +## Pattern Overview |
| 6 | + |
| 7 | +**Overall:** Plugin-based architecture with layered state management and event-driven processing |
| 8 | + |
| 9 | +**Key Characteristics:** |
| 10 | +- Zellij plugin using WASM (WebAssembly) with WASI sandbox |
| 11 | +- Event-driven: Zellij sends events → Plugin processes → State updates → UI renders |
| 12 | +- Single executable with two-screen UI (Main list, NewSession creation) |
| 13 | +- Stateless request handling within plugin instance |
| 14 | + |
| 15 | +## Layers |
| 16 | + |
| 17 | +**Plugin Layer:** |
| 18 | +- Purpose: Zellij plugin trait implementation, event dispatcher |
| 19 | +- Contains: `ZellijPlugin` trait methods (`load`, `update`, `pipe`, `render`) |
| 20 | +- Location: `src/main.rs` |
| 21 | +- Depends on: State layer for coordination |
| 22 | +- Used by: Zellij runtime |
| 23 | + |
| 24 | +**State Layer:** |
| 25 | +- Purpose: Central state management, business logic coordination |
| 26 | +- Contains: `PluginState` struct, key handlers, item combination logic |
| 27 | +- Location: `src/state.rs` |
| 28 | +- Depends on: Config, Session, Zoxide, NewSessionInfo, UI modules |
| 29 | +- Used by: Plugin layer |
| 30 | + |
| 31 | +**Session Layer:** |
| 32 | +- Purpose: Zellij session operations, lifecycle management, stability tracking |
| 33 | +- Contains: `SessionManager`, `SessionItem`, `SessionAction` |
| 34 | +- Location: `src/session/manager.rs`, `src/session/types.rs` |
| 35 | +- Depends on: No internal dependencies (isolated) |
| 36 | +- Used by: State layer |
| 37 | + |
| 38 | +**Zoxide Layer:** |
| 39 | +- Purpose: Directory discovery and fuzzy search |
| 40 | +- Contains: `ZoxideDirectory`, `SearchEngine` |
| 41 | +- Location: `src/zoxide/directory.rs`, `src/zoxide/search.rs` |
| 42 | +- Depends on: Session types (for `SessionItem`) |
| 43 | +- Used by: State layer |
| 44 | + |
| 45 | +**UI Layer:** |
| 46 | +- Purpose: Terminal rendering |
| 47 | +- Contains: `PluginRenderer`, `Theme`, `Colors`, components |
| 48 | +- Location: `src/ui/renderer.rs`, `src/ui/theme.rs`, `src/ui/components.rs` |
| 49 | +- Depends on: Session types, State for display data |
| 50 | +- Used by: Plugin layer (render method) |
| 51 | + |
| 52 | +**Configuration Layer:** |
| 53 | +- Purpose: Zellij layout-based configuration parsing |
| 54 | +- Contains: `Config` struct |
| 55 | +- Location: `src/config.rs` |
| 56 | +- Depends on: None |
| 57 | +- Used by: State layer |
| 58 | + |
| 59 | +## Data Flow |
| 60 | + |
| 61 | +**Plugin Initialization:** |
| 62 | + |
| 63 | +1. `ZellijPlugin::load()` in `src/main.rs:16` → Registers plugin with Zellij |
| 64 | +2. Requests permissions (RunCommands, ReadApplicationState, ChangeApplicationState, MessageAndLaunchOtherPlugins) |
| 65 | +3. Subscribes to events (ModeUpdate, SessionUpdate, Key, RunCommandResult, PermissionRequestResult) |
| 66 | + |
| 67 | +**Permission Grant & Zoxide Query:** |
| 68 | + |
| 69 | +1. User receives PermissionRequestResult → `update()` in `src/main.rs:50` |
| 70 | +2. `fetch_zoxide_directories()` runs `zoxide query -l -s` command |
| 71 | +3. Results returned via RunCommandResult event |
| 72 | + |
| 73 | +**Zoxide Output Processing:** |
| 74 | + |
| 75 | +1. `process_zoxide_output()` in `src/main.rs:161` parses score+path lines |
| 76 | +2. `generate_smart_session_names()` creates context-aware names (`src/main.rs:197-235`) |
| 77 | +3. Names conflict-resolved with parent context and truncated to 29 chars |
| 78 | +4. `update_zoxide_directories()` in `src/state.rs:102` stores directories |
| 79 | + |
| 80 | +**Session Updates:** |
| 81 | + |
| 82 | +1. Zellij sends SessionUpdate event with current and resurrectable sessions |
| 83 | +2. `update_sessions()` in `src/state.rs:57` uses stability tracking (MISSING_THRESHOLD=3) |
| 84 | +3. Prevents UI flickering from Zellij's inconsistent event timing |
| 85 | + |
| 86 | +**User Interaction:** |
| 87 | + |
| 88 | +1. Key event in `update()` → `handle_key()` in `src/state.rs:108` |
| 89 | +2. Routes to main screen or new session screen handlers |
| 90 | +3. Actions trigger session switches, deletions, or new session creation |
| 91 | + |
| 92 | +**State Management:** |
| 93 | +- File-based state for previous session: `/tmp/zsm-previous-session` |
| 94 | +- Each plugin instance has isolated state (per Zellij session) |
| 95 | +- State does not transfer between sessions automatically |
| 96 | + |
| 97 | +## Key Abstractions |
| 98 | + |
| 99 | +**SessionManager:** |
| 100 | +- Purpose: Orchestrates Zellij session operations with stability tracking |
| 101 | +- Examples: `update_sessions_stable()`, `execute_action()`, `generate_incremented_name()` |
| 102 | +- Location: `src/session/manager.rs` |
| 103 | +- Pattern: Stability threshold (MISSING_THRESHOLD=3) prevents UI flicker from inconsistent Zellij events |
| 104 | + |
| 105 | +**SessionItem:** |
| 106 | +- Purpose: Represents displayable items in the session list |
| 107 | +- Examples: `ExistingSession`, `ResurrectableSession`, `Directory` |
| 108 | +- Location: `src/session/types.rs` |
| 109 | +- Pattern: Enum with variant data |
| 110 | + |
| 111 | +**SearchEngine:** |
| 112 | +- Purpose: Fuzzy matching for search functionality |
| 113 | +- Examples: `search()`, `update_search()`, `get_results()` |
| 114 | +- Location: `src/zoxide/search.rs` |
| 115 | +- Pattern: Uses `SkimMatcherV2` from `fuzzy-matcher` crate |
| 116 | + |
| 117 | +**PluginState:** |
| 118 | +- Purpose: Central orchestrator holding all plugin state |
| 119 | +- Examples: `combined_items()`, `display_items()`, `handle_key()` |
| 120 | +- Location: `src/state.rs` |
| 121 | +- Pattern: Singleton state container, coordinates between all layers |
| 122 | + |
| 123 | +## Entry Points |
| 124 | + |
| 125 | +**Plugin Entry:** |
| 126 | +- Location: `src/main.rs:13` - `register_plugin!(PluginState)` macro |
| 127 | +- Triggers: Zellij loads plugin WASM binary |
| 128 | +- Responsibilities: Register plugin with Zellij runtime |
| 129 | + |
| 130 | +**ZellijPlugin Implementation:** |
| 131 | +- Location: `src/main.rs:15-152` |
| 132 | +- Triggers: Zellij events (permissions, keys, session updates) |
| 133 | +- Responsibilities: Event dispatch, zoxide command execution, rendering |
| 134 | + |
| 135 | +**Renderer Entry:** |
| 136 | +- Location: `src/ui/renderer.rs:14` |
| 137 | +- Triggers: Zellij render call |
| 138 | +- Responsibilities: Determine active screen, render UI, display overlays |
| 139 | + |
| 140 | +## Error Handling |
| 141 | + |
| 142 | +**Strategy:** Uses `Option<T>` for nullable values, UI error display for user feedback |
| 143 | + |
| 144 | +**Patterns:** |
| 145 | +- Validation errors shown via `.set_error()` method (`src/state.rs:26`) |
| 146 | +- Error cleared on next keypress |
| 147 | +- Permission denial shows error, prevents zoxide fetch |
| 148 | +- Invalid session names blocked with descriptive messages |
| 149 | + |
| 150 | +## Cross-Cutting Concerns |
| 151 | + |
| 152 | +**Logging:** |
| 153 | +- Plugin logs to Zellij's plugin log output |
| 154 | +- No external logging framework |
| 155 | + |
| 156 | +**Validation:** |
| 157 | +- Session name validation at creation time (`src/state.rs:614-622`) |
| 158 | +- Max 108 bytes, no `/` characters |
| 159 | +- Path validation for zoxide results |
| 160 | + |
| 161 | +**Smart Session Naming:** |
| 162 | +- Complex algorithm spanning `src/main.rs:197-502` |
| 163 | +- Conflict detection, context-aware naming, truncation to 29 chars |
| 164 | +- Respects Unix socket path limits |
| 165 | + |
| 166 | +**WASM Sandbox Handling:** |
| 167 | +- Direct filesystem writes use sandboxed paths |
| 168 | +- Shelling out via `run_command` for persistent file operations |
| 169 | +- Inter-plugin communication via `pipe_message_to_plugin` |
| 170 | + |
| 171 | +--- |
| 172 | + |
| 173 | +*Architecture analysis: 2026-01-13* |
| 174 | +*Update when major patterns change* |
0 commit comments