Stable Diffusion: clickable pod-status dot in the chat bar (copy of the settings indicator; click toggles warmup/shutdown)

738ff23c09bfe7e2cb9c40337737de38d1c2cf96

permissionBRICK <40219477+permissionBRICK@users.noreply.github.com>

2 files changed, +45 -2Showing whitespace changes
public/scripts/extensions/stable-diffusion/index.js+35 -2
@@ -1461,6 +1461,7 @@ const RUNPOD_PING_MS = 60000;
14611461
14621462let runpodStatusTimer = null;
14631463let runpodPingTimer = null;
1464+let runpodLastPhase = 'red';
14641465
14651466function getRunpodLazyUrl() {
14661467 return String(extension_settings.sd.runpod_lazy_url ?? '').trim().replace(/\/$/, '');
@@ -1477,8 +1478,12 @@ function renderRunpodStatus(status) {
14771478 return;
14781479 }
14791480 const phase = status?.state ?? 'red';
1480- dot.classList.remove('sd_runpod_red', 'sd_runpod_orange', 'sd_runpod_green');
1481+ runpodLastPhase = phase;
14811482 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+ }
14821487 const openLink = document.getElementById('sd_runpod_open');
14831488 if (openLink) {
14841489 const showLink = phase === 'green' && status?.url;
@@ -1497,9 +1502,32 @@ function renderRunpodStatus(status) {
14971502 } else {
14981503 text.textContent = 'off';
14991504 }
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+ }
15001509 return phase;
15011510}
15021511
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+
15031531async function pollRunpodStatus() {
15041532 const url = getRunpodLazyUrl();
15051533 if (!url) {
@@ -1547,6 +1575,11 @@ function runpodKeepalive() {
15471575function setupRunpodLoops() {
15481576 clearTimeout(runpodStatusTimer);
15491577 clearInterval(runpodPingTimer);
1578+ ensureRunpodBarDot();
1579+ const barDot = document.getElementById('sd_runpod_bar_dot');
1580+ if (barDot) {
1581+ barDot.style.display = getRunpodLazyUrl() ? '' : 'none';
1582+ }
15501583 if (!getRunpodLazyUrl()) {
15511584 renderRunpodStatus(null);
15521585 return;
public/scripts/extensions/stable-diffusion/style.css+10 -0
@@ -127,3 +127,13 @@
127127.sd_runpod_dot.sd_runpod_red { background: #d9534f; }
128128.sd_runpod_dot.sd_runpod_orange { background: #f0ad4e; }
129129.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+}