diff options
| author | Agando <roduccomm@gmail.com> | 2026-11-09 15:02:17 +0100 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-11-09 15:02:17 +0100 |
| commit | ba15e7a4bf9dc4ab6b312f40c2e0199d4e5a8255 (patch) | |
| tree | 0f834eb7b76b2eefd4c61dae50231c6c1c4385e7 /docs/USAGE.md | |
feat(chat-widget): shippable widget package replacing the demo as the paid download
The store sold a €149 package whose download_url was the sales demo page. This is the widget
alone: 601 lines, no CDN, no operator host, no model name, config extracted to config.example.js,
wx- prefixed ids so embeds cannot collide. Verified in headless Chromium against a mock gateway
(streaming, chips, link detection, unconfigured state).
Diffstat (limited to 'docs/USAGE.md')
| -rw-r--r-- | docs/USAGE.md | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 0000000..b71c3cd --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,233 @@ +# Using the WebX11 Chat Widget + +## 1. Files + +``` +widget.html the widget (HTML + CSS + JS, self-contained) +config.example.js copy to config.js and edit +``` + +Both files must sit in the same directory, because `widget.html` loads +`config.js` with a relative `<script src="config.js">`. If `config.js` is +missing the widget still renders, but it will refuse to send and prints +“not configured” in the panel. + +## 2. Configure + +```bash +cp config.example.js config.js +``` + +Open `config.js` and set at least these: + +```js +window.WX_WIDGET_CONFIG = { + endpoint: 'https://YOUR-HOST/ollama', // your gateway, /api/generate is appended + model: 'llama3.2:latest', + title: 'Chat with us', + accentColor: '#00e5a0', + welcomeMessage: 'Hi! How can I help you today?', + systemPrompt: 'You are a helpful assistant for …' +}; +``` + +Notes: + +- `endpoint` may be a base URL (`https://YOUR-HOST/ollama`) or the full + generate URL (`https://YOUR-HOST/ollama/api/generate`); the widget appends + `/api/generate` only when it is not already there. +- A relative endpoint such as `/ollama` works if the widget and the gateway + share an origin. +- `accentColor` accepts `#rgb` or `#rrggbb`. Bubble/button text switches + between dark and light automatically for contrast. +- `chips` is an array of `[label, message]` pairs. An empty array hides the row. +- `promptUrl` is optional. If set, the widget fetches the system prompt from + that plain-text URL at load, retries once, and falls back to the inline + `systemPrompt` on failure. + +### About the API key — read this once + +The widget is browser code. Every byte of `config.js` and `widget.html` is +visible to any visitor, so **no key, token or password may ever be placed in +them**. Your gateway is responsible for authentication: it holds the key in +a server-side secret (environment variable or root-owned file) and adds it to +the upstream request. The browser only ever talks to your gateway. + +## 3. Embed in an existing site + +`widget.html` is a working demo page. To embed, copy two blocks out of it into +your own template: + +1. The `<div id="wx-chat-widget"> … </div>` block (the markup). +2. The `<style> … </style>` block, and the `<script>` block that follows it. + +Then add one tag before them: + +```html +<script src="/path/to/config.js"></script> +``` + +Order matters: `config.js` must be loaded before the widget script runs, and +the widget script must run after the DOM nodes exist (place it at the end of +`<body>`, or wrap your own init in `DOMContentLoaded`). + +Minimal page: + +```html +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>My site</title> + <!-- paste the widget <style> block here, or link it from a .css file --> +</head> +<body> + <h1>My site</h1> + + <!-- paste <div id="wx-chat-widget"> … </div> here --> + <!-- paste the widget <script> block here --> + + <script src="/path/to/config.js"></script> +</body> +</html> +``` + +The widget uses `wx-` prefixed ids and classes and all CSS is scoped to +`#wx-chat-widget`, so it will not collide with your site's styles. It sets +`z-index: 2147483000`; lower it in your CSS if you have a cookie banner that +must sit above the bubble. + +## 4. The endpoint contract + +The widget sends: + +``` +POST <endpoint> +Content-Type: application/json + +{"model":"<model>","prompt":"<systemPrompt>\n\nUser Question: <text>","stream":true} +``` + +and expects newline-delimited JSON (Ollama `/api/generate` format), where each +line has a `response` string: + +``` +{"model":"llama3.2:latest","response":"Hello","done":false} +{"model":"llama3.2:latest","response":" there","done":false} +{"model":"llama3.2:latest","response":"","done":true} +``` + +Server-sent events (`data: {...}`) and a non-streaming JSON reply with a +`response` field are also accepted, so most OpenAI-style shims work if they +emit one of those shapes. + +## 5. Gateway examples + +nginx reverse proxy in front of a local Ollama: + +```nginx +location /ollama/ { + proxy_pass http://127.0.0.1:11434/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_buffering off; # streaming + proxy_read_timeout 300s; + # your key is added here, server side, e.g. + # proxy_set_header Authorization "..." ; (kept out of this doc on purpose) +} +``` + +Ollama itself refuses cross-origin browser calls unless the origin is allowed: + +```bash +# systemd drop-in or shell environment for the ollama service +OLLAMA_ORIGINS="https://www.example.com,https://example.com" +``` + +Caddy: + +```caddy +handle_path /ollama/* { + reverse_proxy 127.0.0.1:11434 +} +``` + +## 6. Troubleshooting + +**Nothing happens when I open the page.** +Check the browser console. A 404 on `config.js` means the file is not next to +`widget.html` or the path in the `<script src>` is wrong. + +**“This chat is not configured yet.”** +`endpoint` is empty in `config.js`, or `config.js` failed to load. + +**“Sorry — I could not reach the chat service.” with `TypeError: Failed to fetch`.** +This is almost always CORS. The widget runs on *your* domain, so the endpoint +must return `Access-Control-Allow-Origin` for that exact origin, and must +answer the `OPTIONS` preflight (the request carries +`Content-Type: application/json`, which is not a “simple” request). + +The endpoint must allow the customer's origin — every origin that embeds the +widget, including `http://localhost:8000` while testing: + +``` +Access-Control-Allow-Origin: https://www.example.com +Access-Control-Allow-Methods: POST, OPTIONS +Access-Control-Allow-Headers: Content-Type +``` + +`Access-Control-Allow-Origin: *` also works and is fine for a public widget. +Do not combine `*` with credentials; the widget sends no credentials. + +Quick check from a terminal: + +```bash +curl -i -X POST https://YOUR-HOST/ollama/api/generate \ + -H 'Origin: https://www.example.com' \ + -H 'Content-Type: application/json' \ + -d '{"model":"llama3.2:latest","prompt":"hi","stream":false}' +``` + +If the response has no `Access-Control-Allow-Origin` header, the browser will +block it no matter what the widget does. + +**HTTP 401 / 403.** +The gateway is rejecting the request. The key belongs on the gateway, not in +`config.js`. Fix the server-side secret. + +**HTTP 404.** +Wrong path. Confirm the gateway exposes `/api/generate` (the widget appends it +to whatever base you configured). + +**The reply appears all at once instead of streaming.** +A proxy is buffering. In nginx set `proxy_buffering off;` for that location. +Cloudflare and similar CDNs may also buffer unless the response is chunked. + +**The model name is rejected.** +`model` must match exactly what your backend lists (`ollama list`). If you use +a cloud/tagged model, include the tag. + +**Nothing is remembered between messages.** +By design in 1.0.0 — see the limitation note in `README.md`. Each request +contains the system prompt plus the current question only. + +**Styles look wrong inside my site.** +Your page probably sets `* { box-sizing }` or global button styles that reach +into the widget. All widget rules are scoped to `#wx-chat-widget`, but global +rules with higher specificity can still win; add `!important` overrides +targeting `#wx-chat-widget` rather than editing the widget source. + +**The bubble overlaps my cookie banner or WhatsApp button.** +Change `position` in `config.js`, or override in your CSS: + +```css +#wx-chat-widget { bottom: 96px; } +``` + +## 7. Support + +Thirty days of setup help are included with the Setup Package. When asking for +help, include: the page URL, the browser console error, the failing request's +status code, and the `endpoint` value with the hostname replaced by +`YOUR-HOST` (never send a key). |