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.
- package/README.md +281 -35
- package/dist/next/{SageDeskWidget-P3H2VJR5.js → SageDeskWidget-SJVE6QK3.js} +177 -32
- package/dist/next/SageDeskWidget-SJVE6QK3.js.map +1 -0
- package/dist/next/index.cjs +192 -35
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +10 -3
- package/dist/next/index.d.ts +10 -3
- package/dist/next/index.js +10 -4
- package/dist/next/index.js.map +1 -1
- package/dist/react/index.cjs +176 -31
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +14 -5
- package/dist/react/index.d.ts +14 -5
- package/dist/react/index.js +176 -31
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.cjs +376 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +62 -0
- package/dist/server/index.d.ts +62 -0
- package/dist/server/index.js +340 -0
- package/dist/server/index.js.map +1 -0
- package/dist/vanilla/index.cjs +37 -9
- package/dist/vanilla/index.cjs.map +1 -1
- package/dist/vanilla/index.d.cts +4 -2
- package/dist/vanilla/index.d.ts +4 -2
- package/dist/vanilla/index.js +37 -9
- package/dist/vanilla/index.js.map +1 -1
- package/package.json +10 -3
- package/dist/next/SageDeskWidget-P3H2VJR5.js.map +0 -1
package/dist/next/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { useState, useEffect, Suspense, lazy } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
var LazyWidget = lazy(
|
|
7
|
-
() => import("./SageDeskWidget-
|
|
7
|
+
() => import("./SageDeskWidget-SJVE6QK3.js").then((mod) => ({ default: mod.SageDeskWidget })).catch((err) => {
|
|
8
8
|
console.warn("[sagedesk] Failed to load widget bundle:", err);
|
|
9
9
|
const Empty = () => null;
|
|
10
10
|
return { default: Empty };
|
|
@@ -13,17 +13,23 @@ var LazyWidget = lazy(
|
|
|
13
13
|
function SageDeskNext(props) {
|
|
14
14
|
const [mounted, setMounted] = useState(false);
|
|
15
15
|
useEffect(() => {
|
|
16
|
-
|
|
16
|
+
const mode = props.mode ?? "local";
|
|
17
|
+
if (mode === "local" && !props.indexUrl) {
|
|
17
18
|
console.warn(
|
|
18
19
|
'[sagedesk] Missing required prop: indexUrl. The widget will not load.\nMake sure you ran `npx sagedesk build` and are passing indexUrl="/support-index.json" (or wherever you placed the output file in public/).'
|
|
19
20
|
);
|
|
20
|
-
} else if (
|
|
21
|
+
} else if (mode === "llm" && !props.endpoint) {
|
|
22
|
+
console.warn(
|
|
23
|
+
'[sagedesk] Missing required prop: endpoint for LLM mode. The widget will not load.\nProvide your backend route, e.g. endpoint="/api/sagedesk".'
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
if (props.indexUrl && !props.indexUrl.startsWith("/") && !props.indexUrl.startsWith("http")) {
|
|
21
27
|
console.warn(
|
|
22
28
|
`[sagedesk] indexUrl "${props.indexUrl}" looks like a relative path. It should start with "/" (e.g. "/support-index.json") so it resolves correctly from any page.`
|
|
23
29
|
);
|
|
24
30
|
}
|
|
25
31
|
setMounted(true);
|
|
26
|
-
}, []);
|
|
32
|
+
}, [props.mode, props.indexUrl, props.endpoint]);
|
|
27
33
|
if (!mounted) return null;
|
|
28
34
|
return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(LazyWidget, { ...props }) });
|
|
29
35
|
}
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/SageDeskNext.tsx"],"sourcesContent":["'use client';\n\nimport { useState, useEffect, Suspense, lazy } from 'react';\nimport type { SageDeskWidgetProps } from '../react/SageDeskWidget.js';\n\nconst LazyWidget = lazy(() =>\n import('../react/SageDeskWidget.js')\n .then((mod) => ({ default: mod.SageDeskWidget }))\n .catch((err) => {\n console.warn('[sagedesk] Failed to load widget bundle:', err);\n const Empty = () => null;\n return { default: Empty };\n })\n);\n\nexport function SageDeskNext(props: SageDeskWidgetProps) {\n // useState + useEffect ensures server render and initial client hydration\n // both return null (no mismatch). The widget appears only after hydration.\n // Using typeof window directly in render body causes a hydration mismatch\n // in Next.js App Router because 'use client' components still SSR.\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n if (!props.indexUrl) {\n console.warn(\n '[sagedesk] Missing required prop: indexUrl. The widget will not load.\\n' +\n 'Make sure you ran `npx sagedesk build` and are passing indexUrl=\"/support-index.json\" (or wherever you placed the output file in public/).'\n );\n } else if (!props.indexUrl.startsWith('/') && !props.indexUrl.startsWith('http')) {\n console.warn(\n `[sagedesk] indexUrl \"${props.indexUrl}\" looks like a relative path. ` +\n 'It should start with \"/\" (e.g. \"/support-index.json\") so it resolves correctly from any page.'\n );\n }\n setMounted(true);\n }, []);\n\n if (!mounted) return null;\n\n return (\n <Suspense fallback={null}>\n <LazyWidget {...props} />\n </Suspense>\n );\n}\n"],"mappings":";;;AAEA,SAAS,UAAU,WAAW,UAAU,YAAY;
|
|
1
|
+
{"version":3,"sources":["../../src/next/SageDeskNext.tsx"],"sourcesContent":["'use client';\n\nimport { useState, useEffect, Suspense, lazy } from 'react';\nimport type { SageDeskWidgetProps } from '../react/SageDeskWidget.js';\n\nconst LazyWidget = lazy(() =>\n import('../react/SageDeskWidget.js')\n .then((mod) => ({ default: mod.SageDeskWidget }))\n .catch((err) => {\n console.warn('[sagedesk] Failed to load widget bundle:', err);\n const Empty = () => null;\n return { default: Empty };\n })\n);\n\nexport function SageDeskNext(props: SageDeskWidgetProps) {\n // useState + useEffect ensures server render and initial client hydration\n // both return null (no mismatch). The widget appears only after hydration.\n // Using typeof window directly in render body causes a hydration mismatch\n // in Next.js App Router because 'use client' components still SSR.\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n const mode = props.mode ?? 'local';\n\n if (mode === 'local' && !props.indexUrl) {\n console.warn(\n '[sagedesk] Missing required prop: indexUrl. The widget will not load.\\n' +\n 'Make sure you ran `npx sagedesk build` and are passing indexUrl=\"/support-index.json\" (or wherever you placed the output file in public/).'\n );\n } else if (mode === 'llm' && !props.endpoint) {\n console.warn(\n '[sagedesk] Missing required prop: endpoint for LLM mode. The widget will not load.\\n' +\n 'Provide your backend route, e.g. endpoint=\"/api/sagedesk\".'\n );\n }\n\n if (props.indexUrl && !props.indexUrl.startsWith('/') && !props.indexUrl.startsWith('http')) {\n console.warn(\n `[sagedesk] indexUrl \"${props.indexUrl}\" looks like a relative path. ` +\n 'It should start with \"/\" (e.g. \"/support-index.json\") so it resolves correctly from any page.'\n );\n }\n setMounted(true);\n }, [props.mode, props.indexUrl, props.endpoint]);\n\n if (!mounted) return null;\n\n return (\n <Suspense fallback={null}>\n <LazyWidget {...props} />\n </Suspense>\n );\n}\n"],"mappings":";;;AAEA,SAAS,UAAU,WAAW,UAAU,YAAY;AAgD9C;AA7CN,IAAM,aAAa;AAAA,EAAK,MACtB,OAAO,8BAA4B,EAChC,KAAK,CAAC,SAAS,EAAE,SAAS,IAAI,eAAe,EAAE,EAC/C,MAAM,CAAC,QAAQ;AACd,YAAQ,KAAK,4CAA4C,GAAG;AAC5D,UAAM,QAAQ,MAAM;AACpB,WAAO,EAAE,SAAS,MAAM;AAAA,EAC1B,CAAC;AACL;AAEO,SAAS,aAAa,OAA4B;AAKvD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,YAAU,MAAM;AACd,UAAM,OAAO,MAAM,QAAQ;AAE3B,QAAI,SAAS,WAAW,CAAC,MAAM,UAAU;AACvC,cAAQ;AAAA,QACN;AAAA,MAEF;AAAA,IACF,WAAW,SAAS,SAAS,CAAC,MAAM,UAAU;AAC5C,cAAQ;AAAA,QACN;AAAA,MAEF;AAAA,IACF;AAEA,QAAI,MAAM,YAAY,CAAC,MAAM,SAAS,WAAW,GAAG,KAAK,CAAC,MAAM,SAAS,WAAW,MAAM,GAAG;AAC3F,cAAQ;AAAA,QACN,wBAAwB,MAAM,QAAQ;AAAA,MAExC;AAAA,IACF;AACA,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,MAAM,MAAM,MAAM,UAAU,MAAM,QAAQ,CAAC;AAE/C,MAAI,CAAC,QAAS,QAAO;AAErB,SACE,oBAAC,YAAS,UAAU,MAClB,8BAAC,cAAY,GAAG,OAAO,GACzB;AAEJ;","names":[]}
|
package/dist/react/index.cjs
CHANGED
|
@@ -126,17 +126,33 @@ function dotProduct(a, b) {
|
|
|
126
126
|
for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
|
|
127
127
|
return dot;
|
|
128
128
|
}
|
|
129
|
+
function insertSorted(arr, item, maxLen) {
|
|
130
|
+
arr.push(item);
|
|
131
|
+
let i = arr.length - 1;
|
|
132
|
+
while (i > 0 && arr[i - 1].score < arr[i].score) {
|
|
133
|
+
const tmp = arr[i - 1];
|
|
134
|
+
arr[i - 1] = arr[i];
|
|
135
|
+
arr[i] = tmp;
|
|
136
|
+
i--;
|
|
137
|
+
}
|
|
138
|
+
if (arr.length > maxLen) arr.pop();
|
|
139
|
+
}
|
|
129
140
|
function search(queryVector, index, topK = 3, minScore = 0.42) {
|
|
130
141
|
const results = [];
|
|
131
142
|
for (const chunk of index) {
|
|
132
143
|
const score = dotProduct(queryVector, chunk.vector384);
|
|
133
144
|
if (score < minScore) continue;
|
|
134
145
|
if (results.length < topK) {
|
|
135
|
-
results
|
|
136
|
-
results.sort((a, b) => b.score - a.score);
|
|
146
|
+
insertSorted(results, { chunk, score }, topK);
|
|
137
147
|
} else if (score > results[topK - 1].score) {
|
|
138
148
|
results[topK - 1] = { chunk, score };
|
|
139
|
-
|
|
149
|
+
let i = topK - 1;
|
|
150
|
+
while (i > 0 && results[i - 1].score < results[i].score) {
|
|
151
|
+
const tmp = results[i - 1];
|
|
152
|
+
results[i - 1] = results[i];
|
|
153
|
+
results[i] = tmp;
|
|
154
|
+
i--;
|
|
155
|
+
}
|
|
140
156
|
}
|
|
141
157
|
}
|
|
142
158
|
return results;
|
|
@@ -147,15 +163,23 @@ function keywordSearch(query, index, topK = 3) {
|
|
|
147
163
|
const results = [];
|
|
148
164
|
for (const chunk of index) {
|
|
149
165
|
const chunkLower = chunk.textLower || chunk.text.toLowerCase();
|
|
150
|
-
|
|
166
|
+
let matchCount = 0;
|
|
167
|
+
for (const t of terms) {
|
|
168
|
+
if (chunkLower.includes(t)) matchCount++;
|
|
169
|
+
}
|
|
151
170
|
const score = matchCount / terms.length;
|
|
152
171
|
if (score <= 0) continue;
|
|
153
172
|
if (results.length < topK) {
|
|
154
|
-
results
|
|
155
|
-
results.sort((a, b) => b.score - a.score);
|
|
173
|
+
insertSorted(results, { chunk, score }, topK);
|
|
156
174
|
} else if (score > results[topK - 1].score) {
|
|
157
175
|
results[topK - 1] = { chunk, score };
|
|
158
|
-
|
|
176
|
+
let i = topK - 1;
|
|
177
|
+
while (i > 0 && results[i - 1].score < results[i].score) {
|
|
178
|
+
const tmp = results[i - 1];
|
|
179
|
+
results[i - 1] = results[i];
|
|
180
|
+
results[i] = tmp;
|
|
181
|
+
i--;
|
|
182
|
+
}
|
|
159
183
|
}
|
|
160
184
|
}
|
|
161
185
|
return results;
|
|
@@ -258,6 +282,17 @@ function getFallback(config) {
|
|
|
258
282
|
}
|
|
259
283
|
|
|
260
284
|
// src/react/useSageDesk.ts
|
|
285
|
+
function logFallbackWarning(reason) {
|
|
286
|
+
if (!reason) return;
|
|
287
|
+
const messages = {
|
|
288
|
+
"auth-error": "[sagedesk] Support service authentication failed. Showing relevant knowledge instead.",
|
|
289
|
+
"quota-exceeded": "[sagedesk] Support service quota exhausted. Showing relevant knowledge instead.",
|
|
290
|
+
"timeout": "[sagedesk] Support service took too long to respond. Showing relevant knowledge instead.",
|
|
291
|
+
"api-error": "[sagedesk] Support service error. Showing relevant knowledge instead.",
|
|
292
|
+
"malformed-response": "[sagedesk] Support service returned invalid response. Showing relevant knowledge instead."
|
|
293
|
+
};
|
|
294
|
+
console.warn(messages[reason] || "[sagedesk] Support service unavailable. Showing relevant knowledge instead.");
|
|
295
|
+
}
|
|
261
296
|
var initialState = {
|
|
262
297
|
messages: [],
|
|
263
298
|
isOpen: false,
|
|
@@ -310,6 +345,11 @@ function useSageDesk(config) {
|
|
|
310
345
|
const startEngine = (0, import_react.useCallback)(async () => {
|
|
311
346
|
if (engineStartedRef.current) return;
|
|
312
347
|
engineStartedRef.current = true;
|
|
348
|
+
if (config.mode === "llm") {
|
|
349
|
+
setChips(config.agent.suggestedChips ?? []);
|
|
350
|
+
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "ready" } });
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
313
353
|
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "loading-index" } });
|
|
314
354
|
try {
|
|
315
355
|
indexRef.current = await fetchIndex(config.indexUrl);
|
|
@@ -336,7 +376,7 @@ function useSageDesk(config) {
|
|
|
336
376
|
embedderRef.current = new EmbedderRuntime();
|
|
337
377
|
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "degraded" } });
|
|
338
378
|
}
|
|
339
|
-
}, [config.indexUrl, config.agent.suggestedChips, addMessage]);
|
|
379
|
+
}, [config.mode, config.indexUrl, config.agent.suggestedChips, addMessage]);
|
|
340
380
|
const greetingShownRef = (0, import_react.useRef)(false);
|
|
341
381
|
const open = (0, import_react.useCallback)(() => {
|
|
342
382
|
dispatch({ type: "OPEN" });
|
|
@@ -379,8 +419,37 @@ function useSageDesk(config) {
|
|
|
379
419
|
}
|
|
380
420
|
let botText;
|
|
381
421
|
let isFallback = false;
|
|
382
|
-
let
|
|
383
|
-
|
|
422
|
+
let fallbackReason;
|
|
423
|
+
let retrievalMode = "keyword";
|
|
424
|
+
if (config.mode === "llm") {
|
|
425
|
+
if (!config.endpoint) {
|
|
426
|
+
console.warn('[sagedesk] LLM mode requires an "endpoint" prop.');
|
|
427
|
+
botText = getFallback(config.agent);
|
|
428
|
+
isFallback = true;
|
|
429
|
+
} else {
|
|
430
|
+
try {
|
|
431
|
+
const res = await fetch(config.endpoint, {
|
|
432
|
+
method: "POST",
|
|
433
|
+
headers: { "Content-Type": "application/json" },
|
|
434
|
+
body: JSON.stringify({ query: trimmed })
|
|
435
|
+
});
|
|
436
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
437
|
+
const data = await res.json();
|
|
438
|
+
if (data.isFallback || !data.answer) {
|
|
439
|
+
fallbackReason = data.fallbackReason;
|
|
440
|
+
logFallbackWarning(fallbackReason);
|
|
441
|
+
botText = getFallback(config.agent);
|
|
442
|
+
isFallback = true;
|
|
443
|
+
} else {
|
|
444
|
+
botText = data.answer;
|
|
445
|
+
}
|
|
446
|
+
} catch (err) {
|
|
447
|
+
console.warn("[sagedesk] Support service unavailable. Using cached knowledge instead.");
|
|
448
|
+
botText = getFallback(config.agent);
|
|
449
|
+
isFallback = true;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
} else if (!indexRef.current) {
|
|
384
453
|
botText = getFallback(config.agent);
|
|
385
454
|
isFallback = true;
|
|
386
455
|
} else {
|
|
@@ -391,7 +460,7 @@ function useSageDesk(config) {
|
|
|
391
460
|
embedderRef.current,
|
|
392
461
|
config.search
|
|
393
462
|
);
|
|
394
|
-
|
|
463
|
+
retrievalMode = res.mode;
|
|
395
464
|
if (res.results.length > 0) {
|
|
396
465
|
botText = buildAnswer(res.results);
|
|
397
466
|
} else {
|
|
@@ -404,11 +473,13 @@ function useSageDesk(config) {
|
|
|
404
473
|
isFallback = true;
|
|
405
474
|
}
|
|
406
475
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
476
|
+
if (config.mode !== "llm") {
|
|
477
|
+
const elapsed = Date.now() - typingStart;
|
|
478
|
+
const delayBase = retrievalMode === "keyword" || isFallback ? 800 : 3e3;
|
|
479
|
+
const minTypingMs = delayBase + Math.random() * 2e3;
|
|
480
|
+
const remaining = minTypingMs - elapsed;
|
|
481
|
+
if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
|
|
482
|
+
}
|
|
412
483
|
dispatch({ type: "SET_TYPING", payload: false });
|
|
413
484
|
addMessage({ role: "bot", text: botText, isFallback });
|
|
414
485
|
},
|
|
@@ -431,6 +502,51 @@ function useSageDesk(config) {
|
|
|
431
502
|
return { state, chips: activeChips, open, close, submit };
|
|
432
503
|
}
|
|
433
504
|
|
|
505
|
+
// src/react/markdownUtils.ts
|
|
506
|
+
var import_marked = require("marked");
|
|
507
|
+
var import_dompurify = __toESM(require("dompurify"), 1);
|
|
508
|
+
import_marked.marked.setOptions({
|
|
509
|
+
breaks: true,
|
|
510
|
+
gfm: true,
|
|
511
|
+
pedantic: false
|
|
512
|
+
});
|
|
513
|
+
var PURIFY_CONFIG = {
|
|
514
|
+
ALLOWED_TAGS: [
|
|
515
|
+
"p",
|
|
516
|
+
"br",
|
|
517
|
+
"strong",
|
|
518
|
+
"em",
|
|
519
|
+
"u",
|
|
520
|
+
"h1",
|
|
521
|
+
"h2",
|
|
522
|
+
"h3",
|
|
523
|
+
"h4",
|
|
524
|
+
"h5",
|
|
525
|
+
"h6",
|
|
526
|
+
"ul",
|
|
527
|
+
"ol",
|
|
528
|
+
"li",
|
|
529
|
+
"blockquote",
|
|
530
|
+
"code",
|
|
531
|
+
"pre",
|
|
532
|
+
"a",
|
|
533
|
+
"hr"
|
|
534
|
+
],
|
|
535
|
+
ALLOWED_ATTR: ["href", "title", "target", "rel"],
|
|
536
|
+
ALLOW_DATA_ATTR: false
|
|
537
|
+
};
|
|
538
|
+
import_dompurify.default.addHook("afterSanitizeAttributes", (node) => {
|
|
539
|
+
if (node.tagName.toLowerCase() === "a") {
|
|
540
|
+
node.setAttribute("target", "_blank");
|
|
541
|
+
node.setAttribute("rel", "noopener noreferrer");
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
function parseMarkdown(markdown) {
|
|
545
|
+
const html = import_marked.marked.parse(markdown);
|
|
546
|
+
const sanitized = import_dompurify.default.sanitize(html, PURIFY_CONFIG);
|
|
547
|
+
return sanitized;
|
|
548
|
+
}
|
|
549
|
+
|
|
434
550
|
// src/react/SageDeskWidget.tsx
|
|
435
551
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
436
552
|
var STYLE_ID = "sagedesk-widget-styles";
|
|
@@ -460,6 +576,28 @@ var SHARED = `
|
|
|
460
576
|
}
|
|
461
577
|
.sd-r-scrollable::-webkit-scrollbar { display: none !important; }
|
|
462
578
|
.sd-r-scrollable > * { flex-shrink: 0 !important; }
|
|
579
|
+
.sd-r-markdown h1, .sd-r-markdown h2, .sd-r-markdown h3, .sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 {
|
|
580
|
+
margin: 12px 0 8px 0 !important; font-weight: 600 !important; line-height: 1.3 !important;
|
|
581
|
+
}
|
|
582
|
+
.sd-r-markdown h1 { font-size: 1.3em !important; }
|
|
583
|
+
.sd-r-markdown h2 { font-size: 1.2em !important; }
|
|
584
|
+
.sd-r-markdown h3 { font-size: 1.1em !important; }
|
|
585
|
+
.sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 { font-size: 1em !important; }
|
|
586
|
+
.sd-r-markdown strong { font-weight: 600 !important; }
|
|
587
|
+
.sd-r-markdown em { font-style: italic !important; }
|
|
588
|
+
.sd-r-markdown u { text-decoration: underline !important; }
|
|
589
|
+
.sd-r-markdown ul, .sd-r-markdown ol { margin: 8px 0 !important; padding-left: 20px !important; }
|
|
590
|
+
.sd-r-markdown li { margin: 4px 0 !important; }
|
|
591
|
+
.sd-r-markdown blockquote { margin: 8px 0 !important; padding-left: 12px !important; border-left: 3px solid currentColor !important; opacity: 0.8 !important; }
|
|
592
|
+
.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; }
|
|
593
|
+
.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; }
|
|
594
|
+
.sd-r-markdown pre code { background: none !important; padding: 0 !important; }
|
|
595
|
+
.sd-r-markdown hr { border: none !important; border-top: 1px solid currentColor !important; opacity: 0.3 !important; margin: 10px 0 !important; }
|
|
596
|
+
.sd-r-markdown a { text-decoration: underline !important; opacity: 0.9 !important; }
|
|
597
|
+
.sd-r-markdown a:hover { opacity: 1 !important; }
|
|
598
|
+
.sd-r-markdown p { margin: 6px 0 !important; }
|
|
599
|
+
.sd-r-markdown > *:first-child { margin-top: 0 !important; }
|
|
600
|
+
.sd-r-markdown > *:last-child { margin-bottom: 0 !important; }
|
|
463
601
|
@media (max-width: 420px) {
|
|
464
602
|
.sd-r-panel {
|
|
465
603
|
bottom: 0 !important; right: 0 !important; left: 0 !important;
|
|
@@ -546,7 +684,7 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx
|
|
|
546
684
|
{
|
|
547
685
|
href: "https://github.com/mzeeshanwahid/sagedesk",
|
|
548
686
|
target: "_blank",
|
|
549
|
-
rel: "noopener
|
|
687
|
+
rel: "noopener",
|
|
550
688
|
style: {
|
|
551
689
|
color: dark ? "rgba(255,255,255,0.7)" : "#5a5a64",
|
|
552
690
|
fontWeight: 500,
|
|
@@ -558,6 +696,7 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx
|
|
|
558
696
|
] });
|
|
559
697
|
function ClassicMessageBubble({ msg, accent }) {
|
|
560
698
|
const isBot = msg.role === "bot";
|
|
699
|
+
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
561
700
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: isBot ? "flex-start" : "flex-end", gap: "4px" }, children: [
|
|
562
701
|
msg.isFallback && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: "11px", fontWeight: 500, color: "#9b9aa3", margin: 0, padding: "0 4px", fontFamily: "inherit" }, children: "Not sure about that one" }),
|
|
563
702
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
|
|
@@ -570,10 +709,9 @@ function ClassicMessageBubble({ msg, accent }) {
|
|
|
570
709
|
color: isBot ? "#1a1a2e" : "#fff",
|
|
571
710
|
border: isBot ? "1px solid rgba(20,20,40,0.06)" : "none",
|
|
572
711
|
boxShadow: isBot ? "0 1px 2px rgba(20,20,40,0.04)" : `0 6px 16px -6px color-mix(in oklab, ${accent} 60%, transparent)`,
|
|
573
|
-
whiteSpace: "pre-wrap",
|
|
574
712
|
wordBreak: "break-word",
|
|
575
713
|
fontFamily: "inherit"
|
|
576
|
-
}, children: msg.text }),
|
|
714
|
+
}, className: "sd-r-markdown", children: isBot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text }) }),
|
|
577
715
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: {
|
|
578
716
|
fontSize: "11px",
|
|
579
717
|
color: "#a8a8b0",
|
|
@@ -603,6 +741,7 @@ function ClassicTypingIndicator() {
|
|
|
603
741
|
}
|
|
604
742
|
function LightMessageBubble({ msg, accent, agentName }) {
|
|
605
743
|
const isBot = msg.role === "bot";
|
|
744
|
+
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
606
745
|
if (isBot) {
|
|
607
746
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
608
747
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
|
|
@@ -619,7 +758,7 @@ function LightMessageBubble({ msg, accent, agentName }) {
|
|
|
619
758
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "11px", color: "#a8a89e", fontVariantNumeric: "tabular-nums", fontFamily: "inherit" }, children: "just now" })
|
|
620
759
|
] }),
|
|
621
760
|
msg.isFallback && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { fontSize: "11px", color: "#9b9aa3", margin: "0 0 4px", fontFamily: "inherit" }, children: "Not sure about that one" }),
|
|
622
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: "14px", lineHeight: 1.55, color: "#2a2a36", fontFamily: "inherit",
|
|
761
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: "14px", lineHeight: 1.55, color: "#2a2a36", fontFamily: "inherit", wordBreak: "break-word" }, className: "sd-r-markdown", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) })
|
|
623
762
|
] })
|
|
624
763
|
] });
|
|
625
764
|
}
|
|
@@ -667,6 +806,7 @@ function LightTypingIndicator({ accent }) {
|
|
|
667
806
|
}
|
|
668
807
|
function DarkMessageBubble({ msg, accent }) {
|
|
669
808
|
const isBot = msg.role === "bot";
|
|
809
|
+
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
670
810
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: {
|
|
671
811
|
maxWidth: "85%",
|
|
672
812
|
padding: "12px 14px",
|
|
@@ -678,12 +818,11 @@ function DarkMessageBubble({ msg, accent }) {
|
|
|
678
818
|
color: isBot ? "rgba(255,255,255,0.92)" : "#fff",
|
|
679
819
|
alignSelf: isBot ? "flex-start" : "flex-end",
|
|
680
820
|
boxShadow: isBot ? "none" : `0 8px 20px -8px color-mix(in oklab, ${accent} 70%, transparent)`,
|
|
681
|
-
whiteSpace: "pre-wrap",
|
|
682
821
|
wordBreak: "break-word",
|
|
683
822
|
fontFamily: "inherit"
|
|
684
|
-
}, children: [
|
|
823
|
+
}, className: isBot ? "sd-r-markdown" : "", children: [
|
|
685
824
|
msg.isFallback && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: "11px", color: "rgba(255,255,255,0.5)", display: "block", marginBottom: "4px" }, children: "Not sure about that one" }),
|
|
686
|
-
msg.text
|
|
825
|
+
isBot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text })
|
|
687
826
|
] });
|
|
688
827
|
}
|
|
689
828
|
function DarkTypingIndicator() {
|
|
@@ -1303,16 +1442,22 @@ function renderDark(p) {
|
|
|
1303
1442
|
] })
|
|
1304
1443
|
] });
|
|
1305
1444
|
}
|
|
1306
|
-
function SageDeskWidget({ indexUrl, agent, search: search2 }) {
|
|
1307
|
-
|
|
1445
|
+
function SageDeskWidget({ mode, indexUrl, endpoint, agent, search: search2 }) {
|
|
1446
|
+
const resolvedMode = mode ?? "local";
|
|
1447
|
+
if (!agent?.name) {
|
|
1448
|
+
throw new Error('[sagedesk] Required prop "agent.name" is missing.');
|
|
1449
|
+
}
|
|
1450
|
+
if (resolvedMode === "local" && !indexUrl) {
|
|
1308
1451
|
throw new Error(
|
|
1309
|
-
'[sagedesk] Required prop "indexUrl" is missing. Run `npx sagedesk build` and pass the output path, e.g. indexUrl="/support-index.json".'
|
|
1452
|
+
'[sagedesk] Required prop "indexUrl" is missing for local mode. Run `npx sagedesk build` and pass the output path, e.g. indexUrl="/support-index.json".'
|
|
1310
1453
|
);
|
|
1311
1454
|
}
|
|
1312
|
-
if (!
|
|
1313
|
-
throw new Error(
|
|
1455
|
+
if (resolvedMode === "llm" && !endpoint) {
|
|
1456
|
+
throw new Error(
|
|
1457
|
+
'[sagedesk] Required prop "endpoint" is missing for llm mode. Provide your backend route, e.g. endpoint="/api/sagedesk".'
|
|
1458
|
+
);
|
|
1314
1459
|
}
|
|
1315
|
-
const config = { indexUrl, agent, search: search2 };
|
|
1460
|
+
const config = { mode: resolvedMode, indexUrl, endpoint, agent, search: search2 };
|
|
1316
1461
|
const { state, chips, open, close, submit } = useSageDesk(config);
|
|
1317
1462
|
const theme = agent.theme ?? "classic";
|
|
1318
1463
|
const accent = agent.accentColor ?? "#534AB7";
|
|
@@ -1325,7 +1470,7 @@ function SageDeskWidget({ indexUrl, agent, search: search2 }) {
|
|
|
1325
1470
|
const triggerRef = (0, import_react2.useRef)(null);
|
|
1326
1471
|
const [mounted, setMounted] = (0, import_react2.useState)(false);
|
|
1327
1472
|
(0, import_react2.useEffect)(() => {
|
|
1328
|
-
if (!indexUrl.startsWith("/") && !indexUrl.startsWith("http")) {
|
|
1473
|
+
if (resolvedMode === "local" && indexUrl && !indexUrl.startsWith("/") && !indexUrl.startsWith("http")) {
|
|
1329
1474
|
console.warn(
|
|
1330
1475
|
`[sagedesk] indexUrl "${indexUrl}" looks like a relative path. It should start with "/" so it resolves correctly from any page.`
|
|
1331
1476
|
);
|
|
@@ -1367,7 +1512,7 @@ function SageDeskWidget({ indexUrl, agent, search: search2 }) {
|
|
|
1367
1512
|
[handleSubmit]
|
|
1368
1513
|
);
|
|
1369
1514
|
if (!mounted || typeof document === "undefined") return null;
|
|
1370
|
-
const showPoweredBy =
|
|
1515
|
+
const showPoweredBy = true;
|
|
1371
1516
|
const showChips = chips.length > 0;
|
|
1372
1517
|
const panelClass = isClosing ? "sd-r-closing" : state.isOpen ? "sd-r-opening" : "";
|
|
1373
1518
|
const props = {
|