@informedai/react 0.4.13 → 0.4.15

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 CHANGED
@@ -20,9 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AdminChatbot: () => AdminChatbot,
23
24
  InformedAIClient: () => InformedAIClient,
24
25
  InformedAIProvider: () => InformedAIProvider,
25
26
  InformedAssistant: () => InformedAssistant,
27
+ WebsiteChatbot: () => WebsiteChatbot,
26
28
  useInformedAI: () => useInformedAI,
27
29
  useSession: () => useSession
28
30
  });
@@ -755,14 +757,15 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
755
757
  streamingContent,
756
758
  sendMessage,
757
759
  sendQuickAction,
758
- applyPendingValue,
759
- skipTask
760
+ applyPendingValue
760
761
  } = useInformedAI();
761
762
  const [isCollapsed, setIsCollapsed] = (0, import_react2.useState)(defaultCollapsed);
762
763
  const [inputValue, setInputValue] = (0, import_react2.useState)("");
764
+ const [showPendingActions, setShowPendingActions] = (0, import_react2.useState)(true);
763
765
  const messagesEndRef = (0, import_react2.useRef)(null);
764
766
  const inputRef = (0, import_react2.useRef)(null);
765
767
  const wasStreamingRef = (0, import_react2.useRef)(false);
768
+ const lastPendingValueRef = (0, import_react2.useRef)(void 0);
766
769
  (0, import_react2.useEffect)(() => {
767
770
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
768
771
  }, [session?.widgetMessages, streamingContent]);
@@ -772,6 +775,15 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
772
775
  }
773
776
  wasStreamingRef.current = isStreaming;
774
777
  }, [isStreaming]);
778
+ const activeTask = session?.activeTask;
779
+ const pendingValue = activeTask ? session?.taskStates[activeTask]?.pendingValue : void 0;
780
+ const hasPendingValue = pendingValue !== void 0 && pendingValue !== null;
781
+ (0, import_react2.useEffect)(() => {
782
+ if (hasPendingValue && pendingValue !== lastPendingValueRef.current) {
783
+ setShowPendingActions(true);
784
+ }
785
+ lastPendingValueRef.current = pendingValue;
786
+ }, [hasPendingValue, pendingValue]);
775
787
  const handleSubmit = async (e) => {
776
788
  e.preventDefault();
777
789
  if (!inputValue.trim() || isStreaming) return;
@@ -782,9 +794,6 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
782
794
  const handleQuickAction = async (action) => {
783
795
  await sendQuickAction(action.action, action.payload);
784
796
  };
785
- const activeTask = session?.activeTask;
786
- const pendingValue = activeTask ? session?.taskStates[activeTask]?.pendingValue : void 0;
787
- const hasPendingValue = pendingValue !== void 0 && pendingValue !== null;
788
797
  const cssVars = {
789
798
  "--ia-primary": theme.primaryColor,
790
799
  "--ia-bg": theme.backgroundColor,
@@ -981,7 +990,7 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
981
990
  ]
982
991
  }
983
992
  ),
984
- hasPendingValue && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
993
+ hasPendingValue && showPendingActions ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
985
994
  "div",
986
995
  {
987
996
  style: {
@@ -1015,7 +1024,7 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
1015
1024
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1016
1025
  "button",
1017
1026
  {
1018
- onClick: skipTask,
1027
+ onClick: () => setShowPendingActions(false),
1019
1028
  disabled: isStreaming,
1020
1029
  style: {
1021
1030
  padding: "10px 16px",
@@ -1028,13 +1037,12 @@ function AssistantWidget({ className, theme, position = "inline", defaultCollaps
1028
1037
  fontSize: "14px",
1029
1038
  opacity: isStreaming ? 0.5 : 1
1030
1039
  },
1031
- children: "Skip"
1040
+ children: "Not yet"
1032
1041
  }
1033
1042
  )
1034
1043
  ]
1035
1044
  }
1036
- ),
1037
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1045
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1038
1046
  "form",
1039
1047
  {
1040
1048
  onSubmit: handleSubmit,
@@ -1295,19 +1303,765 @@ function LoadingSpinner({ size = 20 }) {
1295
1303
  );
1296
1304
  }
1297
1305
 
1298
- // src/hooks/useSession.ts
1306
+ // src/components/AdminChatbot.tsx
1299
1307
  var import_react3 = require("react");
