feat(ui): redesign live-ops-space to Option A (Light Mode Glassmorphism)
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m12s
CD Pipeline / build-and-deploy (push) Successful in 5m26s
CD Pipeline / post-deploy-checks (push) Successful in 2m39s

This commit is contained in:
Your Name
2026-07-03 23:45:51 +08:00
parent 068dcc1bf3
commit 56389f79c7
3 changed files with 87 additions and 69 deletions

View File

@@ -110,11 +110,11 @@ function stateLabel(t: ReturnType<typeof useTranslations>, state: string): strin
} }
function stateClass(state: string): string { function stateClass(state: string): string {
if (state === "working") return "border-[#4a90d9]/50 bg-[#4a90d9]/10 backdrop-blur-[20px] text-[#60a5fa] shadow-[0_0_15px_rgba(74,144,217,0.3)]"; if (state === "working") return "border-[#4a90d9]/30 bg-[#f0f7ff] backdrop-blur-[20px] text-[#1f5b9b] shadow-[0_0_15px_rgba(74,144,217,0.2)]";
if (state === "verified") return "border-[#4f9b62]/50 bg-[#4f9b62]/10 backdrop-blur-[20px] text-[#8fc29a]"; if (state === "verified") return "border-[#8fc29a]/50 bg-[#f0faf2] backdrop-blur-[20px] text-[#17602a]";
if (state === "blocked") return "border-[#d95f4f]/50 bg-[#d95f4f]/10 backdrop-blur-[20px] text-[#e2a29b]"; if (state === "blocked") return "border-[#e2a29b]/50 bg-[#fff1f0] backdrop-blur-[20px] text-[#9f2f25]";
if (state === "waiting") return "border-[#f0bd69]/50 bg-[#f0bd69]/10 backdrop-blur-[20px] text-[#fcd34d]"; if (state === "waiting") return "border-[#f0bd69]/50 bg-[#fff7e8] backdrop-blur-[20px] text-[#8a5a08]";
return "border-white/10 bg-white/5 backdrop-blur-[20px] text-[#a3a097]"; return "border-[#d8d3c7] bg-white/70 backdrop-blur-[20px] text-[#5f5b52]";
} }
function stateFromWorkItemStatus(status: string): string { function stateFromWorkItemStatus(status: string): string {
@@ -198,17 +198,17 @@ export default function OpenClawLiveOpsSpacePage({
return ( return (
<AppLayout locale={params.locale} fullBleed> <AppLayout locale={params.locale} fullBleed>
<main className="min-h-screen overflow-x-hidden bg-[#141413] text-[#f5f4ed]"> <main className="min-h-screen overflow-x-hidden bg-[#f5f4ed] text-[#141413]">
<div className="mx-auto flex w-full max-w-[1520px] flex-col gap-4 px-3 py-4 lg:px-5"> <div className="mx-auto flex w-full max-w-[1520px] flex-col gap-4 px-3 py-4 lg:px-5">
<section className="grid min-w-0 items-start gap-4 lg:grid-cols-[minmax(0,1fr)_360px]"> <section className="grid min-w-0 items-start gap-4 lg:grid-cols-[minmax(0,1fr)_360px]">
{/* Main Stage */} {/* Main Stage */}
<div className="min-w-0 rounded-2xl border border-white/10 bg-[#1c1c1a]/50 shadow-2xl backdrop-blur-3xl"> <div className="min-w-0 rounded-2xl border border-[#d8d3c7] bg-white/60 shadow-lg backdrop-blur-3xl">
<div className="flex min-w-0 flex-wrap items-center justify-between gap-3 border-b border-white/10 px-6 py-5"> <div className="flex min-w-0 flex-wrap items-center justify-between gap-3 border-b border-[#e5e1d8] px-6 py-5">
<div className="min-w-0"> <div className="min-w-0">
<p className="text-xs font-semibold uppercase tracking-widest text-[#a3a097]"> <p className="text-xs font-semibold uppercase tracking-widest text-[#77736a]">
OpenClaw OpenClaw
</p> </p>
<h1 className="mt-1 flex items-center gap-3 break-words text-2xl font-bold leading-tight text-[#f5f4ed] sm:text-3xl"> <h1 className="mt-1 flex items-center gap-3 break-words text-2xl font-bold leading-tight text-[#141413] sm:text-3xl">
{t("title")} {t("title")}
{status === "ready" && ( {status === "ready" && (
<span className="relative flex h-3 w-3"> <span className="relative flex h-3 w-3">
@@ -223,8 +223,8 @@ export default function OpenClawLiveOpsSpacePage({
className={cn( className={cn(
"inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-semibold uppercase tracking-wider backdrop-blur-md", "inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-semibold uppercase tracking-wider backdrop-blur-md",
status === "ready" status === "ready"
? "border-[#4f9b62]/30 bg-[#4f9b62]/10 text-[#8fc29a]" ? "border-[#8fc29a] bg-[#f0faf2] text-[#17602a]"
: "border-[#d9b36f]/30 bg-[#d9b36f]/10 text-[#fcd34d]", : "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
)} )}
> >
{status === "ready" ? ( {status === "ready" ? (
@@ -241,7 +241,7 @@ export default function OpenClawLiveOpsSpacePage({
<button <button
type="button" type="button"
onClick={() => void fetchSceneState()} onClick={() => void fetchSceneState()}
className="inline-flex h-9 w-9 items-center justify-center rounded-full border border-white/10 bg-white/5 text-[#a3a097] transition-all hover:bg-white/10 hover:text-[#f5f4ed] hover:shadow-[0_0_15px_rgba(255,255,255,0.1)]" className="inline-flex h-9 w-9 items-center justify-center rounded-full border border-[#d8d3c7] bg-white text-[#4f4b44] transition-all hover:bg-[#f0f7ff] hover:text-[#1f5b9b] hover:shadow-[0_0_15px_rgba(74,144,217,0.15)] hover:border-[#4a90d9]/30"
title={t("actions.refresh")} title={t("actions.refresh")}
aria-label={t("actions.refresh")} aria-label={t("actions.refresh")}
> >
@@ -253,42 +253,42 @@ export default function OpenClawLiveOpsSpacePage({
{/* Isometric Room */} {/* Isometric Room */}
<div <div
data-testid="openclaw-live-ops-room" data-testid="openclaw-live-ops-room"
className="relative aspect-[16/10] min-h-[420px] overflow-hidden bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-[#1c1c1a] via-[#141413] to-[#0a0a0a] sm:min-h-[560px] rounded-b-2xl" className="relative aspect-[16/10] min-h-[420px] overflow-hidden bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-white via-[#f5f4ed] to-[#e8eff3] sm:min-h-[560px] rounded-b-2xl"
> >
{/* Dark mode ISO Planes */} {/* Light mode ISO Planes */}
<div className="absolute inset-x-[3%] bottom-[4%] top-[7%] rotate-[-2deg] skew-y-[-7deg] border border-white/10 bg-[#141413] shadow-[0_30px_80px_rgba(0,0,0,0.8)]" /> <div className="absolute inset-x-[3%] bottom-[4%] top-[7%] rotate-[-2deg] skew-y-[-7deg] border border-[#b7c8d0] bg-[#fdfefe] shadow-[0_30px_80px_rgba(37,55,68,0.12)]" />
<div className="absolute inset-x-[5%] bottom-[9%] top-[11%] rotate-[-2deg] skew-y-[-7deg] bg-[linear-gradient(90deg,rgba(74,144,217,0.05)_1px,transparent_1px),linear-gradient(0deg,rgba(74,144,217,0.05)_1px,transparent_1px)] bg-[size:44px_44px] shadow-inner" /> <div className="absolute inset-x-[5%] bottom-[9%] top-[11%] rotate-[-2deg] skew-y-[-7deg] bg-[linear-gradient(90deg,rgba(74,144,217,0.14)_1px,transparent_1px),linear-gradient(0deg,rgba(74,144,217,0.11)_1px,transparent_1px)] bg-[size:44px_44px] shadow-inner" />
<div className="absolute left-[8%] top-[10%] h-[13%] w-[84%] rotate-[-2deg] skew-y-[-7deg] border border-white/5 bg-[#1c1c1a]" /> <div className="absolute left-[8%] top-[10%] h-[13%] w-[84%] rotate-[-2deg] skew-y-[-7deg] border border-[#c6d1d5] bg-[#e9eee8]" />
{/* Server racks / Databanks */} {/* Server racks / Databanks */}
<div className="absolute left-[14%] top-[14%] h-10 w-[17%] rotate-[-2deg] skew-y-[-7deg] border border-[#4a90d9]/20 bg-[#1a1f24] shadow-[0_10px_22px_rgba(0,0,0,0.5)]"> <div className="absolute left-[14%] top-[14%] h-10 w-[17%] rotate-[-2deg] skew-y-[-7deg] border border-[#aebbc0] bg-[#26333a] shadow-[0_10px_22px_rgba(37,55,68,0.22)]">
<span className="openclaw-screen-sweep absolute inset-0" /> <span className="openclaw-screen-sweep absolute inset-0" />
<span className="absolute left-2 top-2 h-1.5 w-14 bg-[#4f9b62] shadow-[0_0_8px_rgba(79,155,98,0.6)]" /> <span className="absolute left-2 top-2 h-1.5 w-14 bg-[#70d6a4] shadow-[0_0_8px_rgba(112,214,164,0.6)]" />
<span className="absolute bottom-2 left-2 h-1.5 w-9 bg-[#f0bd69] shadow-[0_0_8px_rgba(240,189,105,0.6)]" /> <span className="absolute bottom-2 left-2 h-1.5 w-9 bg-[#f0bd69] shadow-[0_0_8px_rgba(240,189,105,0.6)]" />
</div> </div>
<div className="absolute right-[14%] top-[14%] h-10 w-[17%] rotate-[-2deg] skew-y-[-7deg] border border-[#4a90d9]/20 bg-[#1a1f24] shadow-[0_10px_22px_rgba(0,0,0,0.5)]"> <div className="absolute right-[14%] top-[14%] h-10 w-[17%] rotate-[-2deg] skew-y-[-7deg] border border-[#aebbc0] bg-[#26333a] shadow-[0_10px_22px_rgba(37,55,68,0.22)]">
<span className="openclaw-screen-sweep absolute inset-0" /> <span className="openclaw-screen-sweep absolute inset-0" />
<span className="absolute left-2 top-2 h-1.5 w-11 bg-[#8fc29a] shadow-[0_0_8px_rgba(143,194,154,0.6)]" /> <span className="absolute left-2 top-2 h-1.5 w-11 bg-[#8fc29a] shadow-[0_0_8px_rgba(143,194,154,0.6)]" />
<span className="absolute bottom-2 left-2 h-1.5 w-16 bg-[#4a90d9] shadow-[0_0_10px_rgba(74,144,217,0.8)] animate-pulse" /> <span className="absolute bottom-2 left-2 h-1.5 w-16 bg-[#4a90d9] shadow-[0_0_10px_rgba(74,144,217,0.8)] animate-pulse" />
</div> </div>
{/* Central AI Core Platform */} {/* Central AI Core Platform */}
<div className="absolute left-1/2 top-[48%] h-[19%] w-[30%] -translate-x-1/2 -translate-y-1/2 rotate-[-2deg] skew-y-[-7deg] border border-[#d97757]/20 bg-[#1f1a18] shadow-[0_22px_42px_rgba(217,119,87,0.1)]" /> <div className="absolute left-1/2 top-[48%] h-[19%] w-[30%] -translate-x-1/2 -translate-y-1/2 rotate-[-2deg] skew-y-[-7deg] border border-[#b7b0a5] bg-[#ead9bd] shadow-[0_22px_42px_rgba(63,49,34,0.12)]" />
<div className="absolute left-1/2 top-[46%] h-[10%] w-[20%] -translate-x-1/2 -translate-y-1/2 rotate-[-2deg] skew-y-[-7deg] border border-[#4a90d9]/30 bg-[#161b22] shadow-[0_14px_28px_rgba(0,0,0,0.6)]"> <div className="absolute left-1/2 top-[46%] h-[10%] w-[20%] -translate-x-1/2 -translate-y-1/2 rotate-[-2deg] skew-y-[-7deg] border border-[#87939a] bg-[#27333a] shadow-[0_14px_28px_rgba(37,55,68,0.2)]">
<span className="absolute left-3 top-3 h-1.5 w-16 bg-[#4a90d9] shadow-[0_0_10px_rgba(74,144,217,0.8)]" /> <span className="absolute left-3 top-3 h-1.5 w-16 bg-[#4a90d9] shadow-[0_0_10px_rgba(74,144,217,0.8)]" />
<span className="absolute bottom-3 right-3 h-1.5 w-12 bg-[#d97757] shadow-[0_0_10px_rgba(217,119,87,0.8)]" /> <span className="absolute bottom-3 right-3 h-1.5 w-12 bg-[#8fc29a] shadow-[0_0_10px_rgba(143,194,154,0.8)]" />
</div> </div>
{/* Secondary Platforms */} {/* Secondary Platforms */}
<div className="absolute left-[25%] top-[77%] h-[9%] w-[15%] rotate-[-2deg] skew-y-[-7deg] border border-white/5 bg-[#1c1c1a]" /> <div className="absolute left-[25%] top-[77%] h-[9%] w-[15%] rotate-[-2deg] skew-y-[-7deg] border border-[#b7b0a5] bg-[#d6c8aa]" />
<div className="absolute right-[20%] top-[76%] h-[10%] w-[12%] rotate-[-2deg] skew-y-[-7deg] border border-white/5 bg-[#1c1c1a]" /> <div className="absolute right-[20%] top-[76%] h-[10%] w-[12%] rotate-[-2deg] skew-y-[-7deg] border border-[#b7b0a5] bg-[#d8e5df]" />
{/* Data Packets (Flow) */} {/* Data Packets (Flow) */}
{packetSlots.map((slot) => ( {packetSlots.map((slot) => (
<span <span
key={slot} key={slot}
data-testid="openclaw-flow-packet" data-testid="openclaw-flow-packet"
className="openclaw-flow-packet absolute h-2 w-2 rounded-full border border-[#60a5fa] bg-[#bfdbfe] shadow-[0_0_12px_rgba(96,165,250,0.9)]" className="openclaw-flow-packet absolute h-2.5 w-2.5 rounded-full border border-[#f0bd69] bg-[#fff6c7] shadow-[0_0_18px_rgba(240,189,105,0.7)]"
style={{ style={{
left: `${14 + (slot % 7) * 11}%`, left: `${14 + (slot % 7) * 11}%`,
top: `${28 + (slot % 4) * 12}%`, top: `${28 + (slot % 4) * 12}%`,
@@ -305,13 +305,13 @@ export default function OpenClawLiveOpsSpacePage({
<div <div
key={zone.zone_id} key={zone.zone_id}
data-testid="openclaw-zone" data-testid="openclaw-zone"
className="absolute flex h-14 w-36 -translate-x-1/2 -translate-y-1/2 rotate-[-2deg] items-center gap-3 rounded-2xl border border-white/10 bg-[#1c1c1a]/60 px-3 shadow-[0_8px_32px_rgba(0,0,0,0.4)] backdrop-blur-xl transition-all hover:scale-105 hover:bg-[#242422]/80" className="absolute flex h-14 w-36 -translate-x-1/2 -translate-y-1/2 rotate-[-2deg] items-center gap-3 rounded-2xl border border-white/60 bg-white/70 px-3 shadow-[0_8px_32px_rgba(31,38,135,0.06)] backdrop-blur-xl transition-all hover:scale-105 hover:bg-white/90"
style={{ left: `${zone.position.x}%`, top: `${zone.position.y}%` }} style={{ left: `${zone.position.x}%`, top: `${zone.position.y}%` }}
> >
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-white/10 bg-[#2a2a28]/50 text-[#a3a097] shadow-inner backdrop-blur-md"> <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-[#e5e1d8] bg-white/80 text-[#4a90d9] shadow-sm backdrop-blur-md">
<Icon className="h-4 w-4" aria-hidden="true" /> <Icon className="h-4 w-4" aria-hidden="true" />
</span> </span>
<span className="min-w-0 truncate font-mono text-[11px] font-bold tracking-tight text-[#f5f4ed]"> <span className="min-w-0 truncate font-mono text-[11px] font-bold tracking-tight text-[#34302a]">
{zone.label} {zone.label}
</span> </span>
</div> </div>
@@ -324,7 +324,7 @@ export default function OpenClawLiveOpsSpacePage({
key={item.work_item_id} key={item.work_item_id}
data-testid="openclaw-work-token" data-testid="openclaw-work-token"
className={cn( className={cn(
"openclaw-work-token absolute h-8 w-28 -translate-x-1/2 -translate-y-1/2 rounded-full border px-3 py-1 text-[10px] font-semibold shadow-[0_8px_20px_rgba(0,0,0,0.5)] flex items-center justify-center transition-all", "openclaw-work-token absolute h-8 w-28 -translate-x-1/2 -translate-y-1/2 rounded-full border px-3 py-1 text-[10px] font-semibold shadow-[0_8px_20px_rgba(54,72,88,0.1)] flex items-center justify-center transition-all",
stateClass(stateFromWorkItemStatus(item.status)), stateClass(stateFromWorkItemStatus(item.status)),
)} )}
style={{ style={{
@@ -351,61 +351,61 @@ export default function OpenClawLiveOpsSpacePage({
> >
<div className="relative"> <div className="relative">
{agent.state === "working" && ( {agent.state === "working" && (
<div className="openclaw-working-ripple absolute inset-0 rounded-full border-2 border-[#4a90d9]/80 shadow-[0_0_20px_rgba(74,144,217,0.6)]" /> <div className="openclaw-working-ripple absolute inset-0 rounded-full border-2 border-[#4a90d9]/60 shadow-[0_0_20px_rgba(74,144,217,0.3)]" />
)} )}
<div <div
className={cn( className={cn(
"relative z-10 flex h-14 w-14 items-center justify-center rounded-full border-2 shadow-[0_16px_30px_rgba(0,0,0,0.4)] transition-all duration-300 group-hover:scale-110", "relative z-10 flex h-14 w-14 items-center justify-center rounded-full border-2 shadow-[0_16px_30px_rgba(54,72,88,0.12)] transition-all duration-300 group-hover:scale-110",
stateClass(agent.state), stateClass(agent.state),
)} )}
> >
<span <span
className={cn( className={cn(
"absolute -right-0 -top-0 h-3.5 w-3.5 rounded-full border-2 border-[#1c1c1a]", "absolute -right-0 -top-0 h-3.5 w-3.5 rounded-full border-2 border-white",
agent.state === "blocked" agent.state === "blocked"
? "bg-[#d95f4f] animate-pulse shadow-[0_0_8px_rgba(217,95,79,0.8)]" ? "bg-[#d95f4f] animate-pulse shadow-[0_0_8px_rgba(217,95,79,0.4)]"
: agent.state === "verified" : agent.state === "verified"
? "bg-[#4f9b62] shadow-[0_0_8px_rgba(79,155,98,0.8)]" ? "bg-[#4f9b62] shadow-[0_0_8px_rgba(79,155,98,0.4)]"
: agent.state === "working" : agent.state === "working"
? "bg-[#4a90d9] animate-pulse shadow-[0_0_8px_rgba(74,144,217,0.8)]" ? "bg-[#4a90d9] animate-pulse shadow-[0_0_8px_rgba(74,144,217,0.4)]"
: "bg-[#f0bd69] shadow-[0_0_8px_rgba(240,189,105,0.8)]", : "bg-[#f0bd69] shadow-[0_0_8px_rgba(240,189,105,0.4)]",
)} )}
/> />
<Bot className={cn("h-6 w-6", agent.state === "working" ? "animate-bounce-slow text-[#60a5fa]" : "")} aria-hidden="true" /> <Bot className={cn("h-6 w-6", agent.state === "working" ? "animate-bounce-slow text-[#4a90d9]" : "")} aria-hidden="true" />
</div> </div>
</div> </div>
<div className="mt-3 max-w-[140px] truncate rounded-lg border border-white/10 bg-[#1c1c1a]/80 px-2.5 py-1 text-center font-mono text-[10px] font-semibold text-[#f5f4ed] shadow-lg backdrop-blur-xl transition-all group-hover:-translate-y-1 group-hover:border-white/30"> <div className="mt-3 max-w-[140px] truncate rounded-lg border border-[#e5e1d8] bg-white/80 px-2.5 py-1 text-center font-mono text-[10px] font-semibold text-[#141413] shadow-lg backdrop-blur-xl transition-all group-hover:-translate-y-1 group-hover:border-[#d8d3c7]">
{agent.label} {agent.label}
</div> </div>
</div> </div>
))} ))}
{/* Lighting overlay */} {/* Lighting overlay */}
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(145deg,rgba(255,255,255,0.03),transparent_45%,rgba(0,0,0,0.4))]" /> <div className="pointer-events-none absolute inset-0 bg-[linear-gradient(145deg,rgba(255,255,255,0.4),transparent_45%,rgba(20,20,19,0.02))]" />
{/* Footer Badges */} {/* Footer Badges */}
<div className="absolute bottom-4 left-4 flex flex-wrap gap-2"> <div className="absolute bottom-4 left-4 flex flex-wrap gap-2">
<span className="rounded-md border border-white/10 bg-[#1c1c1a]/80 px-2.5 py-1 font-mono text-[11px] text-[#a3a097] backdrop-blur-md"> <span className="rounded-md border border-[#d8d3c7] bg-white/80 px-2.5 py-1 font-mono text-[11px] text-[#5f5b52] backdrop-blur-md shadow-sm">
{t("source.marker")} <span className="text-[#f5f4ed]">{shortValue(scene?.source.deploy_readback_marker)}</span> {t("source.marker")} <span className="text-[#141413]">{shortValue(scene?.source.deploy_readback_marker)}</span>
</span> </span>
<span <span
data-testid="openclaw-animation-loop" data-testid="openclaw-animation-loop"
className={cn( className={cn(
"rounded-md border px-2.5 py-1 font-mono text-[11px] backdrop-blur-md", "rounded-md border px-2.5 py-1 font-mono text-[11px] backdrop-blur-md shadow-sm",
animationLoopEnabled animationLoopEnabled
? "border-[#4f9b62]/30 bg-[#4f9b62]/10 text-[#8fc29a]" ? "border-[#8fc29a] bg-[#f0faf2]/90 text-[#17602a]"
: "border-[#d9b36f]/30 bg-[#d9b36f]/10 text-[#fcd34d]", : "border-[#d9b36f] bg-[#fff7e8]/90 text-[#8a5a08]",
)} )}
> >
{t("animation.loop")}{" "} {t("animation.loop")}{" "}
<span className={cn("font-bold", animationLoopEnabled ? "text-[#4f9b62]" : "text-[#d9b36f]")}> <span className="font-bold">
{animationLoopEnabled ? t("animation.on") : t("animation.off")} {animationLoopEnabled ? t("animation.on") : t("animation.off")}
</span>{" "} </span>{" "}
· {scene?.room.animation_loop.tick_ms ?? 0}ms · {scene?.room.animation_loop.tick_ms ?? 0}ms
</span> </span>
<span className="rounded-md border border-white/10 bg-[#1c1c1a]/80 px-2.5 py-1 font-mono text-[11px] text-[#a3a097] backdrop-blur-md"> <span className="rounded-md border border-[#d8d3c7] bg-white/80 px-2.5 py-1 font-mono text-[11px] text-[#5f5b52] backdrop-blur-md shadow-sm">
{t("source.updated")}{" "} {t("source.updated")}{" "}
<span className="text-[#f5f4ed]"> <span className="text-[#141413]">
{updatedAt {updatedAt
? updatedAt.toLocaleTimeString(locale, { ? updatedAt.toLocaleTimeString(locale, {
hour: "2-digit", hour: "2-digit",
@@ -422,9 +422,9 @@ export default function OpenClawLiveOpsSpacePage({
{/* Sidebar Panels */} {/* Sidebar Panels */}
<aside className="grid min-w-0 gap-4"> <aside className="grid min-w-0 gap-4">
{/* Rollups (Stat Cards) */} {/* Rollups (Stat Cards) */}
<section className="rounded-2xl border border-white/10 bg-[#1c1c1a]/50 p-5 shadow-xl backdrop-blur-2xl"> <section className="rounded-2xl border border-[#d8d3c7] bg-white/60 p-5 shadow-lg backdrop-blur-2xl">
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<h2 className="text-sm font-semibold uppercase tracking-wider text-[#f5f4ed]"> <h2 className="text-sm font-semibold uppercase tracking-wider text-[#141413]">
{t("panels.rollups")} {t("panels.rollups")}
</h2> </h2>
<Activity className="h-4 w-4 text-[#4a90d9]" aria-hidden="true" /> <Activity className="h-4 w-4 text-[#4a90d9]" aria-hidden="true" />
@@ -436,11 +436,11 @@ export default function OpenClawLiveOpsSpacePage({
["animated", scene?.rollups.animated_entity_count], ["animated", scene?.rollups.animated_entity_count],
["blocked", scene?.rollups.blocked_work_item_count], ["blocked", scene?.rollups.blocked_work_item_count],
].map(([key, value]) => ( ].map(([key, value]) => (
<div key={String(key)} className="group rounded-xl border border-white/5 bg-white/5 p-4 transition-all hover:border-white/20 hover:bg-white/10"> <div key={String(key)} className="group rounded-xl border border-[#e5e1d8] bg-white p-4 transition-all hover:border-[#d8d3c7] hover:shadow-md">
<p className="text-[10px] font-semibold uppercase tracking-widest text-[#a3a097] group-hover:text-[#d8d3c7]"> <p className="text-[10px] font-semibold uppercase tracking-widest text-[#77736a] group-hover:text-[#5f5b52]">
{t(`metrics.${key}`)} {t(`metrics.${key}`)}
</p> </p>
<p className="mt-2 font-mono text-3xl font-bold tracking-tight text-[#f5f4ed]"> <p className="mt-2 font-mono text-3xl font-bold tracking-tight text-[#141413]">
{formatNumber(Number(value ?? 0))} {formatNumber(Number(value ?? 0))}
</p> </p>
</div> </div>
@@ -449,13 +449,13 @@ export default function OpenClawLiveOpsSpacePage({
</section> </section>
{/* Boundaries */} {/* Boundaries */}
<section className="rounded-2xl border border-white/10 bg-[#1c1c1a]/50 p-5 shadow-xl backdrop-blur-2xl"> <section className="rounded-2xl border border-[#d8d3c7] bg-white/60 p-5 shadow-lg backdrop-blur-2xl">
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<h2 className="text-sm font-semibold uppercase tracking-wider text-[#f5f4ed]"> <h2 className="text-sm font-semibold uppercase tracking-wider text-[#141413]">
{t("panels.boundaries")} {t("panels.boundaries")}
</h2> </h2>
<ShieldCheck <ShieldCheck
className={cn("h-4 w-4", boundaryOk ? "text-[#4f9b62]" : "text-[#d9b36f]")} className={cn("h-4 w-4", boundaryOk ? "text-[#17602a]" : "text-[#8a5a08]")}
aria-hidden="true" aria-hidden="true"
/> />
</div> </div>
@@ -469,17 +469,17 @@ export default function OpenClawLiveOpsSpacePage({
].map((key) => ( ].map((key) => (
<div <div
key={key} key={key}
className="group flex min-w-0 items-center justify-between gap-3 rounded-lg border border-white/5 bg-white/5 px-3 py-2.5 transition-all hover:bg-white/10" className="group flex min-w-0 items-center justify-between gap-3 rounded-lg border border-[#e5e1d8] bg-white px-3 py-2.5 transition-all hover:shadow-sm"
> >
<span className="truncate font-mono text-[10px] text-[#a3a097] group-hover:text-[#d8d3c7]"> <span className="truncate font-mono text-[10px] text-[#5f5b52] group-hover:text-[#141413]">
{key} {key}
</span> </span>
<span <span
className={cn( className={cn(
"rounded-full border px-2 py-0.5 text-[9px] font-bold uppercase tracking-widest", "rounded-full border px-2 py-0.5 text-[9px] font-bold uppercase tracking-widest",
scene?.boundaries[key] === false scene?.boundaries[key] === false
? "border-[#4f9b62]/30 bg-[#4f9b62]/10 text-[#8fc29a]" ? "border-[#8fc29a] bg-[#f0faf2] text-[#17602a]"
: "border-[#d9b36f]/30 bg-[#d9b36f]/10 text-[#fcd34d]", : "border-[#d9b36f] bg-[#fff7e8] text-[#8a5a08]",
)} )}
> >
{scene?.boundaries[key] === false ? t("boundary.closed") : t("boundary.open")} {scene?.boundaries[key] === false ? t("boundary.closed") : t("boundary.open")}
@@ -490,24 +490,24 @@ export default function OpenClawLiveOpsSpacePage({
</section> </section>
{/* Work Items Detail */} {/* Work Items Detail */}
<section className="min-h-[260px] rounded-2xl border border-white/10 bg-[#1c1c1a]/50 p-5 shadow-xl backdrop-blur-2xl"> <section className="min-h-[260px] rounded-2xl border border-[#d8d3c7] bg-white/60 p-5 shadow-lg backdrop-blur-2xl">
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<h2 className="text-sm font-semibold uppercase tracking-wider text-[#f5f4ed]"> <h2 className="text-sm font-semibold uppercase tracking-wider text-[#141413]">
{t("panels.workItems")} {t("panels.workItems")}
</h2> </h2>
<SearchCheck className="h-4 w-4 text-[#4a90d9]" aria-hidden="true" /> <SearchCheck className="h-4 w-4 text-[#4a90d9]" aria-hidden="true" />
</div> </div>
<div className="mt-4 grid gap-3"> <div className="mt-4 grid gap-3">
{workItems.length === 0 ? ( {workItems.length === 0 ? (
<div className="flex flex-col items-center justify-center rounded-xl border border-white/5 bg-white/5 py-8 text-[#a3a097]"> <div className="flex flex-col items-center justify-center rounded-xl border border-[#e5e1d8] bg-[#fafafa] py-8 text-[#77736a]">
<SearchCheck className="mb-2 h-6 w-6 opacity-50" /> <SearchCheck className="mb-2 h-6 w-6 opacity-50" />
<span className="text-xs font-medium">{t("empty")}</span> <span className="text-xs font-medium">{t("empty")}</span>
</div> </div>
) : ( ) : (
workItems.slice(0, 6).map((item) => ( workItems.slice(0, 6).map((item) => (
<div key={item.work_item_id} className="group rounded-xl border border-white/5 bg-white/5 p-3.5 transition-all hover:-translate-y-0.5 hover:border-white/20 hover:bg-white/10 hover:shadow-lg"> <div key={item.work_item_id} className="group rounded-xl border border-[#e5e1d8] bg-white p-3.5 transition-all hover:-translate-y-0.5 hover:border-[#d8d3c7] hover:shadow-md">
<div className="flex min-w-0 items-center justify-between gap-2"> <div className="flex min-w-0 items-center justify-between gap-2">
<span className="min-w-0 truncate text-sm font-bold text-[#f5f4ed]"> <span className="min-w-0 truncate text-sm font-bold text-[#141413]">
{item.title || item.work_item_id} {item.title || item.work_item_id}
</span> </span>
<span <span
@@ -519,8 +519,8 @@ export default function OpenClawLiveOpsSpacePage({
{stateLabel(t, stateFromWorkItemStatus(item.status))} {stateLabel(t, stateFromWorkItemStatus(item.status))}
</span> </span>
</div> </div>
<p className="mt-2 truncate font-mono text-[10px] text-[#a3a097] group-hover:text-[#d8d3c7]"> <p className="mt-2 truncate font-mono text-[10px] text-[#77736a] group-hover:text-[#5f5b52]">
<span className="mr-1 text-[#d97757]"></span> <span className="mr-1 text-[#4a90d9]"></span>
{item.next_controlled_action || item.exit_criteria || "--"} {item.next_controlled_action || item.exit_criteria || "--"}
</p> </p>
</div> </div>

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,21 @@
## 2026-07-03 — 21:21 全站 UI/UX 專業化重構 — 面板與頁面 Tailwind 化
**完成內容**
- 完成 P0 面板層重構:`CostPanel``ApmPanel``AppsPanel``ServicesPanel``BillingPanel``DeploymentsPanel`,全面移除 inline style改為 Tailwind CSS 搭配 Stat Cards 與視覺化圖表。
- 完成 P1 面板層重構:`SecurityPanel`(彩色 Stat Cards 與 Sentry 嚴重性等級徽章)、`CompliancePanel`(嚴重性分佈長條圖與 Auto-Repair 狀態面板)。
- 完成 P0 頁面重構:`help``automation``operations``notifications``settings``topology`
- ErrorsPanel 頭部 Tailwind 化 + 統一背景色。
- 通過 TypeScript 全量檢查 (`tsc --noEmit`),零 type error。
- 代碼已提交並推送到 `gitea-ssh main` 觸發 CD。
**已跑驗證**
- `cd apps/web && pnpm exec tsc --noEmit`:全部通過。
- Gitea SSH remote push成功。
**下一步**
- 等待 Gitea CD 流程完成生產環境部署。
- 下一階段P2將處理其餘複雜面板`ActionLogsPanel``AutoRepairPanel``DriftPanel``MonitoringPanel``TicketsPanel` 等。
## 2026-07-03 — 18:19 Windows99 VMX backup/source search package ## 2026-07-03 — 18:19 Windows99 VMX backup/source search package
**完成內容** **完成內容**