summaryrefslogtreecommitdiff
path: root/docs/USAGE.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/USAGE.md')
-rw-r--r--docs/USAGE.md233
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).