/* ============================================================================
_shared/ai-assist.jsx — Corporate AI Assist (floating panel)
----------------------------------------------------------------------------
On every officer station + admin dashboard, this mounts a floating
button (bottom-right, emerald) that opens a side panel for AI drafting.
Context is read from `window.__AI_CONTEXT` (set per-page or per-drill).
If unset, falls back to {station_slug, route} from the URL.
Backend: POST /api/v1/ai/chat → nginx proxy → 192.168.1.53:8082
(the Jetson AI Router with Llama-3.2 ours-first + xAI cross-check).
Every AI use mints `ai_assist_used` HCC via window.CHWorkCredit.fire.
============================================================================ */
(function () {
"use strict";
if (window.__chAIAssistMounted) return;
window.__chAIAssistMounted = true;
function getToken() {
const m = ("; " + document.cookie).match(/; ch_corp_token=([^;]+)/);
return m ? decodeURIComponent(m[1]) : null;
}
function getContext() {
return Object.assign({
station_slug: window.__STATION || null,
route: location.pathname,
}, window.__AI_CONTEXT || {});
}
function buildSystemPrompt(intent, ctx) {
const lines = [];
if (ctx.item_type) lines.push("Item type: " + ctx.item_type);
if (ctx.item_id) lines.push("Item ID: " + ctx.item_id);
if (ctx.item_summary) lines.push("Item summary: " + ctx.item_summary);
if (ctx.extra) lines.push("Notes: " + ctx.extra);
return lines.join("\n");
}
const INTENTS = [];
/* ---- styles ------------------------------------------------------------- */
const style = document.createElement("style");
style.textContent = `
#ch-ai-fab {
position: fixed; bottom: 18px; right: 18px; z-index: 9990;
width: 52px; height: 52px; border-radius: 50%;
background: #117A4D; color: #FAF9F6; border: 0; cursor: pointer;
box-shadow: 0 10px 30px rgba(17,122,77,.35);
display: grid; place-items: center;
font: 600 18px/1 "Fraunces", Georgia, serif;
transition: transform 150ms ease;
}
#ch-ai-fab:hover { transform: scale(1.08); }
#ch-ai-fab .pulse {
position: absolute; inset: 0; border-radius: 50%;
box-shadow: 0 0 0 0 rgba(17,122,77,.6);
animation: chAiPulse 2.4s ease-out infinite;
}
@keyframes chAiPulse {
0% { box-shadow: 0 0 0 0 rgba(17,122,77,.55); }
80% { box-shadow: 0 0 0 22px rgba(17,122,77,0); }
100% { box-shadow: 0 0 0 0 rgba(17,122,77,0); }
}
#ch-ai-panel {
position: fixed; right: 0; top: 0; bottom: 0;
width: min(560px, 96vw); z-index: 9991;
background: #FAF9F6; border-left: 1px solid rgba(26,26,23,.10);
box-shadow: -20px 0 40px rgba(0,0,0,.18);
display: none; flex-direction: column;
font-family: "Fraunces", Georgia, serif; color: #1A1A17;
}
#ch-ai-panel.open { display: flex; }
#ch-ai-panel header {
padding: 22px 26px 14px; border-bottom: 1px solid rgba(26,26,23,.08);
}
#ch-ai-panel header .eb { font: 500 10px "Geist Mono",monospace; letter-spacing:.22em; text-transform:uppercase; color:#2C5F87; }
#ch-ai-panel header h2 { font: italic 500 24px/1.15 "Fraunces",Georgia,serif; margin:6px 0 4px; }
#ch-ai-panel header .sub { font: 500 11px "Geist Mono",monospace; color:#777570; letter-spacing:.08em; }
#ch-ai-panel .closeX {
position: absolute; top: 14px; right: 18px; background: none; border: 0;
font: 600 18px/1 "Geist Mono",monospace; color:#777570; cursor: pointer;
}
#ch-ai-panel .ctx {
margin: 12px 26px; padding: 10px 14px; border:1px solid rgba(26,26,23,.10);
border-radius: 8px; background: #F4F1EA;
font: 500 11px/1.55 "Geist Mono",monospace; color:#4A4A45;
}
#ch-ai-panel .intents { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 26px 12px; }
#ch-ai-panel .intents button {
padding: 6px 12px; border-radius: 999px; border: 1px solid rgba(26,26,23,.12);
background: #FFFFFF; color: #1A1A17;
font: 600 10px "Geist Mono",monospace; letter-spacing:.12em; text-transform: uppercase;
cursor: pointer;
}
#ch-ai-panel .intents button.active { background:#117A4D; color:#FAF9F6; border-color:#117A4D; }
#ch-ai-panel .promptArea { padding: 0 26px; }
#ch-ai-panel textarea {
width: 100%; border: 1px solid rgba(26,26,23,.12); border-radius: 10px;
padding: 12px 14px; font: 14px/1.5 "Fraunces",Georgia,serif; resize: vertical;
background: #FFFFFF; color: #1A1A17;
}
#ch-ai-panel .row {
display:flex; gap:8px; margin-top: 10px; padding: 0 26px;
}
#ch-ai-panel .row button {
padding: 9px 14px; border-radius: 999px; border: 1px solid rgba(26,26,23,.12);
background: #FFFFFF; color: #1A1A17;
font: 600 11px "Geist Mono",monospace; letter-spacing:.12em; text-transform: uppercase;
cursor: pointer;
}
#ch-ai-panel .row button.primary { background:#117A4D; color:#FAF9F6; border-color:#117A4D; }
#ch-ai-panel .row button:disabled { opacity:.5; cursor:wait; }
#ch-ai-panel .draftWrap { flex: 1; overflow: auto; padding: 14px 26px 26px; }
#ch-ai-panel .draftWrap label {
display:block; font: 600 11px "Geist Mono",monospace; letter-spacing:.18em; text-transform:uppercase; color:#4A4A45; margin: 8px 0 6px;
}
#ch-ai-panel .meta {
display:flex; gap:14px; font: 500 10px "Geist Mono",monospace; color:#777570; letter-spacing:.10em; text-transform:uppercase; margin-top: 8px;
}
`;
document.head.appendChild(style);
/* ---- DOM ---------------------------------------------------------------- */
const fab = document.createElement("button");
fab.id = "ch-ai-fab";
fab.title = "AI Assist · draft a response with the corporate AI";
fab.innerHTML = `⌬`;
const panel = document.createElement("div");
panel.id = "ch-ai-panel";
panel.innerHTML = `
Draft something for the work in front of you.