@docgenlab.com/chat-widget 0.1.3
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.
- package/README.md +108 -0
- package/dist/api.d.ts +32 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +458 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.cjs +9 -0
- package/dist/index.umd.cjs.map +1 -0
- package/dist/types.d.ts +99 -0
- package/dist/widget.css +1 -0
- package/dist/widget.d.ts +14 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# @docgenlab.com/chat-widget
|
|
2
|
+
|
|
3
|
+
Embeddable AI knowledge-base chat widget for [DocGenLab](https://docgenlab.com). Drop into any site to give your end-users a grounded, citation-backed assistant.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Floating launcher + chat panel (bottom-right or bottom-left)
|
|
8
|
+
- Streaming responses over Server-Sent Events
|
|
9
|
+
- Citation chips for every answer (sources linked to your KB documents)
|
|
10
|
+
- Optional image attachment (PNG/JPEG/WebP/GIF, max 10 MB)
|
|
11
|
+
- Mobile bottom-sheet layout under 480px
|
|
12
|
+
- Theme override (primary color, font, radius)
|
|
13
|
+
- Conversation persistence across page reloads (per-browser via localStorage)
|
|
14
|
+
- No external CSS framework — scoped class names, no global pollution
|
|
15
|
+
|
|
16
|
+
## Security model
|
|
17
|
+
|
|
18
|
+
The `agentKey` you embed is a **publishable key** (`pk_live_...`). It will appear in the network tab. Like Stripe's publishable key, this is by design — security comes from:
|
|
19
|
+
|
|
20
|
+
1. **Domain allowlist** — register the customer's site domains against the key in your DocGenLab dashboard. Requests from other Origins are rejected.
|
|
21
|
+
2. **Rate limit** — per-key and per-IP, enforced server-side.
|
|
22
|
+
3. **Org-level monthly quota** — hard cap on total queries.
|
|
23
|
+
|
|
24
|
+
Never put a **secret key** (`sk_live_...`) in client code.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @docgenlab.com/chat-widget
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage (React / Next.js / Vue / Svelte etc.)
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { DocGenLabChat } from '@docgenlab.com/chat-widget';
|
|
36
|
+
import '@docgenlab.com/chat-widget/style.css';
|
|
37
|
+
|
|
38
|
+
export function MyApp() {
|
|
39
|
+
return (
|
|
40
|
+
<DocGenLabChat
|
|
41
|
+
agentKey="pk_live_..."
|
|
42
|
+
position="bottom-right"
|
|
43
|
+
theme={{ primary: '#7C3AED' }}
|
|
44
|
+
greeting="Hi! Ask me anything about our product."
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Usage (vanilla HTML — WordPress, marketing pages, anything without a build)
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<link rel="stylesheet" href="https://unpkg.com/@docgenlab.com/chat-widget/dist/widget.css" />
|
|
54
|
+
<script src="https://unpkg.com/@docgenlab.com/chat-widget"></script>
|
|
55
|
+
<script>
|
|
56
|
+
DocGenLab.init({
|
|
57
|
+
agentKey: 'pk_live_...',
|
|
58
|
+
position: 'bottom-right',
|
|
59
|
+
theme: { primary: '#7C3AED' },
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
To remove the widget later (e.g. on a sign-out page):
|
|
65
|
+
```js
|
|
66
|
+
DocGenLab.destroy();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Default | Description |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| `agentKey` | string | **required** | The `pk_live_...` key from your dashboard |
|
|
74
|
+
| `apiBaseUrl` | string | `https://api.docgenlab.com` | Override for self-hosted / staging |
|
|
75
|
+
| `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Launcher corner |
|
|
76
|
+
| `theme.primary` | string | `#4F46E5` | Accent color (header, send button, user msg) |
|
|
77
|
+
| `theme.primaryText` | string | `#FFFFFF` | Text on top of the primary color |
|
|
78
|
+
| `theme.radius` | string | `14px` | Border radius for panel/bubble |
|
|
79
|
+
| `theme.fontFamily` | string | system stack | Font family |
|
|
80
|
+
| `greeting` | string | `"Hi — how can I help?"` | Empty-state message |
|
|
81
|
+
| `agentName` | string | from `/agent` API | Override the displayed name |
|
|
82
|
+
| `agentAvatarUrl` | string | none | Optional avatar shown next to assistant msgs |
|
|
83
|
+
| `enableImageUpload` | boolean | `true` | Allow end-users to attach screenshots |
|
|
84
|
+
| `hideBranding` | boolean | `false` | Hide "Powered by DocGenLab" — paid tier |
|
|
85
|
+
| `openOnLoad` | boolean | `false` | Auto-open the panel on first load |
|
|
86
|
+
| `onReady` | `(agent) => void` | — | Fired when the agent resolves |
|
|
87
|
+
| `onError` | `(err) => void` | — | Fired on unrecoverable errors |
|
|
88
|
+
|
|
89
|
+
## Browser support
|
|
90
|
+
|
|
91
|
+
ES2018+ targets. Edge, Chrome, Firefox, Safari (last 2 versions). IE11 not supported.
|
|
92
|
+
|
|
93
|
+
## Bundle size
|
|
94
|
+
|
|
95
|
+
~50 KB gzipped (ESM, with React externalised). UMD bundle (React included) is ~95 KB gzipped.
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install
|
|
101
|
+
npm run dev # Vite dev server
|
|
102
|
+
npm run build # produces dist/index.js (ESM), dist/index.umd.cjs (UMD), dist/widget.css
|
|
103
|
+
npm run typecheck
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
UNLICENSED — internal DocGenLab project. Distribution to customers under separate commercial terms.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client for the /api/v1/public/kb/* surface. No axios — uses native
|
|
3
|
+
* fetch + a manual SSE reader so the bundle stays small.
|
|
4
|
+
*
|
|
5
|
+
* All requests carry two headers:
|
|
6
|
+
* X-DocGenLab-Key — the publishable key (server resolves to agent)
|
|
7
|
+
* X-DocGenLab-Visitor-Id — a stable UUID minted in localStorage
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentInfo, StreamEvent } from './types';
|
|
10
|
+
export interface ClientOptions {
|
|
11
|
+
agentKey: string;
|
|
12
|
+
apiBaseUrl: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class WidgetClient {
|
|
15
|
+
private readonly agentKey;
|
|
16
|
+
private readonly baseUrl;
|
|
17
|
+
private readonly visitorId;
|
|
18
|
+
constructor(opts: ClientOptions);
|
|
19
|
+
private headers;
|
|
20
|
+
getAgent(): Promise<AgentInfo>;
|
|
21
|
+
uploadImage(file: File): Promise<string>;
|
|
22
|
+
/** Fetch a previously-uploaded image as a blob URL. Caller must
|
|
23
|
+
* URL.revokeObjectURL() when done to avoid memory leaks. */
|
|
24
|
+
fetchImage(imagePath: string): Promise<string>;
|
|
25
|
+
/** Stream a chat response via SSE. Returns an abort fn the caller invokes
|
|
26
|
+
* to cancel the stream. */
|
|
27
|
+
streamChat(body: {
|
|
28
|
+
message: string;
|
|
29
|
+
conversation_id?: string;
|
|
30
|
+
image_path?: string;
|
|
31
|
+
}, onEvent: (event: StreamEvent) => void): () => void;
|
|
32
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @docgenlab.com/chat-widget — public entry.
|
|
3
|
+
*
|
|
4
|
+
* Two integration paths:
|
|
5
|
+
*
|
|
6
|
+
* 1. React (recommended for SPAs):
|
|
7
|
+
*
|
|
8
|
+
* import { DocGenLabChat } from '@docgenlab.com/chat-widget';
|
|
9
|
+
* import '@docgenlab.com/chat-widget/style.css';
|
|
10
|
+
*
|
|
11
|
+
* <DocGenLabChat agentKey="pk_live_..." />
|
|
12
|
+
*
|
|
13
|
+
* 2. Vanilla / script tag (WordPress, marketing pages, anything without
|
|
14
|
+
* a build step). Use unpkg or jsdelivr to load the UMD bundle:
|
|
15
|
+
*
|
|
16
|
+
* <link rel="stylesheet" href="https://unpkg.com/@docgenlab.com/chat-widget/dist/widget.css" />
|
|
17
|
+
* <script src="https://unpkg.com/@docgenlab.com/chat-widget"></script>
|
|
18
|
+
* <script>DocGenLab.init({ agentKey: 'pk_live_...' })</script>
|
|
19
|
+
*
|
|
20
|
+
* The UMD bundle exposes a global `DocGenLab` object with `init()` and
|
|
21
|
+
* `destroy()` methods that mount/unmount the widget on a hidden div.
|
|
22
|
+
*/
|
|
23
|
+
import { DocGenLabChat, type DocGenLabChatProps } from './widget';
|
|
24
|
+
import type { ChatWidgetConfig, ChatWidgetTheme, ChatPosition, Citation, AgentInfo, StreamEvent, ChatMessage } from './types';
|
|
25
|
+
export { DocGenLabChat };
|
|
26
|
+
export type { DocGenLabChatProps, ChatWidgetConfig, ChatWidgetTheme, ChatPosition, Citation, AgentInfo, StreamEvent, ChatMessage, };
|
|
27
|
+
export declare function init(config: DocGenLabChatProps): void;
|
|
28
|
+
export declare function destroy(): void;
|
|
29
|
+
declare global {
|
|
30
|
+
interface Window {
|
|
31
|
+
DocGenLab?: {
|
|
32
|
+
init: typeof init;
|
|
33
|
+
destroy: typeof destroy;
|
|
34
|
+
DocGenLabChat: typeof DocGenLabChat;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import { createRoot as he } from "react-dom/client";
|
|
2
|
+
import * as e from "react";
|
|
3
|
+
import { useRef as $, useState as E, useEffect as F, useCallback as ae, createElement as re } from "react";
|
|
4
|
+
const j = "/api/v1/public/kb", le = "dgl-visitor-id";
|
|
5
|
+
function ie() {
|
|
6
|
+
const t = globalThis.crypto;
|
|
7
|
+
return t && typeof t.randomUUID == "function" ? t.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (a) => {
|
|
8
|
+
const r = Math.random() * 16 | 0;
|
|
9
|
+
return (a === "x" ? r : r & 3 | 8).toString(16);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function fe() {
|
|
13
|
+
try {
|
|
14
|
+
let t = localStorage.getItem(le);
|
|
15
|
+
return t || (t = ie(), localStorage.setItem(le, t)), t;
|
|
16
|
+
} catch (t) {
|
|
17
|
+
return ie();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
class ye {
|
|
21
|
+
constructor(a) {
|
|
22
|
+
this.agentKey = a.agentKey, this.baseUrl = a.apiBaseUrl.replace(/\/+$/, ""), this.visitorId = fe();
|
|
23
|
+
}
|
|
24
|
+
headers(a) {
|
|
25
|
+
return {
|
|
26
|
+
"X-DocGenLab-Key": this.agentKey,
|
|
27
|
+
"X-DocGenLab-Visitor-Id": this.visitorId,
|
|
28
|
+
...a || {}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async getAgent() {
|
|
32
|
+
const a = await fetch(`${this.baseUrl}${j}/agent`, {
|
|
33
|
+
method: "GET",
|
|
34
|
+
headers: this.headers()
|
|
35
|
+
});
|
|
36
|
+
if (!a.ok) throw new Error(`getAgent failed: ${a.status}`);
|
|
37
|
+
return a.json();
|
|
38
|
+
}
|
|
39
|
+
async uploadImage(a) {
|
|
40
|
+
const r = new FormData();
|
|
41
|
+
r.append("file", a);
|
|
42
|
+
const n = await fetch(`${this.baseUrl}${j}/chat-image`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: this.headers(),
|
|
45
|
+
// browser sets Content-Type for FormData
|
|
46
|
+
body: r
|
|
47
|
+
});
|
|
48
|
+
if (!n.ok) {
|
|
49
|
+
let o = `${n.status} ${n.statusText}`;
|
|
50
|
+
try {
|
|
51
|
+
const s = await n.json();
|
|
52
|
+
o = (s == null ? void 0 : s.detail) || o;
|
|
53
|
+
} catch (s) {
|
|
54
|
+
}
|
|
55
|
+
throw new Error(o);
|
|
56
|
+
}
|
|
57
|
+
return (await n.json()).image_path;
|
|
58
|
+
}
|
|
59
|
+
/** Fetch a previously-uploaded image as a blob URL. Caller must
|
|
60
|
+
* URL.revokeObjectURL() when done to avoid memory leaks. */
|
|
61
|
+
async fetchImage(a) {
|
|
62
|
+
const r = `${this.baseUrl}${j}/chat-image?path=${encodeURIComponent(a)}&key=${encodeURIComponent(this.agentKey)}`, n = await fetch(r, {
|
|
63
|
+
method: "GET",
|
|
64
|
+
headers: { "X-DocGenLab-Visitor-Id": this.visitorId }
|
|
65
|
+
});
|
|
66
|
+
if (!n.ok) throw new Error(`fetchImage failed: ${n.status}`);
|
|
67
|
+
const l = await n.blob();
|
|
68
|
+
return URL.createObjectURL(l);
|
|
69
|
+
}
|
|
70
|
+
/** Stream a chat response via SSE. Returns an abort fn the caller invokes
|
|
71
|
+
* to cancel the stream. */
|
|
72
|
+
streamChat(a, r) {
|
|
73
|
+
const n = new AbortController();
|
|
74
|
+
return (async () => {
|
|
75
|
+
try {
|
|
76
|
+
const l = await fetch(`${this.baseUrl}${j}/chat`, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
signal: n.signal,
|
|
79
|
+
headers: this.headers({ "Content-Type": "application/json", Accept: "text/event-stream" }),
|
|
80
|
+
body: JSON.stringify(a)
|
|
81
|
+
});
|
|
82
|
+
if (!l.ok || !l.body) {
|
|
83
|
+
let y = `${l.status} ${l.statusText}`;
|
|
84
|
+
try {
|
|
85
|
+
const u = await l.json();
|
|
86
|
+
y = (u == null ? void 0 : u.detail) || y;
|
|
87
|
+
} catch (u) {
|
|
88
|
+
}
|
|
89
|
+
r({ type: "error", error: y });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const o = l.body.getReader(), s = new TextDecoder();
|
|
93
|
+
let m = "";
|
|
94
|
+
for (; ; ) {
|
|
95
|
+
const { value: y, done: u } = await o.read();
|
|
96
|
+
if (u) break;
|
|
97
|
+
m += s.decode(y, { stream: !0 });
|
|
98
|
+
let k;
|
|
99
|
+
for (; (k = m.indexOf(`
|
|
100
|
+
|
|
101
|
+
`)) !== -1; ) {
|
|
102
|
+
const h = m.slice(0, k);
|
|
103
|
+
m = m.slice(k + 2);
|
|
104
|
+
const R = h.trim();
|
|
105
|
+
if (R.startsWith("data:"))
|
|
106
|
+
try {
|
|
107
|
+
const U = JSON.parse(R.slice(5).trim());
|
|
108
|
+
r(U);
|
|
109
|
+
} catch (U) {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (l) {
|
|
114
|
+
(l == null ? void 0 : l.name) !== "AbortError" && r({ type: "error", error: (l == null ? void 0 : l.message) || String(l) });
|
|
115
|
+
}
|
|
116
|
+
})(), () => n.abort();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const N = {
|
|
120
|
+
apiBaseUrl: "https://api.docgenlab.com",
|
|
121
|
+
position: "bottom-right",
|
|
122
|
+
primary: "#4F46E5",
|
|
123
|
+
primaryText: "#FFFFFF",
|
|
124
|
+
radius: "14px",
|
|
125
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
126
|
+
greeting: "Hi — how can I help?"
|
|
127
|
+
};
|
|
128
|
+
function V(t) {
|
|
129
|
+
var Y, Q, Z, ee;
|
|
130
|
+
const a = t.apiBaseUrl || N.apiBaseUrl, r = t.position || N.position, n = t.enableImageUpload !== !1, l = {
|
|
131
|
+
"--dgl-primary": ((Y = t.theme) == null ? void 0 : Y.primary) || N.primary,
|
|
132
|
+
"--dgl-primary-text": ((Q = t.theme) == null ? void 0 : Q.primaryText) || N.primaryText,
|
|
133
|
+
"--dgl-radius": ((Z = t.theme) == null ? void 0 : Z.radius) || N.radius,
|
|
134
|
+
"--dgl-font": ((ee = t.theme) == null ? void 0 : ee.fontFamily) || N.fontFamily
|
|
135
|
+
}, o = $(null);
|
|
136
|
+
o.current || (o.current = new ye({ agentKey: t.agentKey, apiBaseUrl: a }));
|
|
137
|
+
const s = o.current, [m, y] = E(!!t.openOnLoad), [u, k] = E(!1), [h, R] = E(null), [U, ce] = E(null), [S, x] = E([]), [A, O] = E(""), [I, T] = E(!1), [B, P] = E(null), [X, K] = E(null), [v, M] = E(null), [D, G] = E(!1), H = $(null), z = $(null), _ = $(""), W = $(null);
|
|
138
|
+
F(() => {
|
|
139
|
+
!m || h || s.getAgent().then((i) => {
|
|
140
|
+
var c;
|
|
141
|
+
R(i), (c = t.onReady) == null || c.call(t, i);
|
|
142
|
+
}).catch((i) => {
|
|
143
|
+
var c;
|
|
144
|
+
ce(i.message), (c = t.onError) == null || c.call(t, i.message);
|
|
145
|
+
});
|
|
146
|
+
}, [m, h, s, t]), F(() => {
|
|
147
|
+
const i = `dgl-chat:${t.agentKey}`, c = localStorage.getItem(i);
|
|
148
|
+
c && P(c);
|
|
149
|
+
}, [t.agentKey]), F(() => {
|
|
150
|
+
B && localStorage.setItem(`dgl-chat:${t.agentKey}`, B);
|
|
151
|
+
}, [B, t.agentKey]), F(() => {
|
|
152
|
+
W.current && (W.current.scrollTop = W.current.scrollHeight);
|
|
153
|
+
}, [S]);
|
|
154
|
+
const q = ae(() => {
|
|
155
|
+
v && URL.revokeObjectURL(v), K(null), M(null);
|
|
156
|
+
}, [v]), J = ae((i) => {
|
|
157
|
+
if (!i.type.startsWith("image/")) return;
|
|
158
|
+
const c = 10 * 1024 * 1024;
|
|
159
|
+
i.size > c || (v && URL.revokeObjectURL(v), K(i), M(URL.createObjectURL(i)));
|
|
160
|
+
}, [v]), de = async (i) => {
|
|
161
|
+
var te;
|
|
162
|
+
if (i.preventDefault(), !A.trim() || I || D) return;
|
|
163
|
+
const c = A.trim();
|
|
164
|
+
let p, w;
|
|
165
|
+
if (X) {
|
|
166
|
+
try {
|
|
167
|
+
G(!0), p = await s.uploadImage(X);
|
|
168
|
+
} catch (d) {
|
|
169
|
+
(te = t.onError) == null || te.call(t, (d == null ? void 0 : d.message) || "Image upload failed"), G(!1);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
G(!1), w = v || void 0;
|
|
173
|
+
}
|
|
174
|
+
O(""), _.current = "", x((d) => [
|
|
175
|
+
...d,
|
|
176
|
+
{ role: "user", content: c, imagePath: p, imagePreviewUrl: w },
|
|
177
|
+
{ role: "assistant", content: "", streaming: !0 }
|
|
178
|
+
]), T(!0), K(null), M(null), z.current = s.streamChat(
|
|
179
|
+
{ message: c, conversation_id: B || void 0, image_path: p },
|
|
180
|
+
(d) => {
|
|
181
|
+
var ne;
|
|
182
|
+
if (d.type === "conversation")
|
|
183
|
+
P(d.conversation_id);
|
|
184
|
+
else if (d.type === "token") {
|
|
185
|
+
_.current += d.content;
|
|
186
|
+
const f = _.current;
|
|
187
|
+
x((g) => g.map(
|
|
188
|
+
(b, ue) => ue === g.length - 1 && b.role === "assistant" ? { ...b, content: f } : b
|
|
189
|
+
));
|
|
190
|
+
} else d.type === "citations" ? x((f) => f.map(
|
|
191
|
+
(g, b) => b === f.length - 1 && g.role === "assistant" ? { ...g, citations: d.citations } : g
|
|
192
|
+
)) : d.type === "refused" ? (_.current = d.content, x((f) => f.map(
|
|
193
|
+
(g, b) => b === f.length - 1 && g.role === "assistant" ? { ...g, content: d.content, refused: !0 } : g
|
|
194
|
+
))) : d.type === "done" ? (x((f) => f.map(
|
|
195
|
+
(g, b) => b === f.length - 1 && g.role === "assistant" ? { ...g, streaming: !1, id: d.message_id, cacheHit: d.cache_hit } : g
|
|
196
|
+
)), T(!1)) : d.type === "error" && (x((f) => f.map(
|
|
197
|
+
(g, b) => b === f.length - 1 && g.role === "assistant" ? { ...g, content: `Error: ${d.error}`, streaming: !1, refused: !0 } : g
|
|
198
|
+
)), T(!1), (ne = t.onError) == null || ne.call(t, d.error));
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
}, me = () => {
|
|
202
|
+
z.current && z.current(), x([]), P(null), T(!1), localStorage.removeItem(`dgl-chat:${t.agentKey}`), q();
|
|
203
|
+
}, ge = t.agentName || (h == null ? void 0 : h.name) || "Assistant";
|
|
204
|
+
return /* @__PURE__ */ e.createElement("div", { className: `dgl-root dgl-pos-${r}`, style: l }, m && /* @__PURE__ */ e.createElement("div", { className: `dgl-panel ${u ? "dgl-panel-expanded" : ""}` }, /* @__PURE__ */ e.createElement("div", { className: "dgl-header" }, /* @__PURE__ */ e.createElement("div", { className: "dgl-header-info" }, t.agentAvatarUrl && /* @__PURE__ */ e.createElement("img", { src: t.agentAvatarUrl, alt: "", className: "dgl-avatar" }), /* @__PURE__ */ e.createElement("div", { className: "dgl-header-text" }, /* @__PURE__ */ e.createElement("div", { className: "dgl-title" }, ge), (h == null ? void 0 : h.org_name) && /* @__PURE__ */ e.createElement("div", { className: "dgl-subtitle" }, h.org_name))), /* @__PURE__ */ e.createElement("div", { className: "dgl-header-actions" }, /* @__PURE__ */ e.createElement(
|
|
205
|
+
"button",
|
|
206
|
+
{
|
|
207
|
+
type: "button",
|
|
208
|
+
onClick: me,
|
|
209
|
+
className: "dgl-icon-btn",
|
|
210
|
+
title: "New chat",
|
|
211
|
+
"aria-label": "New chat"
|
|
212
|
+
},
|
|
213
|
+
Ue()
|
|
214
|
+
), /* @__PURE__ */ e.createElement(
|
|
215
|
+
"button",
|
|
216
|
+
{
|
|
217
|
+
type: "button",
|
|
218
|
+
onClick: () => k((i) => !i),
|
|
219
|
+
className: "dgl-icon-btn dgl-hide-on-mobile",
|
|
220
|
+
title: u ? "Restore size" : "Expand view",
|
|
221
|
+
"aria-label": u ? "Restore size" : "Expand view"
|
|
222
|
+
},
|
|
223
|
+
u ? Se() : Re()
|
|
224
|
+
), /* @__PURE__ */ e.createElement(
|
|
225
|
+
"button",
|
|
226
|
+
{
|
|
227
|
+
type: "button",
|
|
228
|
+
onClick: () => y(!1),
|
|
229
|
+
className: "dgl-icon-btn",
|
|
230
|
+
title: "Close",
|
|
231
|
+
"aria-label": "Close"
|
|
232
|
+
},
|
|
233
|
+
oe()
|
|
234
|
+
))), /* @__PURE__ */ e.createElement("div", { className: "dgl-messages", ref: W }, S.length === 0 && !U && /* @__PURE__ */ e.createElement("div", { className: "dgl-empty" }, /* @__PURE__ */ e.createElement("div", { className: "dgl-empty-text" }, t.greeting || N.greeting), (h == null ? void 0 : h.description) && /* @__PURE__ */ e.createElement("div", { className: "dgl-empty-sub" }, h.description)), U && /* @__PURE__ */ e.createElement("div", { className: "dgl-error" }, "Couldn't load the assistant: ", U), S.map((i, c) => /* @__PURE__ */ e.createElement(
|
|
235
|
+
Ee,
|
|
236
|
+
{
|
|
237
|
+
key: c,
|
|
238
|
+
message: i,
|
|
239
|
+
client: s,
|
|
240
|
+
isLast: c === S.length - 1,
|
|
241
|
+
agentAvatarUrl: t.agentAvatarUrl,
|
|
242
|
+
onPickFollowup: (p) => {
|
|
243
|
+
O(p);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
))), /* @__PURE__ */ e.createElement("form", { className: "dgl-composer", onSubmit: de }, v && /* @__PURE__ */ e.createElement("div", { className: "dgl-staged-image" }, /* @__PURE__ */ e.createElement("img", { src: v, alt: "attachment" }), /* @__PURE__ */ e.createElement(
|
|
247
|
+
"button",
|
|
248
|
+
{
|
|
249
|
+
type: "button",
|
|
250
|
+
onClick: q,
|
|
251
|
+
className: "dgl-staged-remove",
|
|
252
|
+
"aria-label": "Remove image"
|
|
253
|
+
},
|
|
254
|
+
oe()
|
|
255
|
+
)), /* @__PURE__ */ e.createElement("div", { className: "dgl-composer-row" }, n && /* @__PURE__ */ e.createElement(e.Fragment, null, /* @__PURE__ */ e.createElement(
|
|
256
|
+
"button",
|
|
257
|
+
{
|
|
258
|
+
type: "button",
|
|
259
|
+
className: "dgl-icon-btn",
|
|
260
|
+
onClick: () => {
|
|
261
|
+
var i;
|
|
262
|
+
return (i = H.current) == null ? void 0 : i.click();
|
|
263
|
+
},
|
|
264
|
+
disabled: I || D,
|
|
265
|
+
title: "Attach image",
|
|
266
|
+
"aria-label": "Attach image"
|
|
267
|
+
},
|
|
268
|
+
$e()
|
|
269
|
+
), /* @__PURE__ */ e.createElement(
|
|
270
|
+
"input",
|
|
271
|
+
{
|
|
272
|
+
ref: H,
|
|
273
|
+
type: "file",
|
|
274
|
+
accept: "image/png,image/jpeg,image/webp,image/gif",
|
|
275
|
+
style: { display: "none" },
|
|
276
|
+
onChange: (i) => {
|
|
277
|
+
var p;
|
|
278
|
+
const c = (p = i.target.files) == null ? void 0 : p[0];
|
|
279
|
+
c && J(c), i.target && (i.target.value = "");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
)), /* @__PURE__ */ e.createElement(
|
|
283
|
+
"input",
|
|
284
|
+
{
|
|
285
|
+
className: "dgl-input",
|
|
286
|
+
value: A,
|
|
287
|
+
onChange: (i) => O(i.target.value),
|
|
288
|
+
placeholder: I ? "Thinking…" : v ? "Describe what to know about this image…" : "Type your question…",
|
|
289
|
+
disabled: I,
|
|
290
|
+
onPaste: (i) => {
|
|
291
|
+
var p;
|
|
292
|
+
if (!n) return;
|
|
293
|
+
const c = Array.from(((p = i.clipboardData) == null ? void 0 : p.items) || []).find(
|
|
294
|
+
(w) => w.kind === "file" && w.type.startsWith("image/")
|
|
295
|
+
);
|
|
296
|
+
if (c) {
|
|
297
|
+
const w = c.getAsFile();
|
|
298
|
+
w && (i.preventDefault(), J(w));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
), /* @__PURE__ */ e.createElement(
|
|
303
|
+
"button",
|
|
304
|
+
{
|
|
305
|
+
type: "submit",
|
|
306
|
+
className: "dgl-send-btn",
|
|
307
|
+
disabled: !A.trim() || I || D,
|
|
308
|
+
"aria-label": "Send"
|
|
309
|
+
},
|
|
310
|
+
I || D ? Fe() : Ie()
|
|
311
|
+
)), !t.hideBranding && /* @__PURE__ */ e.createElement("div", { className: "dgl-branding" }, "Powered by ", /* @__PURE__ */ e.createElement("a", { href: "https://docgenlab.com", target: "_blank", rel: "noopener noreferrer" }, "DocGenLab")))), /* @__PURE__ */ e.createElement(
|
|
312
|
+
"button",
|
|
313
|
+
{
|
|
314
|
+
type: "button",
|
|
315
|
+
className: "dgl-launcher",
|
|
316
|
+
onClick: () => y((i) => !i),
|
|
317
|
+
"aria-label": m ? "Close chat" : "Open chat"
|
|
318
|
+
},
|
|
319
|
+
m ? Le() : Ce()
|
|
320
|
+
));
|
|
321
|
+
}
|
|
322
|
+
function Ee({
|
|
323
|
+
message: t,
|
|
324
|
+
client: a,
|
|
325
|
+
isLast: r,
|
|
326
|
+
agentAvatarUrl: n,
|
|
327
|
+
onPickFollowup: l
|
|
328
|
+
}) {
|
|
329
|
+
var m;
|
|
330
|
+
if (t.role === "user")
|
|
331
|
+
return /* @__PURE__ */ e.createElement("div", { className: "dgl-msg dgl-msg-user" }, /* @__PURE__ */ e.createElement("div", { className: "dgl-bubble dgl-bubble-user" }, (t.imagePath || t.imagePreviewUrl) && /* @__PURE__ */ e.createElement(be, { client: a, imagePath: t.imagePath, previewUrl: t.imagePreviewUrl }), t.content));
|
|
332
|
+
const o = ke(t.content), s = t.streaming ? [] : we(t.content);
|
|
333
|
+
return /* @__PURE__ */ e.createElement("div", { className: "dgl-msg dgl-msg-assistant" }, n ? /* @__PURE__ */ e.createElement("img", { src: n, className: "dgl-avatar", alt: "" }) : /* @__PURE__ */ e.createElement("div", { className: "dgl-avatar dgl-avatar-fallback" }, Ae()), /* @__PURE__ */ e.createElement("div", { className: "dgl-msg-body" }, t.refused ? /* @__PURE__ */ e.createElement("div", { className: "dgl-refused" }, o) : /* @__PURE__ */ e.createElement("div", { className: "dgl-assistant-text" }, t.streaming && !o ? /* @__PURE__ */ e.createElement(Ne, null) : /* @__PURE__ */ e.createElement(e.Fragment, null, xe(o), t.streaming && /* @__PURE__ */ e.createElement("span", { className: "dgl-typing-cursor" }, "▍"))), !t.streaming && !!((m = t.citations) != null && m.length) && /* @__PURE__ */ e.createElement(ve, { citations: t.citations }), s.length > 0 && /* @__PURE__ */ e.createElement(pe, { suggestions: s, onPick: l })));
|
|
334
|
+
}
|
|
335
|
+
function pe({
|
|
336
|
+
suggestions: t,
|
|
337
|
+
onPick: a
|
|
338
|
+
}) {
|
|
339
|
+
return /* @__PURE__ */ e.createElement("div", { className: "dgl-followups" }, /* @__PURE__ */ e.createElement("div", { className: "dgl-followups-label" }, "Continue exploring"), /* @__PURE__ */ e.createElement("div", { className: "dgl-followups-list" }, t.map((r, n) => /* @__PURE__ */ e.createElement(
|
|
340
|
+
"button",
|
|
341
|
+
{
|
|
342
|
+
key: n,
|
|
343
|
+
type: "button",
|
|
344
|
+
className: "dgl-followup-chip",
|
|
345
|
+
onClick: () => a(r)
|
|
346
|
+
},
|
|
347
|
+
r
|
|
348
|
+
))));
|
|
349
|
+
}
|
|
350
|
+
function be({
|
|
351
|
+
client: t,
|
|
352
|
+
imagePath: a,
|
|
353
|
+
previewUrl: r
|
|
354
|
+
}) {
|
|
355
|
+
const [n, l] = E(r || null);
|
|
356
|
+
return F(() => {
|
|
357
|
+
if (r || !a) return;
|
|
358
|
+
let o = null;
|
|
359
|
+
return t.fetchImage(a).then((s) => {
|
|
360
|
+
o = s, l(s);
|
|
361
|
+
}).catch(() => l(null)), () => {
|
|
362
|
+
o && URL.revokeObjectURL(o);
|
|
363
|
+
};
|
|
364
|
+
}, [t, a, r]), n ? /* @__PURE__ */ e.createElement("a", { href: n, target: "_blank", rel: "noopener noreferrer", className: "dgl-user-image-link" }, /* @__PURE__ */ e.createElement("img", { src: n, className: "dgl-user-image", alt: "attached" })) : null;
|
|
365
|
+
}
|
|
366
|
+
function ve({ citations: t }) {
|
|
367
|
+
const a = /* @__PURE__ */ new Set(), r = t.filter((n) => a.has(n.source_id) ? !1 : (a.add(n.source_id), !0));
|
|
368
|
+
return r.length ? /* @__PURE__ */ e.createElement("div", { className: "dgl-citations" }, r.map((n) => /* @__PURE__ */ e.createElement("span", { key: n.chunk_id, className: "dgl-citation", title: n.snippet }, n.source_name, n.page ? ` · p${n.page}` : ""))) : null;
|
|
369
|
+
}
|
|
370
|
+
function we(t) {
|
|
371
|
+
const a = t.match(/<followups>([\s\S]*?)<\/followups>/i);
|
|
372
|
+
return a ? a[1].split(`
|
|
373
|
+
`).map((r) => r.trim()).map((r) => r.replace(/^[-*•\d.)\s"']+|["']$/g, "").trim()).filter((r) => r.length >= 4 && r.length <= 120).slice(0, 3) : [];
|
|
374
|
+
}
|
|
375
|
+
function ke(t) {
|
|
376
|
+
return t.replace(/\n*<followups>[\s\S]*?(<\/followups>|$)/gi, "").replace(/\s*\[\s*Section:[^\]]*\]?/gi, "").replace(/\s*\[\s*doc:[^\]]*\]?/gi, "").replace(/\s*\[\s*(?:document|doc|source|ref|reference)[^\]]*\]?/gi, "").replace(/\s*\[\s*(?:p\.?\s*)?\d+(?:\s*[,–-]\s*\d+)*\s*\]/gi, "").replace(/[,;]?\s*doc:\s*[0-9a-fA-F][0-9a-fA-F-]{6,}\]?/gi, "").replace(/[,;]\s*(?:\d+|doc:?\s*[0-9a-fA-F-]+)\s*\]?/gi, "").replace(/([\w%])\s*\]/g, "$1").replace(/\s+([,.;:!?])/g, "$1").replace(/[ \t]+/g, " ").replace(/\n{3,}/g, `
|
|
377
|
+
|
|
378
|
+
`).trim();
|
|
379
|
+
}
|
|
380
|
+
function xe(t) {
|
|
381
|
+
return t.split(/\n{2,}/).map((r, n) => {
|
|
382
|
+
const l = r.split(`
|
|
383
|
+
`);
|
|
384
|
+
if (l.every((s) => /^(\s*)([-*]|\d+\.)\s/.test(s)) && l.length > 0) {
|
|
385
|
+
const s = /^\d+\./.test(l[0].trim()), m = l.map((u) => u.replace(/^\s*(?:[-*]|\d+\.)\s+/, "")), y = s ? "ol" : "ul";
|
|
386
|
+
return /* @__PURE__ */ e.createElement(y, { key: n, className: `dgl-md-list ${s ? "dgl-md-ol" : "dgl-md-ul"}` }, m.map((u, k) => /* @__PURE__ */ e.createElement("li", { key: k }, se(u))));
|
|
387
|
+
}
|
|
388
|
+
return /* @__PURE__ */ e.createElement("p", { key: n, className: "dgl-md-p" }, r.split(`
|
|
389
|
+
`).map((s, m, y) => /* @__PURE__ */ e.createElement(e.Fragment, { key: m }, se(s), m < y.length - 1 && /* @__PURE__ */ e.createElement("br", null))));
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
function se(t) {
|
|
393
|
+
const a = /(`[^`]+`|\*\*[^*\n]+\*\*|\*[^*\n]+\*|\[[^\]]+\]\([^)]+\))/g;
|
|
394
|
+
return t.split(a).map((n, l) => {
|
|
395
|
+
if (!n) return null;
|
|
396
|
+
if (n.startsWith("`") && n.endsWith("`"))
|
|
397
|
+
return /* @__PURE__ */ e.createElement("code", { key: l, className: "dgl-md-code" }, n.slice(1, -1));
|
|
398
|
+
if (n.startsWith("**") && n.endsWith("**"))
|
|
399
|
+
return /* @__PURE__ */ e.createElement("strong", { key: l }, n.slice(2, -2));
|
|
400
|
+
if (n.startsWith("*") && n.endsWith("*") && n.length > 2)
|
|
401
|
+
return /* @__PURE__ */ e.createElement("em", { key: l }, n.slice(1, -1));
|
|
402
|
+
const o = n.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
|
|
403
|
+
return o ? /* @__PURE__ */ e.createElement("a", { key: l, href: o[2], target: "_blank", rel: "noopener noreferrer", className: "dgl-md-link" }, o[1]) : /* @__PURE__ */ e.createElement(e.Fragment, { key: l }, n);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function Ne() {
|
|
407
|
+
return /* @__PURE__ */ e.createElement("span", { className: "dgl-thinking" }, /* @__PURE__ */ e.createElement("span", { className: "dgl-thinking-dot" }), /* @__PURE__ */ e.createElement("span", { className: "dgl-thinking-dot" }), /* @__PURE__ */ e.createElement("span", { className: "dgl-thinking-dot" }));
|
|
408
|
+
}
|
|
409
|
+
function Ce() {
|
|
410
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "currentColor" }, /* @__PURE__ */ e.createElement("path", { d: "M4 4h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H8l-4 4V6a2 2 0 0 1 2-2z" }));
|
|
411
|
+
}
|
|
412
|
+
function Le() {
|
|
413
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ e.createElement("polyline", { points: "6 9 12 15 18 9" }));
|
|
414
|
+
}
|
|
415
|
+
function oe() {
|
|
416
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ e.createElement("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), /* @__PURE__ */ e.createElement("line", { x1: "6", y1: "6", x2: "18", y2: "18" }));
|
|
417
|
+
}
|
|
418
|
+
function Ue() {
|
|
419
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ e.createElement("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), /* @__PURE__ */ e.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }));
|
|
420
|
+
}
|
|
421
|
+
function Ie() {
|
|
422
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "currentColor" }, /* @__PURE__ */ e.createElement("path", { d: "M2 21l21-9L2 3v7l15 2-15 2z" }));
|
|
423
|
+
}
|
|
424
|
+
function $e() {
|
|
425
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "18", height: "18", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ e.createElement("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), /* @__PURE__ */ e.createElement("circle", { cx: "8.5", cy: "8.5", r: "1.5" }), /* @__PURE__ */ e.createElement("polyline", { points: "21 15 16 10 5 21" }));
|
|
426
|
+
}
|
|
427
|
+
function Fe() {
|
|
428
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "16", height: "16", className: "dgl-spin", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" }, /* @__PURE__ */ e.createElement("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }));
|
|
429
|
+
}
|
|
430
|
+
function Re() {
|
|
431
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ e.createElement("polyline", { points: "15 3 21 3 21 9" }), /* @__PURE__ */ e.createElement("polyline", { points: "9 21 3 21 3 15" }), /* @__PURE__ */ e.createElement("line", { x1: "21", y1: "3", x2: "14", y2: "10" }), /* @__PURE__ */ e.createElement("line", { x1: "3", y1: "21", x2: "10", y2: "14" }));
|
|
432
|
+
}
|
|
433
|
+
function Se() {
|
|
434
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ e.createElement("polyline", { points: "4 14 10 14 10 20" }), /* @__PURE__ */ e.createElement("polyline", { points: "20 10 14 10 14 4" }), /* @__PURE__ */ e.createElement("line", { x1: "14", y1: "10", x2: "21", y2: "3" }), /* @__PURE__ */ e.createElement("line", { x1: "3", y1: "21", x2: "10", y2: "14" }));
|
|
435
|
+
}
|
|
436
|
+
function Ae() {
|
|
437
|
+
return /* @__PURE__ */ e.createElement("svg", { viewBox: "0 0 24 24", width: "14", height: "14", fill: "currentColor" }, /* @__PURE__ */ e.createElement("path", { d: "M12 2l2.39 6.95L21 11l-6.61 2.05L12 20l-2.39-6.95L3 11l6.61-2.05L12 2z" }));
|
|
438
|
+
}
|
|
439
|
+
let C = null, L = null;
|
|
440
|
+
function Te(t) {
|
|
441
|
+
if (typeof document == "undefined")
|
|
442
|
+
throw new Error("DocGenLab.init() requires a browser environment.");
|
|
443
|
+
if (C) {
|
|
444
|
+
C.render(re(V, t));
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
L = document.createElement("div"), L.id = "docgenlab-chat-widget-root", document.body.appendChild(L), C = he(L), C.render(re(V, t));
|
|
448
|
+
}
|
|
449
|
+
function Be() {
|
|
450
|
+
C && (C.unmount(), C = null), L && (L.remove(), L = null);
|
|
451
|
+
}
|
|
452
|
+
typeof window != "undefined" && (window.DocGenLab = { init: Te, destroy: Be, DocGenLabChat: V });
|
|
453
|
+
export {
|
|
454
|
+
V as DocGenLabChat,
|
|
455
|
+
Be as destroy,
|
|
456
|
+
Te as init
|
|
457
|
+
};
|
|
458
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/api.ts","../src/widget.tsx","../src/index.ts"],"sourcesContent":["/**\n * Thin client for the /api/v1/public/kb/* surface. No axios — uses native\n * fetch + a manual SSE reader so the bundle stays small.\n *\n * All requests carry two headers:\n * X-DocGenLab-Key — the publishable key (server resolves to agent)\n * X-DocGenLab-Visitor-Id — a stable UUID minted in localStorage\n */\n\nimport type { AgentInfo, StreamEvent } from './types';\n\nconst PUBLIC_BASE = '/api/v1/public/kb';\nconst VISITOR_KEY = 'dgl-visitor-id';\n\n/** Generate a UUID v4. Avoids the `crypto.randomUUID` dependency for older\n * browsers; falls back to a Math.random-based variant. */\nfunction uuid(): string {\n const c = (globalThis as any).crypto;\n if (c && typeof c.randomUUID === 'function') return c.randomUUID();\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {\n const r = (Math.random() * 16) | 0;\n const v = ch === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_KEY);\n if (!id) {\n id = uuid();\n localStorage.setItem(VISITOR_KEY, id);\n }\n return id;\n } catch {\n // Storage unavailable (private mode, blocked) — use a session-scoped id.\n return uuid();\n }\n}\n\nexport interface ClientOptions {\n agentKey: string;\n apiBaseUrl: string;\n}\n\nexport class WidgetClient {\n private readonly agentKey: string;\n private readonly baseUrl: string;\n private readonly visitorId: string;\n\n constructor(opts: ClientOptions) {\n this.agentKey = opts.agentKey;\n this.baseUrl = opts.apiBaseUrl.replace(/\\/+$/, '');\n this.visitorId = getVisitorId();\n }\n\n private headers(extra?: Record<string, string>): Record<string, string> {\n return {\n 'X-DocGenLab-Key': this.agentKey,\n 'X-DocGenLab-Visitor-Id': this.visitorId,\n ...(extra || {}),\n };\n }\n\n async getAgent(): Promise<AgentInfo> {\n const resp = await fetch(`${this.baseUrl}${PUBLIC_BASE}/agent`, {\n method: 'GET',\n headers: this.headers(),\n });\n if (!resp.ok) throw new Error(`getAgent failed: ${resp.status}`);\n return resp.json();\n }\n\n async uploadImage(file: File): Promise<string> {\n const fd = new FormData();\n fd.append('file', file);\n const resp = await fetch(`${this.baseUrl}${PUBLIC_BASE}/chat-image`, {\n method: 'POST',\n headers: this.headers(), // browser sets Content-Type for FormData\n body: fd,\n });\n if (!resp.ok) {\n let detail = `${resp.status} ${resp.statusText}`;\n try {\n const j = await resp.json();\n detail = j?.detail || detail;\n } catch { /* keep status */ }\n throw new Error(detail);\n }\n const j = await resp.json();\n return j.image_path as string;\n }\n\n /** Fetch a previously-uploaded image as a blob URL. Caller must\n * URL.revokeObjectURL() when done to avoid memory leaks. */\n async fetchImage(imagePath: string): Promise<string> {\n const url = `${this.baseUrl}${PUBLIC_BASE}/chat-image?path=${encodeURIComponent(imagePath)}&key=${encodeURIComponent(this.agentKey)}`;\n const resp = await fetch(url, {\n method: 'GET',\n headers: { 'X-DocGenLab-Visitor-Id': this.visitorId },\n });\n if (!resp.ok) throw new Error(`fetchImage failed: ${resp.status}`);\n const blob = await resp.blob();\n return URL.createObjectURL(blob);\n }\n\n /** Stream a chat response via SSE. Returns an abort fn the caller invokes\n * to cancel the stream. */\n streamChat(\n body: { message: string; conversation_id?: string; image_path?: string },\n onEvent: (event: StreamEvent) => void,\n ): () => void {\n const ctrl = new AbortController();\n (async () => {\n try {\n const resp = await fetch(`${this.baseUrl}${PUBLIC_BASE}/chat`, {\n method: 'POST',\n signal: ctrl.signal,\n headers: this.headers({ 'Content-Type': 'application/json', Accept: 'text/event-stream' }),\n body: JSON.stringify(body),\n });\n if (!resp.ok || !resp.body) {\n let detail = `${resp.status} ${resp.statusText}`;\n try { const j = await resp.json(); detail = j?.detail || detail; } catch {}\n onEvent({ type: 'error', error: detail });\n return;\n }\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n let buf = '';\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl;\n while ((nl = buf.indexOf('\\n\\n')) !== -1) {\n const raw = buf.slice(0, nl);\n buf = buf.slice(nl + 2);\n const line = raw.trim();\n if (!line.startsWith('data:')) continue;\n try {\n const evt = JSON.parse(line.slice(5).trim()) as StreamEvent;\n onEvent(evt);\n } catch {\n // Ignore malformed SSE frames\n }\n }\n }\n } catch (e: any) {\n if (e?.name !== 'AbortError') {\n onEvent({ type: 'error', error: e?.message || String(e) });\n }\n }\n })();\n return () => ctrl.abort();\n }\n}\n","import * as React from 'react';\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type FormEvent } from 'react';\nimport type { ChatMessage, ChatWidgetConfig, AgentInfo, Citation } from './types';\nimport { WidgetClient } from './api';\nimport './styles.css';\n\nconst DEFAULTS = {\n apiBaseUrl: 'https://api.docgenlab.com',\n position: 'bottom-right' as const,\n primary: '#4F46E5',\n primaryText: '#FFFFFF',\n radius: '14px',\n fontFamily: 'system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif',\n greeting: 'Hi — how can I help?',\n enableImageUpload: true,\n};\n\nexport interface DocGenLabChatProps extends ChatWidgetConfig {\n /** Called when the widget mounts and successfully resolves the agent. */\n onReady?: (agent: AgentInfo) => void;\n /** Called for unrecoverable errors so the customer can log them. */\n onError?: (err: string) => void;\n}\n\n/**\n * Floating-bubble chat widget. Renders a launcher button bottom-right\n * (or -left) that expands to a chat panel.\n */\nexport function DocGenLabChat(props: DocGenLabChatProps) {\n const baseUrl = props.apiBaseUrl || DEFAULTS.apiBaseUrl;\n const position = props.position || DEFAULTS.position;\n const enableImageUpload = props.enableImageUpload !== false;\n\n const themeVars: CSSProperties & Record<string, string> = {\n ['--dgl-primary' as any]: props.theme?.primary || DEFAULTS.primary,\n ['--dgl-primary-text' as any]: props.theme?.primaryText || DEFAULTS.primaryText,\n ['--dgl-radius' as any]: props.theme?.radius || DEFAULTS.radius,\n ['--dgl-font' as any]: props.theme?.fontFamily || DEFAULTS.fontFamily,\n };\n\n const clientRef = useRef<WidgetClient | null>(null);\n if (!clientRef.current) {\n clientRef.current = new WidgetClient({ agentKey: props.agentKey, apiBaseUrl: baseUrl });\n }\n const client = clientRef.current;\n\n const [open, setOpen] = useState(!!props.openOnLoad);\n // Expanded: roughly 1.6x the default, capped to viewport on small screens.\n // Toggled by the icon next to close in the header.\n const [expanded, setExpanded] = useState(false);\n const [agent, setAgent] = useState<AgentInfo | null>(null);\n const [agentError, setAgentError] = useState<string | null>(null);\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [input, setInput] = useState('');\n const [streaming, setStreaming] = useState(false);\n const [conversationId, setConversationId] = useState<string | null>(null);\n const [attachedFile, setAttachedFile] = useState<File | null>(null);\n const [attachedPreview, setAttachedPreview] = useState<string | null>(null);\n const [uploadingImage, setUploadingImage] = useState(false);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const abortRef = useRef<(() => void) | null>(null);\n const contentRef = useRef('');\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Resolve agent on first open\n useEffect(() => {\n if (!open || agent) return;\n client.getAgent()\n .then((info) => {\n setAgent(info);\n props.onReady?.(info);\n })\n .catch((e: Error) => {\n setAgentError(e.message);\n props.onError?.(e.message);\n });\n }, [open, agent, client, props]);\n\n // Restore conversation_id if we have one in localStorage\n useEffect(() => {\n const k = `dgl-chat:${props.agentKey}`;\n const stored = localStorage.getItem(k);\n if (stored) setConversationId(stored);\n }, [props.agentKey]);\n\n // Persist conversation id whenever it changes\n useEffect(() => {\n if (conversationId) {\n localStorage.setItem(`dgl-chat:${props.agentKey}`, conversationId);\n }\n }, [conversationId, props.agentKey]);\n\n // Auto-scroll on new messages\n useEffect(() => {\n if (scrollRef.current) {\n scrollRef.current.scrollTop = scrollRef.current.scrollHeight;\n }\n }, [messages]);\n\n const clearAttachment = useCallback(() => {\n if (attachedPreview) URL.revokeObjectURL(attachedPreview);\n setAttachedFile(null);\n setAttachedPreview(null);\n }, [attachedPreview]);\n\n const stageImage = useCallback((file: File) => {\n if (!file.type.startsWith('image/')) return;\n const MAX = 10 * 1024 * 1024;\n if (file.size > MAX) return;\n if (attachedPreview) URL.revokeObjectURL(attachedPreview);\n setAttachedFile(file);\n setAttachedPreview(URL.createObjectURL(file));\n }, [attachedPreview]);\n\n const onSubmit = async (e: FormEvent) => {\n e.preventDefault();\n if (!input.trim() || streaming || uploadingImage) return;\n const userText = input.trim();\n\n // Upload image (if any) first\n let imagePath: string | undefined;\n let previewForTurn: string | undefined;\n if (attachedFile) {\n try {\n setUploadingImage(true);\n imagePath = await client.uploadImage(attachedFile);\n } catch (e: any) {\n props.onError?.(e?.message || 'Image upload failed');\n setUploadingImage(false);\n return;\n }\n setUploadingImage(false);\n previewForTurn = attachedPreview || undefined;\n }\n\n setInput('');\n contentRef.current = '';\n setMessages((prev) => [\n ...prev,\n { role: 'user', content: userText, imagePath, imagePreviewUrl: previewForTurn },\n { role: 'assistant', content: '', streaming: true },\n ]);\n setStreaming(true);\n // Clear the staged attachment; the turn owns the preview URL now\n setAttachedFile(null);\n setAttachedPreview(null);\n\n abortRef.current = client.streamChat(\n { message: userText, conversation_id: conversationId || undefined, image_path: imagePath },\n (evt) => {\n if (evt.type === 'conversation') {\n setConversationId(evt.conversation_id);\n } else if (evt.type === 'token') {\n contentRef.current += evt.content;\n const snap = contentRef.current;\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant' ? { ...m, content: snap } : m\n ));\n } else if (evt.type === 'citations') {\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant' ? { ...m, citations: evt.citations } : m\n ));\n } else if (evt.type === 'refused') {\n contentRef.current = evt.content;\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant'\n ? { ...m, content: evt.content, refused: true }\n : m\n ));\n } else if (evt.type === 'done') {\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant'\n ? { ...m, streaming: false, id: evt.message_id, cacheHit: evt.cache_hit }\n : m\n ));\n setStreaming(false);\n } else if (evt.type === 'error') {\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant'\n ? { ...m, content: `Error: ${evt.error}`, streaming: false, refused: true }\n : m\n ));\n setStreaming(false);\n props.onError?.(evt.error);\n }\n },\n );\n };\n\n const newChat = () => {\n if (abortRef.current) abortRef.current();\n setMessages([]);\n setConversationId(null);\n setStreaming(false);\n localStorage.removeItem(`dgl-chat:${props.agentKey}`);\n clearAttachment();\n };\n\n const displayName = props.agentName || agent?.name || 'Assistant';\n\n return (\n <div className={`dgl-root dgl-pos-${position}`} style={themeVars}>\n {open && (\n <div className={`dgl-panel ${expanded ? 'dgl-panel-expanded' : ''}`}>\n <div className=\"dgl-header\">\n <div className=\"dgl-header-info\">\n {props.agentAvatarUrl && (\n <img src={props.agentAvatarUrl} alt=\"\" className=\"dgl-avatar\" />\n )}\n <div className=\"dgl-header-text\">\n <div className=\"dgl-title\">{displayName}</div>\n {agent?.org_name && (\n <div className=\"dgl-subtitle\">{agent.org_name}</div>\n )}\n </div>\n </div>\n <div className=\"dgl-header-actions\">\n <button\n type=\"button\"\n onClick={newChat}\n className=\"dgl-icon-btn\"\n title=\"New chat\"\n aria-label=\"New chat\"\n >\n {svgPlus()}\n </button>\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"dgl-icon-btn dgl-hide-on-mobile\"\n title={expanded ? 'Restore size' : 'Expand view'}\n aria-label={expanded ? 'Restore size' : 'Expand view'}\n >\n {expanded ? svgShrink() : svgExpand()}\n </button>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"dgl-icon-btn\"\n title=\"Close\"\n aria-label=\"Close\"\n >\n {svgX()}\n </button>\n </div>\n </div>\n\n <div className=\"dgl-messages\" ref={scrollRef}>\n {messages.length === 0 && !agentError && (\n <div className=\"dgl-empty\">\n <div className=\"dgl-empty-text\">\n {props.greeting || DEFAULTS.greeting}\n </div>\n {agent?.description && (\n <div className=\"dgl-empty-sub\">{agent.description}</div>\n )}\n </div>\n )}\n {agentError && (\n <div className=\"dgl-error\">\n Couldn't load the assistant: {agentError}\n </div>\n )}\n {messages.map((m, idx) => (\n <MessageBubble\n key={idx}\n message={m}\n client={client}\n isLast={idx === messages.length - 1}\n agentAvatarUrl={props.agentAvatarUrl}\n onPickFollowup={(q) => {\n setInput(q);\n // Don't auto-send; user reviews + presses send.\n // Same UX as the dashboard.\n }}\n />\n ))}\n </div>\n\n <form className=\"dgl-composer\" onSubmit={onSubmit}>\n {attachedPreview && (\n <div className=\"dgl-staged-image\">\n <img src={attachedPreview} alt=\"attachment\" />\n <button\n type=\"button\"\n onClick={clearAttachment}\n className=\"dgl-staged-remove\"\n aria-label=\"Remove image\"\n >\n {svgX()}\n </button>\n </div>\n )}\n <div className=\"dgl-composer-row\">\n {enableImageUpload && (\n <>\n <button\n type=\"button\"\n className=\"dgl-icon-btn\"\n onClick={() => fileInputRef.current?.click()}\n disabled={streaming || uploadingImage}\n title=\"Attach image\"\n aria-label=\"Attach image\"\n >\n {svgImage()}\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp,image/gif\"\n style={{ display: 'none' }}\n onChange={(e) => {\n const f = e.target.files?.[0];\n if (f) stageImage(f);\n if (e.target) e.target.value = '';\n }}\n />\n </>\n )}\n <input\n className=\"dgl-input\"\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder={\n streaming\n ? 'Thinking…'\n : attachedPreview\n ? 'Describe what to know about this image…'\n : 'Type your question…'\n }\n disabled={streaming}\n onPaste={(e) => {\n if (!enableImageUpload) return;\n const item = Array.from(e.clipboardData?.items || []).find(\n (it) => it.kind === 'file' && it.type.startsWith('image/')\n );\n if (item) {\n const f = item.getAsFile();\n if (f) { e.preventDefault(); stageImage(f); }\n }\n }}\n />\n <button\n type=\"submit\"\n className=\"dgl-send-btn\"\n disabled={!input.trim() || streaming || uploadingImage}\n aria-label=\"Send\"\n >\n {streaming || uploadingImage ? svgSpinner() : svgSend()}\n </button>\n </div>\n {!props.hideBranding && (\n <div className=\"dgl-branding\">\n Powered by <a href=\"https://docgenlab.com\" target=\"_blank\" rel=\"noopener noreferrer\">DocGenLab</a>\n </div>\n )}\n </form>\n </div>\n )}\n\n <button\n type=\"button\"\n className=\"dgl-launcher\"\n onClick={() => setOpen((v) => !v)}\n aria-label={open ? 'Close chat' : 'Open chat'}\n >\n {open ? svgChevronDown() : svgChat()}\n </button>\n </div>\n );\n}\n\n// ─── Message bubble ─────────────────────────────────────────────────────\n\nfunction MessageBubble({\n message,\n client,\n isLast,\n agentAvatarUrl,\n onPickFollowup,\n}: {\n message: ChatMessage;\n client: WidgetClient;\n isLast: boolean;\n agentAvatarUrl?: string;\n onPickFollowup: (q: string) => void;\n}) {\n if (message.role === 'user') {\n return (\n <div className=\"dgl-msg dgl-msg-user\">\n <div className=\"dgl-bubble dgl-bubble-user\">\n {(message.imagePath || message.imagePreviewUrl) && (\n <UserImage client={client} imagePath={message.imagePath} previewUrl={message.imagePreviewUrl} />\n )}\n {message.content}\n </div>\n </div>\n );\n }\n const cleaned = cleanForDisplay(message.content);\n const followups = !message.streaming ? extractFollowups(message.content) : [];\n return (\n <div className=\"dgl-msg dgl-msg-assistant\">\n {agentAvatarUrl ? (\n <img src={agentAvatarUrl} className=\"dgl-avatar\" alt=\"\" />\n ) : (\n <div className=\"dgl-avatar dgl-avatar-fallback\">{svgSparkles()}</div>\n )}\n <div className=\"dgl-msg-body\">\n {message.refused ? (\n <div className=\"dgl-refused\">{cleaned}</div>\n ) : (\n <div className=\"dgl-assistant-text\">\n {/* Tokens haven't started arriving yet — show thinking dots\n instead of an empty bubble. */}\n {message.streaming && !cleaned ? (\n <ThinkingDots />\n ) : (\n <>\n {renderMarkdown(cleaned)}\n {message.streaming && <span className=\"dgl-typing-cursor\">▍</span>}\n </>\n )}\n </div>\n )}\n {!message.streaming && !!message.citations?.length && (\n <CitationChips citations={message.citations} />\n )}\n {followups.length > 0 && (\n <FollowupChips suggestions={followups} onPick={onPickFollowup} />\n )}\n </div>\n </div>\n );\n}\n\n/** Clickable chips below an assistant answer suggesting next questions.\n * Clicking pre-fills the input — the user reviews + presses send. */\nfunction FollowupChips({\n suggestions,\n onPick,\n}: {\n suggestions: string[];\n onPick: (q: string) => void;\n}) {\n return (\n <div className=\"dgl-followups\">\n <div className=\"dgl-followups-label\">Continue exploring</div>\n <div className=\"dgl-followups-list\">\n {suggestions.map((q, i) => (\n <button\n key={i}\n type=\"button\"\n className=\"dgl-followup-chip\"\n onClick={() => onPick(q)}\n >\n {q}\n </button>\n ))}\n </div>\n </div>\n );\n}\n\nfunction UserImage({\n client,\n imagePath,\n previewUrl,\n}: { client: WidgetClient; imagePath?: string; previewUrl?: string }) {\n const [url, setUrl] = useState<string | null>(previewUrl || null);\n useEffect(() => {\n if (previewUrl || !imagePath) return;\n let revoke: string | null = null;\n client.fetchImage(imagePath)\n .then((u) => { revoke = u; setUrl(u); })\n .catch(() => setUrl(null));\n return () => { if (revoke) URL.revokeObjectURL(revoke); };\n }, [client, imagePath, previewUrl]);\n if (!url) return null;\n return (\n <a href={url} target=\"_blank\" rel=\"noopener noreferrer\" className=\"dgl-user-image-link\">\n <img src={url} className=\"dgl-user-image\" alt=\"attached\" />\n </a>\n );\n}\n\nfunction CitationChips({ citations }: { citations: Citation[] }) {\n // Dedupe by source — many chunks often come from the same doc, no need to spam.\n const seen = new Set<string>();\n const unique = citations.filter((c) => {\n if (seen.has(c.source_id)) return false;\n seen.add(c.source_id);\n return true;\n });\n if (!unique.length) return null;\n return (\n <div className=\"dgl-citations\">\n {unique.map((c) => (\n <span key={c.chunk_id} className=\"dgl-citation\" title={c.snippet}>\n {c.source_name}{c.page ? ` · p${c.page}` : ''}\n </span>\n ))}\n </div>\n );\n}\n\n/** Pull the suggested follow-up questions out of the trailing\n * <followups>...</followups> block (max 3, 4-120 chars each). Returns []\n * if the block is missing, mid-stream, or empty. */\nfunction extractFollowups(content: string): string[] {\n const match = content.match(/<followups>([\\s\\S]*?)<\\/followups>/i);\n if (!match) return [];\n return match[1]\n .split('\\n')\n .map((line) => line.trim())\n .map((line) => line.replace(/^[-*•\\d.)\\s\"']+|[\"']$/g, '').trim())\n .filter((line) => line.length >= 4 && line.length <= 120)\n .slice(0, 3);\n}\n\n/** Strip citation markers, [Section: ...] tags, [doc:abc...] refs, the\n * trailing <followups>...</followups> block, and any leftover orphan\n * punctuation. Ported from the dashboard's cleanCitations so the widget\n * matches the main app's chat exactly.\n *\n * Why this is needed: the system prompt asks the LLM not to emit inline\n * citations, but it sometimes leaks `[doc:11]` / `[1]` / etc. anyway. The\n * user-visible answer must be clean — citations render as separate chips. */\nfunction cleanForDisplay(text: string): string {\n return text\n .replace(/\\n*<followups>[\\s\\S]*?(<\\/followups>|$)/gi, '')\n .replace(/\\s*\\[\\s*Section:[^\\]]*\\]?/gi, '')\n .replace(/\\s*\\[\\s*doc:[^\\]]*\\]?/gi, '')\n .replace(/\\s*\\[\\s*(?:document|doc|source|ref|reference)[^\\]]*\\]?/gi, '')\n .replace(/\\s*\\[\\s*(?:p\\.?\\s*)?\\d+(?:\\s*[,–-]\\s*\\d+)*\\s*\\]/gi, '')\n .replace(/[,;]?\\s*doc:\\s*[0-9a-fA-F][0-9a-fA-F-]{6,}\\]?/gi, '')\n .replace(/[,;]\\s*(?:\\d+|doc:?\\s*[0-9a-fA-F-]+)\\s*\\]?/gi, '')\n .replace(/([\\w%])\\s*\\]/g, '$1')\n .replace(/\\s+([,.;:!?])/g, '$1')\n .replace(/[ \\t]+/g, ' ')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/** Minimal markdown → React renderer for assistant answers. Handles the\n * patterns the system prompt emits: **bold**, `inline code`, paragraph\n * breaks, and `-` / `*` / `1.` lists. Deliberately no full markdown\n * parser to keep the bundle small — for richer needs, the customer can\n * override by post-processing `onToken` themselves later. */\nfunction renderMarkdown(text: string): React.ReactNode {\n // Split into paragraphs first (blank-line separated)\n const blocks = text.split(/\\n{2,}/);\n return blocks.map((block, i) => {\n const lines = block.split('\\n');\n const listMatch = lines.every((l) => /^(\\s*)([-*]|\\d+\\.)\\s/.test(l));\n if (listMatch && lines.length > 0) {\n const isOrdered = /^\\d+\\./.test(lines[0].trim());\n const items = lines.map((l) => l.replace(/^\\s*(?:[-*]|\\d+\\.)\\s+/, ''));\n const Tag = isOrdered ? 'ol' : 'ul';\n return (\n <Tag key={i} className={`dgl-md-list ${isOrdered ? 'dgl-md-ol' : 'dgl-md-ul'}`}>\n {items.map((it, j) => (\n <li key={j}>{renderInline(it)}</li>\n ))}\n </Tag>\n );\n }\n return (\n <p key={i} className=\"dgl-md-p\">\n {block.split('\\n').map((line, j, arr) => (\n <React.Fragment key={j}>\n {renderInline(line)}\n {j < arr.length - 1 && <br />}\n </React.Fragment>\n ))}\n </p>\n );\n });\n}\n\n/** Inline markdown: **bold**, *italic*, `code`, [text](url). */\nfunction renderInline(text: string): React.ReactNode {\n // Tokenize. We avoid a full parser; this regex captures the supported\n // patterns in order of precedence (code first so it shields its contents).\n const pattern = /(`[^`]+`|\\*\\*[^*\\n]+\\*\\*|\\*[^*\\n]+\\*|\\[[^\\]]+\\]\\([^)]+\\))/g;\n const parts = text.split(pattern);\n return parts.map((part, idx) => {\n if (!part) return null;\n if (part.startsWith('`') && part.endsWith('`')) {\n return <code key={idx} className=\"dgl-md-code\">{part.slice(1, -1)}</code>;\n }\n if (part.startsWith('**') && part.endsWith('**')) {\n return <strong key={idx}>{part.slice(2, -2)}</strong>;\n }\n if (part.startsWith('*') && part.endsWith('*') && part.length > 2) {\n return <em key={idx}>{part.slice(1, -1)}</em>;\n }\n const linkMatch = part.match(/^\\[([^\\]]+)\\]\\(([^)]+)\\)$/);\n if (linkMatch) {\n return (\n <a key={idx} href={linkMatch[2]} target=\"_blank\" rel=\"noopener noreferrer\" className=\"dgl-md-link\">\n {linkMatch[1]}\n </a>\n );\n }\n return <React.Fragment key={idx}>{part}</React.Fragment>;\n });\n}\n\n/** Bouncing-dots loading indicator shown after Send but before the first\n * token arrives. Matches the dashboard's ThinkingDots cadence. */\nfunction ThinkingDots() {\n return (\n <span className=\"dgl-thinking\">\n <span className=\"dgl-thinking-dot\" />\n <span className=\"dgl-thinking-dot\" />\n <span className=\"dgl-thinking-dot\" />\n </span>\n );\n}\n\n// ─── Inline SVG icons (no external deps) ────────────────────────────────\n\nfunction svgChat() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"currentColor\">\n <path d=\"M4 4h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H8l-4 4V6a2 2 0 0 1 2-2z\" />\n </svg>\n );\n}\nfunction svgChevronDown() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n );\n}\nfunction svgX() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\nfunction svgPlus() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n );\n}\nfunction svgSend() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\">\n <path d=\"M2 21l21-9L2 3v7l15 2-15 2z\" />\n </svg>\n );\n}\nfunction svgImage() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n );\n}\nfunction svgSpinner() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" className=\"dgl-spin\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\">\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n );\n}\nfunction svgExpand() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"15 3 21 3 21 9\" />\n <polyline points=\"9 21 3 21 3 15\" />\n <line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\" />\n <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n </svg>\n );\n}\nfunction svgShrink() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"4 14 10 14 10 20\" />\n <polyline points=\"20 10 14 10 14 4\" />\n <line x1=\"14\" y1=\"10\" x2=\"21\" y2=\"3\" />\n <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n </svg>\n );\n}\nfunction svgSparkles() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"currentColor\">\n <path d=\"M12 2l2.39 6.95L21 11l-6.61 2.05L12 20l-2.39-6.95L3 11l6.61-2.05L12 2z\" />\n </svg>\n );\n}\n","/**\n * @docgenlab.com/chat-widget — public entry.\n *\n * Two integration paths:\n *\n * 1. React (recommended for SPAs):\n *\n * import { DocGenLabChat } from '@docgenlab.com/chat-widget';\n * import '@docgenlab.com/chat-widget/style.css';\n *\n * <DocGenLabChat agentKey=\"pk_live_...\" />\n *\n * 2. Vanilla / script tag (WordPress, marketing pages, anything without\n * a build step). Use unpkg or jsdelivr to load the UMD bundle:\n *\n * <link rel=\"stylesheet\" href=\"https://unpkg.com/@docgenlab.com/chat-widget/dist/widget.css\" />\n * <script src=\"https://unpkg.com/@docgenlab.com/chat-widget\"></script>\n * <script>DocGenLab.init({ agentKey: 'pk_live_...' })</script>\n *\n * The UMD bundle exposes a global `DocGenLab` object with `init()` and\n * `destroy()` methods that mount/unmount the widget on a hidden div.\n */\n\nimport { createRoot, type Root } from 'react-dom/client';\nimport { createElement } from 'react';\nimport { DocGenLabChat, type DocGenLabChatProps } from './widget';\nimport type { ChatWidgetConfig, ChatWidgetTheme, ChatPosition, Citation, AgentInfo, StreamEvent, ChatMessage } from './types';\n\nexport { DocGenLabChat };\nexport type {\n DocGenLabChatProps,\n ChatWidgetConfig,\n ChatWidgetTheme,\n ChatPosition,\n Citation,\n AgentInfo,\n StreamEvent,\n ChatMessage,\n};\n\n// ─── Vanilla bootstrap ──────────────────────────────────────────────────\n\nlet _root: Root | null = null;\nlet _container: HTMLDivElement | null = null;\n\nexport function init(config: DocGenLabChatProps): void {\n if (typeof document === 'undefined') {\n throw new Error('DocGenLab.init() requires a browser environment.');\n }\n if (_root) {\n // Re-mount on second call (config change)\n _root.render(createElement(DocGenLabChat, config));\n return;\n }\n _container = document.createElement('div');\n _container.id = 'docgenlab-chat-widget-root';\n document.body.appendChild(_container);\n _root = createRoot(_container);\n _root.render(createElement(DocGenLabChat, config));\n}\n\nexport function destroy(): void {\n if (_root) {\n _root.unmount();\n _root = null;\n }\n if (_container) {\n _container.remove();\n _container = null;\n }\n}\n\n// Make sure the UMD global has these too. Vite assigns the default export to\n// the configured `name` ('DocGenLab'); explicitly attach methods so vanilla\n// HTML can use `DocGenLab.init(...)` without an import.\n// (This is a no-op when consumed via ESM imports.)\ndeclare global {\n interface Window {\n DocGenLab?: { init: typeof init; destroy: typeof destroy; DocGenLabChat: typeof DocGenLabChat };\n }\n}\nif (typeof window !== 'undefined') {\n window.DocGenLab = { init, destroy, DocGenLabChat };\n}\n"],"names":["PUBLIC_BASE","VISITOR_KEY","uuid","c","ch","getVisitorId","id","e","WidgetClient","opts","extra","resp","file","fd","detail","j","imagePath","url","blob","body","onEvent","ctrl","reader","decoder","buf","value","done","nl","raw","line","evt","DEFAULTS","DocGenLabChat","props","baseUrl","position","enableImageUpload","themeVars","_a","_b","_c","_d","clientRef","useRef","client","open","setOpen","useState","expanded","setExpanded","agent","setAgent","agentError","setAgentError","messages","setMessages","input","setInput","streaming","setStreaming","conversationId","setConversationId","attachedFile","setAttachedFile","attachedPreview","setAttachedPreview","uploadingImage","setUploadingImage","fileInputRef","abortRef","contentRef","scrollRef","useEffect","info","k","stored","clearAttachment","useCallback","stageImage","MAX","onSubmit","userText","previewForTurn","prev","snap","m","i","newChat","displayName","React","svgPlus","v","svgShrink","svgExpand","svgX","idx","MessageBubble","q","svgImage","f","item","it","svgSpinner","svgSend","svgChevronDown","svgChat","message","isLast","agentAvatarUrl","onPickFollowup","UserImage","cleaned","cleanForDisplay","followups","extractFollowups","svgSparkles","ThinkingDots","renderMarkdown","CitationChips","FollowupChips","suggestions","onPick","previewUrl","setUrl","revoke","u","citations","seen","unique","content","match","text","block","lines","l","isOrdered","items","Tag","renderInline","arr","pattern","part","linkMatch","_root","_container","init","config","createElement","createRoot","destroy"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
(function(v,$){typeof exports=="object"&&typeof module!="undefined"?$(exports,require("react-dom/client"),require("react")):typeof define=="function"&&define.amd?define(["exports","react-dom/client","react"],$):(v=typeof globalThis!="undefined"?globalThis:v||self,$(v.DocGenLab={},v.ReactDOM,v.React))})(this,function(v,$,i){"use strict";function ce(t){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const a in t)if(a!=="default"){const n=Object.getOwnPropertyDescriptor(t,a);Object.defineProperty(r,a,n.get?n:{enumerable:!0,get:()=>t[a]})}}return r.default=t,Object.freeze(r)}const e=ce(i),F="/api/v1/public/kb",X="dgl-visitor-id";function q(){const t=globalThis.crypto;return t&&typeof t.randomUUID=="function"?t.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,r=>{const a=Math.random()*16|0;return(r==="x"?a:a&3|8).toString(16)})}function de(){try{let t=localStorage.getItem(X);return t||(t=q(),localStorage.setItem(X,t)),t}catch(t){return q()}}class me{constructor(r){this.agentKey=r.agentKey,this.baseUrl=r.apiBaseUrl.replace(/\/+$/,""),this.visitorId=de()}headers(r){return{"X-DocGenLab-Key":this.agentKey,"X-DocGenLab-Visitor-Id":this.visitorId,...r||{}}}async getAgent(){const r=await fetch(`${this.baseUrl}${F}/agent`,{method:"GET",headers:this.headers()});if(!r.ok)throw new Error(`getAgent failed: ${r.status}`);return r.json()}async uploadImage(r){const a=new FormData;a.append("file",r);const n=await fetch(`${this.baseUrl}${F}/chat-image`,{method:"POST",headers:this.headers(),body:a});if(!n.ok){let c=`${n.status} ${n.statusText}`;try{const o=await n.json();c=(o==null?void 0:o.detail)||c}catch(o){}throw new Error(c)}return(await n.json()).image_path}async fetchImage(r){const a=`${this.baseUrl}${F}/chat-image?path=${encodeURIComponent(r)}&key=${encodeURIComponent(this.agentKey)}`,n=await fetch(a,{method:"GET",headers:{"X-DocGenLab-Visitor-Id":this.visitorId}});if(!n.ok)throw new Error(`fetchImage failed: ${n.status}`);const l=await n.blob();return URL.createObjectURL(l)}streamChat(r,a){const n=new AbortController;return(async()=>{try{const l=await fetch(`${this.baseUrl}${F}/chat`,{method:"POST",signal:n.signal,headers:this.headers({"Content-Type":"application/json",Accept:"text/event-stream"}),body:JSON.stringify(r)});if(!l.ok||!l.body){let p=`${l.status} ${l.statusText}`;try{const h=await l.json();p=(h==null?void 0:h.detail)||p}catch(h){}a({type:"error",error:p});return}const c=l.body.getReader(),o=new TextDecoder;let u="";for(;;){const{value:p,done:h}=await c.read();if(h)break;u+=o.decode(p,{stream:!0});let L;for(;(L=u.indexOf(`
|
|
2
|
+
|
|
3
|
+
`))!==-1;){const f=u.slice(0,L);u=u.slice(L+2);const T=f.trim();if(T.startsWith("data:"))try{const U=JSON.parse(T.slice(5).trim());a(U)}catch(U){}}}}catch(l){(l==null?void 0:l.name)!=="AbortError"&&a({type:"error",error:(l==null?void 0:l.message)||String(l)})}})(),()=>n.abort()}}const x={apiBaseUrl:"https://api.docgenlab.com",position:"bottom-right",primary:"#4F46E5",primaryText:"#FFFFFF",radius:"14px",fontFamily:'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',greeting:"Hi — how can I help?"};function A(t){var ae,re,le,se;const r=t.apiBaseUrl||x.apiBaseUrl,a=t.position||x.position,n=t.enableImageUpload!==!1,l={"--dgl-primary":((ae=t.theme)==null?void 0:ae.primary)||x.primary,"--dgl-primary-text":((re=t.theme)==null?void 0:re.primaryText)||x.primaryText,"--dgl-radius":((le=t.theme)==null?void 0:le.radius)||x.radius,"--dgl-font":((se=t.theme)==null?void 0:se.fontFamily)||x.fontFamily},c=i.useRef(null);c.current||(c.current=new me({agentKey:t.agentKey,apiBaseUrl:r}));const o=c.current,[u,p]=i.useState(!!t.openOnLoad),[h,L]=i.useState(!1),[f,T]=i.useState(null),[U,Ie]=i.useState(null),[D,S]=i.useState([]),[j,M]=i.useState(""),[I,O]=i.useState(!1),[_,K]=i.useState(null),[Z,G]=i.useState(null),[k,z]=i.useState(null),[B,R]=i.useState(!1),ee=i.useRef(null),V=i.useRef(null),P=i.useRef(""),W=i.useRef(null);i.useEffect(()=>{!u||f||o.getAgent().then(s=>{var d;T(s),(d=t.onReady)==null||d.call(t,s)}).catch(s=>{var d;Ie(s.message),(d=t.onError)==null||d.call(t,s.message)})},[u,f,o,t]),i.useEffect(()=>{const s=`dgl-chat:${t.agentKey}`,d=localStorage.getItem(s);d&&K(d)},[t.agentKey]),i.useEffect(()=>{_&&localStorage.setItem(`dgl-chat:${t.agentKey}`,_)},[_,t.agentKey]),i.useEffect(()=>{W.current&&(W.current.scrollTop=W.current.scrollHeight)},[D]);const te=i.useCallback(()=>{k&&URL.revokeObjectURL(k),G(null),z(null)},[k]),ne=i.useCallback(s=>{if(!s.type.startsWith("image/"))return;const d=10*1024*1024;s.size>d||(k&&URL.revokeObjectURL(k),G(s),z(URL.createObjectURL(s)))},[k]),$e=async s=>{var ie;if(s.preventDefault(),!j.trim()||I||B)return;const d=j.trim();let E,w;if(Z){try{R(!0),E=await o.uploadImage(Z)}catch(m){(ie=t.onError)==null||ie.call(t,(m==null?void 0:m.message)||"Image upload failed"),R(!1);return}R(!1),w=k||void 0}M(""),P.current="",S(m=>[...m,{role:"user",content:d,imagePath:E,imagePreviewUrl:w},{role:"assistant",content:"",streaming:!0}]),O(!0),G(null),z(null),V.current=o.streamChat({message:d,conversation_id:_||void 0,image_path:E},m=>{var oe;if(m.type==="conversation")K(m.conversation_id);else if(m.type==="token"){P.current+=m.content;const y=P.current;S(g=>g.map((b,Te)=>Te===g.length-1&&b.role==="assistant"?{...b,content:y}:b))}else m.type==="citations"?S(y=>y.map((g,b)=>b===y.length-1&&g.role==="assistant"?{...g,citations:m.citations}:g)):m.type==="refused"?(P.current=m.content,S(y=>y.map((g,b)=>b===y.length-1&&g.role==="assistant"?{...g,content:m.content,refused:!0}:g))):m.type==="done"?(S(y=>y.map((g,b)=>b===y.length-1&&g.role==="assistant"?{...g,streaming:!1,id:m.message_id,cacheHit:m.cache_hit}:g)),O(!1)):m.type==="error"&&(S(y=>y.map((g,b)=>b===y.length-1&&g.role==="assistant"?{...g,content:`Error: ${m.error}`,streaming:!1,refused:!0}:g)),O(!1),(oe=t.onError)==null||oe.call(t,m.error))})},Fe=()=>{V.current&&V.current(),S([]),K(null),O(!1),localStorage.removeItem(`dgl-chat:${t.agentKey}`),te()},Ae=t.agentName||(f==null?void 0:f.name)||"Assistant";return e.createElement("div",{className:`dgl-root dgl-pos-${a}`,style:l},u&&e.createElement("div",{className:`dgl-panel ${h?"dgl-panel-expanded":""}`},e.createElement("div",{className:"dgl-header"},e.createElement("div",{className:"dgl-header-info"},t.agentAvatarUrl&&e.createElement("img",{src:t.agentAvatarUrl,alt:"",className:"dgl-avatar"}),e.createElement("div",{className:"dgl-header-text"},e.createElement("div",{className:"dgl-title"},Ae),(f==null?void 0:f.org_name)&&e.createElement("div",{className:"dgl-subtitle"},f.org_name))),e.createElement("div",{className:"dgl-header-actions"},e.createElement("button",{type:"button",onClick:Fe,className:"dgl-icon-btn",title:"New chat","aria-label":"New chat"},we()),e.createElement("button",{type:"button",onClick:()=>L(s=>!s),className:"dgl-icon-btn dgl-hide-on-mobile",title:h?"Restore size":"Expand view","aria-label":h?"Restore size":"Expand view"},h?Se():Le()),e.createElement("button",{type:"button",onClick:()=>p(!1),className:"dgl-icon-btn",title:"Close","aria-label":"Close"},J()))),e.createElement("div",{className:"dgl-messages",ref:W},D.length===0&&!U&&e.createElement("div",{className:"dgl-empty"},e.createElement("div",{className:"dgl-empty-text"},t.greeting||x.greeting),(f==null?void 0:f.description)&&e.createElement("div",{className:"dgl-empty-sub"},f.description)),U&&e.createElement("div",{className:"dgl-error"},"Couldn't load the assistant: ",U),D.map((s,d)=>e.createElement(ue,{key:d,message:s,client:o,isLast:d===D.length-1,agentAvatarUrl:t.agentAvatarUrl,onPickFollowup:E=>{M(E)}}))),e.createElement("form",{className:"dgl-composer",onSubmit:$e},k&&e.createElement("div",{className:"dgl-staged-image"},e.createElement("img",{src:k,alt:"attachment"}),e.createElement("button",{type:"button",onClick:te,className:"dgl-staged-remove","aria-label":"Remove image"},J())),e.createElement("div",{className:"dgl-composer-row"},n&&e.createElement(e.Fragment,null,e.createElement("button",{type:"button",className:"dgl-icon-btn",onClick:()=>{var s;return(s=ee.current)==null?void 0:s.click()},disabled:I||B,title:"Attach image","aria-label":"Attach image"},Ne()),e.createElement("input",{ref:ee,type:"file",accept:"image/png,image/jpeg,image/webp,image/gif",style:{display:"none"},onChange:s=>{var E;const d=(E=s.target.files)==null?void 0:E[0];d&&ne(d),s.target&&(s.target.value="")}})),e.createElement("input",{className:"dgl-input",value:j,onChange:s=>M(s.target.value),placeholder:I?"Thinking…":k?"Describe what to know about this image…":"Type your question…",disabled:I,onPaste:s=>{var E;if(!n)return;const d=Array.from(((E=s.clipboardData)==null?void 0:E.items)||[]).find(w=>w.kind==="file"&&w.type.startsWith("image/"));if(d){const w=d.getAsFile();w&&(s.preventDefault(),ne(w))}}}),e.createElement("button",{type:"submit",className:"dgl-send-btn",disabled:!j.trim()||I||B,"aria-label":"Send"},I||B?Ce():xe())),!t.hideBranding&&e.createElement("div",{className:"dgl-branding"},"Powered by ",e.createElement("a",{href:"https://docgenlab.com",target:"_blank",rel:"noopener noreferrer"},"DocGenLab")))),e.createElement("button",{type:"button",className:"dgl-launcher",onClick:()=>p(s=>!s),"aria-label":u?"Close chat":"Open chat"},u?ke():ve()))}function ue({message:t,client:r,isLast:a,agentAvatarUrl:n,onPickFollowup:l}){var u;if(t.role==="user")return e.createElement("div",{className:"dgl-msg dgl-msg-user"},e.createElement("div",{className:"dgl-bubble dgl-bubble-user"},(t.imagePath||t.imagePreviewUrl)&&e.createElement(he,{client:r,imagePath:t.imagePath,previewUrl:t.imagePreviewUrl}),t.content));const c=pe(t.content),o=t.streaming?[]:ye(t.content);return e.createElement("div",{className:"dgl-msg dgl-msg-assistant"},n?e.createElement("img",{src:n,className:"dgl-avatar",alt:""}):e.createElement("div",{className:"dgl-avatar dgl-avatar-fallback"},Ue()),e.createElement("div",{className:"dgl-msg-body"},t.refused?e.createElement("div",{className:"dgl-refused"},c):e.createElement("div",{className:"dgl-assistant-text"},t.streaming&&!c?e.createElement(be,null):e.createElement(e.Fragment,null,Ee(c),t.streaming&&e.createElement("span",{className:"dgl-typing-cursor"},"▍"))),!t.streaming&&!!((u=t.citations)!=null&&u.length)&&e.createElement(fe,{citations:t.citations}),o.length>0&&e.createElement(ge,{suggestions:o,onPick:l})))}function ge({suggestions:t,onPick:r}){return e.createElement("div",{className:"dgl-followups"},e.createElement("div",{className:"dgl-followups-label"},"Continue exploring"),e.createElement("div",{className:"dgl-followups-list"},t.map((a,n)=>e.createElement("button",{key:n,type:"button",className:"dgl-followup-chip",onClick:()=>r(a)},a))))}function he({client:t,imagePath:r,previewUrl:a}){const[n,l]=i.useState(a||null);return i.useEffect(()=>{if(a||!r)return;let c=null;return t.fetchImage(r).then(o=>{c=o,l(o)}).catch(()=>l(null)),()=>{c&&URL.revokeObjectURL(c)}},[t,r,a]),n?e.createElement("a",{href:n,target:"_blank",rel:"noopener noreferrer",className:"dgl-user-image-link"},e.createElement("img",{src:n,className:"dgl-user-image",alt:"attached"})):null}function fe({citations:t}){const r=new Set,a=t.filter(n=>r.has(n.source_id)?!1:(r.add(n.source_id),!0));return a.length?e.createElement("div",{className:"dgl-citations"},a.map(n=>e.createElement("span",{key:n.chunk_id,className:"dgl-citation",title:n.snippet},n.source_name,n.page?` · p${n.page}`:""))):null}function ye(t){const r=t.match(/<followups>([\s\S]*?)<\/followups>/i);return r?r[1].split(`
|
|
4
|
+
`).map(a=>a.trim()).map(a=>a.replace(/^[-*•\d.)\s"']+|["']$/g,"").trim()).filter(a=>a.length>=4&&a.length<=120).slice(0,3):[]}function pe(t){return t.replace(/\n*<followups>[\s\S]*?(<\/followups>|$)/gi,"").replace(/\s*\[\s*Section:[^\]]*\]?/gi,"").replace(/\s*\[\s*doc:[^\]]*\]?/gi,"").replace(/\s*\[\s*(?:document|doc|source|ref|reference)[^\]]*\]?/gi,"").replace(/\s*\[\s*(?:p\.?\s*)?\d+(?:\s*[,–-]\s*\d+)*\s*\]/gi,"").replace(/[,;]?\s*doc:\s*[0-9a-fA-F][0-9a-fA-F-]{6,}\]?/gi,"").replace(/[,;]\s*(?:\d+|doc:?\s*[0-9a-fA-F-]+)\s*\]?/gi,"").replace(/([\w%])\s*\]/g,"$1").replace(/\s+([,.;:!?])/g,"$1").replace(/[ \t]+/g," ").replace(/\n{3,}/g,`
|
|
5
|
+
|
|
6
|
+
`).trim()}function Ee(t){return t.split(/\n{2,}/).map((a,n)=>{const l=a.split(`
|
|
7
|
+
`);if(l.every(o=>/^(\s*)([-*]|\d+\.)\s/.test(o))&&l.length>0){const o=/^\d+\./.test(l[0].trim()),u=l.map(h=>h.replace(/^\s*(?:[-*]|\d+\.)\s+/,"")),p=o?"ol":"ul";return e.createElement(p,{key:n,className:`dgl-md-list ${o?"dgl-md-ol":"dgl-md-ul"}`},u.map((h,L)=>e.createElement("li",{key:L},H(h))))}return e.createElement("p",{key:n,className:"dgl-md-p"},a.split(`
|
|
8
|
+
`).map((o,u,p)=>e.createElement(e.Fragment,{key:u},H(o),u<p.length-1&&e.createElement("br",null))))})}function H(t){const r=/(`[^`]+`|\*\*[^*\n]+\*\*|\*[^*\n]+\*|\[[^\]]+\]\([^)]+\))/g;return t.split(r).map((n,l)=>{if(!n)return null;if(n.startsWith("`")&&n.endsWith("`"))return e.createElement("code",{key:l,className:"dgl-md-code"},n.slice(1,-1));if(n.startsWith("**")&&n.endsWith("**"))return e.createElement("strong",{key:l},n.slice(2,-2));if(n.startsWith("*")&&n.endsWith("*")&&n.length>2)return e.createElement("em",{key:l},n.slice(1,-1));const c=n.match(/^\[([^\]]+)\]\(([^)]+)\)$/);return c?e.createElement("a",{key:l,href:c[2],target:"_blank",rel:"noopener noreferrer",className:"dgl-md-link"},c[1]):e.createElement(e.Fragment,{key:l},n)})}function be(){return e.createElement("span",{className:"dgl-thinking"},e.createElement("span",{className:"dgl-thinking-dot"}),e.createElement("span",{className:"dgl-thinking-dot"}),e.createElement("span",{className:"dgl-thinking-dot"}))}function ve(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"22",height:"22",fill:"currentColor"},e.createElement("path",{d:"M4 4h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H8l-4 4V6a2 2 0 0 1 2-2z"}))}function ke(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"22",height:"22",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round"},e.createElement("polyline",{points:"6 9 12 15 18 9"}))}function J(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},e.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),e.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"}))}function we(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"16",height:"16",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},e.createElement("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),e.createElement("line",{x1:"5",y1:"12",x2:"19",y2:"12"}))}function xe(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"16",height:"16",fill:"currentColor"},e.createElement("path",{d:"M2 21l21-9L2 3v7l15 2-15 2z"}))}function Ne(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"18",height:"18",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},e.createElement("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2"}),e.createElement("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),e.createElement("polyline",{points:"21 15 16 10 5 21"}))}function Ce(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"16",height:"16",className:"dgl-spin",fill:"none",stroke:"currentColor",strokeWidth:"3",strokeLinecap:"round"},e.createElement("path",{d:"M21 12a9 9 0 1 1-6.219-8.56"}))}function Le(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},e.createElement("polyline",{points:"15 3 21 3 21 9"}),e.createElement("polyline",{points:"9 21 3 21 3 15"}),e.createElement("line",{x1:"21",y1:"3",x2:"14",y2:"10"}),e.createElement("line",{x1:"3",y1:"21",x2:"10",y2:"14"}))}function Se(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},e.createElement("polyline",{points:"4 14 10 14 10 20"}),e.createElement("polyline",{points:"20 10 14 10 14 4"}),e.createElement("line",{x1:"14",y1:"10",x2:"21",y2:"3"}),e.createElement("line",{x1:"3",y1:"21",x2:"10",y2:"14"}))}function Ue(){return e.createElement("svg",{viewBox:"0 0 24 24",width:"14",height:"14",fill:"currentColor"},e.createElement("path",{d:"M12 2l2.39 6.95L21 11l-6.61 2.05L12 20l-2.39-6.95L3 11l6.61-2.05L12 2z"}))}let N=null,C=null;function Y(t){if(typeof document=="undefined")throw new Error("DocGenLab.init() requires a browser environment.");if(N){N.render(i.createElement(A,t));return}C=document.createElement("div"),C.id="docgenlab-chat-widget-root",document.body.appendChild(C),N=$.createRoot(C),N.render(i.createElement(A,t))}function Q(){N&&(N.unmount(),N=null),C&&(C.remove(),C=null)}typeof window!="undefined"&&(window.DocGenLab={init:Y,destroy:Q,DocGenLabChat:A}),v.DocGenLabChat=A,v.destroy=Q,v.init=Y,Object.defineProperty(v,Symbol.toStringTag,{value:"Module"})});
|
|
9
|
+
//# sourceMappingURL=index.umd.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.umd.cjs","sources":["../src/api.ts","../src/widget.tsx","../src/index.ts"],"sourcesContent":["/**\n * Thin client for the /api/v1/public/kb/* surface. No axios — uses native\n * fetch + a manual SSE reader so the bundle stays small.\n *\n * All requests carry two headers:\n * X-DocGenLab-Key — the publishable key (server resolves to agent)\n * X-DocGenLab-Visitor-Id — a stable UUID minted in localStorage\n */\n\nimport type { AgentInfo, StreamEvent } from './types';\n\nconst PUBLIC_BASE = '/api/v1/public/kb';\nconst VISITOR_KEY = 'dgl-visitor-id';\n\n/** Generate a UUID v4. Avoids the `crypto.randomUUID` dependency for older\n * browsers; falls back to a Math.random-based variant. */\nfunction uuid(): string {\n const c = (globalThis as any).crypto;\n if (c && typeof c.randomUUID === 'function') return c.randomUUID();\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {\n const r = (Math.random() * 16) | 0;\n const v = ch === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nfunction getVisitorId(): string {\n try {\n let id = localStorage.getItem(VISITOR_KEY);\n if (!id) {\n id = uuid();\n localStorage.setItem(VISITOR_KEY, id);\n }\n return id;\n } catch {\n // Storage unavailable (private mode, blocked) — use a session-scoped id.\n return uuid();\n }\n}\n\nexport interface ClientOptions {\n agentKey: string;\n apiBaseUrl: string;\n}\n\nexport class WidgetClient {\n private readonly agentKey: string;\n private readonly baseUrl: string;\n private readonly visitorId: string;\n\n constructor(opts: ClientOptions) {\n this.agentKey = opts.agentKey;\n this.baseUrl = opts.apiBaseUrl.replace(/\\/+$/, '');\n this.visitorId = getVisitorId();\n }\n\n private headers(extra?: Record<string, string>): Record<string, string> {\n return {\n 'X-DocGenLab-Key': this.agentKey,\n 'X-DocGenLab-Visitor-Id': this.visitorId,\n ...(extra || {}),\n };\n }\n\n async getAgent(): Promise<AgentInfo> {\n const resp = await fetch(`${this.baseUrl}${PUBLIC_BASE}/agent`, {\n method: 'GET',\n headers: this.headers(),\n });\n if (!resp.ok) throw new Error(`getAgent failed: ${resp.status}`);\n return resp.json();\n }\n\n async uploadImage(file: File): Promise<string> {\n const fd = new FormData();\n fd.append('file', file);\n const resp = await fetch(`${this.baseUrl}${PUBLIC_BASE}/chat-image`, {\n method: 'POST',\n headers: this.headers(), // browser sets Content-Type for FormData\n body: fd,\n });\n if (!resp.ok) {\n let detail = `${resp.status} ${resp.statusText}`;\n try {\n const j = await resp.json();\n detail = j?.detail || detail;\n } catch { /* keep status */ }\n throw new Error(detail);\n }\n const j = await resp.json();\n return j.image_path as string;\n }\n\n /** Fetch a previously-uploaded image as a blob URL. Caller must\n * URL.revokeObjectURL() when done to avoid memory leaks. */\n async fetchImage(imagePath: string): Promise<string> {\n const url = `${this.baseUrl}${PUBLIC_BASE}/chat-image?path=${encodeURIComponent(imagePath)}&key=${encodeURIComponent(this.agentKey)}`;\n const resp = await fetch(url, {\n method: 'GET',\n headers: { 'X-DocGenLab-Visitor-Id': this.visitorId },\n });\n if (!resp.ok) throw new Error(`fetchImage failed: ${resp.status}`);\n const blob = await resp.blob();\n return URL.createObjectURL(blob);\n }\n\n /** Stream a chat response via SSE. Returns an abort fn the caller invokes\n * to cancel the stream. */\n streamChat(\n body: { message: string; conversation_id?: string; image_path?: string },\n onEvent: (event: StreamEvent) => void,\n ): () => void {\n const ctrl = new AbortController();\n (async () => {\n try {\n const resp = await fetch(`${this.baseUrl}${PUBLIC_BASE}/chat`, {\n method: 'POST',\n signal: ctrl.signal,\n headers: this.headers({ 'Content-Type': 'application/json', Accept: 'text/event-stream' }),\n body: JSON.stringify(body),\n });\n if (!resp.ok || !resp.body) {\n let detail = `${resp.status} ${resp.statusText}`;\n try { const j = await resp.json(); detail = j?.detail || detail; } catch {}\n onEvent({ type: 'error', error: detail });\n return;\n }\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n let buf = '';\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl;\n while ((nl = buf.indexOf('\\n\\n')) !== -1) {\n const raw = buf.slice(0, nl);\n buf = buf.slice(nl + 2);\n const line = raw.trim();\n if (!line.startsWith('data:')) continue;\n try {\n const evt = JSON.parse(line.slice(5).trim()) as StreamEvent;\n onEvent(evt);\n } catch {\n // Ignore malformed SSE frames\n }\n }\n }\n } catch (e: any) {\n if (e?.name !== 'AbortError') {\n onEvent({ type: 'error', error: e?.message || String(e) });\n }\n }\n })();\n return () => ctrl.abort();\n }\n}\n","import * as React from 'react';\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type FormEvent } from 'react';\nimport type { ChatMessage, ChatWidgetConfig, AgentInfo, Citation } from './types';\nimport { WidgetClient } from './api';\nimport './styles.css';\n\nconst DEFAULTS = {\n apiBaseUrl: 'https://api.docgenlab.com',\n position: 'bottom-right' as const,\n primary: '#4F46E5',\n primaryText: '#FFFFFF',\n radius: '14px',\n fontFamily: 'system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif',\n greeting: 'Hi — how can I help?',\n enableImageUpload: true,\n};\n\nexport interface DocGenLabChatProps extends ChatWidgetConfig {\n /** Called when the widget mounts and successfully resolves the agent. */\n onReady?: (agent: AgentInfo) => void;\n /** Called for unrecoverable errors so the customer can log them. */\n onError?: (err: string) => void;\n}\n\n/**\n * Floating-bubble chat widget. Renders a launcher button bottom-right\n * (or -left) that expands to a chat panel.\n */\nexport function DocGenLabChat(props: DocGenLabChatProps) {\n const baseUrl = props.apiBaseUrl || DEFAULTS.apiBaseUrl;\n const position = props.position || DEFAULTS.position;\n const enableImageUpload = props.enableImageUpload !== false;\n\n const themeVars: CSSProperties & Record<string, string> = {\n ['--dgl-primary' as any]: props.theme?.primary || DEFAULTS.primary,\n ['--dgl-primary-text' as any]: props.theme?.primaryText || DEFAULTS.primaryText,\n ['--dgl-radius' as any]: props.theme?.radius || DEFAULTS.radius,\n ['--dgl-font' as any]: props.theme?.fontFamily || DEFAULTS.fontFamily,\n };\n\n const clientRef = useRef<WidgetClient | null>(null);\n if (!clientRef.current) {\n clientRef.current = new WidgetClient({ agentKey: props.agentKey, apiBaseUrl: baseUrl });\n }\n const client = clientRef.current;\n\n const [open, setOpen] = useState(!!props.openOnLoad);\n // Expanded: roughly 1.6x the default, capped to viewport on small screens.\n // Toggled by the icon next to close in the header.\n const [expanded, setExpanded] = useState(false);\n const [agent, setAgent] = useState<AgentInfo | null>(null);\n const [agentError, setAgentError] = useState<string | null>(null);\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [input, setInput] = useState('');\n const [streaming, setStreaming] = useState(false);\n const [conversationId, setConversationId] = useState<string | null>(null);\n const [attachedFile, setAttachedFile] = useState<File | null>(null);\n const [attachedPreview, setAttachedPreview] = useState<string | null>(null);\n const [uploadingImage, setUploadingImage] = useState(false);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n const abortRef = useRef<(() => void) | null>(null);\n const contentRef = useRef('');\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Resolve agent on first open\n useEffect(() => {\n if (!open || agent) return;\n client.getAgent()\n .then((info) => {\n setAgent(info);\n props.onReady?.(info);\n })\n .catch((e: Error) => {\n setAgentError(e.message);\n props.onError?.(e.message);\n });\n }, [open, agent, client, props]);\n\n // Restore conversation_id if we have one in localStorage\n useEffect(() => {\n const k = `dgl-chat:${props.agentKey}`;\n const stored = localStorage.getItem(k);\n if (stored) setConversationId(stored);\n }, [props.agentKey]);\n\n // Persist conversation id whenever it changes\n useEffect(() => {\n if (conversationId) {\n localStorage.setItem(`dgl-chat:${props.agentKey}`, conversationId);\n }\n }, [conversationId, props.agentKey]);\n\n // Auto-scroll on new messages\n useEffect(() => {\n if (scrollRef.current) {\n scrollRef.current.scrollTop = scrollRef.current.scrollHeight;\n }\n }, [messages]);\n\n const clearAttachment = useCallback(() => {\n if (attachedPreview) URL.revokeObjectURL(attachedPreview);\n setAttachedFile(null);\n setAttachedPreview(null);\n }, [attachedPreview]);\n\n const stageImage = useCallback((file: File) => {\n if (!file.type.startsWith('image/')) return;\n const MAX = 10 * 1024 * 1024;\n if (file.size > MAX) return;\n if (attachedPreview) URL.revokeObjectURL(attachedPreview);\n setAttachedFile(file);\n setAttachedPreview(URL.createObjectURL(file));\n }, [attachedPreview]);\n\n const onSubmit = async (e: FormEvent) => {\n e.preventDefault();\n if (!input.trim() || streaming || uploadingImage) return;\n const userText = input.trim();\n\n // Upload image (if any) first\n let imagePath: string | undefined;\n let previewForTurn: string | undefined;\n if (attachedFile) {\n try {\n setUploadingImage(true);\n imagePath = await client.uploadImage(attachedFile);\n } catch (e: any) {\n props.onError?.(e?.message || 'Image upload failed');\n setUploadingImage(false);\n return;\n }\n setUploadingImage(false);\n previewForTurn = attachedPreview || undefined;\n }\n\n setInput('');\n contentRef.current = '';\n setMessages((prev) => [\n ...prev,\n { role: 'user', content: userText, imagePath, imagePreviewUrl: previewForTurn },\n { role: 'assistant', content: '', streaming: true },\n ]);\n setStreaming(true);\n // Clear the staged attachment; the turn owns the preview URL now\n setAttachedFile(null);\n setAttachedPreview(null);\n\n abortRef.current = client.streamChat(\n { message: userText, conversation_id: conversationId || undefined, image_path: imagePath },\n (evt) => {\n if (evt.type === 'conversation') {\n setConversationId(evt.conversation_id);\n } else if (evt.type === 'token') {\n contentRef.current += evt.content;\n const snap = contentRef.current;\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant' ? { ...m, content: snap } : m\n ));\n } else if (evt.type === 'citations') {\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant' ? { ...m, citations: evt.citations } : m\n ));\n } else if (evt.type === 'refused') {\n contentRef.current = evt.content;\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant'\n ? { ...m, content: evt.content, refused: true }\n : m\n ));\n } else if (evt.type === 'done') {\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant'\n ? { ...m, streaming: false, id: evt.message_id, cacheHit: evt.cache_hit }\n : m\n ));\n setStreaming(false);\n } else if (evt.type === 'error') {\n setMessages((prev) => prev.map((m, i) =>\n i === prev.length - 1 && m.role === 'assistant'\n ? { ...m, content: `Error: ${evt.error}`, streaming: false, refused: true }\n : m\n ));\n setStreaming(false);\n props.onError?.(evt.error);\n }\n },\n );\n };\n\n const newChat = () => {\n if (abortRef.current) abortRef.current();\n setMessages([]);\n setConversationId(null);\n setStreaming(false);\n localStorage.removeItem(`dgl-chat:${props.agentKey}`);\n clearAttachment();\n };\n\n const displayName = props.agentName || agent?.name || 'Assistant';\n\n return (\n <div className={`dgl-root dgl-pos-${position}`} style={themeVars}>\n {open && (\n <div className={`dgl-panel ${expanded ? 'dgl-panel-expanded' : ''}`}>\n <div className=\"dgl-header\">\n <div className=\"dgl-header-info\">\n {props.agentAvatarUrl && (\n <img src={props.agentAvatarUrl} alt=\"\" className=\"dgl-avatar\" />\n )}\n <div className=\"dgl-header-text\">\n <div className=\"dgl-title\">{displayName}</div>\n {agent?.org_name && (\n <div className=\"dgl-subtitle\">{agent.org_name}</div>\n )}\n </div>\n </div>\n <div className=\"dgl-header-actions\">\n <button\n type=\"button\"\n onClick={newChat}\n className=\"dgl-icon-btn\"\n title=\"New chat\"\n aria-label=\"New chat\"\n >\n {svgPlus()}\n </button>\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"dgl-icon-btn dgl-hide-on-mobile\"\n title={expanded ? 'Restore size' : 'Expand view'}\n aria-label={expanded ? 'Restore size' : 'Expand view'}\n >\n {expanded ? svgShrink() : svgExpand()}\n </button>\n <button\n type=\"button\"\n onClick={() => setOpen(false)}\n className=\"dgl-icon-btn\"\n title=\"Close\"\n aria-label=\"Close\"\n >\n {svgX()}\n </button>\n </div>\n </div>\n\n <div className=\"dgl-messages\" ref={scrollRef}>\n {messages.length === 0 && !agentError && (\n <div className=\"dgl-empty\">\n <div className=\"dgl-empty-text\">\n {props.greeting || DEFAULTS.greeting}\n </div>\n {agent?.description && (\n <div className=\"dgl-empty-sub\">{agent.description}</div>\n )}\n </div>\n )}\n {agentError && (\n <div className=\"dgl-error\">\n Couldn't load the assistant: {agentError}\n </div>\n )}\n {messages.map((m, idx) => (\n <MessageBubble\n key={idx}\n message={m}\n client={client}\n isLast={idx === messages.length - 1}\n agentAvatarUrl={props.agentAvatarUrl}\n onPickFollowup={(q) => {\n setInput(q);\n // Don't auto-send; user reviews + presses send.\n // Same UX as the dashboard.\n }}\n />\n ))}\n </div>\n\n <form className=\"dgl-composer\" onSubmit={onSubmit}>\n {attachedPreview && (\n <div className=\"dgl-staged-image\">\n <img src={attachedPreview} alt=\"attachment\" />\n <button\n type=\"button\"\n onClick={clearAttachment}\n className=\"dgl-staged-remove\"\n aria-label=\"Remove image\"\n >\n {svgX()}\n </button>\n </div>\n )}\n <div className=\"dgl-composer-row\">\n {enableImageUpload && (\n <>\n <button\n type=\"button\"\n className=\"dgl-icon-btn\"\n onClick={() => fileInputRef.current?.click()}\n disabled={streaming || uploadingImage}\n title=\"Attach image\"\n aria-label=\"Attach image\"\n >\n {svgImage()}\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/png,image/jpeg,image/webp,image/gif\"\n style={{ display: 'none' }}\n onChange={(e) => {\n const f = e.target.files?.[0];\n if (f) stageImage(f);\n if (e.target) e.target.value = '';\n }}\n />\n </>\n )}\n <input\n className=\"dgl-input\"\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder={\n streaming\n ? 'Thinking…'\n : attachedPreview\n ? 'Describe what to know about this image…'\n : 'Type your question…'\n }\n disabled={streaming}\n onPaste={(e) => {\n if (!enableImageUpload) return;\n const item = Array.from(e.clipboardData?.items || []).find(\n (it) => it.kind === 'file' && it.type.startsWith('image/')\n );\n if (item) {\n const f = item.getAsFile();\n if (f) { e.preventDefault(); stageImage(f); }\n }\n }}\n />\n <button\n type=\"submit\"\n className=\"dgl-send-btn\"\n disabled={!input.trim() || streaming || uploadingImage}\n aria-label=\"Send\"\n >\n {streaming || uploadingImage ? svgSpinner() : svgSend()}\n </button>\n </div>\n {!props.hideBranding && (\n <div className=\"dgl-branding\">\n Powered by <a href=\"https://docgenlab.com\" target=\"_blank\" rel=\"noopener noreferrer\">DocGenLab</a>\n </div>\n )}\n </form>\n </div>\n )}\n\n <button\n type=\"button\"\n className=\"dgl-launcher\"\n onClick={() => setOpen((v) => !v)}\n aria-label={open ? 'Close chat' : 'Open chat'}\n >\n {open ? svgChevronDown() : svgChat()}\n </button>\n </div>\n );\n}\n\n// ─── Message bubble ─────────────────────────────────────────────────────\n\nfunction MessageBubble({\n message,\n client,\n isLast,\n agentAvatarUrl,\n onPickFollowup,\n}: {\n message: ChatMessage;\n client: WidgetClient;\n isLast: boolean;\n agentAvatarUrl?: string;\n onPickFollowup: (q: string) => void;\n}) {\n if (message.role === 'user') {\n return (\n <div className=\"dgl-msg dgl-msg-user\">\n <div className=\"dgl-bubble dgl-bubble-user\">\n {(message.imagePath || message.imagePreviewUrl) && (\n <UserImage client={client} imagePath={message.imagePath} previewUrl={message.imagePreviewUrl} />\n )}\n {message.content}\n </div>\n </div>\n );\n }\n const cleaned = cleanForDisplay(message.content);\n const followups = !message.streaming ? extractFollowups(message.content) : [];\n return (\n <div className=\"dgl-msg dgl-msg-assistant\">\n {agentAvatarUrl ? (\n <img src={agentAvatarUrl} className=\"dgl-avatar\" alt=\"\" />\n ) : (\n <div className=\"dgl-avatar dgl-avatar-fallback\">{svgSparkles()}</div>\n )}\n <div className=\"dgl-msg-body\">\n {message.refused ? (\n <div className=\"dgl-refused\">{cleaned}</div>\n ) : (\n <div className=\"dgl-assistant-text\">\n {/* Tokens haven't started arriving yet — show thinking dots\n instead of an empty bubble. */}\n {message.streaming && !cleaned ? (\n <ThinkingDots />\n ) : (\n <>\n {renderMarkdown(cleaned)}\n {message.streaming && <span className=\"dgl-typing-cursor\">▍</span>}\n </>\n )}\n </div>\n )}\n {!message.streaming && !!message.citations?.length && (\n <CitationChips citations={message.citations} />\n )}\n {followups.length > 0 && (\n <FollowupChips suggestions={followups} onPick={onPickFollowup} />\n )}\n </div>\n </div>\n );\n}\n\n/** Clickable chips below an assistant answer suggesting next questions.\n * Clicking pre-fills the input — the user reviews + presses send. */\nfunction FollowupChips({\n suggestions,\n onPick,\n}: {\n suggestions: string[];\n onPick: (q: string) => void;\n}) {\n return (\n <div className=\"dgl-followups\">\n <div className=\"dgl-followups-label\">Continue exploring</div>\n <div className=\"dgl-followups-list\">\n {suggestions.map((q, i) => (\n <button\n key={i}\n type=\"button\"\n className=\"dgl-followup-chip\"\n onClick={() => onPick(q)}\n >\n {q}\n </button>\n ))}\n </div>\n </div>\n );\n}\n\nfunction UserImage({\n client,\n imagePath,\n previewUrl,\n}: { client: WidgetClient; imagePath?: string; previewUrl?: string }) {\n const [url, setUrl] = useState<string | null>(previewUrl || null);\n useEffect(() => {\n if (previewUrl || !imagePath) return;\n let revoke: string | null = null;\n client.fetchImage(imagePath)\n .then((u) => { revoke = u; setUrl(u); })\n .catch(() => setUrl(null));\n return () => { if (revoke) URL.revokeObjectURL(revoke); };\n }, [client, imagePath, previewUrl]);\n if (!url) return null;\n return (\n <a href={url} target=\"_blank\" rel=\"noopener noreferrer\" className=\"dgl-user-image-link\">\n <img src={url} className=\"dgl-user-image\" alt=\"attached\" />\n </a>\n );\n}\n\nfunction CitationChips({ citations }: { citations: Citation[] }) {\n // Dedupe by source — many chunks often come from the same doc, no need to spam.\n const seen = new Set<string>();\n const unique = citations.filter((c) => {\n if (seen.has(c.source_id)) return false;\n seen.add(c.source_id);\n return true;\n });\n if (!unique.length) return null;\n return (\n <div className=\"dgl-citations\">\n {unique.map((c) => (\n <span key={c.chunk_id} className=\"dgl-citation\" title={c.snippet}>\n {c.source_name}{c.page ? ` · p${c.page}` : ''}\n </span>\n ))}\n </div>\n );\n}\n\n/** Pull the suggested follow-up questions out of the trailing\n * <followups>...</followups> block (max 3, 4-120 chars each). Returns []\n * if the block is missing, mid-stream, or empty. */\nfunction extractFollowups(content: string): string[] {\n const match = content.match(/<followups>([\\s\\S]*?)<\\/followups>/i);\n if (!match) return [];\n return match[1]\n .split('\\n')\n .map((line) => line.trim())\n .map((line) => line.replace(/^[-*•\\d.)\\s\"']+|[\"']$/g, '').trim())\n .filter((line) => line.length >= 4 && line.length <= 120)\n .slice(0, 3);\n}\n\n/** Strip citation markers, [Section: ...] tags, [doc:abc...] refs, the\n * trailing <followups>...</followups> block, and any leftover orphan\n * punctuation. Ported from the dashboard's cleanCitations so the widget\n * matches the main app's chat exactly.\n *\n * Why this is needed: the system prompt asks the LLM not to emit inline\n * citations, but it sometimes leaks `[doc:11]` / `[1]` / etc. anyway. The\n * user-visible answer must be clean — citations render as separate chips. */\nfunction cleanForDisplay(text: string): string {\n return text\n .replace(/\\n*<followups>[\\s\\S]*?(<\\/followups>|$)/gi, '')\n .replace(/\\s*\\[\\s*Section:[^\\]]*\\]?/gi, '')\n .replace(/\\s*\\[\\s*doc:[^\\]]*\\]?/gi, '')\n .replace(/\\s*\\[\\s*(?:document|doc|source|ref|reference)[^\\]]*\\]?/gi, '')\n .replace(/\\s*\\[\\s*(?:p\\.?\\s*)?\\d+(?:\\s*[,–-]\\s*\\d+)*\\s*\\]/gi, '')\n .replace(/[,;]?\\s*doc:\\s*[0-9a-fA-F][0-9a-fA-F-]{6,}\\]?/gi, '')\n .replace(/[,;]\\s*(?:\\d+|doc:?\\s*[0-9a-fA-F-]+)\\s*\\]?/gi, '')\n .replace(/([\\w%])\\s*\\]/g, '$1')\n .replace(/\\s+([,.;:!?])/g, '$1')\n .replace(/[ \\t]+/g, ' ')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/** Minimal markdown → React renderer for assistant answers. Handles the\n * patterns the system prompt emits: **bold**, `inline code`, paragraph\n * breaks, and `-` / `*` / `1.` lists. Deliberately no full markdown\n * parser to keep the bundle small — for richer needs, the customer can\n * override by post-processing `onToken` themselves later. */\nfunction renderMarkdown(text: string): React.ReactNode {\n // Split into paragraphs first (blank-line separated)\n const blocks = text.split(/\\n{2,}/);\n return blocks.map((block, i) => {\n const lines = block.split('\\n');\n const listMatch = lines.every((l) => /^(\\s*)([-*]|\\d+\\.)\\s/.test(l));\n if (listMatch && lines.length > 0) {\n const isOrdered = /^\\d+\\./.test(lines[0].trim());\n const items = lines.map((l) => l.replace(/^\\s*(?:[-*]|\\d+\\.)\\s+/, ''));\n const Tag = isOrdered ? 'ol' : 'ul';\n return (\n <Tag key={i} className={`dgl-md-list ${isOrdered ? 'dgl-md-ol' : 'dgl-md-ul'}`}>\n {items.map((it, j) => (\n <li key={j}>{renderInline(it)}</li>\n ))}\n </Tag>\n );\n }\n return (\n <p key={i} className=\"dgl-md-p\">\n {block.split('\\n').map((line, j, arr) => (\n <React.Fragment key={j}>\n {renderInline(line)}\n {j < arr.length - 1 && <br />}\n </React.Fragment>\n ))}\n </p>\n );\n });\n}\n\n/** Inline markdown: **bold**, *italic*, `code`, [text](url). */\nfunction renderInline(text: string): React.ReactNode {\n // Tokenize. We avoid a full parser; this regex captures the supported\n // patterns in order of precedence (code first so it shields its contents).\n const pattern = /(`[^`]+`|\\*\\*[^*\\n]+\\*\\*|\\*[^*\\n]+\\*|\\[[^\\]]+\\]\\([^)]+\\))/g;\n const parts = text.split(pattern);\n return parts.map((part, idx) => {\n if (!part) return null;\n if (part.startsWith('`') && part.endsWith('`')) {\n return <code key={idx} className=\"dgl-md-code\">{part.slice(1, -1)}</code>;\n }\n if (part.startsWith('**') && part.endsWith('**')) {\n return <strong key={idx}>{part.slice(2, -2)}</strong>;\n }\n if (part.startsWith('*') && part.endsWith('*') && part.length > 2) {\n return <em key={idx}>{part.slice(1, -1)}</em>;\n }\n const linkMatch = part.match(/^\\[([^\\]]+)\\]\\(([^)]+)\\)$/);\n if (linkMatch) {\n return (\n <a key={idx} href={linkMatch[2]} target=\"_blank\" rel=\"noopener noreferrer\" className=\"dgl-md-link\">\n {linkMatch[1]}\n </a>\n );\n }\n return <React.Fragment key={idx}>{part}</React.Fragment>;\n });\n}\n\n/** Bouncing-dots loading indicator shown after Send but before the first\n * token arrives. Matches the dashboard's ThinkingDots cadence. */\nfunction ThinkingDots() {\n return (\n <span className=\"dgl-thinking\">\n <span className=\"dgl-thinking-dot\" />\n <span className=\"dgl-thinking-dot\" />\n <span className=\"dgl-thinking-dot\" />\n </span>\n );\n}\n\n// ─── Inline SVG icons (no external deps) ────────────────────────────────\n\nfunction svgChat() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"currentColor\">\n <path d=\"M4 4h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H8l-4 4V6a2 2 0 0 1 2-2z\" />\n </svg>\n );\n}\nfunction svgChevronDown() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"6 9 12 15 18 9\" />\n </svg>\n );\n}\nfunction svgX() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n </svg>\n );\n}\nfunction svgPlus() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n );\n}\nfunction svgSend() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\">\n <path d=\"M2 21l21-9L2 3v7l15 2-15 2z\" />\n </svg>\n );\n}\nfunction svgImage() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"18\" height=\"18\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n );\n}\nfunction svgSpinner() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" className=\"dgl-spin\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"3\" strokeLinecap=\"round\">\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n );\n}\nfunction svgExpand() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"15 3 21 3 21 9\" />\n <polyline points=\"9 21 3 21 3 15\" />\n <line x1=\"21\" y1=\"3\" x2=\"14\" y2=\"10\" />\n <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n </svg>\n );\n}\nfunction svgShrink() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"4 14 10 14 10 20\" />\n <polyline points=\"20 10 14 10 14 4\" />\n <line x1=\"14\" y1=\"10\" x2=\"21\" y2=\"3\" />\n <line x1=\"3\" y1=\"21\" x2=\"10\" y2=\"14\" />\n </svg>\n );\n}\nfunction svgSparkles() {\n return (\n <svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"currentColor\">\n <path d=\"M12 2l2.39 6.95L21 11l-6.61 2.05L12 20l-2.39-6.95L3 11l6.61-2.05L12 2z\" />\n </svg>\n );\n}\n","/**\n * @docgenlab.com/chat-widget — public entry.\n *\n * Two integration paths:\n *\n * 1. React (recommended for SPAs):\n *\n * import { DocGenLabChat } from '@docgenlab.com/chat-widget';\n * import '@docgenlab.com/chat-widget/style.css';\n *\n * <DocGenLabChat agentKey=\"pk_live_...\" />\n *\n * 2. Vanilla / script tag (WordPress, marketing pages, anything without\n * a build step). Use unpkg or jsdelivr to load the UMD bundle:\n *\n * <link rel=\"stylesheet\" href=\"https://unpkg.com/@docgenlab.com/chat-widget/dist/widget.css\" />\n * <script src=\"https://unpkg.com/@docgenlab.com/chat-widget\"></script>\n * <script>DocGenLab.init({ agentKey: 'pk_live_...' })</script>\n *\n * The UMD bundle exposes a global `DocGenLab` object with `init()` and\n * `destroy()` methods that mount/unmount the widget on a hidden div.\n */\n\nimport { createRoot, type Root } from 'react-dom/client';\nimport { createElement } from 'react';\nimport { DocGenLabChat, type DocGenLabChatProps } from './widget';\nimport type { ChatWidgetConfig, ChatWidgetTheme, ChatPosition, Citation, AgentInfo, StreamEvent, ChatMessage } from './types';\n\nexport { DocGenLabChat };\nexport type {\n DocGenLabChatProps,\n ChatWidgetConfig,\n ChatWidgetTheme,\n ChatPosition,\n Citation,\n AgentInfo,\n StreamEvent,\n ChatMessage,\n};\n\n// ─── Vanilla bootstrap ──────────────────────────────────────────────────\n\nlet _root: Root | null = null;\nlet _container: HTMLDivElement | null = null;\n\nexport function init(config: DocGenLabChatProps): void {\n if (typeof document === 'undefined') {\n throw new Error('DocGenLab.init() requires a browser environment.');\n }\n if (_root) {\n // Re-mount on second call (config change)\n _root.render(createElement(DocGenLabChat, config));\n return;\n }\n _container = document.createElement('div');\n _container.id = 'docgenlab-chat-widget-root';\n document.body.appendChild(_container);\n _root = createRoot(_container);\n _root.render(createElement(DocGenLabChat, config));\n}\n\nexport function destroy(): void {\n if (_root) {\n _root.unmount();\n _root = null;\n }\n if (_container) {\n _container.remove();\n _container = null;\n }\n}\n\n// Make sure the UMD global has these too. Vite assigns the default export to\n// the configured `name` ('DocGenLab'); explicitly attach methods so vanilla\n// HTML can use `DocGenLab.init(...)` without an import.\n// (This is a no-op when consumed via ESM imports.)\ndeclare global {\n interface Window {\n DocGenLab?: { init: typeof init; destroy: typeof destroy; DocGenLabChat: typeof DocGenLabChat };\n }\n}\nif (typeof window !== 'undefined') {\n window.DocGenLab = { init, destroy, DocGenLabChat };\n}\n"],"names":["PUBLIC_BASE","VISITOR_KEY","uuid","c","ch","r","getVisitorId","id","e","WidgetClient","opts","extra","resp","file","fd","detail","j","imagePath","url","blob","body","onEvent","ctrl","reader","decoder","buf","value","done","nl","raw","line","evt","DEFAULTS","DocGenLabChat","props","baseUrl","position","enableImageUpload","themeVars","_a","_b","_c","_d","clientRef","useRef","client","open","setOpen","useState","expanded","setExpanded","agent","setAgent","agentError","setAgentError","messages","setMessages","input","setInput","streaming","setStreaming","conversationId","setConversationId","attachedFile","setAttachedFile","attachedPreview","setAttachedPreview","uploadingImage","setUploadingImage","fileInputRef","abortRef","contentRef","scrollRef","useEffect","info","k","stored","clearAttachment","useCallback","stageImage","MAX","onSubmit","userText","previewForTurn","prev","snap","m","i","newChat","displayName","React","svgPlus","v","svgShrink","svgExpand","svgX","idx","MessageBubble","q","svgImage","f","item","it","svgSpinner","svgSend","svgChevronDown","svgChat","message","isLast","agentAvatarUrl","onPickFollowup","UserImage","cleaned","cleanForDisplay","followups","extractFollowups","svgSparkles","ThinkingDots","renderMarkdown","CitationChips","FollowupChips","suggestions","onPick","previewUrl","setUrl","revoke","u","citations","seen","unique","content","match","text","block","lines","l","isOrdered","items","Tag","renderInline","arr","pattern","part","linkMatch","_root","_container","init","config","createElement","createRoot","destroy"],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface of @docgenlab/chat-widget.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the backend's /api/v1/public/kb/* shape, kept minimal — anything
|
|
5
|
+
* the customer's app code might touch is exported.
|
|
6
|
+
*/
|
|
7
|
+
export interface ChatWidgetTheme {
|
|
8
|
+
/** Primary accent color (used for the bubble, send button, user-msg bg). */
|
|
9
|
+
primary?: string;
|
|
10
|
+
/** Text color used on top of the primary (usually white). */
|
|
11
|
+
primaryText?: string;
|
|
12
|
+
/** Border radius for the panel + bubble. Default '14px'. */
|
|
13
|
+
radius?: string;
|
|
14
|
+
/** Font family. Default falls back to system stack. */
|
|
15
|
+
fontFamily?: string;
|
|
16
|
+
}
|
|
17
|
+
export type ChatPosition = 'bottom-right' | 'bottom-left';
|
|
18
|
+
export interface ChatWidgetConfig {
|
|
19
|
+
/** Required. The pk_live_... publishable key for the target agent. */
|
|
20
|
+
agentKey: string;
|
|
21
|
+
/**
|
|
22
|
+
* Base URL of the DocGenLab API (without trailing slash). Defaults to
|
|
23
|
+
* https://api.docgenlab.com — override for self-hosted or staging.
|
|
24
|
+
*/
|
|
25
|
+
apiBaseUrl?: string;
|
|
26
|
+
/** Position of the floating launcher. Default 'bottom-right'. */
|
|
27
|
+
position?: ChatPosition;
|
|
28
|
+
/** Theme overrides. */
|
|
29
|
+
theme?: ChatWidgetTheme;
|
|
30
|
+
/**
|
|
31
|
+
* Greeting message shown above the input when the chat is empty.
|
|
32
|
+
* Default: "Hi — how can I help?".
|
|
33
|
+
*/
|
|
34
|
+
greeting?: string;
|
|
35
|
+
/** Override the agent display name. Default: the agent's name from /agent. */
|
|
36
|
+
agentName?: string;
|
|
37
|
+
/** URL of an avatar image shown next to assistant messages. */
|
|
38
|
+
agentAvatarUrl?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Allow end-users to attach an image to a message. Default true.
|
|
41
|
+
* Disable per-agent in cases where uploaded images would risk PII.
|
|
42
|
+
*/
|
|
43
|
+
enableImageUpload?: boolean;
|
|
44
|
+
/** Hide the "Powered by DocGenLab" footer. Paid tier only. */
|
|
45
|
+
hideBranding?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Auto-open the panel on first load? Default false. Useful for landing
|
|
48
|
+
* pages where chat IS the primary CTA.
|
|
49
|
+
*/
|
|
50
|
+
openOnLoad?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface Citation {
|
|
53
|
+
chunk_id: string;
|
|
54
|
+
source_id: string;
|
|
55
|
+
source_name: string;
|
|
56
|
+
score: number;
|
|
57
|
+
page?: number;
|
|
58
|
+
snippet?: string;
|
|
59
|
+
}
|
|
60
|
+
export type StreamEvent = {
|
|
61
|
+
type: 'conversation';
|
|
62
|
+
conversation_id: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'token';
|
|
65
|
+
content: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'citations';
|
|
68
|
+
citations: Citation[];
|
|
69
|
+
} | {
|
|
70
|
+
type: 'refused';
|
|
71
|
+
content: string;
|
|
72
|
+
} | {
|
|
73
|
+
type: 'done';
|
|
74
|
+
message_id?: string;
|
|
75
|
+
cache_hit?: boolean;
|
|
76
|
+
refused?: boolean;
|
|
77
|
+
model?: string;
|
|
78
|
+
} | {
|
|
79
|
+
type: 'error';
|
|
80
|
+
error: string;
|
|
81
|
+
code?: string;
|
|
82
|
+
};
|
|
83
|
+
export interface AgentInfo {
|
|
84
|
+
id: string;
|
|
85
|
+
name: string;
|
|
86
|
+
description?: string;
|
|
87
|
+
org_name?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface ChatMessage {
|
|
90
|
+
id?: string;
|
|
91
|
+
role: 'user' | 'assistant';
|
|
92
|
+
content: string;
|
|
93
|
+
citations?: Citation[];
|
|
94
|
+
imagePath?: string;
|
|
95
|
+
imagePreviewUrl?: string;
|
|
96
|
+
streaming?: boolean;
|
|
97
|
+
refused?: boolean;
|
|
98
|
+
cacheHit?: boolean;
|
|
99
|
+
}
|
package/dist/widget.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.dgl-root{position:fixed;z-index:2147483000;bottom:20px;font-family:var(--dgl-font);color-scheme:light;--dgl-bg: #ffffff;--dgl-fg: #1f2937;--dgl-muted: #6b7280;--dgl-border: #e5e7eb;--dgl-bubble-assistant: #f3f4f6}.dgl-pos-bottom-right{right:20px}.dgl-pos-bottom-left{left:20px}.dgl-launcher{background:var(--dgl-primary);color:var(--dgl-primary-text);border:none;width:56px;height:56px;border-radius:50%;cursor:pointer;box-shadow:0 6px 20px #0000002e;display:flex;align-items:center;justify-content:center;transition:transform .12s ease}.dgl-launcher:hover{transform:scale(1.06)}.dgl-launcher:focus-visible{outline:3px solid color-mix(in srgb,var(--dgl-primary) 50%,transparent)}.dgl-panel{position:absolute;bottom:72px;width:380px;height:min(600px,80vh);background:var(--dgl-bg);color:var(--dgl-fg);border:1px solid var(--dgl-border);border-radius:var(--dgl-radius);box-shadow:0 20px 50px #0003;display:flex;flex-direction:column;overflow:hidden;animation:dgl-pop .18s ease-out;transition:width .22s ease,height .22s ease;text-align:left}.dgl-panel-expanded{width:min(640px,calc(100vw - 40px));height:min(820px,calc(100vh - 100px))}.dgl-hide-on-mobile{display:flex}@media (max-width: 480px){.dgl-hide-on-mobile{display:none}}.dgl-pos-bottom-right .dgl-panel{right:0}.dgl-pos-bottom-left .dgl-panel{left:0}@keyframes dgl-pop{0%{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}.dgl-header{background:var(--dgl-primary);color:var(--dgl-primary-text);padding:12px 14px;display:flex;align-items:center;justify-content:space-between;gap:8px}.dgl-header-info{display:flex;align-items:center;gap:10px;min-width:0}.dgl-header-text{min-width:0}.dgl-title{font-weight:600;font-size:14px;line-height:1.2}.dgl-subtitle{font-size:11px;opacity:.85;margin-top:2px}.dgl-header-actions{display:flex;gap:4px}.dgl-avatar{width:32px;height:32px;border-radius:50%;object-fit:cover;flex-shrink:0}.dgl-avatar-fallback{background:#fff3;color:var(--dgl-primary-text);display:flex;align-items:center;justify-content:center}.dgl-icon-btn{background:transparent;border:none;color:inherit;width:28px;height:28px;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:.85;transition:background .12s ease,opacity .12s ease}.dgl-icon-btn:hover{background:#0000000f;opacity:1}.dgl-icon-btn:disabled{cursor:not-allowed;opacity:.4}.dgl-header .dgl-icon-btn:hover{background:#ffffff26}.dgl-messages{flex:1;overflow-y:auto;padding:14px;background:#fafbfc;display:flex;flex-direction:column;gap:10px}.dgl-empty{margin:auto 0;text-align:center;color:var(--dgl-muted);padding:20px}.dgl-empty-text{font-size:14px;font-weight:500;color:var(--dgl-fg);margin-bottom:6px}.dgl-empty-sub{font-size:12px}.dgl-error{margin:auto 0;text-align:center;color:#b91c1c;background:#fee2e2;padding:10px;border-radius:8px;font-size:12px}.dgl-msg{display:flex;gap:8px}.dgl-msg-user{justify-content:flex-end}.dgl-msg-assistant{justify-content:flex-start}.dgl-msg-assistant .dgl-avatar{width:26px;height:26px;align-self:flex-start;margin-top:2px}.dgl-msg-body{flex:1;min-width:0;max-width:88%}.dgl-bubble{padding:9px 12px;border-radius:14px;font-size:13.5px;line-height:1.45;white-space:pre-wrap;word-wrap:break-word}.dgl-bubble-user{background:var(--dgl-primary);color:var(--dgl-primary-text);border-bottom-right-radius:4px;max-width:78%}.dgl-assistant-text{color:var(--dgl-fg);font-size:13.5px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word}.dgl-refused{background:#fef3c7;border:1px solid #fde68a;color:#92400e;padding:8px 12px;border-radius:12px;font-size:13px}.dgl-user-image-link{display:block;margin-bottom:6px}.dgl-user-image{max-width:100%;max-height:200px;border-radius:8px;border:1px solid rgba(255,255,255,.2);background:#ffffff1a;object-fit:contain}.dgl-typing-cursor{color:var(--dgl-muted);animation:dgl-blink 1s steps(2) infinite}@keyframes dgl-blink{50%{opacity:0}}.dgl-thinking{display:inline-flex;gap:4px;align-items:center;padding:6px 0}.dgl-thinking-dot{width:6px;height:6px;border-radius:50%;background:var(--dgl-muted);animation:dgl-bounce 1.2s infinite ease-in-out}.dgl-thinking-dot:nth-child(2){animation-delay:.15s}.dgl-thinking-dot:nth-child(3){animation-delay:.3s}@keyframes dgl-bounce{0%,80%,to{transform:scale(.6);opacity:.4}40%{transform:scale(1);opacity:1}}.dgl-md-p{margin:6px 0;line-height:1.55;word-wrap:break-word}.dgl-md-p:first-child{margin-top:0}.dgl-md-p:last-child{margin-bottom:0}.dgl-md-list{margin:6px 0;padding-left:20px}.dgl-md-list li{margin:3px 0;line-height:1.5}.dgl-md-ul{list-style:disc}.dgl-md-ol{list-style:decimal}.dgl-md-code{background:#f3f4f6;color:#1f2937;padding:1px 5px;border-radius:4px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:12px}.dgl-md-link{color:var(--dgl-primary);text-decoration:underline}.dgl-citations{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.dgl-citation{font-size:10.5px;background:#eef2ff;color:#4338ca;padding:2px 8px;border-radius:999px;border:1px solid #c7d2fe;cursor:default}.dgl-followups{margin-top:10px}.dgl-followups-label{font-size:9.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--dgl-muted);margin-bottom:5px}.dgl-followups-list{display:flex;flex-wrap:wrap;gap:4px}.dgl-followup-chip{font-size:11.5px;background:#fff;color:var(--dgl-fg);border:1px solid var(--dgl-border);padding:4px 10px;border-radius:999px;cursor:pointer;transition:background .12s ease,border-color .12s ease;font-family:inherit;text-align:left}.dgl-followup-chip:hover{background:#eef2ff;border-color:#c7d2fe;color:var(--dgl-primary)}.dgl-composer{border-top:1px solid var(--dgl-border);padding:10px 12px 8px;background:#fff}.dgl-staged-image{display:flex;align-items:center;gap:6px;margin-bottom:8px;padding:4px 6px;background:#f3f4f6;border-radius:8px;border:1px solid var(--dgl-border);position:relative}.dgl-staged-image img{width:42px;height:42px;object-fit:cover;border-radius:4px}.dgl-staged-remove{background:#0009;color:#fff;border:none;width:18px;height:18px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;margin-left:auto}.dgl-composer-row{display:flex;align-items:center;gap:6px;border:1px solid var(--dgl-border);border-radius:10px;padding:4px 6px;transition:border-color .12s ease}.dgl-composer-row:focus-within{border-color:var(--dgl-primary)}.dgl-input{flex:1;border:none;outline:none;background:transparent;color:var(--dgl-fg);font:inherit;font-size:13.5px;padding:6px 4px}.dgl-input::placeholder{color:var(--dgl-muted)}.dgl-send-btn{background:var(--dgl-primary);color:var(--dgl-primary-text);border:none;width:32px;height:32px;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center}.dgl-send-btn:disabled{background:#d1d5db;cursor:not-allowed}.dgl-branding{margin-top:6px;font-size:10px;color:var(--dgl-muted);text-align:center}.dgl-branding a{color:var(--dgl-muted);text-decoration:none}.dgl-branding a:hover{text-decoration:underline}.dgl-spin{animation:dgl-spin .9s linear infinite}@keyframes dgl-spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (max-width: 480px){.dgl-panel{position:fixed;bottom:0;left:0;right:0;width:100vw;height:90vh;border-radius:14px 14px 0 0}.dgl-pos-bottom-right .dgl-panel,.dgl-pos-bottom-left .dgl-panel{left:0;right:0}}
|
package/dist/widget.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ChatWidgetConfig, AgentInfo } from './types';
|
|
3
|
+
import './styles.css';
|
|
4
|
+
export interface DocGenLabChatProps extends ChatWidgetConfig {
|
|
5
|
+
/** Called when the widget mounts and successfully resolves the agent. */
|
|
6
|
+
onReady?: (agent: AgentInfo) => void;
|
|
7
|
+
/** Called for unrecoverable errors so the customer can log them. */
|
|
8
|
+
onError?: (err: string) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Floating-bubble chat widget. Renders a launcher button bottom-right
|
|
12
|
+
* (or -left) that expands to a chat panel.
|
|
13
|
+
*/
|
|
14
|
+
export declare function DocGenLabChat(props: DocGenLabChatProps): React.JSX.Element;
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@docgenlab.com/chat-widget",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Embeddable AI knowledge chat widget for DocGenLab — drop into any site to give end-users a grounded RAG assistant.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.umd.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.umd.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./style.css": "./dist/widget.css"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "vite",
|
|
22
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": ">=18.0.0",
|
|
27
|
+
"react-dom": ">=18.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/react": "^18.2.0",
|
|
31
|
+
"@types/react-dom": "^18.2.0",
|
|
32
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
33
|
+
"typescript": "^5.4.0",
|
|
34
|
+
"vite": "^5.4.0"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"rag",
|
|
38
|
+
"ai",
|
|
39
|
+
"chat",
|
|
40
|
+
"knowledge-base",
|
|
41
|
+
"widget",
|
|
42
|
+
"embeddable",
|
|
43
|
+
"docgenlab"
|
|
44
|
+
],
|
|
45
|
+
"license": "UNLICENSED",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/docgenlab/docgenie"
|
|
49
|
+
}
|
|
50
|
+
}
|