sagedesk 2.2.0 → 2.3.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/README.md CHANGED
@@ -40,6 +40,15 @@ Because the embedder stays in the browser, the server function carries no native
40
40
 
41
41
  ---
42
42
 
43
+ ## Framework Support
44
+
45
+ | | Supported |
46
+ |---|---|
47
+ | **Frontend** | Vanilla HTML/JS, React, Next.js (App Router) |
48
+ | **Backend** (LLM Mode) | Next.js API Routes, Express, any Node.js server, Vercel, AWS Lambda |
49
+
50
+ ---
51
+
43
52
  ## Installation
44
53
 
45
54
  ```bash
@@ -326,41 +335,10 @@ sagedesk includes built-in resilience for LLM mode. If the LLM provider fails-wh
326
335
 
327
336
  2. **Automatic Fallback** - When an LLM request fails, the server returns the best matching knowledge chunks without synthesis. Visitors still get relevant, grounded information.
328
337
 
329
- 3. **Developer Transparency** - The browser console logs meaningful warnings for debugging:
330
- - `"[sagedesk] Support service authentication failed. Showing relevant knowledge instead."` - Invalid or expired API key
331
- - `"[sagedesk] Support service quota exhausted. Showing relevant knowledge instead."` - Rate limit hit
332
- - `"[sagedesk] Support service took too long to respond. Showing relevant knowledge instead."` - Timeout
333
- - `"[sagedesk] Support service error. Showing relevant knowledge instead."` - Generic API error
334
- - `"[sagedesk] Support service returned invalid response. Showing relevant knowledge instead."` - Malformed response
338
+ 3. **Developer Transparency** - The browser console logs a `[sagedesk]` warning for each failure class: auth error, quota exhausted, timeout, generic API error, and malformed response. Each message describes the cause and notes that the fallback was triggered.
335
339
 
336
340
  4. **User Experience** - Visitors always see a fallback message (configured via `agent.fallback` or `agent.fallbackPool`) alongside relevant knowledge chunks. No errors are exposed to users.
337
341
 
338
- ### Configuring Timeout
339
-
340
- Adjust the LLM request timeout based on your provider's typical response time:
341
-
342
- ```ts
343
- // Next.js
344
- export const POST = createSageDeskHandler({
345
- indexPath: './public/support-index.json',
346
- provider: 'deepseek',
347
- apiKey: process.env.SAGEDESK_LLM_API_KEY!,
348
- model: 'deepseek-chat',
349
- llmTimeoutMs: 8000, // 8 seconds
350
- });
351
- ```
352
-
353
- ```ts
354
- // Express
355
- app.use('/api/sagedesk', createSageDeskMiddleware({
356
- indexPath: './public/support-index.json',
357
- provider: 'openai',
358
- apiKey: process.env.SAGEDESK_LLM_API_KEY!,
359
- model: 'gpt-4o-mini',
360
- llmTimeoutMs: 10000, // 10 seconds
361
- }));
362
- ```
363
-
364
342
  ---
365
343
 
366
344
  ## Widget Configuration (`AgentConfig`)
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
 
3
3
  // src/react/SageDeskWidget.tsx
4
- import {
4
+ import React, {
5
5
  useState as useState2,
6
6
  useRef as useRef2,
7
7
  useEffect as useEffect2,
8
- useCallback as useCallback2
8
+ useCallback as useCallback2,
9
+ useMemo as useMemo2
9
10
  } from "react";
10
11
  import { createPortal } from "react-dom";
11
12
 
@@ -265,6 +266,7 @@ function logFallbackWarning(reason) {
265
266
  }
266
267
  var initialState = {
267
268
  messages: [],
269
+ userMessages: [],
268
270
  isOpen: false,
269
271
  isTyping: false,
270
272
  engineStatus: "idle",
@@ -277,8 +279,11 @@ function reducer(state, action) {
277
279
  return { ...state, isOpen: true };
278
280
  case "CLOSE":
279
281
  return { ...state, isOpen: false };
280
- case "ADD_MESSAGE":
281
- return { ...state, messages: [...state.messages, action.payload] };
282
+ case "ADD_MESSAGE": {
283
+ const newMessages = [...state.messages, action.payload];
284
+ const newUserMessages = action.payload.role === "user" ? [...state.userMessages, action.payload] : state.userMessages;
285
+ return { ...state, messages: newMessages, userMessages: newUserMessages };
286
+ }
282
287
  case "SET_TYPING":
283
288
  return { ...state, isTyping: action.payload };
284
289
  case "SET_ENGINE_STATUS":
@@ -301,6 +306,7 @@ function useSageDesk(config) {
301
306
  const embedderRef = useRef(null);
302
307
  const engineStartedRef = useRef(false);
303
308
  const msgCounterRef = useRef(0);
309
+ const engineReadyCallbacksRef = useRef([]);
304
310
  const [chips, setChips] = useState([]);
305
311
  const makeId = () => `msg-${++msgCounterRef.current}`;
306
312
  const addMessage = useCallback(
@@ -312,6 +318,11 @@ function useSageDesk(config) {
312
318
  },
313
319
  []
314
320
  );
321
+ const notifyEngineReady = useCallback(() => {
322
+ const callbacks = engineReadyCallbacksRef.current;
323
+ engineReadyCallbacksRef.current = [];
324
+ callbacks.forEach((cb) => cb());
325
+ }, []);
315
326
  const startEngine = useCallback(async () => {
316
327
  if (engineStartedRef.current) return;
317
328
  engineStartedRef.current = true;
@@ -322,9 +333,11 @@ function useSageDesk(config) {
322
333
  embedderRef.current = new EmbedderRuntime();
323
334
  await embedderRef.current.load(config.agent.model);
324
335
  dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "ready" } });
336
+ notifyEngineReady();
325
337
  } catch (err) {
326
338
  console.warn("[sagedesk] WASM embedder failed to load - LLM mode will use fallback messages.", err);
327
339
  dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "error-model", error: String(err) } });
340
+ notifyEngineReady();
328
341
  }
329
342
  return;
330
343
  }
@@ -337,6 +350,7 @@ function useSageDesk(config) {
337
350
  type: "SET_ENGINE_STATUS",
338
351
  payload: { status: "error-index", error: String(err) }
339
352
  });
353
+ notifyEngineReady();
340
354
  addMessage({
341
355
  role: "bot",
342
356
  text: "I'm having trouble loading right now. Please try again in a moment."
@@ -349,12 +363,14 @@ function useSageDesk(config) {
349
363
  embedderRef.current = new EmbedderRuntime();
350
364
  await embedderRef.current.load(config.agent.model);
351
365
  dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "ready" } });
366
+ notifyEngineReady();
352
367
  } catch (err) {
353
368
  console.warn("[sagedesk] WASM model failed to load, falling back to keyword search -", err);
354
369
  embedderRef.current = new EmbedderRuntime();
355
370
  dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "degraded" } });
371
+ notifyEngineReady();
356
372
  }
357
- }, [config.mode, config.indexUrl, config.agent.model, config.agent.suggestedChips, addMessage]);
373
+ }, [config.mode, config.indexUrl, config.agent.model, config.agent.suggestedChips, addMessage, notifyEngineReady]);
358
374
  const greetingShownRef = useRef(false);
359
375
  const open = useCallback(() => {
360
376
  dispatch({ type: "OPEN" });
@@ -371,16 +387,12 @@ function useSageDesk(config) {
371
387
  dispatch({ type: "CLOSE" });
372
388
  }, []);
373
389
  const waitForEngine = useCallback(() => {
390
+ const s = engineStatusRef.current;
391
+ if (s === "ready" || s === "degraded" || s === "error-index" || s === "error-model") {
392
+ return Promise.resolve();
393
+ }
374
394
  return new Promise((resolve) => {
375
- const check = () => {
376
- const s = engineStatusRef.current;
377
- if (s === "ready" || s === "degraded" || s === "error-index" || s === "error-model") {
378
- resolve();
379
- } else {
380
- setTimeout(check, 100);
381
- }
382
- };
383
- check();
395
+ engineReadyCallbacksRef.current.push(resolve);
384
396
  });
385
397
  }, []);
386
398
  const submit = useCallback(
@@ -461,8 +473,8 @@ function useSageDesk(config) {
461
473
  }
462
474
  if (config.mode !== "llm") {
463
475
  const elapsed = Date.now() - typingStart;
464
- const delayBase = retrievalMode === "keyword" || isFallback ? 800 : 3e3;
465
- const minTypingMs = delayBase + Math.random() * 2e3;
476
+ const delayBase = retrievalMode === "keyword" || isFallback ? 400 : 800;
477
+ const minTypingMs = delayBase + Math.random() * 400;
466
478
  const remaining = minTypingMs - elapsed;
467
479
  if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
468
480
  }
@@ -480,11 +492,9 @@ function useSageDesk(config) {
480
492
  return () => document.removeEventListener("keydown", handler);
481
493
  }, [state.isOpen, close]);
482
494
  const activeChips = useMemo(() => {
483
- const askedTexts = new Set(
484
- state.messages.filter((m) => m.role === "user").map((m) => m.text.toLowerCase().trim())
485
- );
495
+ const askedTexts = new Set(state.userMessages.map((m) => m.text.toLowerCase().trim()));
486
496
  return chips.filter((chip) => !askedTexts.has(chip.toLowerCase().trim()));
487
- }, [chips, state.messages]);
497
+ }, [chips, state.userMessages]);
488
498
  return { state, chips: activeChips, open, close, submit };
489
499
  }
490
500
 
@@ -680,9 +690,9 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ jsxs("div", { style: {
680
690
  }
681
691
  )
682
692
  ] });
683
- function ClassicMessageBubble({ msg, accent }) {
693
+ var ClassicMessageBubble = React.memo(function ClassicMessageBubble2({ msg, accent }) {
684
694
  const isBot = msg.role === "bot";
685
- const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
695
+ const renderedHtml = useMemo2(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);
686
696
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: isBot ? "flex-start" : "flex-end", gap: "4px" }, children: [
687
697
  msg.isFallback && /* @__PURE__ */ jsx("p", { style: { fontSize: "11px", fontWeight: 500, color: "#9b9aa3", margin: 0, padding: "0 4px", fontFamily: "inherit" }, children: "Not sure about that one" }),
688
698
  /* @__PURE__ */ jsx("div", { style: {
@@ -707,7 +717,7 @@ function ClassicMessageBubble({ msg, accent }) {
707
717
  fontFamily: "inherit"
708
718
  }, children: "just now" })
709
719
  ] });
710
- }
720
+ });
711
721
  function ClassicTypingIndicator() {
712
722
  const dot = { width: 6, height: 6, borderRadius: "50%", background: "#c8c8ce", display: "inline-block" };
713
723
  return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-start", gap: "4px" }, children: /* @__PURE__ */ jsxs("div", { style: {
@@ -725,9 +735,9 @@ function ClassicTypingIndicator() {
725
735
  /* @__PURE__ */ jsx("span", { style: dot, className: "sd-r-dot-3" })
726
736
  ] }) });
727
737
  }
728
- function LightMessageBubble({ msg, accent, agentName }) {
738
+ var LightMessageBubble = React.memo(function LightMessageBubble2({ msg, accent, agentName }) {
729
739
  const isBot = msg.role === "bot";
730
- const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
740
+ const renderedHtml = useMemo2(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);
731
741
  if (isBot) {
732
742
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px" }, children: [
733
743
  /* @__PURE__ */ jsx("div", { style: {
@@ -762,7 +772,7 @@ function LightMessageBubble({ msg, accent, agentName }) {
762
772
  wordBreak: "break-word",
763
773
  fontFamily: "inherit"
764
774
  }, children: msg.text }) });
765
- }
775
+ });
766
776
  function LightTypingIndicator({ accent }) {
767
777
  const dot = { width: 6, height: 6, borderRadius: "50%", background: "#c4c4be", display: "inline-block" };
768
778
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px" }, children: [
@@ -790,9 +800,9 @@ function LightTypingIndicator({ accent }) {
790
800
  ] })
791
801
  ] });
792
802
  }
793
- function DarkMessageBubble({ msg, accent }) {
803
+ var DarkMessageBubble = React.memo(function DarkMessageBubble2({ msg, accent }) {
794
804
  const isBot = msg.role === "bot";
795
- const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
805
+ const renderedHtml = useMemo2(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);
796
806
  return /* @__PURE__ */ jsxs("div", { style: {
797
807
  maxWidth: "85%",
798
808
  padding: "12px 14px",
@@ -810,7 +820,7 @@ function DarkMessageBubble({ msg, accent }) {
810
820
  msg.isFallback && /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: "rgba(255,255,255,0.5)", display: "block", marginBottom: "4px" }, children: "Not sure about that one" }),
811
821
  isBot ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text })
812
822
  ] });
813
- }
823
+ });
814
824
  function DarkTypingIndicator() {
815
825
  const dot = { width: 6, height: 6, borderRadius: "50%", background: "rgba(255,255,255,0.4)", display: "inline-block" };
816
826
  return /* @__PURE__ */ jsxs("div", { style: {
@@ -829,7 +839,7 @@ function DarkTypingIndicator() {
829
839
  /* @__PURE__ */ jsx("span", { style: dot, className: "sd-r-dot-3" })
830
840
  ] });
831
841
  }
832
- function renderClassic(p) {
842
+ function ClassicTheme(p) {
833
843
  const {
834
844
  agent,
835
845
  state,
@@ -1018,7 +1028,7 @@ function renderClassic(p) {
1018
1028
  ] })
1019
1029
  ] });
1020
1030
  }
1021
- function renderLight(p) {
1031
+ function LightTheme(p) {
1022
1032
  const {
1023
1033
  agent,
1024
1034
  state,
@@ -1206,7 +1216,7 @@ function renderLight(p) {
1206
1216
  ] })
1207
1217
  ] });
1208
1218
  }
1209
- function renderDark(p) {
1219
+ function DarkTheme(p) {
1210
1220
  const {
1211
1221
  agent,
1212
1222
  state,
@@ -1443,7 +1453,10 @@ function SageDeskWidget({ mode, indexUrl, endpoint, agent, search: search2 }) {
1443
1453
  '[sagedesk] Required prop "endpoint" is missing for llm mode. Provide your backend route, e.g. endpoint="/api/sagedesk".'
1444
1454
  );
1445
1455
  }
1446
- const config = { mode: resolvedMode, indexUrl, endpoint, agent, search: search2 };
1456
+ const config = useMemo2(
1457
+ () => ({ mode: resolvedMode, indexUrl, endpoint, agent, search: search2 }),
1458
+ [resolvedMode, indexUrl, endpoint, agent, search2]
1459
+ );
1447
1460
  const { state, chips, open, close, submit } = useSageDesk(config);
1448
1461
  const theme = agent.theme ?? "classic";
1449
1462
  const accent = agent.accentColor ?? "#534AB7";
@@ -1522,10 +1535,10 @@ function SageDeskWidget({ mode, indexUrl, endpoint, agent, search: search2 }) {
1522
1535
  open,
1523
1536
  submit
1524
1537
  };
1525
- const content = theme === "dark" ? renderDark(props) : theme === "light" ? renderLight(props) : renderClassic(props);
1538
+ const content = theme === "dark" ? /* @__PURE__ */ jsx(DarkTheme, { ...props }) : theme === "light" ? /* @__PURE__ */ jsx(LightTheme, { ...props }) : /* @__PURE__ */ jsx(ClassicTheme, { ...props });
1526
1539
  return createPortal(content, document.body);
1527
1540
  }
1528
1541
  export {
1529
1542
  SageDeskWidget
1530
1543
  };
1531
- //# sourceMappingURL=SageDeskWidget-TNVTYDDX.js.map
1544
+ //# sourceMappingURL=SageDeskWidget-ZJJGXTTC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/SageDeskWidget.tsx","../../src/react/useSageDesk.ts","../../src/core/embedder.ts","../../src/core/search.ts","../../src/core/retriever.ts","../../src/core/renderer.ts","../../src/core/fallback.ts","../../src/react/markdownUtils.ts"],"sourcesContent":["import React, {\n useState,\n useRef,\n useEffect,\n useCallback,\n useMemo,\n CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { useSageDesk } from './useSageDesk.js';\nimport { parseMarkdown } from './markdownUtils.js';\nimport type { SageDeskConfig, ChatMessage, Theme, SageDeskMode } from '../core/types.js';\n\n// ─── Per-theme CSS injection ──────────────────────────────────────────────────\n// Only the styles for the active theme are injected, keeping the runtime\n// stylesheet small. Each block includes shared keyframes + theme-specific rules.\n\nconst STYLE_ID = 'sagedesk-widget-styles';\n\nconst SHARED = `\n @keyframes sd-bounce {\n 0%, 60%, 100% { transform: translateY(0); }\n 30% { transform: translateY(-5px); }\n }\n @keyframes sd-panel-open {\n from { transform: scale(0.94) translateY(6px); opacity: 0; }\n to { transform: scale(1) translateY(0); opacity: 1; }\n }\n @keyframes sd-panel-close {\n from { transform: scale(1) translateY(0); opacity: 1; }\n to { transform: scale(0.94) translateY(6px); opacity: 0; }\n }\n .sd-r-opening { animation: sd-panel-open 200ms cubic-bezier(0.34,1.56,0.64,1) both; }\n .sd-r-closing { animation: sd-panel-close 150ms ease-in both; }\n .sd-r-dot-1 { animation: sd-bounce 1.2s ease-in-out infinite; }\n .sd-r-dot-2 { animation: sd-bounce 1.2s ease-in-out 0.2s infinite; }\n .sd-r-dot-3 { animation: sd-bounce 1.2s ease-in-out 0.4s infinite; }\n .sd-r-scrollable {\n flex-wrap: nowrap !important;\n overflow-x: auto !important;\n scrollbar-width: none !important;\n -ms-overflow-style: none !important;\n }\n .sd-r-scrollable::-webkit-scrollbar { display: none !important; }\n .sd-r-scrollable > * { flex-shrink: 0 !important; }\n .sd-r-markdown h1, .sd-r-markdown h2, .sd-r-markdown h3, .sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 {\n margin: 12px 0 8px 0 !important; font-weight: 600 !important; line-height: 1.3 !important;\n }\n .sd-r-markdown h1 { font-size: 1.3em !important; }\n .sd-r-markdown h2 { font-size: 1.2em !important; }\n .sd-r-markdown h3 { font-size: 1.1em !important; }\n .sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 { font-size: 1em !important; }\n .sd-r-markdown strong { font-weight: 600 !important; }\n .sd-r-markdown em { font-style: italic !important; }\n .sd-r-markdown u { text-decoration: underline !important; }\n .sd-r-markdown ul, .sd-r-markdown ol { margin: 8px 0 !important; padding-left: 20px !important; }\n .sd-r-markdown li { margin: 4px 0 !important; }\n .sd-r-markdown blockquote { margin: 8px 0 !important; padding-left: 12px !important; border-left: 3px solid currentColor !important; opacity: 0.8 !important; }\n .sd-r-markdown code { font-family: 'Monaco', 'Courier New', monospace !important; font-size: 0.9em !important; padding: 2px 4px !important; background: rgba(0,0,0,0.05) !important; border-radius: 3px !important; }\n .sd-r-markdown pre { background: rgba(0,0,0,0.05) !important; padding: 8px 10px !important; border-radius: 6px !important; overflow-x: auto !important; margin: 8px 0 !important; }\n .sd-r-markdown pre code { background: none !important; padding: 0 !important; }\n .sd-r-markdown hr { border: none !important; border-top: 1px solid currentColor !important; opacity: 0.3 !important; margin: 10px 0 !important; }\n .sd-r-markdown a { text-decoration: underline !important; opacity: 0.9 !important; }\n .sd-r-markdown a:hover { opacity: 1 !important; }\n .sd-r-markdown p { margin: 6px 0 !important; }\n .sd-r-markdown > *:first-child { margin-top: 0 !important; }\n .sd-r-markdown > *:last-child { margin-bottom: 0 !important; }\n @media (max-width: 420px) {\n .sd-r-panel {\n bottom: 0 !important; right: 0 !important; left: 0 !important;\n width: auto !important; max-width: 100% !important;\n border-radius: 20px 20px 0 0 !important;\n max-height: 85vh !important;\n transform-origin: bottom center !important;\n }\n }\n`;\n\nconst THEME_CSS: Record<Theme, string> = {\n classic: `${SHARED}\n .sd-r-trigger:hover { transform: scale(1.06) !important; }\n .sd-r-close-btn:hover { background: rgba(255,255,255,0.22) !important; }\n .sd-r-chip:hover { opacity: 0.8; }\n .sd-r-send:hover { opacity: 0.85; }\n .sd-r-send:active { transform: scale(0.95); }\n .sd-r-input:focus { outline: none; }\n .sd-r-input::placeholder { color: #a8a8b0; }\n `,\n light: `${SHARED}\n .sd-r-trigger-light:hover { box-shadow: 0 16px 32px -10px rgba(40,30,90,0.24),0 2px 10px rgba(40,30,90,0.08) !important; }\n .sd-r-chip-light:hover { opacity: 0.75; }\n .sd-r-send:hover { opacity: 0.85; }\n .sd-r-send:active { transform: scale(0.95); }\n .sd-r-input:focus { outline: none; }\n .sd-r-input::placeholder { color: #a8a89e; }\n `,\n dark: `${SHARED}\n @keyframes sd-blink { 0%,100%{opacity:1} 50%{opacity:0} }\n .sd-r-cursor { animation: sd-blink 1s infinite; }\n .sd-r-trigger:hover { transform: scale(1.04) !important; }\n .sd-r-prompt:hover { background: rgba(255,255,255,0.07) !important; }\n .sd-r-send:hover { opacity: 0.85; }\n .sd-r-send:active { transform: scale(0.95); }\n .sd-r-input:focus { outline: none; }\n .sd-r-input::placeholder { color: rgba(255,255,255,0.35); }\n `,\n};\n\nfunction injectStyles(theme: Theme): void {\n if (typeof document === 'undefined') return;\n const id = `${STYLE_ID}-${theme}`;\n if (document.getElementById(id)) return;\n const style = document.createElement('style');\n style.id = id;\n style.textContent = THEME_CSS[theme];\n document.head.prepend(style);\n}\n\n// ─── Icons ────────────────────────────────────────────────────────────────────\n\nconst IconChat = ({ size = 22 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M5 6.5A2.5 2.5 0 017.5 4h9A2.5 2.5 0 0119 6.5v7A2.5 2.5 0 0116.5 16H11l-4 3.5V16H7.5A2.5 2.5 0 015 13.5v-7z\"\n stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nconst IconSend = ({ size = 14 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M4 12L20 4l-4 16-4-7-8-1z\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinejoin=\"round\" />\n </svg>\n);\n\nconst IconClose = ({ size = 14 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M6 6l12 12M18 6L6 18\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" />\n </svg>\n);\n\nconst IconBot = ({ size = 22 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <rect x=\"4\" y=\"8\" width=\"16\" height=\"12\" rx=\"3\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n <circle cx=\"9\" cy=\"13.5\" r=\"1.5\" fill=\"currentColor\" />\n <circle cx=\"15\" cy=\"13.5\" r=\"1.5\" fill=\"currentColor\" />\n <path d=\"M9.5 16.5h5\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" />\n <path d=\"M12 8V5\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" />\n <circle cx=\"12\" cy=\"4\" r=\"1.2\" fill=\"currentColor\" />\n </svg>\n);\n\nconst IconPerson = ({ size = 18 }: { size?: number }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M12 12.5a4 4 0 100-8 4 4 0 000 8z\" />\n <path d=\"M5 20.5c0-3.5 3.13-6 7-6s7 2.5 7 6\" />\n </svg>\n);\n\n// ─── Shared sub-components ────────────────────────────────────────────────────\n\nconst PoweredBy = ({ dark = false }: { dark?: boolean }) => (\n <div style={{\n fontSize: '11px',\n color: dark ? 'rgba(255,255,255,0.35)' : '#a8a8b0',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '4px',\n }}>\n Powered by{' '}\n <a\n href=\"https://github.com/mzeeshanwahid/sagedesk\"\n target=\"_blank\"\n rel=\"noopener\"\n style={{\n color: dark ? 'rgba(255,255,255,0.7)' : '#5a5a64',\n fontWeight: 500,\n textDecoration: 'none',\n }}\n >\n sagedesk\n </a>\n </div>\n);\n\n// ─── Classic theme components ─────────────────────────────────────────────────\n\nconst ClassicMessageBubble = React.memo(function ClassicMessageBubble({ msg, accent }: { msg: ChatMessage; accent: string }) {\n const isBot = msg.role === 'bot';\n const renderedHtml = useMemo(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: isBot ? 'flex-start' : 'flex-end', gap: '4px' }}>\n {msg.isFallback && (\n <p style={{ fontSize: '11px', fontWeight: 500, color: '#9b9aa3', margin: 0, padding: '0 4px', fontFamily: 'inherit' }}>\n Not sure about that one\n </p>\n )}\n <div style={{\n maxWidth: '82%',\n padding: '10px 14px',\n fontSize: '14px',\n lineHeight: 1.5,\n borderRadius: isBot ? '16px 16px 16px 6px' : '16px 16px 6px 16px',\n background: isBot ? '#fff' : accent,\n color: isBot ? '#1a1a2e' : '#fff',\n border: isBot ? '1px solid rgba(20,20,40,0.06)' : 'none',\n boxShadow: isBot\n ? '0 1px 2px rgba(20,20,40,0.04)'\n : `0 6px 16px -6px color-mix(in oklab, ${accent} 60%, transparent)`,\n wordBreak: 'break-word',\n fontFamily: 'inherit',\n }} className=\"sd-r-markdown\">\n {isBot ? (\n <div dangerouslySetInnerHTML={{ __html: renderedHtml }} />\n ) : (\n <div style={{ whiteSpace: 'pre-wrap' }}>{msg.text}</div>\n )}\n </div>\n <span style={{\n fontSize: '11px', color: '#a8a8b0', marginTop: '2px',\n padding: isBot ? '0 0 0 4px' : '0 4px 0 0',\n fontVariantNumeric: 'tabular-nums', fontFamily: 'inherit',\n }}>just now</span>\n </div>\n );\n});\n\nfunction ClassicTypingIndicator() {\n const dot: CSSProperties = { width: 6, height: 6, borderRadius: '50%', background: '#c8c8ce', display: 'inline-block' };\n return (\n <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: '4px' }}>\n <div style={{\n padding: '10px 14px', borderRadius: '16px 16px 16px 6px',\n background: '#fff', border: '1px solid rgba(20,20,40,0.06)',\n boxShadow: '0 1px 2px rgba(20,20,40,0.04)',\n display: 'flex', alignItems: 'center', gap: '4px',\n }}>\n <span style={dot} className=\"sd-r-dot-1\" />\n <span style={dot} className=\"sd-r-dot-2\" />\n <span style={dot} className=\"sd-r-dot-3\" />\n </div>\n </div>\n );\n}\n\n// ─── Light theme components ───────────────────────────────────────────────────\n\nconst LightMessageBubble = React.memo(function LightMessageBubble({ msg, accent, agentName }: { msg: ChatMessage; accent: string; agentName: string }) {\n const isBot = msg.role === 'bot';\n const renderedHtml = useMemo(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);\n\n if (isBot) {\n return (\n <div style={{ display: 'flex', gap: '10px' }}>\n <div style={{\n width: 28, height: 28, borderRadius: '50%',\n background: `linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 60%, #fff))`,\n flexShrink: 0, marginTop: '2px',\n }} />\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ display: 'flex', alignItems: 'baseline', gap: '8px', marginBottom: '4px' }}>\n <span style={{ fontSize: '13px', fontWeight: 600, color: '#1a1a2e', fontFamily: 'inherit' }}>{agentName}</span>\n <span style={{ fontSize: '11px', color: '#a8a89e', fontVariantNumeric: 'tabular-nums', fontFamily: 'inherit' }}>just now</span>\n </div>\n {msg.isFallback && (\n <p style={{ fontSize: '11px', color: '#9b9aa3', margin: '0 0 4px', fontFamily: 'inherit' }}>Not sure about that one</p>\n )}\n <div style={{ fontSize: '14px', lineHeight: 1.55, color: '#2a2a36', fontFamily: 'inherit', wordBreak: 'break-word' }} className=\"sd-r-markdown\">\n <div dangerouslySetInnerHTML={{ __html: renderedHtml }} />\n </div>\n </div>\n </div>\n );\n }\n return (\n <div style={{ display: 'flex', justifyContent: 'flex-end' }}>\n <div style={{\n maxWidth: '78%', padding: '11px 15px', fontSize: '14px', lineHeight: 1.5,\n borderRadius: '16px 16px 4px 16px',\n background: `color-mix(in oklab, ${accent} 10%, white)`,\n color: accent,\n border: `1px solid color-mix(in oklab, ${accent} 22%, transparent)`,\n fontWeight: 500, whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'inherit',\n }}>\n {msg.text}\n </div>\n </div>\n );\n});\n\nfunction LightTypingIndicator({ accent }: { accent: string }) {\n const dot: CSSProperties = { width: 6, height: 6, borderRadius: '50%', background: '#c4c4be', display: 'inline-block' };\n return (\n <div style={{ display: 'flex', gap: '10px' }}>\n <div style={{\n width: 28, height: 28, borderRadius: '50%',\n background: `linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 60%, #fff))`,\n flexShrink: 0, marginTop: '2px',\n }} />\n <div style={{\n padding: '10px 14px', borderRadius: '16px 16px 16px 4px',\n background: '#fff', border: '1px solid rgba(20,20,40,0.07)',\n boxShadow: '0 1px 2px rgba(20,20,40,0.04)',\n display: 'inline-flex', alignItems: 'center', gap: '4px',\n }}>\n <span style={dot} className=\"sd-r-dot-1\" />\n <span style={dot} className=\"sd-r-dot-2\" />\n <span style={dot} className=\"sd-r-dot-3\" />\n </div>\n </div>\n );\n}\n\n// ─── Dark theme components ────────────────────────────────────────────────────\n\nconst DarkMessageBubble = React.memo(function DarkMessageBubble({ msg, accent }: { msg: ChatMessage; accent: string }) {\n const isBot = msg.role === 'bot';\n const renderedHtml = useMemo(() => isBot ? parseMarkdown(msg.text) : msg.text, [isBot, msg.text]);\n\n return (\n <div style={{\n maxWidth: '85%',\n padding: '12px 14px',\n fontSize: '14px',\n lineHeight: isBot ? 1.6 : 1.5,\n borderRadius: isBot ? '14px 14px 14px 6px' : '14px 14px 6px 14px',\n background: isBot\n ? 'rgba(255,255,255,0.05)'\n : `linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 75%, #1a1340))`,\n border: isBot ? '1px solid rgba(255,255,255,0.06)' : 'none',\n color: isBot ? 'rgba(255,255,255,0.92)' : '#fff',\n alignSelf: isBot ? 'flex-start' : 'flex-end',\n boxShadow: isBot ? 'none' : `0 8px 20px -8px color-mix(in oklab, ${accent} 70%, transparent)`,\n wordBreak: 'break-word',\n fontFamily: 'inherit',\n }} className={isBot ? 'sd-r-markdown' : ''}>\n {msg.isFallback && (\n <span style={{ fontSize: '11px', color: 'rgba(255,255,255,0.5)', display: 'block', marginBottom: '4px' }}>\n Not sure about that one\n </span>\n )}\n {isBot ? (\n <div dangerouslySetInnerHTML={{ __html: renderedHtml }} />\n ) : (\n <div style={{ whiteSpace: 'pre-wrap' }}>{msg.text}</div>\n )}\n </div>\n );\n});\n\nfunction DarkTypingIndicator() {\n const dot: CSSProperties = { width: 6, height: 6, borderRadius: '50%', background: 'rgba(255,255,255,0.4)', display: 'inline-block' };\n return (\n <div style={{\n maxWidth: '85%', padding: '12px 14px',\n borderRadius: '14px 14px 14px 6px',\n background: 'rgba(255,255,255,0.05)',\n border: '1px solid rgba(255,255,255,0.06)',\n alignSelf: 'flex-start',\n display: 'flex', alignItems: 'center', gap: '4px',\n }}>\n <span style={dot} className=\"sd-r-dot-1\" />\n <span style={dot} className=\"sd-r-dot-2\" />\n <span style={dot} className=\"sd-r-dot-3\" />\n </div>\n );\n}\n\n// ─── Theme render functions ───────────────────────────────────────────────────\n\ninterface ThemeRenderProps {\n agent: SageDeskConfig['agent'];\n state: ReturnType<typeof useSageDesk>['state'];\n chips: string[];\n accent: string;\n isLeft: boolean;\n inputValue: string;\n setInputValue: (v: string) => void;\n handleClose: () => void;\n handleSubmit: () => void;\n handleKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void;\n isClosing: boolean;\n panelClass: string;\n showChips: boolean;\n showPoweredBy: boolean;\n threadRef: React.RefObject<HTMLDivElement>;\n inputRef: React.RefObject<HTMLInputElement>;\n triggerRef: React.RefObject<HTMLButtonElement>;\n open: () => void;\n submit: (text: string) => void;\n}\n\nfunction ClassicTheme(p: ThemeRenderProps) {\n const {\n agent, state, chips, accent, isLeft, inputValue, setInputValue,\n handleClose, handleSubmit, handleKeyDown, isClosing, panelClass,\n showChips, showPoweredBy, threadRef, inputRef, triggerRef, open, submit,\n } = p;\n\n const side = isLeft ? 'left' : 'right';\n\n const triggerStyle: CSSProperties = {\n position: 'fixed', bottom: '28px', [side]: '28px',\n width: '56px', height: '56px', borderRadius: '50%',\n background: `linear-gradient(135deg, ${accent} 0%, color-mix(in oklab, ${accent} 78%, #1a1340) 100%)`,\n border: 'none', color: '#fff', cursor: 'pointer',\n display: 'grid', placeItems: 'center',\n zIndex: 9999, padding: 0, transition: 'transform 150ms ease',\n boxShadow: `0 14px 28px -6px color-mix(in oklab, ${accent} 55%, transparent), 0 4px 10px rgba(40,30,90,0.15)`,\n };\n\n const panelStyle: CSSProperties = {\n position: 'fixed', bottom: '96px', [side]: '28px',\n width: '380px', height: '580px', maxHeight: '580px', borderRadius: '20px',\n background: '#ffffff',\n boxShadow: '0 1px 0 rgba(20,20,40,0.04), 0 24px 48px -16px rgba(40,30,90,0.22), 0 4px 14px rgba(40,30,90,0.08)',\n border: '1px solid rgba(20,20,40,0.04)',\n display: 'flex', flexDirection: 'column', overflow: 'hidden',\n zIndex: 9999, transformOrigin: isLeft ? 'bottom left' : 'bottom right',\n };\n\n const headerGrad = `linear-gradient(135deg, ${accent} 0%, color-mix(in oklab, ${accent} 78%, #1a1340) 100%)`;\n\n return (\n <>\n <button ref={triggerRef} style={triggerStyle} className=\"sd-r-trigger\" onClick={state.isOpen ? handleClose : open}\n aria-label=\"Open support chat\" aria-expanded={state.isOpen}>\n <IconChat size={22} />\n </button>\n\n {(state.isOpen || isClosing) && (\n <div style={panelStyle} className={`sd-r-panel ${panelClass}`} role=\"dialog\" aria-label={agent.name}>\n {/* Header */}\n <div style={{ padding: '18px 20px', background: headerGrad, color: '#fff', display: 'flex', alignItems: 'center', gap: '12px', flexShrink: 0 }}>\n <div style={{ position: 'relative', width: 40, height: 40 }}>\n <div style={{ width: 40, height: 40, borderRadius: '50%', background: 'rgba(255,255,255,0.18)', display: 'grid', placeItems: 'center', overflow: 'hidden' }}>\n {agent.avatarUrl\n ? <img src={agent.avatarUrl} alt={agent.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />\n : <IconPerson size={20} />}\n </div>\n <div style={{ position: 'absolute', right: -1, bottom: -1, width: 12, height: 12, borderRadius: '50%', background: '#22c55e', boxShadow: `0 0 0 2.5px ${accent}` }} />\n </div>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: '15px', fontWeight: 600, letterSpacing: '-0.01em' }}>{agent.name}</div>\n <div style={{ fontSize: '12px', opacity: 0.85, display: 'flex', alignItems: 'center', gap: '6px', marginTop: '3px' }}>\n <span style={{ width: 6, height: 6, borderRadius: '50%', background: '#4ade80', boxShadow: '0 0 0 2px rgba(74,222,128,0.25)' }} />\n Typically replies in under a minute\n </div>\n </div>\n <button className=\"sd-r-close-btn\" onClick={handleClose} aria-label=\"Close chat\" style={{\n background: 'rgba(255,255,255,0.14)', border: 'none', color: '#fff',\n width: 30, height: 30, borderRadius: 8, cursor: 'pointer',\n display: 'grid', placeItems: 'center', padding: 0, transition: 'background 120ms',\n }}>\n <IconClose size={14} />\n </button>\n </div>\n\n {/* Thread */}\n <div ref={threadRef} role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\" style={{\n flex: 1, padding: '22px 18px 18px', overflowY: 'auto',\n background: '#fbfbfa', display: 'flex', flexDirection: 'column', gap: '18px',\n scrollbarWidth: 'thin', overscrollBehavior: 'contain',\n }}>\n {state.messages.map(msg => <ClassicMessageBubble key={msg.id} msg={msg} accent={accent} />)}\n {state.isTyping && <ClassicTypingIndicator />}\n </div>\n\n {/* Suggested chips */}\n {showChips && (\n <div style={{ padding: state.hasSentMessage ? '0 0 16px' : '0 18px 16px', background: '#fbfbfa', flexShrink: 0 }}>\n <div style={{\n fontSize: '11px', color: '#9b9aa3', letterSpacing: '0.06em', textTransform: 'uppercase', fontWeight: 500,\n paddingLeft: state.hasSentMessage ? 18 : 4, marginBottom: '6px'\n }}>\n Suggested\n </div>\n <div className={state.hasSentMessage ? 'sd-r-scrollable' : ''} style={{\n display: 'flex', gap: '6px', flexWrap: state.hasSentMessage ? 'nowrap' : 'wrap',\n padding: state.hasSentMessage ? '0 18px' : 0\n }}>\n {chips.map(chip => (\n <button key={chip} className=\"sd-r-chip\" onClick={() => { setInputValue(''); submit(chip); }} style={{\n fontSize: '12.5px', padding: '7px 12px', borderRadius: '999px',\n background: '#fff', border: `1px solid color-mix(in oklab, ${accent} 24%, transparent)`,\n color: accent, cursor: 'pointer', fontWeight: 500, fontFamily: 'inherit',\n letterSpacing: '-0.005em',\n }}>\n {chip}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Composer */}\n <div style={{ padding: '14px 14px 16px', borderTop: '1px solid rgba(20,20,40,0.06)', background: '#fff', flexShrink: 0 }}>\n <div style={{\n display: 'flex', alignItems: 'center', gap: '6px',\n background: '#f5f4f0', borderRadius: '12px', padding: '5px 6px 5px 14px',\n border: `1px solid ${inputValue ? `color-mix(in oklab, ${accent} 30%, transparent)` : 'transparent'}`,\n boxShadow: inputValue ? `0 0 0 4px color-mix(in oklab, ${accent} 12%, transparent)` : 'none',\n transition: 'border-color .15s, box-shadow .15s',\n }}>\n <input ref={inputRef} value={inputValue}\n onChange={e => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Write a message…\"\n className=\"sd-r-input\"\n aria-label=\"Type your question\"\n autoComplete=\"off\"\n style={{ flex: 1, background: 'transparent', border: 'none', outline: 'none', fontSize: '14px', padding: '8px 0', fontFamily: 'inherit', color: '#1a1a2e' }}\n />\n <button className=\"sd-r-send\" onClick={handleSubmit} aria-label=\"Send message\" style={{\n width: 32, height: 32, borderRadius: 8, padding: 0,\n background: inputValue ? accent : `color-mix(in oklab, ${accent} 22%, #e5e3dc)`,\n border: 'none', color: '#fff', cursor: 'pointer',\n display: 'grid', placeItems: 'center', transition: 'background .15s',\n }}>\n <IconSend size={14} />\n </button>\n </div>\n {showPoweredBy && <div style={{ marginTop: '10px' }}><PoweredBy /></div>}\n </div>\n </div>\n )}\n </>\n );\n}\n\nfunction LightTheme(p: ThemeRenderProps) {\n const {\n agent, state, chips, accent, isLeft, inputValue, setInputValue,\n handleClose, handleSubmit, handleKeyDown, isClosing, panelClass,\n showChips, showPoweredBy, threadRef, inputRef, triggerRef, open, submit,\n } = p;\n\n const side = isLeft ? 'left' : 'right';\n\n const triggerStyle: CSSProperties = {\n position: 'fixed', bottom: '28px', [side]: '28px',\n height: '52px', padding: '0 8px 0 20px', borderRadius: '999px',\n background: '#fdfcf9', border: '1px solid rgba(20,20,40,0.08)',\n cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '14px',\n boxShadow: '0 12px 26px -8px rgba(40,30,90,0.18), 0 2px 8px rgba(40,30,90,0.06)',\n zIndex: 9999, fontFamily: 'inherit', transition: 'box-shadow 150ms ease',\n };\n\n const panelStyle: CSSProperties = {\n position: 'fixed', bottom: '92px', [side]: '28px',\n width: '400px', height: '580px', maxHeight: '580px', borderRadius: '22px',\n background: '#fdfcf9',\n boxShadow: '0 1px 0 rgba(20,20,40,0.04), 0 30px 60px -20px rgba(40,30,90,0.18), 0 6px 16px rgba(40,30,90,0.06)',\n border: '1px solid rgba(20,20,40,0.05)',\n display: 'flex', flexDirection: 'column', overflow: 'hidden',\n zIndex: 9999, transformOrigin: isLeft ? 'bottom left' : 'bottom right',\n };\n\n return (\n <>\n <button ref={triggerRef} style={triggerStyle} className=\"sd-r-trigger-light\" onClick={state.isOpen ? handleClose : open}\n aria-label=\"Open support chat\" aria-expanded={state.isOpen}>\n <span style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', lineHeight: 1.2 }}>\n <span style={{ fontSize: '14px', fontWeight: 600, color: '#1a1a2e' }}>Chat with us</span>\n <span style={{ fontSize: '11px', color: '#9b9aa3', marginTop: '2px' }}>We typically reply in 1m</span>\n </span>\n <span style={{ width: '36px', height: '36px', borderRadius: '50%', background: accent, color: '#fff', display: 'grid', placeItems: 'center', flexShrink: 0 }}>\n <IconChat size={18} />\n </span>\n </button>\n\n {(state.isOpen || isClosing) && (\n <div style={panelStyle} className={`sd-r-panel ${panelClass}`} role=\"dialog\" aria-label={agent.name}>\n {/* Header */}\n <div style={{ padding: '18px 20px 14px', borderBottom: '1px solid rgba(20,20,40,0.05)', display: 'flex', alignItems: 'center', gap: '12px', background: '#fdfcf9', flexShrink: 0 }}>\n <div style={{\n width: 36, height: 36, borderRadius: '50%', flexShrink: 0,\n background: `linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 55%, #fff))`,\n display: 'grid', placeItems: 'center', overflow: 'hidden', color: '#fff',\n }}>\n {agent.avatarUrl\n ? <img src={agent.avatarUrl} alt={agent.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />\n : <IconPerson size={16} />}\n </div>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: '14.5px', fontWeight: 600, color: '#1a1a2e', letterSpacing: '-0.01em' }}>{agent.name}</div>\n <div style={{ fontSize: '12px', color: '#7a7a82', display: 'flex', alignItems: 'center', gap: '6px', marginTop: '2px' }}>\n <span style={{ width: 6, height: 6, borderRadius: '50%', background: '#22c55e' }} />\n Online · replies in under a minute\n </div>\n </div>\n <button onClick={handleClose} aria-label=\"Close chat\" style={{\n background: 'transparent', border: 'none', color: '#9b9aa3',\n width: 30, height: 30, borderRadius: 8, cursor: 'pointer',\n display: 'grid', placeItems: 'center', padding: 0,\n }}>\n <IconClose size={16} />\n </button>\n </div>\n\n {/* Thread */}\n <div ref={threadRef} role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\" style={{\n flex: 1, padding: '22px 20px 16px', overflowY: 'auto',\n display: 'flex', flexDirection: 'column', gap: '22px',\n scrollbarWidth: 'thin', overscrollBehavior: 'contain',\n }}>\n {state.messages.map(msg => <LightMessageBubble key={msg.id} msg={msg} accent={accent} agentName={agent.name} />)}\n {state.isTyping && <LightTypingIndicator accent={accent} />}\n </div>\n\n {/* Chips */}\n {showChips && (\n <div className={state.hasSentMessage ? 'sd-r-scrollable' : ''} style={{\n padding: '0 20px 14px', display: 'flex',\n flexWrap: state.hasSentMessage ? 'nowrap' : 'wrap',\n gap: '6px', flexShrink: 0\n }}>\n {chips.map(chip => (\n <button key={chip} className=\"sd-r-chip-light\" onClick={() => { setInputValue(''); submit(chip); }} style={{\n fontSize: '12px', padding: '5px 10px', borderRadius: '6px',\n background: '#f4f3ee', border: 'none', color: '#5a5a64',\n cursor: 'pointer', fontFamily: 'inherit', fontWeight: 500,\n }}>\n {chip}\n </button>\n ))}\n </div>\n )}\n\n {/* Composer */}\n <div style={{ padding: '14px', borderTop: '1px solid rgba(20,20,40,0.05)', background: '#fdfcf9', flexShrink: 0 }}>\n <div style={{\n background: '#fff',\n border: `1px solid ${inputValue ? `color-mix(in oklab, ${accent} 40%, transparent)` : 'rgba(20,20,40,0.1)'}`,\n borderRadius: '14px', padding: '10px 12px',\n transition: 'border-color .15s, box-shadow .15s',\n boxShadow: inputValue ? `0 0 0 4px color-mix(in oklab, ${accent} 12%, transparent)` : 'none',\n }}>\n <input ref={inputRef} value={inputValue}\n onChange={e => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Write a message…\"\n className=\"sd-r-input\"\n aria-label=\"Type your question\"\n autoComplete=\"off\"\n style={{ width: '100%', background: 'transparent', border: 'none', outline: 'none', fontSize: '14px', fontFamily: 'inherit', color: '#1a1a2e' }}\n />\n <div style={{ display: 'flex', alignItems: 'center', marginTop: '8px', gap: '6px' }}>\n <div style={{ flex: 1 }} />\n <button className=\"sd-r-send\" onClick={handleSubmit} aria-label=\"Send message\" style={{\n width: 28, height: 28, borderRadius: 7, padding: 0,\n background: inputValue ? accent : '#e8e7e0',\n color: inputValue ? '#fff' : '#a8a89e',\n border: 'none', cursor: 'pointer', display: 'grid', placeItems: 'center',\n transition: 'background .15s',\n }}>\n <IconSend size={13} />\n </button>\n </div>\n </div>\n {showPoweredBy && <div style={{ marginTop: '10px' }}><PoweredBy /></div>}\n </div>\n </div>\n )}\n </>\n );\n}\n\nfunction DarkTheme(p: ThemeRenderProps) {\n const {\n agent, state, chips, accent, isLeft, inputValue, setInputValue,\n handleClose, handleSubmit, handleKeyDown, isClosing, panelClass,\n showChips, showPoweredBy, threadRef, inputRef, triggerRef, open, submit,\n } = p;\n\n const side = isLeft ? 'left' : 'right';\n\n const triggerStyle: CSSProperties = {\n position: 'fixed', bottom: '28px', [side]: '28px',\n width: '60px', height: '60px', borderRadius: '50%',\n background: `linear-gradient(135deg, color-mix(in oklab, ${accent} 70%, #1a1340), #1a1a2e)`,\n border: '1px solid rgba(255,255,255,0.12)',\n color: '#fff', cursor: 'pointer', display: 'grid', placeItems: 'center',\n zIndex: 9999, padding: 0, transition: 'transform 150ms ease',\n boxShadow: '0 16px 32px -8px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.1)',\n };\n\n const glowStyle: CSSProperties = {\n position: 'fixed', bottom: '18px', [side]: '18px',\n width: '80px', height: '80px', borderRadius: '50%',\n background: `radial-gradient(circle, color-mix(in oklab, ${accent} 50%, transparent), transparent 70%)`,\n filter: 'blur(10px)', pointerEvents: 'none', zIndex: 9998,\n };\n\n const panelStyle: CSSProperties = {\n position: 'fixed', bottom: '100px', [side]: '28px',\n width: '380px', height: '580px', maxHeight: '580px', borderRadius: '22px',\n background: 'rgba(18, 16, 32, 0.86)',\n backdropFilter: 'blur(40px) saturate(180%)',\n WebkitBackdropFilter: 'blur(40px) saturate(180%)',\n boxShadow: '0 30px 60px -20px rgba(0,0,0,0.4), 0 4px 14px rgba(0,0,0,0.2), inset 0 1px 0 rgba(255,255,255,0.06)',\n border: '1px solid rgba(255,255,255,0.08)',\n display: 'flex', flexDirection: 'column', overflow: 'hidden',\n zIndex: 9999, color: '#fff',\n transformOrigin: isLeft ? 'bottom left' : 'bottom right',\n };\n\n return (\n <>\n {/* Ambient launcher glow */}\n <div style={glowStyle} aria-hidden=\"true\" />\n\n <button ref={triggerRef} style={triggerStyle} className=\"sd-r-trigger\" onClick={state.isOpen ? handleClose : open}\n aria-label=\"Open support chat\" aria-expanded={state.isOpen}>\n <IconBot size={22} />\n </button>\n\n {(state.isOpen || isClosing) && (\n <div style={panelStyle} className={`sd-r-panel ${panelClass}`} role=\"dialog\" aria-label={agent.name}>\n {/* Ambient top glow inside panel */}\n <div aria-hidden=\"true\" style={{\n position: 'absolute', top: 0, left: 0, right: 0, height: 200, pointerEvents: 'none',\n background: `radial-gradient(ellipse 80% 100% at 50% 0%, color-mix(in oklab, ${accent} 40%, transparent) 0%, transparent 70%)`,\n }} />\n\n {/* Header */}\n <div style={{ padding: '20px 20px 16px', display: 'flex', alignItems: 'center', gap: '12px', position: 'relative', zIndex: 1, flexShrink: 0 }}>\n <div style={{ position: 'relative' }}>\n <div style={{\n width: 38, height: 38, borderRadius: '50%', display: 'grid', placeItems: 'center', overflow: 'hidden',\n background: `linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 50%, #fff))`,\n boxShadow: `0 0 24px color-mix(in oklab, ${accent} 50%, transparent)`,\n }}>\n {agent.avatarUrl\n ? <img src={agent.avatarUrl} alt={agent.name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />\n : <IconBot size={16} />}\n </div>\n <div style={{ position: 'absolute', right: -2, bottom: -2, width: 12, height: 12, borderRadius: '50%', background: '#22c55e', boxShadow: '0 0 0 2.5px rgba(18,16,32,1)' }} />\n </div>\n <div style={{ flex: 1 }}>\n <div style={{ fontSize: '15px', fontWeight: 600, letterSpacing: '-0.01em' }}>{agent.name}</div>\n <div style={{ fontSize: '12px', color: 'rgba(255,255,255,0.55)', marginTop: '3px', display: 'flex', alignItems: 'center', gap: '6px' }}>\n <span style={{ width: 5, height: 5, borderRadius: '50%', background: '#4ade80', boxShadow: '0 0 6px #4ade80' }} />\n Trained on this site · always on\n </div>\n </div>\n <button onClick={handleClose} aria-label=\"Close chat\" style={{\n background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.08)',\n color: 'rgba(255,255,255,0.7)', width: 30, height: 30, borderRadius: 8,\n cursor: 'pointer', display: 'grid', placeItems: 'center', padding: 0,\n }}>\n <IconClose size={14} />\n </button>\n </div>\n\n {/* Thread */}\n <div ref={threadRef} role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\" style={{\n flex: 1, padding: '8px 20px 16px', overflowY: 'auto',\n display: 'flex', flexDirection: 'column', gap: '14px',\n position: 'relative', zIndex: 1, scrollbarWidth: 'thin',\n overscrollBehavior: 'contain',\n }}>\n {state.messages.map(msg => <DarkMessageBubble key={msg.id} msg={msg} accent={accent} />)}\n {state.isTyping && <DarkTypingIndicator />}\n </div>\n\n {/* Suggested prompts */}\n {showChips && (\n <div style={{ padding: state.hasSentMessage ? '0 0 16px' : '0 20px 16px', position: 'relative', zIndex: 1, flexShrink: 0 }}>\n {!state.hasSentMessage && (\n <div style={{ fontSize: '11px', color: 'rgba(255,255,255,0.4)', letterSpacing: '0.08em', textTransform: 'uppercase', fontWeight: 500, marginBottom: '8px' }}>\n Try asking\n </div>\n )}\n <div className={state.hasSentMessage ? 'sd-r-scrollable' : ''} style={{\n display: 'flex',\n flexDirection: state.hasSentMessage ? 'row' : 'column',\n gap: '6px',\n padding: state.hasSentMessage ? '0 20px' : 0\n }}>\n {chips.map(chip => (\n <button key={chip} className=\"sd-r-prompt\" onClick={() => { setInputValue(''); submit(chip); }} style={{\n textAlign: 'left', padding: '10px 14px', borderRadius: '10px',\n background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.07)',\n color: 'rgba(255,255,255,0.85)', cursor: 'pointer', fontSize: '13px',\n fontFamily: 'inherit', display: 'flex', alignItems: 'center', gap: '8px',\n transition: 'background .12s',\n width: state.hasSentMessage ? 'auto' : '100%',\n whiteSpace: state.hasSentMessage ? 'nowrap' : 'normal',\n }}>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M9 6l6 6-6 6\" stroke=\"currentColor\" strokeWidth=\"1.6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n {chip}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Composer */}\n <div style={{ padding: '14px', borderTop: '1px solid rgba(255,255,255,0.06)', position: 'relative', zIndex: 1, flexShrink: 0 }}>\n <div style={{\n display: 'flex', alignItems: 'center', gap: '8px',\n background: 'rgba(255,255,255,0.05)',\n border: `1px solid ${inputValue ? `color-mix(in oklab, ${accent} 60%, transparent)` : 'rgba(255,255,255,0.08)'}`,\n borderRadius: '12px', padding: '4px 4px 4px 14px',\n transition: 'border-color .15s',\n }}>\n <input ref={inputRef} value={inputValue}\n onChange={e => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Write a message…\"\n className=\"sd-r-input\"\n aria-label=\"Type your question\"\n autoComplete=\"off\"\n style={{ flex: 1, background: 'transparent', border: 'none', outline: 'none', fontSize: '14px', padding: '9px 0', fontFamily: 'inherit', color: '#fff' }}\n />\n <button className=\"sd-r-send\" onClick={handleSubmit} aria-label=\"Send message\" style={{\n width: 32, height: 32, borderRadius: 9, padding: 0, border: 'none', cursor: 'pointer',\n background: inputValue\n ? `linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 60%, #fff))`\n : 'rgba(255,255,255,0.08)',\n color: inputValue ? '#fff' : 'rgba(255,255,255,0.4)',\n display: 'grid', placeItems: 'center', transition: 'background .15s',\n boxShadow: inputValue ? `0 4px 12px -2px color-mix(in oklab, ${accent} 60%, transparent)` : 'none',\n }}>\n <IconSend size={14} />\n </button>\n </div>\n {showPoweredBy && <div style={{ marginTop: '10px' }}><PoweredBy dark /></div>}\n </div>\n </div>\n )}\n </>\n );\n}\n\n// ─── Main component ───────────────────────────────────────────────────────────\n\nexport interface SageDeskWidgetProps {\n /** Operating mode. 'local' (default) runs entirely in the browser via WASM. 'llm' posts to the consumer's own backend. */\n mode?: SageDeskMode;\n /** URL to the pre-built vector index. Required in local mode. */\n indexUrl?: string;\n /** Consumer's backend endpoint that accepts POST { query }. Required in llm mode. */\n endpoint?: string;\n agent: SageDeskConfig['agent'];\n search?: SageDeskConfig['search'];\n}\n\nexport function SageDeskWidget({ mode, indexUrl, endpoint, agent, search }: SageDeskWidgetProps) {\n const resolvedMode = mode ?? 'local';\n\n if (!agent?.name) {\n throw new Error('[sagedesk] Required prop \"agent.name\" is missing.');\n }\n\n if (resolvedMode === 'local' && !indexUrl) {\n throw new Error(\n '[sagedesk] Required prop \"indexUrl\" is missing for local mode. ' +\n 'Run `npx sagedesk build` and pass the output path, e.g. indexUrl=\"/support-index.json\".'\n );\n }\n\n if (resolvedMode === 'llm' && !endpoint) {\n throw new Error(\n '[sagedesk] Required prop \"endpoint\" is missing for llm mode. ' +\n 'Provide your backend route, e.g. endpoint=\"/api/sagedesk\".'\n );\n }\n\n const config = useMemo<SageDeskConfig>(\n () => ({ mode: resolvedMode, indexUrl, endpoint, agent, search }),\n [resolvedMode, indexUrl, endpoint, agent, search]\n );\n const { state, chips, open, close, submit } = useSageDesk(config);\n\n const theme = agent.theme ?? 'classic';\n const accent = agent.accentColor ?? '#534AB7';\n const position = agent.position ?? 'bottom-right';\n const isLeft = position === 'bottom-left';\n\n const [inputValue, setInputValue] = useState('');\n const [isClosing, setIsClosing] = useState(false);\n const threadRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n if (resolvedMode === 'local' && indexUrl &&\n !indexUrl.startsWith('/') && !indexUrl.startsWith('http')) {\n console.warn(\n `[sagedesk] indexUrl \"${indexUrl}\" looks like a relative path. ` +\n 'It should start with \"/\" so it resolves correctly from any page.'\n );\n }\n setMounted(true);\n injectStyles(theme);\n }, []);\n\n useEffect(() => {\n if (threadRef.current) {\n threadRef.current.scrollTop = threadRef.current.scrollHeight;\n }\n }, [state.messages, state.isTyping]);\n\n useEffect(() => {\n if (state.isOpen) {\n setTimeout(() => inputRef.current?.focus(), 50);\n }\n }, [state.isOpen]);\n\n const handleClose = useCallback(() => {\n setIsClosing(true);\n setTimeout(() => {\n setIsClosing(false);\n close();\n triggerRef.current?.focus();\n }, 150);\n }, [close]);\n\n const handleSubmit = useCallback(() => {\n const text = inputValue.trim();\n if (!text) return;\n setInputValue('');\n submit(text);\n }, [inputValue, submit]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n },\n [handleSubmit]\n );\n\n if (!mounted || typeof document === 'undefined') return null;\n\n const showPoweredBy = true;\n const showChips = chips.length > 0;\n const panelClass = isClosing ? 'sd-r-closing' : state.isOpen ? 'sd-r-opening' : '';\n\n const props: ThemeRenderProps = {\n agent, state, chips, accent, isLeft, inputValue, setInputValue,\n handleClose, handleSubmit, handleKeyDown, isClosing, panelClass,\n showChips, showPoweredBy, threadRef, inputRef, triggerRef, open, submit,\n };\n\n const content =\n theme === 'dark' ? <DarkTheme {...props} /> :\n theme === 'light' ? <LightTheme {...props} /> :\n <ClassicTheme {...props} />;\n\n return createPortal(content, document.body);\n}\n","import { useState, useEffect, useReducer, useCallback, useRef, useMemo } from 'react';\nimport { EmbedderRuntime } from '../core/embedder.js';\nimport { fetchIndex, retrieve } from '../core/retriever.js';\nimport { buildAnswer, extractChips } from '../core/renderer.js';\nimport { getFallback } from '../core/fallback.js';\nimport type {\n SageDeskConfig,\n IndexChunk,\n ChatMessage,\n EngineStatus,\n} from '../core/types.js';\n\n// ─── Fallback logging ─────────────────────────────────────────────────────────\n\nfunction logFallbackWarning(reason?: string): void {\n if (!reason) return;\n\n const messages: Record<string, string> = {\n 'auth-error': '[sagedesk] Support service authentication failed. Showing relevant knowledge instead.',\n 'quota-exceeded': '[sagedesk] Support service quota exhausted. Showing relevant knowledge instead.',\n 'timeout': '[sagedesk] Support service took too long to respond. Showing relevant knowledge instead.',\n 'api-error': '[sagedesk] Support service error. Showing relevant knowledge instead.',\n 'malformed-response': '[sagedesk] Support service returned invalid response. Showing relevant knowledge instead.',\n };\n\n console.warn(messages[reason] || '[sagedesk] Support service unavailable. Showing relevant knowledge instead.');\n}\n\n// ─── State ────────────────────────────────────────────────────────────────────\n\ninterface State {\n messages: ChatMessage[];\n userMessages: ChatMessage[];\n isOpen: boolean;\n isTyping: boolean;\n engineStatus: EngineStatus;\n engineError: string | null;\n hasSentMessage: boolean;\n}\n\ntype Action =\n | { type: 'OPEN' }\n | { type: 'CLOSE' }\n | { type: 'ADD_MESSAGE'; payload: ChatMessage }\n | { type: 'SET_TYPING'; payload: boolean }\n | { type: 'SET_ENGINE_STATUS'; payload: { status: EngineStatus; error?: string } }\n | { type: 'MARK_SENT' };\n\nconst initialState: State = {\n messages: [],\n userMessages: [],\n isOpen: false,\n isTyping: false,\n engineStatus: 'idle',\n engineError: null,\n hasSentMessage: false,\n};\n\nfunction reducer(state: State, action: Action): State {\n switch (action.type) {\n case 'OPEN':\n return { ...state, isOpen: true };\n case 'CLOSE':\n return { ...state, isOpen: false };\n case 'ADD_MESSAGE': {\n const newMessages = [...state.messages, action.payload];\n const newUserMessages = action.payload.role === 'user'\n ? [...state.userMessages, action.payload]\n : state.userMessages;\n return { ...state, messages: newMessages, userMessages: newUserMessages };\n }\n case 'SET_TYPING':\n return { ...state, isTyping: action.payload };\n case 'SET_ENGINE_STATUS':\n return {\n ...state,\n engineStatus: action.payload.status,\n engineError: action.payload.error ?? null,\n };\n case 'MARK_SENT':\n return { ...state, hasSentMessage: true };\n default:\n return state;\n }\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────────────────\n\nexport interface UseSageDeskReturn {\n state: State;\n chips: string[];\n open: () => void;\n close: () => void;\n submit: (text: string) => void;\n}\n\nexport function useSageDesk(config: SageDeskConfig): UseSageDeskReturn {\n const [state, dispatch] = useReducer(reducer, initialState);\n\n // Mirror engineStatus in a ref so async functions always read the current value.\n const engineStatusRef = useRef<EngineStatus>('idle');\n engineStatusRef.current = state.engineStatus;\n\n const indexRef = useRef<IndexChunk[] | null>(null);\n const embedderRef = useRef<EmbedderRuntime | null>(null);\n const engineStartedRef = useRef(false);\n const msgCounterRef = useRef(0);\n const engineReadyCallbacksRef = useRef<Array<() => void>>([]);\n const [chips, setChips] = useState<string[]>([]);\n\n const makeId = () => `msg-${++msgCounterRef.current}`;\n\n const addMessage = useCallback(\n (msg: Pick<ChatMessage, 'role' | 'text' | 'isFallback'>) => {\n dispatch({\n type: 'ADD_MESSAGE',\n payload: { ...msg, id: makeId(), timestamp: new Date() },\n });\n },\n []\n );\n\n const notifyEngineReady = useCallback(() => {\n const callbacks = engineReadyCallbacksRef.current;\n engineReadyCallbacksRef.current = [];\n callbacks.forEach(cb => cb());\n }, []);\n\n // Start engine once when widget first opens\n const startEngine = useCallback(async () => {\n if (engineStartedRef.current) return;\n engineStartedRef.current = true;\n\n // LLM mode: skip the local index, but still load the WASM embedder so the\n // browser can vectorize queries before POSTing them. This keeps the server\n // free of native ONNX bindings (and of Vercel's 300MB function limit).\n if (config.mode === 'llm') {\n setChips(config.agent.suggestedChips ?? []);\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'loading-model' } });\n\n try {\n embedderRef.current = new EmbedderRuntime();\n await embedderRef.current.load(config.agent.model);\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'ready' } });\n notifyEngineReady();\n } catch (err) {\n console.warn('[sagedesk] WASM embedder failed to load - LLM mode will use fallback messages.', err);\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'error-model', error: String(err) } });\n notifyEngineReady();\n }\n return;\n }\n\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'loading-index' } });\n\n try {\n indexRef.current = await fetchIndex(config.indexUrl!);\n } catch (err) {\n console.warn('[sagedesk] Failed to load knowledge index from', config.indexUrl, '-', err);\n dispatch({\n type: 'SET_ENGINE_STATUS',\n payload: { status: 'error-index', error: String(err) },\n });\n notifyEngineReady();\n addMessage({\n role: 'bot',\n text: \"I'm having trouble loading right now. Please try again in a moment.\",\n });\n return;\n }\n\n setChips(extractChips(indexRef.current, config.agent.suggestedChips));\n\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'loading-model' } });\n\n try {\n embedderRef.current = new EmbedderRuntime();\n await embedderRef.current.load(config.agent.model);\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'ready' } });\n notifyEngineReady();\n } catch (err) {\n console.warn('[sagedesk] WASM model failed to load, falling back to keyword search -', err);\n embedderRef.current = new EmbedderRuntime();\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'degraded' } });\n notifyEngineReady();\n }\n }, [config.mode, config.indexUrl, config.agent.model, config.agent.suggestedChips, addMessage, notifyEngineReady]);\n\n const greetingShownRef = useRef(false);\n\n const open = useCallback(() => {\n dispatch({ type: 'OPEN' });\n\n if (!greetingShownRef.current) {\n greetingShownRef.current = true;\n addMessage({\n role: 'bot',\n text: config.agent.greeting ?? 'Hey, how can I help you today?',\n });\n }\n\n startEngine();\n }, [config.agent.greeting, addMessage, startEngine]);\n\n const close = useCallback(() => {\n dispatch({ type: 'CLOSE' });\n }, []);\n\n const waitForEngine = useCallback((): Promise<void> => {\n const s = engineStatusRef.current;\n if (s === 'ready' || s === 'degraded' || s === 'error-index' || s === 'error-model') {\n return Promise.resolve();\n }\n return new Promise<void>((resolve) => {\n engineReadyCallbacksRef.current.push(resolve);\n });\n }, []);\n\n const submit = useCallback(\n async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n\n const typingStart = Date.now();\n\n dispatch({ type: 'MARK_SENT' });\n addMessage({ role: 'user', text: trimmed });\n dispatch({ type: 'SET_TYPING', payload: true });\n\n const currentStatus = engineStatusRef.current;\n if (\n currentStatus !== 'ready' &&\n currentStatus !== 'degraded' &&\n currentStatus !== 'error-index' &&\n currentStatus !== 'error-model'\n ) {\n await waitForEngine();\n }\n\n let botText: string;\n let isFallback = false;\n let fallbackReason: string | undefined;\n let retrievalMode: 'vector' | 'keyword' = 'keyword';\n\n if (config.mode === 'llm') {\n // LLM mode: embed the query in the browser, then POST {query, queryVector}\n // to the consumer's backend. The server does retrieval + LLM synthesis only.\n if (!config.endpoint) {\n console.warn('[sagedesk] LLM mode requires an \"endpoint\" prop.');\n botText = getFallback(config.agent);\n isFallback = true;\n } else if (!embedderRef.current?.isReady) {\n console.warn('[sagedesk] Embedder not ready - showing fallback.');\n botText = getFallback(config.agent);\n isFallback = true;\n } else {\n try {\n const vector = await embedderRef.current.embed(trimmed);\n const res = await fetch(config.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n query: trimmed,\n queryVector: Array.from(vector),\n }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as {\n answer: string;\n isFallback: boolean;\n fallbackReason?: string;\n };\n if (data.isFallback || !data.answer) {\n fallbackReason = data.fallbackReason;\n logFallbackWarning(fallbackReason);\n botText = getFallback(config.agent);\n isFallback = true;\n } else {\n botText = data.answer;\n }\n } catch (err) {\n console.warn('[sagedesk] Support service unavailable. Using cached knowledge instead.');\n botText = getFallback(config.agent);\n isFallback = true;\n }\n }\n } else if (!indexRef.current) {\n botText = getFallback(config.agent);\n isFallback = true;\n } else {\n try {\n const res = await retrieve(\n trimmed,\n indexRef.current,\n embedderRef.current!,\n config.search\n );\n retrievalMode = res.mode;\n if (res.results.length > 0) {\n botText = buildAnswer(res.results);\n } else {\n botText = getFallback(config.agent);\n isFallback = true;\n }\n } catch (err) {\n console.warn('[sagedesk] Query failed, showing fallback -', err);\n botText = getFallback(config.agent);\n isFallback = true;\n }\n }\n\n // In LLM mode the real network+LLM latency already provides natural delay.\n // In local mode apply an artificial \"thinking\" pause for a more natural feel.\n if (config.mode !== 'llm') {\n const elapsed = Date.now() - typingStart;\n const delayBase = (retrievalMode === 'keyword' || isFallback) ? 400 : 800;\n const minTypingMs = delayBase + Math.random() * 400;\n const remaining = minTypingMs - elapsed;\n if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));\n }\n\n dispatch({ type: 'SET_TYPING', payload: false });\n addMessage({ role: 'bot', text: botText, isFallback });\n },\n [addMessage, waitForEngine, config.agent, config.search]\n );\n\n // Keyboard: Escape closes\n useEffect(() => {\n if (!state.isOpen) return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') close();\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [state.isOpen, close]);\n\n const activeChips = useMemo(() => {\n const askedTexts = new Set(state.userMessages.map((m) => m.text.toLowerCase().trim()));\n return chips.filter((chip) => !askedTexts.has(chip.toLowerCase().trim()));\n }, [chips, state.userMessages]);\n\n return { state, chips: activeChips, open, close, submit };\n}\n","import type { SageDeskModel } from './types';\n\ntype PipelineFn = (\n text: string,\n options: { pooling: string; normalize: boolean }\n) => Promise<{ data: Float32Array }>;\n\n// Maps each supported model alias to its full Xenova/HuggingFace model ID.\nconst XENOVA_IDS: Record<SageDeskModel, string> = {\n 'all-MiniLM-L6-v2': 'Xenova/all-MiniLM-L6-v2',\n 'bge-small-en-v1-5': 'Xenova/bge-small-en-v1.5',\n 'paraphrase-multilingual-MiniLM-L12-v2': 'Xenova/paraphrase-multilingual-MiniLM-L12-v2',\n 'all-mpnet-base-v2': 'Xenova/all-mpnet-base-v2',\n};\n\nexport class EmbedderRuntime {\n private _ready = false;\n private _failed = false;\n\n // Module-level singleton so the WASM model is loaded at most once per page,\n // regardless of how many widget instances exist.\n private static _pipelineInstance: PipelineFn | null = null;\n private static _loadingPromise: Promise<void> | null = null;\n\n async load(model: SageDeskModel = 'all-MiniLM-L6-v2'): Promise<void> {\n if (this._ready) return;\n if (this._failed) throw new Error('EmbedderRuntime previously failed to load');\n\n if (EmbedderRuntime._loadingPromise) {\n await EmbedderRuntime._loadingPromise;\n this._ready = true;\n return;\n }\n\n const modelId = XENOVA_IDS[model];\n\n EmbedderRuntime._loadingPromise = (async () => {\n try {\n const { pipeline } = await import('@huggingface/transformers');\n EmbedderRuntime._pipelineInstance = (await pipeline(\n 'feature-extraction',\n modelId,\n { dtype: 'q8' }\n )) as unknown as PipelineFn;\n } catch (err) {\n EmbedderRuntime._loadingPromise = null;\n EmbedderRuntime._pipelineInstance = null;\n throw err;\n }\n })();\n\n try {\n await EmbedderRuntime._loadingPromise;\n this._ready = true;\n } catch (err) {\n this._failed = true;\n throw err;\n }\n }\n\n async embed(text: string): Promise<Float32Array> {\n if (!EmbedderRuntime._pipelineInstance) {\n await this.load();\n }\n\n try {\n const output = await EmbedderRuntime._pipelineInstance!(text, {\n pooling: 'mean',\n normalize: true,\n });\n return output.data;\n } catch (err) {\n throw new Error(`Embedding failed: ${String(err)}`);\n }\n }\n\n get isReady(): boolean {\n return this._ready;\n }\n\n get hasFailed(): boolean {\n return this._failed;\n }\n\n /** @internal */\n static _reset(): void {\n EmbedderRuntime._pipelineInstance = null;\n EmbedderRuntime._loadingPromise = null;\n }\n}\n","import type { IndexChunk, SearchResult } from './types';\n\n// Both the query vector (embedder.ts, normalize:true) and stored vectors\n// (builder-embedder.ts, normalize:true) are guaranteed unit-length, so\n// cosine similarity reduces to a plain dot product - no norms needed.\nfunction dotProduct(a: Float32Array, b: Float32Array): number {\n if (a.length !== b.length) {\n throw new Error(`Vector dimension mismatch: query(${a.length}) vs index(${b.length})`);\n }\n let dot = 0;\n for (let i = 0; i < a.length; i++) dot += a[i] * b[i];\n return dot;\n}\n\n// Inserts item at the correct descending-score position, then trims to maxLen.\n// Avoids Array.sort overhead on every insertion for small topK arrays.\nfunction insertSorted(arr: SearchResult[], item: SearchResult, maxLen: number): void {\n arr.push(item);\n let i = arr.length - 1;\n while (i > 0 && arr[i - 1].score < arr[i].score) {\n const tmp = arr[i - 1]; arr[i - 1] = arr[i]; arr[i] = tmp;\n i--;\n }\n if (arr.length > maxLen) arr.pop();\n}\n\nexport function search(\n queryVector: Float32Array,\n index: IndexChunk[],\n topK = 3,\n minScore = 0.42\n): SearchResult[] {\n const results: SearchResult[] = [];\n\n for (const chunk of index) {\n const score = dotProduct(queryVector, chunk.vector384 as Float32Array);\n if (score < minScore) continue;\n\n if (results.length < topK) {\n insertSorted(results, { chunk, score }, topK);\n } else if (score > results[topK - 1].score) {\n results[topK - 1] = { chunk, score };\n let i = topK - 1;\n while (i > 0 && results[i - 1].score < results[i].score) {\n const tmp = results[i - 1]; results[i - 1] = results[i]; results[i] = tmp;\n i--;\n }\n }\n }\n\n return results;\n}\n\nexport function keywordSearch(\n query: string,\n index: IndexChunk[],\n topK = 3\n): SearchResult[] {\n const terms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2)\n .map((w) => w.replace(/[^a-z0-9]/g, ''));\n\n if (terms.length === 0) return [];\n\n const results: SearchResult[] = [];\n\n for (const chunk of index) {\n const chunkLower = chunk.textLower || chunk.text.toLowerCase();\n let matchCount = 0;\n for (const t of terms) {\n if (chunkLower.includes(t)) matchCount++;\n }\n const score = matchCount / terms.length;\n\n if (score <= 0) continue;\n\n if (results.length < topK) {\n insertSorted(results, { chunk, score }, topK);\n } else if (score > results[topK - 1].score) {\n results[topK - 1] = { chunk, score };\n let i = topK - 1;\n while (i > 0 && results[i - 1].score < results[i].score) {\n const tmp = results[i - 1]; results[i - 1] = results[i]; results[i] = tmp;\n i--;\n }\n }\n }\n\n return results;\n}\n\nexport async function loadIndex(url: string): Promise<IndexChunk[]> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Failed to fetch index (HTTP ${res.status}): ${url}`);\n }\n const data = await res.json();\n // Support both the new { meta, chunks } format and the legacy bare-array format.\n const chunks: IndexChunk[] = Array.isArray(data)\n ? data\n : (data as { chunks: IndexChunk[] }).chunks;\n\n // Materialize lowercase versions and convert vectors to Float32Array once at load time.\n for (const chunk of chunks) {\n chunk.textLower = chunk.text.toLowerCase();\n if (Array.isArray(chunk.vector384)) {\n chunk.vector384 = new Float32Array(chunk.vector384);\n }\n if (Array.isArray(chunk.vector768)) {\n chunk.vector768 = new Float32Array(chunk.vector768);\n }\n }\n\n return chunks;\n}\n","import { search, keywordSearch, loadIndex } from './search';\nimport type { EmbedderRuntime } from './embedder';\nimport type { IndexChunk, SearchResult, SageDeskConfig } from './types';\n\nexport async function fetchIndex(url: string): Promise<IndexChunk[]> {\n try {\n return await loadIndex(url);\n } catch (err) {\n throw new Error(`Could not load knowledge index: ${String(err)}`);\n }\n}\n\nexport async function retrieve(\n text: string,\n index: IndexChunk[],\n embedder: EmbedderRuntime,\n config?: SageDeskConfig['search']\n): Promise<{ results: SearchResult[]; mode: 'vector' | 'keyword' }> {\n const topK = config?.topK ?? 3;\n const minScore = config?.minScore ?? 0.42;\n\n if (embedder.isReady) {\n try {\n const vector = await embedder.embed(text);\n const results = search(vector, index, topK, minScore);\n return { results, mode: 'vector' };\n } catch {\n // Fall through to keyword search\n }\n }\n\n const results = keywordSearch(text, index, topK);\n return { results, mode: 'keyword' };\n}\n","import type { SearchResult } from './types';\n\nexport function buildAnswer(results: SearchResult[]): string {\n if (results.length === 0) return '';\n // Deduplicate by sourceId: query expansion produces multiple chunks per\n // source entry (same answer, different query phrasings) - show each source once.\n const seen = new Set<string>();\n const parts: string[] = [];\n for (const r of results) {\n if (!seen.has(r.chunk.sourceId)) {\n seen.add(r.chunk.sourceId);\n parts.push(r.chunk.text);\n }\n }\n return parts.join('\\n\\n');\n}\n\nexport function extractChips(\n index: { text: string; question?: string; sourceId?: string }[],\n override?: string[]\n): string[] {\n if (override && override.length > 0) return override.slice(0, 5);\n\n const chips: string[] = [];\n const seenText = new Set<string>();\n const seenSource = new Set<string>();\n\n for (const chunk of index) {\n if (chips.length >= 5) break;\n\n // Deduplicate by sourceId if available to ensure variety of answers.\n if (chunk.sourceId) {\n if (seenSource.has(chunk.sourceId)) continue;\n seenSource.add(chunk.sourceId);\n }\n\n const candidate = chunk.question ?? extractFirstSentence(chunk.text);\n if (candidate && !seenText.has(candidate)) {\n seenText.add(candidate);\n chips.push(candidate);\n }\n }\n\n return chips;\n}\n\nfunction extractFirstSentence(text: string): string {\n const match = text.match(/^[^\\n.!?]{10,80}[.!?\\n]?/);\n if (!match) return text.slice(0, 60);\n return match[0].trim();\n}\n","import type { AgentConfig } from './types';\n\nconst DEFAULT_POOL = [\n \"That one's a bit outside what I have notes on right now. Feel free to reach out directly and I'll make sure you get a proper answer.\",\n \"Hmm, I don't have a great answer for that one yet. You're welcome to get in touch and a real person will help.\",\n \"I want to give you the right answer, not a guess. If you reach out through the contact page someone will follow up with you.\",\n];\n\nlet rotationIndex = 0;\n\nexport function getFallback(config: AgentConfig): string {\n const pool =\n config.fallbackPool && config.fallbackPool.length > 0\n ? config.fallbackPool\n : config.fallback\n ? [config.fallback]\n : DEFAULT_POOL;\n\n const message = pool[rotationIndex % pool.length];\n rotationIndex = (rotationIndex + 1) % pool.length;\n\n if (config.contactUrl) {\n return `${message} You can reach us at: ${config.contactUrl}`;\n }\n\n return message;\n}\n","import { marked } from 'marked';\nimport DOMPurify from 'dompurify';\n\n// Configure marked for safe rendering\nmarked.setOptions({\n breaks: true,\n gfm: true,\n pedantic: false,\n});\n\n// Configure DOMPurify for safe HTML\nconst PURIFY_CONFIG = {\n ALLOWED_TAGS: [\n 'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'ul', 'ol', 'li', 'blockquote', 'code', 'pre', 'a', 'hr'\n ],\n ALLOWED_ATTR: ['href', 'title', 'target', 'rel'],\n ALLOW_DATA_ATTR: false,\n};\n\nDOMPurify.addHook('afterSanitizeAttributes', (node) => {\n if (node.tagName.toLowerCase() === 'a') {\n node.setAttribute('target', '_blank');\n node.setAttribute('rel', 'noopener noreferrer');\n }\n});\n\nexport function parseMarkdown(markdown: string): string {\n const html = marked.parse(markdown) as string;\n const sanitized = DOMPurify.sanitize(html, PURIFY_CONFIG);\n return sanitized;\n}\n"],"mappings":"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","names":["useState","useRef","useEffect","useCallback","useMemo","results","ClassicMessageBubble","useMemo","LightMessageBubble","DarkMessageBubble","search","useState","useRef","useEffect","useCallback"]}