fix(custom-model): move the switching/loading status to a centred banner

The "Claude started - switching to <endpoint>..." and "Loading <model> on
<endpoint>... this can take a while" messages lived in the top-right toast
corner along with everything else, easy to miss given they can each sit on
screen for well over a minute (a real llama-swap model load).

Adds _showCenterStatus() (panels-ui.js): a single, reused, screen-centred
banner with a spinner, non-blocking (no backdrop, pointer-events: none on
the wrapper) so it never gets in the way of using the app while it's up.
Both call sites (_runCustomModelEntryViaRestart's switching message,
_watchLlamaSwapLoading's loading message) now use it instead of showToast.
Every OTHER status in these two flows - the llama-swap conflict warning
already moved to its own modal, apply failures, cancellation, and
_watchLlamaSwapLoading's own final "ready"/"still waiting" outcome - stays
exactly where it was, in the corner.

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 15:40:43 +08:00
co-authored by Claude Sonnet 5
parent 2936ba6e3d
commit 01b32ee6cd
4 changed files with 153 additions and 33 deletions
+60
View File
@@ -8522,6 +8522,66 @@ kbd {
opacity: 1;
}
/* Custom Model Endpoint Profiles: the "switching backends" / "loading model" states
(docs/custom-model-endpoints-plan.md) — a small set of messages prominent and
screen-centred rather than corner toasts, since they can sit on screen for well
over a minute (a real llama-swap model load) and are easy to mistake for nothing
happening. Non-blocking: `pointer-events: none` on the wrapper (no backdrop, no
click-catcher) with `auto` restored only on the card itself, purely so the text
inside remains selectable — there is nothing to click to dismiss it early. */
.center-status-banner {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0.96);
z-index: 10001;
display: flex;
align-items: center;
gap: 0.75rem;
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 10px;
padding: 1rem 1.5rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
font-size: 0.95rem;
font-weight: 500;
color: var(--text);
max-width: min(90vw, 460px);
text-align: left;
opacity: 0;
pointer-events: none;
transition:
opacity 0.2s ease,
transform 0.2s ease;
}
.center-status-banner.show {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
.center-status-spinner {
flex-shrink: 0;
width: 18px;
height: 18px;
border-radius: 50%;
border: 2px solid var(--border);
border-top-color: var(--accent, var(--text));
animation: center-status-spin 0.8s linear infinite;
}
@keyframes center-status-spin {
to {
transform: rotate(360deg);
}
}
.center-status-text {
pointer-events: auto;
white-space: pre-wrap;
word-break: break-word;
}
.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); }