| = | add task to the selected session (name → optional duration) |
| + _ ) | zoom in / zoom out / reset to 1× (0.5×–4× row height; ) is shift+0; the selected row stays put) |
| - | add a 5-minute break immediately (no prompt); press again to grow the selected break by 5 minutes — . sets a manual duration |
| ↑ ↓ | navigate items and session binding rows — starts on the selected day, keeps going into neighbouring days and the selected day follows (never scrolls) |
| ← → | move the selected day (the highlighted date in the header) |
| PgUp PgDn | scroll the vertical viewport one page |
| wheel | scroll the viewport naturally (1:1 with the wheel, at any zoom; 1 row = 15 min) |
| Enter | edit name of the selected item / rename the session — the current name is pre-filled, so just edit it |
| . | edit duration (the current one is pre-filled; a bare ±shift adjusts it relatively, or suffix it on a value: 1h +30m) or the time of a binding (9:30 am, 9:30 am +1d, or a bare ±shift to adjust the current time relatively — +2d, -2h, +30m, any duration; an explicit offset always wins over the night-shift convenience) |
| [ | create a start-only session: start time only (empty = now) — cap it later with ] on its start row. A time before a selected end-only session's end closes it instead; at/after creates a new start-only. A bare hour without am/pm (e.g. 9:30) takes the current am/pm |
| ] | end time (default now + 2h) — merges with the previous start-only session if one is adjacent, else a standalone end-only session |
| ; | toggle split/join of the selected session at the current row (an item-less session splits into a start-only and an end-only one) |
| / | split the selected task (tasks with a duration only) |
| ⌫ | delete item; on a binding row delete the session (refused if it has items) |
| Shift+↑ Shift+↓ | reorder the selected item within its session |
| Shift+← Shift+→ | move the selected item to the previous / next session |
| ⌘+↑ ⌘+↓ | jump to the first / last item of the current session (Ctrl+arrow on Windows/Linux) |
| ⌘+← ⌘+→ | jump to the previous / next session in time order |
| ⌘+Shift+↑ ⌘+Shift+↓ | move the selected item to the top / bottom of its session |
| ⌘+Shift+← ⌘+Shift+→ | move the selected item to the previous / next session |
| \ | | undo · redo (255 deep) |
| Shift+E | export the active plan as a downloadable .svg with the plan JSON embedded (re-importable) |
| Shift+I | import a plan from an exported .svg (or a plan .json) — opens the file picker; the plan is added as a new plan |
| top bar | plan management: the dropdown switches plans, + creates one, 🗑 deletes the current one (asks to confirm) — every plan lives separately in local storage and autosaves |
| ? | this help |
| Esc | cancel prompt · close help |
Sessions are spans, not days: a night shift 10:00 pm → 6:00 am is one session whose frame crosses the day
boundary (↘ / ↖ cont.). Uncapped sessions stack until they hit a neighbour's binding;
running into an opposite uncapped session adds the missing binding automatically — one undo covers it.
Clicking a row selects it (the selected day follows the click); clicking a day header or
empty column just moves the selected day. State lives in the browser's
local storage — multiple plans, switchable from the top bar, with no
server round-trips after the page loads. export downloads the active
plan as an .svg with its JSON embedded; import brings such a file
(or a plan .json) back in as a new plan.