<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Greentic GUI · Chat</title>
<link rel="icon" href="data:," />
<style>
:root { --brand:#16a34a; --bg:#0b1220; --panel:#0f172a; --line:#1e293b; }
html,body { height:100%; margin:0; font-family: system-ui,-apple-system,Segoe UI,Roboto,sans-serif; background:var(--bg); color:#e2e8f0; }
#app { display:flex; flex-direction:column; height:100%; max-width:900px; margin:0 auto; }
header { display:flex; align-items:center; gap:10px; padding:12px 16px; background:var(--panel); border-bottom:1px solid var(--line); }
header .dot { width:10px; height:10px; border-radius:50%; background:var(--brand); box-shadow:0 0 8px var(--brand); }
header h1 { font-size:15px; font-weight:600; margin:0; }
header .meta { margin-left:auto; font-size:12px; color:#94a3b8; }
#log { flex:1; overflow-y:auto; padding:18px; display:flex; flex-direction:column; gap:12px; }
.row { display:flex; }
.row.user { justify-content:flex-end; }
.bubble { max-width:80%; padding:9px 13px; border-radius:14px; line-height:1.4; white-space:pre-wrap; word-break:break-word; }
.user .bubble { background:var(--brand); color:#04140a; border-bottom-right-radius:4px; }
.bot .bubble { background:#fff; color:#0b1220; border-bottom-left-radius:4px; }
.card { background:#fff; color:#0b1220; border-radius:12px; padding:6px 10px; max-width:560px; }
.note .bubble { background:#1e293b; color:#94a3b8; font-size:13px; }
.err .bubble { background:#7f1d1d; color:#fee2e2; }
form { display:flex; gap:8px; padding:12px 16px; border-top:1px solid var(--line); background:var(--panel); }
input { flex:1; padding:11px 14px; border-radius:10px; border:1px solid var(--line); background:#0b1220; color:#e2e8f0; font-size:14px; }
button { padding:0 18px; border:0; border-radius:10px; background:var(--brand); color:#04140a; font-weight:600; cursor:pointer; }
</style>
</head>
<body>
<div id="app">
<header>
<span class="dot"></span>
<h1>Greentic GUI · Chat</h1>
<span class="meta">new deployment model · via greentic-gui worker gateway</span>
</header>
<div id="log"></div>
<form id="composer" autocomplete="off">
<input id="input" placeholder="Type a message…" />
<button type="submit">Send</button>
</form>
</div>
<script src="/adaptivecards.min.js"></script>
<script>
const WORKER_URL = '/api/gui/worker/message';
const SESSION_ID = 'web-' + (self.crypto && crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(36));
const USER_ID = 'web-user';
const log = document.getElementById('log');
const input = document.getElementById('input');
function row(kind) { const r = document.createElement('div'); r.className = 'row ' + kind; log.appendChild(r); return r; }
function bubble(kind, text) { const r = row(kind); const b = document.createElement('div'); b.className = 'bubble'; b.textContent = text; r.appendChild(b); scroll(); }
function scroll() { log.scrollTop = log.scrollHeight; }
function renderCard(cardJson) {
const r = row('bot');
const host = document.createElement('div'); host.className = 'card'; r.appendChild(host);
try {
const ac = new AdaptiveCards.AdaptiveCard();
ac.onExecuteAction = (action) => {
const name = action.getJsonTypeName ? action.getJsonTypeName() : '';
if (name === 'Action.OpenUrl' && action.url) { window.open(action.url, '_blank'); return; }
const data = action.data || {};
const label = data.text || action.title || JSON.stringify(data);
bubble('user', String(label));
send(data);
};
ac.parse(cardJson);
host.appendChild(ac.render());
} catch (e) {
host.textContent = 'Adaptive Card render error: ' + e.message;
}
scroll();
}
function renderMessages(resp) {
const messages = (resp && resp.messages) || [];
if (!messages.length) { bubble('bot', '(no reply)'); return; }
for (const m of messages) {
if (m.kind === 'adaptive-card') renderCard(m.payload);
else if (m.kind === 'text') bubble('bot', (m.payload && m.payload.text) || '');
else bubble('bot', JSON.stringify(m.payload, null, 2));
}
}
async function send(payload) {
try {
const res = await fetch(WORKER_URL, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
worker_id: 'chat',
payload,
context: { session_id: SESSION_ID, user_id: USER_ID },
}),
});
if (!res.ok) {
const body = await res.text();
const hint = res.status === 500 ? ' — is a bundle deployed? Try: gtc op deploy --bundle <file> --bundle-id quickstart' : '';
const r = row('err'); const b = document.createElement('div'); b.className = 'bubble';
b.textContent = `⚠️ HTTP ${res.status}: ${body.slice(0, 200)}${hint}`; r.appendChild(b); scroll();
return;
}
renderMessages(await res.json());
} catch (e) {
const r = row('err'); const b = document.createElement('div'); b.className = 'bubble';
b.textContent = '⚠️ ' + e.message; r.appendChild(b); scroll();
}
}
document.getElementById('composer').addEventListener('submit', (e) => {
e.preventDefault();
const text = input.value.trim();
if (!text) return;
bubble('user', text);
input.value = '';
send({ text });
});
send({});
input.focus();
</script>
</body>
</html>