@kite-copilot/chat-panel 0.2.43 → 0.2.44

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.cjs CHANGED
@@ -70,7 +70,7 @@ var import_react = __toESM(require("react"), 1);
70
70
  var import_client = require("react-dom/client");
71
71
 
72
72
  // src/ChatPanel.tsx
73
- var React5 = __toESM(require("react"), 1);
73
+ var React6 = __toESM(require("react"), 1);
74
74
  var import_supabase_js = require("@supabase/supabase-js");
75
75
 
76
76
  // src/lib/utils.ts
@@ -436,6 +436,47 @@ function useUserAuth({
436
436
  return { authState, retry };
437
437
  }
438
438
 
439
+ // src/hooks/useOrgConfig.ts
440
+ var React5 = __toESM(require("react"), 1);
441
+ function useOrgConfig({ agentUrl, orgId }) {
442
+ const [state, setState] = React5.useState({
443
+ status: "idle",
444
+ config: null,
445
+ error: null
446
+ });
447
+ React5.useEffect(() => {
448
+ if (!agentUrl || !orgId) {
449
+ console.log("[useOrgConfig] Skipping - missing agentUrl or orgId", { agentUrl, orgId });
450
+ return;
451
+ }
452
+ const fetchConfig = async () => {
453
+ setState({ status: "loading", config: null, error: null });
454
+ const url = `${agentUrl}/org/${orgId}/config`;
455
+ console.log("[useOrgConfig] Fetching org config from:", url);
456
+ try {
457
+ const response = await fetch(url, {
458
+ method: "GET",
459
+ headers: {
460
+ "Accept": "application/json"
461
+ }
462
+ });
463
+ if (!response.ok) {
464
+ throw new Error(`Failed to fetch org config (${response.status})`);
465
+ }
466
+ const config = await response.json();
467
+ console.log("[useOrgConfig] Received config:", config);
468
+ setState({ status: "success", config, error: null });
469
+ } catch (error) {
470
+ const message = error instanceof Error ? error.message : "Failed to fetch org config";
471
+ console.error("[useOrgConfig] Error:", message);
472
+ setState({ status: "error", config: null, error: message });
473
+ }
474
+ };
475
+ fetchConfig();
476
+ }, [agentUrl, orgId]);
477
+ return state;
478
+ }
479
+
439
480
  // src/components/ui/card.tsx
440
481
  var import_jsx_runtime7 = require("react/jsx-runtime");
441
482
  function Card({ className, ...props }) {
@@ -1036,7 +1077,7 @@ function TypingIndicator({ className = "" }) {
1036
1077
 
1037
1078
  // src/ChatPanel.tsx
1038
1079
  var import_jsx_runtime10 = require("react/jsx-runtime");
1039
- var CHAT_PANEL_VERSION = true ? "0.2.43" : "dev";
1080
+ var CHAT_PANEL_VERSION = true ? "0.2.44" : "dev";
1040
1081
  var DEFAULT_AGENT_URL = "http://localhost:5002";
1041
1082
  var PANEL_WIDTH = 340;
1042
1083
  function unescapeJsonString(str) {
@@ -1500,16 +1541,18 @@ function ChatPanel({
1500
1541
  supabaseAnonKey,
1501
1542
  productBackendUrl
1502
1543
  } = {}) {
1503
- const [messages, setMessages] = React5.useState(initialMessages);
1504
- const [input, setInput] = React5.useState("");
1505
- const [sessionId, setSessionId] = React5.useState(() => crypto.randomUUID());
1544
+ const [messages, setMessages] = React6.useState(initialMessages);
1545
+ const [input, setInput] = React6.useState("");
1546
+ const [sessionId, setSessionId] = React6.useState(() => crypto.randomUUID());
1547
+ const orgConfigState = useOrgConfig({ agentUrl, orgId: orgId || "" });
1548
+ const effectiveProductBackendUrl = orgConfigState.config?.productBackendUrl || productBackendUrl;
1506
1549
  const { authState, retry: retryAuth } = useUserAuth({
1507
- productBackendUrl,
1550
+ productBackendUrl: effectiveProductBackendUrl,
1508
1551
  sessionId,
1509
- enabled: !!productBackendUrl
1510
- // Only enable if URL is provided
1552
+ enabled: !!effectiveProductBackendUrl && orgConfigState.status === "success"
1553
+ // Only enable after config is fetched
1511
1554
  });
1512
- const effectiveUser = React5.useMemo(() => {
1555
+ const effectiveUser = React6.useMemo(() => {
1513
1556
  if (authState.status === "authenticated") {
1514
1557
  return {
1515
1558
  userId: authState.user.id,
@@ -1527,16 +1570,16 @@ function ChatPanel({
1527
1570
  isInternal: false
1528
1571
  };
1529
1572
  }, [authState, userId, userName, userEmail]);
1530
- const [isEscalated, setIsEscalated] = React5.useState(false);
1531
- const escalationWsRef = React5.useRef(null);
1532
- const [agentIsTyping, setAgentIsTyping] = React5.useState(false);
1533
- const supabaseRef = React5.useRef(null);
1534
- const typingChannelRef = React5.useRef(null);
1535
- const typingTimeoutRef = React5.useRef(null);
1536
- React5.useEffect(() => {
1573
+ const [isEscalated, setIsEscalated] = React6.useState(false);
1574
+ const escalationWsRef = React6.useRef(null);
1575
+ const [agentIsTyping, setAgentIsTyping] = React6.useState(false);
1576
+ const supabaseRef = React6.useRef(null);
1577
+ const typingChannelRef = React6.useRef(null);
1578
+ const typingTimeoutRef = React6.useRef(null);
1579
+ React6.useEffect(() => {
1537
1580
  console.log(`[KiteChat] Chat Panel v${CHAT_PANEL_VERSION} loaded`);
1538
1581
  }, []);
1539
- const resetSession = React5.useCallback(() => {
1582
+ const resetSession = React6.useCallback(() => {
1540
1583
  console.log("[KiteChat] resetSession called", { isEscalated, hasSupabase: !!supabaseRef.current, sessionId });
1541
1584
  if (isEscalated && supabaseRef.current && sessionId) {
1542
1585
  console.log("[KiteChat] Updating customer_status to disconnected for session:", sessionId);
@@ -1566,12 +1609,12 @@ function ChatPanel({
1566
1609
  typingChannelRef.current = null;
1567
1610
  }
1568
1611
  }, [isEscalated, sessionId]);
1569
- React5.useEffect(() => {
1612
+ React6.useEffect(() => {
1570
1613
  if (supabaseUrl && supabaseAnonKey && !supabaseRef.current) {
1571
1614
  supabaseRef.current = (0, import_supabase_js.createClient)(supabaseUrl, supabaseAnonKey);
1572
1615
  }
1573
1616
  }, [supabaseUrl, supabaseAnonKey]);
1574
- React5.useEffect(() => {
1617
+ React6.useEffect(() => {
1575
1618
  if (!isEscalated || !sessionId || !supabaseRef.current) {
1576
1619
  return;
1577
1620
  }
@@ -1608,8 +1651,8 @@ function ChatPanel({
1608
1651
  }
1609
1652
  };
1610
1653
  }, [isEscalated, sessionId]);
1611
- const heartbeatIntervalRef = React5.useRef(null);
1612
- const updateCustomerStatus = React5.useCallback(async (status) => {
1654
+ const heartbeatIntervalRef = React6.useRef(null);
1655
+ const updateCustomerStatus = React6.useCallback(async (status) => {
1613
1656
  if (!supabaseRef.current || !sessionId) return;
1614
1657
  try {
1615
1658
  await supabaseRef.current.from("escalations").update({
@@ -1620,7 +1663,7 @@ function ChatPanel({
1620
1663
  console.error("[KiteChat] Failed to update customer status:", err);
1621
1664
  }
1622
1665
  }, [sessionId]);
1623
- React5.useEffect(() => {
1666
+ React6.useEffect(() => {
1624
1667
  if (!isEscalated || !sessionId || !supabaseRef.current) {
1625
1668
  return;
1626
1669
  }
@@ -1668,7 +1711,7 @@ function ChatPanel({
1668
1711
  }
1669
1712
  };
1670
1713
  }, [isEscalated, sessionId, updateCustomerStatus]);
1671
- const sendTypingIndicator = React5.useCallback((isTyping) => {
1714
+ const sendTypingIndicator = React6.useCallback((isTyping) => {
1672
1715
  if (!typingChannelRef.current) {
1673
1716
  console.log("[KiteChat] Cannot send typing - channel not ready");
1674
1717
  return;
@@ -1684,8 +1727,8 @@ function ChatPanel({
1684
1727
  payload: { sender: "user", isTyping }
1685
1728
  });
1686
1729
  }, [isEscalated]);
1687
- const userTypingTimeoutRef = React5.useRef(null);
1688
- const handleTypingStart = React5.useCallback(() => {
1730
+ const userTypingTimeoutRef = React6.useRef(null);
1731
+ const handleTypingStart = React6.useCallback(() => {
1689
1732
  if (!isEscalated || !supabaseRef.current) return;
1690
1733
  sendTypingIndicator(true);
1691
1734
  updateCustomerStatus("active");
@@ -1696,19 +1739,19 @@ function ChatPanel({
1696
1739
  sendTypingIndicator(false);
1697
1740
  }, 1500);
1698
1741
  }, [isEscalated, sendTypingIndicator, updateCustomerStatus]);
1699
- const streamIntervals = React5.useRef({});
1742
+ const streamIntervals = React6.useRef({});
1700
1743
  const isEmpty = messages.length === 0;
1701
- const [phase, setPhase] = React5.useState("idle");
1702
- const [progressSteps, setProgressSteps] = React5.useState([]);
1703
- const phaseTimers = React5.useRef([]);
1744
+ const [phase, setPhase] = React6.useState("idle");
1745
+ const [progressSteps, setProgressSteps] = React6.useState([]);
1746
+ const phaseTimers = React6.useRef([]);
1704
1747
  const lastRole = messages.length ? messages[messages.length - 1].role : void 0;
1705
- const [panelView, setPanelView] = React5.useState(
1748
+ const [panelView, setPanelView] = React6.useState(
1706
1749
  "landing"
1707
1750
  );
1708
- const [currentFolderId, setCurrentFolderId] = React5.useState(void 0);
1709
- const [startingQuestions, setStartingQuestions] = React5.useState(startingQuestionsProp || defaultStartingQuestions);
1710
- const [loadingQuestions, setLoadingQuestions] = React5.useState(false);
1711
- React5.useEffect(() => {
1751
+ const [currentFolderId, setCurrentFolderId] = React6.useState(void 0);
1752
+ const [startingQuestions, setStartingQuestions] = React6.useState(startingQuestionsProp || defaultStartingQuestions);
1753
+ const [loadingQuestions, setLoadingQuestions] = React6.useState(false);
1754
+ React6.useEffect(() => {
1712
1755
  if (startingQuestionsEndpoint && !startingQuestionsProp) {
1713
1756
  setLoadingQuestions(true);
1714
1757
  fetch(startingQuestionsEndpoint).then((res) => res.json()).then((data) => {
@@ -1720,16 +1763,16 @@ function ChatPanel({
1720
1763
  }).finally(() => setLoadingQuestions(false));
1721
1764
  }
1722
1765
  }, [startingQuestionsEndpoint, startingQuestionsProp]);
1723
- React5.useEffect(() => {
1766
+ React6.useEffect(() => {
1724
1767
  if (startingQuestionsProp) {
1725
1768
  setStartingQuestions(startingQuestionsProp);
1726
1769
  }
1727
1770
  }, [startingQuestionsProp]);
1728
- const [activeGuide, setActiveGuide] = React5.useState(void 0);
1729
- const activeGuideRef = React5.useRef(void 0);
1730
- const latestBulkSummaryNavigationRef = React5.useRef(null);
1731
- const [guideComplete, setGuideComplete] = React5.useState(false);
1732
- React5.useEffect(() => {
1771
+ const [activeGuide, setActiveGuide] = React6.useState(void 0);
1772
+ const activeGuideRef = React6.useRef(void 0);
1773
+ const latestBulkSummaryNavigationRef = React6.useRef(null);
1774
+ const [guideComplete, setGuideComplete] = React6.useState(false);
1775
+ React6.useEffect(() => {
1733
1776
  window.resetIntegrationNotification = () => {
1734
1777
  localStorage.removeItem("gmailNotificationSeen");
1735
1778
  console.log(
@@ -1763,7 +1806,7 @@ function ChatPanel({
1763
1806
  );
1764
1807
  };
1765
1808
  }, []);
1766
- React5.useEffect(() => {
1809
+ React6.useEffect(() => {
1767
1810
  if (activeGuide) {
1768
1811
  if (!activeGuideRef.current || activeGuideRef.current.id !== activeGuide.id || activeGuideRef.current.stepIndex !== activeGuide.stepIndex) {
1769
1812
  activeGuideRef.current = activeGuide;
@@ -1772,21 +1815,21 @@ function ChatPanel({
1772
1815
  activeGuideRef.current = void 0;
1773
1816
  }
1774
1817
  }, [activeGuide]);
1775
- const [pendingNavigation, setPendingNavigation] = React5.useState(null);
1776
- const [pendingAction, setPendingAction] = React5.useState(null);
1777
- const [actionFormData, setActionFormData] = React5.useState({});
1778
- const messagesEndRef = React5.useRef(null);
1779
- const messagesContainerRef = React5.useRef(null);
1780
- const currentStepRef = React5.useRef(null);
1818
+ const [pendingNavigation, setPendingNavigation] = React6.useState(null);
1819
+ const [pendingAction, setPendingAction] = React6.useState(null);
1820
+ const [actionFormData, setActionFormData] = React6.useState({});
1821
+ const messagesEndRef = React6.useRef(null);
1822
+ const messagesContainerRef = React6.useRef(null);
1823
+ const currentStepRef = React6.useRef(null);
1781
1824
  const { cursorState, moveTo, hide } = useGuideCursor();
1782
- const [pendingFile, setPendingFile] = React5.useState(null);
1783
- const [pendingBulkSession, setPendingBulkSession] = React5.useState(null);
1784
- const pendingBulkSessionRef = React5.useRef(null);
1785
- const fileInputRef = React5.useRef(null);
1786
- const [searchExpanded, setSearchExpanded] = React5.useState(false);
1787
- const [searchInput, setSearchInput] = React5.useState("");
1788
- const searchInputRef = React5.useRef(null);
1789
- React5.useEffect(() => {
1825
+ const [pendingFile, setPendingFile] = React6.useState(null);
1826
+ const [pendingBulkSession, setPendingBulkSession] = React6.useState(null);
1827
+ const pendingBulkSessionRef = React6.useRef(null);
1828
+ const fileInputRef = React6.useRef(null);
1829
+ const [searchExpanded, setSearchExpanded] = React6.useState(false);
1830
+ const [searchInput, setSearchInput] = React6.useState("");
1831
+ const searchInputRef = React6.useRef(null);
1832
+ React6.useEffect(() => {
1790
1833
  if (!activeGuide || activeGuide.id !== "add-api-key" || activeGuide.stepIndex !== 2) {
1791
1834
  return;
1792
1835
  }
@@ -1812,7 +1855,7 @@ function ChatPanel({
1812
1855
  const interval = setInterval(checkForDialogOpen, 300);
1813
1856
  return () => clearInterval(interval);
1814
1857
  }, [activeGuide, hide]);
1815
- React5.useEffect(() => {
1858
+ React6.useEffect(() => {
1816
1859
  return () => {
1817
1860
  Object.values(streamIntervals.current).forEach(
1818
1861
  (id) => window.clearInterval(id)
@@ -1822,7 +1865,7 @@ function ChatPanel({
1822
1865
  phaseTimers.current = [];
1823
1866
  };
1824
1867
  }, []);
1825
- React5.useEffect(() => {
1868
+ React6.useEffect(() => {
1826
1869
  if (activeGuide && messages.length > 0) {
1827
1870
  const lastMessage = messages[messages.length - 1];
1828
1871
  if (lastMessage.kind === "guideStep" || lastMessage.kind === "guideComplete") {
@@ -1839,7 +1882,7 @@ function ChatPanel({
1839
1882
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1840
1883
  }
1841
1884
  }, [messages, phase, activeGuide]);
1842
- const latestBulkSummaryNavigation = React5.useMemo(() => {
1885
+ const latestBulkSummaryNavigation = React6.useMemo(() => {
1843
1886
  for (let i = messages.length - 1; i >= 0; i--) {
1844
1887
  const msg = messages[i];
1845
1888
  if (msg.kind === "bulkSummary" && msg.bulkSummary?.navigationPage && msg.bulkSummary.successes > 0) {
@@ -1848,13 +1891,13 @@ function ChatPanel({
1848
1891
  }
1849
1892
  return null;
1850
1893
  }, [messages]);
1851
- React5.useEffect(() => {
1894
+ React6.useEffect(() => {
1852
1895
  latestBulkSummaryNavigationRef.current = latestBulkSummaryNavigation;
1853
1896
  }, [latestBulkSummaryNavigation]);
1854
- React5.useEffect(() => {
1897
+ React6.useEffect(() => {
1855
1898
  pendingBulkSessionRef.current = pendingBulkSession;
1856
1899
  }, [pendingBulkSession]);
1857
- React5.useEffect(() => {
1900
+ React6.useEffect(() => {
1858
1901
  const handleKeyDown = (e) => {
1859
1902
  if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
1860
1903
  const currentBulkSession = pendingBulkSessionRef.current;
@@ -1917,7 +1960,7 @@ function ChatPanel({
1917
1960
  guideComplete,
1918
1961
  onNavigate
1919
1962
  ]);
1920
- const connectToEscalationWs = React5.useCallback((currentSessionId) => {
1963
+ const connectToEscalationWs = React6.useCallback((currentSessionId) => {
1921
1964
  if (!agentUrl) return;
1922
1965
  if (escalationWsRef.current) {
1923
1966
  escalationWsRef.current.close();
@@ -1960,7 +2003,7 @@ function ChatPanel({
1960
2003
  };
1961
2004
  escalationWsRef.current = ws;
1962
2005
  }, [agentUrl]);
1963
- const sendEscalatedMessage = React5.useCallback(async (content) => {
2006
+ const sendEscalatedMessage = React6.useCallback(async (content) => {
1964
2007
  if (!escalationWsRef.current || escalationWsRef.current.readyState !== WebSocket.OPEN) {
1965
2008
  console.error("[KiteChat] Escalation WebSocket not connected");
1966
2009
  return false;
@@ -1977,14 +2020,14 @@ function ChatPanel({
1977
2020
  return false;
1978
2021
  }
1979
2022
  }, [updateCustomerStatus]);
1980
- React5.useEffect(() => {
2023
+ React6.useEffect(() => {
1981
2024
  return () => {
1982
2025
  if (escalationWsRef.current) {
1983
2026
  escalationWsRef.current.close();
1984
2027
  }
1985
2028
  };
1986
2029
  }, []);
1987
- React5.useEffect(() => {
2030
+ React6.useEffect(() => {
1988
2031
  if (isEscalated && sessionId) {
1989
2032
  connectToEscalationWs(sessionId);
1990
2033
  }
@@ -3213,7 +3256,38 @@ ${userText}`
3213
3256
  ] })
3214
3257
  ] }) }) });
3215
3258
  }
3216
- if (productBackendUrl) {
3259
+ if (orgConfigState.status === "loading") {
3260
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3261
+ "section",
3262
+ {
3263
+ className: `fixed top-0 right-0 z-40 flex flex-col bg-white border-l border-gray-200 h-full overflow-hidden transition-transform duration-300 ${isOpen ? "translate-x-0" : "translate-x-full"}`,
3264
+ style: { width: `${PANEL_WIDTH}px` },
3265
+ children: [
3266
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between px-4 py-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white shrink-0", children: [
3267
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2.5", children: [
3268
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.Sparkles, { className: "h-3.5 w-3.5 text-black", fill: "currentColor" }),
3269
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-sm font-semibold text-gray-800", children: "Copilot" })
3270
+ ] }),
3271
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3272
+ Button,
3273
+ {
3274
+ variant: "ghost",
3275
+ size: "sm",
3276
+ className: "h-7 w-7 p-0 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full",
3277
+ onClick: () => onClose?.(),
3278
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.X, { className: "h-4 w-4" })
3279
+ }
3280
+ )
3281
+ ] }),
3282
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "text-center", children: [
3283
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.Loader2, { className: "h-8 w-8 animate-spin text-gray-400 mx-auto mb-3" }),
3284
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-sm text-gray-500", children: "Loading..." })
3285
+ ] }) })
3286
+ ]
3287
+ }
3288
+ );
3289
+ }
3290
+ if (effectiveProductBackendUrl) {
3217
3291
  if (authState.status === "loading") {
3218
3292
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
3219
3293
  "section",
@@ -4388,7 +4462,7 @@ ${userText}`
4388
4462
  message.id
4389
4463
  );
4390
4464
  }
4391
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(React5.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4465
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(React6.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
4392
4466
  "div",
4393
4467
  {
4394
4468
  ref: isCurrentGuideStep ? currentStepRef : null,
@@ -4645,7 +4719,7 @@ function ChatPanelWithToggle({
4645
4719
  supabaseAnonKey,
4646
4720
  productBackendUrl
4647
4721
  }) {
4648
- const [internalIsOpen, setInternalIsOpen] = React5.useState(defaultOpen);
4722
+ const [internalIsOpen, setInternalIsOpen] = React6.useState(defaultOpen);
4649
4723
  const isOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalIsOpen;
4650
4724
  const setIsOpen = (open) => {
4651
4725
  if (controlledIsOpen === void 0) {
@@ -4653,7 +4727,7 @@ function ChatPanelWithToggle({
4653
4727
  }
4654
4728
  onOpenChange?.(open);
4655
4729
  };
4656
- React5.useEffect(() => {
4730
+ React6.useEffect(() => {
4657
4731
  const originalPadding = document.body.style.paddingRight;
4658
4732
  const originalTransition = document.body.style.transition;
4659
4733
  document.body.style.transition = "padding-right 0.3s ease";
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  cn,
32
32
  createKiteChat,
33
33
  useGuideCursor
34
- } from "./chunk-XZM4VX5Y.js";
34
+ } from "./chunk-G74XTXWW.js";
35
35
  export {
36
36
  ApiKeyList,
37
37
  AssistantActivity,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kite-copilot/chat-panel",
3
- "version": "0.2.43",
3
+ "version": "0.2.44",
4
4
  "description": "AI-powered chat panel SDK with programmatic lifecycle control",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",