CxWebApp/static/index.html
CxAI Agent 055e350108
Some checks are pending
build-and-push / image (push) Waiting to run
feat: initial CxWebApp (macOS shell + swift-app wired to CxLLM-SDK)
2026-05-16 14:32:01 -05:00

330 lines
16 KiB
HTML

<!DOCTYPE html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark light" />
<title>CxWebApp · Console</title>
<link rel="icon" href="/favicon.ico" type="image/svg+xml" />
<link rel="stylesheet" href="/static/css/style.css" />
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: { extend: { colors: { brand: '#7c5cff' } } }
};
</script>
</head>
<body class="h-full bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100 antialiased">
<div id="app" class="min-h-full flex flex-col">
<!-- Top bar -->
<header class="border-b border-slate-200 dark:border-slate-800 bg-white/70 dark:bg-slate-900/70 backdrop-blur">
<div class="max-w-6xl mx-auto px-4 py-3 flex items-center gap-3">
<div class="w-9 h-9 rounded-lg bg-brand/20 grid place-items-center">
<svg viewBox="0 0 24 24" class="w-5 h-5 text-brand" fill="none" stroke="currentColor" stroke-width="2">
<path d="M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4zM14 14h6v6h-6z"/>
</svg>
</div>
<div class="flex-1">
<h1 class="font-semibold leading-tight">CxWebApp</h1>
<p class="text-xs text-slate-500" id="header-sub">C++ / Crow · loading…</p>
</div>
<span id="health-pill"
class="px-2.5 py-1 rounded-full text-xs font-medium bg-slate-200 text-slate-700 dark:bg-slate-800 dark:text-slate-300">
checking
</span>
<button id="theme-toggle"
class="ml-1 px-2 py-1 rounded-md text-xs border border-slate-300 dark:border-slate-700 hover:bg-slate-100 dark:hover:bg-slate-800">
theme
</button>
</div>
<!-- Tabs -->
<nav class="max-w-6xl mx-auto px-4 flex gap-1 overflow-x-auto">
<button data-tab="dashboard" class="tab tab-active">Dashboard</button>
<button data-tab="items" class="tab">Items</button>
<button data-tab="system" class="tab">System</button>
<button data-tab="diffusion" class="tab">Diffusion</button>
<button data-tab="demand" class="tab">Demand</button>
<button data-tab="lang" class="tab">LangSmith</button>
<button data-tab="slack" class="tab">Slack</button>
<button data-tab="mac" class="tab">macOS App</button>
<button data-tab="websocket" class="tab">WebSocket</button>
<button data-tab="api" class="tab">API Explorer</button>
<button data-tab="about" class="tab">About</button>
</nav>
</header>
<main class="max-w-6xl mx-auto px-4 py-6 w-full flex-1">
<!-- Dashboard -->
<section data-pane="dashboard" class="pane">
<div class="grid gap-4 md:grid-cols-3">
<div class="card">
<div class="card-label">Health</div>
<div class="card-value" id="d-health"></div>
<div class="card-sub" id="d-health-sub">last checked: never</div>
</div>
<div class="card">
<div class="card-label">Uptime</div>
<div class="card-value" id="d-uptime"></div>
<div class="card-sub">seconds since first request</div>
</div>
<div class="card">
<div class="card-label">Items</div>
<div class="card-value" id="d-items"></div>
<div class="card-sub">tracked in-memory</div>
</div>
</div>
<div class="mt-6 card">
<div class="flex items-center justify-between mb-3">
<h2 class="font-semibold">Recent activity</h2>
<button id="d-refresh" class="btn-secondary">Refresh</button>
</div>
<pre id="d-log" class="text-xs leading-relaxed text-slate-600 dark:text-slate-400 whitespace-pre-wrap h-48 overflow-auto"></pre>
</div>
</section>
<!-- Items -->
<section data-pane="items" class="pane hidden">
<div class="card">
<h2 class="font-semibold mb-3">Items</h2>
<form id="add-form" class="flex flex-wrap gap-2 mb-4">
<input type="text" id="item-name" placeholder="Name" required
class="input flex-1 min-w-[12rem]" />
<input type="text" id="item-desc" placeholder="Description"
class="input flex-1 min-w-[14rem]" />
<button type="submit" class="btn-primary">Add</button>
</form>
<div id="items-list" class="divide-y divide-slate-200 dark:divide-slate-800"></div>
</div>
</section>
<!-- System -->
<section data-pane="system" class="pane hidden">
<div class="grid gap-4 md:grid-cols-2">
<div class="card">
<h2 class="font-semibold mb-3">/api/version</h2>
<pre id="sys-version" class="text-xs"></pre>
</div>
<div class="card">
<h2 class="font-semibold mb-3">/api/system</h2>
<pre id="sys-system" class="text-xs"></pre>
</div>
</div>
</section>
<!-- Diffusion -->
<section data-pane="diffusion" class="pane hidden">
<div class="grid gap-4 md:grid-cols-2">
<div class="card">
<h2 class="font-semibold mb-3">Generate</h2>
<form id="diff-form" class="space-y-2">
<textarea id="diff-prompt" rows="3" class="input w-full" placeholder="prompt"></textarea>
<textarea id="diff-neg" rows="2" class="input w-full" placeholder="negative prompt"></textarea>
<div class="grid grid-cols-3 gap-2">
<input type="number" id="diff-steps" class="input" value="20" min="1" max="150" />
<input type="number" id="diff-w" class="input" value="512" min="64" max="2048" />
<input type="number" id="diff-h" class="input" value="512" min="64" max="2048" />
</div>
<div class="grid grid-cols-2 gap-2">
<input type="number" id="diff-cfg" class="input" value="7" step="0.5" min="1" max="30" />
<input type="text" id="diff-sampler" class="input" placeholder="sampler (e.g. Euler a)" />
</div>
<div class="flex gap-2">
<button type="submit" class="btn-primary">Generate</button>
<button type="button" id="diff-models" class="btn-secondary">List models</button>
<button type="button" id="diff-samplers" class="btn-secondary">Samplers</button>
<button type="button" id="diff-progress" class="btn-secondary">Progress</button>
</div>
</form>
<div id="diff-status" class="text-xs text-slate-500 mt-2">idle</div>
</div>
<div class="card">
<h2 class="font-semibold mb-3">Result</h2>
<div id="diff-gallery" class="grid grid-cols-2 gap-2"></div>
<pre id="diff-meta" class="text-xs mt-3 max-h-48 overflow-auto"></pre>
</div>
</div>
</section>
<!-- Demand -->
<section data-pane="demand" class="pane hidden">
<div class="grid gap-4 md:grid-cols-2">
<div class="card">
<h2 class="font-semibold mb-3">Start a run</h2>
<form id="demand-form" class="space-y-2">
<div class="grid grid-cols-2 gap-2">
<input type="number" id="demand-count" class="input" value="5" min="0" max="200" placeholder="designs per run" />
<select id="demand-platform" class="input"></select>
</div>
<div class="grid grid-cols-3 gap-2">
<input type="number" id="demand-top" class="input" placeholder="top trends" />
<input type="number" id="demand-var" class="input" placeholder="variations" />
<input type="number" id="demand-min" class="input" placeholder="min score" />
</div>
<div class="flex gap-3 text-sm items-center">
<label class="flex gap-1 items-center"><input type="checkbox" id="demand-dry" checked /> dry-run</label>
<label class="flex gap-1 items-center"><input type="checkbox" id="demand-up" /> upload</label>
<label class="flex gap-1 items-center"><input type="checkbox" id="demand-web" /> web-trends</label>
<label class="flex gap-1 items-center"><input type="checkbox" id="demand-q" /> quantum</label>
</div>
<button type="submit" class="btn-primary">Run</button>
</form>
<div id="demand-status" class="text-xs text-slate-500 mt-2">idle</div>
</div>
<div class="card">
<h2 class="font-semibold mb-3">Jobs</h2>
<button id="demand-refresh" class="btn-secondary mb-3">Refresh</button>
<div id="demand-jobs" class="space-y-2 text-sm max-h-64 overflow-auto"></div>
<h3 class="font-semibold mt-4 mb-2">Reports</h3>
<div id="demand-reports" class="space-y-1 text-xs max-h-48 overflow-auto"></div>
<pre id="demand-report-body" class="text-xs mt-3 max-h-64 overflow-auto"></pre>
</div>
</div>
</section>
<!-- LangSmith -->
<section data-pane="lang" class="pane hidden">
<div class="card">
<h2 class="font-semibold mb-3">Pipelines</h2>
<div id="lang-pipelines" class="space-y-2 text-sm"></div>
<div class="grid gap-2 md:grid-cols-2 mt-4">
<div>
<label class="text-xs text-slate-500">Selected pipeline</label>
<select id="lang-pipeline" class="input w-full"></select>
<label class="text-xs text-slate-500 mt-2 block">Input JSON</label>
<textarea id="lang-input" rows="6" class="input w-full font-mono text-xs"
placeholder='{ "circuit": "bell", "shots": 1024 }'></textarea>
<div class="flex gap-2 mt-2">
<button id="lang-run" class="btn-primary">Run</button>
<button id="lang-health" class="btn-secondary">Health</button>
</div>
<div id="lang-status" class="text-xs text-slate-500 mt-2">idle</div>
</div>
<div>
<label class="text-xs text-slate-500">Result</label>
<pre id="lang-result" class="text-xs h-64 overflow-auto bg-slate-900 text-slate-100 rounded-lg p-3"></pre>
</div>
</div>
</div>
</section>
<!-- Slack -->
<section data-pane="slack" class="pane hidden">
<div class="grid gap-4 md:grid-cols-2">
<div class="card">
<h2 class="font-semibold mb-3">Status</h2>
<pre id="slack-health" class="text-xs"></pre>
<h3 class="font-semibold mt-4 mb-2">Config</h3>
<pre id="slack-info" class="text-xs"></pre>
<h3 class="font-semibold mt-4 mb-2">Memory</h3>
<pre id="slack-memory" class="text-xs"></pre>
<button id="slack-refresh" class="btn-secondary mt-2">Refresh</button>
</div>
<div class="card">
<h2 class="font-semibold mb-3">Tools</h2>
<div id="slack-tools" class="text-xs max-h-48 overflow-auto"></div>
<h3 class="font-semibold mt-4 mb-2">Send (one-shot)</h3>
<form id="slack-form" class="space-y-2">
<textarea id="slack-text" rows="3" class="input w-full" placeholder="message text"></textarea>
<div class="grid grid-cols-2 gap-2">
<input id="slack-channel" class="input" value="rest" />
<input id="slack-thread" class="input" value="rest" />
</div>
<button type="submit" class="btn-primary">Respond</button>
</form>
<pre id="slack-reply" class="text-xs mt-3 max-h-48 overflow-auto"></pre>
</div>
</div>
</section>
<!-- macOS App -->
<section data-pane="mac" class="pane hidden">
<div class="card">
<h2 class="font-semibold mb-3">CxAI macOS App</h2>
<p class="text-sm text-slate-500 mb-3">
SwiftPM app shipped from <code>apps/cxai-mac</code>. The CxWebApp
container serves this build-info; download URL is configurable via
<code>CXAI_MAC_DOWNLOAD_URL</code>.
</p>
<pre id="mac-info" class="text-xs"></pre>
<a id="mac-download" class="btn-primary mt-3 inline-block hidden">Download .app</a>
<button id="mac-refresh" class="btn-secondary mt-3">Refresh</button>
</div>
</section>
<section data-pane="websocket" class="pane hidden">
<div class="card">
<h2 class="font-semibold mb-3">WebSocket Echo</h2>
<p class="text-sm text-slate-500 mb-3">
Connects to <code class="px-1 py-0.5 bg-slate-200 dark:bg-slate-800 rounded">/ws/echo</code> on the C++ Crow backend.
</p>
<div class="flex gap-2 mb-3">
<button id="ws-connect" class="btn-primary">Connect</button>
<button id="ws-disconnect" class="btn-secondary">Disconnect</button>
<span id="ws-state" class="text-sm self-center text-slate-500">disconnected</span>
</div>
<form id="ws-form" class="flex gap-2 mb-3">
<input type="text" id="ws-input" placeholder="message"
class="input flex-1" />
<button type="submit" class="btn-primary">Send</button>
</form>
<pre id="ws-log" class="text-xs h-64 overflow-auto bg-slate-900 text-emerald-300 rounded-lg p-3"></pre>
</div>
</section>
<!-- API Explorer -->
<section data-pane="api" class="pane hidden">
<div class="card">
<h2 class="font-semibold mb-3">API Explorer</h2>
<div class="flex gap-2 mb-3 flex-wrap">
<select id="api-method" class="input w-24">
<option>GET</option><option>POST</option><option>DELETE</option>
</select>
<input id="api-path" class="input flex-1 min-w-[16rem]" value="/api/health" />
<button id="api-send" class="btn-primary">Send</button>
</div>
<textarea id="api-body" rows="4" class="input w-full font-mono text-xs mb-3"
placeholder='{"name":"Sample","description":"From explorer"}'></textarea>
<div class="flex items-center justify-between mb-2">
<span id="api-status" class="text-xs text-slate-500"></span>
<button id="api-clear" class="text-xs text-slate-500 hover:underline">clear</button>
</div>
<pre id="api-out" class="text-xs h-64 overflow-auto bg-slate-900 text-slate-100 rounded-lg p-3"></pre>
</div>
</section>
<!-- About -->
<section data-pane="about" class="pane hidden">
<div class="card prose dark:prose-invert max-w-none">
<h2 class="font-semibold">About CxWebApp</h2>
<p class="text-sm">
A C++20 web application built on <a href="https://crowcpp.org" class="text-brand">Crow</a>,
running inside a multi-stage Debian container behind Caddy with Let's Encrypt TLS.
</p>
<ul class="text-sm list-disc pl-5 space-y-1">
<li><strong>Backend:</strong> C++20 / Crow v1.2.0 / Asio 1.30 / multi-threaded.</li>
<li><strong>Frontend:</strong> Vanilla JS + Tailwind (CDN). SPA shell with tabs.</li>
<li><strong>Native shell:</strong> SwiftPM macOS app in <code>swift-app/</code> wrapping this UI in a WKWebView with native menus.</li>
<li><strong>Container:</strong> <code>registry.76-13-126-127.nip.io/cxai/cxwebapp:latest</code></li>
</ul>
</div>
</section>
</main>
<footer class="border-t border-slate-200 dark:border-slate-800 mt-auto">
<div class="max-w-6xl mx-auto px-4 py-3 text-xs text-slate-500 flex justify-between">
<span>CxWebApp</span>
<span id="footer-build">build · —</span>
</div>
</footer>
</div>
<script type="module" src="/static/js/app.js"></script>
</body>
</html>