v0.7.8 — the setup screen was unreachable for anyone who had ever played
Third report of the same symptom, this time with the build confirmed current on screen, which ruled out v0.7.7's caching fault and left the real cause exposed. v0.7.5 skipped the setup screen whenever load() found a save, reasoned as "a saved game is a game to resume". A browser that has ever played solitaire always has one, so the door could never reach the screen again — and the fresh private window that appeared to vindicate v0.7.7 simply had no save. Two real faults were stacked; the caching one is fixed and had been masking this. The door outranks a saved game now: ?solitaire is a request to set one up, while a bare reload still resumes (pinned by its own test). Since Deal clears the save, the screen carries #ss-resume and says what Deal costs, so the door cannot destroy a game in progress. Also, per Jesse, riding along rather than taking its own release: the splash footer now names both ways to play. 868 tests pass, four new. The reproduction was a failing test written before the fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdG46Ja2PEDBkpqiDazMoX
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
af68aac78d
commit
193800a649
+1
-1
@@ -100,7 +100,7 @@ footer{margin-top:26px;color:var(--dim);font-size:11px;display:flex;gap:18px;fle
|
||||
|
||||
<footer>
|
||||
<span>build <span id="build">__BUILD__</span></span>
|
||||
<span>solitaire runs entirely in your browser — no server code required</span>
|
||||
<span>Multiplayer runs on StartOS server. Solitaire runs entirely in your browser.</span>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
|
||||
+35
-9
@@ -773,16 +773,24 @@ function start(): void {
|
||||
* (Jesse, 2026-08-29 — "let the user choose their options like the start of a multiplayer game";
|
||||
* "asking first is the only path").
|
||||
*
|
||||
* A saved game or an explicit `seed=` both mean this visit is not "no plan yet" — a saved game is
|
||||
* a game to resume, and a seed names a specific deal someone already chose to share or bookmark,
|
||||
* the same reasoning `?lobby` already uses to skip past the doors on an invite link. `hand` is the
|
||||
* one field every `commitNewGame` write always sets (`rulesToUrl`), so its presence means this
|
||||
* navigation IS the setup screen's own Deal button, landing back here to actually deal — checking
|
||||
* it is what stops the screen asking itself the question a second time.
|
||||
* Three things answer the question and so skip the screen, in this order of precedence:
|
||||
* `hand` (every `commitNewGame` write sets it, so this navigation IS the Deal button landing back
|
||||
* here to deal), `seed` (a specific deal someone chose to share or bookmark), and — only when the
|
||||
* player did not explicitly ask to set one up — an existing save, which is a game to resume.
|
||||
*
|
||||
* THE DOOR OUTRANKS A SAVED GAME, and getting that wrong is what made this feature unreachable
|
||||
* for three releases. v0.7.5 skipped the screen whenever `load()` found ANYTHING, reasoned as "a
|
||||
* saved game is a game to resume" — but a browser that has ever played solitaire always has one,
|
||||
* so the door could never reach the screen again. Reported three times (Jesse, 2026-08-29 twice
|
||||
* and 2026-08-30); a private window appeared to absolve it only because it had never played and
|
||||
* so had no save. Clicking "Play solitaire" is a request to set a game up, not to resume one — a
|
||||
* BARE reload is the resume case, and still is. `#ss-resume` is what keeps the save reachable, so
|
||||
* this costs nobody the game they were playing.
|
||||
*/
|
||||
if (!saved && requested === null && !params.has('hand')) {
|
||||
const askedToSetUp = params.get('solitaire') !== null;
|
||||
if (requested === null && !params.has('hand') && (askedToSetUp || !saved)) {
|
||||
showScreen('solitairesetup');
|
||||
runSolitaireSetup(params);
|
||||
runSolitaireSetup(params, saved !== null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2173,7 +2181,7 @@ if (newBtn && dlg) {
|
||||
* Solitaire defaults, since there is no live game to compare against yet, and reuses the identical
|
||||
* `wireGameTypeBlock`/`commitNewGame` pair the in-game dialog uses — the two are one design, not two.
|
||||
*/
|
||||
function runSolitaireSetup(params: URLSearchParams): void {
|
||||
function runSolitaireSetup(params: URLSearchParams, hasSave = false): void {
|
||||
const screen = document.getElementById('solitairesetup');
|
||||
const dealBtn = document.getElementById('ss-deal');
|
||||
if (!screen || !dealBtn) return;
|
||||
@@ -2185,6 +2193,24 @@ function runSolitaireSetup(params: URLSearchParams): void {
|
||||
const seedField = document.getElementById('ss-seed') as HTMLInputElement | null;
|
||||
if (seedField) seedField.value = params.get('seed') ?? '';
|
||||
|
||||
/**
|
||||
* THE WAY BACK TO A GAME IN PROGRESS, and the reason the door is allowed to outrank a save at all.
|
||||
* Dealing from here calls `clearSave()`, so a player who reached this screen from the splash — by
|
||||
* clicking "Play solitaire", which nobody reads as "throw away what I was playing" — needs their
|
||||
* game one button away and needs to be told what Deal costs.
|
||||
*
|
||||
* Resuming is a navigation to the BARE url rather than a session built here: `start()` already
|
||||
* resumes a save on a bare load, and routing both paths through it keeps one place that turns a
|
||||
* URL into a game.
|
||||
*/
|
||||
const resumeBtn = document.getElementById('ss-resume');
|
||||
const savedNote = document.getElementById('ss-saved-note');
|
||||
if (resumeBtn) {
|
||||
resumeBtn.hidden = !hasSave;
|
||||
resumeBtn.onclick = () => void (location.search = '');
|
||||
}
|
||||
if (savedNote) savedNote.hidden = !hasSave;
|
||||
|
||||
ss.selectPreset('solitaire');
|
||||
dealBtn.onclick = () => commitNewGame(ss, seedField?.value ?? '');
|
||||
}
|
||||
|
||||
@@ -765,7 +765,15 @@ ul.blocked li{padding:2px 0}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- Shown only when `station-master.save.v1` holds a game. Dealing from this screen CLEARS that
|
||||
save (`commitNewGame` calls `clearSave`), so without a way back the door would be a way to
|
||||
lose a game in progress — and the door is reached by clicking "Play solitaire", which nobody
|
||||
reads as "discard what I was playing". -->
|
||||
<p class="ng-note" id="ss-saved-note" hidden>You have a solitaire game in progress. Dealing a new
|
||||
one below replaces it — there is no undo for that.</p>
|
||||
|
||||
<menu class="ng-buttons">
|
||||
<button id="ss-resume" type="button" hidden>Continue saved game</button>
|
||||
<button id="ss-deal" type="button">Deal</button>
|
||||
</menu>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user