mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 01:37:27 +00:00
iOS: add welcome home canvas
This commit is contained in:
committed by
Nimrod Gutman
parent
8ba1b6eff1
commit
67746a12de
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>Canvas</title>
|
||||
<title>OpenClaw</title>
|
||||
<script>
|
||||
(() => {
|
||||
try {
|
||||
@@ -15,99 +15,339 @@
|
||||
}
|
||||
if (/android/i.test(navigator.userAgent || '')) {
|
||||
document.documentElement.dataset.platform = 'android';
|
||||
} else {
|
||||
document.documentElement.dataset.platform = 'ios';
|
||||
}
|
||||
} catch (_) {}
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
:root { color-scheme: dark; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body::before, body::after { animation: none !important; }
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
--bg: #06070b;
|
||||
--panel: rgba(14, 17, 24, 0.74);
|
||||
--panel-strong: rgba(18, 23, 32, 0.86);
|
||||
--line: rgba(255, 255, 255, 0.1);
|
||||
--line-strong: rgba(255, 255, 255, 0.18);
|
||||
--text: rgba(255, 255, 255, 0.96);
|
||||
--muted: rgba(222, 229, 239, 0.72);
|
||||
--soft: rgba(222, 229, 239, 0.5);
|
||||
--accent: #8ec5ff;
|
||||
--accent-strong: #5b9dff;
|
||||
--accent-warm: #ff9159;
|
||||
--accent-rose: #ff5fa2;
|
||||
--state: #7d8ca3;
|
||||
--safe-top: env(safe-area-inset-top, 0px);
|
||||
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
||||
}
|
||||
html,body { height:100%; margin:0; }
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
|
||||
background:
|
||||
radial-gradient(1200px 900px at 15% 20%, rgba(42, 113, 255, 0.18), rgba(0,0,0,0) 55%),
|
||||
radial-gradient(900px 700px at 85% 30%, rgba(255, 0, 138, 0.14), rgba(0,0,0,0) 60%),
|
||||
radial-gradient(1000px 900px at 60% 90%, rgba(0, 209, 255, 0.10), rgba(0,0,0,0) 60%),
|
||||
#000;
|
||||
radial-gradient(900px 640px at 12% 10%, rgba(91, 157, 255, 0.36), rgba(0, 0, 0, 0) 58%),
|
||||
radial-gradient(840px 600px at 88% 16%, rgba(255, 95, 162, 0.24), rgba(0, 0, 0, 0) 62%),
|
||||
radial-gradient(960px 720px at 50% 100%, rgba(255, 145, 89, 0.18), rgba(0, 0, 0, 0) 60%),
|
||||
linear-gradient(180deg, #090b11 0%, #05060a 100%);
|
||||
color: var(--text);
|
||||
overflow: hidden;
|
||||
}
|
||||
:root[data-platform="android"] body {
|
||||
background:
|
||||
radial-gradient(1200px 900px at 15% 20%, rgba(42, 113, 255, 0.62), rgba(0,0,0,0) 55%),
|
||||
radial-gradient(900px 700px at 85% 30%, rgba(255, 0, 138, 0.52), rgba(0,0,0,0) 60%),
|
||||
radial-gradient(1000px 900px at 60% 90%, rgba(0, 209, 255, 0.48), rgba(0,0,0,0) 60%),
|
||||
#0b1328;
|
||||
}
|
||||
body::before {
|
||||
content:"";
|
||||
position: fixed;
|
||||
inset: -20%;
|
||||
background:
|
||||
repeating-linear-gradient(0deg, rgba(255,255,255,0.03) 0, rgba(255,255,255,0.03) 1px,
|
||||
transparent 1px, transparent 48px),
|
||||
repeating-linear-gradient(90deg, rgba(255,255,255,0.03) 0, rgba(255,255,255,0.03) 1px,
|
||||
transparent 1px, transparent 48px);
|
||||
transform: translate3d(0,0,0) rotate(-7deg);
|
||||
will-change: transform, opacity;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
opacity: 0.45;
|
||||
pointer-events: none;
|
||||
animation: openclaw-grid-drift 140s ease-in-out infinite alternate;
|
||||
}
|
||||
:root[data-platform="android"] body::before { opacity: 0.80; }
|
||||
|
||||
body::before,
|
||||
body::after {
|
||||
content:"";
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: -35%;
|
||||
background:
|
||||
radial-gradient(900px 700px at 30% 30%, rgba(42,113,255,0.16), rgba(0,0,0,0) 60%),
|
||||
radial-gradient(800px 650px at 70% 35%, rgba(255,0,138,0.12), rgba(0,0,0,0) 62%),
|
||||
radial-gradient(900px 800px at 55% 75%, rgba(0,209,255,0.10), rgba(0,0,0,0) 62%);
|
||||
filter: blur(28px);
|
||||
opacity: 0.52;
|
||||
will-change: transform, opacity;
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
transform: translate3d(0,0,0);
|
||||
inset: -10%;
|
||||
pointer-events: none;
|
||||
animation: openclaw-glow-drift 110s ease-in-out infinite alternate;
|
||||
}
|
||||
:root[data-platform="android"] body::after { opacity: 0.85; }
|
||||
@supports (mix-blend-mode: screen) {
|
||||
body::after { mix-blend-mode: screen; }
|
||||
|
||||
body::before {
|
||||
background:
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
rgba(255, 255, 255, 0.025) 0,
|
||||
rgba(255, 255, 255, 0.025) 1px,
|
||||
transparent 1px,
|
||||
transparent 52px
|
||||
),
|
||||
repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(255, 255, 255, 0.025) 0,
|
||||
rgba(255, 255, 255, 0.025) 1px,
|
||||
transparent 1px,
|
||||
transparent 52px
|
||||
);
|
||||
opacity: 0.42;
|
||||
transform: rotate(-7deg);
|
||||
}
|
||||
@supports not (mix-blend-mode: screen) {
|
||||
body::after { opacity: 0.70; }
|
||||
}
|
||||
@keyframes openclaw-grid-drift {
|
||||
0% { transform: translate3d(-12px, 8px, 0) rotate(-7deg); opacity: 0.40; }
|
||||
50% { transform: translate3d( 10px,-7px, 0) rotate(-6.6deg); opacity: 0.56; }
|
||||
100% { transform: translate3d(-8px, 6px, 0) rotate(-7.2deg); opacity: 0.42; }
|
||||
}
|
||||
@keyframes openclaw-glow-drift {
|
||||
0% { transform: translate3d(-18px, 12px, 0) scale(1.02); opacity: 0.40; }
|
||||
50% { transform: translate3d( 14px,-10px, 0) scale(1.05); opacity: 0.52; }
|
||||
100% { transform: translate3d(-10px, 8px, 0) scale(1.03); opacity: 0.43; }
|
||||
|
||||
body::after {
|
||||
background:
|
||||
radial-gradient(700px 460px at 20% 18%, rgba(142, 197, 255, 0.18), rgba(0, 0, 0, 0) 62%),
|
||||
radial-gradient(720px 520px at 84% 20%, rgba(255, 95, 162, 0.14), rgba(0, 0, 0, 0) 66%),
|
||||
radial-gradient(860px 620px at 52% 88%, rgba(255, 145, 89, 0.14), rgba(0, 0, 0, 0) 64%);
|
||||
filter: blur(28px);
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
body[data-state="connected"] { --state: #61d58b; }
|
||||
body[data-state="connecting"] { --state: #ffd05f; }
|
||||
body[data-state="error"] { --state: #ff6d6d; }
|
||||
body[data-state="offline"] { --state: #95a3b9; }
|
||||
|
||||
canvas {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display:block;
|
||||
width:100vw;
|
||||
height:100vh;
|
||||
touch-action: none;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: block;
|
||||
z-index: 1;
|
||||
}
|
||||
:root[data-platform="android"] #openclaw-canvas {
|
||||
background:
|
||||
radial-gradient(1100px 800px at 20% 15%, rgba(42, 113, 255, 0.78), rgba(0,0,0,0) 58%),
|
||||
radial-gradient(900px 650px at 82% 28%, rgba(255, 0, 138, 0.66), rgba(0,0,0,0) 62%),
|
||||
radial-gradient(1000px 900px at 60% 88%, rgba(0, 209, 255, 0.58), rgba(0,0,0,0) 62%),
|
||||
#141c33;
|
||||
|
||||
#openclaw-home {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
padding: calc(var(--safe-top) + 22px) 18px calc(var(--safe-bottom) + 18px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.shell {
|
||||
width: min(100%, 760px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 28px;
|
||||
background: linear-gradient(180deg, rgba(18, 24, 34, 0.86), rgba(10, 13, 19, 0.94));
|
||||
border: 1px solid var(--line);
|
||||
box-shadow: 0 28px 90px rgba(0, 0, 0, 0.42);
|
||||
padding: 22px 22px 18px;
|
||||
}
|
||||
|
||||
.hero::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -30% auto auto -20%;
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
border-radius: 999px;
|
||||
background: radial-gradient(circle, rgba(142, 197, 255, 0.18), rgba(0, 0, 0, 0));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.eyebrow-dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 999px;
|
||||
background: var(--state);
|
||||
box-shadow: 0 0 18px color-mix(in srgb, var(--state) 68%, transparent);
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
margin: 18px 0 0;
|
||||
font-size: clamp(32px, 7vw, 52px);
|
||||
line-height: 0.98;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
margin: 14px 0 0;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
color: var(--muted);
|
||||
max-width: 32rem;
|
||||
}
|
||||
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr 1fr;
|
||||
gap: 12px;
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.meta-card,
|
||||
.agent-card {
|
||||
border-radius: 22px;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
backdrop-filter: blur(18px);
|
||||
-webkit-backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.meta-card {
|
||||
padding: 16px 16px 15px;
|
||||
}
|
||||
|
||||
.meta-label {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--soft);
|
||||
}
|
||||
|
||||
.meta-value {
|
||||
margin-top: 8px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.meta-subtitle {
|
||||
margin-top: 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.agent-focus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.agent-badge {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(142, 197, 255, 0.22), rgba(91, 157, 255, 0.1)),
|
||||
rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.agent-focus .name {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.agent-focus .caption {
|
||||
margin-top: 4px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 16px 16px 14px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.section-count {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: var(--soft);
|
||||
}
|
||||
|
||||
.agent-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.agent-card {
|
||||
padding: 13px 13px 12px;
|
||||
}
|
||||
|
||||
.agent-card.active {
|
||||
background: var(--panel-strong);
|
||||
border-color: var(--line-strong);
|
||||
box-shadow: inset 0 0 0 1px rgba(142, 197, 255, 0.12);
|
||||
}
|
||||
|
||||
.agent-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.agent-row .badge {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.agent-row .name {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.agent-row .caption {
|
||||
margin-top: 3px;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 18px;
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px dashed rgba(255, 255, 255, 0.12);
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
margin-top: 12px;
|
||||
color: var(--soft);
|
||||
font-size: 12px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
#openclaw-status {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -115,41 +355,116 @@
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: column;
|
||||
padding-top: calc(20px + env(safe-area-inset-top, 0px));
|
||||
padding-top: calc(var(--safe-top) + 18px);
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#openclaw-status .card {
|
||||
text-align: center;
|
||||
padding: 16px 18px;
|
||||
border-radius: 14px;
|
||||
background: rgba(18, 18, 22, 0.42);
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
box-shadow: 0 18px 60px rgba(0,0,0,0.55);
|
||||
background: rgba(18, 18, 22, 0.46);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.55);
|
||||
-webkit-backdrop-filter: blur(14px);
|
||||
backdrop-filter: blur(14px);
|
||||
}
|
||||
|
||||
#openclaw-status .title {
|
||||
font: 600 20px -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
|
||||
letter-spacing: 0.2px;
|
||||
color: rgba(255,255,255,0.92);
|
||||
text-shadow: 0 0 22px rgba(42, 113, 255, 0.35);
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
#openclaw-status .subtitle {
|
||||
margin-top: 6px;
|
||||
font: 500 12px -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
|
||||
color: rgba(255,255,255,0.58);
|
||||
color: rgba(255, 255, 255, 0.58);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
#openclaw-home {
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
border-radius: 24px;
|
||||
padding: 18px 16px 16px;
|
||||
}
|
||||
|
||||
.hero-grid,
|
||||
.agent-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
body::before,
|
||||
body::after {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<body data-state="offline">
|
||||
<canvas id="openclaw-canvas"></canvas>
|
||||
<div id="openclaw-home">
|
||||
<div class="shell">
|
||||
<div class="hero">
|
||||
<div class="eyebrow">
|
||||
<span class="eyebrow-dot"></span>
|
||||
<span id="openclaw-home-eyebrow">Welcome to OpenClaw</span>
|
||||
</div>
|
||||
<h1 id="openclaw-home-title">Your phone stays quiet until it is needed</h1>
|
||||
<p id="openclaw-home-subtitle">
|
||||
Pair this device to your gateway to wake it only for real work, keep a live agent overview handy, and avoid battery-draining background loops.
|
||||
</p>
|
||||
|
||||
<div class="hero-grid">
|
||||
<div class="meta-card">
|
||||
<div class="meta-label">Gateway</div>
|
||||
<div class="meta-value" id="openclaw-home-gateway">Gateway</div>
|
||||
<div class="meta-subtitle" id="openclaw-home-gateway-caption">Connect to load your agents</div>
|
||||
</div>
|
||||
|
||||
<div class="meta-card">
|
||||
<div class="meta-label">Active Agent</div>
|
||||
<div class="agent-focus">
|
||||
<div class="agent-badge" id="openclaw-home-active-badge">OC</div>
|
||||
<div>
|
||||
<div class="name" id="openclaw-home-active-name">Main</div>
|
||||
<div class="caption" id="openclaw-home-active-caption">Connect to load your agents</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="meta-card section">
|
||||
<div class="section-header">
|
||||
<div class="section-title">Live agents</div>
|
||||
<div class="section-count" id="openclaw-home-agent-count">0 agents</div>
|
||||
</div>
|
||||
<div class="agent-grid" id="openclaw-home-agent-grid"></div>
|
||||
<div class="footer-note" id="openclaw-home-footer">
|
||||
When connected, the gateway can wake the phone with a silent push instead of holding an always-on session.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="openclaw-status">
|
||||
<div class="card">
|
||||
<div class="title" id="openclaw-status-title">Ready</div>
|
||||
<div class="subtitle" id="openclaw-status-subtitle">Waiting for agent</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const canvas = document.getElementById('openclaw-canvas');
|
||||
@@ -157,6 +472,20 @@
|
||||
const statusEl = document.getElementById('openclaw-status');
|
||||
const titleEl = document.getElementById('openclaw-status-title');
|
||||
const subtitleEl = document.getElementById('openclaw-status-subtitle');
|
||||
const home = {
|
||||
root: document.getElementById('openclaw-home'),
|
||||
eyebrow: document.getElementById('openclaw-home-eyebrow'),
|
||||
title: document.getElementById('openclaw-home-title'),
|
||||
subtitle: document.getElementById('openclaw-home-subtitle'),
|
||||
gateway: document.getElementById('openclaw-home-gateway'),
|
||||
gatewayCaption: document.getElementById('openclaw-home-gateway-caption'),
|
||||
activeBadge: document.getElementById('openclaw-home-active-badge'),
|
||||
activeName: document.getElementById('openclaw-home-active-name'),
|
||||
activeCaption: document.getElementById('openclaw-home-active-caption'),
|
||||
agentCount: document.getElementById('openclaw-home-agent-count'),
|
||||
agentGrid: document.getElementById('openclaw-home-agent-grid'),
|
||||
footer: document.getElementById('openclaw-home-footer')
|
||||
};
|
||||
const debugStatusEnabledByQuery = (() => {
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
@@ -172,54 +501,114 @@
|
||||
|
||||
function resize() {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const w = Math.max(1, Math.floor(window.innerWidth * dpr));
|
||||
const h = Math.max(1, Math.floor(window.innerHeight * dpr));
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const width = Math.max(1, Math.floor(window.innerWidth * dpr));
|
||||
const height = Math.max(1, Math.floor(window.innerHeight * dpr));
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
}
|
||||
|
||||
function setDebugStatusEnabled(enabled) {
|
||||
debugStatusEnabled = !!enabled;
|
||||
if (!debugStatusEnabled) {
|
||||
statusEl.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus(title, subtitle) {
|
||||
if (!debugStatusEnabled) return;
|
||||
if (!title && !subtitle) {
|
||||
statusEl.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
statusEl.style.display = 'flex';
|
||||
if (typeof title === 'string') titleEl.textContent = title;
|
||||
if (typeof subtitle === 'string') subtitleEl.textContent = subtitle;
|
||||
}
|
||||
|
||||
function clearChildren(node) {
|
||||
while (node.firstChild) node.removeChild(node.firstChild);
|
||||
}
|
||||
|
||||
function createAgentCard(agent) {
|
||||
const card = document.createElement('div');
|
||||
card.className = `agent-card${agent.isActive ? ' active' : ''}`;
|
||||
|
||||
const row = document.createElement('div');
|
||||
row.className = 'agent-row';
|
||||
|
||||
const badge = document.createElement('div');
|
||||
badge.className = 'badge';
|
||||
badge.textContent = agent.badge || 'OC';
|
||||
|
||||
const text = document.createElement('div');
|
||||
|
||||
const name = document.createElement('div');
|
||||
name.className = 'name';
|
||||
name.textContent = agent.name || agent.id || 'Agent';
|
||||
|
||||
const caption = document.createElement('div');
|
||||
caption.className = 'caption';
|
||||
caption.textContent = agent.caption || 'Ready';
|
||||
|
||||
text.appendChild(name);
|
||||
text.appendChild(caption);
|
||||
row.appendChild(badge);
|
||||
row.appendChild(text);
|
||||
card.appendChild(row);
|
||||
return card;
|
||||
}
|
||||
|
||||
function renderHome(state) {
|
||||
if (!state || typeof state !== 'object') return;
|
||||
|
||||
document.body.dataset.state = state.gatewayState || 'offline';
|
||||
home.root.style.display = 'flex';
|
||||
home.eyebrow.textContent = state.eyebrow || 'Welcome to OpenClaw';
|
||||
home.title.textContent = state.title || 'OpenClaw';
|
||||
home.subtitle.textContent = state.subtitle || '';
|
||||
home.gateway.textContent = state.gatewayLabel || 'Gateway';
|
||||
home.gatewayCaption.textContent = state.gatewayState === 'connected'
|
||||
? `${state.agentCount || 0} agent${state.agentCount === 1 ? '' : 's'} available`
|
||||
: (state.activeAgentCaption || 'Connect to load your agents');
|
||||
home.activeBadge.textContent = state.activeAgentBadge || 'OC';
|
||||
home.activeName.textContent = state.activeAgentName || 'Main';
|
||||
home.activeCaption.textContent = state.activeAgentCaption || '';
|
||||
home.agentCount.textContent = `${state.agentCount || 0} agent${state.agentCount === 1 ? '' : 's'}`;
|
||||
home.footer.textContent = state.footer || '';
|
||||
|
||||
clearChildren(home.agentGrid);
|
||||
const agents = Array.isArray(state.agents) ? state.agents : [];
|
||||
if (!agents.length) {
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'empty-state';
|
||||
empty.textContent = state.gatewayState === 'connected'
|
||||
? 'Your gateway is online. Agents will appear here as soon as the current scope reports them.'
|
||||
: 'Connect this phone to your gateway and the live agent overview will appear here.';
|
||||
home.agentGrid.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
agents.forEach((agent) => {
|
||||
home.agentGrid.appendChild(createAgentCard(agent));
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('resize', resize);
|
||||
resize();
|
||||
|
||||
const setDebugStatusEnabled = (enabled) => {
|
||||
debugStatusEnabled = !!enabled;
|
||||
if (!statusEl) return;
|
||||
if (!debugStatusEnabled) {
|
||||
statusEl.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
if (statusEl && !debugStatusEnabled) {
|
||||
if (!debugStatusEnabled) {
|
||||
statusEl.style.display = 'none';
|
||||
}
|
||||
|
||||
const api = {
|
||||
window.__openclaw = {
|
||||
canvas,
|
||||
ctx,
|
||||
setDebugStatusEnabled,
|
||||
setStatus: (title, subtitle) => {
|
||||
if (!statusEl || !debugStatusEnabled) return;
|
||||
if (!title && !subtitle) {
|
||||
statusEl.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
statusEl.style.display = 'flex';
|
||||
if (titleEl && typeof title === 'string') titleEl.textContent = title;
|
||||
if (subtitleEl && typeof subtitle === 'string') subtitleEl.textContent = subtitle;
|
||||
if (!debugStatusEnabled) {
|
||||
clearTimeout(window.__statusTimeout);
|
||||
window.__statusTimeout = setTimeout(() => {
|
||||
statusEl.style.display = 'none';
|
||||
}, 3000);
|
||||
} else {
|
||||
clearTimeout(window.__statusTimeout);
|
||||
}
|
||||
}
|
||||
setStatus,
|
||||
renderHome
|
||||
};
|
||||
window.__openclaw = api;
|
||||
})();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user