Stage 17 #2: Connecting indicator + auto-retry, instead of red toasts
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 36s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 55s

Connectivity failures become state, not a toast on every attempt. A global online
signal (lib/connection.svelte.ts) flips on a transport unavailable / rate_limited and
on the live stream's drop, driving a pure-CSS header spinner + 'Connecting…' in place
of the title and softly disabling the in-game server actions (commit / exchange / pass
/ hint; local board/rack/reset stay live).

- transport: exec auto-retries with capped exponential backoff — every op on a
  rate-limit (rejected before processing, safe), reads only on unavailable (a mutation
  is never blindly re-sent, to avoid double-applying one whose response was lost; its
  button is disabled while offline so the player re-issues on reconnect). A reachability
  watcher (profile.get probe) and any successful traffic clear the signal.
- the old red error.unavailable toast is gone (handleError suppresses connection codes;
  the indicator replaces it). A server-data screen still opens with the spinner and
  fills on reconnect (global indicator + read auto-retry), so navigation is never dead.
- pure retry policy unit-tested (retry.ts); a mock-only window.__conn hook drives a
  Chromium+WebKit e2e (indicator shows offline, the action disables, both clear on
  reconnect). Full suite + build green.
- docs: ARCHITECTURE transport note, FUNCTIONAL (+ _ru), PLAN tracker (incl. #1 — the
  bot already drains all updates, no change).

Also records #1 as investigated/no-change in PLAN. Other server-action buttons (chat
send, profile save, …) still degrade to a safe no-op offline; visual disable is easy to
extend.
This commit is contained in:
Ilia Denisov
2026-06-09 01:48:20 +02:00
parent 844f26bbae
commit ef61b778fc
16 changed files with 334 additions and 18 deletions
+32
View File
@@ -0,0 +1,32 @@
<script lang="ts">
let { size = '1em' }: { size?: string } = $props();
</script>
<span class="sp" style="--sp-size: {size}" aria-hidden="true"></span>
<style>
/* A pure-CSS ring with a single gap (a ~3/4 arc) that rotates — no bundled graphics. It
inherits the surrounding text colour via currentColor, so it works in any header/theme. */
.sp {
display: inline-block;
width: var(--sp-size, 1em);
height: var(--sp-size, 1em);
border: 2px solid currentColor;
border-top-color: transparent;
border-radius: 50%;
opacity: 0.85;
vertical-align: -0.15em;
animation: sp-spin 0.7s linear infinite;
}
@keyframes sp-spin {
to {
transform: rotate(360deg);
}
}
/* Reduced motion: slow the rotation rather than freeze it (a still ring reads as broken). */
@media (prefers-reduced-motion: reduce) {
.sp {
animation-duration: 1.8s;
}
}
</style>