Files
clawdbot/src/canvas-host/a2ui/index.html
Peter Steinberger c379191f80 chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
2026-01-14 15:02:19 +00:00

308 lines
9.8 KiB
HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Clawdbot Canvas</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";
}
} catch (_) {}
})();
</script>
<style>
:root {
color-scheme: dark;
}
@media (prefers-reduced-motion: reduce) {
body::before,
body::after {
animation: none !important;
}
}
html,
body {
height: 100%;
margin: 0;
}
body {
font:
14px system-ui,
-apple-system,
BlinkMacSystemFont,
"Roboto",
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.1), rgba(0, 0, 0, 0) 60%),
#000;
color: #e5e7eb;
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: clawdbot-grid-drift 140s ease-in-out infinite alternate;
}
:root[data-platform="android"] body::before {
opacity: 0.8;
}
body::after {
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.1), 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);
pointer-events: none;
animation: clawdbot-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;
}
}
@supports not (mix-blend-mode: screen) {
body::after {
opacity: 0.7;
}
}
@keyframes clawdbot-grid-drift {
0% {
transform: translate3d(-12px, 8px, 0) rotate(-7deg);
opacity: 0.4;
}
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 clawdbot-glow-drift {
0% {
transform: translate3d(-18px, 12px, 0) scale(1.02);
opacity: 0.4;
}
50% {
transform: translate3d(14px, -10px, 0) scale(1.05);
opacity: 0.52;
}
100% {
transform: translate3d(-10px, 8px, 0) scale(1.03);
opacity: 0.43;
}
}
canvas {
position: fixed;
inset: 0;
display: block;
width: 100vw;
height: 100vh;
touch-action: none;
z-index: 1;
}
:root[data-platform="android"] #clawdbot-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;
}
#clawdbot-status {
position: fixed;
inset: 0;
display: none;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 24px;
box-sizing: border-box;
pointer-events: none;
z-index: 3;
}
#clawdbot-status .card {
width: min(560px, 88vw);
text-align: left;
padding: 14px 16px 12px;
border-radius: 16px;
background: linear-gradient(140deg, rgba(23, 24, 35, 0.78), rgba(18, 19, 28, 0.55));
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow:
0 16px 46px rgba(0, 0, 0, 0.52),
inset 0 1px 0 rgba(255, 255, 255, 0.06);
-webkit-backdrop-filter: blur(18px) saturate(140%);
backdrop-filter: blur(18px) saturate(140%);
}
#clawdbot-status .title {
font:
600 12px/1.2 -apple-system,
BlinkMacSystemFont,
"SF Pro Text",
system-ui,
sans-serif;
letter-spacing: 0.45px;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.7);
}
#clawdbot-status .subtitle {
margin-top: 8px;
font:
500 13px/1.45 -apple-system,
BlinkMacSystemFont,
"SF Pro Text",
system-ui,
sans-serif;
color: rgba(255, 255, 255, 0.9);
white-space: pre-wrap;
overflow-wrap: anywhere;
}
clawdbot-a2ui-host {
display: block;
height: 100%;
position: fixed;
inset: 0;
z-index: 4;
--clawdbot-a2ui-inset-top: 28px;
--clawdbot-a2ui-inset-right: 0px;
--clawdbot-a2ui-inset-bottom: 0px;
--clawdbot-a2ui-inset-left: 0px;
--clawdbot-a2ui-scroll-pad-bottom: 0px;
--clawdbot-a2ui-status-top: calc(50% - 18px);
--clawdbot-a2ui-empty-top: 18px;
}
</style>
</head>
<body>
<canvas id="clawdbot-canvas"></canvas>
<div id="clawdbot-status">
<div class="card">
<div class="title" id="clawdbot-status-title">Ready</div>
<div class="subtitle" id="clawdbot-status-subtitle">Waiting for agent</div>
</div>
</div>
<clawdbot-a2ui-host></clawdbot-a2ui-host>
<script src="a2ui.bundle.js"></script>
<script>
(() => {
const canvas = document.getElementById("clawdbot-canvas");
const ctx = canvas.getContext("2d");
const statusEl = document.getElementById("clawdbot-status");
const titleEl = document.getElementById("clawdbot-status-title");
const subtitleEl = document.getElementById("clawdbot-status-subtitle");
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 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;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
window.addEventListener("resize", resize);
resize();
const setDebugStatusEnabled = (enabled) => {
debugStatusEnabled = !!enabled;
if (!statusEl) return;
if (!debugStatusEnabled) {
statusEl.style.display = "none";
}
};
if (statusEl && !debugStatusEnabled) {
statusEl.style.display = "none";
}
window.__clawdbot = {
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);
}
},
};
})();
</script>
</body>
</html>