mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 04:57:26 +00:00
615 lines
19 KiB
HTML
615 lines
19 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
<title>OpenClaw</title>
|
|
<script>
|
|
(() => {
|
|
try {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const platform = (params.get('platform') || '').trim().toLowerCase();
|
|
if (platform) {
|
|
document.documentElement.dataset.platform = platform;
|
|
return;
|
|
}
|
|
if (/android/i.test(navigator.userAgent || '')) {
|
|
document.documentElement.dataset.platform = 'android';
|
|
} else {
|
|
document.documentElement.dataset.platform = 'ios';
|
|
}
|
|
} catch (_) {}
|
|
})();
|
|
</script>
|
|
<style>
|
|
: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;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", system-ui, sans-serif;
|
|
background:
|
|
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;
|
|
}
|
|
|
|
body::before,
|
|
body::after {
|
|
content: "";
|
|
position: fixed;
|
|
inset: -10%;
|
|
pointer-events: none;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
display: block;
|
|
z-index: 1;
|
|
}
|
|
|
|
#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;
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
flex-direction: column;
|
|
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.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;
|
|
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);
|
|
}
|
|
|
|
@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 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');
|
|
const ctx = canvas.getContext('2d');
|
|
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);
|
|
const raw = params.get('debugStatus') ?? params.get('debug');
|
|
if (!raw) return false;
|
|
const normalized = String(raw).trim().toLowerCase();
|
|
return normalized === '1' || normalized === 'true' || normalized === 'yes';
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
})();
|
|
let debugStatusEnabled = debugStatusEnabledByQuery;
|
|
|
|
function resize() {
|
|
const dpr = window.devicePixelRatio || 1;
|
|
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();
|
|
|
|
if (!debugStatusEnabled) {
|
|
statusEl.style.display = 'none';
|
|
}
|
|
|
|
window.__openclaw = {
|
|
canvas,
|
|
ctx,
|
|
setDebugStatusEnabled,
|
|
setStatus,
|
|
renderHome
|
|
};
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|