From 71a702f375750829c634b552217c9925d549828b Mon Sep 17 00:00:00 2001 From: Anders Betts Date: Wed, 23 Sep 2026 10:58:17 +0200 Subject: tui: context menu on → / ^O; no F-keys, ^N or ^Enter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit → (where no caret uses it) and ^O (everywhere, also in table cells) open "Åtgärder": a box at the right edge with every action of the view and its key. Screens declare their actions with tui_set_actions(); choosing one feeds its key to the widget, so the menu and accelerators share one path. Function keys, Ctrl+N (now n), Ctrl+Enter and Ctrl+F are gone and no extended keyboard protocol is enabled, so the TUI works in a browser terminal; save/post/validate/preview/template/attach are menu actions. make check rejects F-keys, ^N and ^Enter in clients/. Co-Authored-By: Claude Opus 5.5 --- scripts/check-consistency.sh | 25 +++++++- scripts/tui-golden.py | 134 ++++++++++++++++++++++++------------------- 2 files changed, 100 insertions(+), 59 deletions(-) (limited to 'scripts') diff --git a/scripts/check-consistency.sh b/scripts/check-consistency.sh index 3ca9b0c..d8054e2 100755 --- a/scripts/check-consistency.sh +++ b/scripts/check-consistency.sh @@ -6,7 +6,10 @@ # "Command", and vice versa; # 2. every error code passed to fail()/failf()/add_error() in src/*.c is # documented in PROTOCOL.md section 5.3, and vice versa (documented but -# unused codes are warnings only, some are reserved). +# unused codes are warnings only, some are reserved); +# 3. the TUI uses no key a browser terminal cannot deliver: no function +# keys (KEY_F), no Ctrl+N and no Ctrl+Enter (docs/TUI-GUIDELINES.md +# "Keys and the web"). # # Run from the repo root: sh scripts/check-consistency.sh # Exits 0 when there is no drift, 1 when drift is found, 2 on parse errors. @@ -19,6 +22,8 @@ # PROTOCOL_MD protocol document, default $REPO_ROOT/docs/PROTOCOL.md # SRC_GLOB shell glob of sources scanned for error codes, # default $REPO_ROOT/src/*.c +# CLIENTS_GLOB shell glob of TUI sources scanned for keys, +# default $REPO_ROOT/clients/*.c # # Commands deliberately kept out of the PROTOCOL.md tables. Prefer documenting # the command in a table over adding it here. Keep sorted, space separated. @@ -188,6 +193,24 @@ else echo "(none)" fi +section "keys: F-keys, Ctrl+N or Ctrl+Enter used in the TUI" +CLIENTS_GLOB=${CLIENTS_GLOB:-"$REPO_ROOT/clients/*.c"} +: > "$tmp/bad_keys" +for f in $CLIENTS_GLOB; do + [ -f "$f" ] || continue + grep -nE 'KEY_F\(|TUI_KEY_CTRL_N|KEY_CTRL_N|CTRL_ENTER' "$f" | + sed "s|^|$f:|" >> "$tmp/bad_keys" || true +done +if [ -s "$tmp/bad_keys" ]; then + while IFS= read -r line; do + echo "key: not web-safe: $line" + problems=$((problems + 1)) + done < "$tmp/bad_keys" + status=1 +else + echo "(none)" +fi + section "warnings (not failures)" if [ -s "$tmp/code_unused" ]; then while IFS= read -r code; do diff --git a/scripts/tui-golden.py b/scripts/tui-golden.py index b6aaada..bce00eb 100755 --- a/scripts/tui-golden.py +++ b/scripts/tui-golden.py @@ -57,12 +57,8 @@ KEYS = { "pgup": "\x1b[5~", "pgdn": "\x1b[6~", "ctrlc": "\x03", - "ctrln": "\x0e", - "ctrlenter": "\x1b[27;5;13~", - "ctrlenter-kitty": "\x1b[13;5u", - "f5": "\x1b[15~", - "f9": "\x1b[20~", - "f2": "\x1bOQ", + "ctrlo": "\x0f", # context menu, also inside a table cell + "f5": "\x1b[15~", # only to prove that F-keys do nothing "backspace": "\x7f", "tab": "\t", } @@ -120,7 +116,7 @@ SCENARIOS = [ }, { "keys": ["enter"], - "expect": ["piltangenter/PgUp/PgDn rullar", "kvitto"], + "expect": ["upp/ned/PgUp/PgDn rullar", "kvitto"], }, { "keys": ["esc"], @@ -140,7 +136,7 @@ SCENARIOS = [ }, { "keys": ["enter"], - "expect": ["piltangenter/PgUp/PgDn rullar", "kvitto"], + "expect": ["upp/ned/PgUp/PgDn rullar", "kvitto"], }, { "keys": ["esc"], @@ -184,7 +180,7 @@ SCENARIOS = [ "expect": ["Bankavstämning", "GOLDEN INSÄTTNING", "CDON"], "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Nytt verifikat", "GOLDEN INSÄTTNING", "123,00", "1930"], }, @@ -224,14 +220,19 @@ SCENARIOS = [ "expect": ["Bankavstämning", "CDON", "1 omatchade"], "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Nytt verifikat", "CDON", "24,09", "1930"], }, { "keys": ["\t\t\t\t\t\t\t", "3001", "\t", "24,09"], }, { - "keys": ["ctrlenter"], + "keys": ["ctrlo"], + "expect": ["Åtgärder", "Bokför", "Validera (torrkörning)", + "Hämta mall…"], + }, + { + "keys": ["enter"], "expect": ["Bokfört"], }, { @@ -243,7 +244,7 @@ SCENARIOS = [ "expect": ["→ A2", "0 omatchade"], }, { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Transaktionen är redan matchad."], }, { @@ -268,7 +269,7 @@ SCENARIOS = [ "expect": ["Kunder", "Testkund AB"], "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Ny kund", ""], }, { @@ -284,11 +285,11 @@ SCENARIOS = [ "expect": [" Utkastkund"], }, { - "keys": ["f2"], - "expect": ["Åtgärder", "Öppna", "Radera utkast"], + "keys": ["right"], + "expect": ["Åtgärder", "Radera utkast", "Öppna", "Enter"], }, { - "keys": ["down", "enter"], + "keys": ["enter"], "expect": ["Radera utkastet?"], }, { @@ -303,7 +304,7 @@ SCENARIOS = [ "expect": ["Kunder", "Testkund AB"], "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Ny kund", ""], }, { @@ -311,7 +312,11 @@ SCENARIOS = [ "expect": ["Sparad kund", ""], }, { - "keys": ["f9"], + "keys": ["right"], + "expect": ["Åtgärder", "Spara", "Validera"], + }, + { + "keys": ["enter"], "expect": ["Sparat."], }, { @@ -325,7 +330,7 @@ SCENARIOS = [ "screen": "invoices", "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Ny faktura", "Kund"], }, ], @@ -363,7 +368,7 @@ SCENARIOS = [ "Betalning faktura"], }, { - "keys": ["f9"], + "keys": ["ctrlo", "enter"], "expect": ["Bokfört"], }, { @@ -382,11 +387,11 @@ SCENARIOS = [ "expect": ["Anställda", "Testanställd", "Ny anställd"], }, { - "name": "employee-new-f9", + "name": "employee-new-save", "screen": "employees", "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Ny anställd", "Personnummer"], }, { @@ -398,7 +403,7 @@ SCENARIOS = [ "expect": ["19900101-1234"], }, { - "keys": ["f9"], + "keys": ["right", "enter"], "expect": ["Anställd skapad."], }, { @@ -407,32 +412,6 @@ SCENARIOS = [ }, ], }, - { - "name": "employee-new-kitty", - "screen": "employees", - "steps": [ - { - "keys": ["ctrln"], - "expect": ["Ny anställd", "Personnummer"], - }, - { - "keys": ["enter", "Kitty Person", "enter"], - "expect": ["Kitty Person"], - }, - { - "keys": ["down", "enter", "19900202-5678", "enter"], - "expect": ["19900202-5678"], - }, - { - "keys": ["ctrlenter-kitty"], - "expect": ["Anställd skapad."], - }, - { - "keys": ["enter"], - "expect": ["Anställda", "Kitty Person"], - }, - ], - }, { "name": "company", "screen": "company", @@ -515,11 +494,15 @@ SCENARIOS = [ "screen": "payroll", "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Ny lönekörning", "Period", "Bokför körning"], }, { - "keys": ["f5"], + "keys": ["right"], + "expect": ["Åtgärder", "Förhandsvisa", "Bokför körning…"], + }, + { + "keys": ["enter"], "expect": ["Testanställd", "30 000,00", "Avgifter"], }, { @@ -527,7 +510,7 @@ SCENARIOS = [ "expect": ["Ny lönekörning", "Bokför körning"], }, { - "keys": ["ctrlenter"], + "keys": ["right", "down", "enter"], "expect": ["Bokför lönekörningen"], }, { @@ -645,6 +628,42 @@ SCENARIOS = [ }, ], }, + { + # → opens the context menu with the screen's actions first and the + # key of each action; choosing one is the same as pressing its key. + # F-keys do nothing (browsers own them). + "name": "context-menu", + "screen": "vouchers", + "expect": ["sorterade på nummer, stigande"], + "steps": [ + { + "keys": ["right"], + "expect": ["Åtgärder", "Nytt verifikat", "Byt sortering", + "Gå till rad / sök", "Tillbaka"], + }, + { + "keys": ["esc"], + "expect": ["Enter = öppna → = åtgärder"], + }, + { + "keys": ["right", "down", "enter"], + "expect": ["sorterade på nummer, fallande"], + }, + { + "keys": ["home", "enter", "right"], + "expect": ["Rätta (ändringsverifikat)…", "Bifoga fil…", + "Nästa verifikat"], + }, + { + "keys": ["left"], + "expect": ["n = nytt c = rätta a = bifoga"], + }, + { + "keys": ["esc", "f5", "s", "s", "s"], + "expect": ["sorterade på nummer, stigande"], + }, + ], + }, { # Backspace in a freshly opened field edits the value; the next # key must not replace everything. @@ -652,7 +671,7 @@ SCENARIOS = [ "screen": "customers", "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Ny kund", ""], }, { @@ -672,7 +691,7 @@ SCENARIOS = [ "screen": "vouchers", "steps": [ { - "keys": ["ctrln"], + "keys": ["n"], "expect": ["Nytt verifikat", "Datum", "Konto"], }, { @@ -1355,10 +1374,9 @@ def main(argv): report_failure(name, missing, "", before) failures += 1 continue - if (b"\x1b[>4;2m" not in app.raw or - b"\x1b[>1u" not in app.raw): - report_failure(name, ["keyboard protocol enable " - "sequence"], "", before) + if (b"\x1b[>4;2m" in app.raw or b"\x1b[>1u" in app.raw): + report_failure(name, ["no extended keyboard protocol " + "(legacy keys only)"], "", before) failures += 1 continue current = before -- cgit v1.3