diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index cf1fb2e6..5d41ae06 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -1357,6 +1357,11 @@ "marker": "marker", "updated": "updated" }, + "animation": { + "loop": "loop", + "on": "on", + "off": "off" + }, "panels": { "rollups": "Scene metrics", "boundaries": "Safety boundaries", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 3458ddc0..fe873e7c 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -1357,6 +1357,11 @@ "marker": "部署 marker", "updated": "更新" }, + "animation": { + "loop": "動畫迴圈", + "on": "開啟", + "off": "關閉" + }, "panels": { "rollups": "場景指標", "boundaries": "安全邊界", diff --git a/apps/web/src/app/[locale]/openclaw/live-ops-space/page.tsx b/apps/web/src/app/[locale]/openclaw/live-ops-space/page.tsx index 76f47e58..b6f27528 100644 --- a/apps/web/src/app/[locale]/openclaw/live-ops-space/page.tsx +++ b/apps/web/src/app/[locale]/openclaw/live-ops-space/page.tsx @@ -190,6 +190,11 @@ export default function OpenClawLiveOpsSpacePage({ "host_or_k8s_write_performed", ].every((key) => scene.boundaries[key] === false); }, [scene]); + const packetSlots = useMemo(() => { + const count = Math.max(8, Math.min(14, scene?.rollups.animated_entity_count ?? 10)); + return Array.from({ length: count }, (_, index) => index); + }, [scene]); + const animationLoopEnabled = scene?.room.animation_loop.enabled === true; return ( @@ -238,16 +243,53 @@ export default function OpenClawLiveOpsSpacePage({ -
-
-
+
+
+
+
+
+ + + +
+
+ + + +
+ +
+
+ + +
+
+
+ + {packetSlots.map((slot) => ( + + ))} {zones.map((zone) => { const Icon = zoneIconByKind[zone.kind] ?? Activity; return (
@@ -263,6 +305,7 @@ export default function OpenClawLiveOpsSpacePage({ {workItems.map((item, index) => (
(
+
@@ -306,6 +362,19 @@ export default function OpenClawLiveOpsSpacePage({ {t("source.marker")} {shortValue(scene?.source.deploy_readback_marker)} + + {t("animation.loop")}{" "} + {animationLoopEnabled ? t("animation.on") : t("animation.off")} ·{" "} + {scene?.room.animation_loop.tick_ms ?? 0}ms + {t("source.updated")}{" "} {updatedAt @@ -328,7 +397,7 @@ export default function OpenClawLiveOpsSpacePage({
-
+
{[ ["agents", scene?.rollups.agent_count], ["workItems", scene?.rollups.work_item_count], @@ -454,9 +523,55 @@ export default function OpenClawLiveOpsSpacePage({ animation: openclaw-work-token-flow 6.2s ease-in-out infinite; will-change: transform; } + @keyframes openclaw-flow-packet { + 0% { + transform: translate3d(-8px, 10px, 0) scale(0.76); + opacity: 0; + } + 18% { + opacity: 1; + } + 50% { + transform: translate3d(22px, -14px, 0) scale(1); + opacity: 0.86; + } + 100% { + transform: translate3d(48px, -28px, 0) scale(0.72); + opacity: 0; + } + } + @keyframes openclaw-screen-sweep { + 0% { + transform: translateX(-100%); + opacity: 0; + } + 30% { + opacity: 0.42; + } + 100% { + transform: translateX(160%); + opacity: 0; + } + } + .openclaw-flow-packet { + animation: openclaw-flow-packet 5.8s ease-in-out infinite; + will-change: transform, opacity; + } + .openclaw-screen-sweep { + background: linear-gradient( + 90deg, + transparent, + rgba(112, 214, 164, 0.3), + transparent + ); + animation: openclaw-screen-sweep 3.8s linear infinite; + will-change: transform, opacity; + } @media (prefers-reduced-motion: reduce) { .openclaw-agent, - .openclaw-work-token { + .openclaw-work-token, + .openclaw-flow-packet, + .openclaw-screen-sweep { animation: none; } }