diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/DECISIONS.md | 14 | ||||
| -rw-r--r-- | docs/PROTOCOL.md | 63 | ||||
| -rw-r--r-- | docs/STATE.md | 30 | ||||
| -rw-r--r-- | docs/TUI-GUIDELINES.md | 164 |
4 files changed, 177 insertions, 94 deletions
diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 5499610..c18f49b 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -261,6 +261,20 @@ kept verbatim from the STATE.md they were pruned from (2026-09-21). row, the `F2` menu, delete from list and editor); the other screens follow. +29. **Context menu and web-safe keys (2026-09-23)**: `→` opens a context + menu ("Åtgärder") where no caret uses the arrow, `^O` everywhere + (also in table cells); it is a box at the right edge listing every + action with its key, so it doubles as the hotkey list. Choosing an + action feeds its key to the widget, so the menu and the accelerator + share one path. All function keys are removed (F2 menu, F4 mall, F5 + refresh/validate/preview, F7, F9 save/post) and so are `Ctrl+N` (now + `n`), `Ctrl+Enter` and `Ctrl+F`; the extended keyboard protocols are no + longer enabled. Reason: the TUI is to be served in a browser terminal + for users outside the home network (the owner and Petter first, others + later), and browsers own the F-keys and `Ctrl+N`/`T`/`W`. Supersedes + the key parts of #11 and #28; `make check` rejects F-keys, `^N` and + `^Enter` in `clients/`. + ## Completed work formerly listed under "Pending decisions" - Attachments are complete: download (voucher detail `f`, Underlag `Enter`, diff --git a/docs/PROTOCOL.md b/docs/PROTOCOL.md index 9ab9620..709a475 100644 --- a/docs/PROTOCOL.md +++ b/docs/PROTOCOL.md @@ -954,9 +954,10 @@ commands. Implemented screens (0.1.0-dev): `voucher.list` calls) and `s` cycles its sort order (number or date, ascending or descending, remembered in `tui.conf`); in the detail view up/down step to the previous/next voucher in that order. -- **Nytt verifikat** — row editor with live balance display, F5 dry-run - validation and F9 posting; one `client_ref` per form makes retries safe. - F4 applies a konteringsmall (prompts for template and `x`). +- **Nytt verifikat** — row editor with live balance display; the context + menu (`^O`) offers Bokför, Validera (dry run), Hämta mall… (prompts for + template and `x`) and Bifoga fil…; one `client_ref` per form makes + retries safe. - **Ingående balans** — the effective opening balances of the selected fiscal year (carry-forward plus any `IB` vouchers, as the reports compute them); enter accounts with signed amounts (positive debit, negative @@ -967,7 +968,8 @@ commands. Implemented screens (0.1.0-dev): as `YYYY-MM-DD - YYYY-MM-DD` (plus label and open/closed) so broken fiscal years are visible. All screens then work in the selected year. - **Mallar** — list, create and edit templates in the same form style as - vouchers (Tab, dynamic rows, F7 clear row, F5 validate, F9 save); archive + vouchers (Tab, dynamic rows, `^X` clear row, Validera and Spara in the + context menu); archive keeps the template but hides it from the list. - **Underlag** — inbox of unlinked attachments; `a` uploads a file and Enter opens **Granska** (text in a pager, other files in the desktop @@ -976,47 +978,49 @@ commands. Implemented screens (0.1.0-dev): - **Bankavstämning** — imported bank transactions (`bank.import`) matched against vouchers on the bank account, with suggestions; Enter matches the suggested voucher (or picks another), `u` unmatches, `a` imports a SEB CSV. - `Ctrl+N` (or the last row `Skapa nytt verifikat…` in the match list) opens + `n` (or the last row `Skapa nytt verifikat…` in the match list) opens **Nytt verifikat** prefilled with the transaction's date, text, signed amount and bank account; posting it auto-matches the new voucher and returns to the list. A failed auto-match keeps the posted voucher and shows the server error. - **Fakturor** — invoice list (`invoice.list`, newest first) with number, date, customer, total and status (`utfärdad`/`krediterad`/`betald - <datum>`). Ctrl+N opens the form, Enter the detail. The form has the + <datum>`). `n` opens the form, Enter the detail. The form has the customer picker, invoice/due (due defaults from the customer's payment days) and delivery dates, er/var referens and rows (beskrivning, antal, enhet, à-pris, moms, anm); a row with only beskrivning is a free-text line - (`text` rows, no amount). `F5` previews the real PDF (`invoice.preview`, - nothing stored, no number consumed), `F9` issues (`invoice.issue`) and + (`text` rows, no amount). The context menu's Förhandsvisa PDF shows the + real PDF (`invoice.preview`, nothing stored, no number consumed), Utfärda + issues (`invoice.issue`) and then asks "Skicka faktura <nr> till <e-post>?". The detail shows header and rows and offers `p = visa PDF` (`invoice.pdf`), `s = skicka` (`invoice.send`), `u = duplicera` (a new draft with the same rows and today's dates) and, on unpaid invoices, `b = kvittera betalning`: a prefilled payment voucher (debit `bank_account`, credit `invoice_receivable_account`) is opened in the ordinary voucher form and, - once posted, linked with `invoice.pay`. In the list, `n` sets the next - invoice number (`invoice.sequence_get`/`sequence_set`, owner-only). + once posted, linked with `invoice.pay`. In the list, the context menu's + Ändra nästa fakturanummer… sets the next invoice number + (`invoice.sequence_get`/`sequence_set`, owner-only). - **Kunder** — the customer register (name, address, postal code, city, VAT - number, e-mail, your reference, payment days, notes). Ctrl+N creates, - Enter edits (F5 validates with a dry run, Ctrl+Enter saves), `d` - archives/reactivates. + number, e-mail, your reference, payment days, notes). `n` creates, Enter + edits (Spara row or menu; Validera is a dry run), `d` archives/ + reactivates a customer or deletes a draft. - **Lönekörningar** — the monthly payroll runs (`payroll.run_list`, newest - period first) with status (`bokförd`/`betald`), gross and net. Ctrl+N + period first) with status (`bokförd`/`betald`), gross and net. `n` starts a new run (period YYYY-MM and pay date, defaulting into the selected fiscal year) when an active employee with a monthly salary exists, Enter opens an existing run. The run screen shows the status and - totals, `F5` previews (`payroll.run_preview` for a new run, the stored - lines for a posted one), `Ctrl+Enter` posts after confirmation + totals, Förhandsvisa (`payroll.run_preview` for a new run, the stored + lines for a posted one) and Bokför körning… posts after confirmation (`payroll.run_post`, showing the voucher), and the manual steps are the action rows **Lönebesked** (PDF via `payroll.payslip`), **AGI-underlag** (`payroll.agi`, owner only) and **Betala skatt & avgifter** (`payroll.pay_tax`); a disabled step names its reason. The list title warns when the current year's tax tables are missing. - **Anställda** — the employee register (name, masked personnummer, salary, - tax table/column, e-mail). Ctrl+N creates, Enter edits (an empty - personnummer keeps the stored one), `d` archives/reactivates; F5 - validates with a dry run and Ctrl+Enter saves. + tax table/column, e-mail). `n` creates, Enter edits (an empty + personnummer keeps the stored one), `d` archives/reactivates; Validera + (dry run) and Spara are in the context menu. - **Skattetabeller** (via **System**) — stored tax table years, the current year's status, fetch time and source. Owners fetch Skatteverket's official monthly table (`payroll.tax_tables_fetch`) or import a file offline @@ -1028,9 +1032,9 @@ commands. Implemented screens (0.1.0-dev): Report tables keep their column-header row pinned while the body scrolls. The draft asks for the board's proposed dividend, kept per fiscal year with `fiscal_year.update`. -- **Bokslut** — periodiseringsfond and tax rate fields; F5 shows the posting - plan as a `bokslut.post` dry run, `^Enter` (or F9) asks for confirmation - and posts the plan. +- **Bokslut** — periodiseringsfond and tax rate fields; Visa bokslutsplan + shows the posting plan as a `bokslut.post` dry run, Bokför planen… asks + for confirmation and posts the plan. - **Information om året** — the per-year årsredovisning details (material events, AGM and payment dates, proposed dividend, employees, other notes), edited per field; a new fiscal year inherits the stable fields. The @@ -1051,12 +1055,15 @@ commands. Implemented screens (0.1.0-dev): - **Revision** (via **System**) — chain verification and behandlingshistorik. -**Ctrl+N is the universal "add" key**: it starts a new verifikat from the -dashboard, the voucher list and the voucher detail view; a new mall from the -Mallar menu; a new customer or employee from their registers; a new payroll -run from Lönekörningar; a new fiscal year from the year picker; and maps to -the editor in Ingående balans and to file upload in Underlag. F5 is the -universal refresh. Hints show the keys per screen. +**`→` (or `^O`) opens the context menu** ("Åtgärder") with every action of +the view and the key that does the same; `^O` also works while typing in a +table cell. **`n` is the universal "add" key**: a new verifikat from the +dashboard, the voucher list and the voucher detail view; a new mall, +customer, employee, payroll run, rule or board member from their lists; a +new fiscal year from the year picker; file upload in Underlag. `r` is the +universal refresh in lists and views. The TUI uses no function keys and no +browser-reserved control keys (`Ctrl+N`/`T`/`W`), so it runs unchanged in a +browser terminal; see TUI-GUIDELINES.md "Keys and the web". **Ctrl+R reloads the client in place** (for hot-reloading after a rebuild): it re-execs the installed binary with `--org`, `--fy` and `--screen NAME` diff --git a/docs/STATE.md b/docs/STATE.md index 4dcbb35..77ef9a4 100644 --- a/docs/STATE.md +++ b/docs/STATE.md @@ -14,6 +14,18 @@ unit tests and the docs consistency check. ## Resume here (2026-09-23) +- **Context menu and web-safe keys (2026-09-23, branch + `eff/context-menu`)**: `→` (or `^O`, also in table cells) opens + "Åtgärder", a box at the right edge with every action of the view and + its key; every screen declares its actions (`tui_set_actions`). All + F-keys, `Ctrl+N` (now `n`), `Ctrl+Enter` and `Ctrl+F` are gone, and the + extended keyboard protocols are no longer enabled, so the TUI can run in + a browser terminal; `make check` rejects F-keys/`^N`/`^Enter` in + `clients/`. Save/post/validate/preview/template/attach in forms are menu + actions. Decision #29; spec in TUI-GUIDELINES.md "Actions and the context + menu" and "Keys and the web". Next for the web: an upload/download path + through the browser instead of the local file browser, and per-user + isolation of the TUI processes. - **Byt lösenord (2026-09-23, deployed in `v0.1.68`)**: new command `user.set_password` (own password, password session only, current password required and rate limited like logins, ≥ 10 characters, other sessions closed, audited without secrets) and a @@ -254,8 +266,8 @@ Original entries for the struck items are in `docs/DECISIONS.md`. Drive the TUI over a pty with `scripts/tui-sandbox.sh -- ./build/bokftui --socket /tmp/opencode/bokf-local/sock --org 1 --fy 1 ...` plus a small driver that feeds keys and an ANSI renderer (recreate if gone; arrows are - `ESC O B/A`, Tab `\t`, `^X` `\x18`, `^Enter` `ESC[27;5;13~`, F5 - `ESC[15~`). Never test against the live daemon. + `ESC O B/A`, Tab `\t`, `^X` `\x18`, `^O` `\x0f` for the context + menu). Never test against the live daemon. - Demo: db `~/bokf-demo/bokfd.db`, socket `~/bokf-demo/bokfd.sock`, @@ -271,18 +283,20 @@ Original entries for the struck items are in `docs/DECISIONS.md`. (schema v1, old binary) — recreate or migrate it with the current build if it is still wanted. - TUI smoke tests: drive over a pty with `script -qec`; function-key escape - sequences are timing-sensitive there (not an app bug). Arrows arrive as - application-mode sequences (`ESC O B` for Down), not `ESC [ B`, because - curses enables the keypad. `Ctrl+N/C/F` are single bytes and reliable. + sequences are timing-sensitive there (not an app bug; the TUI no longer + uses them). Arrows arrive as application-mode sequences (`ESC O B` for + Down), not `ESC [ B`, because curses enables the keypad. `^O`/`^X`/`^C` + are single bytes and reliable. Always wrap the run in `scripts/tui-sandbox.sh -- ./build/bokftui ...`: it isolates `XDG_CONFIG_HOME`/`XDG_CACHE_HOME` so a test can never overwrite the real `~/.config/bokf/tui.conf` or `~/.cache/bokf/tui.log`. ## Known caveats -- Saving with Ctrl+Enter needs a terminal that speaks xterm - `modifyOtherKeys` level 2 or the Kitty keyboard protocol; gnome-terminal/VTE - sends neither, so the hints advertise `F9`, which works everywhere. +- The TUI enables no extended keyboard protocol (no xterm + `modifyOtherKeys`, no Kitty protocol) and uses no function keys; saving + and posting are context-menu actions, which work in every terminal and + in a browser terminal. - Developer tooling: the `g_cmd_<domain>[]` tables in `src/cmd_*.c` carry declarative argument schemas (`CMD_ARGS`); `describe` emits them and the diff --git a/docs/TUI-GUIDELINES.md b/docs/TUI-GUIDELINES.md index 28b7f89..650f026 100644 --- a/docs/TUI-GUIDELINES.md +++ b/docs/TUI-GUIDELINES.md @@ -7,7 +7,9 @@ implementations. Inspired by Midnight Commander, htop, mutt and calcurse. The **Interaction model** section is the agreed target (settled 2026-09-22, `DECISIONS.md` #28). The **Kunder** pilot is implemented: drafts in `$XDG_CACHE_HOME/bokf/drafts.json`, `<UTKAST>` marking, the explicit `Spara` -row, the `F2` action menu and draft deletion work there. Every other screen +row and draft deletion work there. The context menu (`→`/`^O`, below) and +the web-safe keys are implemented in every screen (2026-09-23, +`DECISIONS.md` #29). Every other screen still follows the sections below; the interaction model wins where they conflict as each screen moves over. @@ -38,16 +40,17 @@ saves a whole form, never deletes and never posts. | Class | Draft | Commit event | `Esc`/`q` | Examples | |---|---|---|---|---| -| **Register** | in memory + local draft file | explicit `Spara` (the save action row, `F9`) after validation | back; the draft stays | customers, employees, templates, momsregler, org data | -| **Document** | the form is the draft | explicit `Posta` (the save action row, `F9`); immutable once written | back; confirm only when dirty | verifikat, fakturor, löneruns, bokslut | +| **Register** | in memory + local draft file | explicit `Spara` (the save action row or the context menu) after validation | back; the draft stays | customers, employees, templates, momsregler, org data | +| **Document** | the form is the draft | explicit `Posta` (the save action row or the context menu); immutable once written | back; confirm only when dirty | verifikat, fakturor, löneruns, bokslut | | **Settings** | an edit buffer until `Spara` | explicit `Spara` | back | faktura/SMTP/serie-inställningar | - Every savable form ends with a visible commit action row (`Spara`, or - `Posta` for documents); `Enter` on the focused row commits (it is an item - with an action), `F9` is the accelerator. -- `Ctrl+Enter` is **dropped** as a commit key: gnome-terminal/VTE cannot - send it. The save action row and `F9` are the only commit gestures, which - makes every terminal behave the same. + `Posta` for documents), or — in a row table, where rows follow — offers it + first in the context menu (`^O`, `Enter`). `Enter` on the focused row + commits (it is an item with an action). +- There is no commit hotkey: `Ctrl+Enter` cannot be sent by gnome-terminal/ + VTE and `F9` is dropped with the other function keys (see "Keys and the + web"). The action row and the menu work in every terminal and browser. - Commit is blocked while the data is invalid: the first invalid field is focused and its error shown. The draft is untouched. A server error at commit keeps the draft as well; only a successful command removes it. @@ -64,8 +67,8 @@ saves a whole form, never deletes and never posts. and shows up in its list immediately. - Drafts are marked `<UTKAST>` directly after the row number in lists (`3. <UTKAST> Namn`) and in the editor's frame title (`Kund <UTKAST>`). -- **Delete draft** is an action both on the list row (via `F2`) and inside - the editor, with a confirmation. It removes the memory and file draft and +- **Delete draft** is an action both on the list row (`d`, also in the + context menu) and inside the editor (context menu), with a confirmation. It removes the memory and file draft and never touches the backend. A successful commit removes the draft too. - Drafts are client-local and never synced; another client sees the last committed value. @@ -76,7 +79,7 @@ saves a whole form, never deletes and never posts. Bitwarden session file; decide whether such fields are excluded from drafts. -### Actions and the `F2` menu +### Actions and the context menu Screens declare actions, never keys: @@ -84,41 +87,79 @@ Screens declare actions, never keys: struct tui_action { const char *id; /* stable, e.g. "customer.archive" */ const char *label; /* Swedish UI text */ - int key; /* accelerator; 0 = menu only */ + int key; /* accelerator, or a virtual key (menu only) */ int enabled; /* 1 runnable, 0 dimmed with a reason, -1 heading */ const char *reason; /* why a disabled action is dim */ }; ``` -- One ordered action list per context drives everything: `F2` opens the - `Åtgärder` menu, the same list dispatches the accelerator keys and builds - the footer hint. A key can no longer exist outside the registry. -- The menu is sectioned: **Aktuell rad** (item actions), **Skärmen** (save, - delete draft, attach, …), **Globalt** (`F5` uppdatera, `Ctrl+R` ladda om, - …). Destructive actions are last and still ask for confirmation. -- `Enter` in the menu runs the highlighted action; disabled actions are dim - with their reason (as in `tui_form_action` today); `Esc` closes. The - actions that complex forms hide behind hotkeys today live here unchanged. -- The footer shows at most the two or three most important contextual - actions plus `F2 = fler`. Universal navigation keys (`Tab`, arrows, - `PgUp`/`PgDn`, `Home`/`End`) are not repeated there. -- The session's key decisions: letter accelerators stay, `F9` is the only - commit key (`Ctrl+Enter` is dropped) and `F2` only — no `§` binding (it is - not reliably encodable across terminals). +- **`→` opens the context menu** ("Åtgärder") wherever no caret uses the + arrow: lists, menus, pagers (report/detail views), forms and a row + table's header fields. **`^O` opens it everywhere**, also while typing in + a table cell, where `→` moves the caret. The menu is a box at the right + edge next to the selected row; each line shows the label and, right- + aligned, the key that does the same thing — the menu doubles as the + hotkey list. Up/down/Home/End move, `Enter` (or the action's own key) + runs, `Esc`/`←`/`q`/`^O` close. A disabled action is dim and `Enter` + shows its reason; a separator line divides the groups. +- The screen registers its actions with `tui_set_actions(acts, n)` right + before the widget call; the widget takes them at entry, so they never leak + into a widget opened from a hook. The menu lists the screen's actions + first, then the widget's own (Öppna, Ny, Gå till/sök, Uppdatera, + Tillbaka; in forms Ändra/Utför; in row tables Ändra fält/Rensa raden, + Validera, Spara, Avbryt), skipping own actions whose key the screen took + (`tui_ctx_merge`). +- **Choosing an action is the same as pressing its key**: the widget gets + the key as if it was typed, so the accelerator and the menu share one + code path in the screen's key hook. An action without a physical key uses + a virtual key: `TUI_KEY_SUBMIT` (spara/bokför/utfärda — was F9), + `TUI_KEY_REFRESH` (uppdatera/validera/förhandsvisa — was F5) or + `TUI_KEY_ACTION(n)` for the screen's own menu-only actions (mall, bifoga + in the voucher form, nästa fakturanummer). +- Right before the menu opens the key hook gets `TUI_KEY_MENU`, so a screen + can set `enabled`/`label`/`reason` for the current row in its own array + (the Kunder list shows "Radera utkast" for a draft row and "Arkivera" or + "Återaktivera" otherwise, all on `d`). +- Footers show `→ = åtgärder` (`^O` in row tables) and at most the few + letter accelerators that matter on the screen. Universal navigation keys + are not repeated there. + +### Keys and the web + +The TUI is meant to run in a browser terminal too (xterm.js/ttyd). A +browser keeps some keys for itself, so the TUI only uses keys every +terminal and browser delivers (checked by `make check`): + +- **No function keys.** F1/F3/F5/F6/F7/F10/F11/F12 are browser keys and F2, + F4, F9 need `Fn` on many laptops. Their old jobs are context-menu actions + (F2 → `→`, F5 → "Uppdatera"/"Validera"/"Förhandsvisa" with `r` in lists + and pagers, F9 → "Spara"/"Bokför"/"Utfärda", F4 → "Hämta mall…"). +- **No `Ctrl+N`, `Ctrl+T`, `Ctrl+W`, `Ctrl+Tab`**: Chrome never lets a page + have them. "Ny" is `n` in lists, menus and the voucher detail. +- **No `Ctrl+Enter`** and no extended keyboard protocol: the TUI no longer + enables xterm `modifyOtherKeys` or the Kitty protocol, so every key comes + in its legacy encoding. +- **Allowed**: arrows, `Enter`, `Esc`, `Tab`/`Shift-Tab`, PgUp/PgDn, + Home/End, letters and digits, and the control keys a page can take + (`^O` menu, `^X` clear row, `^A`/`^E`/`^U` in fields, `^C` quit, `^R` + reload — `^R` is a developer convenience and never the only way). +- Every action is reachable with arrows + `Enter` through the menu; a + letter accelerator is a shortcut, never the only path. ### Implementation status -1. Done: `struct tui_action`, `tui_action_menu()` and `tui_action_hint()` in - `clients/tui.[ch]`, unit-tested in `tests/test_tui.c`. -2. Partly done: the Kunder list builds its `F2` actions in a key hook and - appends `F2 = åtgärder` to the footer via `tui_list_hint_extra()`; other - lists have no actions yet, and `tui_rt` still uses its own key branches. +1. Done: `struct tui_action`, the context menu (`tui_action_menu_at`, + `tui_set_actions`, `tui_ctx_merge`, `tui_key_name`) and `→`/`^O` in all + widgets, unit-tested in `tests/test_tui.c`; pty scenario `context-menu`. +2. Done: every screen declares its actions; no F-keys, `^N` or `^Enter` + remain (`scripts/check-consistency.sh` fails on `KEY_F(`, `^N` and + `CTRL_ENTER` in `clients/`). 3. Done for Kunder: `clients/drafts.[ch]` (JSON store, atomic 0600 write, temporary ids, `<UTKAST>` marking, delete action), unit-tested. -4. Done for Kunder: draft/`Spara` model with the `Spara` action row, - `F2` menu and draft deletion from both the list and the editor. The - other register screens and the settings forms (explicit `Spara`, no - per-field autosave) are next. +4. Done for Kunder: draft/`Spara` model with the `Spara` action row and + draft deletion from both the list and the editor. The other register + screens and the settings forms (explicit `Spara`, no per-field autosave) + are next. 5. Done for Kunder: pty scenarios `customer-draft` and `customer-draft-save` cover create, edit, `<UTKAST>`, delete and save. @@ -143,8 +184,10 @@ there. | 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 | +| `→` | Open the context menu ("Åtgärder") for the selected row/view: every action with its key. Not in a table cell, where `→` moves the caret | +| `^O` | Open the context menu anywhere, also while typing in a table cell | +| `n` | Ny: new verifikat (dashboard, voucher list/detail), new mall, kund, faktura, anställd, lönekörning, regel, ledamot, new voucher from a bank transaction, upload (Underlag) | +| `r` | Uppdatera: reload the list or view | | `Esc` / `q` | Back one level. At the dashboard it does nothing — Esc never exits the app. In a row table (`tui_rt_run`) and while a field is being edited `q` is text; only `Esc` goes back there | | `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) | @@ -153,21 +196,22 @@ there. | `g` | Live goto or search in every list and menu. The first key decides: a digit makes it "Gå till rad/nummer:" (the selection follows the number as you type, backspace steps back, `g` closes); any other printable key makes it "Sök:" — the selection jumps to the first row (from the top) whose text contains the typed text, case-insensitive also for åäö; up/down step to the next/previous match and wrap; "(ingen träff)" when nothing matches. Digits, `g`, `q` and screen keys (`s` = sortera, `d`…) are text while searching. Backspace to empty returns to the undecided prompt. Enter/Esc close the prompt without opening anything; the selection stays | | arrows, PgUp/PgDn, Home/End | Move/scroll; selection always stays visible. Pages stop at the first/last row, they never wrap | | `e` | Edit the shown object (IB, where applicable) | -| `a` | Add/upload (Underlag) | +| `a` | Add/upload: upload (Underlag), import a bank file (Bankavstämning), attach a file (voucher detail) | | `c` | Correct (voucher detail) | | `d` | Delete/arkivera the selected row (only where the action exists; asks for confirmation) | | `s` | Verifikat list: cycle the sort order — nummer stigande/fallande, datum stigande/fallande (ties by series and number). The title shows the current order; it is saved as `voucher_sort` in `tui.conf` | -| up/down (voucher detail) | Previous/next voucher in the list's current order; the title shows `(n av N)`. The detail scrolls with PgUp/PgDn/Home/End (`TUI_PAGER_NO_ARROWS`). A voucher posted from the detail (`^N`) is shown without stepping until the list is reopened | +| up/down (voucher detail) | Previous/next voucher in the list's current order; the title shows `(n av N)`. The detail scrolls with PgUp/PgDn/Home/End (`TUI_PAGER_NO_ARROWS`). A voucher posted from the detail (`n`) is shown without stepping until the list is reopened | | `f` | Voucher detail: list the voucher's underlag — Enter opens Granska (text in a pager, PDFs/images in the desktop viewer) or Ladda ned…, `d` removes the link (asks first). Underlag: Enter does the same | -| `u` / `b` | Faktura detail: `u` duplicates the invoice into a new draft (same rows, dates reset to today), `b` (unpaid invoices) prefills and posts the payment voucher, then marks the invoice paid | -| `Ctrl+F` | Attach a file via the file browser (voucher form and voucher detail) | +| `p` / `s` / `u` / `b` | Faktura detail: `p` shows the PDF, `s` sends it, `u` duplicates the invoice into a new draft (same rows, dates reset to today), `b` (unpaid invoices) prefills and posts the payment voucher, then marks the invoice paid | +| `u` | Bankavstämning: remove the highlighted transaction's match | | `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. Needs xterm `modifyOtherKeys` level 2 or the Kitty keyboard protocol (xterm, kitty, foot, WezTerm); gnome-terminal/VTE sends neither, so the hints advertise `F9`, which works everywhere. The interaction model drops `Ctrl+Enter` entirely — don't add it to new views | -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. +Saving, posting, validating, previewing, templates and attaching in forms +have no hotkey: they are context-menu actions (see "Keys and the web"). +Every key a screen handles is listed in its context menu; the footer shows +`→ = åtgärder` and the most important letters. Control keys are written +compactly as `^O`, `^X`, `^C`, `^R` to save width. ## Lists (`select_list`, `menu`) @@ -175,7 +219,7 @@ written compactly as `^N`, `^A`, `^C`, `^R` to save width. - Verifikation ids are shown concatenated as `series+number` (`V-8`, `A8`), using the org's `series_voucher` (Bolaget → Verifikationsserier) for new vouchers. -- The last row may be an action (e.g. `+ Nytt verifikat (Ctrl+N)`); selecting it +- The last row may be an action (e.g. `+ Nytt verifikat (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. @@ -200,7 +244,7 @@ written compactly as `^N`, `^A`, `^C`, `^R` to save width. address as the last argument to `tui_form_run`, `tui_form_run_hook` and `tui_form_run_actions` (like a list cursor). On entry `*focus` is clamped to a selectable row — out of range snaps to the nearest end, a heading row - snaps forward and wraps — and on every return (`Esc`, `F5`, `^Enter`, a + snaps forward and wraps — and on every return (`Esc`, Uppdatera, Spara, a chosen action, an edited field) the row is written back, so a re-run after a save or a round-trip through another screen lands on the same row. `NULL` starts at the first focusable row and stores nothing. @@ -234,7 +278,7 @@ written compactly as `^N`, `^A`, `^C`, `^R` to save width. 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`. + happens on Validera/Spara. - 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. @@ -259,7 +303,7 @@ written compactly as `^N`, `^A`, `^C`, `^R` to save width. does nothing. The hardware cursor only shows in a table cell. `q` is text here — only `Esc` leaves the table. Moving to another cell (arrows, `Enter`, `Tab`) resets the caret to the end of the new cell. A focused - cell scrolls horizontally like a form field. `F5`/`Ctrl+Enter`/`Esc` + cell scrolls horizontally like a form field. `^O`/`Esc` work the same from both. - A form may carry an action list under its fields (`tui_form_run_actions`): fields first, then action rows, one focus ring (`Tab`/`Shift-Tab`/ @@ -276,17 +320,20 @@ written compactly as `^N`, `^A`, `^C`, `^R` to save width. resultat` voucher that `bokslut.post` creates; a closed year counts as posted) and the actions Årsredovisning (K2), Inkomstdeklaration (INK2/SRU), Bokslutsplan (torrkörning) and Bokför bokslut — the last - dimmed with a reason when the year is closed or already posted. `F5` - shows the plan, `Ctrl+Enter` posts it. -- Validation: `F5` validates without writing and reports exactly what is wrong + dimmed with a reason when the year is closed or already posted. The + context menu's "Visa bokslutsplan" shows the plan, "Bokför planen…" posts + it. +- Validation: the "Validera" action (`TUI_KEY_REFRESH`) 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 +- Saving: the "Spara"/"Bokför" action (`TUI_KEY_SUBMIT`) writes, shows a + confirmation message, and returns to the previous view. `Esc` cancels without saving. - Screen-specific keys (template picker, attach, link, download) are handled in a key hook, never with a local loop. The hook runs for keys the widget itself does not handle and returns `TUI_HOOK_*`: `STAY` (consumed, redraw), - `BACK`, `REFRESH` or `SUBMIT` (the widget returns as on `Esc`/`F5`/ - `Ctrl+Enter`). Hooks exist on `tui_form_run_hook`, `tui_rt_set_key`, + `BACK`, `REFRESH` or `SUBMIT` (the widget returns as on `Esc` or the + Uppdatera/Spara actions). Hooks exist on `tui_form_run_hook`, `tui_rt_set_key`, `tui_pager_hook` and `tui_select_list_hook`. - File browser (`file_browser`): starts in the directory where the last attachment was picked (remembered in `tui.conf`), falling back to `$HOME` @@ -404,8 +451,9 @@ only place that touches ncurses. Rules: one-off questions use the prompts (`tui_prompt_into`, `tui_date_prompt_into`, `tui_amount_prompt_into`, `tui_choice_prompt`). All of them share one field editor. -5. Support `F5` if the data can change elsewhere. +5. Support refresh (`r` / "Uppdatera") if the data can change elsewhere. 6. Update `PROTOCOL.md` §8 and this file if you add a new key or interaction. 7. Declare the screen's actions in one `tui_action` list (once the - interaction model is implemented); dispatch, the `F2` menu and the footer + interaction model is implemented); dispatch, the context menu and the + footer hint all read that list, so a key cannot exist without a visible action. |
