5.2 KiB
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
- The month is the home screen. Launching Nocal immediately shows six complete Monday-first weeks. There is no dashboard or splash screen.
- 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 moreline is used instead of clipping silently. - 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.
- 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.
- 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.
- 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:
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 |
| 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. The next
local-data work adds / search, calendar visibility toggles, and recurring-event
and time-zone expansion.
Accessibility
- Information is never encoded by color alone.
NO_COLORand 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.