sagedesk 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -96,17 +96,33 @@ function dotProduct(a, b) {
96
96
  for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
97
97
  return dot;
98
98
  }
99
+ function insertSorted(arr, item, maxLen) {
100
+ arr.push(item);
101
+ let i = arr.length - 1;
102
+ while (i > 0 && arr[i - 1].score < arr[i].score) {
103
+ const tmp = arr[i - 1];
104
+ arr[i - 1] = arr[i];
105
+ arr[i] = tmp;
106
+ i--;
107
+ }
108
+ if (arr.length > maxLen) arr.pop();
109
+ }
99
110
  function search(queryVector, index, topK = 3, minScore = 0.42) {
100
111
  const results = [];
101
112
  for (const chunk of index) {
102
113
  const score = dotProduct(queryVector, chunk.vector384);
103
114
  if (score < minScore) continue;
104
115
  if (results.length < topK) {
105
- results.push({ chunk, score });
106
- results.sort((a, b) => b.score - a.score);
116
+ insertSorted(results, { chunk, score }, topK);
107
117
  } else if (score > results[topK - 1].score) {
108
118
  results[topK - 1] = { chunk, score };
109
- results.sort((a, b) => b.score - a.score);
119
+ let i = topK - 1;
120
+ while (i > 0 && results[i - 1].score < results[i].score) {
121
+ const tmp = results[i - 1];
122
+ results[i - 1] = results[i];
123
+ results[i] = tmp;
124
+ i--;
125
+ }
110
126
  }
111
127
  }
112
128
  return results;
@@ -117,15 +133,23 @@ function keywordSearch(query, index, topK = 3) {
117
133
  const results = [];
118
134
  for (const chunk of index) {
119
135
  const chunkLower = chunk.textLower || chunk.text.toLowerCase();
120
- const matchCount = terms.filter((t) => chunkLower.includes(t)).length;
136
+ let matchCount = 0;
137
+ for (const t of terms) {
138
+ if (chunkLower.includes(t)) matchCount++;
139
+ }
121
140
  const score = matchCount / terms.length;
122
141
  if (score <= 0) continue;
123
142
  if (results.length < topK) {
124
- results.push({ chunk, score });
125
- results.sort((a, b) => b.score - a.score);
143
+ insertSorted(results, { chunk, score }, topK);
126
144
  } else if (score > results[topK - 1].score) {
127
145
  results[topK - 1] = { chunk, score };
128
- results.sort((a, b) => b.score - a.score);
146
+ let i = topK - 1;
147
+ while (i > 0 && results[i - 1].score < results[i].score) {
148
+ const tmp = results[i - 1];
149
+ results[i - 1] = results[i];
150
+ results[i] = tmp;
151
+ i--;
152
+ }
129
153
  }
130
154
  }
131
155
  return results;
@@ -228,6 +252,17 @@ function getFallback(config) {
228
252
  }
229
253
 
230
254
  // src/react/useSageDesk.ts
255
+ function logFallbackWarning(reason) {
256
+ if (!reason) return;
257
+ const messages = {
258
+ "auth-error": "[sagedesk] Support service authentication failed. Showing relevant knowledge instead.",
259
+ "quota-exceeded": "[sagedesk] Support service quota exhausted. Showing relevant knowledge instead.",
260
+ "timeout": "[sagedesk] Support service took too long to respond. Showing relevant knowledge instead.",
261
+ "api-error": "[sagedesk] Support service error. Showing relevant knowledge instead.",
262
+ "malformed-response": "[sagedesk] Support service returned invalid response. Showing relevant knowledge instead."
263
+ };
264
+ console.warn(messages[reason] || "[sagedesk] Support service unavailable. Showing relevant knowledge instead.");
265
+ }
231
266
  var initialState = {
232
267
  messages: [],
233
268
  isOpen: false,
@@ -280,6 +315,11 @@ function useSageDesk(config) {
280
315
  const startEngine = useCallback(async () => {
281
316
  if (engineStartedRef.current) return;
282
317
  engineStartedRef.current = true;
318
+ if (config.mode === "llm") {
319
+ setChips(config.agent.suggestedChips ?? []);
320
+ dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "ready" } });
321
+ return;
322
+ }
283
323
  dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "loading-index" } });
284
324
  try {
285
325
  indexRef.current = await fetchIndex(config.indexUrl);
@@ -306,7 +346,7 @@ function useSageDesk(config) {
306
346
  embedderRef.current = new EmbedderRuntime();
307
347
  dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "degraded" } });
