aboutsummaryrefslogtreecommitdiff
path: root/docs/TUI-GUIDELINES.md
blob: a54fef8ce8cd16fe16efc487fd6680bbf0ed43fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# bokftui guidelines

Rules for the ncurses client so every view behaves the same. When in doubt,
copy the behaviour of the voucher list / voucher form; they are the reference
implementations. Inspired by Midnight Commander, htop, mutt and calcurse.

## Session start

After login the org picker ("Välj organisation att representera") is always
shown; the selected org is fixed for the session (`--org ID` bypasses it for
scripts). The fiscal year is chosen from the dashboard and is changeable
during the session; the `Räkenskapsår` screen also closes and reopens years
there.

## Universal keys

| Key | Meaning |
|---|---|
| `Ctrl+N` | Add: new verifikat (dashboard, voucher list/detail), new mall (Mallar), new fiscal year (year picker), open editor (IB), upload file (Underlag) |
| `F5` | Refresh the view |
| `Esc` / `q` | Back one level. At the dashboard it does nothing — Esc never exits the app |
| `Ctrl+C` | Quit the application (closes the session). The only key that exits |
| `Ctrl+R` | Reload the client in place: re-execs the installed binary and restores session, org, fiscal year and the current view (for after a rebuild) |
| `Ctrl+A` | Close or reopen the highlighted räkenskapsår (Räkenskapsår screen; asks for confirmation) |
| `1`–`9` | In lists: jump to that row. In menus: activate that item |
| `g` | Live goto: "Gå till rad/nummer:" updates the selection as you type digits (backspace steps back); Enter closes the prompt without opening anything |
| arrows, PgUp/PgDn, Home/End | Move/scroll; selection always stays visible |
| `e` | Edit the shown object (IB, where applicable) |
| `a` | Add/upload (Underlag) |
| `c` | Correct (voucher detail) |
| `d` | Delete/arkivera the selected row (only where the action exists; asks for confirmation) |
| `f` | Voucher detail: list the voucher's underlag — Enter fetches, `d` removes the link (asks first). Underlag: Enter fetches |
| `Ctrl+F` | Attach a file via the file browser (voucher form and voucher detail) |
| `k` | Underlag: link the highlighted attachment to a voucher picked from a list |
| `Ctrl+X` | Clear the current row — only inside row editors (never "new") |
| `Ctrl+Enter` | Save/post the current form. Enabled via xterm `modifyOtherKeys`; terminals that cannot send it keep `F9` working |

Every screen prints its keys in the footer via `hints()`. If a key exists, the
footer shows it; if the footer shows it, the key works. Control keys are
written compactly as `^N`, `^A`, `^C`, `^R` to save width.

## Lists (`select_list`, `menu`)

- Rows are numbered `NN. text`, right-aligned so 2- and 3-digit numbers line up.
- Verifikation ids are shown concatenated as `series+number` (`V-8`, `A8`),
  using the org's `default_series` (Inställningar) for new vouchers.
- The last row may be an action (e.g. `+ Nytt verifikat   (Ctrl+N)`); selecting it
  runs the action instead of opening a detail view.
- Selection memory: lists remember the selected row by identity (voucher id),
  not index, across detail round-trips, refreshes and screen re-entry.
- Digits move the highlight; only Enter activates. Menus are the exception:
  digits activate directly (they are shortcuts).
- Empty lists are never a dead end: show a message or keep the add-row.

## Forms

- Layout: bold header fields at the top, then a bold column header, then rows;
  footer hints carry the keys.
- Field navigation: `Tab` / `Shift-Tab` forward/back, arrows Up/Down between
  rows. `Enter` advances in simple forms; it never saves unless the screen is
  a one-line prompt.
- The active field is drawn reverse-video and holds the hardware cursor at the
  caret. The caret is a real position: ←/→/Home/End/Del work inside the field,
  `Ctrl+U` clears it (see `field_edit`).
- First keystroke in a freshly focused field replaces its content
  (`field_fresh`), so prefilled values like dates can be typed over.
- Date fields (`date_field_edit`, `date_prompt`) accept digits only and insert
  the dashes themselves: type `20260315` and the field shows `2026-03-15`.
  Backspace deletes a digit (with its separator), ←/→/Home/End move by digit,
  and the caret renders like in any other field. Validation (`util_parse_iso_date`)
  happens on F5/`^Enter`.
- Derived values (account names, balances) are dim and non-editable.
- Row tables: always exactly one empty trailing row; entering data appends a
  new empty row; an empty row followed by another empty row collapses.
  `Ctrl+X` clears the selected row.
- Validation: `F5` validates without writing and reports exactly what is wrong
  (field, row number). Server `dry_run` is used where available.
- Saving: `Ctrl+Enter` writes, shows a confirmation message, and returns to
  the previous view. `Esc` cancels without saving.
- File browser (`file_browser`): starts in the org's `attachment_dir` (or
  `$HOME`; a leading `~` is expanded to `$HOME`), `.. (uppåt)` is the first row, directories sort first with a
  trailing `/`, hidden files are skipped. Enter enters a directory or picks a
  file; `Esc` cancels. Selected files are uploaded immediately as unlinked
  underlag and linked when the voucher is posted.

## Messages

- Info/confirmation: `message(title, ...)` box, dismissed with Enter.
- Quitting: only `Ctrl+C` (from anywhere) or "Logga ut / avsluta" ends the
  app; `Esc`/`q` only navigate. After `Ctrl+C` every screen unwinds, the
  session is closed and the terminal restored.
- Errors: `show_error(title, resp)` prints `CODE: message` from the server
  error object; if the response is missing/empty it says
  "Inget svar från servern (kör daemonen?)". Never render an empty error.
- Irreversible actions (close year, lock, archive) ask first.

## Layout and text

- Screen frame: title top-left, dim status line under it
  (`org | start - end | role | user`), hints in the last line.
- Columns are padded with `pad_field()` (UTF-8 display width) and truncated to
  their column; amounts right-aligned via `kr_format()`; never pad with `%s`
  widths directly on user text.
- Selection = reverse video, headers = bold, derived data = dim.
- All user-visible TUI text is Swedish; server messages are English.

## Adding a view — checklist

1. Data comes from public protocol commands only.
2. Wrap the screen in `frame()`/`hints()`; return `Esc`/`q` to the parent.
3. Use `menu()` or `select_list()` instead of writing a new loop; pass
   `allow_new`/`allow_refresh` so the universal keys apply.
4. Forms use the shared editor (`field_edit`) and the row-normalising helpers.
5. Support `F5` if the data can change elsewhere.
6. Update `PROTOCOL.md` §8 and this file if you add a new key or interaction.