# Architecture
## Module overview
```
src/
├── main.rs Entry point, winit ApplicationHandler
├── app/ Application state, event routing
│ ├── mod.rs App struct, keyboard/pty/resize event handlers
│ └── config.rs User configuration (font, cols, rows, flags)
├── pty/ PTY management (forkpty, shell I/O)
│ ├── mod.rs
│ └── session.rs Session: spawn, write, resize, reader thread
├── ansi/ ANSI/VT escape sequence parsing
│ ├── mod.rs
│ └── parser.rs VTE wrapper, byte-to-terminal-state dispatch
├── terminal/ Terminal grid engine
│ ├── mod.rs
│ ├── cell.rs Cell data (char, color, attrs)
│ ├── grid.rs Screen buffer, cursor, scroll, clear
│ ├── scrollback.rs Scrollback ring buffer
│ ├── selection.rs Selection state
│ └── state.rs Active/primary screen state and terminal modes
├── renderer/ wgpu + glyphon GPU rendering
│ └── mod.rs Renderer struct, row cache, draw loop
├── input/ Input event handling
│ ├── mod.rs
│ ├── keyboard.rs Key-to-PTY-byte mapping
│ └── mouse.rs Mouse position to cell mapping
```
## Data flow (latency-optimized)
```
KeyPress (winit)
│ (event loop, no blocking)
▼
key_to_bytes()
│ (no allocs for common keys)
▼
PTY::write(&[u8])
│ (blocking write with retry loop)
▼
Shell (zsh) in PTY slave
│ (processes input asynchronously)
▼
PTY reader thread (poll)
│ (dedicated thread, non-blocking poll)
▼
EventLoopProxy::send_event(PtyOutput)
│ (bounded channel to main thread)
▼
Main thread: ansi::Parser::advance()
│ (updates TerminalState, modes, grid, scrollback, dirty rows)
▼
Grid (dirty row tracking)
│ coalesced request_redraw()
▼
about_to_wait (once per frame)
│ only if needs_redraw
▼
Renderer::draw()
│ 1. update only dirty row buffers
│ 2. build N text areas
│ 3. prepare + render (glyphon/wgpu)
│ 4. grid.clear_dirty()
▼
Surface::present() → vsync → Metal → Screen
```
**Threading**: One dedicated PTY reader thread. All rendering, grid mutation,
and input handling on the main (UI) thread. No locks or mutex contention
on the hot path — the reader thread sends events via `EventLoopProxy`
which is a lock-free queue.