1300
- function useSession(options) {
1301
- const { apiUrl, documentTypeId, sessionId, onSessionChange, onError } = options;
1302
- const [session, setSession] = (0, import_react3.useState)(null);
1303
- const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
1308
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1309
+ var defaultTheme2 = {
1310
+ primaryColor: "#3b82f6",
1311
+ // Blue (different from widget's amber)
1312
+ backgroundColor: "#ffffff",
1313
+ textColor: "#1c1917",
1314
+ borderRadius: "12px",
1315
+ fontFamily: "system-ui, -apple-system, sans-serif"
1316
+ };
1317
+ function AdminChatbot({ className, ...config }) {
1318
+ const theme = { ...defaultTheme2, ...config.theme };
1319
+ const apiUrl = config.apiUrl || "https://api.informedai.app/api/v1";
1320
+ const [messages, setMessages] = (0, import_react3.useState)([]);
1321
+ const [inputValue, setInputValue] = (0, import_react3.useState)("");
1322
+ const [systemPrompt, setSystemPrompt] = (0, import_react3.useState)(config.systemPrompt || "");
1323
+ const [showSystemPromptEditor, setShowSystemPromptEditor] = (0, import_react3.useState)(false);
1324
+ const [isStreaming, setIsStreaming] = (0, import_react3.useState)(false);
1325
+ const [streamingContent, setStreamingContent] = (0, import_react3.useState)("");
1304
1326
  const [error, setError] = (0, import_react3.useState)(null);
1305
- const clientRef = (0, import_react3.useRef)(null);
1306
- const isStreamingRef = (0, import_react3.useRef)(false);
1327
+ const [isCollapsed, setIsCollapsed] = (0, import_react3.useState)(config.defaultCollapsed ?? false);
1328
+ const messagesEndRef = (0, import_react3.useRef)(null);
1329
+ const inputRef = (0, import_react3.useRef)(null);
1307
1330
  (0, import_react3.useEffect)(() => {
1331
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1332
+ }, [messages, streamingContent]);
1333
+ const sendMessage = async (message) => {
1334
+ if (!message.trim() || isStreaming) return;
1335
+ setError(null);
1336
+ setIsStreaming(true);
1337
+ setStreamingContent("");
1338
+ const newMessages = [...messages, { role: "user", content: message }];
1339
+ setMessages(newMessages);
1340
+ try {
1341
+ const response = await fetch(`${apiUrl}/admin-chatbot/message`, {
1342
+ method: "POST",
1343
+ headers: {
1344
+ "Content-Type": "application/json",
1345
+ Authorization: `Bearer ${config.apiKey}`
1346
+ },
1347
+ body: JSON.stringify({
1348
+ message,
1349
+ history: messages,
1350
+ // Send previous messages (not including current)
1351
+ systemPrompt: systemPrompt || void 0
1352
+ })
1353
+ });
1354
+ if (!response.ok) {
1355
+ const err = await response.json().catch(() => ({ error: "Request failed" }));
1356
+ throw new Error(err.error || `HTTP ${response.status}`);
1357
+ }
1358
+ const reader = response.body?.getReader();
1359
+ if (!reader) throw new Error("No response body");
1360
+ const decoder = new TextDecoder();
1361
+ let buffer = "";
1362
+ let fullContent = "";
1363
+ while (true) {
1364
+ const { done, value } = await reader.read();
1365
+ if (done) break;
1366
+ buffer += decoder.decode(value, { stream: true });
1367
+ const lines = buffer.split("\n");
1368
+ buffer = lines.pop() || "";
1369
+ for (const line of lines) {
1370
+ if (line.startsWith("event:")) continue;
1371
+ if (line.startsWith("data:")) {
1372
+ const data = line.slice(5).trim();
1373
+ try {
1374
+ const parsed = JSON.parse(data);
1375
+ if (parsed.content) {
1376
+ fullContent += parsed.content;
1377
+ setStreamingContent(fullContent);
1378
+ }
1379
+ if (parsed.fullContent) {
1380
+ fullContent = parsed.fullContent;
1381
+ }
1382
+ } catch {
1383
+ }
1384
+ }
1385
+ }
1386
+ }
1387
+ setMessages([...newMessages, { role: "assistant", content: fullContent }]);
1388
+ setStreamingContent("");
1389
+ } catch (err) {
1390
+ setError(err instanceof Error ? err.message : "Failed to send message");
1391
+ } finally {
1392
+ setIsStreaming(false);
1393
+ }
1394
+ };
1395
+ const handleSubmit = (e) => {
1396
+ e.preventDefault();
1397
+ if (!inputValue.trim() || isStreaming) return;
1398
+ const message = inputValue.trim();
1399
+ setInputValue("");
1400
+ sendMessage(message);
1401
+ };
1402
+ const clearConversation = () => {
1403
+ setMessages([]);
1404
+ setStreamingContent("");
1405
+ setError(null);
1406
+ };
1407
+ const cssVars = {
1408
+ "--ac-primary": theme.primaryColor,
1409
+ "--ac-bg": theme.backgroundColor,
1410
+ "--ac-text": theme.textColor,
1411
+ "--ac-radius": theme.borderRadius,
1412
+ "--ac-font": theme.fontFamily
1413
+ };
1414
+ const positionStyles = config.position === "inline" ? {} : {
1415
+ position: "fixed",
1416
+ [config.position === "bottom-left" ? "left" : "right"]: "20px",
1417
+ bottom: "20px",
1418
+ zIndex: 9999
1419
+ };
1420
+ if (isCollapsed && config.position !== "inline") {
1421
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1422
+ "button",
1423
+ {
1424
+ onClick: () => setIsCollapsed(false),
1425
+ className,
1426
+ style: {
1427
+ ...cssVars,
1428
+ ...positionStyles,
1429
+ width: "56px",
1430
+ height: "56px",
1431
+ borderRadius: "50%",
1432
+ backgroundColor: "var(--ac-primary)",
1433
+ color: "white",
1434
+ border: "none",
1435
+ cursor: "pointer",
1436
+ display: "flex",
1437
+ alignItems: "center",
1438
+ justifyContent: "center",
1439
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
1440
+ fontFamily: "var(--ac-font)"
1441
+ },
1442
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) })
1443
+ }
1444
+ );
1445
+ }
1446
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1447
+ "div",
1448
+ {
1449
+ className,
1450
+ style: {
1451
+ ...cssVars,
1452
+ ...positionStyles,
1453
+ width: config.position === "inline" ? "100%" : "400px",
1454
+ maxHeight: config.position === "inline" ? "600px" : "80vh",
1455
+ backgroundColor: "var(--ac-bg)",
1456
+ borderRadius: "var(--ac-radius)",
1457
+ border: "1px solid #e5e5e5",
1458
+ fontFamily: "var(--ac-font)",
1459
+ display: "flex",
1460
+ flexDirection: "column",
1461
+ boxShadow: config.position === "inline" ? "none" : "0 4px 20px rgba(0,0,0,0.15)",
1462
+ overflow: "hidden"
1463
+ },
1464
+ children: [
1465
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1466
+ "div",
1467
+ {
1468
+ style: {
1469
+ padding: "16px",
1470
+ borderBottom: "1px solid #e5e5e5",
1471
+ display: "flex",
1472
+ alignItems: "center",
1473
+ justifyContent: "space-between",
1474
+ backgroundColor: "var(--ac-primary)",
1475
+ color: "white"
1476
+ },
1477
+ children: [
1478
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1479
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1480
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1481
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 16v-4M12 8h.01" })
1482
+ ] }),
1483
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontWeight: 600 }, children: config.title || "Knowledge Library" })
1484
+ ] }),
1485
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
1486
+ config.allowEditSystemPrompt && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1487
+ "button",
1488
+ {
1489
+ onClick: () => setShowSystemPromptEditor(!showSystemPromptEditor),
1490
+ style: {
1491
+ background: "rgba(255,255,255,0.2)",
1492
+ border: "none",
1493
+ borderRadius: "4px",
1494
+ padding: "4px 8px",
1495
+ color: "white",
1496
+ cursor: "pointer",
1497
+ fontSize: "12px"
1498
+ },
1499
+ children: showSystemPromptEditor ? "Hide Prompt" : "Edit Prompt"
1500
+ }
1501
+ ),
1502
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1503
+ "button",
1504
+ {
1505
+ onClick: clearConversation,
1506
+ style: {
1507
+ background: "rgba(255,255,255,0.2)",
1508
+ border: "none",
1509
+ borderRadius: "4px",
1510
+ padding: "4px 8px",
1511
+ color: "white",
1512
+ cursor: "pointer",
1513
+ fontSize: "12px"
1514
+ },
1515
+ children: "Clear"
1516
+ }
1517
+ ),
1518
+ config.position !== "inline" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1519
+ "button",
1520
+ {
1521
+ onClick: () => setIsCollapsed(true),
1522
+ style: {
1523
+ background: "none",
1524
+ border: "none",
1525
+ color: "white",
1526
+ cursor: "pointer",
1527
+ padding: "4px"
1528
+ },
1529
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1530
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1531
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1532
+ ] })
1533
+ }
1534
+ )
1535
+ ] })
1536
+ ]
1537
+ }
1538
+ ),
1539
+ showSystemPromptEditor && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { padding: "12px", borderBottom: "1px solid #e5e5e5", backgroundColor: "#f9fafb" }, children: [
1540
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("label", { style: { display: "block", fontSize: "12px", fontWeight: 500, marginBottom: "4px", color: "#6b7280" }, children: "System Prompt" }),
1541
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1542
+ "textarea",
1543
+ {
1544
+ value: systemPrompt,
1545
+ onChange: (e) => setSystemPrompt(e.target.value),
1546
+ placeholder: "Enter a system prompt to guide the chatbot's behavior...",
1547
+ style: {
1548
+ width: "100%",
1549
+ minHeight: "80px",
1550
+ padding: "8px",
1551
+ borderRadius: "6px",
1552
+ border: "1px solid #d1d5db",
1553
+ fontSize: "13px",
1554
+ fontFamily: "var(--ac-font)",
1555
+ resize: "vertical"
1556
+ }
1557
+ }
1558
+ )
1559
+ ] }),
1560
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1561
+ "div",
1562
+ {
1563
+ style: {
1564
+ flex: 1,
1565
+ overflowY: "auto",
1566
+ padding: "16px",
1567
+ display: "flex",
1568
+ flexDirection: "column",
1569
+ gap: "12px",
1570
+ minHeight: "200px"
1571
+ },
1572
+ children: [
1573
+ messages.length === 0 && !streamingContent && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { textAlign: "center", color: "#9ca3af", padding: "40px 20px" }, children: [
1574
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1575
+ "svg",
1576
+ {
1577
+ width: "48",
1578
+ height: "48",
1579
+ viewBox: "0 0 24 24",
1580
+ fill: "none",
1581
+ stroke: "currentColor",
1582
+ strokeWidth: "1.5",
1583
+ style: { margin: "0 auto 16px" },
1584
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
1585
+ }
1586
+ ),
1587
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { style: { margin: 0, fontSize: "14px" }, children: "Ask a question about your knowledge library" })
1588
+ ] }),
1589
+ messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1590
+ "div",
1591
+ {
1592
+ style: {
1593
+ alignSelf: msg.role === "user" ? "flex-end" : "flex-start",
1594
+ maxWidth: "85%"
1595
+ },
1596
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1597
+ "div",
1598
+ {
1599
+ style: {
1600
+ padding: "10px 14px",
1601
+ borderRadius: "12px",
1602
+ backgroundColor: msg.role === "user" ? "var(--ac-primary)" : "#f3f4f6",
1603
+ color: msg.role === "user" ? "white" : "var(--ac-text)",
1604
+ fontSize: "14px",
1605
+ lineHeight: "1.5",
1606
+ whiteSpace: "pre-wrap"
1607
+ },
1608
+ children: msg.content
1609
+ }
1610
+ )
1611
+ },
1612
+ i
1613
+ )),
1614
+ streamingContent && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { alignSelf: "flex-start", maxWidth: "85%" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1615
+ "div",
1616
+ {
1617
+ style: {
1618
+ padding: "10px 14px",
1619
+ borderRadius: "12px",
1620
+ backgroundColor: "#f3f4f6",
1621
+ color: "var(--ac-text)",
1622
+ fontSize: "14px",
1623
+ lineHeight: "1.5",
1624
+ whiteSpace: "pre-wrap"
1625
+ },
1626
+ children: [
1627
+ streamingContent,
1628
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { opacity: 0.5, animation: "blink 1s infinite" }, children: "|" })
1629
+ ]
1630
+ }
1631
+ ) }),
1632
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1633
+ "div",
1634
+ {
1635
+ style: {
1636
+ padding: "10px 14px",
1637
+ borderRadius: "8px",
1638
+ backgroundColor: "#fef2f2",
1639
+ color: "#dc2626",
1640
+ fontSize: "13px"
1641
+ },
1642
+ children: error
1643
+ }
1644
+ ),
1645
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: messagesEndRef })
1646
+ ]
1647
+ }
1648
+ ),
1649
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("form", { onSubmit: handleSubmit, style: { padding: "12px", borderTop: "1px solid #e5e5e5" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
1650
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1651
+ "input",
1652
+ {
1653
+ ref: inputRef,
1654
+ type: "text",
1655
+ value: inputValue,
1656
+ onChange: (e) => setInputValue(e.target.value),
1657
+ placeholder: config.placeholder || "Ask a question...",
1658
+ disabled: isStreaming,
1659
+ style: {
1660
+ flex: 1,
1661
+ padding: "10px 14px",
1662
+ borderRadius: "8px",
1663
+ border: "1px solid #d1d5db",
1664
+ fontSize: "14px",
1665
+ fontFamily: "var(--ac-font)",
1666
+ outline: "none"
1667
+ }
1668
+ }
1669
+ ),
1670
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1671
+ "button",
1672
+ {
1673
+ type: "submit",
1674
+ disabled: isStreaming || !inputValue.trim(),
1675
+ style: {
1676
+ padding: "10px 16px",
1677
+ borderRadius: "8px",
1678
+ backgroundColor: isStreaming || !inputValue.trim() ? "#d1d5db" : "var(--ac-primary)",
1679
+ color: "white",
1680
+ border: "none",
1681
+ cursor: isStreaming || !inputValue.trim() ? "not-allowed" : "pointer",
1682
+ fontWeight: 500,
1683
+ fontSize: "14px"
1684
+ },
1685
+ children: isStreaming ? "..." : "Send"
1686
+ }
1687
+ )
1688
+ ] }) }),
1689
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
1690
+ @keyframes blink {
1691
+ 0%, 50% { opacity: 1; }
1692
+ 51%, 100% { opacity: 0; }
1693
+ }
1694
+ ` })
1695
+ ]
1696
+ }
1697
+ );
1698
+ }
1699
+
1700
+ // src/components/WebsiteChatbot.tsx
1701
+ var import_react4 = require("react");
1702
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1703
+ var defaultTheme3 = {
1704
+ primaryColor: "#f59e0b",
1705
+ // Amber
1706
+ backgroundColor: "#ffffff",
1707
+ textColor: "#1c1917",
1708
+ borderRadius: "12px",
1709
+ fontFamily: "system-ui, -apple-system, sans-serif"
1710
+ };
1711
+ function WebsiteChatbot({ className, ...config }) {
1712
+ const theme = { ...defaultTheme3, ...config.theme };
1713
+ const apiUrl = config.apiUrl || "https://api.informedai.app/api/v1";
1714
+ const [messages, setMessages] = (0, import_react4.useState)([]);
1715
+ const [inputValue, setInputValue] = (0, import_react4.useState)("");
1716
+ const [isStreaming, setIsStreaming] = (0, import_react4.useState)(false);
1717
+ const [streamingContent, setStreamingContent] = (0, import_react4.useState)("");
1718
+ const [error, setError] = (0, import_react4.useState)(null);
1719
+ const [isCollapsed, setIsCollapsed] = (0, import_react4.useState)(config.defaultCollapsed ?? false);
1720
+ const messagesEndRef = (0, import_react4.useRef)(null);
1721
+ (0, import_react4.useEffect)(() => {
1722
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1723
+ }, [messages, streamingContent]);
1724
+ const sendMessage = async (message) => {
1725
+ if (!message.trim() || isStreaming) return;
1726
+ setError(null);
1727
+ setIsStreaming(true);
1728
+ setStreamingContent("");
1729
+ const newMessages = [...messages, { role: "user", content: message }];
1730
+ setMessages(newMessages);
1731
+ try {
1732
+ const response = await fetch(`${apiUrl}/website-agent/message`, {
1733
+ method: "POST",
1734
+ headers: {
1735
+ "Content-Type": "application/json"
1736
+ },
1737
+ body: JSON.stringify({
1738
+ agentId: config.agentId,
1739
+ message,
1740
+ history: messages
1741
+ })
1742
+ });
1743
+ if (!response.ok) {
1744
+ const err = await response.json().catch(() => ({ error: "Request failed" }));
1745
+ throw new Error(err.error || `HTTP ${response.status}`);
1746
+ }
1747
+ const reader = response.body?.getReader();
1748
+ if (!reader) throw new Error("No response body");
1749
+ const decoder = new TextDecoder();
1750
+ let buffer = "";
1751
+ let fullContent = "";
1752
+ while (true) {
1753
+ const { done, value } = await reader.read();
1754
+ if (done) break;
1755
+ buffer += decoder.decode(value, { stream: true });
1756
+ const lines = buffer.split("\n");
1757
+ buffer = lines.pop() || "";
1758
+ for (const line of lines) {
1759
+ if (line.startsWith("event:")) continue;
1760
+ if (line.startsWith("data:")) {
1761
+ const data = line.slice(5).trim();
1762
+ try {
1763
+ const parsed = JSON.parse(data);
1764
+ if (parsed.content) {
1765
+ fullContent += parsed.content;
1766
+ setStreamingContent(fullContent);
1767
+ }
1768
+ if (parsed.fullContent) {
1769
+ fullContent = parsed.fullContent;
1770
+ }
1771
+ } catch {
1772
+ }
1773
+ }
1774
+ }
1775
+ }
1776
+ setMessages([...newMessages, { role: "assistant", content: fullContent }]);
1777
+ setStreamingContent("");
1778
+ } catch (err) {
1779
+ setError(err instanceof Error ? err.message : "Failed to send message");
1780
+ } finally {
1781
+ setIsStreaming(false);
1782
+ }
1783
+ };
1784
+ const handleSubmit = (e) => {
1785
+ e.preventDefault();
1786
+ if (!inputValue.trim() || isStreaming) return;
1787
+ const message = inputValue.trim();
1788
+ setInputValue("");
1789
+ sendMessage(message);
1790
+ };
1791
+ const clearConversation = () => {
1792
+ setMessages([]);
1793
+ setStreamingContent("");
1794
+ setError(null);
1795
+ };
1796
+ const cssVars = {
1797
+ "--wc-primary": theme.primaryColor,
1798
+ "--wc-bg": theme.backgroundColor,
1799
+ "--wc-text": theme.textColor,
1800
+ "--wc-radius": theme.borderRadius,
1801
+ "--wc-font": theme.fontFamily
1802
+ };
1803
+ const positionStyles = config.position === "inline" ? {} : {
1804
+ position: "fixed",
1805
+ [config.position === "bottom-left" ? "left" : "right"]: "20px",
1806
+ bottom: "20px",
1807
+ zIndex: 9999
1808
+ };
1809
+ if (isCollapsed && config.position !== "inline") {
1810
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1811
+ "button",
1812
+ {
1813
+ onClick: () => setIsCollapsed(false),
1814
+ className,
1815
+ style: {
1816
+ ...cssVars,
1817
+ ...positionStyles,
1818
+ width: "56px",
1819
+ height: "56px",
1820
+ borderRadius: "50%",
1821
+ backgroundColor: "var(--wc-primary)",
1822
+ color: "white",
1823
+ border: "none",
1824
+ cursor: "pointer",
1825
+ display: "flex",
1826
+ alignItems: "center",
1827
+ justifyContent: "center",
1828
+ boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
1829
+ fontFamily: "var(--wc-font)"
1830
+ },
1831
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) })
1832
+ }
1833
+ );
1834
+ }
1835
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1836
+ "div",
1837
+ {
1838
+ className,
1839
+ style: {
1840
+ ...cssVars,
1841
+ ...positionStyles,
1842
+ width: config.position === "inline" ? "100%" : "400px",
1843
+ maxHeight: config.position === "inline" ? "600px" : "80vh",
1844
+ backgroundColor: "var(--wc-bg)",
1845
+ borderRadius: "var(--wc-radius)",
1846
+ border: "1px solid #e5e5e5",
1847
+ fontFamily: "var(--wc-font)",
1848
+ display: "flex",
1849
+ flexDirection: "column",
1850
+ boxShadow: config.position === "inline" ? "none" : "0 4px 20px rgba(0,0,0,0.15)",
1851
+ overflow: "hidden"
1852
+ },
1853
+ children: [
1854
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1855
+ "div",
1856
+ {
1857
+ style: {
1858
+ padding: "16px",
1859
+ borderBottom: "1px solid #e5e5e5",
1860
+ display: "flex",
1861
+ alignItems: "center",
1862
+ justifyContent: "space-between",
1863
+ backgroundColor: "var(--wc-primary)",
1864
+ color: "white"
1865
+ },
1866
+ children: [
1867
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1868
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1869
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
1870
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "2", y1: "12", x2: "22", y2: "12" }),
1871
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" })
1872
+ ] }),
1873
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontWeight: 600 }, children: config.title || "Chat" })
1874
+ ] }),
1875
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
1876
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1877
+ "button",
1878
+ {
1879
+ onClick: clearConversation,
1880
+ style: {
1881
+ background: "rgba(255,255,255,0.2)",
1882
+ border: "none",
1883
+ borderRadius: "4px",
1884
+ padding: "4px 8px",
1885
+ color: "white",
1886
+ cursor: "pointer",
1887
+ fontSize: "12px"
1888
+ },
1889
+ children: "Clear"
1890
+ }
1891
+ ),
1892
+ config.position !== "inline" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1893
+ "button",
1894
+ {
1895
+ onClick: () => setIsCollapsed(true),
1896
+ style: {
1897
+ background: "none",
1898
+ border: "none",
1899
+ color: "white",
1900
+ cursor: "pointer",
1901
+ padding: "4px"
1902
+ },
1903
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1904
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1905
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1906
+ ] })
1907
+ }
1908
+ )
1909
+ ] })
1910
+ ]
1911
+ }
1912
+ ),
1913
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1914
+ "div",
1915
+ {
1916
+ style: {
1917
+ flex: 1,
1918
+ overflowY: "auto",
1919
+ padding: "16px",
1920
+ display: "flex",
1921
+ flexDirection: "column",
1922
+ gap: "12px",
1923
+ minHeight: "200px"
1924
+ },
1925
+ children: [
1926
+ messages.length === 0 && !streamingContent && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { textAlign: "center", color: "#9ca3af", padding: "40px 20px" }, children: [
1927
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1928
+ "svg",
1929
+ {
1930
+ width: "48",
1931
+ height: "48",
1932
+ viewBox: "0 0 24 24",
1933
+ fill: "none",
1934
+ stroke: "currentColor",
1935
+ strokeWidth: "1.5",
1936
+ style: { margin: "0 auto 16px" },
1937
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })
1938
+ }
1939
+ ),
1940
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: 0, fontSize: "14px" }, children: "How can I help you today?" })
1941
+ ] }),
1942
+ messages.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1943
+ "div",
1944
+ {
1945
+ style: {
1946
+ alignSelf: msg.role === "user" ? "flex-end" : "flex-start",
1947
+ maxWidth: "85%"
1948
+ },
1949
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1950
+ "div",
1951
+ {
1952
+ style: {
1953
+ padding: "10px 14px",
1954
+ borderRadius: "12px",
1955
+ backgroundColor: msg.role === "user" ? "var(--wc-primary)" : "#f3f4f6",
1956
+ color: msg.role === "user" ? "white" : "var(--wc-text)",
1957
+ fontSize: "14px",
1958
+ lineHeight: "1.5",
1959
+ whiteSpace: "pre-wrap"
1960
+ },
1961
+ children: msg.content
1962
+ }
1963
+ )
1964
+ },
1965
+ i
1966
+ )),
1967
+ streamingContent && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { alignSelf: "flex-start", maxWidth: "85%" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1968
+ "div",
1969
+ {
1970
+ style: {
1971
+ padding: "10px 14px",
1972
+ borderRadius: "12px",
1973
+ backgroundColor: "#f3f4f6",
1974
+ color: "var(--wc-text)",
1975
+ fontSize: "14px",
1976
+ lineHeight: "1.5",
1977
+ whiteSpace: "pre-wrap"
1978
+ },
1979
+ children: [
1980
+ streamingContent,
1981
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.5, animation: "blink 1s infinite" }, children: "|" })
1982
+ ]
1983
+ }
1984
+ ) }),
1985
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1986
+ "div",
1987
+ {
1988
+ style: {
1989
+ padding: "10px 14px",
1990
+ borderRadius: "8px",
1991
+ backgroundColor: "#fef2f2",
1992
+ color: "#dc2626",
1993
+ fontSize: "13px"
1994
+ },
1995
+ children: error
1996
+ }
1997
+ ),
1998
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref: messagesEndRef })
1999
+ ]
2000
+ }
2001
+ ),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("form", { onSubmit: handleSubmit, style: { padding: "12px", borderTop: "1px solid #e5e5e5" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
2003
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2004
+ "input",
2005
+ {
2006
+ type: "text",
2007
+ value: inputValue,
2008
+ onChange: (e) => setInputValue(e.target.value),
2009
+ placeholder: config.placeholder || "Type a message...",
2010
+ disabled: isStreaming,
2011
+ style: {
2012
+ flex: 1,
2013
+ padding: "10px 14px",
2014
+ borderRadius: "8px",
2015
+ border: "1px solid #d1d5db",
2016
+ fontSize: "14px",
2017
+ fontFamily: "var(--wc-font)",
2018
+ outline: "none"
2019
+ }
2020
+ }
2021
+ ),
2022
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2023
+ "button",
2024
+ {
2025
+ type: "submit",
2026
+ disabled: isStreaming || !inputValue.trim(),
2027
+ style: {
2028
+ padding: "10px 16px",
2029
+ borderRadius: "8px",
2030
+ backgroundColor: isStreaming || !inputValue.trim() ? "#d1d5db" : "var(--wc-primary)",
2031
+ color: "white",
2032
+ border: "none",
2033
+ cursor: isStreaming || !inputValue.trim() ? "not-allowed" : "pointer",
2034
+ fontWeight: 500,
2035
+ fontSize: "14px"
2036
+ },
2037
+ children: isStreaming ? "..." : "Send"
2038
+ }
2039
+ )
2040
+ ] }) }),
2041
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
2042
+ @keyframes blink {
2043
+ 0%, 50% { opacity: 1; }
2044
+ 51%, 100% { opacity: 0; }
2045
+ }
2046
+ ` })
2047
+ ]
2048
+ }
2049
+ );
2050
+ }
2051
+
2052
+ // src/hooks/useSession.ts
2053
+ var import_react5 = require("react");
2054
+ function useSession(options) {
2055
+ const { apiUrl, documentTypeId, sessionId, onSessionChange, onError } = options;
2056
+ const [session, setSession] = (0, import_react5.useState)(null);
2057
+ const [isLoading, setIsLoading] = (0, import_react5.useState)(true);
2058
+ const [error, setError] = (0, import_react5.useState)(null);
2059
+ const clientRef = (0, import_react5.useRef)(null);
2060
+ const isStreamingRef = (0, import_react5.useRef)(false);
2061
+ (0, import_react5.useEffect)(() => {
1308
2062
  clientRef.current = new InformedAIClient(apiUrl);
1309
2063
  }, [apiUrl]);
1310
- (0, import_react3.useEffect)(() => {
2064
+ (0, import_react5.useEffect)(() => {
1311
2065
  async function initialize() {
1312
2066
  if (!clientRef.current) return;
1313
2067
  try {
@@ -1332,7 +2086,7 @@ function useSession(options) {
1332
2086
  }
1333
2087
  initialize();
1334
2088
  }, [documentTypeId, sessionId, onSessionChange, onError]);
1335
- const handleSSEEvent = (0, import_react3.useCallback)((event) => {
2089
+ const handleSSEEvent = (0, import_react5.useCallback)((event) => {
1336
2090
  if (event.type === "done" || event.type === "session_update") {
1337
2091
  if (event.session) {
1338
2092
  setSession(event.session);
@@ -1347,7 +2101,7 @@ function useSession(options) {
1347
2101
  isStreamingRef.current = false;
1348
2102
  }
1349
2103
  }, [onSessionChange, onError]);
1350
- const sendMessage = (0, import_react3.useCallback)(async (message) => {
2104
+ const sendMessage = (0, import_react5.useCallback)(async (message) => {
1351
2105
  if (!clientRef.current || !session || isStreamingRef.current) return;
1352
2106
  try {
1353
2107
  isStreamingRef.current = true;
@@ -1360,7 +2114,7 @@ function useSession(options) {
1360
2114
  isStreamingRef.current = false;
1361
2115
  }
1362
2116
  }, [session, handleSSEEvent, onError]);
1363
- const sendQuickAction = (0, import_react3.useCallback)(async (action) => {
2117
+ const sendQuickAction = (0, import_react5.useCallback)(async (action) => {
1364
2118
  if (!clientRef.current || !session || isStreamingRef.current) return;
1365
2119
  try {
1366
2120
  isStreamingRef.current = true;
@@ -1380,7 +2134,7 @@ function useSession(options) {
1380
2134
  isStreamingRef.current = false;
1381
2135
  }
1382
2136
  }, [session, handleSSEEvent, onSessionChange, onError]);
1383
- const applyPendingValue = (0, import_react3.useCallback)(async () => {
2137
+ const applyPendingValue = (0, import_react5.useCallback)(async () => {
1384
2138
  if (!clientRef.current || !session) return;
1385
2139
  try {
1386
2140
  setError(null);
@@ -1393,7 +2147,7 @@ function useSession(options) {
1393
2147
  onError?.(error2);
1394
2148
  }
1395
2149
  }, [session, onSessionChange, onError]);
1396
- const skipTask = (0, import_react3.useCallback)(async () => {
2150
+ const skipTask = (0, import_react5.useCallback)(async () => {
1397
2151
  if (!clientRef.current || !session) return;
1398
2152
  try {
1399
2153
  setError(null);
@@ -1418,9 +2172,11 @@ function useSession(options) {
1418
2172
  }
1419
2173
  // Annotate the CommonJS export names for ESM import in node:
1420
2174
  0 && (module.exports = {
2175
+ AdminChatbot,
1421
2176
  InformedAIClient,
1422
2177
  InformedAIProvider,
1423
2178
  InformedAssistant,
2179
+ WebsiteChatbot,
1424
2180
  useInformedAI,
1425
2181
  useSession
1426
2182
  });