Add a Monday-default --week-start option for every weekday and carry it through domain layout, full and compact rendering, event query windows, print mode, and the interactive TUI. Preserve byte-compatible Monday output and cover rotated headers, spill dates, invalid input, sanitizer behavior, and live terminal restoration.
138 lines
8.1 KiB
Markdown
138 lines
8.1 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 weeks, Monday-first by default. `--week-start` can rotate the grid
|
|
to any weekday. 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` |
|
|
| Open/close 42-day agenda | `g`; `Esc` also closes |
|
|
| Choose agenda occurrence | arrows, `j` / `k`, `Tab` / `Shift-Tab`, then `Enter` |
|
|
| Shift agenda by 42 days | `PageUp` / `PageDown`, `p` / `n` |
|
|
| 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, a read-only 42-day agenda, validated add/edit/delete forms,
|
|
atomic `.ics` writes, and explicit non-interactive calendar import/export.
|
|
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. `g` opens an occurrence-aware agenda at the selected month date;
|
|
navigation is bounded to consecutive 42-day windows and `Enter` returns to the
|
|
exact occurrence in the month grid. `Esc` or `g` closes the agenda without
|
|
changing the month selection unless an occurrence was chosen. Hidden calendars
|
|
remain filtered there, while calendar selection and search can overlay the
|
|
agenda. Agenda navigation never persists state, and add/edit/delete remain in
|
|
the month workflow. Week-start selection rotates only the month grid, header,
|
|
and the visible range used by its month queries; storage, agenda windows, and
|
|
recurrence rules are unchanged. The default remains Monday for backward
|
|
compatibility. The CLI accepts the exact lowercase values `monday`, `tuesday`,
|
|
`wednesday`, `thursday`, `friday`, `saturday`, and `sunday`. The next local-data
|
|
work adds advanced recurrence overrides and rule editing.
|
|
|
|
## Local file interchange
|
|
|
|
Import and export are explicit command-line workflows. They never contact the
|
|
network and do not enter the interactive TUI.
|
|
|
|
- `nocal --import SOURCE --calendar TARGET` atomically merges events into the
|
|
target. Exact same-UID, same-field events are duplicates and are skipped. A
|
|
differing same-UID event, an empty or duplicate UID or invalid event interval
|
|
in either file, or content in either file that cannot be safely round-tripped
|
|
rejects the complete import before the target is written. A successful
|
|
merge keeps the target's exact previous bytes as its adjacent `.bak`; a
|
|
duplicate-only import leaves both target and backup untouched.
|
|
- `nocal --export DESTINATION --calendar SOURCE` validates the source and
|
|
atomically creates a canonical calendar. It never overwrites an existing
|
|
destination and refuses any source whose events are invalid or that cannot
|
|
be safely round-tripped.
|
|
|
|
The two operations are mutually exclusive and cannot be combined with demo,
|
|
frame printing, or backup restoration. These restrictions keep destructive
|
|
ambiguity out of automation and make every file-writing mode deliberate.
|
|
|
|
## 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.
|