HoopAI AI Chat Widget — Design Spec
Date: 2026-03-29
Status: Approved
Problem
The current VoiceGlow/ConvoCore widget is broken and unreliable. It is a third-party hosted service with no control over uptime, UI, or AI behaviour. API keys cannot be managed securely and the UI does not match the HoopAI design system.
Goal
Replace VoiceGlow entirely with a fully custom AI chat widget powered by Claude Haiku 4.5 via OpenRouter, backed by a Cloudflare Worker proxy that keeps all API keys off the client. The widget should match Mintlify’s native AI panel UX — a right-side drawer that pushes page content left — and feel like a first-class part of the docs site.
Architecture
Browser (help.hoopai.com — static Mintlify)
theme/chat-widget.js
│
│ POST /chat (SSE stream)
▼
Cloudflare Worker (hoopai-chat-api.workers.dev)
│ env: OPENROUTER_API_KEY (secret, never in code)
│ env: BRAVE_SEARCH_API_KEY (secret, optional)
│
├──▶ OpenRouter → anthropic/claude-haiku-4-5 (streaming)
└──▶ Brave Search API (web search tool)
Why Cloudflare Workers
- Edge-deployed, ~0ms cold start, global low latency
- Free tier: 100k requests/day — more than sufficient for a help centre
- Secrets stored in Cloudflare dashboard, never in git or code
- Streaming SSE supported natively
- No separate server to maintain
Part 1 — Cloudflare Worker
Location: worker/ directory in the repo (Mintlify ignores this)
Files
worker/
wrangler.toml — worker config (name, compat date, vars)
src/index.js — worker entry point
Endpoint: POST /chat
Request body:
{
"messages": [{ "role": "user", "content": "..." }],
"pageContext": { "title": "...", "url": "..." }
}
Response: text/event-stream (SSE), OpenRouter streaming format passed through directly.
| Tool | Description | Implementation |
|---|
web_search | Search the web for current info | Calls Brave Search API from the Worker |
create_support_ticket | Create a support ticket | Sends structured payload to a configurable webhook URL |
Security
- CORS:
Access-Control-Allow-Origin locked to https://help.hoopai.com and http://localhost:3000
- No other origin can call the worker
OPENROUTER_API_KEY set via wrangler secret put — never in code
- Rate limiting: Cloudflare’s default per-IP rate limiting applies
System prompt (injected by worker)
You are a helpful support assistant for HoopAI — an all-in-one CRM and marketing platform.
You are embedded in the HoopAI Help Center documentation site.
Current page: {pageContext.title} ({pageContext.url})
Answer questions about HoopAI features directly and concisely.
When the user asks about something not covered in your training, use web_search to find current information.
When a user has an unresolved issue that needs human support, offer to create a support ticket using create_support_ticket.
Never mention GoHighLevel, HighLevel, GHL, Marketing Muse, or WhoPayI — always say "HoopAI" or "Hoop".
Location: theme/chat-widget.js (replaces current VoiceGlow file entirely)
CSS: New #hoopai-panel-* rules added to theme/custom.css
Panel Behaviour
- Fixed right-side drawer,
position: fixed, top: 0, right: 0, height: 100vh
- Default width:
380px. Expanded width: 680px. Both transition smoothly.
- When open:
body.hoopai-panel-open #content-side-layout { margin-right: 380px } — content pushes left
- When expanded: margin increases to
680px
- Transition:
0.25s ease on both the panel and the content margin
- Z-index: high enough to sit above Mintlify’s sidebar overlay but below modals
- Inherits dark/light mode automatically via existing
--ds-* CSS custom properties
Panel Structure (HTML)
#hoopai-panel
#hoopai-panel-header
.hoopai-panel-logo (sparkle icon)
.hoopai-panel-title ("Ask AI")
.hoopai-panel-actions
#hoopai-expand-btn (expand/collapse)
#hoopai-clear-btn (clear conversation)
#hoopai-close-btn (close panel)
#hoopai-panel-messages
.hoopai-msg-empty (shown when no messages — suggested prompts)
.hoopai-msg-user (user message bubble)
.hoopai-msg-assistant (assistant bubble, streams in token by token)
.hoopai-msg-tool (tool call pill: "Searching the web…")
#hoopai-panel-footer
#hoopai-input (textarea, auto-grows)
#hoopai-send-btn (send, disabled while streaming)
Suggested prompts (empty state)
Three prompts generated from the current page title shown as clickable chips. Clicking one fills the input and sends.
Streaming
- Uses
fetch with ReadableStream to consume the SSE from the Worker
- Renders tokens progressively into the assistant bubble as they arrive
- Shows an animated three-dot indicator while waiting for first token
- Send button disabled + shows stop icon during streaming; clicking it aborts the stream
When the AI calls web_search or create_support_ticket, a pill appears in the message list:
🔍 Searching the web… → replaced with 🔍 Web search complete when done
🎫 Creating support ticket… → replaced with ✓ Ticket created when done
Page context injection
Every new conversation automatically prepends page context. When triggered from:
- Navbar / page Ask AI button — context message shown as a small pill above the input:
📄 Reading: <page title>
- Code block Ask AI button — code snippet shown in a collapsed expandable above the input
Ask AI entry points (all preserved from current widget)
- Navbar button (desktop) —
#assistant-entry
- Mobile header button —
#assistant-entry-mobile
- Code block button —
data-ask-ai-injected
- Page-level button —
#page-ask-ai-btn
- Keyboard shortcut —
Ctrl+I / Cmd+I
Dark / light mode
All colours use var(--ds-*) tokens from theme/custom.css. No hardcoded colours in widget JS or CSS. Panel automatically matches the site theme.
Part 3 — Deployment Steps
- Install Wrangler CLI locally:
npm install -g wrangler
- Authenticate:
CLOUDFLARE_API_TOKEN=<token> wrangler whoami
- Deploy worker:
CLOUDFLARE_API_TOKEN=<token> wrangler deploy
- Set secrets:
wrangler secret put OPENROUTER_API_KEY
wrangler secret put BRAVE_SEARCH_API_KEY (optional, enables web search)
- Capture deployed Worker URL (
https://hoopai-chat-api.<account>.workers.dev)
- Embed Worker URL in
theme/chat-widget.js as WORKER_URL constant
- Remove VoiceGlow script and
window.VG_CONFIG from widget JS
Out of Scope (this phase)
- Custom domain for the Worker (can be added later in Cloudflare dashboard)
- Conversation persistence across page loads (each page load starts fresh)
- User authentication / per-user history
- HoopAI CRM integration for ticket creation (webhook URL to be provided by user)
- Brave Search setup (slot wired, key optional — web search gracefully disabled if key absent)
Last modified on April 16, 2026