Derive session calendars from event metadata and apply one visibility contract to month rendering, appointment focus, and search without filtering persistence writes. Add keyboard picker coverage, full-model save regression tests, documentation, warning-clean builds, sanitizer verification, and live PTY terminal restoration checks.
102 lines
5.8 KiB
Markdown
102 lines
5.8 KiB
Markdown
# Nocal product and interaction specification
|
|
|
|
Nocal is a local-first, keyboard-driven month calendar for Linux terminals. Its
|
|
visual target is the information density of a desktop month view without
|
|
pretending that a terminal is a pixel canvas. The terminal emulator owns the
|
|
typeface and palette; Nocal owns hierarchy, spacing, and restrained emphasis.
|
|
|
|
## Product principles
|
|
|
|
1. **The month is the home screen.** Launching Nocal immediately shows six
|
|
complete Monday-first weeks. There is no dashboard or splash screen.
|
|
2. **Useful at a glance.** Every visible day shows as many appointments as fit,
|
|
ordered as all-day first and then by start time. A final `+N more` line is
|
|
used instead of clipping silently.
|
|
3. **Keyboard-native.** Arrow keys and Vim keys move by day or week; month and
|
|
today jumps are single keystrokes. Every action remains discoverable in the
|
|
footer and help overlay.
|
|
4. **Terminal-native aesthetics.** Nocal uses default foreground/background and
|
|
the terminal's ANSI semantic colors. It never paints a fixed RGB theme over
|
|
the user's Kitty, foot, Alacritty, or WezTerm theme.
|
|
5. **Local-first, sync-ready.** The canonical domain model is independent of
|
|
iCalendar files and of any future remote API. Sync engines translate into
|
|
domain objects and never leak provider-specific types into the UI.
|
|
6. **Fast enough to feel instant.** Startup should remain below 50 ms for a
|
|
normal local calendar and redraw should be flicker-free at interactive
|
|
resize rates.
|
|
|
|
## Month view
|
|
|
|
The full terminal is treated as a responsive canvas:
|
|
|
|
```text
|
|
JULY 2026 Today Fri 17 Jul
|
|
Mon Tue Wed Thu Fri Sat Sun
|
|
┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┬────────────┐
|
|
│ 29 │ 30 │ 1 │ 2 │ 3 │ 4 │ 5 │
|
|
│ │ │ 09:30 Team │ │ All day … │ │ │
|
|
├────────────┼────────────┼────────────┼────────────┼────────────┼────────────┼────────────┤
|
|
│ … │
|
|
└─────────────────────────────────────────────────────────────────────────────────────────┘
|
|
←↓↑→ move PgUp/PgDn month t today ? help q quit 2 appointments
|
|
```
|
|
|
|
- At 100 columns and above, Nocal draws the full bordered grid.
|
|
- Narrow cells abbreviate times and ellipsize summaries by display width.
|
|
- When height is constrained, appointment lines are reduced before structural
|
|
chrome. At extremely small sizes a clear minimum-size message replaces a
|
|
broken grid.
|
|
- Days outside the focused month remain visible but dim.
|
|
- The selected date is reverse-video, today is bold/underlined, and collisions
|
|
use semantic ANSI accents. These attributes work on monochrome terminals.
|
|
- The footer reports the selected day's appointment count and the most useful
|
|
keys for the available width.
|
|
|
|
## Interaction map
|
|
|
|
| Action | Keys |
|
|
| --- | --- |
|
|
| Previous/next day | `Left` / `Right`, `h` / `l` |
|
|
| Previous/next week | `Up` / `Down`, `k` / `j` |
|
|
| Previous/next month | `PageUp` / `PageDown`, `p` / `n` |
|
|
| Jump to today | `t` |
|
|
| Next/previous appointment | `Tab` / `Shift-Tab` |
|
|
| Read focused appointment | `Enter` |
|
|
| Browse inside reader | arrows, `h j k l`, `Tab` / `Shift-Tab` |
|
|
| Add appointment | `a` |
|
|
| Edit focused appointment | `e` |
|
|
| Delete focused appointment | `d`, then `y` to confirm |
|
|
| Undo/redo successful mutation | `u` / `Ctrl-R` |
|
|
| Move between editor fields | `Tab` / `Shift-Tab`, arrows |
|
|
| Save/cancel editor | `Ctrl-S` / `Esc` |
|
|
| Return to month/unfocus | `Esc` |
|
|
| Search appointments | `/`, then type and press `Enter` |
|
|
| Choose search result | arrows, `j` / `k`, then `Enter` |
|
|
| Toggle calendar visibility | `c`, arrows or `j` / `k`, then `Enter` |
|
|
| Help | `?` |
|
|
| Quit | `q` |
|
|
|
|
The first usable foundation supports day and appointment navigation, a
|
|
full-frame reader, validated add/edit/delete forms, and atomic `.ics` writes.
|
|
Dense days keep every appointment keyboard-reachable even when all lines do
|
|
not fit in the cell. Unknown or unsupported iCalendar content makes a source
|
|
read-only instead of being discarded. Saves reject external changes, retain a
|
|
last-known-good backup, and feed bounded session undo/redo history. Supported
|
|
recurring instances appear throughout the month, preserve civil time across
|
|
daylight-saving changes, and expose their source zone and whole-series mutation
|
|
semantics in the reader. `/` searches occurrence-aware title, description,
|
|
location, and calendar-ID matches in a finite ten-year window centered on the
|
|
selected date. `c` toggles session-only calendar visibility consistently across
|
|
the grid, appointment focus, and search without filtering the model supplied to
|
|
persistence. The next local-data work adds an agenda view, then advanced
|
|
recurrence overrides and rule editing.
|
|
|
|
## Accessibility
|
|
|
|
- Information is never encoded by color alone.
|
|
- `NO_COLOR` and terminals without color remain fully usable.
|
|
- Borders and icons have ASCII fallbacks; Unicode width is calculated rather
|
|
than assumed.
|
|
- Focus is always visible and keyboard operation is complete.
|
|
- Motion is limited to direct redraws; there are no decorative animations.
|