@informedai/react 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,995 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ InformedAIClient: () => InformedAIClient,
24
+ InformedAIProvider: () => InformedAIProvider,
25
+ InformedAssistant: () => InformedAssistant,
26
+ useDocument: () => useDocument,
27
+ useInformedAI: () => useInformedAI,
28
+ useSession: () => useSession
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/components/InformedAssistant.tsx
33
+ var import_react2 = require("react");
34
+
35
+ // src/context/InformedAIContext.tsx
36
+ var import_react = require("react");
37
+
38
+ // src/utils/api-client.ts
39
+ var DEFAULT_API_URL = "https://api.informedassistant.ai/api/v1";
40
+ var InformedAIClient = class {
41
+ constructor(apiKey, apiUrl) {
42
+ this.apiKey = apiKey;
43
+ this.apiUrl = apiUrl || DEFAULT_API_URL;
44
+ }
45
+ getHeaders() {
46
+ return {
47
+ "Content-Type": "application/json",
48
+ "Authorization": `Bearer ${this.apiKey}`
49
+ };
50
+ }
51
+ async request(endpoint, options = {}) {
52
+ const response = await fetch(`${this.apiUrl}${endpoint}`, {
53
+ ...options,
54
+ headers: {
55
+ ...this.getHeaders(),
56
+ ...options.headers
57
+ }
58
+ });
59
+ if (!response.ok) {
60
+ const error = await response.json().catch(() => ({ error: "Request failed" }));
61
+ throw new Error(error.error || `HTTP ${response.status}`);
62
+ }
63
+ if (response.status === 204) {
64
+ return {};
65
+ }
66
+ return response.json();
67
+ }
68
+ // ========================================================================
69
+ // Document Operations
70
+ // ========================================================================
71
+ async getDocument(id) {
72
+ return this.request(`/documents/${id}`);
73
+ }
74
+ async getDocumentType(id) {
75
+ return this.request(`/document-types/${id}`);
76
+ }
77
+ async updateDocumentField(documentId, field, value) {
78
+ return this.request(`/documents/${documentId}/field`, {
79
+ method: "PATCH",
80
+ body: JSON.stringify({ field, value })
81
+ });
82
+ }
83
+ // ========================================================================
84
+ // Session Operations
85
+ // ========================================================================
86
+ async createSession(documentId) {
87
+ return this.request("/sessions", {
88
+ method: "POST",
89
+ body: JSON.stringify({ documentId })
90
+ });
91
+ }
92
+ async getSession(id) {
93
+ return this.request(`/sessions/${id}`);
94
+ }
95
+ async deleteSession(id) {
96
+ await this.request(`/sessions/${id}`, { method: "DELETE" });
97
+ }
98
+ /**
99
+ * Send a message to the session with SSE streaming
100
+ */
101
+ async sendMessage(sessionId, message, onEvent) {
102
+ const response = await fetch(`${this.apiUrl}/sessions/${sessionId}/message`, {
103
+ method: "POST",
104
+ headers: this.getHeaders(),
105
+ body: JSON.stringify({ message })
106
+ });
107
+ if (!response.ok) {
108
+ const error = await response.json().catch(() => ({ error: "Request failed" }));
109
+ throw new Error(error.error || `HTTP ${response.status}`);
110
+ }
111
+ await this.processSSEStream(response, onEvent);
112
+ }
113
+ /**
114
+ * Send a quick action to the session with SSE streaming
115
+ */
116
+ async sendQuickAction(sessionId, action, payload, onEvent) {
117
+ const response = await fetch(`${this.apiUrl}/sessions/${sessionId}/quick-action`, {
118
+ method: "POST",
119
+ headers: this.getHeaders(),
120
+ body: JSON.stringify({ action, payload })
121
+ });
122
+ if (!response.ok) {
123
+ const error = await response.json().catch(() => ({ error: "Request failed" }));
124
+ throw new Error(error.error || `HTTP ${response.status}`);
125
+ }
126
+ let finalSession = null;
127
+ await this.processSSEStream(response, (event) => {
128
+ onEvent?.(event);
129
+ if (event.session) {
130
+ finalSession = event.session;
131
+ }
132
+ });
133
+ if (!finalSession) {
134
+ throw new Error("No session returned from quick action");
135
+ }
136
+ return finalSession;
137
+ }
138
+ /**
139
+ * Apply the pending value for the active task
140
+ */
141
+ async applyPendingValue(sessionId) {
142
+ return this.request(`/sessions/${sessionId}/apply`, {
143
+ method: "POST"
144
+ });
145
+ }
146
+ /**
147
+ * Skip the active task
148
+ */
149
+ async skipTask(sessionId) {
150
+ return this.request(`/sessions/${sessionId}/skip`, {
151
+ method: "POST"
152
+ });
153
+ }
154
+ // ========================================================================
155
+ // SSE Stream Processing
156
+ // ========================================================================
157
+ async processSSEStream(response, onEvent) {
158
+ const reader = response.body?.getReader();
159
+ if (!reader) {
160
+ throw new Error("No response body");
161
+ }
162
+ const decoder = new TextDecoder();
163
+ let buffer = "";
164
+ try {
165
+ while (true) {
166
+ const { done, value } = await reader.read();
167
+ if (done) break;
168
+ buffer += decoder.decode(value, { stream: true });
169
+ const lines = buffer.split("\n");
170
+ buffer = lines.pop() || "";
171
+ for (const line of lines) {
172
+ if (line.startsWith("data: ")) {
173
+ try {
174
+ const data = JSON.parse(line.slice(6));
175
+ onEvent(data);
176
+ } catch {
177
+ }
178
+ }
179
+ }
180
+ }
181
+ if (buffer.startsWith("data: ")) {
182
+ try {
183
+ const data = JSON.parse(buffer.slice(6));
184
+ onEvent(data);
185
+ } catch {
186
+ }
187
+ }
188
+ } finally {
189
+ reader.releaseLock();
190
+ }
191
+ }
192
+ };
193
+
194
+ // src/context/InformedAIContext.tsx
195
+ var import_jsx_runtime = require("react/jsx-runtime");
196
+ var InformedAIContext = (0, import_react.createContext)(null);
197
+ function useInformedAI() {
198
+ const context = (0, import_react.useContext)(InformedAIContext);
199
+ if (!context) {
200
+ throw new Error("useInformedAI must be used within an InformedAIProvider");
201
+ }
202
+ return context;
203
+ }
204
+ function InformedAIProvider({ config, children }) {
205
+ const [session, setSession] = (0, import_react.useState)(null);
206
+ const [document, setDocument] = (0, import_react.useState)(null);
207
+ const [documentType, setDocumentType] = (0, import_react.useState)(null);
208
+ const [isLoading, setIsLoading] = (0, import_react.useState)(true);
209
+ const [isStreaming, setIsStreaming] = (0, import_react.useState)(false);
210
+ const [error, setError] = (0, import_react.useState)(null);
211
+ const [streamingContent, setStreamingContent] = (0, import_react.useState)("");
212
+ const clientRef = (0, import_react.useRef)(null);
213
+ (0, import_react.useEffect)(() => {
214
+ clientRef.current = new InformedAIClient(config.apiKey, config.apiUrl);
215
+ }, [config.apiKey, config.apiUrl]);
216
+ (0, import_react.useEffect)(() => {
217
+ async function initialize() {
218
+ if (!clientRef.current) return;
219
+ try {
220
+ setIsLoading(true);
221
+ setError(null);
222
+ const doc = await clientRef.current.getDocument(config.documentId);
223
+ setDocument(doc);
224
+ const dt = await clientRef.current.getDocumentType(doc.documentTypeId);
225
+ setDocumentType(dt);
226
+ let sess;
227
+ if (config.sessionId) {
228
+ sess = await clientRef.current.getSession(config.sessionId);
229
+ } else {
230
+ sess = await clientRef.current.createSession(config.documentId);
231
+ }
232
+ setSession(sess);
233
+ config.onSessionChange?.(sess);
234
+ } catch (err) {
235
+ const error2 = err instanceof Error ? err : new Error("Initialization failed");
236
+ setError(error2);
237
+ config.onError?.(error2);
238
+ } finally {
239
+ setIsLoading(false);
240
+ }
241
+ }
242
+ initialize();
243
+ }, [config.documentId, config.sessionId]);
244
+ const handleSSEEvent = (0, import_react.useCallback)((event) => {
245
+ if (event.type === "content" && event.content) {
246
+ setStreamingContent((prev) => prev + event.content);
247
+ }
248
+ if (event.type === "done" || event.type === "session_update") {
249
+ if (event.session) {
250
+ setSession(event.session);
251
+ config.onSessionChange?.(event.session);
252
+ if (event.session.activeTask) {
253
+ const taskState = event.session.taskStates[event.session.activeTask];
254
+ if (taskState?.pendingValue !== void 0) {
255
+ }
256
+ }
257
+ }
258
+ setStreamingContent("");
259
+ setIsStreaming(false);
260
+ }
261
+ if (event.type === "error") {
262
+ const error2 = new Error(event.error || "Stream error");
263
+ setError(error2);
264
+ config.onError?.(error2);
265
+ setIsStreaming(false);
266
+ }
267
+ }, [config]);
268
+ const sendMessage = (0, import_react.useCallback)(async (message) => {
269
+ if (!clientRef.current || !session) return;
270
+ try {
271
+ setIsStreaming(true);
272
+ setStreamingContent("");
273
+ setError(null);
274
+ await clientRef.current.sendMessage(session.id, message, handleSSEEvent);
275
+ } catch (err) {
276
+ const error2 = err instanceof Error ? err : new Error("Failed to send message");
277
+ setError(error2);
278
+ config.onError?.(error2);
279
+ setIsStreaming(false);
280
+ }
281
+ }, [session, handleSSEEvent, config]);
282
+ const sendQuickAction = (0, import_react.useCallback)(async (action, payload) => {
283
+ if (!clientRef.current || !session) return;
284
+ try {
285
+ setIsStreaming(true);
286
+ setStreamingContent("");
287
+ setError(null);
288
+ const newSession = await clientRef.current.sendQuickAction(
289
+ session.id,
290
+ action,
291
+ payload,
292
+ handleSSEEvent
293
+ );
294
+ setSession(newSession);
295
+ config.onSessionChange?.(newSession);
296
+ } catch (err) {
297
+ const error2 = err instanceof Error ? err : new Error("Failed to send quick action");
298
+ setError(error2);
299
+ config.onError?.(error2);
300
+ } finally {
301
+ setIsStreaming(false);
302
+ }
303
+ }, [session, handleSSEEvent, config]);
304
+ const applyPendingValue = (0, import_react.useCallback)(async () => {
305
+ if (!clientRef.current || !session) return;
306
+ try {
307
+ setError(null);
308
+ const activeTask = session.activeTask;
309
+ const pendingValue = activeTask ? session.taskStates[activeTask]?.pendingValue : void 0;
310
+ const newSession = await clientRef.current.applyPendingValue(session.id);
311
+ setSession(newSession);
312
+ config.onSessionChange?.(newSession);
313
+ if (activeTask && pendingValue !== void 0) {
314
+ config.onFieldApply?.(activeTask, pendingValue);
315
+ }
316
+ } catch (err) {
317
+ const error2 = err instanceof Error ? err : new Error("Failed to apply value");
318
+ setError(error2);
319
+ config.onError?.(error2);
320
+ }
321
+ }, [session, config]);
322
+ const skipTask = (0, import_react.useCallback)(async () => {
323
+ if (!clientRef.current || !session) return;
324
+ try {
325
+ setError(null);
326
+ const newSession = await clientRef.current.skipTask(session.id);
327
+ setSession(newSession);
328
+ config.onSessionChange?.(newSession);
329
+ } catch (err) {
330
+ const error2 = err instanceof Error ? err : new Error("Failed to skip task");
331
+ setError(error2);
332
+ config.onError?.(error2);
333
+ }
334
+ }, [session, config]);
335
+ const clearError = (0, import_react.useCallback)(() => {
336
+ setError(null);
337
+ }, []);
338
+ const value = {
339
+ session,
340
+ document,
341
+ documentType,
342
+ isLoading,
343
+ isStreaming,
344
+ error,
345
+ streamingContent,
346
+ sendMessage,
347
+ sendQuickAction,
348
+ applyPendingValue,
349
+ skipTask,
350
+ clearError
351
+ };
352
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InformedAIContext.Provider, { value, children });
353
+ }
354
+
355
+ // src/components/InformedAssistant.tsx
356
+ var import_jsx_runtime2 = require("react/jsx-runtime");
357
+ var defaultTheme = {
358
+ primaryColor: "#f59e0b",
359
+ // Amber
360
+ backgroundColor: "#ffffff",
361
+ textColor: "#1c1917",
362
+ borderRadius: "12px",
363
+ fontFamily: "system-ui, -apple-system, sans-serif"
364
+ };
365
+ function InformedAssistant({ className, ...config }) {
366
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(InformedAIProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
367
+ AssistantWidget,
368
+ {
369
+ className,
370
+ theme: { ...defaultTheme, ...config.theme },
371
+ position: config.position,
372
+ defaultCollapsed: config.defaultCollapsed
373
+ }
374
+ ) });
375
+ }
376
+ function AssistantWidget({ className, theme, position = "inline", defaultCollapsed = false }) {
377
+ const {
378
+ session,
379
+ documentType,
380
+ isLoading,
381
+ isStreaming,
382
+ error,
383
+ streamingContent,
384
+ sendMessage,
385
+ sendQuickAction,
386
+ applyPendingValue,
387
+ skipTask
388
+ } = useInformedAI();
389
+ const [isCollapsed, setIsCollapsed] = (0, import_react2.useState)(defaultCollapsed);
390
+ const [inputValue, setInputValue] = (0, import_react2.useState)("");
391
+ const messagesEndRef = (0, import_react2.useRef)(null);
392
+ (0, import_react2.useEffect)(() => {
393
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
394
+ }, [session?.widgetMessages, streamingContent]);
395
+ const handleSubmit = async (e) => {
396
+ e.preventDefault();
397
+ if (!inputValue.trim() || isStreaming) return;
398
+ const message = inputValue.trim();
399
+ setInputValue("");
400
+ await sendMessage(message);
401
+ };
402
+ const handleQuickAction = async (action) => {
403
+ await sendQuickAction(action.action, action.payload);
404
+ };
405
+ const activeTask = session?.activeTask;
406
+ const pendingValue = activeTask ? session?.taskStates[activeTask]?.pendingValue : void 0;
407
+ const hasPendingValue = pendingValue !== void 0 && pendingValue !== null;
408
+ const cssVars = {
409
+ "--ia-primary": theme.primaryColor,
410
+ "--ia-bg": theme.backgroundColor,
411
+ "--ia-text": theme.textColor,
412
+ "--ia-radius": theme.borderRadius,
413
+ "--ia-font": theme.fontFamily
414
+ };
415
+ const positionStyles = position === "inline" ? {} : {
416
+ position: "fixed",
417
+ [position === "bottom-right" ? "right" : "left"]: "20px",
418
+ bottom: "20px",
419
+ zIndex: 9999
420
+ };
421
+ if (isLoading) {
422
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
423
+ "div",
424
+ {
425
+ className,
426
+ style: {
427
+ ...cssVars,
428
+ ...positionStyles,
429
+ width: position === "inline" ? "100%" : "380px",
430
+ backgroundColor: "var(--ia-bg)",
431
+ borderRadius: "var(--ia-radius)",
432
+ border: "1px solid #e5e5e5",
433
+ fontFamily: "var(--ia-font)",
434
+ padding: "24px",
435
+ textAlign: "center",
436
+ color: "#737373"
437
+ },
438
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", gap: "8px" }, children: [
439
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LoadingSpinner, {}),
440
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Loading assistant..." })
441
+ ] })
442
+ }
443
+ );
444
+ }
445
+ if (error) {
446
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
447
+ "div",
448
+ {
449
+ className,
450
+ style: {
451
+ ...cssVars,
452
+ ...positionStyles,
453
+ width: position === "inline" ? "100%" : "380px",
454
+ backgroundColor: "var(--ia-bg)",
455
+ borderRadius: "var(--ia-radius)",
456
+ border: "1px solid #fecaca",
457
+ fontFamily: "var(--ia-font)",
458
+ padding: "24px",
459
+ textAlign: "center",
460
+ color: "#dc2626"
461
+ },
462
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { style: { margin: 0 }, children: error.message })
463
+ }
464
+ );
465
+ }
466
+ if (isCollapsed && position !== "inline") {
467
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
468
+ "button",
469
+ {
470
+ onClick: () => setIsCollapsed(false),
471
+ style: {
472
+ ...positionStyles,
473
+ width: "56px",
474
+ height: "56px",
475
+ borderRadius: "50%",
476
+ backgroundColor: theme.primaryColor,
477
+ border: "none",
478
+ cursor: "pointer",
479
+ display: "flex",
480
+ alignItems: "center",
481
+ justifyContent: "center",
482
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)"
483
+ },
484
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChatIcon, { color: "#fff" })
485
+ }
486
+ );
487
+ }
488
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
489
+ "div",
490
+ {
491
+ className,
492
+ style: {
493
+ ...cssVars,
494
+ ...positionStyles,
495
+ width: position === "inline" ? "100%" : "380px",
496
+ height: position === "inline" ? "auto" : "500px",
497
+ maxHeight: position === "inline" ? "600px" : "500px",
498
+ backgroundColor: "var(--ia-bg)",
499
+ borderRadius: "var(--ia-radius)",
500
+ border: "1px solid #e5e5e5",
501
+ fontFamily: "var(--ia-font)",
502
+ display: "flex",
503
+ flexDirection: "column",
504
+ overflow: "hidden",
505
+ boxShadow: position !== "inline" ? "0 4px 24px rgba(0,0,0,0.12)" : void 0
506
+ },
507
+ children: [
508
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
509
+ "div",
510
+ {
511
+ style: {
512
+ padding: "16px",
513
+ borderBottom: "1px solid #e5e5e5",
514
+ display: "flex",
515
+ alignItems: "center",
516
+ justifyContent: "space-between"
517
+ },
518
+ children: [
519
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
520
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SparklesIcon, { color: theme.primaryColor }),
521
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontWeight: 600, color: "var(--ia-text)" }, children: "InformedAI Assistant" })
522
+ ] }),
523
+ position !== "inline" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
524
+ "button",
525
+ {
526
+ onClick: () => setIsCollapsed(true),
527
+ style: {
528
+ background: "none",
529
+ border: "none",
530
+ cursor: "pointer",
531
+ padding: "4px",
532
+ color: "#737373"
533
+ },
534
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MinimizeIcon, {})
535
+ }
536
+ )
537
+ ]
538
+ }
539
+ ),
540
+ activeTask && documentType && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
541
+ "div",
542
+ {
543
+ style: {
544
+ padding: "8px 16px",
545
+ backgroundColor: `${theme.primaryColor}15`,
546
+ borderBottom: "1px solid #e5e5e5",
547
+ fontSize: "13px"
548
+ },
549
+ children: [
550
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "#737373" }, children: "Working on: " }),
551
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontWeight: 500, color: theme.primaryColor }, children: documentType.schema.fields[activeTask]?.label || activeTask })
552
+ ]
553
+ }
554
+ ),
555
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
556
+ "div",
557
+ {
558
+ style: {
559
+ flex: 1,
560
+ overflowY: "auto",
561
+ padding: "16px",
562
+ display: "flex",
563
+ flexDirection: "column",
564
+ gap: "12px"
565
+ },
566
+ children: [
567
+ session?.widgetMessages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
568
+ MessageBubble,
569
+ {
570
+ message: msg,
571
+ theme,
572
+ onQuickAction: handleQuickAction
573
+ },
574
+ msg.id
575
+ )),
576
+ isStreaming && streamingContent && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
577
+ "div",
578
+ {
579
+ style: {
580
+ padding: "12px 16px",
581
+ backgroundColor: "#f5f5f5",
582
+ borderRadius: "12px",
583
+ borderBottomLeftRadius: "4px",
584
+ color: "var(--ia-text)",
585
+ fontSize: "14px",
586
+ lineHeight: 1.5,
587
+ whiteSpace: "pre-wrap"
588
+ },
589
+ children: [
590
+ streamingContent,
591
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { opacity: 0.5 }, children: "\u258A" })
592
+ ]
593
+ }
594
+ ),
595
+ isStreaming && !streamingContent && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px", color: "#737373" }, children: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LoadingSpinner, { size: 16 }),
597
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: "14px" }, children: "Thinking..." })
598
+ ] }),
599
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: messagesEndRef })
600
+ ]
601
+ }
602
+ ),
603
+ hasPendingValue && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
604
+ "div",
605
+ {
606
+ style: {
607
+ padding: "12px 16px",
608
+ borderTop: "1px solid #e5e5e5",
609
+ backgroundColor: `${theme.primaryColor}10`,
610
+ display: "flex",
611
+ gap: "8px"
612
+ },
613
+ children: [
614
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
615
+ "button",
616
+ {
617
+ onClick: applyPendingValue,
618
+ disabled: isStreaming,
619
+ style: {
620
+ flex: 1,
621
+ padding: "10px 16px",
622
+ backgroundColor: theme.primaryColor,
623
+ color: "#fff",
624
+ border: "none",
625
+ borderRadius: "8px",
626
+ fontWeight: 500,
627
+ cursor: "pointer",
628
+ fontSize: "14px",
629
+ opacity: isStreaming ? 0.5 : 1
630
+ },
631
+ children: "Apply"
632
+ }
633
+ ),
634
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
635
+ "button",
636
+ {
637
+ onClick: skipTask,
638
+ disabled: isStreaming,
639
+ style: {
640
+ padding: "10px 16px",
641
+ backgroundColor: "transparent",
642
+ color: "#737373",
643
+ border: "1px solid #e5e5e5",
644
+ borderRadius: "8px",
645
+ fontWeight: 500,
646
+ cursor: "pointer",
647
+ fontSize: "14px",
648
+ opacity: isStreaming ? 0.5 : 1
649
+ },
650
+ children: "Skip"
651
+ }
652
+ )
653
+ ]
654
+ }
655
+ ),
656
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
657
+ "form",
658
+ {
659
+ onSubmit: handleSubmit,
660
+ style: {
661
+ padding: "12px 16px",
662
+ borderTop: "1px solid #e5e5e5",
663
+ display: "flex",
664
+ gap: "8px"
665
+ },
666
+ children: [
667
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
668
+ "input",
669
+ {
670
+ type: "text",
671
+ value: inputValue,
672
+ onChange: (e) => setInputValue(e.target.value),
673
+ placeholder: "Type a message...",
674
+ disabled: isStreaming,
675
+ style: {
676
+ flex: 1,
677
+ padding: "10px 14px",
678
+ border: "1px solid #e5e5e5",
679
+ borderRadius: "8px",
680
+ fontSize: "14px",
681
+ outline: "none",
682
+ fontFamily: "inherit"
683
+ }
684
+ }
685
+ ),
686
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
687
+ "button",
688
+ {
689
+ type: "submit",
690
+ disabled: !inputValue.trim() || isStreaming,
691
+ style: {
692
+ padding: "10px 16px",
693
+ backgroundColor: theme.primaryColor,
694
+ color: "#fff",
695
+ border: "none",
696
+ borderRadius: "8px",
697
+ fontWeight: 500,
698
+ cursor: "pointer",
699
+ fontSize: "14px",
700
+ opacity: !inputValue.trim() || isStreaming ? 0.5 : 1
701
+ },
702
+ children: "Send"
703
+ }
704
+ )
705
+ ]
706
+ }
707
+ )
708
+ ]
709
+ }
710
+ );
711
+ }
712
+ function MessageBubble({ message, theme, onQuickAction }) {
713
+ const isUser = message.role === "user";
714
+ if (message.type === "quick_actions" && message.quickActions) {
715
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: message.quickActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
716
+ "button",
717
+ {
718
+ onClick: () => onQuickAction(action),
719
+ style: {
720
+ padding: "8px 14px",
721
+ backgroundColor: "#f5f5f5",
722
+ border: "1px solid #e5e5e5",
723
+ borderRadius: "20px",
724
+ fontSize: "13px",
725
+ cursor: "pointer",
726
+ color: theme.textColor,
727
+ transition: "all 0.15s"
728
+ },
729
+ onMouseOver: (e) => {
730
+ e.currentTarget.style.backgroundColor = `${theme.primaryColor}15`;
731
+ e.currentTarget.style.borderColor = theme.primaryColor;
732
+ },
733
+ onMouseOut: (e) => {
734
+ e.currentTarget.style.backgroundColor = "#f5f5f5";
735
+ e.currentTarget.style.borderColor = "#e5e5e5";
736
+ },
737
+ children: action.label
738
+ },
739
+ action.id
740
+ )) });
741
+ }
742
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
743
+ "div",
744
+ {
745
+ style: {
746
+ display: "flex",
747
+ justifyContent: isUser ? "flex-end" : "flex-start"
748
+ },
749
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
750
+ "div",
751
+ {
752
+ style: {
753
+ maxWidth: "85%",
754
+ padding: "12px 16px",
755
+ backgroundColor: isUser ? theme.primaryColor : "#f5f5f5",
756
+ color: isUser ? "#fff" : theme.textColor,
757
+ borderRadius: "12px",
758
+ borderBottomRightRadius: isUser ? "4px" : "12px",
759
+ borderBottomLeftRadius: isUser ? "12px" : "4px",
760
+ fontSize: "14px",
761
+ lineHeight: 1.5,
762
+ whiteSpace: "pre-wrap"
763
+ },
764
+ children: message.content
765
+ }
766
+ )
767
+ }
768
+ );
769
+ }
770
+ function SparklesIcon({ color = "currentColor" }) {
771
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", children: [
772
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3z" }),
773
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 13l1 3 3 1-3 1-1 3-1-3-3-1 3-1 1-3z" }),
774
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 17l1 2 2 1-2 1-1 2-1-2-2-1 2-1 1-2z" })
775
+ ] });
776
+ }
777
+ function ChatIcon({ color = "currentColor" }) {
778
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" }) });
779
+ }
780
+ function MinimizeIcon() {
781
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M19 9l-7 7-7-7" }) });
782
+ }
783
+ function LoadingSpinner({ size = 20 }) {
784
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
785
+ "svg",
786
+ {
787
+ width: size,
788
+ height: size,
789
+ viewBox: "0 0 24 24",
790
+ fill: "none",
791
+ style: { animation: "ia-spin 1s linear infinite" },
792
+ children: [
793
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ia-spin { to { transform: rotate(360deg); } }` }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10", stroke: "#e5e5e5", strokeWidth: "3" }),
795
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
796
+ "path",
797
+ {
798
+ d: "M12 2a10 10 0 019.17 6",
799
+ stroke: "currentColor",
800
+ strokeWidth: "3",
801
+ strokeLinecap: "round"
802
+ }
803
+ )
804
+ ]
805
+ }
806
+ );
807
+ }
808
+
809
+ // src/hooks/useSession.ts
810
+ var import_react3 = require("react");
811
+ function useSession(options) {
812
+ const { apiKey, apiUrl, documentId, sessionId, onSessionChange, onError } = options;
813
+ const [session, setSession] = (0, import_react3.useState)(null);
814
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
815
+ const [error, setError] = (0, import_react3.useState)(null);
816
+ const clientRef = (0, import_react3.useRef)(null);
817
+ const isStreamingRef = (0, import_react3.useRef)(false);
818
+ (0, import_react3.useEffect)(() => {
819
+ clientRef.current = new InformedAIClient(apiKey, apiUrl);
820
+ }, [apiKey, apiUrl]);
821
+ (0, import_react3.useEffect)(() => {
822
+ async function initialize() {
823
+ if (!clientRef.current) return;
824
+ try {
825
+ setIsLoading(true);
826
+ setError(null);
827
+ let sess;
828
+ if (sessionId) {
829
+ sess = await clientRef.current.getSession(sessionId);
830
+ } else {
831
+ sess = await clientRef.current.createSession(documentId);
832
+ }
833
+ setSession(sess);
834
+ onSessionChange?.(sess);
835
+ } catch (err) {
836
+ const error2 = err instanceof Error ? err : new Error("Failed to initialize session");
837
+ setError(error2);
838
+ onError?.(error2);
839
+ } finally {
840
+ setIsLoading(false);
841
+ }
842
+ }
843
+ initialize();
844
+ }, [documentId, sessionId, onSessionChange, onError]);
845
+ const handleSSEEvent = (0, import_react3.useCallback)((event) => {
846
+ if (event.type === "done" || event.type === "session_update") {
847
+ if (event.session) {
848
+ setSession(event.session);
849
+ onSessionChange?.(event.session);
850
+ }
851
+ isStreamingRef.current = false;
852
+ }
853
+ if (event.type === "error") {
854
+ const error2 = new Error(event.error || "Stream error");
855
+ setError(error2);
856
+ onError?.(error2);
857
+ isStreamingRef.current = false;
858
+ }
859
+ }, [onSessionChange, onError]);
860
+ const sendMessage = (0, import_react3.useCallback)(async (message) => {
861
+ if (!clientRef.current || !session || isStreamingRef.current) return;
862
+ try {
863
+ isStreamingRef.current = true;
864
+ setError(null);
865
+ await clientRef.current.sendMessage(session.id, message, handleSSEEvent);
866
+ } catch (err) {
867
+ const error2 = err instanceof Error ? err : new Error("Failed to send message");
868
+ setError(error2);
869
+ onError?.(error2);
870
+ isStreamingRef.current = false;
871
+ }
872
+ }, [session, handleSSEEvent, onError]);
873
+ const sendQuickAction = (0, import_react3.useCallback)(async (action, payload) => {
874
+ if (!clientRef.current || !session || isStreamingRef.current) return;
875
+ try {
876
+ isStreamingRef.current = true;
877
+ setError(null);
878
+ const newSession = await clientRef.current.sendQuickAction(
879
+ session.id,
880
+ action,
881
+ payload,
882
+ handleSSEEvent
883
+ );
884
+ setSession(newSession);
885
+ onSessionChange?.(newSession);
886
+ } catch (err) {
887
+ const error2 = err instanceof Error ? err : new Error("Failed to send quick action");
888
+ setError(error2);
889
+ onError?.(error2);
890
+ } finally {
891
+ isStreamingRef.current = false;
892
+ }
893
+ }, [session, handleSSEEvent, onSessionChange, onError]);
894
+ const applyPendingValue = (0, import_react3.useCallback)(async () => {
895
+ if (!clientRef.current || !session) return;
896
+ try {
897
+ setError(null);
898
+ const newSession = await clientRef.current.applyPendingValue(session.id);
899
+ setSession(newSession);
900
+ onSessionChange?.(newSession);
901
+ } catch (err) {
902
+ const error2 = err instanceof Error ? err : new Error("Failed to apply value");
903
+ setError(error2);
904
+ onError?.(error2);
905
+ }
906
+ }, [session, onSessionChange, onError]);
907
+ const skipTask = (0, import_react3.useCallback)(async () => {
908
+ if (!clientRef.current || !session) return;
909
+ try {
910
+ setError(null);
911
+ const newSession = await clientRef.current.skipTask(session.id);
912
+ setSession(newSession);
913
+ onSessionChange?.(newSession);
914
+ } catch (err) {
915
+ const error2 = err instanceof Error ? err : new Error("Failed to skip task");
916
+ setError(error2);
917
+ onError?.(error2);
918
+ }
919
+ }, [session, onSessionChange, onError]);
920
+ return {
921
+ session,
922
+ isLoading,
923
+ error,
924
+ sendMessage,
925
+ sendQuickAction,
926
+ applyPendingValue,
927
+ skipTask
928
+ };
929
+ }
930
+
931
+ // src/hooks/useDocument.ts
932
+ var import_react4 = require("react");
933
+ function useDocument(options) {
934
+ const { apiKey, apiUrl, documentId, onError } = options;
935
+ const [document, setDocument] = (0, import_react4.useState)(null);
936
+ const [documentType, setDocumentType] = (0, import_react4.useState)(null);
937
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(true);
938
+ const [error, setError] = (0, import_react4.useState)(null);
939
+ const clientRef = (0, import_react4.useRef)(null);
940
+ (0, import_react4.useEffect)(() => {
941
+ clientRef.current = new InformedAIClient(apiKey, apiUrl);
942
+ }, [apiKey, apiUrl]);
943
+ const fetchDocument = (0, import_react4.useCallback)(async () => {
944
+ if (!clientRef.current) return;
945
+ try {
946
+ setIsLoading(true);
947
+ setError(null);
948
+ const doc = await clientRef.current.getDocument(documentId);
949
+ setDocument(doc);
950
+ const dt = await clientRef.current.getDocumentType(doc.documentTypeId);
951
+ setDocumentType(dt);
952
+ } catch (err) {
953
+ const error2 = err instanceof Error ? err : new Error("Failed to fetch document");
954
+ setError(error2);
955
+ onError?.(error2);
956
+ } finally {
957
+ setIsLoading(false);
958
+ }
959
+ }, [documentId, onError]);
960
+ (0, import_react4.useEffect)(() => {
961
+ fetchDocument();
962
+ }, [fetchDocument]);
963
+ const updateField = (0, import_react4.useCallback)(async (fieldName, value) => {
964
+ if (!clientRef.current || !document) return;
965
+ try {
966
+ setError(null);
967
+ const updatedDoc = await clientRef.current.updateDocumentField(document.id, fieldName, value);
968
+ setDocument(updatedDoc);
969
+ } catch (err) {
970
+ const error2 = err instanceof Error ? err : new Error("Failed to update field");
971
+ setError(error2);
972
+ onError?.(error2);
973
+ }
974
+ }, [document, onError]);
975
+ const refetch = (0, import_react4.useCallback)(async () => {
976
+ await fetchDocument();
977
+ }, [fetchDocument]);
978
+ return {
979
+ document,
980
+ documentType,
981
+ isLoading,
982
+ error,
983
+ updateField,
984
+ refetch
985
+ };
986
+ }
987
+ // Annotate the CommonJS export names for ESM import in node:
988
+ 0 && (module.exports = {
989
+ InformedAIClient,
990
+ InformedAIProvider,
991
+ InformedAssistant,
992
+ useDocument,
993
+ useInformedAI,
994
+ useSession
995
+ });