The extension question was hidden behind the results screen (Gitea#11)

Jesse, playing v0.7.8: "Solitaire game ended. I did not have an option
to extend the game by a day."

The engine and the Frame were right — checked before changing anything.
A solitaire game at the end of its timetable reaches awaitingExtension
with extensionVotes [null], and renderEnding writes "play one more Day"
into #actions. It then opens #resultsdlg, which is MODAL, so those
buttons were directly underneath a dialog whose only control was Close.

The results dialog now carries the question itself, hidden unless a vote
is pending: Play One More Day / End the Game Here, casting the same
game.extend intent. The #actions buttons stay as the fallback once it is
closed.

TODO.md #35 recorded extended play as verified on phoenix.local — over
the HTTP API, which renders no dialog. What was proven was that the
server supports it, not that a player can reach it. Noted there.

Rides along in the unshipped v0.7.9. 870 tests pass, one new.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
This commit is contained in:
Jesse.Markowitz
2026-08-30 01:26:03 -04:00
co-authored by Claude Sonnet 5
parent f2c87b6871
commit e035dda5a3
5 changed files with 101 additions and 2 deletions
+27
View File
@@ -1554,6 +1554,33 @@ function showResults(f: Frame): void {
const body = document.getElementById('resultsbody');
if (!dlg || !body) return;
body.innerHTML = resultsHtml(f);
/**
* ASK THE EXTENSION QUESTION ON THE THING THAT IS ACTUALLY IN FRONT OF THE PLAYER.
*
* This dialog opens itself at every ending and it is MODAL, so `renderEnding`'s own "play one more
* Day" buttons — written into `#actions` — are behind it. The player read a results screen offering
* nothing but Close and concluded the game was over, which is exactly what it looked like (Jesse,
* 2026-08-30). Gitea#11 was verified over the HTTP API, where there is no dialog to be behind.
*
* The buttons in `#actions` stay, and are still correct: they are what remains after this is
* closed, and what a player who reopened the results with "see the full results" comes back to.
* Voting from either place submits the same intent.
*/
const yes = document.getElementById('rs-extend-yes') as HTMLButtonElement | null;
const no = document.getElementById('rs-extend-no') as HTMLButtonElement | null;
const asking = f.status === 'awaitingExtension' && f.extensionVotes[f.viewer] === null;
if (yes && no) {
yes.hidden = !asking;
no.hidden = !asking;
if (asking) {
// `method="dialog"` closes it on click; the vote rides along. Assigned every time rather than
// once, because `f` is a fresh Frame on each ending.
yes.onclick = () => void session.submit({ type: 'game.extend', player: f.viewer, agree: true });
no.onclick = () => void session.submit({ type: 'game.extend', player: f.viewer, agree: false });
}
}
// A redraw can arrive while it is open — `showModal` throws on an already-open dialog rather
// than doing nothing (the same trap `noteDayEnd` documents).
if (!dlg.open) dlg.showModal();
+9
View File
@@ -1099,10 +1099,19 @@ ul.blocked li{padding:2px 0}
<!-- THE END-OF-GAME RESULTS (Gitea#16). Filled by `resultsHtml` and opened from `renderEnding`,
which puts it up once per ending unasked and leaves a button to reopen it. Reopenable matters:
Gitea#11 lets a table play past the end, and continuing must not cost you the results screen. -->
<!-- THE EXTENSION QUESTION IS ASKED HERE, not only behind this dialog (Gitea#11 + #16).
This opens ITSELF at every ending, and it is modal — so `renderEnding`'s "play one more Day"
buttons, which it writes into `#actions`, sit underneath it. A player saw a results screen whose
only control was Close and reasonably concluded the game was over: reported by Jesse
2026-08-30, "Solitaire game ended. I did not have an option to extend the game by a day."
Gitea#11 was verified over the HTTP API, which renders no dialog, so the browser never was.
The two extension buttons are hidden unless the game is actually awaiting a vote. -->
<dialog id="resultsdlg" aria-labelledby="rs-title">
<form method="dialog">
<div id="resultsbody"></div>
<menu class="ng-buttons">
<button value="extend-yes" id="rs-extend-yes" type="submit" hidden>Play One More Day</button>
<button value="extend-no" id="rs-extend-no" type="submit" hidden>End the Game Here</button>
<button value="ok" id="rs-ok" type="submit">Close</button>
</menu>
</form>