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/react/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
type SageDeskModel = 'all-MiniLM-L6-v2' | 'bge-small-en-v1-5' | 'paraphrase-multilingual-MiniLM-L12-v2' | 'all-mpnet-base-v2';
|
|
4
4
|
type Theme = 'classic' | 'light' | 'dark';
|
|
5
|
+
type SageDeskMode = 'local' | 'llm';
|
|
5
6
|
interface AgentConfig {
|
|
6
7
|
name: string;
|
|
7
8
|
model?: SageDeskModel;
|
|
@@ -13,7 +14,6 @@ interface AgentConfig {
|
|
|
13
14
|
position?: 'bottom-right' | 'bottom-left';
|
|
14
15
|
avatarUrl?: string;
|
|
15
16
|
contactUrl?: string;
|
|
16
|
-
poweredBy?: boolean;
|
|
17
17
|
suggestedChips?: string[];
|
|
18
18
|
}
|
|
19
19
|
interface SearchConfig {
|
|
@@ -21,25 +21,34 @@ interface SearchConfig {
|
|
|
21
21
|
topK?: number;
|
|
22
22
|
}
|
|
23
23
|
interface SageDeskConfig {
|
|
24
|
-
|
|
24
|
+
mode?: SageDeskMode;
|
|
25
|
+
indexUrl?: string;
|
|
26
|
+
endpoint?: string;
|
|
25
27
|
agent: AgentConfig;
|
|
26
28
|
search?: SearchConfig;
|
|
27
29
|
}
|
|
30
|
+
type FallbackReason = 'auth-error' | 'quota-exceeded' | 'timeout' | 'api-error' | 'malformed-response';
|
|
28
31
|
interface ChatMessage {
|
|
29
32
|
id: string;
|
|
30
33
|
role: 'user' | 'bot';
|
|
31
34
|
text: string;
|
|
32
35
|
isFallback?: boolean;
|
|
36
|
+
fallbackReason?: FallbackReason;
|
|
33
37
|
timestamp: Date;
|
|
34
38
|
}
|
|
35
39
|
type EngineStatus = 'idle' | 'loading-index' | 'loading-model' | 'ready' | 'error-index' | 'error-model' | 'degraded';
|
|
36
40
|
|
|
37
41
|
interface SageDeskWidgetProps {
|
|
38
|
-
|
|
42
|
+
/** Operating mode. 'local' (default) runs entirely in the browser via WASM. 'llm' posts to the consumer's own backend. */
|
|
43
|
+
mode?: SageDeskMode;
|
|
44
|
+
/** URL to the pre-built vector index. Required in local mode. */
|
|
45
|
+
indexUrl?: string;
|
|
46
|
+
/** Consumer's backend endpoint that accepts POST { query }. Required in llm mode. */
|
|
47
|
+
endpoint?: string;
|
|
39
48
|
agent: SageDeskConfig['agent'];
|
|
40
49
|
search?: SageDeskConfig['search'];
|
|
41
50
|
}
|
|
42
|
-
declare function SageDeskWidget({ indexUrl, agent, search }: SageDeskWidgetProps): React.ReactPortal | null;
|
|
51
|
+
declare function SageDeskWidget({ mode, indexUrl, endpoint, agent, search }: SageDeskWidgetProps): React.ReactPortal | null;
|
|
43
52
|
|
|
44
53
|
interface State {
|
|
45
54
|
messages: ChatMessage[];
|
|
@@ -58,4 +67,4 @@ interface UseSageDeskReturn {
|
|
|
58
67
|
}
|
|
59
68
|
declare function useSageDesk(config: SageDeskConfig): UseSageDeskReturn;
|
|
60
69
|
|
|
61
|
-
export { type AgentConfig, type ChatMessage, type SageDeskConfig, SageDeskWidget, type SageDeskWidgetProps, type SearchConfig, type UseSageDeskReturn, useSageDesk };
|
|
70
|
+
export { type AgentConfig, type ChatMessage, type SageDeskConfig, type SageDeskMode, SageDeskWidget, type SageDeskWidgetProps, type SearchConfig, type UseSageDeskReturn, useSageDesk };
|
package/dist/react/index.js
CHANGED
|
@@ -94,17 +94,33 @@ function dotProduct(a, b) {
|
|
|
94
94
|
for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
|
|
95
95
|
return dot;
|
|
96
96
|
}
|
|
97
|
+
function insertSorted(arr, item, maxLen) {
|
|
98
|
+
arr.push(item);
|
|
99
|
+
let i = arr.length - 1;
|
|
100
|
+
while (i > 0 && arr[i - 1].score < arr[i].score) {
|
|
101
|
+
const tmp = arr[i - 1];
|
|
102
|
+
arr[i - 1] = arr[i];
|
|
103
|
+
arr[i] = tmp;
|
|
104
|
+
i--;
|
|
105
|
+
}
|
|
106
|
+
if (arr.length > maxLen) arr.pop();
|
|
107
|
+
}
|
|
97
108
|
function search(queryVector, index, topK = 3, minScore = 0.42) {
|
|
98
109
|
const results = [];
|
|
99
110
|
for (const chunk of index) {
|
|
100
111
|
const score = dotProduct(queryVector, chunk.vector384);
|
|
101
112
|
if (score < minScore) continue;
|
|
102
113
|
if (results.length < topK) {
|
|
103
|
-
results
|
|
104
|
-
results.sort((a, b) => b.score - a.score);
|
|
114
|
+
insertSorted(results, { chunk, score }, topK);
|
|
105
115
|
} else if (score > results[topK - 1].score) {
|
|
106
116
|
results[topK - 1] = { chunk, score };
|
|
107
|
-
|
|
117
|
+
let i = topK - 1;
|
|
118
|
+
while (i > 0 && results[i - 1].score < results[i].score) {
|
|
119
|
+
const tmp = results[i - 1];
|
|
120
|
+
results[i - 1] = results[i];
|
|
121
|
+
results[i] = tmp;
|
|
122
|
+
i--;
|
|
123
|
+
}
|
|
108
124
|
}
|
|
109
125
|
}
|
|
110
126
|
return results;
|
|
@@ -115,15 +131,23 @@ function keywordSearch(query, index, topK = 3) {
|
|
|
115
131
|
const results = [];
|
|
116
132
|
for (const chunk of index) {
|
|
117
133
|
const chunkLower = chunk.textLower || chunk.text.toLowerCase();
|
|
118
|
-
|
|
134
|
+
let matchCount = 0;
|
|
135
|
+
for (const t of terms) {
|
|
136
|
+
if (chunkLower.includes(t)) matchCount++;
|
|
137
|
+
}
|
|
119
138
|
const score = matchCount / terms.length;
|
|
120
139
|
if (score <= 0) continue;
|
|
121
140
|
if (results.length < topK) {
|
|
122
|
-
results
|
|
123
|
-
results.sort((a, b) => b.score - a.score);
|
|
141
|
+
insertSorted(results, { chunk, score }, topK);
|
|
124
142
|
} else if (score > results[topK - 1].score) {
|
|
125
143
|
results[topK - 1] = { chunk, score };
|
|
126
|
-
|
|
144
|
+
let i = topK - 1;
|
|
145
|
+
while (i > 0 && results[i - 1].score < results[i].score) {
|
|
146
|
+
const tmp = results[i - 1];
|
|
147
|
+
results[i - 1] = results[i];
|
|
148
|
+
results[i] = tmp;
|
|
149
|
+
i--;
|
|
150
|
+
}
|
|
127
151
|
}
|
|
128
152
|
}
|
|
129
153
|
return results;
|
|
@@ -226,6 +250,17 @@ function getFallback(config) {
|
|
|
226
250
|
}
|
|
227
251
|
|
|
228
252
|
// src/react/useSageDesk.ts
|
|
253
|
+
function logFallbackWarning(reason) {
|
|
254
|
+
if (!reason) return;
|
|
255
|
+
const messages = {
|
|
256
|
+
"auth-error": "[sagedesk] Support service authentication failed. Showing relevant knowledge instead.",
|
|
257
|
+
"quota-exceeded": "[sagedesk] Support service quota exhausted. Showing relevant knowledge instead.",
|
|
258
|
+
"timeout": "[sagedesk] Support service took too long to respond. Showing relevant knowledge instead.",
|
|
259
|
+
"api-error": "[sagedesk] Support service error. Showing relevant knowledge instead.",
|
|
260
|
+
"malformed-response": "[sagedesk] Support service returned invalid response. Showing relevant knowledge instead."
|
|
261
|
+
};
|
|
262
|
+
console.warn(messages[reason] || "[sagedesk] Support service unavailable. Showing relevant knowledge instead.");
|
|
263
|
+
}
|
|
229
264
|
var initialState = {
|
|
230
265
|
messages: [],
|
|
231
266
|
isOpen: false,
|
|
@@ -278,6 +313,11 @@ function useSageDesk(config) {
|
|
|
278
313
|
const startEngine = useCallback(async () => {
|
|
279
314
|
if (engineStartedRef.current) return;
|
|
280
315
|
engineStartedRef.current = true;
|
|
316
|
+
if (config.mode === "llm") {
|
|
317
|
+
setChips(config.agent.suggestedChips ?? []);
|
|
318
|
+
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "ready" } });
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
281
321
|
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "loading-index" } });
|
|
282
322
|
try {
|
|
283
323
|
indexRef.current = await fetchIndex(config.indexUrl);
|
|
@@ -304,7 +344,7 @@ function useSageDesk(config) {
|
|
|
304
344
|
embedderRef.current = new EmbedderRuntime();
|
|
305
345
|
dispatch({ type: "SET_ENGINE_STATUS", payload: { status: "degraded" } });
|
|
306
346
|
}
|
|
307
|
-
}, [config.indexUrl, config.agent.suggestedChips, addMessage]);
|
|
347
|
+
}, [config.mode, config.indexUrl, config.agent.suggestedChips, addMessage]);
|
|
308
348
|
const greetingShownRef = useRef(false);
|
|
309
349
|
const open = useCallback(() => {
|
|
310
350
|
dispatch({ type: "OPEN" });
|
|
@@ -347,8 +387,37 @@ function useSageDesk(config) {
|
|
|
347
387
|
}
|
|
348
388
|
let botText;
|
|
349
389
|
let isFallback = false;
|
|
350
|
-
let
|
|
351
|
-
|
|
390
|
+
let fallbackReason;
|
|
391
|
+
let retrievalMode = "keyword";
|
|
392
|
+
if (config.mode === "llm") {
|
|
393
|
+
if (!config.endpoint) {
|
|
394
|
+
console.warn('[sagedesk] LLM mode requires an "endpoint" prop.');
|
|
395
|
+
botText = getFallback(config.agent);
|
|
396
|
+
isFallback = true;
|
|
397
|
+
} else {
|
|
398
|
+
try {
|
|
399
|
+
const res = await fetch(config.endpoint, {
|
|
400
|
+
method: "POST",
|
|
401
|
+
headers: { "Content-Type": "application/json" },
|
|
402
|
+
body: JSON.stringify({ query: trimmed })
|
|
403
|
+
});
|
|
404
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
405
|
+
const data = await res.json();
|
|
406
|
+
if (data.isFallback || !data.answer) {
|
|
407
|
+
fallbackReason = data.fallbackReason;
|
|
408
|
+
logFallbackWarning(fallbackReason);
|
|
409
|
+
botText = getFallback(config.agent);
|
|
410
|
+
isFallback = true;
|
|
411
|
+
} else {
|
|
412
|
+
botText = data.answer;
|
|
413
|
+
}
|
|
414
|
+
} catch (err) {
|
|
415
|
+
console.warn("[sagedesk] Support service unavailable. Using cached knowledge instead.");
|
|
416
|
+
botText = getFallback(config.agent);
|
|
417
|
+
isFallback = true;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
} else if (!indexRef.current) {
|
|
352
421
|
botText = getFallback(config.agent);
|
|
353
422
|
isFallback = true;
|
|
354
423
|
} else {
|
|
@@ -359,7 +428,7 @@ function useSageDesk(config) {
|
|
|
359
428
|
embedderRef.current,
|
|
360
429
|
config.search
|
|
361
430
|
);
|
|
362
|
-
|
|
431
|
+
retrievalMode = res.mode;
|
|
363
432
|
if (res.results.length > 0) {
|
|
364
433
|
botText = buildAnswer(res.results);
|
|
365
434
|
} else {
|
|
@@ -372,11 +441,13 @@ function useSageDesk(config) {
|
|
|
372
441
|
isFallback = true;
|
|
373
442
|
}
|
|
374
443
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
444
|
+
if (config.mode !== "llm") {
|
|
445
|
+
const elapsed = Date.now() - typingStart;
|
|
446
|
+
const delayBase = retrievalMode === "keyword" || isFallback ? 800 : 3e3;
|
|
447
|
+
const minTypingMs = delayBase + Math.random() * 2e3;
|
|
448
|
+
const remaining = minTypingMs - elapsed;
|
|
449
|
+
if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));
|
|
450
|
+
}
|
|
380
451
|
dispatch({ type: "SET_TYPING", payload: false });
|
|
381
452
|
addMessage({ role: "bot", text: botText, isFallback });
|
|
382
453
|
},
|
|
@@ -399,6 +470,51 @@ function useSageDesk(config) {
|
|
|
399
470
|
return { state, chips: activeChips, open, close, submit };
|
|
400
471
|
}
|
|
401
472
|
|
|
473
|
+
// src/react/markdownUtils.ts
|
|
474
|
+
import { marked } from "marked";
|
|
475
|
+
import DOMPurify from "dompurify";
|
|
476
|
+
marked.setOptions({
|
|
477
|
+
breaks: true,
|
|
478
|
+
gfm: true,
|
|
479
|
+
pedantic: false
|
|
480
|
+
});
|
|
481
|
+
var PURIFY_CONFIG = {
|
|
482
|
+
ALLOWED_TAGS: [
|
|
483
|
+
"p",
|
|
484
|
+
"br",
|
|
485
|
+
"strong",
|
|
486
|
+
"em",
|
|
487
|
+
"u",
|
|
488
|
+
"h1",
|
|
489
|
+
"h2",
|
|
490
|
+
"h3",
|
|
491
|
+
"h4",
|
|
492
|
+
"h5",
|
|
493
|
+
"h6",
|
|
494
|
+
"ul",
|
|
495
|
+
"ol",
|
|
496
|
+
"li",
|
|
497
|
+
"blockquote",
|
|
498
|
+
"code",
|
|
499
|
+
"pre",
|
|
500
|
+
"a",
|
|
501
|
+
"hr"
|
|
502
|
+
],
|
|
503
|
+
ALLOWED_ATTR: ["href", "title", "target", "rel"],
|
|
504
|
+
ALLOW_DATA_ATTR: false
|
|
505
|
+
};
|
|
506
|
+
DOMPurify.addHook("afterSanitizeAttributes", (node) => {
|
|
507
|
+
if (node.tagName.toLowerCase() === "a") {
|
|
508
|
+
node.setAttribute("target", "_blank");
|
|
509
|
+
node.setAttribute("rel", "noopener noreferrer");
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
function parseMarkdown(markdown) {
|
|
513
|
+
const html = marked.parse(markdown);
|
|
514
|
+
const sanitized = DOMPurify.sanitize(html, PURIFY_CONFIG);
|
|
515
|
+
return sanitized;
|
|
516
|
+
}
|
|
517
|
+
|
|
402
518
|
// src/react/SageDeskWidget.tsx
|
|
403
519
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
404
520
|
var STYLE_ID = "sagedesk-widget-styles";
|
|
@@ -428,6 +544,28 @@ var SHARED = `
|
|
|
428
544
|
}
|
|
429
545
|
.sd-r-scrollable::-webkit-scrollbar { display: none !important; }
|
|
430
546
|
.sd-r-scrollable > * { flex-shrink: 0 !important; }
|
|
547
|
+
.sd-r-markdown h1, .sd-r-markdown h2, .sd-r-markdown h3, .sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 {
|
|
548
|
+
margin: 12px 0 8px 0 !important; font-weight: 600 !important; line-height: 1.3 !important;
|
|
549
|
+
}
|
|
550
|
+
.sd-r-markdown h1 { font-size: 1.3em !important; }
|
|
551
|
+
.sd-r-markdown h2 { font-size: 1.2em !important; }
|
|
552
|
+
.sd-r-markdown h3 { font-size: 1.1em !important; }
|
|
553
|
+
.sd-r-markdown h4, .sd-r-markdown h5, .sd-r-markdown h6 { font-size: 1em !important; }
|
|
554
|
+
.sd-r-markdown strong { font-weight: 600 !important; }
|
|
555
|
+
.sd-r-markdown em { font-style: italic !important; }
|
|
556
|
+
.sd-r-markdown u { text-decoration: underline !important; }
|
|
557
|
+
.sd-r-markdown ul, .sd-r-markdown ol { margin: 8px 0 !important; padding-left: 20px !important; }
|
|
558
|
+
.sd-r-markdown li { margin: 4px 0 !important; }
|
|
559
|
+
.sd-r-markdown blockquote { margin: 8px 0 !important; padding-left: 12px !important; border-left: 3px solid currentColor !important; opacity: 0.8 !important; }
|
|
560
|
+
.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; }
|
|
561
|
+
.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; }
|
|
562
|
+
.sd-r-markdown pre code { background: none !important; padding: 0 !important; }
|
|
563
|
+
.sd-r-markdown hr { border: none !important; border-top: 1px solid currentColor !important; opacity: 0.3 !important; margin: 10px 0 !important; }
|
|
564
|
+
.sd-r-markdown a { text-decoration: underline !important; opacity: 0.9 !important; }
|
|
565
|
+
.sd-r-markdown a:hover { opacity: 1 !important; }
|
|
566
|
+
.sd-r-markdown p { margin: 6px 0 !important; }
|
|
567
|
+
.sd-r-markdown > *:first-child { margin-top: 0 !important; }
|
|
568
|
+
.sd-r-markdown > *:last-child { margin-bottom: 0 !important; }
|
|
431
569
|
@media (max-width: 420px) {
|
|
432
570
|
.sd-r-panel {
|
|
433
571
|
bottom: 0 !important; right: 0 !important; left: 0 !important;
|
|
@@ -514,7 +652,7 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ jsxs("div", { style: {
|
|
|
514
652
|
{
|
|
515
653
|
href: "https://github.com/mzeeshanwahid/sagedesk",
|
|
516
654
|
target: "_blank",
|
|
517
|
-
rel: "noopener
|
|
655
|
+
rel: "noopener",
|
|
518
656
|
style: {
|
|
519
657
|
color: dark ? "rgba(255,255,255,0.7)" : "#5a5a64",
|
|
520
658
|
fontWeight: 500,
|
|
@@ -526,6 +664,7 @@ var PoweredBy = ({ dark = false }) => /* @__PURE__ */ jsxs("div", { style: {
|
|
|
526
664
|
] });
|
|
527
665
|
function ClassicMessageBubble({ msg, accent }) {
|
|
528
666
|
const isBot = msg.role === "bot";
|
|
667
|
+
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
529
668
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: isBot ? "flex-start" : "flex-end", gap: "4px" }, children: [
|
|
530
669
|
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" }),
|
|
531
670
|
/* @__PURE__ */ jsx("div", { style: {
|
|
@@ -538,10 +677,9 @@ function ClassicMessageBubble({ msg, accent }) {
|
|
|
538
677
|
color: isBot ? "#1a1a2e" : "#fff",
|
|
539
678
|
border: isBot ? "1px solid rgba(20,20,40,0.06)" : "none",
|
|
540
679
|
boxShadow: isBot ? "0 1px 2px rgba(20,20,40,0.04)" : `0 6px 16px -6px color-mix(in oklab, ${accent} 60%, transparent)`,
|
|
541
|
-
whiteSpace: "pre-wrap",
|
|
542
680
|
wordBreak: "break-word",
|
|
543
681
|
fontFamily: "inherit"
|
|
544
|
-
}, children: msg.text }),
|
|
682
|
+
}, className: "sd-r-markdown", children: isBot ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text }) }),
|
|
545
683
|
/* @__PURE__ */ jsx("span", { style: {
|
|
546
684
|
fontSize: "11px",
|
|
547
685
|
color: "#a8a8b0",
|
|
@@ -571,6 +709,7 @@ function ClassicTypingIndicator() {
|
|
|
571
709
|
}
|
|
572
710
|
function LightMessageBubble({ msg, accent, agentName }) {
|
|
573
711
|
const isBot = msg.role === "bot";
|
|
712
|
+
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
574
713
|
if (isBot) {
|
|
575
714
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px" }, children: [
|
|
576
715
|
/* @__PURE__ */ jsx("div", { style: {
|
|
@@ -587,7 +726,7 @@ function LightMessageBubble({ msg, accent, agentName }) {
|
|
|
587
726
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: "#a8a89e", fontVariantNumeric: "tabular-nums", fontFamily: "inherit" }, children: "just now" })
|
|
588
727
|
] }),
|
|
589
728
|
msg.isFallback && /* @__PURE__ */ jsx("p", { style: { fontSize: "11px", color: "#9b9aa3", margin: "0 0 4px", fontFamily: "inherit" }, children: "Not sure about that one" }),
|
|
590
|
-
/* @__PURE__ */ jsx("div", { style: { fontSize: "14px", lineHeight: 1.55, color: "#2a2a36", fontFamily: "inherit",
|
|
729
|
+
/* @__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 } }) })
|
|
591
730
|
] })
|
|
592
731
|
] });
|
|
593
732
|
}
|
|
@@ -635,6 +774,7 @@ function LightTypingIndicator({ accent }) {
|
|
|
635
774
|
}
|
|
636
775
|
function DarkMessageBubble({ msg, accent }) {
|
|
637
776
|
const isBot = msg.role === "bot";
|
|
777
|
+
const renderedHtml = isBot ? parseMarkdown(msg.text) : msg.text;
|
|
638
778
|
return /* @__PURE__ */ jsxs("div", { style: {
|
|
639
779
|
maxWidth: "85%",
|
|
640
780
|
padding: "12px 14px",
|
|
@@ -646,12 +786,11 @@ function DarkMessageBubble({ msg, accent }) {
|
|
|
646
786
|
color: isBot ? "rgba(255,255,255,0.92)" : "#fff",
|
|
647
787
|
alignSelf: isBot ? "flex-start" : "flex-end",
|
|
648
788
|
boxShadow: isBot ? "none" : `0 8px 20px -8px color-mix(in oklab, ${accent} 70%, transparent)`,
|
|
649
|
-
whiteSpace: "pre-wrap",
|
|
650
789
|
wordBreak: "break-word",
|
|
651
790
|
fontFamily: "inherit"
|
|
652
|
-
}, children: [
|
|
791
|
+
}, className: isBot ? "sd-r-markdown" : "", children: [
|
|
653
792
|
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" }),
|
|
654
|
-
msg.text
|
|
793
|
+
isBot ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderedHtml } }) : /* @__PURE__ */ jsx("div", { style: { whiteSpace: "pre-wrap" }, children: msg.text })
|
|
655
794
|
] });
|
|
656
795
|
}
|
|
657
796
|
function DarkTypingIndicator() {
|
|
@@ -1271,16 +1410,22 @@ function renderDark(p) {
|
|
|
1271
1410
|
] })
|
|
1272
1411
|
] });
|
|
1273
1412
|
}
|
|
1274
|
-
function SageDeskWidget({ indexUrl, agent, search: search2 }) {
|
|
1275
|
-
|
|
1413
|
+
function SageDeskWidget({ mode, indexUrl, endpoint, agent, search: search2 }) {
|
|
1414
|
+
const resolvedMode = mode ?? "local";
|
|
1415
|
+
if (!agent?.name) {
|
|
1416
|
+
throw new Error('[sagedesk] Required prop "agent.name" is missing.');
|
|
1417
|
+
}
|
|
1418
|
+
if (resolvedMode === "local" && !indexUrl) {
|
|
1276
1419
|
throw new Error(
|
|
1277
|
-
'[sagedesk] Required prop "indexUrl" is missing. Run `npx sagedesk build` and pass the output path, e.g. indexUrl="/support-index.json".'
|
|
1420
|
+
'[sagedesk] Required prop "indexUrl" is missing for local mode. Run `npx sagedesk build` and pass the output path, e.g. indexUrl="/support-index.json".'
|
|
1278
1421
|
);
|
|
1279
1422
|
}
|
|
1280
|
-
if (!
|
|
1281
|
-
throw new Error(
|
|
1423
|
+
if (resolvedMode === "llm" && !endpoint) {
|
|
1424
|
+
throw new Error(
|
|
1425
|
+
'[sagedesk] Required prop "endpoint" is missing for llm mode. Provide your backend route, e.g. endpoint="/api/sagedesk".'
|
|
1426
|
+
);
|
|
1282
1427
|
}
|
|
1283
|
-
const config = { indexUrl, agent, search: search2 };
|
|
1428
|
+
const config = { mode: resolvedMode, indexUrl, endpoint, agent, search: search2 };
|
|
1284
1429
|
const { state, chips, open, close, submit } = useSageDesk(config);
|
|
1285
1430
|
const theme = agent.theme ?? "classic";
|
|
1286
1431
|
const accent = agent.accentColor ?? "#534AB7";
|
|
@@ -1293,7 +1438,7 @@ function SageDeskWidget({ indexUrl, agent, search: search2 }) {
|
|
|
1293
1438
|
const triggerRef = useRef2(null);
|
|
1294
1439
|
const [mounted, setMounted] = useState2(false);
|
|
1295
1440
|
useEffect2(() => {
|
|
1296
|
-
if (!indexUrl.startsWith("/") && !indexUrl.startsWith("http")) {
|
|
1441
|
+
if (resolvedMode === "local" && indexUrl && !indexUrl.startsWith("/") && !indexUrl.startsWith("http")) {
|
|
1297
1442
|
console.warn(
|
|
1298
1443
|
`[sagedesk] indexUrl "${indexUrl}" looks like a relative path. It should start with "/" so it resolves correctly from any page.`
|
|
1299
1444
|
);
|
|
@@ -1335,7 +1480,7 @@ function SageDeskWidget({ indexUrl, agent, search: search2 }) {
|
|
|
1335
1480
|
[handleSubmit]
|
|
1336
1481
|
);
|
|
1337
1482
|
if (!mounted || typeof document === "undefined") return null;
|
|
1338
|
-
const showPoweredBy =
|
|
1483
|
+
const showPoweredBy = true;
|
|
1339
1484
|
const showChips = chips.length > 0;
|
|
1340
1485
|
const panelClass = isClosing ? "sd-r-closing" : state.isOpen ? "sd-r-opening" : "";
|
|
1341
1486
|
const props = {
|