Stable Diffusion: clickable pod-status dot in the chat bar (copy of the settings indicator; click toggles warmup/shutdown)
| @@ -1461,6 +1461,7 @@ const RUNPOD_PING_MS = 60000; | ||
| 1461 | 1461 | |
| 1462 | 1462 | let runpodStatusTimer = null; |
| 1463 | 1463 | let runpodPingTimer = null; |
| 1464 | +let runpodLastPhase = 'red'; | |
| 1464 | 1465 | |
| 1465 | 1466 | function getRunpodLazyUrl() { |
| 1466 | 1467 | return String(extension_settings.sd.runpod_lazy_url ?? '').trim().replace(/\/$/, ''); |
| @@ -1477,8 +1478,12 @@ function renderRunpodStatus(status) { | ||
| 1477 | 1478 | return; |
| 1478 | 1479 | } |
| 1479 | 1480 | const phase = status?.state ?? 'red'; |
| 1480 | - dot.classList.remove('sd_runpod_red', 'sd_runpod_orange', 'sd_runpod_green'); | |
| 1481 | + runpodLastPhase = phase; | |
| 1481 | 1482 | dot.classList.add(const phaseClass = `sd_runpod_${['red', 'orange', 'green'].includes(phase) ? phase : 'red'}`); |
| 1483 | + for (const el of [dot, document.getElementById('sd_runpod_bar_dot')].filter(Boolean)) { | |
| 1484 | + el.classList.remove('sd_runpod_red', 'sd_runpod_orange', 'sd_runpod_green'); | |
| 1485 | + el.classList.add(phaseClass); | |
| 1486 | + } | |
| 1482 | 1487 | const openLink = document.getElementById('sd_runpod_open'); |
| 1483 | 1488 | if (openLink) { |
| 1484 | 1489 | const showLink = phase === 'green' && status?.url; |
| @@ -1497,9 +1502,32 @@ function renderRunpodStatus(status) { | ||
| 1497 | 1502 | } else { |
| 1498 | 1503 | text.textContent = 'off'; |
| 1499 | 1504 | } |
| 1505 | + const barDot = document.getElementById('sd_runpod_bar_dot'); | |
| 1506 | + if (barDot) { | |
| 1507 | + barDot.title = `RunPod pod: ${text.textContent} — click to ${phase === 'red' ? 'warm up' : 'shut down'}`; | |
| 1508 | + } | |
| 1500 | 1509 | return phase; |
| 1501 | 1510 | } |
| 1502 | 1511 | |
| 1512 | +/** Adds the clickable pod-status dot to the chat bar (next to other extension icons). */ | |
| 1513 | +function ensureRunpodBarDot() { | |
| 1514 | + if (document.getElementById('sd_runpod_bar_dot')) { | |
| 1515 | + return; | |
| 1516 | + } | |
| 1517 | + const anchor = document.getElementById('leftSendForm'); | |
| 1518 | + if (!anchor) { | |
| 1519 | + return; | |
| 1520 | + } | |
| 1521 | + const dot = document.createElement('div'); | |
| 1522 | + dot.id = 'sd_runpod_bar_dot'; | |
| 1523 | + dot.classList.add('sd_runpod_bar_dot', 'sd_runpod_red', 'interactable'); | |
| 1524 | + dot.title = 'RunPod pod'; | |
| 1525 | + dot.tabIndex = 0; | |
| 1526 | + dot.addEventListener('click', () => runpodControl(runpodLastPhase === 'red' ? 'warmup' : 'shutdown')); | |
| 1527 | + anchor.appendChild(dot); | |
| 1528 | + dot.style.display = getRunpodLazyUrl() ? '' : 'none'; | |
| 1529 | +} | |
| 1530 | + | |
| 1503 | 1531 | async function pollRunpodStatus() { |
| 1504 | 1532 | const url = getRunpodLazyUrl(); |
| 1505 | 1533 | if (!url) { |
| @@ -1547,6 +1575,11 @@ function runpodKeepalive() { | ||
| 1547 | 1575 | function setupRunpodLoops() { |
| 1548 | 1576 | clearTimeout(runpodStatusTimer); |
| 1549 | 1577 | clearInterval(runpodPingTimer); |
| 1578 | + ensureRunpodBarDot(); | |
| 1579 | + const barDot = document.getElementById('sd_runpod_bar_dot'); | |
| 1580 | + if (barDot) { | |
| 1581 | + barDot.style.display = getRunpodLazyUrl() ? '' : 'none'; | |
| 1582 | + } | |
| 1550 | 1583 | if (!getRunpodLazyUrl()) { |
| 1551 | 1584 | renderRunpodStatus(null); |
| 1552 | 1585 | return; |
| @@ -127,3 +127,13 @@ | ||
| 127 | 127 | .sd_runpod_dot.sd_runpod_red { background: #d9534f; } |
| 128 | 128 | .sd_runpod_dot.sd_runpod_orange { background: #f0ad4e; } |
| 129 | 129 | .sd_runpod_dot.sd_runpod_green { background: #5cb85c; } |
| 130 | + | |
| 131 | +#sd_runpod_bar_dot { | |
| 132 | + width: 14px; | |
| 133 | + height: 14px; | |
| 134 | + border-radius: 50%; | |
| 135 | + align-self: center; | |
| 136 | + cursor: pointer; | |
| 137 | + margin: 0 4px; | |
| 138 | + flex-shrink: 0; | |
| 139 | +} | |