bunnyquery 1.1.4 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +112 -148
  2. package/bunnyquery.css +761 -952
  3. package/bunnyquery.js +3679 -2478
  4. package/package.json +22 -11
package/README.md CHANGED
@@ -1,193 +1,157 @@
1
- # BunnyQuery Client
1
+ # BunnyQuery
2
2
 
3
- An embeddable, zero-dependency AI chat widget that connects to a [BunnyQuery](https://www.bunnyquery.com) project. Drop two `<script>` tags into any HTML page and your users get a fully featured chat UI backed by Claude or OpenAI — with login, password recovery, account settings, file attachments, persistent history, and MCP-based authentication.
3
+ An embeddable, dependency-free AI chat widget for [Skapi](https://www.skapi.com)-powered
4
+ projects. Drop it into any web page and your users get a full chat experience —
5
+ account login/signup, conversation history, file & folder uploads, and a settings
6
+ panel — all talking to your project's **BunnyQuery** AI agent.
4
7
 
5
- ---
8
+ BunnyQuery is a standalone vanilla-JS port of the BunnyQuery (www.bunnyquery.com) agent
9
+ chatbox. It ships as a single IIFE that exposes `window.BunnyQuery`, plus one
10
+ stylesheet. No build step, no framework, no npm install.
6
11
 
7
12
  ## Features
8
13
 
9
- - **Plug-and-play embed** one `<script>` tag, one `BunnyQuery.init()` call.
10
- - **Claude & OpenAI support** the AI platform is selected in your project settings; the embed reads it automatically.
11
- - **Built-in account UI**
12
- - Login form (email/username + password).
13
- - **Forgot password** flow request a code, set a new password without leaving the embed.
14
- - **Account settings** dialog — change email (with verification), change password, see verification status.
15
- - **MCP OAuth** — authentication uses the [Model Context Protocol](https://modelcontextprotocol.io/) OAuth flow (RFC 7591 + PKCE). API keys never reach the browser. After a host login the embed silently exchanges the Skapi session for an MCP access token.
16
- - **Persistent history** previous messages are loaded from the server on startup and paginated as the user scrolls up.
17
- - **File attachments** — files are uploaded to the Skapi host-file store and linked into the message as temporary CDN URLs.
18
- - **Clear / logout** modal-confirmed actions that respect in-flight requests.
19
- - **Responsive** — collapses to full-screen on narrow viewports.
20
- - **Read-only mode** — the attach button is hidden automatically when `freeze_database` is set in the project settings.
14
+ - **AI chat** against your project's configured agent (Claude or OpenAI under the
15
+ hood), with streaming-style "Thinking…" indicators and a background indexing queue.
16
+ - **Authentication** email/password login, optional signup, password change,
17
+ email verification, account recovery, and "Sign in with Google".
18
+ - **Conversation history** — paginated, with "Fetching history…" indicators on
19
+ first load and on scroll-up.
20
+ - **Attachments** — drag-and-drop files and folders, per-file upload status
21
+ (uploading / failed / indexed), and overflow collapsing for large batches.
22
+ - **Settings panel** — in-place inside the chat: light/dark theme, account details,
23
+ newsletter subscription, clear history, and remove account.
24
+ - **Theming** — light and dark modes via CSS custom properties; the choice is
25
+ remembered in `localStorage` and falls back to the OS preference.
21
26
 
22
- ---
23
-
24
- ## Quick Start
27
+ ## Requirements
25
28
 
26
- ### 1. Get your project ID
29
+ - A BunnyQuery project (you need its **project ID**).
30
+ - The [`skapi-js`](https://www.npmjs.com/package/skapi-js) SDK loaded on the page.
31
+ - A mount element with an explicit height (the widget fills its container).
27
32
 
28
- Sign in at [www.bunnyquery.com](https://www.bunnyquery.com), open your project, and copy the **Project ID** from the project settings page.
33
+ ## Quick start
29
34
 
30
- ### 2. Embed it
35
+ Add the two BunnyQuery files and the Skapi SDK, give it a sized container, then
36
+ call `BunnyQuery.init()`:
31
37
 
32
38
  ```html
33
39
  <!DOCTYPE html>
34
40
  <html lang="en">
35
41
  <head>
36
- <meta charset="UTF-8" />
37
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
38
- <!-- 1. Skapi SDK -->
39
- <script src="https://cdn.jsdelivr.net/npm/skapi-js@latest/dist/skapi.js"></script>
40
- <!-- 2. BunnyQuery widget (CSS is injected automatically) -->
41
- <script src="https://cdn.jsdelivr.net/npm/bunnyquery@latest/bunnyquery.js"></script>
42
+ <meta charset="UTF-8" />
43
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
44
+
45
+ <!-- Skapi SDK + BunnyQuery -->
46
+ <script src="https://cdn.jsdelivr.net/npm/skapi-js@latest/dist/skapi.js"></script>
47
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bunnyquery@latest/bunnyquery.css" />
48
+ <script src="https://cdn.jsdelivr.net/npm/bunnyquery@latest/bunnyquery.js"></script>
42
49
  </head>
43
- <body>
44
- <!-- 3. Mount point -->
45
- <div id="bq-client"></div>
46
-
47
- <script type="module">
48
- const skapi = new Skapi('YOUR_PROJECT_ID', { autoLogin: true });
49
- await BunnyQuery.init(skapi, 'bq-client');
50
- </script>
50
+ <body style="margin: 0">
51
+ <!-- The widget fills this element, so give it a height -->
52
+ <div id="chatbox" style="width: 100%; height: 100dvh"></div>
53
+
54
+ <script>
55
+ // 1. Create your Skapi instance
56
+ const skapi = new Skapi("<your-project-id>", { autoLogin: true });
57
+
58
+ // 2. Mount BunnyQuery into the container
59
+ BunnyQuery.init(skapi, "chatbox", {
60
+ theme: "light",
61
+ signup: true,
62
+ });
63
+ </script>
51
64
  </body>
52
65
  </html>
53
66
  ```
54
67
 
55
- That's it. The widget renders the chat if the user is signed in, otherwise it shows the login form.
56
-
57
- ---
58
-
59
- ## API Reference
60
-
61
- ### `BunnyQuery.init(skapi, elementId, dev?)` → `Promise<BunnyQuery>`
62
-
63
- Initialises the widget and returns the instance.
64
-
65
- | Parameter | Type | Description |
66
- | ----------- | -------------------------- | -------------------------------------------------------------------------------------------------------- |
67
- | `skapi` | `Skapi` | An initialised `Skapi` instance. |
68
- | `elementId` | `string \| HTMLElement` | The id string or DOM element to mount the widget into. |
69
- | `dev` | `boolean` *(optional)* | When `true`, routes every MCP call to the dev MCP host (`MCP_DEV_BASE_URL`). Omit or pass `false` in production. |
70
-
71
- ### Static configuration
72
-
73
- Set these **before** calling `init()`:
74
-
75
- | Property | Default | Description |
76
- | -------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
77
- | `BunnyQuery.MCP_BASE_URL` | `https://mcp.broadwayinc.computer` | Production MCP OAuth server base URL. Override to target a different MCP host. Automatically swapped to the dev host when `init(..., true)` is used. |
78
- | `BunnyQuery.STYLESHEET_URL`| *(auto-resolved)* | Override the CSS file URL when auto-resolution fails (e.g. CDN-hosted scripts behind a custom domain). |
79
-
80
- The widget auto-injects `bunnyquery.css` from the same directory as `bunnyquery.js`. If you bundle the script yourself or serve it from a path that breaks auto-resolution:
81
-
82
- ```js
83
- BunnyQuery.STYLESHEET_URL = 'https://your-cdn.example.com/bunnyquery.css';
84
- await BunnyQuery.init(skapi, 'bq-client');
85
- ```
86
-
87
- ---
88
-
89
- ## Built-in Account UI
90
-
91
- ### Login
68
+ That's it BunnyQuery takes over the `#chatbox` element and renders the login or
69
+ chat view depending on the user's session.
92
70
 
93
- Users who are not signed in see a username/password form. After a successful login the embed silently mints an MCP access token via `oauth.exchangeSession(skapi.session)` and switches to the chat view.
71
+ ## Files
94
72
 
95
- ### Forgot password
73
+ | File | Purpose |
74
+ | --------------- | ------------------------------------------------------------- |
75
+ | `bunnyquery.js` | The widget. Exposes the global `window.BunnyQuery`. |
76
+ | `bunnyquery.css` | All styles, scoped under `.bq-agent` / `[data-bq-theme]`. |
96
77
 
97
- A **Forgot password?** link sits under the login submit button and opens a two-step recovery flow inside the same overlay:
78
+ Host them yourself (same origin recommended) or from a CDN.
98
79
 
99
- 1. The user enters their email and presses **Send code** — calls `skapi.forgotPassword({ email })`.
100
- 2. The user enters the verification code from their inbox plus a new password (with confirmation), and presses **Reset password** — calls `skapi.resetPassword({ email, code, new_password })`.
80
+ ## API
101
81
 
102
- On success the embed returns to the login form with the email pre-filled and a confirmation notice.
82
+ ### `BunnyQuery.init(skapi, target, opts?)`
103
83
 
104
- ### Account settings
84
+ Mounts the widget. Returns the `BunnyQuery` object.
105
85
 
106
- Once signed in, the chat header shows a **Settings** button between **Clear** and **Logout**. It opens a modal with two sections:
86
+ | Argument | Type | Description |
87
+ | -------- | --------------------- | ----------------------------------------------------------------- |
88
+ | `skapi` | `Skapi` | A constructed Skapi instance. **Required.** |
89
+ | `target` | `string \| Element` | The mount element, or the `id` of one. **Required.** |
90
+ | `opts` | `object` | Options (see below). Optional. |
107
91
 
108
- - **Email** — shows the current address with a *verified / unverified* badge.
109
- - For unverified accounts (or right after a change) the user can press **Send code** (`skapi.verifyEmail()`), enter the received code, and press **Verify** (`skapi.verifyEmail({ code })`).
110
- - Below that, **Change email** updates the address via `skapi.updateProfile({ email })`, marks it unverified, and re-opens the verify flow.
111
- - **Password** — current / new / confirm fields with a client-side match check, calls `skapi.changePassword({ current_password, new_password })`.
112
- - Hidden automatically for OpenID / social-login accounts (Cognito password change is not available).
92
+ ### Options
113
93
 
114
- ---
94
+ | Option | Type | Default | Description |
95
+ | ------------------------ | --------- | -------- | -------------------------------------------------------------------------------------------- |
96
+ | `theme` | `string` | `"light"`| Initial theme, `"light"` or `"dark"`. Overridden by a remembered choice or OS preference. |
97
+ | `signup` | `boolean` | `false` | Enable signup flows (and account remove/recover). When `false`, only existing users can log in. |
98
+ | `googleClientId` | `string` | `null` | Google OAuth client ID. Set this to show "Sign in with Google". |
99
+ | `googleClientSecretName` | `string` | `"ggl"` | The Skapi client-secret name holding your Google OAuth secret. |
100
+ | `signupConfirmationUrl` | `string` | `null` | Link target used in the signup confirmation email. Defaults to the current page URL. |
101
+ | `dev` | `boolean` | `false` | Use the development MCP host and `skapi.app` db-CDN host instead of production. |
102
+ | `mcpBaseUrl` | `string` | `null` | Override the MCP OAuth server base URL entirely (advanced). |
103
+ | `hostDomain` | `string` | `null` | db-CDN host for temporary file URLs. Defaults to `skapi.app` (dev) / `skapi.com` (prod). |
115
104
 
116
- ## Customisation
105
+ ### Methods
117
106
 
118
- ### Sizing
107
+ The `BunnyQuery` global also exposes:
119
108
 
120
- The widget fills the element you mount it into. Control size and chrome with normal CSS:
109
+ | Method | Description |
110
+ | -------------------- | ----------------------------------------------------------------- |
111
+ | `setTheme(theme)` | Apply `"light"` or `"dark"` and persist it. |
112
+ | `toggleTheme()` | Switch between light and dark. |
113
+ | `logout()` | Sign the current user out and return to the login view. |
114
+ | `version` | The widget version string. |
121
115
 
122
- ```css
123
- #bq-client {
124
- width: 800px;
125
- height: 600px;
126
- border: 1px solid #ccc;
127
- }
116
+ ```js
117
+ BunnyQuery.setTheme("dark");
118
+ BunnyQuery.toggleTheme();
119
+ BunnyQuery.logout();
128
120
  ```
129
121
 
130
- ### CSS custom properties
122
+ > `init()` is idempotent — calling it twice logs a warning and returns the existing
123
+ > instance rather than re-mounting.
131
124
 
132
- All colours are CSS custom properties scoped to `.bq-agent`:
125
+ ## Theming
133
126
 
134
- | Variable | Default | Usage |
135
- | ----------------- | ---------- | -------------------- |
136
- | `--bq-ink` | `#111` | Primary text |
137
- | `--bq-paper` | `#fff` | Background |
138
- | `--bq-muted` | `#666` | Secondary text |
139
- | `--bq-line` | `#c8c8c8` | Borders |
140
- | `--bq-hover-bg` | `#ebebeb` | Hover states |
141
- | `--bq-pink` | `#c2185b` | Links |
142
- | `--bq-danger` | `#c44` | Destructive actions |
143
-
144
- Override them on a parent element:
127
+ BunnyQuery is themed with CSS custom properties (`--bq-*`) under a
128
+ `[data-bq-theme="light"|"dark"]` attribute that the widget sets on its own root.
129
+ To customize colors, override the variables in your own stylesheet **after**
130
+ `bunnyquery.css`, scoped to `.bq-agent`:
145
131
 
146
132
  ```css
147
- #bq-client {
148
- --bq-ink: #1a1a2e;
149
- --bq-paper: #f5f5f5;
150
- --bq-pink: #7c3aed;
133
+ .bq-agent {
134
+ --bq-main: #ff4fa3;
135
+ --bq-ink: #111;
151
136
  }
152
137
  ```
153
138
 
154
- ---
155
-
156
- ## How It Works
157
-
158
- 1. **Skapi** handles user authentication and acts as the serverless backend (database, file storage, request queueing).
159
- 2. **MCP OAuth** — after login the embed exchanges the Skapi session for an MCP access token via `/oauth/session-exchange` (no browser redirect needed). The token is cached in `localStorage` and refreshed automatically on stale-token errors.
160
- 3. **AI requests** are made through `skapi.clientSecretRequest`, which keeps your Claude / OpenAI API key on the Skapi edge — it is never sent to the browser.
161
- 4. **History** is fetched with `skapi.clientSecretRequestHistory` and merged with the live message list. Pending (in-flight) requests are polled every 4 seconds until they resolve.
162
-
163
- ---
164
-
165
- ## Project Structure
166
-
167
- ```
168
- bunnyquery-client/
169
- ├── index.html # Example host page
170
- ├── bunnyquery.js # Widget source (vanilla JS, no build step)
171
- ├── bunnyquery.css # Widget styles (auto-injected)
172
- └── package.json # Dev server only (basic-node-server)
173
- ```
174
-
175
- Run locally:
176
-
177
- ```sh
178
- npm run dev # serves the folder on http://localhost:3333
179
- ```
180
-
181
- ---
182
-
183
- ## Requirements
139
+ The active theme is saved to `localStorage`, so a returning user keeps their choice.
184
140
 
185
- - A [Skapi](https://www.skapi.com) project with an AI agent configured (Claude or OpenAI).
186
- - A modern browser with `crypto.subtle`, `fetch`, and ES2020 support.
187
- - Node.js 16+ (dev server only).
141
+ ## OAuth & redirects
188
142
 
189
- ---
143
+ BunnyQuery connects to your AI agent through an MCP OAuth server
144
+ (`mcp.broadwayinc.computer` in production, `mcp-dev.broadwayinc.computer` when
145
+ `dev: true`). After authorization, the OAuth server redirects back to **the current
146
+ host page** — BunnyQuery reads the `?code=…&state=…` parameters, completes the
147
+ exchange, and cleans them from the URL automatically. No dedicated callback page is
148
+ needed; just make sure the page that hosts the widget is a stable, reachable URL.
190
149
 
191
- ## License
150
+ ## Notes
192
151
 
193
- ISC
152
+ - The widget fills its mount element. Give that element a real height (e.g.
153
+ `height: 100dvh`) or it will collapse.
154
+ - File and folder uploads are stored in your Skapi project's database storage and
155
+ served from a temporary db-CDN URL (`hostDomain`); links in chat refresh on expiry.
156
+ - The agent shown in the header (`BunnyQuery · <project name>`) reflects the project
157
+ configured for your Skapi service.