diff --git a/python/sglang/multimodal_gen/apps/realtime_webui/app.js b/python/sglang/multimodal_gen/apps/realtime_webui/app.js index e42cb30a1..02f0ee997 100644 --- a/python/sglang/multimodal_gen/apps/realtime_webui/app.js +++ b/python/sglang/multimodal_gen/apps/realtime_webui/app.js @@ -264,32 +264,6 @@ function drawIdle() { renderedPreviewFrames = 0; ctx.fillStyle = "#11140f"; ctx.fillRect(0, 0, w, h); - - const surface = ctx.createLinearGradient(0, 0, 0, h); - surface.addColorStop(0, "rgba(238,241,236,0.045)"); - surface.addColorStop(0.5, "rgba(238,241,236,0.012)"); - surface.addColorStop(1, "rgba(0,0,0,0.16)"); - ctx.fillStyle = surface; - ctx.fillRect(0, 0, w, h); - - ctx.strokeStyle = "rgba(238,241,236,0.11)"; - ctx.lineWidth = 1; - ctx.strokeRect(0.5, 0.5, w - 1, h - 1); - ctx.strokeStyle = "rgba(238,241,236,0.08)"; - ctx.beginPath(); - if (ctx.roundRect) { - ctx.roundRect(w * 0.38, h * 0.42, w * 0.24, h * 0.16, 18); - } else { - ctx.rect(w * 0.38, h * 0.42, w * 0.24, h * 0.16); - } - ctx.stroke(); - - ctx.fillStyle = "rgba(238,241,236,0.22)"; - for (let i = -1; i <= 1; i++) { - ctx.beginPath(); - ctx.arc(w * 0.5 + i * 22, h * 0.5, 4.5, 0, Math.PI * 2); - ctx.fill(); - } } function resetStreamStats() { diff --git a/python/sglang/multimodal_gen/apps/realtime_webui/index.html b/python/sglang/multimodal_gen/apps/realtime_webui/index.html index c0cfadac8..804d33358 100644 --- a/python/sglang/multimodal_gen/apps/realtime_webui/index.html +++ b/python/sglang/multimodal_gen/apps/realtime_webui/index.html @@ -4,7 +4,7 @@ sglang-diffusion Realtime Studio - +
@@ -145,6 +145,6 @@
- + diff --git a/python/sglang/multimodal_gen/apps/realtime_webui/styles.css b/python/sglang/multimodal_gen/apps/realtime_webui/styles.css index 77bf5454b..0e937e678 100644 --- a/python/sglang/multimodal_gen/apps/realtime_webui/styles.css +++ b/python/sglang/multimodal_gen/apps/realtime_webui/styles.css @@ -281,6 +281,11 @@ button:focus-visible { font-size: 12px; } +.topbar > * { + flex: 0 0 auto; + align-self: center; +} + .topbar-spacer { flex: 1; } .preview-scale-control { display: inline-flex; @@ -290,6 +295,7 @@ button:focus-visible { margin: 0; color: rgba(232, 234, 223, 0.72); font-size: 11px; + line-height: 1; } .preview-scale-control input { width: 92px; @@ -307,17 +313,20 @@ button:focus-visible { #statusText { display: inline-block; min-width: 92px; + line-height: 1; } #chunkText { display: inline-block; min-width: 70px; + line-height: 1; } .stage-stat { display: inline-flex; - align-items: baseline; + align-items: center; gap: 5px; min-width: max-content; color: rgba(232, 234, 223, 0.72); + line-height: 1; } .stage-stat b { color: #fffdf7; @@ -357,34 +366,12 @@ button:focus-visible { } .preview-loader { - position: relative; - width: 7px; - height: 7px; + width: 18px; + height: 18px; border-radius: 50%; - background: rgba(238, 241, 236, 0.88); - animation: previewDotPulse 1.05s ease-in-out infinite; -} - -.preview-loader::before, -.preview-loader::after { - content: ""; - position: absolute; - top: 0; - width: 7px; - height: 7px; - border-radius: 50%; - background: rgba(238, 241, 236, 0.88); - animation: previewDotPulse 1.05s ease-in-out infinite; -} - -.preview-loader::before { - left: -16px; - animation-delay: -0.18s; -} - -.preview-loader::after { - left: 16px; - animation-delay: 0.18s; + border: 2px solid rgba(238, 241, 236, 0.22); + border-top-color: rgba(238, 241, 236, 0.82); + animation: previewProgressSpin 0.8s linear infinite; } .stage-controls { @@ -626,7 +613,6 @@ button:focus-visible { .preview-scale-control { min-width: 160px; } } -@keyframes previewDotPulse { - 0%, 80%, 100% { opacity: 0.32; } - 40% { opacity: 1; } +@keyframes previewProgressSpin { + to { transform: rotate(360deg); } } diff --git a/python/sglang/multimodal_gen/test/unit/realtime/test_realtime_webui.py b/python/sglang/multimodal_gen/test/unit/realtime/test_realtime_webui.py index 73cef4b84..99031d7ef 100644 --- a/python/sglang/multimodal_gen/test/unit/realtime/test_realtime_webui.py +++ b/python/sglang/multimodal_gen/test/unit/realtime/test_realtime_webui.py @@ -48,8 +48,8 @@ def test_realtime_webui_presets_do_not_emit_camera_scripts(): assert "Info" not in index_html assert 'id="steps" type="number" value="4"' in index_html assert 'id="guidance" type="number" value="1"' in index_html - assert "styles.css?v=realtime-sr-v35" in index_html - assert "app.js?v=realtime-sr-v35" in index_html + assert "styles.css?v=realtime-sr-v38" in index_html + assert "app.js?v=realtime-sr-v38" in index_html assert 'const DECODER_WORKER_URL = "./decoder_worker.js?v=rgb-worker-v6";' in app_js assert "const DEFAULT_TARGET_FPS = 25;" in app_js assert "const DEFAULT_FRAME_INTERPOLATION_EXP = 1;" in app_js @@ -58,6 +58,8 @@ def test_realtime_webui_presets_do_not_emit_camera_scripts(): assert "const DEFAULT_PREVIEW_SCALE = 120;" in app_js assert 'setPreviewState("waiting")' in app_js assert "stage.dataset.previewState = state" in app_js + assert "previewProgressSpin" in styles_css + assert "previewDotPulse" not in styles_css assert 'document.querySelector(".preview-frame")' in app_js assert 'previewFrame.style.setProperty("--preview-scale"' in app_js assert "cancelAnimationFrame(previewScaleFrame)" in app_js