330 lines
16 KiB
HTML
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>
|