feat(custom-model): live countdown on the loading banner; timeout is now an error

The loading banner now shows a live countdown against its own timeout
(updated every poll, so every second by default) instead of a static
"this can take a while" — e.g. "Loading qwen3.8-27b (16.4 GB, typically
~1-3 min) on llama-swap - 47s remaining".

If the countdown reaches zero and the model still isn't ready, this is now
treated as a real failure rather than a "keep waiting" shrug:
- The banner turns into a sticky error (_showCenterStatus gains a `type`
  option - 'error' drops the spinner and adds a close button, since nothing
  is "in progress" anymore and a sticky message needs a way to dismiss it),
  naming the llama-swap server's own logs as where to look for detail.
- The session that load was for is closed automatically (closeSession) -
  requested explicitly: a console left open and pointed at a model that
  never finished loading is worse than no console at all. Both apply paths
  now thread the new session's id through to _watchLlamaSwapLoading for
  this (new required 3rd parameter, after endpointId/modelId).

_watchLlamaSwapGeneration's existing stale-call guard extends naturally to
this: a superseded call's own eventual timeout recognises it no longer owns
the banner and neither shows the error nor closes a session that may by
then belong to a different, newer launch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RqZeHrRS6DYcGcGX2p9EwG
This commit is contained in:
Devvyn
2026-09-16 20:22:02 +08:00
co-authored by Claude Sonnet 5
parent 55dae31530
commit 7bbe408e44
7 changed files with 181 additions and 64 deletions
+26
View File
@@ -8577,11 +8577,37 @@ kbd {
}
.center-status-text {
flex: 1;
pointer-events: auto;
white-space: pre-wrap;
word-break: break-word;
}
/* Error variant: the load didn't finish in time — nothing is "in progress" anymore (no
spinner), and since this one doesn't dismiss itself, it needs a close button the user
can actually click, so pointer-events is restored here too (see the wrapper's own
comment on why that's `none` by default). */
.center-status-error {
border-color: rgba(239, 68, 68, 0.5);
}
.center-status-close {
flex-shrink: 0;
pointer-events: auto;
background: none;
border: none;
color: inherit;
opacity: 0.6;
font-size: 1.2rem;
line-height: 1;
padding: 0 0.15rem;
cursor: pointer;
}
.center-status-close:hover {
opacity: 1;
}
.toast-success { border-color: rgba(34, 197, 94, 0.4); }
.toast-error { border-color: rgba(239, 68, 68, 0.4); }
.toast-warning { border-color: rgba(234, 179, 8, 0.4); }