"Home" and "Find a player" both went to the home page. Swap the second
link for a search box in the header so a player can be looked up from
any page ("/" focuses it). Phones get a search button that jumps to the
search on the home page instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A won board has no answer row, and passing that null straight to
replaceChildren printed it as the text "null" under the board. Add a
fill() helper that skips null children like h() does and use it on the
board page. Bump the asset version so browsers load the new wordle.js
alongside the new board.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuild the browser pages around shared wordle.css / wordle.js, still
served by the unchanged nginx config (same URLs and ?user= / ?board=
parameters, data from /wordle/rawstats and /wordle/rawboard):
- home: animated terminal demo, copyable ssh command, countdown to the
next UTC day, player search with recent searches, and a leaderboard
of followed players (kept in the browser) with today's results
- stats: stat cards, guess distribution with losses, today's result and
the last 14 days
- history: calendar heatmap and a card per game, loading more on demand
- board: flip-in tiles, hold (or R) to reveal letters, the answer on a
loss, copyable spoiler-free emoji result, previous/next game
- dark/light/system themes and a high contrast mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since progress is saved before the final reveal, save_result runs with
ROW still on the last row, so losing on the sixth guess highlighted the
"6" bar as if the game had been won. Only highlight after a win.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The clean up step was skipped whenever a transfer failed, leaving the
deploy key on the runner. Run it even when an earlier step fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy the pages in www/html/wordle to /var/www/html/wordle on the server,
where nginx serves them from, on every deploy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The game saves usernames in lowercase, but the search box and the
user/board URL parameters were used as typed, so searching "Adam"
reported that the user had never played. Lowercase them before use.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Board days are UTC epoch days (midnight UTC), but toLocaleDateString
formatted them in the viewer's timezone, so anyone west of UTC saw
every game dated the day before. Format them in UTC.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copies of the pages served from /var/www/html/wordle on the server:
the user search page (index.html), stats.html, games.html and
game.html. Browsers are sent here by nginx while curl gets the raw
board and stats files. These aren't deployed by the workflow yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl-D doesn't move the cursor to a new line, but the retry prompts
move up a line assuming Enter was pressed, so each Ctrl-D climbed up
the screen over the keyboard and board. It also quit the game, losing
an offline game to a stray key press.
On a terminal, print the missing newline and carry on as if Enter was
pressed. Only exit when input has really ended (not a terminal), so a
closed pipe still can't spin the retry loop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace "GREEN Issue", "YELLOW Issue" and "POOL Issue" with messages
that say which letter broke which rule:
3rd letter must be A
Must contain R
1st letter can't be R (was yellow there before)
S is not in the word (strict mode, grey letter reused)
hard_mode_check now records the failing position, and a yellow letter
reused in the same spot gets its own code (6, previously the unused
"NEW Issue"). Drop the unused "Not A Valid Guess" code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a loss the answer line was written to RESULT_FILE with the border
indent and bold, but CUR_RESULT_FILE only got the letters. Move the
answer line into render_answer and use it for the terminal and both
files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At end of input (Ctrl-D, or stdin closed) read returns straight away
with an empty guess, so the "Must be 5 letters" loop spun forever at
full CPU. If the game's parent process was killed first, the subshell
was left orphaned and spinning.
Exit retrieve_guess when read hits EOF, pass that back through the
retry prompts with a single recursive call, and quit from main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
update_stats ran for every online game, so practice games played after
the daily counted towards played/wins/losses and a practice loss reset
the streak. Move it inside the DAILY_DONE check with the board files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An average of 3.04 was shown as 3.4 because the hundredths weren't
padded. Pad with parameter expansion to keep the script bash-only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hard_mode_check uses the previous row's greens and yellows, but these
weren't written to the progress file, so after resuming a game the next
guess could drop known green and yellow letters. Save them alongside
the rest of the game state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Progress was only saved at the start of a game and on Ctrl-C, so a
dropped connection lost every guess made so far. Ctrl-C during the
reveal also saved the scored row without advancing ROW, which gave the
player that guess back on resume.
Save straight after each guess is scored (before the reveal) with the
next row, record the result before the final reveal, and make the
SIGINT trap only tidy up the terminal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Usernames are used in file names and URLs, so a '/' could write files
outside the game directory and a space broke the share links. Only
allow a-z, 0-9 and _ (max 32 chars) and re-prompt otherwise.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>