Files
awoooi/.playwright-mcp/sprint5r-approved-design.html
OG T c180bdaaac docs: Sprint 5R 前端重構批准 — ADR-065 + 設計稿 + Skills + LOGBOOK
- ADR-065: Sprint 5R 前端重構決策(版本 A 批准)
- sprint5r-approved-design.html: 統帥批准的設計稿存檔
- Skills 01 v1.7: 品牌 Logo/AwoooI 一致性鐵律
- LOGBOOK: Sprint 5R 開始實施

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 15:15:43 +08:00

783 lines
38 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1440">
<title>AWOOOI AI 戰情指揮中心 — 版本 A忠實還原 + 微增強</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Syne:wght@400;600;700;800&family=JetBrains+Mono:wght@300;400;500&family=VT323&display=swap" rel="stylesheet">
<style>
:root {
--bg: #f5f4ed;
--card: #fff;
--surface: #faf9f3;
--bdr: #e0ddd4;
--text: #141413;
--text2: #555550;
--text3: #87867f;
--accent: #d97757;
--green: #22C55E;
--red: #cc2200;
--blue: #4A90D9;
--orange: #F59E0B;
--purple: #A855F7;
}
*, *::before, *::after { margin:0; padding:0; box-sizing:border-box; }
body {
font-family: 'DM Mono', monospace;
background: var(--bg);
color: var(--text);
overflow: hidden;
height: 100vh;
width: 1440px;
display: flex;
font-size: 12px;
line-height: 1.4;
}
/* SIDEBAR */
.sidebar {
width: 200px;
min-width: 200px;
background: var(--card);
border-right: 0.5px solid var(--bdr);
display: flex;
flex-direction: column;
height: 100vh;
}
.brand {
height: 72px;
display: flex;
align-items: center;
gap: 10px;
padding: 0 16px;
border-bottom: 0.5px solid var(--bdr);
}
.brand-text {
display: flex;
align-items: baseline;
gap: 0;
line-height: 1;
}
.brand-text .a { font-family: 'DM Mono', monospace; font-size: 20px; font-weight: 700; color: #141413; margin-right: -4px; }
.brand-text .w { font-family: 'VT323', monospace; font-size: 26px; color: var(--accent); letter-spacing: -1px; line-height: 1; }
.brand-text .i { font-family: 'DM Mono', monospace; font-size: 20px; font-weight: 700; color: #141413; margin-left: -3px; }
.nav { flex:1; padding: 12px 8px; display:flex; flex-direction:column; gap:2px; }
.nav-item {
display: flex; align-items: center; gap: 8px;
padding: 8px 12px; border-radius: 6px; cursor: pointer;
font-size: 12px; color: var(--text2); text-decoration: none;
transition: background 0.15s;
}
.nav-item:hover { background: var(--surface); }
.nav-item.active { background: rgba(217,119,87,0.08); color: var(--accent); font-weight: 500; }
.nav-item .dot { width:6px; height:6px; border-radius:50%; flex-shrink:0; }
.nav-sep { height:0.5px; background:var(--bdr); margin:8px 12px; }
.nav-label { font-size:9px; color:var(--text3); padding:4px 12px; text-transform:uppercase; letter-spacing:1px; }
.nav-bottom { padding:8px; border-top:0.5px solid var(--bdr); }
/* CONTENT */
.content { flex:1; display:flex; flex-direction:column; height:100vh; overflow:hidden; }
/* TITLE BAR */
.titlebar {
height: 48px; min-height:48px;
display: flex; align-items: center; justify-content: space-between;
padding: 0 20px;
border-bottom: 0.5px solid var(--bdr);
background: var(--card);
}
.titlebar h1 { font-family:'Syne',sans-serif; font-size:20px; font-weight:800; }
.titlebar-right { display:flex; align-items:center; gap:12px; }
.pulse-dot { width:8px;height:8px;border-radius:50%;background:var(--green);display:inline-block;animation:blink 2s infinite; }
.model-badge { font-size:11px; color:var(--text2); display:flex; align-items:center; gap:6px; }
.lang-btn { font-size:11px; padding:2px 8px; border-radius:4px; border:0.5px solid var(--bdr); background:transparent; cursor:pointer; color:var(--text3); }
.lang-btn.active { background:var(--text); color:var(--card); border-color:var(--text); }
.avatar { width:28px;height:28px;border-radius:50%;background:var(--accent);display:flex;align-items:center;justify-content:center;color:#fff;font-size:12px;font-weight:700; }
/* TAB BAR */
.tabbar {
height:36px; min-height:36px;
display:flex; align-items:stretch;
padding:0 20px; gap:0;
border-bottom:0.5px solid var(--bdr);
background:var(--card);
}
.tab {
padding:0 16px; display:flex; align-items:center; gap:6px;
font-size:12px; color:var(--text3); cursor:pointer;
border-bottom:2px solid transparent; position:relative;
}
.tab.active { color:var(--accent); border-bottom-color:var(--accent); font-weight:500; }
.tab-badge { background:var(--red);color:#fff;font-size:9px;padding:1px 5px;border-radius:8px;font-weight:500; }
/* LOBSTER SWIM */
.swim-lane {
height:14px; min-height:14px;
background:var(--surface);
position:relative;
overflow:hidden;
border-bottom:0.5px solid var(--bdr);
}
.swim-lobster {
position:absolute;
top:1px;
animation: swim-wide 25s linear infinite, chibi-bob 0.7s ease-in-out infinite;
}
/* KPI STRIP */
.kpi-strip {
display:flex; gap:8px; padding:8px 20px;
border-bottom:0.5px solid var(--bdr);
background:var(--surface);
min-height:60px;
}
.kpi-card {
flex:1; background:var(--card); border:0.5px solid var(--bdr);
border-radius:8px; padding:8px 12px;
display:flex; flex-direction:column; gap:2px;
}
.kpi-label { font-size:10px; color:var(--text3); }
.kpi-val { font-size:18px; font-weight:500; }
.kpi-sub { font-size:9px; color:var(--text3); }
.kpi-bar { height:3px; border-radius:2px; background:#eee; margin-top:2px; }
.kpi-bar-fill { height:100%; border-radius:2px; }
.trend-up { color:var(--green); font-size:10px; }
/* MAIN BODY */
.main-body {
flex:1; display:flex; gap:12px; padding:12px 20px; overflow:hidden;
}
.col-left { flex:6; display:flex; flex-direction:column; gap:10px; overflow:hidden; }
.col-right { flex:4; display:flex; flex-direction:column; gap:10px; overflow:hidden; }
/* CARDS */
.card {
background:var(--card);
border:0.5px solid var(--bdr);
border-radius:10px;
overflow:hidden;
}
.card-header {
display:flex; align-items:center; gap:8px;
padding:8px 12px;
border-bottom:0.5px solid var(--bdr);
font-size:12px; font-weight:500;
}
.card-header .hdot { width:6px;height:6px;border-radius:50%;background:var(--accent);flex-shrink:0; }
.card-header .link { margin-left:auto; font-size:10px; color:var(--accent); text-decoration:none; cursor:pointer; }
.card-header .cnt-badge { font-size:9px; background:var(--orange); color:#fff; padding:1px 6px; border-radius:8px; }
.card-body { padding:10px 12px; }
/* INCIDENT */
.incident {
border-left:3px solid var(--orange);
padding:8px 10px;
margin-bottom:8px;
background:var(--surface);
border-radius:0 6px 6px 0;
}
.incident.p2 { border-left-color:var(--blue); }
.sev-badge {
display:inline-block; font-size:9px; font-weight:700; padding:1px 6px; border-radius:4px; color:#fff;
}
.sev-p1 { background:var(--orange); }
.sev-p2 { background:var(--blue); }
.incident-title { font-size:13px; font-weight:500; margin:4px 0 2px; }
.incident-meta { font-size:10px; color:var(--text3); margin-bottom:6px; }
/* FLOW PIPELINE */
.flow-pipe {
display:flex; align-items:center; gap:0; margin:6px 0;
font-size:9px; position:relative;
}
.flow-step {
display:flex; flex-direction:column; align-items:center; gap:2px;
position:relative; flex:1;
}
.flow-step .circle {
width:18px;height:18px;border-radius:50%;
display:flex;align-items:center;justify-content:center;
font-size:8px; border:1.5px solid #ccc; background:#fff; color:var(--text3);
position:relative; z-index:1;
}
.flow-step.done .circle { background:var(--orange); border-color:var(--orange); color:#fff; }
.flow-step.active .circle { background:#fff; border-color:var(--orange); color:var(--orange); }
.flow-step.p2-done .circle { background:var(--blue); border-color:var(--blue); color:#fff; }
.flow-step.p2-active .circle {
background:#fff; border-color:var(--blue); color:var(--blue);
animation: card-glow-p2 1.5s ease-in-out infinite;
}
.flow-step .label { font-size:8px; color:var(--text3); }
.flow-line {
height:2px; flex:1; background:#e0ddd4; margin:0 -2px; position:relative; top:-6px; z-index:0;
}
.flow-line.done { background:var(--orange); }
.flow-line.p2-done { background:var(--blue); }
.flow-openclaw-icon {
width:20px; height:20px; border-radius:50%; overflow:hidden;
animation: lobster-bob 1.5s ease-in-out infinite;
display:flex; align-items:center; justify-content:center;
}
.flow-openclaw-icon img { width:20px; height:20px; }
/* AI PROPOSAL */
.ai-proposal {
background:rgba(245,158,11,0.08);
border:0.5px solid rgba(245,158,11,0.25);
border-radius:6px; padding:6px 10px;
font-size:11px; color:var(--text); margin:6px 0;
}
.btn-row { display:flex; gap:6px; margin-top:6px; }
.btn {
padding:4px 12px; border-radius:6px; font-size:11px; cursor:pointer;
border:0.5px solid var(--bdr); font-family:'DM Mono',monospace;
}
.btn-approve { background:var(--green); color:#fff; border-color:var(--green); }
.btn-reject { background:transparent; color:var(--text3); }
.btn-approve-orange { background:var(--orange); color:#fff; border-color:var(--orange); }
/* DONUT */
.donut-area { display:flex; align-items:center; gap:16px; }
.donut-stats { display:grid; grid-template-columns:1fr 1fr; gap:4px 16px; font-size:11px; }
.donut-stat { display:flex; align-items:center; gap:6px; }
.donut-stat .d-dot { width:6px;height:6px;border-radius:50%;flex-shrink:0; }
/* ACTIVITY */
.activity-item {
display:flex; align-items:flex-start; gap:8px; padding:3px 0;
font-size:11px; line-height:1.4;
}
.activity-item .time { font-family:'JetBrains Mono',monospace; font-size:10px; color:var(--text3); flex-shrink:0; }
.activity-item .a-dot { width:4px;height:4px;border-radius:50%;flex-shrink:0;margin-top:5px; }
.activity-item code { font-family:'JetBrains Mono',monospace; font-size:10px; background:var(--surface); padding:0 3px; border-radius:2px; }
/* OPENCLAW ENGINE */
.oc-panel { display:flex; gap:12px; }
.oc-right { flex:1; }
.oc-brand { display:flex; align-items:baseline; gap:0; margin-bottom:4px; line-height:1; }
.oc-brand .w { font-family:'DM Mono',monospace; font-size:15px; font-weight:700; color:var(--text); }
.oc-brand .o { font-family:'VT323',monospace; font-size:24px; color:var(--accent); letter-spacing:1px; line-height:1; }
.oc-brand .c { font-family:'DM Mono',monospace; font-size:15px; font-weight:700; color:var(--text); }
.oc-badge { display:inline-block; font-size:9px; padding:2px 8px; border-radius:4px; background:rgba(74,144,217,0.1); color:var(--blue); margin-bottom:4px; }
.oc-status { font-size:11px; color:var(--text2); margin-bottom:4px; }
.oc-dots { display:inline-flex; gap:3px; }
.oc-dots span { width:4px;height:4px;border-radius:50%;background:var(--blue);animation:oc-p 1.4s infinite; }
.oc-dots span:nth-child(2) { animation-delay:0.2s; }
.oc-dots span:nth-child(3) { animation-delay:0.4s; }
.oc-sep { height:0.5px; background:var(--bdr); margin:6px 0; }
.oc-stats { font-size:10px; color:var(--text3); display:flex; gap:8px; flex-wrap:wrap; }
.oc-stats b { color:var(--text2); font-weight:500; }
/* AI TERMINAL */
.ai-terminal {
background:#141413; color:#a0e8a0; font-family:'JetBrains Mono',monospace;
font-size:10px; border-radius:6px; padding:8px; margin-top:6px;
max-height:80px; overflow:hidden; line-height:1.5;
}
.ai-terminal .cursor { color:#F59E0B; animation:cursor-blink 1s step-end infinite; }
/* PENDING APPROVALS */
.card.pending { border-color:rgba(245,158,11,0.3); }
.approval-item {
padding:8px; margin-bottom:6px; background:var(--surface); border-radius:6px;
}
.approval-item .ap-title { font-size:12px; font-weight:500; margin-bottom:2px; }
.approval-item .ap-target { font-family:'JetBrains Mono',monospace; font-size:10px; color:var(--text3); margin-bottom:4px; }
.risk-badge { font-size:9px; padding:1px 6px; border-radius:4px; font-weight:600; }
.risk-low { background:rgba(34,197,94,0.1); color:var(--green); }
.risk-med { background:rgba(245,158,11,0.1); color:var(--orange); }
/* INFRA */
.infra-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
.infra-node {
border:0.5px solid var(--bdr); border-radius:6px; padding:8px;
font-size:10px;
}
.infra-node .in-title { font-size:11px; font-weight:500; margin-bottom:2px; }
.infra-node .in-sub { font-size:9px; color:var(--text3); margin-bottom:4px; }
.infra-node .in-services { display:flex; flex-wrap:wrap; gap:3px; }
.in-svc {
font-size:9px; padding:1px 5px; border-radius:3px;
background:var(--surface); border:0.5px solid var(--bdr);
}
.in-svc.warn { border-color:var(--orange); background:rgba(245,158,11,0.06); }
.in-svc.diag { border-color:var(--blue); background:rgba(74,144,217,0.06); }
.infra-node.glow-warn { background:rgba(245,158,11,0.03); }
/* HOST VIEW */
.host-grid { display:grid; grid-template-columns:1fr 1fr; gap:6px; }
.host-node { border:0.5px solid var(--bdr); border-radius:6px; padding:8px; font-size:10px; }
.host-node .hn-title { font-size:11px; font-weight:500; margin-bottom:2px; }
.host-node .hn-ip { font-size:9px; color:var(--text3); font-family:'JetBrains Mono',monospace; margin-bottom:4px; }
.prog-row { display:flex; align-items:center; gap:4px; margin-bottom:2px; font-size:9px; }
.prog-bar { flex:1; height:4px; background:#eee; border-radius:2px; }
.prog-fill { height:100%;border-radius:2px; }
/* AI MODEL */
.model-grid { display:grid; grid-template-columns:1fr 1fr; gap:4px; }
.model-item {
display:flex; align-items:center; gap:6px; font-size:10px;
padding:4px 6px; background:var(--surface); border-radius:4px;
}
.model-item .m-dot { width:5px;height:5px;border-radius:50%;background:var(--green); }
/* MONITOR TOOLS */
.tool-grid { display:grid; grid-template-columns:1fr 1fr 1fr; gap:4px; }
.tool-item {
display:flex; align-items:center; gap:6px; font-size:10px; padding:4px 6px;
background:var(--surface); border-radius:4px;
}
.tool-item .t-bar { width:3px; height:20px; border-radius:2px; flex-shrink:0; }
.tool-item .t-name { font-weight:500; font-size:10px; }
.tool-item .t-meta { font-size:9px; color:var(--text3); }
/* FLOATING */
.fab {
position:fixed; bottom:16px; right:16px;
background:var(--text); color:var(--card);
padding:8px 16px; border-radius:8px; font-size:12px;
font-family:'JetBrains Mono',monospace;
cursor:pointer; z-index:100;
border:0.5px solid var(--text3);
box-shadow:0 2px 8px rgba(0,0,0,0.15);
}
/* TOGGLE */
.toggle-group { display:flex; margin-left:auto; gap:0; }
.toggle-btn {
font-size:10px; padding:2px 8px; border:0.5px solid var(--bdr);
background:transparent; cursor:pointer; color:var(--text3);
font-family:'DM Mono',monospace;
}
.toggle-btn:first-child { border-radius:4px 0 0 4px; }
.toggle-btn:last-child { border-radius:0 4px 4px 0; }
.toggle-btn.active { background:var(--text); color:var(--card); border-color:var(--text); }
/* ANIMATIONS */
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.3} }
@keyframes swim-wide { 0%{left:-20px;transform:scaleX(1)} 49%{left:calc(100% - 10px);transform:scaleX(1)} 50%{left:calc(100% - 10px);transform:scaleX(-1)} 99%{left:-20px;transform:scaleX(-1)} 100%{left:-20px;transform:scaleX(1)} }
@keyframes chibi-bob { 0%,100%{top:1px} 50%{top:-1px} }
@keyframes lobster-bob { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-3px)} }
@keyframes card-glow-p2 { 0%,100%{box-shadow:0 0 0 0 rgba(74,144,217,0)} 50%{box-shadow:0 0 6px 2px rgba(74,144,217,0.35)} }
@keyframes oc-p { 0%,100%{opacity:0.3} 50%{opacity:1} }
@keyframes cursor-blink { 0%,100%{opacity:1} 50%{opacity:0} }
</style>
</head>
<body>
<!-- SIDEBAR -->
<aside class="sidebar">
<div class="brand">
<svg width="36" height="36" viewBox="0 0 140 140" fill="none">
<defs>
<linearGradient id="hdr-ceramic" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="40%" stop-color="#F8F8F8"/><stop offset="70%" stop-color="#E8E8E8"/><stop offset="100%" stop-color="#D8D8D8"/></linearGradient>
<radialGradient id="hdr-led" cx="40%" cy="35%" r="60%"><stop offset="0%" stop-color="#7AB8F5"/><stop offset="100%" stop-color="#2B6CB0"/></radialGradient>
</defs>
<circle cx="70" cy="70" r="32" fill="url(#hdr-ceramic)" stroke="#E0E0E0" stroke-width="1"/>
<circle cx="70" cy="70" r="16" fill="url(#hdr-led)"><animate attributeName="r" values="14;17;14" dur="2s" repeatCount="indefinite"/></circle>
<circle cx="70" cy="70" r="8" fill="white" opacity=".8"/>
<path d="M70 38L70 18L58 6M70 18L82 6" stroke="url(#hdr-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/><path d="M70 38L70 18L58 6M70 18L82 6" stroke="#4A90D9" stroke-width="3" stroke-linecap="round" fill="none" opacity=".5"/>
<path d="M38 70L18 70L6 58M18 70L6 82" stroke="url(#hdr-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/><path d="M38 70L18 70L6 58M18 70L6 82" stroke="#4A90D9" stroke-width="3" stroke-linecap="round" fill="none" opacity=".5"/>
<path d="M102 70L122 70L134 58M122 70L134 82" stroke="url(#hdr-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/><path d="M102 70L122 70L134 58M122 70L134 82" stroke="#4A90D9" stroke-width="3" stroke-linecap="round" fill="none" opacity=".5"/>
<path d="M48 92L28 112L16 116" stroke="url(#hdr-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/>
<path d="M92 92L112 112L124 116" stroke="url(#hdr-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/>
<circle cx="70" cy="70" r="42" fill="none" stroke="#4A90D9" stroke-width="1" stroke-dasharray="6 6" opacity=".3"><animateTransform attributeName="transform" type="rotate" from="0 70 70" to="360 70 70" dur="8s" repeatCount="indefinite"/></circle>
</svg>
<span class="brand-text"><span class="a">A</span><span class="w">wooo</span><span class="i">I</span></span>
</div>
<nav class="nav">
<a class="nav-item active"><span class="dot" style="background:var(--accent)"></span>指令中心</a>
<a class="nav-item"><span class="dot" style="background:var(--blue)"></span>可觀測性</a>
<a class="nav-item"><span class="dot" style="background:var(--green)"></span>自動化</a>
<a class="nav-item"><span class="dot" style="background:var(--purple)"></span>營運</a>
<a class="nav-item"><span class="dot" style="background:var(--red)"></span>安全合規</a>
<a class="nav-item"><span class="dot" style="background:var(--text3)"></span>知識</a>
<div class="nav-sep"></div>
<div class="nav-label">LEGACY</div>
<a class="nav-item" style="color:#c0bfb8">經典 AI 中心</a>
</nav>
<div class="nav-bottom">
<a class="nav-item"><span class="dot" style="background:var(--text3)"></span>終端</a>
<a class="nav-item"><span class="dot" style="background:var(--text3)"></span>設定</a>
</div>
</aside>
<!-- CONTENT -->
<main class="content">
<!-- TITLE BAR -->
<div class="titlebar">
<h1>AI中心</h1>
<div class="titlebar-right">
<button class="lang-btn active"></button>
<button class="lang-btn">EN</button>
<div class="avatar">OG</div>
</div>
</div>
<!-- TAB BAR -->
<div class="tabbar">
<div class="tab active">戰情總覽</div>
<div class="tab">告警 & 授權 <span class="tab-badge">2</span></div>
<div class="tab">活動串流</div>
<div class="tab">處置統計</div>
</div>
<!-- KPI STRIP -->
<div class="kpi-strip">
<div class="kpi-card">
<span class="kpi-label">系統健康</span>
<span class="kpi-val" style="color:var(--green)">98.5%</span>
<div class="kpi-bar"><div class="kpi-bar-fill" style="width:98.5%;background:var(--green)"></div></div>
</div>
<div class="kpi-card">
<span class="kpi-label">活動事件</span>
<span class="kpi-val" style="color:var(--orange)">2</span>
<span class="kpi-sub">P1:1 P2:1</span>
</div>
<div class="kpi-card">
<span class="kpi-label">自動修復率</span>
<span class="kpi-val" style="color:var(--green)">72% <span class="trend-up">↑5%</span></span>
<div class="kpi-bar"><div class="kpi-bar-fill" style="width:72%;background:linear-gradient(90deg,var(--green),#6ee7b7)"></div></div>
</div>
<div class="kpi-card">
<span class="kpi-label">待審批</span>
<span class="kpi-val" style="color:var(--orange)">3</span>
<span class="kpi-sub">等待決策</span>
</div>
<div class="kpi-card">
<span class="kpi-label">本週操作</span>
<span class="kpi-val">1,245</span>
</div>
</div>
<!-- MAIN BODY -->
<div class="main-body">
<!-- LEFT COLUMN -->
<div class="col-left">
<!-- ACTIVE INCIDENTS -->
<div class="card" style="flex-shrink:0;">
<div class="card-header">
<span class="hdot"></span>
<span>活躍事件</span>
<span class="cnt-badge">2</span>
<a class="link">查看全部告警 →</a>
</div>
<div class="card-body">
<!-- P1 -->
<div class="incident">
<span class="sev-badge sev-p1">P1</span>
<div class="incident-title">API 回應延遲超標</div>
<div class="incident-meta">awoooi-api @ awoooi-prod · 3 alerts · investigating</div>
<div class="flow-pipe">
<div class="flow-step done"><div class="circle"></div><div class="label">告警</div></div>
<div class="flow-line done"></div>
<div class="flow-step done"><div class="circle"></div><div class="label">偵測</div></div>
<div class="flow-line done"></div>
<div class="flow-step done"><div class="circle"></div><div class="label">分析</div></div>
<div class="flow-line done"></div>
<div class="flow-step active"><div class="flow-openclaw-icon"><img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/openclaw.png" alt="OpenClaw"/></div><div class="label" style="font-weight:700">提案</div></div>
<div class="flow-line"></div>
<div class="flow-step"><div class="circle"></div><div class="label">授權</div></div>
<div class="flow-line"></div>
<div class="flow-step"><div class="circle"></div><div class="label">執行</div></div>
<div class="flow-line"></div>
<div class="flow-step"><div class="circle"></div><div class="label">完成</div></div>
</div>
<div class="ai-proposal">▶ AI 提案restart_deployment awoooi-api (信心度 91%)</div>
<div class="btn-row">
<button class="btn btn-approve">批准執行</button>
<button class="btn btn-reject">拒絕</button>
</div>
</div>
<!-- P2 -->
<div class="incident p2">
<span class="sev-badge sev-p2">P2</span>
<div class="incident-title">Redis 連線數偏高</div>
<div class="incident-meta">redis @ 192.168.0.188 · investigating</div>
<div class="flow-pipe">
<div class="flow-step p2-done"><div class="circle"></div><div class="label">告警</div></div>
<div class="flow-line p2-done"></div>
<div class="flow-step p2-done"><div class="circle"></div><div class="label">偵測</div></div>
<div class="flow-line p2-done"></div>
<div class="flow-step p2-active"><div class="flow-openclaw-icon"><img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/openclaw.png" alt="OpenClaw"/></div><div class="label" style="font-weight:700">分析</div></div>
<div class="flow-line"></div>
<div class="flow-step"><div class="circle"></div><div class="label">提案</div></div>
<div class="flow-line"></div>
<div class="flow-step"><div class="circle"></div><div class="label">授權</div></div>
<div class="flow-line"></div>
<div class="flow-step"><div class="circle"></div><div class="label">執行</div></div>
<div class="flow-line"></div>
<div class="flow-step"><div class="circle"></div><div class="label">完成</div></div>
</div>
</div>
</div>
</div>
<!-- DISPOSITION STATS -->
<div class="card" style="flex-shrink:0;">
<div class="card-header">
<span class="hdot"></span>
<span>處置統計</span>
<a class="link">查看完整報表 →</a>
</div>
<div class="card-body">
<div class="donut-area">
<svg width="56" height="56" viewBox="0 0 56 56">
<circle cx="28" cy="28" r="22" fill="none" stroke="#eee" stroke-width="6"/>
<!-- green 70% = 252deg -->
<circle cx="28" cy="28" r="22" fill="none" stroke="var(--green)" stroke-width="6" stroke-dasharray="96.8 41.2" stroke-dashoffset="34.6" stroke-linecap="round"/>
<!-- orange 22% -->
<circle cx="28" cy="28" r="22" fill="none" stroke="var(--orange)" stroke-width="6" stroke-dasharray="30.4 107.6" stroke-dashoffset="131.8" stroke-linecap="round"/>
<!-- purple 6% -->
<circle cx="28" cy="28" r="22" fill="none" stroke="var(--purple)" stroke-width="6" stroke-dasharray="8.3 129.7" stroke-dashoffset="101.4" stroke-linecap="round"/>
<!-- blue 2% -->
<circle cx="28" cy="28" r="22" fill="none" stroke="var(--blue)" stroke-width="6" stroke-dasharray="2.8 135.2" stroke-dashoffset="93.1" stroke-linecap="round"/>
<text x="28" y="30" text-anchor="middle" font-size="11" font-family="DM Mono" font-weight="500" fill="var(--text)">72%</text>
</svg>
<div class="donut-stats">
<div class="donut-stat"><span class="d-dot" style="background:var(--green)"></span> 自動修復 <b>142</b></div>
<div class="donut-stat"><span class="d-dot" style="background:var(--orange)"></span> 人工核准 <b>45</b></div>
<div class="donut-stat"><span class="d-dot" style="background:var(--purple)"></span> 手動處理 <b>12</b></div>
<div class="donut-stat"><span class="d-dot" style="background:var(--blue)"></span> 冷啟動 <b>5</b></div>
</div>
</div>
</div>
</div>
<!-- RECENT ACTIVITY -->
<div class="card" style="flex:1;min-height:0;">
<div class="card-header">
<span class="hdot"></span>
<span>最近活動</span>
<a class="link">查看活動串流 →</a>
</div>
<div class="card-body">
<div class="activity-item"><span class="time">18:05</span><span class="a-dot" style="background:var(--green)"></span><span>心跳確認 <code>mon/mon1</code> Ready</span></div>
<div class="activity-item"><span class="time">18:04</span><span class="a-dot" style="background:var(--blue)"></span><span><b>OpenClaw</b> 匹配 Playbook <code>restart_worker</code> (91%)</span></div>
<div class="activity-item"><span class="time">18:02</span><span class="a-dot" style="background:var(--red)"></span><span><b>Prometheus</b> Worker CPU 89%</span></div>
<div class="activity-item"><span class="time">17:58</span><span class="a-dot" style="background:var(--green)"></span><span>自動修復完成 <code>restart: api</code> (12s)</span></div>
</div>
</div>
</div>
<!-- RIGHT COLUMN -->
<div class="col-right">
<!-- OPENCLAW ENGINE -->
<div class="card" style="flex-shrink:0;">
<div class="card-header">
<span class="hdot"></span>
<span>OPENCLAW 認知引擎</span>
</div>
<div class="card-body">
<div class="oc-panel">
<svg width="68" height="68" viewBox="0 0 140 140" fill="none" style="flex-shrink:0">
<defs>
<linearGradient id="oc-ceramic" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="40%" stop-color="#F8F8F8"/><stop offset="70%" stop-color="#E8E8E8"/><stop offset="100%" stop-color="#D8D8D8"/></linearGradient>
<radialGradient id="oc-led" cx="40%" cy="35%" r="60%"><stop offset="0%" stop-color="#7AB8F5"/><stop offset="100%" stop-color="#2B6CB0"/></radialGradient>
</defs>
<circle cx="70" cy="70" r="32" fill="url(#oc-ceramic)" stroke="#E0E0E0" stroke-width="1"/>
<circle cx="70" cy="70" r="16" fill="url(#oc-led)"><animate attributeName="r" values="14;17;14" dur="2s" repeatCount="indefinite"/></circle>
<circle cx="70" cy="70" r="8" fill="white" opacity=".8"/>
<path d="M70 38L70 18L58 6M70 18L82 6" stroke="url(#oc-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/><path d="M70 38L70 18L58 6M70 18L82 6" stroke="#4A90D9" stroke-width="3" stroke-linecap="round" fill="none" opacity=".5"/>
<path d="M38 70L18 70L6 58M18 70L6 82" stroke="url(#oc-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/><path d="M38 70L18 70L6 58M18 70L6 82" stroke="#4A90D9" stroke-width="3" stroke-linecap="round" fill="none" opacity=".5"/>
<path d="M102 70L122 70L134 58M122 70L134 82" stroke="url(#oc-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/><path d="M102 70L122 70L134 58M122 70L134 82" stroke="#4A90D9" stroke-width="3" stroke-linecap="round" fill="none" opacity=".5"/>
<path d="M48 92L28 112L16 116" stroke="url(#oc-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/>
<path d="M92 92L112 112L124 116" stroke="url(#oc-ceramic)" stroke-width="6" stroke-linecap="round" fill="none"/>
<circle cx="70" cy="70" r="42" fill="none" stroke="#4A90D9" stroke-width="1" stroke-dasharray="6 6" opacity=".3"><animateTransform attributeName="transform" type="rotate" from="0 70 70" to="360 70 70" dur="8s" repeatCount="indefinite"/></circle>
</svg>
<div class="oc-right">
<div class="oc-brand"><span class="w">W</span><span class="o">○○○</span><span class="c">Claw</span></div>
<div class="oc-badge">WoooClaw Pipeline</div>
<div class="oc-status">[AGENT] patrolling... <span class="oc-dots"><span></span><span></span><span></span></span></div>
<div class="oc-sep"></div>
<div class="oc-stats">
<span>模型: <b>openclaw_nemo</b></span> <span>● 運行中</span>
</div>
<div class="oc-stats" style="margin-top:2px">
<span>今日分析: <b>23</b></span>
<span>成功率: <b>91%</b></span>
<span>MTTR: <b>8.2m</b></span>
</div>
</div>
</div>
<div class="ai-terminal">
<div>[18:03] Analyzing worker CPU spike...</div>
<div>[18:03] Root cause: OOM pressure</div>
<div>[18:03] Matched: restart_worker (91%)</div>
<div>[18:03] Awaiting approval <span class="cursor"></span></div>
</div>
</div>
</div>
<!-- PENDING APPROVALS -->
<div class="card pending" style="flex-shrink:0;">
<div class="card-header">
<span class="hdot" style="background:var(--orange)"></span>
<span>待審批任務</span>
<span class="cnt-badge">3</span>
<a class="link">查看全部授權 →</a>
</div>
<div class="card-body">
<div class="approval-item">
<div class="ap-title" style="color:var(--red)">Worker 高負載警告</div>
<div class="ap-target">ssh://wooo@192.168.0.110/restart</div>
<span class="risk-badge risk-low">LOW RISK</span>
<div class="btn-row">
<button class="btn btn-approve" title="點擊批准">批准</button>
<button class="btn btn-reject">拒絕</button>
</div>
</div>
<div class="approval-item">
<div class="ap-title" style="color:var(--orange)">Redis 記憶體壓力</div>
<div class="ap-target">ansible://188/clear_redis_cache.yml</div>
<span class="risk-badge risk-med">MEDIUM</span>
<div class="btn-row">
<button class="btn btn-approve-orange" title="高風險操作需長按確認">長按批准</button>
<button class="btn btn-reject">拒絕</button>
</div>
</div>
</div>
</div>
<!-- INFRASTRUCTURE -->
<div class="card" style="flex-shrink:0;">
<div class="card-header">
<span class="hdot"></span>
<span>基礎架構</span>
<div class="toggle-group">
<button class="toggle-btn" onclick="switchView('host')">主機</button>
<button class="toggle-btn active" onclick="switchView('topo')">拓撲</button>
</div>
<a class="link">展開全圖 →</a>
</div>
<div class="card-body">
<!-- TOPO VIEW -->
<div id="view-topo" class="infra-grid">
<div class="infra-node" style="border-color:var(--blue)">
<div class="in-title">🏗️ 基礎設施 (.110)</div>
<div class="in-sub">7 服務 · ✓ 全部健康</div>
<div class="in-services">
<span class="in-svc">●Gitea</span><span class="in-svc">●Harbor</span><span class="in-svc">●Sentry</span><span class="in-svc">●Prom</span>
</div>
</div>
<div class="infra-node" style="border-color:var(--orange)">
<div class="in-title">🧠 AI/數據 (.188)</div>
<div class="in-sub">7 服務 · ⚡ OpenClaw 診斷中</div>
<div class="in-services">
<span class="in-svc">●PG</span><span class="in-svc">●Redis</span><span class="in-svc diag">●OpenClaw⚡</span><span class="in-svc">●Ollama</span>
</div>
</div>
<div class="infra-node glow-warn" style="border-color:var(--purple)">
<div class="in-title">☸️ K3s 叢集</div>
<div class="in-sub">5 服務 · ⚠️ Worker CPU 89%</div>
<div class="in-services">
<span class="in-svc">●api×2</span><span class="in-svc">●web×2</span><span class="in-svc warn">worker</span>
</div>
</div>
<div class="infra-node" style="border-color:var(--orange)">
<div class="in-title">🌐 外部服務</div>
<div class="in-sub">3 服務 · ✓ 全部可達</div>
<div class="in-services">
<span class="in-svc">●Gemini</span><span class="in-svc">●NVIDIA</span><span class="in-svc">●CF</span>
</div>
</div>
</div>
<!-- HOST VIEW -->
<div id="view-host" class="host-grid" style="display:none">
<div class="host-node">
<div class="hn-title">DevOps 金庫</div>
<div class="hn-ip">192.168.0.110</div>
<div class="prog-row">CPU<div class="prog-bar"><div class="prog-fill" style="width:35%;background:var(--green)"></div></div>35%</div>
<div class="prog-row">RAM<div class="prog-bar"><div class="prog-fill" style="width:55%;background:var(--green)"></div></div>55%</div>
</div>
<div class="host-node">
<div class="hn-title">AI+Web 中心</div>
<div class="hn-ip">192.168.0.188</div>
<div class="prog-row">CPU<div class="prog-bar"><div class="prog-fill" style="width:67%;background:var(--orange)"></div></div>67%</div>
<div class="prog-row">RAM<div class="prog-bar"><div class="prog-fill" style="width:72%;background:var(--orange)"></div></div>72%</div>
</div>
<div class="host-node">
<div class="hn-title">K3s Master</div>
<div class="hn-ip">192.168.0.120</div>
<div class="prog-row">CPU<div class="prog-bar"><div class="prog-fill" style="width:45%;background:var(--green)"></div></div>45%</div>
<div class="prog-row">RAM<div class="prog-bar"><div class="prog-fill" style="width:60%;background:var(--green)"></div></div>60%</div>
</div>
<div class="host-node">
<div class="hn-title">K3s Worker</div>
<div class="hn-ip">192.168.0.121</div>
<div class="prog-row">CPU<div class="prog-bar"><div class="prog-fill" style="width:0;background:#ccc"></div></div>--</div>
<div class="prog-row">RAM<div class="prog-bar"><div class="prog-fill" style="width:0;background:#ccc"></div></div>--</div>
</div>
</div>
</div>
</div>
<!-- AI MODEL STATUS -->
<div class="card" style="flex-shrink:0;">
<div class="card-header">
<span class="hdot"></span>
<span>AI 模型狀態</span>
</div>
<div class="card-body">
<div class="model-grid">
<div class="model-item"><span class="m-dot"></span>OpenClaw Nemo (local)</div>
<div class="model-item"><span class="m-dot"></span>Ollama gemma3 (local)</div>
<div class="model-item"><span class="m-dot"></span>Gemini Pro (cloud)</div>
<div class="model-item"><span class="m-dot"></span>NVIDIA NIM (cloud)</div>
</div>
</div>
</div>
<!-- MONITOR TOOLS -->
<div class="card" style="flex:1;min-height:0;">
<div class="card-header">
<span class="hdot"></span>
<span>監控工具</span>
</div>
<div class="card-body">
<div class="tool-grid">
<div class="tool-item"><div class="t-bar" style="background:var(--blue)"></div><div><div class="t-name">SigNoz</div><div class="t-meta">Traces · Logs</div></div></div>
<div class="tool-item"><div class="t-bar" style="background:#E85530"></div><div><div class="t-name">Grafana</div><div class="t-meta">3 Dashboards</div></div></div>
<div class="tool-item"><div class="t-bar" style="background:var(--green)"></div><div><div class="t-name">Prometheus</div><div class="t-meta">23 targets</div></div></div>
<div class="tool-item"><div class="t-bar" style="background:var(--orange)"></div><div><div class="t-name">Langfuse</div><div class="t-meta">LLMOps</div></div></div>
<div class="tool-item"><div class="t-bar" style="background:var(--red)"></div><div><div class="t-name">Sentry</div><div class="t-meta">2 Projects</div></div></div>
<div class="tool-item"><div class="t-bar" style="background:var(--purple)"></div><div><div class="t-name">Gitea</div><div class="t-meta">CI/CD</div></div></div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- FLOATING FAB -->
<div class="fab">⌨ Omni-Terminal [⌘J]</div>
<script>
function switchView(v) {
const topo = document.getElementById('view-topo');
const host = document.getElementById('view-host');
const btns = document.querySelectorAll('.toggle-btn');
if (v === 'host') {
topo.style.display = 'none';
host.style.display = 'grid';
btns[0].classList.add('active');
btns[1].classList.remove('active');
} else {
topo.style.display = 'grid';
host.style.display = 'none';
btns[0].classList.remove('active');
btns[1].classList.add('active');
}
}
</script>
</body>
</html>