From d6d7117bd8f6fc9fe4adab9919a887e86ab42fb2 Mon Sep 17 00:00:00 2001 From: Daniel McCoy Stephenson Date: Sat, 13 Jun 2026 08:03:13 -0600 Subject: [PATCH 1/3] =?UTF-8?q?ux:=20web=20client=20=E2=80=94=20add=20keyb?= =?UTF-8?q?oard=20control=20for=20menus=20and=20prompts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the browser front-end the player could only act by clicking; the console and pygame front-ends both accept number keys to choose an option. A returning player who expects to press "1" to fish got no response, and pressing Enter to dismiss a dialogue did nothing — the web UI silently broke a convention the rest of the game establishes (Nielsen #7 flexibility and efficiency of use; Krug: conventions over invention). This binds number keys (1-9) to option selection and Enter/Space to advancing a dialogue or the timed fishing prompt, while leaving the text-entry field to handle its own keys. A live `currentScreen` reference drives the handler and is cleared on submit so a stray keypress can't double-submit. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/ui/webUserInterface.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/ui/webUserInterface.py b/src/ui/webUserInterface.py index dd6533b..72f86ad 100644 --- a/src/ui/webUserInterface.py +++ b/src/ui/webUserInterface.py @@ -41,6 +41,7 @@
Connecting…
From 1167b8f3a37152c9b846dc7d7f55c95188d214ae Mon Sep 17 00:00:00 2001 From: Daniel McCoy Stephenson Date: Sat, 13 Jun 2026 08:03:43 -0600 Subject: [PATCH 2/3] =?UTF-8?q?ux:=20web=20client=20=E2=80=94=20mark=20des?= =?UTF-8?q?tructive=20options=20with=20a=20danger=20style?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every option rendered as the same neutral-blue button, so "Delete a Save File" and the "Yes, delete it" confirmation looked exactly like "Sell Fish" or "Load Slot 1". Deleting a save is irreversible, and giving an irreversible action the same affordance as benign ones invites a mis-click (Nielsen #5 error prevention; Nielsen #4 consistency and standards / Krug conventions — destructive actions conventionally read as red). Options whose label mentions "delete" now render with a red danger style, so the deletion path and its confirmation stand out from ordinary choices on the way to an unrecoverable action. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/ui/webUserInterface.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ui/webUserInterface.py b/src/ui/webUserInterface.py index 72f86ad..1bc0b24 100644 --- a/src/ui/webUserInterface.py +++ b/src/ui/webUserInterface.py @@ -31,6 +31,8 @@ border: 1px solid #2a4a5a; border-radius: 4px; cursor: pointer; font-family: monospace; font-size: 1rem; } button:hover { background: #1f4a63; } + button.danger { background: #4a1620; border-color: #7a2a35; } + button.danger:hover { background: #63202c; } input { width: 100%; padding: .6rem; font-family: monospace; font-size: 1rem; background: #163345; color: #e0f0ff; border: 1px solid #2a4a5a; border-radius: 4px; } @@ -78,7 +80,10 @@ if (screen.prompt) app.append(el("div", { className: "prompt", textContent: screen.prompt })); if (screen.type === "options") { screen.options.forEach((opt, i) => { - const b = el("button", { textContent: `[${i + 1}] ${opt}` }); + const b = el("button", { + textContent: `[${i + 1}] ${opt}`, + className: /delete/i.test(opt) ? "danger" : "", + }); b.onclick = () => send(String(i + 1)); app.append(b); }); From 23f67e410f67164a865a1aa502b1591a823b54ba Mon Sep 17 00:00:00 2001 From: Daniel McCoy Stephenson Date: Sat, 13 Jun 2026 08:04:25 -0600 Subject: [PATCH 3/3] =?UTF-8?q?ux:=20web=20client=20=E2=80=94=20surface=20?= =?UTF-8?q?lost=20connection=20instead=20of=20freezing=20silently?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the game process stopped (a crash, or the window/terminal closed) the client kept polling /state in a swallowed try/catch and just sat on the last screen forever. To the player the game looked frozen with no hint that anything was wrong or that they should restart it (Nielsen #1 visibility of system status; Nielsen #9 help users recognize and recover from errors). After a few consecutive failed polls the client now shows a "Lost connection — is it still running?" banner and keeps retrying, clearing it automatically if the server comes back. The intentional "game ended" screen is left untouched so a clean quit doesn't get an alarming banner. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/ui/webUserInterface.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/ui/webUserInterface.py b/src/ui/webUserInterface.py index 1bc0b24..80a0411 100644 --- a/src/ui/webUserInterface.py +++ b/src/ui/webUserInterface.py @@ -44,14 +44,31 @@