esync

Directory watching and remote syncing
Log | Files | Refs | README | LICENSE

2026-03-01-tui-improvements-design.md (3620B)


      1 # TUI Improvements Design
      2 
      3 ## Problems
      4 
      5 1. **"Syncing" events pollute the event list.** The handler sends `{File: ".", Status: "syncing"}` before every rsync run. These pile up as permanent `⟳ .  syncing...` rows and never clear.
      6 
      7 2. **Per-file events don't scale.** A sync transferring 1000 files would produce 1000 event rows, overwhelming the list and slowing TUI updates.
      8 
      9 3. **Event list doesn't fill the terminal.** The visible row count uses a hardcoded `height-10` offset. Tall terminals waste space; short terminals clip.
     10 
     11 4. **No scrolling or timestamps.** Events are a flat, non-navigable list with no time information.
     12 
     13 5. **`r` (full resync) is a dead key.** Shown in the help bar but has no handler.
     14 
     15 6. **Stats bar shows 0.** The `totalSynced` / `totalBytes` / `totalErrors` counters are never updated because nothing sends `SyncStatsMsg`.
     16 
     17 ## Design
     18 
     19 ### Syncing indicator: transient header status
     20 
     21 Remove "syncing" events from the event list. Add a new message type `SyncStatusMsg string` that updates only the header status line. The handler sends `SyncStatusMsg("syncing")` before rsync runs and `SyncStatusMsg("watching")` after. No syncing rows appear in the event list.
     22 
     23 ### Top-level grouping of file events
     24 
     25 After rsync completes, the handler in `cmd/sync.go` groups `result.Files` by top-level path component:
     26 
     27 - Files in subdirectories are grouped by their first path segment. `cmd/sync.go` + `cmd/init.go` + `cmd/root.go` become one event: `✓ cmd/  3 files  12.3KB`.
     28 - Files at the root level get individual events: `✓ main.go  2.1KB`.
     29 
     30 Grouping happens in the handler after rsync returns, so it adds no overhead to the transfer. The TUI receives at most `N_top_level_dirs + N_root_files` events per sync.
     31 
     32 ### Event list fills terminal, scrollable with timestamps
     33 
     34 **Layout**: compute available event rows as `height - 6`:
     35 - Header: 3 lines (title, paths, status + blank)
     36 - Stats + help: 3 lines
     37 
     38 Pad with empty lines when fewer events exist so the section always fills.
     39 
     40 **Timestamps**: each event row includes `HH:MM:SS` from `evt.Time`:
     41 ```
     42   15:04:05  ✓ cmd/                   3 files  12.3KB  120ms
     43   15:04:05  ✓ main.go                          2.1KB  120ms
     44   15:03:58  ✓ internal/              5 files  45.2KB  200ms
     45 ```
     46 
     47 **Scrolling**: add `offset int` to `DashboardModel`. `j`/`k` or `↑`/`↓` move the viewport. The event list is a window into `filteredEvents()[offset:offset+viewHeight]`.
     48 
     49 ### `r` triggers full resync
     50 
     51 Add a `resyncCh chan struct{}` to `AppModel`, exposed via `ResyncChan()`. When the user presses `r`, the dashboard emits a `ResyncRequestMsg`. AppModel catches it and sends on the channel. The handler in `cmd/sync.go` listens on `resyncCh` in a goroutine and calls `s.Run()` when signalled, feeding results back through the existing event channel.
     52 
     53 ### Stats bar accumulates
     54 
     55 The handler updates running totals (`totalSynced`, `totalBytes`, `totalErrors`) after each sync and sends a `SyncStatsMsg`. The dashboard renders these in the stats section.
     56 
     57 ## Event row format
     58 
     59 ```
     60   HH:MM:SS  icon  name(padded)  detail      size     duration
     61   15:04:05  ✓     cmd/          3 files    12.3KB     120ms
     62   15:04:05  ✓     main.go                   2.1KB     120ms
     63   15:04:05  ✗     internal/     error       ─          ─
     64 ```
     65 
     66 ## Files to change
     67 
     68 - `internal/tui/dashboard.go` — timestamps, scrolling, fill terminal, remove syncing events
     69 - `internal/tui/app.go` — new message types (`SyncStatusMsg`, `ResyncRequestMsg`), resync channel
     70 - `cmd/sync.go` — top-level grouping, stats accumulation, resync listener, remove syncing event send