Commit Graph
4 Commits
Author SHA1 Message Date
tiennm99 d5f89665d8 test(web): cover the difficulty-hint table and the last-difficulty setting
difficultyHints is walked against the Difficulty schema the same way
difficultyLabels already is, and the settings store gets coverage for
lastDifficulty: starts unset, persists across a reload, ignores a corrupt
stored value, and stays usable in memory when storage is hostile.
2026-09-21 16:59:39 +07:00
tiennm99 0003a4510b feat(web): show why a word scored what it did, and let a dead end be claimed
The chain shows each word's score breakdown as small chips beside the total.
A "Bí từ" button next to the input claims a dead end on the player's own
turn, armed the same way resign is; a false claim is answered inline near
the input rather than in the general error banner. A rejection for a word
not in the dictionary offers the one real word a diacritic typo differs
from, filling and focusing the field on click, and a button to report the
word as real, acknowledged with a confirmation once the server has heard it.
2026-09-21 01:55:04 +07:00
tiennm99 90cd679639 feat(web): state the rules, reach the chat from the top of the board, lint the tree
A /rules page says, in one place, what nothing in the app said before:
the chain rule, the clock, what a dead end costs, elimination and the last
player standing, how a word is scored and the reconnect window. Linked
from the landing page and from the board and lobby headers.

The chat control moves above the chain as a pill with the unread count,
where it can be reached on a phone mid-game, and the lobby's folded chat
now carries an unread badge too.

ESLint with the Svelte and JSDoc plugins, run in CI; the real findings
it turned up (missing each keys, untyped timer handles) are fixed.
2026-09-21 01:17:52 +07:00
tiennm99 64106735ec feat(web): add SvelteKit vs-bot frontend
The board, the home screen and the game-over panel, in Vietnamese, served by
the Go binary as a static single-page app.

The store is a reducer over ServerMessage and computes nothing. Validity, turn
order, scores and the result are read from the wire, which is what lets one
screen serve the bot now and online play later. Every Vietnamese string lives in
one module, including the map from RejectReason to a message, so the server can
send UI keys instead of prose.

The word field is uncontrolled. A Telex or VNI input method composes a diacritic
across several keystrokes, and writing the value back on each one cancels the
composition. It is read on submit and cleared only there.

The countdown is drawn against the server's clock, estimated from the ping round
trip, and settles 300ms early so the ring never claims more time than the server
allows.

The screen owns the socket and the game while it is mounted, and gives the
current game up on the way out. Asking for a game is stored intent rather than a
condition inferred from the board being empty: clearing the board for a rematch
is that same condition, so the inference sent a second StartBotGame and the
server built two rooms that then destroyed each other.

A test greps the built bundle for dictionary words and holds a size budget, so
the wordlist cannot reach the browser unnoticed. Another reads the error codes
out of the Go transport and fails when one has no Vietnamese message.
2026-09-05 12:55:02 +07:00