308
348
  }
309
- }, [config.indexUrl, config.agent.suggestedChips, addMessage]);
349
+ }, [config.mode, config.indexUrl, config.agent.suggestedChips, addMessage]);
310
350
  const greetingShownRef = useRef(false);
311
351
  const open = useCallback(() => {
312
352
  dispatch({ type: "OPEN" });
@@ -349,8 +389,37 @@ function useSageDesk(config) {
349
389
  }
350
390
  let botText;
351
391
  let isFallback = false;
352
- let mode = "keyword";
353
- if (!indexRef.current) {
392
+ let fallbackReason;
393
+ let retrievalMode = "keyword";
394
+ if (config.mode === "llm") {
395
+ if (!config.endpoint) {
396
+ console.warn('[sagedesk] LLM mode requires an "endpoint" prop.');
397
+ botText = getFallback(config.agent);
398
+ isFallback = true;
399
+ } else {
400
+ try {
401
+ const res = await fetch(config.endpoint, {
402
+ method: "POST",
403
+ headers: { "Content-Type": "application/json" },
404
+ body: JSON.stringify({ query: trimmed })
405
+ });
406
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
407
+ const data = await res.json();
408
+ if (data.isFallback || !data.answer) {
409
+ fallbackReason = data.fallbackReason;
410
+ logFallbackWarning(fallbackReason);
411
+ botText = getFallback(config.agent);
412
+ isFallback = true;
413
+ } else {
414
+ botText = data.answer;
415
+ }
416
+ } catch (err) {
417
+ console.warn("[sagedesk] Support service unavailable. Using cached knowledge instead.");
418
+ botText = getFallback(config.agent);
419
+ isFallback = true;
420
+ }
421
+ }
422
+ } else if (!indexRef.current) {
354
423
  botText = getFallback(config.agent);
355
424
  isFallback = true;
356
425
  } else {
@@ -361,7 +430,7 @@ function useSageDesk(config) {
361
430
  embedderRef.current,
362
431
  config.search
363
432
  );
364
- mode = res.mode;
433
+ retrievalMode = res.mode;
365
434
  if (res.results.length > 0) {
366
435
  botText = buildAnswer(res.results);
367
436
  } else {
@@ -374,11 +443,13 @@ function useSageDesk(config) {
374
443
  isFallback = true;
375
444
  }
376
445
  }
377
- const elapsed = Date.now() - typingStart;
378
- const delayBase = mode === "keyword" || isFallback ? 800 : 3e3;
379
- const minTypingMs = delayBase + Math.random() * 2e3;
380
- const remaining = minTypingMs - elapsed;
381
- if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
446
+ if (config.mode !== "llm") {
447
+ const elapsed = Date.now() - typingStart;
448
+ const delayBase = retrievalMode === "keyword" || isFallback ? 800 : 3e3;
449
+ const minTypingMs = delayBase + Math.random() * 2e3;
450
+ const remaining = minTypingMs - elapsed;
451
+ if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
452
+ }
382
453
  dispatch({ type: "SET_TYPING", payload: false });
383
454
  addMessage({ role: "bot", text: botText, isFallback });
384
455
  },
@@ -401,6 +472,51 @@ function useSageDesk(config) {
401
472
  return { state, chips: activeChips, open, close, submit };
402
473
  }
403
474
 
475
+ // src/react/markdownUtils.ts
476
+ import { marked } from "marked";
477
+ import DOMPurify from "dompurify";
478
+ marked.setOptions({
479
+ breaks: true,
480
+ gfm: true,
481
+ pedantic: false
482
+ });
483
+ var PURIFY_CONFIG = {
484
+ ALLOWED_TAGS: [
485
+ "p",
486
+ "br",
487
+ "strong",
488
+ "em",
489
+ "u",
490
+ "h1",
491
+ "h2",
492
+ "h3",
493
+ "h4",
494
+ "h5",
495
+ "h6",
496
+ "ul",
497
+ "ol",
498
+ "li",
499
+ "blockquote",
500
+ "code",
501
+ "pre",
502
+ "a",
503
+ "hr"
504
+ ],
505
+ ALLOWED_ATTR: ["href", "title", "target", "rel"],
506
+ ALLOW_DATA_ATTR: false
507
+ };
508
+ DOMPurify.addHook("afterSanitizeAttributes", (node) => {
509
+ if (node.tagName.toLowerCase() === "a") {
510
+ node.setAttribute("target", "_blank");
511
+ node.setAttribute("rel", "noopener noreferrer");
512
+ }
513
+ });
514
+ function parseMarkdown(markdown) {
515
+ const html = marked.parse(markdown);
516
+ const sanitized = DOMPurify.sanitize(html, PURIFY_CONFIG);
517
+ return sanitized;
518
+ }
519
+
404
520
  // src/react/SageDeskWidget.tsx
405
521
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
406
522
  var STYLE_ID = "sagedesk-widget-styles";
@@ -430,6 +546,28 @@ var SHARED = `
430
546
  }
431
547
  .sd-r-scrollable::-webkit-scrollbar { display: none !important; }
432
548
  .sd-r-scrollable > * { flex-shrink: 0 !important; }
549
+ .sd-r-markdown h1, .sd-r-markdown h2, .sd-r-markdown h3, .sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 {
550
+ margin: 12px 0 8px 0 !important; font-weight: 600 !important; line-height: 1.3 !important;
551
+ }
552
+ .sd-r-markdown h1 { font-size: 1.3em !important; }
553
+ .sd-r-markdown h2 { font-size: 1.2em !important; }
554
+ .sd-r-markdown h3 { font-size: 1.1em !important; }
555
+ .sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 { font-size: 1em !important; }
556
+ .sd-r-markdown strong { font-weight: 600 !important; }
557
+ .sd-r-markdown em { font-style: italic !important; }
558
+ .sd-r-markdown u { text-decoration: underline !important; }
559
+ .sd-r-markdown ul, .sd-r-markdown ol { margin: 8px 0 !important; padding-left: 20px !important; }
560
+ .sd-r-markdown li { margin: 4px 0 !important; }
561
+ .sd-r-markdown blockquote { margin: 8px 0 !important; padding-left: 12px !important; border-left: 3px solid currentColor !important; opacity: 0.8 !important; }
562
+ .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; }
563
+ .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; }
564
+ .sd-r-markdown pre code { background: none !important; padding: 0 !important; }
565
+ .sd-r-markdown hr { border: none !important; border-top: 1px solid currentColor !important; opacity: 0.3 !important; margin: 10px 0 !important; }
566
+ .sd-r-markdown a { text-decoration: underline !important; opacity: 0.9 !important; }
567
+ .sd-r-markdown a:hover { opacity: 1 !important; }
568
+ .sd-r-markdown p { margin: 6px 0 !important; }
569
+ .sd-r-markdown > *:first-child { margin-top: 0 !important; }
570
+ .sd-r-markdown > *:last-child { margin-bottom: 0 !important; }
433
571
  @media (max-width: 420px) {
434
572
  .sd-r-panel {
435
573
  bottom: 0 !important; right: 0 !important; left: 0 !important;
@@ -516,7 +654,7 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ jsxs("div", { style: {
516
654
  {
517
655
  href: "https://github.com/mzeeshanwahid/sagedesk",
518
656
  target: "_blank",
519
- rel: "noopener noreferrer",
657
+ rel: "noopener",
520
658
  style: {
521
659
  color: dark ? "rgba(255,255,255,0.7)" : "#5a5a64",
522
660
  fontWeight: 500,
@@ -528,6 +666,7 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ jsxs("div", { style: {
528
666
  ] });
529
667
  function ClassicMessageBubble({ msg, accent }) {
530
668
  const isBot = msg.role === "bot";
669
+ const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
531
670
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: isBot ? "flex-start" : "flex-end", gap: "4px" }, children: [
532
671
  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" }),
533
672
  /* @__PURE__ */ jsx("div", { style: {
@@ -540,10 +679,9 @@ function ClassicMessageBubble({ msg, accent }) {
540
679
  color: isBot ? "#1a1a2e" : "#fff",
541
680
  border: isBot ? "1px solid rgba(20,20,40,0.06)" : "none",
542
681
  boxShadow: isBot ? "0 1px 2px rgba(20,20,40,0.04)" : `0 6px 16px -6px color-mix(in oklab, ${accent} 60%, transparent)`,
543
- whiteSpace: "pre-wrap",
544
682
  wordBreak: "break-word",
545
683
  fontFamily: "inherit"
546
- }, children: msg.text }),
684
+ }, className: "sd-r-markdown", children: isBot ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text }) }),
547
685
  /* @__PURE__ */ jsx("span", { style: {
548
686
  fontSize: "11px",
549
687
  color: "#a8a8b0",
@@ -573,6 +711,7 @@ function ClassicTypingIndicator() {
573
711
  }
574
712
  function LightMessageBubble({ msg, accent, agentName }) {
575
713
  const isBot = msg.role === "bot";
714
+ const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
576
715
  if (isBot) {
577
716
  return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px" }, children: [
578
717
  /* @__PURE__ */ jsx("div", { style: {
@@ -589,7 +728,7 @@ function LightMessageBubble({ msg, accent, agentName }) {
589
728
  /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: "#a8a89e", fontVariantNumeric: "tabular-nums", fontFamily: "inherit" }, children: "just now" })
590
729
  ] }),
591
730
  msg.isFallback && /* @__PURE__ */ jsx("p", { style: { fontSize: "11px", color: "#9b9aa3", margin: "0 0 4px", fontFamily: "inherit" }, children: "Not sure about that one" }),
592
- /* @__PURE__ */ jsx("div", { style: { fontSize: "14px", lineHeight: 1.55, color: "#2a2a36", fontFamily: "inherit", whiteSpace: "pre-wrap", wordBreak: "break-word" }, children: msg.text })
731
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "14px", lineHeight: 1.55, color: "#2a2a36", fontFamily: "inherit", wordBreak: "break-word" }, className: "sd-r-markdown", children: /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) })
593
732
  ] })
594
733
  ] });
595
734
  }
@@ -637,6 +776,7 @@ function LightTypingIndicator({ accent }) {
637
776
  }
638
777
  function DarkMessageBubble({ msg, accent }) {
639
778
  const isBot = msg.role === "bot";
779
+ const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
640
780
  return /* @__PURE__ */ jsxs("div", { style: {
641
781
  maxWidth: "85%",
642
782
  padding: "12px 14px",
@@ -648,12 +788,11 @@ function DarkMessageBubble({ msg, accent }) {
648
788
  color: isBot ? "rgba(255,255,255,0.92)" : "#fff",
649
789
  alignSelf: isBot ? "flex-start" : "flex-end",
650
790
  boxShadow: isBot ? "none" : `0 8px 20px -8px color-mix(in oklab, ${accent} 70%, transparent)`,
651
- whiteSpace: "pre-wrap",
652
791
  wordBreak: "break-word",
653
792
  fontFamily: "inherit"
654
- }, children: [
793
+ }, className: isBot ? "sd-r-markdown" : "", children: [
655
794
  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" }),
656
- msg.text
795
+ isBot ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text })
657
796
  ] });
658
797
  }
659
798
  function DarkTypingIndicator() {
@@ -1273,16 +1412,22 @@ function renderDark(p) {
1273
1412
  ] })
1274
1413
  ] });
1275
1414
  }
1276
- function SageDeskWidget({ indexUrl, agent, search: search2 }) {
1277
- if (!indexUrl) {
1415
+ function SageDeskWidget({ mode, indexUrl, endpoint, agent, search: search2 }) {
1416
+ const resolvedMode = mode ?? "local";
1417
+ if (!agent?.name) {
1418
+ throw new Error('[sagedesk] Required prop "agent.name" is missing.');
1419
+ }
1420
+ if (resolvedMode === "local" && !indexUrl) {
1278
1421
  throw new Error(
1279
- '[sagedesk] Required prop "indexUrl" is missing. Run `npx sagedesk build` and pass the output path, e.g. indexUrl="/support-index.json".'
1422
+ '[sagedesk] Required prop "indexUrl" is missing for local mode. Run `npx sagedesk build` and pass the output path, e.g. indexUrl="/support-index.json".'
1280
1423
  );
1281
1424
  }
1282
- if (!agent?.name) {
1283
- throw new Error('[sagedesk] Required prop "agent.name" is missing.');
1425
+ if (resolvedMode === "llm" && !endpoint) {
1426
+ throw new Error(
1427
+ '[sagedesk] Required prop "endpoint" is missing for llm mode. Provide your backend route, e.g. endpoint="/api/sagedesk".'
1428
+ );
1284
1429
  }
1285
- const config = { indexUrl, agent, search: search2 };
1430
+ const config = { mode: resolvedMode, indexUrl, endpoint, agent, search: search2 };
1286
1431
  const { state, chips, open, close, submit } = useSageDesk(config);
1287
1432
  const theme = agent.theme ?? "classic";
1288
1433
  const accent = agent.accentColor ?? "#534AB7";
@@ -1295,7 +1440,7 @@ function SageDeskWidget({ indexUrl, agent, search: search2 }) {
1295
1440
  const triggerRef = useRef2(null);
1296
1441
  const [mounted, setMounted] = useState2(false);
1297
1442
  useEffect2(() => {
1298
- if (!indexUrl.startsWith("/") && !indexUrl.startsWith("http")) {
1443
+ if (resolvedMode === "local" && indexUrl && !indexUrl.startsWith("/") && !indexUrl.startsWith("http")) {
1299
1444
  console.warn(
1300
1445
  `[sagedesk] indexUrl "${indexUrl}" looks like a relative path. It should start with "/" so it resolves correctly from any page.`
1301
1446
  );
@@ -1337,7 +1482,7 @@ function SageDeskWidget({ indexUrl, agent, search: search2 }) {
1337
1482
  [handleSubmit]
1338
1483
  );
1339
1484
  if (!mounted || typeof document === "undefined") return null;
1340
- const showPoweredBy = agent.poweredBy !== false;
1485
+ const showPoweredBy = true;
1341
1486
  const showChips = chips.length > 0;
1342
1487
  const panelClass = isClosing ? "sd-r-closing" : state.isOpen ? "sd-r-opening" : "";
1343
1488
  const props = {
@@ -1367,4 +1512,4 @@ function SageDeskWidget({ indexUrl, agent, search: search2 }) {
1367
1512
  export {
1368
1513
  SageDeskWidget
1369
1514
  };
1370
- //# sourceMappingURL=SageDeskWidget-P3H2VJR5.js.map
1515
+ //# sourceMappingURL=SageDeskWidget-SJVE6QK3.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 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\nfunction ClassicMessageBubble({ msg, accent }: { msg: ChatMessage; accent: string }) {\n const isBot = msg.role === 'bot';\n const renderedHtml = isBot ? parseMarkdown(msg.text) : 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\nfunction LightMessageBubble({ msg, accent, agentName }: { msg: ChatMessage; accent: string; agentName: string }) {\n const isBot = msg.role === 'bot';\n const renderedHtml = isBot ? parseMarkdown(msg.text) : 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\nfunction DarkMessageBubble({ msg, accent }: { msg: ChatMessage; accent: string }) {\n const isBot = msg.role === 'bot';\n const renderedHtml = isBot ? parseMarkdown(msg.text) : 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 renderClassic(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 renderLight(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 renderDark(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: SageDeskConfig = { mode: resolvedMode, indexUrl, endpoint, agent, search };\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' ? renderDark(props) :\n theme === 'light' ? renderLight(props) :\n renderClassic(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 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 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 return { ...state, messages: [...state.messages, action.payload] };\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 polling loops always read the current\n // value without capturing a stale closure.\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 [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 // 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: no local index or WASM model needed - mark ready immediately.\n if (config.mode === 'llm') {\n setChips(config.agent.suggestedChips ?? []);\n dispatch({ type: 'SET_ENGINE_STATUS', payload: { status: 'ready' } });\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 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 } 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 }\n }, [config.mode, config.indexUrl, config.agent.suggestedChips, addMessage]);\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 // Uses engineStatusRef so the polling check always reads the live value,\n // not the stale closure value captured when waitForEngine was first called.\n const waitForEngine = useCallback((): Promise<void> => {\n return new Promise((resolve) => {\n const check = () => {\n const s = engineStatusRef.current;\n if (\n s === 'ready' ||\n s === 'degraded' ||\n s === 'error-index' ||\n s === 'error-model'\n ) {\n resolve();\n } else {\n setTimeout(check, 100);\n }\n };\n check();\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: POST query to the consumer's own backend endpoint.\n if (!config.endpoint) {\n console.warn('[sagedesk] LLM mode requires an \"endpoint\" prop.');\n botText = getFallback(config.agent);\n isFallback = true;\n } else {\n try {\n const res = await fetch(config.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ query: trimmed }),\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) ? 800 : 3000;\n const minTypingMs = delayBase + Math.random() * 2000;\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(\n state.messages\n .filter((m) => m.role === 'user')\n .map((m) => m.text.toLowerCase().trim())\n );\n return chips.filter((chip) => !askedTexts.has(chip.toLowerCase().trim()));\n }, [chips, state.messages]);\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","results","search","useState","useRef","useEffect","useCallback"]}