fix: spec compliance for TUI DIALOGS section

- Rename CertFormResult.cancelled → confirmed (True=submit, False=cancel/Esc)
- Change _FORM_FIELDS from list to tuple
- Update _FORM_LABELS to exact values: CN, Email, Password
- Update _FORM_FIELD_Y to {cn:3, email:5, password:7}
- Add password param to show_cert_form (uses generate_password() if empty)
- Add mask=True to password InputField (both init and F5-regen sites)
- Add ord("Q") to show_confirm false-return condition
- Add KEY_UP/KEY_DOWN navigation in show_cert_form
- Add Ctrl-S (0x13) as immediate submit key in show_cert_form
- Update tests/test_dialogs.py: confirmed=True on submit, confirmed=False on cancel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad Doloman
2026-06-24 00:13:46 +03:00
parent fefec14929
commit 565a152243
2 changed files with 37 additions and 23 deletions

View File

@@ -483,7 +483,7 @@ def show_confirm(stdscr, message: str) -> bool:
key = win.getch()
if key in (ord("y"), ord("Y")):
return True
if key in (ord("n"), ord("N"), 27, ord("q")):
if key in (ord("n"), ord("N"), 27, ord("q"), ord("Q")):
return False
@@ -492,27 +492,28 @@ class CertFormResult:
cn: str
email: str
password: str
cancelled: bool
confirmed: bool
_FORM_FIELDS = ["cn", "email", "password"]
_FORM_FIELDS = ("cn", "email", "password")
_FORM_LABELS = {
"cn": "Common Name (CN):",
"email": "Email address:",
"password": "Password [F5 = regenerate]:",
"cn": "CN",
"email": "Email",
"password": "Password",
}
_FORM_FIELD_Y = {"cn": 3, "email": 6, "password": 9}
_FORM_FIELD_Y = {"cn": 3, "email": 5, "password": 7}
def show_cert_form(
stdscr,
cn: str = "",
email: str = "",
password: str = "",
cn_readonly: bool = False,
) -> CertFormResult:
"""Modal cert-detail form.
Tab / Shift-Tab cycle fields. F5 regenerates password.
Enter on last field confirms. Escape cancels."""
Tab / Shift-Tab / Up / Down cycle fields. F5 regenerates password.
Enter on last field or Ctrl-S confirms. Escape cancels."""
sh, sw = stdscr.getmaxyx()
h, w = 16, 62
win = curses.newwin(h, w, (sh - h) // 2, (sw - w) // 2)
@@ -524,10 +525,20 @@ def show_cert_form(
"cn": InputField(win, _FORM_FIELD_Y["cn"], 3, fw, initial=cn),
"email": InputField(win, _FORM_FIELD_Y["email"], 3, fw, initial=email),
"password": InputField(win, _FORM_FIELD_Y["password"], 3, fw,
initial=generate_password()),
initial=password if password else generate_password(),
mask=True),
}
focus = 0
def _submit() -> CertFormResult:
final_cn = cn if cn_readonly else fields["cn"].value
return CertFormResult(
cn=final_cn,
email=fields["email"].value,
password=fields["password"].value,
confirmed=True,
)
while True:
win.clear()
draw_box(win, "Certificate Details")
@@ -549,7 +560,7 @@ def show_cert_form(
else:
fields[name].draw()
hint = "Tab=next F5=regen password Enter=confirm Esc=cancel"
hint = "Tab=next F5=regen password Ctrl-S=confirm Esc=cancel"
try:
win.addstr(h - 2, 2, hint[:w - 4], curses.color_pair(COLOR_DISABLED))
except curses.error:
@@ -559,29 +570,32 @@ def show_cert_form(
key = win.getch()
if key == 27:
return CertFormResult(cn=cn, email="", password="", cancelled=True)
return CertFormResult(cn=cn, email="", password="", confirmed=False)
if key == 0x13: # Ctrl-S: immediate submit
return _submit()
if key == 9: # Tab
focus = (focus + 1) % len(active)
continue
if key == curses.KEY_BTAB: # Shift-Tab
focus = (focus - 1) % len(active)
continue
if key == curses.KEY_DOWN:
focus = (focus + 1) % len(active)
continue
if key == curses.KEY_UP:
focus = (focus - 1) % len(active)
continue
if key == curses.KEY_F5:
fields["password"] = InputField(
win, _FORM_FIELD_Y["password"], 3, fw, initial=generate_password(),
win, _FORM_FIELD_Y["password"], 3, fw,
initial=generate_password(), mask=True,
)
continue
if key in (10, 13, curses.KEY_ENTER):
if focus < len(active) - 1:
focus += 1
continue
final_cn = cn if cn_readonly else fields["cn"].value
return CertFormResult(
cn=final_cn,
email=fields["email"].value,
password=fields["password"].value,
cancelled=False,
)
return _submit()
fields[active[focus]].handle_key(key)