sagedesk 1.0.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/LICENSE +21 -0
- package/README.md +199 -0
- package/dist/cli/index.cjs +9153 -0
- package/dist/next/SageDeskWidget-P3H2VJR5.js +1370 -0
- package/dist/next/SageDeskWidget-P3H2VJR5.js.map +1 -0
- package/dist/next/index.cjs +1478 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +37 -0
- package/dist/next/index.d.ts +37 -0
- package/dist/next/index.js +33 -0
- package/dist/next/index.js.map +1 -0
- package/dist/react/index.cjs +1402 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +61 -0
- package/dist/react/index.d.ts +61 -0
- package/dist/react/index.js +1369 -0
- package/dist/react/index.js.map +1 -0
- package/dist/vanilla/index.cjs +1454 -0
- package/dist/vanilla/index.cjs.map +1 -0
- package/dist/vanilla/index.d.cts +61 -0
- package/dist/vanilla/index.d.ts +61 -0
- package/dist/vanilla/index.js +1416 -0
- package/dist/vanilla/index.js.map +1 -0
- package/knowledge.example.json +85 -0
- package/package.json +130 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/vanilla/ui.ts","../../src/core/embedder.ts","../../src/core/search.ts","../../src/core/retriever.ts","../../src/core/renderer.ts","../../src/core/fallback.ts","../../src/vanilla/widget.ts","../../src/vanilla/index.ts"],"sourcesContent":["import type { Theme } from '../core/types.js';\n\n// ─── Theme dispatch ───────────────────────────────────────────────────────────\n\nexport function getWidgetStyles(accent: string, theme: Theme = 'classic'): string {\n if (theme === 'light') return getLightStyles(accent);\n if (theme === 'dark') return getDarkStyles(accent);\n return getClassicStyles(accent);\n}\n\n// ─── Classic ──────────────────────────────────────────────────────────────────\n// Gradient header, distinguished bubbles with subtle shadow, quick-reply pills.\n\nexport function getClassicStyles(accent: string): string {\n return `\n:host {\n display: block;\n position: fixed;\n inset: 0;\n z-index: 9999;\n pointer-events: none;\n overflow: visible;\n font-family: inherit;\n --sd-accent: ${accent};\n}\n\n*, *::before, *::after { box-sizing: border-box; font-family: inherit; }\n\n/* ─── Trigger ───────────────────────────────────────────────── */\n\n.sd-trigger {\n position: fixed;\n bottom: 28px;\n right: 24px;\n width: 56px;\n height: 56px;\n border-radius: 50%;\n background: linear-gradient(135deg, ${accent} 0%, color-mix(in oklab, ${accent} 78%, #1a1340) 100%);\n border: none;\n cursor: pointer;\n display: grid;\n place-items: center;\n pointer-events: all;\n transition: transform 150ms ease;\n z-index: 9999;\n outline: none;\n padding: 0;\n color: #fff;\n box-shadow: 0 14px 28px -6px color-mix(in oklab, ${accent} 55%, transparent), 0 4px 10px rgba(40,30,90,0.15);\n}\n.sd-trigger:hover { transform: scale(1.06); }\n.sd-trigger:focus-visible { outline: 2px solid ${accent}; outline-offset: 3px; }\n.sd-trigger.pos-left { right: auto; left: 24px; }\n\n/* ─── Panel ─────────────────────────────────────────────────── */\n\n.sd-panel {\n position: fixed;\n bottom: 96px;\n right: 24px;\n width: 380px;\n height: 580px;\n max-height: 580px;\n border-radius: 20px;\n border: 1px solid rgba(20,20,40,0.04);\n background: #fff;\n box-shadow: 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 overflow: hidden;\n display: flex;\n flex-direction: column;\n pointer-events: all;\n z-index: 9999;\n transform-origin: bottom right;\n}\n.sd-panel.pos-left { right: auto; left: 24px; transform-origin: bottom left; }\n.sd-panel[data-open=\"true\"] { animation: sd-panel-open 200ms cubic-bezier(0.34,1.56,0.64,1) both; }\n.sd-panel[data-closing=\"true\"] { animation: sd-panel-close 150ms ease-in both; }\n.sd-panel[data-open=\"false\"]:not([data-closing=\"true\"]) { display: none; }\n\n@keyframes sd-panel-open { from { transform: scale(0.94) translateY(6px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } }\n@keyframes sd-panel-close { from { transform: scale(1) translateY(0); opacity: 1; } to { transform: scale(0.94) translateY(6px); opacity: 0; } }\n\n/* ─── Header ────────────────────────────────────────────────── */\n\n.sd-header {\n padding: 18px 20px;\n background: linear-gradient(135deg, ${accent} 0%, color-mix(in oklab, ${accent} 78%, #1a1340) 100%);\n color: #fff;\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n position: relative;\n}\n\n.sd-avatar-wrap { position: relative; width: 40px; height: 40px; flex-shrink: 0; }\n\n.sd-avatar {\n width: 40px;\n height: 40px;\n border-radius: 50%;\n background: rgba(255,255,255,0.18);\n display: grid;\n place-items: center;\n overflow: hidden;\n}\n.sd-avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }\n\n.sd-online-dot {\n position: absolute;\n right: -1px;\n bottom: -1px;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: #22c55e;\n box-shadow: 0 0 0 2.5px ${accent};\n}\n\n.sd-agent-info { flex: 1; min-width: 0; }\n.sd-agent-name { font-size: 15px; font-weight: 600; color: #fff; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; padding: 0; letter-spacing: -0.01em; }\n.sd-status { font-size: 12px; color: rgba(255,255,255,0.85); display: flex; align-items: center; gap: 6px; margin-top: 3px; }\n.sd-status-dot { width: 6px; height: 6px; border-radius: 50%; background: #4ade80; flex-shrink: 0; box-shadow: 0 0 0 2px rgba(74,222,128,0.25); }\n\n.sd-close {\n width: 30px; height: 30px;\n background: rgba(255,255,255,0.14);\n border: none; color: #fff; border-radius: 8px;\n cursor: pointer; display: grid; place-items: center;\n flex-shrink: 0; outline: none; padding: 0;\n transition: background 120ms;\n}\n.sd-close:hover { background: rgba(255,255,255,0.22); }\n.sd-close:focus-visible { outline: 2px solid rgba(255,255,255,0.6); }\n\n/* ─── Thread ────────────────────────────────────────────────── */\n\n.sd-thread {\n flex: 1; overflow-y: auto; overflow-x: hidden;\n padding: 22px 18px 18px;\n background: #fbfbfa;\n min-height: 280px;\n display: flex; flex-direction: column; gap: 18px;\n scrollbar-width: thin; scrollbar-color: rgba(20,20,40,0.12) transparent;\n overscroll-behavior: contain;\n}\n.sd-thread::-webkit-scrollbar { width: 4px; }\n.sd-thread::-webkit-scrollbar-track { background: transparent; }\n.sd-thread::-webkit-scrollbar-thumb { background: rgba(20,20,40,0.12); border-radius: 2px; }\n\n/* ─── Messages ──────────────────────────────────────────────── */\n\n.sd-msg-wrap { display: flex; flex-direction: column; }\n.sd-msg-wrap.bot { align-items: flex-start; }\n.sd-msg-wrap.user { align-items: flex-end; }\n\n.sd-fallback-label { font-size: 11px; font-weight: 500; color: #9b9aa3; margin: 0 0 4px; padding: 0 4px; }\n\n.sd-bubble {\n max-width: 82%; padding: 10px 14px;\n font-size: 14px; line-height: 1.5;\n word-break: break-word; white-space: pre-wrap;\n}\n.sd-bubble.bot {\n background: #fff;\n border: 1px solid rgba(20,20,40,0.06);\n border-radius: 16px 16px 16px 6px;\n color: #1a1a2e;\n box-shadow: 0 1px 2px rgba(20,20,40,0.04);\n}\n.sd-bubble.user {\n background: ${accent};\n color: #fff;\n border-radius: 16px 16px 6px 16px;\n box-shadow: 0 6px 16px -6px color-mix(in oklab, ${accent} 60%, transparent);\n}\n\n.sd-timestamp { font-size: 11px; color: #a8a8b0; padding: 0 4px; margin-top: 4px; font-variant-numeric: tabular-nums; }\n.sd-msg-wrap.user .sd-timestamp { padding: 0 4px 0 0; }\n.sd-msg-wrap.bot .sd-timestamp { padding: 0 0 0 4px; }\n\n/* ─── Typing indicator ──────────────────────────────────────── */\n\n.sd-typing {\n display: flex; align-items: center; gap: 4px;\n padding: 10px 14px;\n background: #fff; border: 1px solid rgba(20,20,40,0.06);\n border-radius: 16px 16px 16px 6px;\n box-shadow: 0 1px 2px rgba(20,20,40,0.04);\n align-self: flex-start;\n}\n.sd-dot { width: 6px; height: 6px; border-radius: 50%; background: #c8c8ce; animation: sd-bounce 1.2s ease-in-out infinite; }\n.sd-dot:nth-child(2) { animation-delay: 0.2s; }\n.sd-dot:nth-child(3) { animation-delay: 0.4s; }\n@keyframes sd-bounce { 0%,60%,100%{transform:translateY(0)} 30%{transform:translateY(-5px)} }\n\n/* ─── Chips ─────────────────────────────────────────────────── */\n\n.sd-chips { padding: 0 18px 16px; background: #fbfbfa; flex-shrink: 0; }\n.sd-chips.scrollable { padding: 0 0 16px; }\n.sd-chips-label { font-size: 11px; color: #9b9aa3; letter-spacing: 0.06em; text-transform: uppercase; font-weight: 500; padding-left: 4px; margin-bottom: 6px; }\n.sd-chips.scrollable .sd-chips-label { padding-left: 18px; }\n.sd-chips-row { display: flex; flex-wrap: wrap; gap: 6px; }\n.sd-chips.scrollable .sd-chips-row {\n flex-wrap: nowrap;\n overflow-x: auto;\n padding: 0 18px;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.sd-chips.scrollable .sd-chips-row::-webkit-scrollbar { display: none; }\n.sd-chips.scrollable .sd-chip { flex-shrink: 0; }\n\n.sd-chip {\n font-size: 12.5px; padding: 7px 12px; border-radius: 999px;\n background: #fff; border: 1px solid color-mix(in oklab, ${accent} 24%, transparent);\n color: ${accent}; cursor: pointer; font-weight: 500; letter-spacing: -0.005em;\n outline: none; transition: opacity 100ms;\n}\n.sd-chip:hover { opacity: 0.8; }\n.sd-chip:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Composer ──────────────────────────────────────────────── */\n\n.sd-composer { padding: 14px 14px 16px; border-top: 1px solid rgba(20,20,40,0.06); background: #fff; flex-shrink: 0; }\n\n.sd-input-wrap {\n display: flex; align-items: center; gap: 6px;\n background: #f5f4f0; border-radius: 12px; padding: 5px 6px 5px 14px;\n border: 1px solid transparent; transition: border-color .15s, box-shadow .15s;\n}\n.sd-input-wrap:focus-within {\n border-color: color-mix(in oklab, ${accent} 30%, transparent);\n box-shadow: 0 0 0 4px color-mix(in oklab, ${accent} 12%, transparent);\n}\n\n.sd-input { flex: 1; background: transparent; border: none; outline: none; font-size: 14px; padding: 8px 0; color: #1a1a2e; min-width: 0; }\n.sd-input::placeholder { color: #a8a8b0; }\n\n.sd-send {\n width: 32px; height: 32px; border-radius: 8px;\n background: color-mix(in oklab, ${accent} 22%, #e5e3dc);\n border: none; color: #fff; cursor: pointer; display: grid; place-items: center;\n flex-shrink: 0; padding: 0; outline: none; transition: background .15s, opacity .1s, transform .1s;\n}\n.sd-send.active { background: ${accent}; }\n.sd-send:hover { opacity: 0.85; }\n.sd-send:active { transform: scale(0.95); }\n.sd-send:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Footer ─────────────────────────────────────────────────── */\n\n.sd-footer {\n height: 34px; background: #fff; border-top: 1px solid rgba(20,20,40,0.04);\n display: flex; align-items: center; justify-content: center;\n font-size: 11px; color: #a8a8b0; gap: 5px; flex-shrink: 0;\n}\n.sd-footer[hidden] { display: none; }\n.sd-footer-link { color: #5a5a64; font-weight: 500; text-decoration: none; }\n.sd-footer-link:hover { text-decoration: underline; }\n\n/* ─── Mobile ─────────────────────────────────────────────────── */\n\n@media (max-width: 420px) {\n .sd-panel {\n bottom: 0; right: 0; left: 0; width: auto; max-width: 100%;\n height: auto; max-height: 85vh;\n border-radius: 20px 20px 0 0;\n transform-origin: bottom center;\n }\n .sd-panel.pos-left { right: 0; transform-origin: bottom center; }\n}\n`;\n}\n\n// ─── Light (Editorial) ────────────────────────────────────────────────────────\n// Warm white, content-led. Avatar with gradient, text-style bot messages,\n// accent-tinted user bubbles.\n\nexport function getLightStyles(accent: string): string {\n return `\n:host {\n display: block; position: fixed; inset: 0;\n z-index: 9999; pointer-events: none; overflow: visible; font-family: inherit;\n --sd-accent: ${accent};\n}\n*, *::before, *::after { box-sizing: border-box; font-family: inherit; }\n\n/* ─── Trigger (pill) ────────────────────────────────────────── */\n\n.sd-trigger {\n position: fixed; bottom: 28px; right: 24px;\n height: 52px; padding: 0 8px 0 20px; border-radius: 999px;\n background: #fdfcf9; border: 1px solid rgba(20,20,40,0.08);\n cursor: pointer; display: flex; align-items: center; gap: 14px;\n pointer-events: all; z-index: 9999; font-family: inherit;\n box-shadow: 0 12px 26px -8px rgba(40,30,90,0.18), 0 2px 8px rgba(40,30,90,0.06);\n transition: box-shadow 150ms ease; outline: none;\n}\n.sd-trigger:hover { box-shadow: 0 16px 32px -10px rgba(40,30,90,0.24), 0 2px 10px rgba(40,30,90,0.08); }\n.sd-trigger:focus-visible { outline: 2px solid ${accent}; outline-offset: 3px; }\n.sd-trigger.pos-left { right: auto; left: 24px; }\n\n.sd-trigger-label { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.2; }\n.sd-trigger-label-main { font-size: 14px; font-weight: 600; color: #1a1a2e; }\n.sd-trigger-label-sub { font-size: 11px; color: #9b9aa3; margin-top: 2px; }\n\n.sd-trigger-circle {\n width: 36px; height: 36px; border-radius: 50%;\n background: ${accent}; color: #fff;\n display: grid; place-items: center; flex-shrink: 0;\n}\n\n/* ─── Panel ─────────────────────────────────────────────────── */\n\n.sd-panel {\n position: fixed; bottom: 92px; right: 24px;\n width: 400px; height: 580px; max-height: 580px; border-radius: 22px;\n border: 1px solid rgba(20,20,40,0.05);\n background: #fdfcf9;\n box-shadow: 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 overflow: hidden; display: flex; flex-direction: column;\n pointer-events: all; z-index: 9999; transform-origin: bottom right;\n}\n.sd-panel.pos-left { right: auto; left: 24px; transform-origin: bottom left; }\n.sd-panel[data-open=\"true\"] { animation: sd-panel-open 200ms cubic-bezier(0.34,1.56,0.64,1) both; }\n.sd-panel[data-closing=\"true\"] { animation: sd-panel-close 150ms ease-in both; }\n.sd-panel[data-open=\"false\"]:not([data-closing=\"true\"]) { display: none; }\n@keyframes sd-panel-open { from { transform: scale(0.94) translateY(6px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } }\n@keyframes sd-panel-close { from { transform: scale(1) translateY(0); opacity: 1; } to { transform: scale(0.94) translateY(6px); opacity: 0; } }\n\n/* ─── Header ────────────────────────────────────────────────── */\n\n.sd-header {\n padding: 18px 20px 14px; background: #fdfcf9;\n border-bottom: 1px solid rgba(20,20,40,0.05);\n display: flex; align-items: center; gap: 12px; flex-shrink: 0;\n}\n.sd-avatar-wrap { width: 36px; height: 36px; flex-shrink: 0; }\n.sd-avatar {\n width: 36px; height: 36px; border-radius: 50%; overflow: hidden;\n background: linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 55%, #fff));\n display: grid; place-items: center; color: #fff;\n}\n.sd-avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }\n.sd-agent-info { flex: 1; min-width: 0; }\n.sd-agent-name { font-size: 14.5px; font-weight: 600; color: #1a1a2e; letter-spacing: -0.01em; margin: 0; padding: 0; }\n.sd-status { font-size: 12px; color: #7a7a82; display: flex; align-items: center; gap: 6px; margin-top: 2px; }\n.sd-status-dot { width: 6px; height: 6px; border-radius: 50%; background: #22c55e; flex-shrink: 0; }\n.sd-close {\n width: 30px; height: 30px; background: transparent; border: none;\n color: #9b9aa3; border-radius: 8px; cursor: pointer;\n display: grid; place-items: center; padding: 0; outline: none;\n}\n.sd-close:hover { color: #5a5a64; }\n.sd-close:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Thread ────────────────────────────────────────────────── */\n\n.sd-thread {\n flex: 1; overflow-y: auto; overflow-x: hidden;\n padding: 22px 20px 16px; min-height: 280px;\n display: flex; flex-direction: column; gap: 22px;\n scrollbar-width: thin; scrollbar-color: rgba(20,20,40,0.1) transparent;\n overscroll-behavior: contain;\n}\n.sd-thread::-webkit-scrollbar { width: 4px; }\n.sd-thread::-webkit-scrollbar-thumb { background: rgba(20,20,40,0.1); border-radius: 2px; }\n\n/* ─── Messages ──────────────────────────────────────────────── */\n\n.sd-msg-wrap { display: flex; flex-direction: column; }\n.sd-msg-wrap.bot { align-items: flex-start; flex-direction: row; gap: 10px; }\n.sd-msg-wrap.user { align-items: flex-end; }\n\n/* Bot: avatar + text column */\n.sd-bot-avatar {\n width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0; margin-top: 2px;\n background: linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 60%, #fff));\n}\n.sd-bot-body { flex: 1; min-width: 0; }\n.sd-bot-meta { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }\n.sd-bot-name { font-size: 13px; font-weight: 600; color: #1a1a2e; }\n.sd-timestamp { font-size: 11px; color: #a8a89e; font-variant-numeric: tabular-nums; }\n\n.sd-fallback-label { font-size: 11px; color: #9b9aa3; margin: 0 0 4px; }\n\n.sd-bubble { word-break: break-word; white-space: pre-wrap; }\n.sd-bubble.bot { font-size: 14px; line-height: 1.55; color: #2a2a36; }\n.sd-bubble.user {\n max-width: 78%; padding: 11px 15px; font-size: 14px; line-height: 1.5;\n border-radius: 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 font-weight: 500;\n}\n\n/* ─── Typing indicator ──────────────────────────────────────── */\n\n.sd-typing {\n display: flex; align-items: flex-start; gap: 10px;\n}\n.sd-typing-avatar {\n width: 28px; height: 28px; border-radius: 50%; flex-shrink: 0; margin-top: 2px;\n background: linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 60%, #fff));\n}\n.sd-typing-dots {\n display: inline-flex; align-items: center; gap: 4px;\n padding: 10px 14px; border-radius: 16px 16px 16px 4px;\n background: #fff; border: 1px solid rgba(20,20,40,0.07);\n box-shadow: 0 1px 2px rgba(20,20,40,0.04);\n}\n.sd-dot { width: 6px; height: 6px; border-radius: 50%; background: #c4c4be; animation: sd-bounce 1.2s ease-in-out infinite; }\n.sd-dot:nth-child(2) { animation-delay: 0.2s; }\n.sd-dot:nth-child(3) { animation-delay: 0.4s; }\n@keyframes sd-bounce { 0%,60%,100%{transform:translateY(0)} 30%{transform:translateY(-5px)} }\n\n/* ─── Chips ─────────────────────────────────────────────────── */\n\n.sd-chips { padding: 0 20px 14px; display: flex; flex-wrap: wrap; gap: 6px; flex-shrink: 0; }\n.sd-chips.scrollable {\n flex-wrap: nowrap;\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.sd-chips.scrollable::-webkit-scrollbar { display: none; }\n.sd-chips.scrollable .sd-chip { flex-shrink: 0; }\n\n.sd-chip {\n font-size: 12px; padding: 5px 10px; border-radius: 6px;\n background: #f4f3ee; border: none; color: #5a5a64;\n cursor: pointer; font-weight: 500; outline: none; transition: opacity 100ms;\n}\n.sd-chip:hover { opacity: 0.75; }\n.sd-chip:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Composer ──────────────────────────────────────────────── */\n\n.sd-composer { padding: 14px; border-top: 1px solid rgba(20,20,40,0.05); background: #fdfcf9; flex-shrink: 0; }\n.sd-input-wrap {\n background: #fff; border-radius: 14px; padding: 10px 12px;\n border: 1px solid rgba(20,20,40,0.1); transition: border-color .15s, box-shadow .15s;\n}\n.sd-input-wrap:focus-within {\n border-color: color-mix(in oklab, ${accent} 40%, transparent);\n box-shadow: 0 0 0 4px color-mix(in oklab, ${accent} 12%, transparent);\n}\n.sd-input { width: 100%; background: transparent; border: none; outline: none; font-size: 14px; color: #1a1a2e; display: block; }\n.sd-input::placeholder { color: #a8a89e; }\n.sd-input-actions { display: flex; align-items: center; margin-top: 8px; }\n.sd-send {\n width: 28px; height: 28px; border-radius: 7px; margin-left: auto;\n background: #e8e7e0; border: none; color: #a8a89e;\n cursor: pointer; display: grid; place-items: center; padding: 0; outline: none;\n transition: background .15s, color .15s, opacity .1s, transform .1s;\n}\n.sd-send.active { background: ${accent}; color: #fff; }\n.sd-send:hover { opacity: 0.85; }\n.sd-send:active { transform: scale(0.95); }\n.sd-send:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Footer ─────────────────────────────────────────────────── */\n\n.sd-footer {\n height: 34px; background: #fdfcf9; border-top: 1px solid rgba(20,20,40,0.04);\n display: flex; align-items: center; justify-content: center;\n font-size: 11px; color: #a8a89e; gap: 5px; flex-shrink: 0;\n}\n.sd-footer[hidden] { display: none; }\n.sd-footer-link { color: #5a5a64; font-weight: 500; text-decoration: none; }\n.sd-footer-link:hover { text-decoration: underline; }\n\n@media (max-width: 420px) {\n .sd-panel { bottom: 0; right: 0; left: 0; width: auto; max-width: 100%; height: auto; max-height: 85vh; border-radius: 22px 22px 0 0; transform-origin: bottom center; }\n .sd-panel.pos-left { right: 0; transform-origin: bottom center; }\n}\n`;\n}\n\n// ─── Dark (Glass) ─────────────────────────────────────────────────────────────\n// Frosted dark surface with ambient accent glow, gradient user bubbles,\n// sparkle icon header, \"Try asking\" prompts.\n\nexport function getDarkStyles(accent: string): string {\n return `\n:host {\n display: block; position: fixed; inset: 0;\n z-index: 9999; pointer-events: none; overflow: visible; font-family: inherit;\n --sd-accent: ${accent};\n}\n*, *::before, *::after { box-sizing: border-box; font-family: inherit; }\n\n/* ─── Launcher glow ─────────────────────────────────────────── */\n\n.sd-trigger-glow {\n position: fixed; bottom: 18px; right: 18px;\n width: 80px; height: 80px; border-radius: 50%;\n background: radial-gradient(circle, color-mix(in oklab, ${accent} 50%, transparent), transparent 70%);\n filter: blur(10px); pointer-events: none; z-index: 9998;\n}\n.sd-trigger-glow.pos-left { right: auto; left: 18px; }\n\n/* ─── Trigger ───────────────────────────────────────────────── */\n\n.sd-trigger {\n position: fixed; bottom: 28px; right: 24px;\n width: 60px; height: 60px; border-radius: 50%;\n background: linear-gradient(135deg, color-mix(in oklab, ${accent} 70%, #1a1340), #1a1a2e);\n border: 1px solid rgba(255,255,255,0.12); color: #fff;\n cursor: pointer; display: grid; place-items: center;\n pointer-events: all; z-index: 9999; padding: 0;\n transition: transform 150ms ease; outline: none;\n box-shadow: 0 16px 32px -8px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.1);\n}\n.sd-trigger:hover { transform: scale(1.04); }\n.sd-trigger:focus-visible { outline: 2px solid ${accent}; outline-offset: 3px; }\n.sd-trigger.pos-left { right: auto; left: 24px; }\n\n/* ─── Panel ─────────────────────────────────────────────────── */\n\n.sd-panel {\n position: fixed; bottom: 100px; right: 24px;\n width: 380px; height: 580px; max-height: 580px; border-radius: 22px;\n background: rgba(18, 16, 32, 0.86);\n backdrop-filter: blur(40px) saturate(180%);\n -webkit-backdrop-filter: blur(40px) saturate(180%);\n box-shadow: 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 overflow: hidden; display: flex; flex-direction: column;\n pointer-events: all; z-index: 9999; color: #fff;\n transform-origin: bottom right;\n}\n.sd-panel.pos-left { right: auto; left: 24px; transform-origin: bottom left; }\n.sd-panel[data-open=\"true\"] { animation: sd-panel-open 200ms cubic-bezier(0.34,1.56,0.64,1) both; }\n.sd-panel[data-closing=\"true\"] { animation: sd-panel-close 150ms ease-in both; }\n.sd-panel[data-open=\"false\"]:not([data-closing=\"true\"]) { display: none; }\n@keyframes sd-panel-open { from { transform: scale(0.94) translateY(6px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } }\n@keyframes sd-panel-close { from { transform: scale(1) translateY(0); opacity: 1; } to { transform: scale(0.94) translateY(6px); opacity: 0; } }\n\n/* ─── Panel top-glow overlay ────────────────────────────────── */\n\n.sd-panel-glow {\n position: absolute; top: 0; left: 0; right: 0; height: 200px; pointer-events: 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\n.sd-header {\n padding: 20px 20px 16px; display: flex; align-items: center; gap: 12px;\n position: relative; z-index: 1; flex-shrink: 0;\n}\n.sd-avatar-wrap { position: relative; flex-shrink: 0; }\n.sd-avatar {\n width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; overflow: hidden;\n background: linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 50%, #fff));\n box-shadow: 0 0 24px color-mix(in oklab, ${accent} 50%, transparent);\n}\n.sd-avatar-img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }\n.sd-online-dot {\n position: absolute; right: -2px; bottom: -2px;\n width: 12px; height: 12px; border-radius: 50%;\n background: #22c55e; box-shadow: 0 0 0 2.5px rgba(18,16,32,1);\n}\n.sd-agent-info { flex: 1; }\n.sd-agent-name { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; margin: 0; padding: 0; }\n.sd-status { font-size: 12px; color: rgba(255,255,255,0.55); display: flex; align-items: center; gap: 6px; margin-top: 3px; }\n.sd-status-dot { width: 5px; height: 5px; border-radius: 50%; background: #4ade80; box-shadow: 0 0 6px #4ade80; flex-shrink: 0; }\n.sd-close {\n width: 30px; height: 30px;\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); border-radius: 8px;\n cursor: pointer; display: grid; place-items: center; padding: 0; outline: none;\n}\n.sd-close:hover { background: rgba(255,255,255,0.1); color: #fff; }\n.sd-close:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Thread ────────────────────────────────────────────────── */\n\n.sd-thread {\n flex: 1; overflow-y: auto; overflow-x: hidden;\n padding: 8px 20px 16px; min-height: 280px;\n display: flex; flex-direction: column; gap: 14px;\n position: relative; z-index: 1;\n scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.1) transparent;\n overscroll-behavior: contain;\n}\n.sd-thread::-webkit-scrollbar { width: 4px; }\n.sd-thread::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 2px; }\n\n/* ─── Messages ──────────────────────────────────────────────── */\n\n.sd-msg-wrap { display: flex; flex-direction: column; }\n.sd-msg-wrap.bot { align-items: flex-start; }\n.sd-msg-wrap.user { align-items: flex-end; }\n\n.sd-fallback-label { font-size: 11px; color: rgba(255,255,255,0.5); margin: 0 0 4px; }\n\n.sd-bubble { max-width: 85%; padding: 12px 14px; font-size: 14px; word-break: break-word; white-space: pre-wrap; }\n.sd-bubble.bot {\n line-height: 1.6; border-radius: 14px 14px 14px 6px;\n background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.06);\n color: rgba(255,255,255,0.92);\n}\n.sd-bubble.user {\n line-height: 1.5; border-radius: 14px 14px 6px 14px;\n background: linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 75%, #1a1340));\n color: #fff;\n box-shadow: 0 8px 20px -8px color-mix(in oklab, ${accent} 70%, transparent);\n}\n\n.sd-timestamp { font-size: 11px; color: rgba(255,255,255,0.3); padding: 0 4px; margin-top: 4px; font-variant-numeric: tabular-nums; }\n\n/* ─── Typing indicator ──────────────────────────────────────── */\n\n.sd-typing {\n max-width: 85%; padding: 12px 14px;\n border-radius: 14px 14px 14px 6px;\n background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.06);\n display: flex; align-items: center; gap: 4px; align-self: flex-start;\n}\n.sd-dot { width: 6px; height: 6px; border-radius: 50%; background: rgba(255,255,255,0.4); animation: sd-bounce 1.2s ease-in-out infinite; }\n.sd-dot:nth-child(2) { animation-delay: 0.2s; }\n.sd-dot:nth-child(3) { animation-delay: 0.4s; }\n@keyframes sd-bounce { 0%,60%,100%{transform:translateY(0)} 30%{transform:translateY(-5px)} }\n\n/* ─── \"Try asking\" chips ────────────────────────────────────── */\n\n.sd-chips { padding: 0 20px 16px; position: relative; z-index: 1; flex-shrink: 0; }\n.sd-chips.scrollable {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.sd-chips.scrollable::-webkit-scrollbar { display: none; }\n.sd-chips.scrollable .sd-chips-label { display: none; }\n.sd-chips.scrollable .sd-chip { flex-shrink: 0; margin-bottom: 0; width: auto; white-space: nowrap; }\n\n.sd-chips-label { font-size: 11px; color: rgba(255,255,255,0.4); letter-spacing: 0.08em; text-transform: uppercase; font-weight: 500; margin-bottom: 8px; }\n.sd-chip {\n display: flex; align-items: center; gap: 8px;\n width: 100%; text-align: left; padding: 10px 14px; border-radius: 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; font-size: 13px;\n margin-bottom: 6px; outline: none; transition: background .12s;\n}\n.sd-chip:last-child { margin-bottom: 0; }\n.sd-chip:hover { background: rgba(255,255,255,0.07); }\n.sd-chip:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Composer ──────────────────────────────────────────────── */\n\n.sd-composer {\n padding: 14px; border-top: 1px solid rgba(255,255,255,0.06);\n position: relative; z-index: 1; flex-shrink: 0;\n}\n.sd-input-wrap {\n display: flex; align-items: center; gap: 8px;\n background: rgba(255,255,255,0.05);\n border: 1px solid rgba(255,255,255,0.08);\n border-radius: 12px; padding: 4px 4px 4px 14px;\n transition: border-color .15s;\n}\n.sd-input-wrap:focus-within { border-color: color-mix(in oklab, ${accent} 60%, transparent); }\n.sd-input { flex: 1; background: transparent; border: none; outline: none; font-size: 14px; padding: 9px 0; color: #fff; min-width: 0; }\n.sd-input::placeholder { color: rgba(255,255,255,0.35); }\n.sd-send {\n width: 32px; height: 32px; border-radius: 9px; border: none; padding: 0;\n background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.4);\n cursor: pointer; display: grid; place-items: center; flex-shrink: 0;\n transition: background .15s, color .15s, box-shadow .15s, opacity .1s, transform .1s; outline: none;\n}\n.sd-send.active {\n background: linear-gradient(135deg, ${accent}, color-mix(in oklab, ${accent} 60%, #fff));\n color: #fff;\n box-shadow: 0 4px 12px -2px color-mix(in oklab, ${accent} 60%, transparent);\n}\n.sd-send:hover { opacity: 0.85; }\n.sd-send:active { transform: scale(0.95); }\n.sd-send:focus-visible { outline: 2px solid ${accent}; outline-offset: 2px; }\n\n/* ─── Footer ─────────────────────────────────────────────────── */\n\n.sd-footer {\n height: 34px; border-top: 1px solid rgba(255,255,255,0.05);\n display: flex; align-items: center; justify-content: center;\n font-size: 11px; color: rgba(255,255,255,0.35); gap: 5px; flex-shrink: 0;\n position: relative; z-index: 1;\n}\n.sd-footer[hidden] { display: none; }\n.sd-footer-link { color: rgba(255,255,255,0.7); font-weight: 500; text-decoration: none; }\n.sd-footer-link:hover { text-decoration: underline; }\n\n@media (max-width: 420px) {\n .sd-panel { bottom: 0; right: 0; left: 0; width: auto; max-width: 100%; height: auto; max-height: 85vh; border-radius: 22px 22px 0 0; transform-origin: bottom center; }\n .sd-panel.pos-left { right: 0; transform-origin: bottom center; }\n}\n`;\n}\n\n// ── SVG icons ──────────────────────────────────────────────────────────────────\n\nexport const ICON_CHAT = `<svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path 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\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linejoin=\"round\"/></svg>`;\n\nexport const ICON_CHAT_SM = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path 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\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linejoin=\"round\"/></svg>`;\n\nexport const ICON_PERSON = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 12.5a4 4 0 100-8 4 4 0 000 8z\"/><path d=\"M5 20.5c0-3.5 3.13-6 7-6s7 2.5 7 6\"/></svg>`;\n\nexport const ICON_SEND = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path d=\"M4 12L20 4l-4 16-4-7-8-1z\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linejoin=\"round\"/></svg>`;\n\nexport const ICON_CLOSE = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path d=\"M6 6l12 12M18 6L6 18\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\"/></svg>`;\n\nexport const ICON_SPARKLE = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path d=\"M12 3l1.6 4.8L18 9l-4.4 1.4L12 15l-1.6-4.6L6 9l4.4-1.2L12 3z\" fill=\"currentColor\"/></svg>`;\n\nexport const ICON_SPARKLE_LG = `<svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path d=\"M12 3l1.6 4.8L18 9l-4.4 1.4L12 15l-1.6-4.6L6 9l4.4-1.2L12 3z\" fill=\"currentColor\"/></svg>`;\n\nexport const ICON_BOT = `<svg width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><rect x=\"4\" y=\"8\" width=\"16\" height=\"12\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.6\"/><circle cx=\"9\" cy=\"13.5\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"15\" cy=\"13.5\" r=\"1.5\" fill=\"currentColor\"/><path d=\"M9.5 16.5h5\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\"/><path d=\"M12 8V5\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\"/><circle cx=\"12\" cy=\"4\" r=\"1.2\" fill=\"currentColor\"/></svg>`;\n\nexport const ICON_BOT_SM = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><rect x=\"4\" y=\"8\" width=\"16\" height=\"12\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.6\"/><circle cx=\"9\" cy=\"13.5\" r=\"1.5\" fill=\"currentColor\"/><circle cx=\"15\" cy=\"13.5\" r=\"1.5\" fill=\"currentColor\"/><path d=\"M9.5 16.5h5\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\"/><path d=\"M12 8V5\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\"/><circle cx=\"12\" cy=\"4\" r=\"1.2\" fill=\"currentColor\"/></svg>`;\n\nexport const ICON_CHEVRON = `<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\"><path d=\"M9 6l6 6-6 6\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>`;\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\nexport function search(\n queryVector: Float32Array,\n index: IndexChunk[],\n topK = 3,\n minScore = 0.42\n): SearchResult[] {\n // Use a simple selection sort approach for small topK, \n // or a full sort if index is small. For very large indexes, \n // a proper Heap would be better, but O(N * topK) is already better than O(N log N).\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 results.push({ chunk, score });\n results.sort((a, b) => b.score - a.score);\n } else if (score > results[topK - 1].score) {\n results[topK - 1] = { chunk, score };\n results.sort((a, b) => b.score - a.score);\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 const matchCount = terms.filter((t) => chunkLower.includes(t)).length;\n const score = matchCount / terms.length;\n\n if (score <= 0) continue;\n\n if (results.length < topK) {\n results.push({ chunk, score });\n results.sort((a, b) => b.score - a.score);\n } else if (score > results[topK - 1].score) {\n results[topK - 1] = { chunk, score };\n results.sort((a, b) => b.score - a.score);\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 { getWidgetStyles, ICON_CHAT, ICON_CHAT_SM, ICON_PERSON, ICON_SEND, ICON_CLOSE, ICON_BOT, ICON_BOT_SM, ICON_CHEVRON } from './ui.js';\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 { SageDeskConfig, IndexChunk, ChatMessage, Theme } from '../core/types.js';\n\nexport class SageDeskWidget extends HTMLElement {\n private _config!: SageDeskConfig;\n private _shadow!: ShadowRoot;\n private _index: IndexChunk[] | null = null;\n private _embedder: EmbedderRuntime | null = null;\n private _messages: ChatMessage[] = [];\n private _isOpen = false;\n private _isTyping = false;\n private _engineReady = false;\n private _engineError: string | null = null;\n private _msgCounter = 0;\n private _hasSentMessage = false;\n\n static get observedAttributes() {\n return ['config'];\n }\n\n init(config: SageDeskConfig): void {\n this._config = config;\n this._mount();\n }\n\n private get _theme(): Theme {\n return this._config.agent.theme ?? 'classic';\n }\n\n private _mount(): void {\n const accent = this._config.agent.accentColor ?? '#534AB7';\n const position = this._config.agent.position ?? 'bottom-right';\n const theme = this._theme;\n\n this._shadow = this.attachShadow({ mode: 'open' });\n\n const style = document.createElement('style');\n style.textContent = getWidgetStyles(accent, theme);\n this._shadow.appendChild(style);\n\n this._buildDOM(accent, position, theme);\n this._bindEvents();\n }\n\n private _buildDOM(accent: string, position: string, theme: Theme): void {\n const posClass = position === 'bottom-left' ? 'pos-left' : '';\n const agentName = this._config.agent.name ?? 'Support';\n\n // Dark theme: ambient glow div behind trigger\n if (theme === 'dark') {\n const glow = document.createElement('div');\n glow.className = `sd-trigger-glow${posClass ? ' ' + posClass : ''}`;\n this._shadow.appendChild(glow);\n }\n\n // Trigger button\n const trigger = document.createElement('button');\n trigger.className = `sd-trigger${posClass ? ' ' + posClass : ''}`;\n trigger.setAttribute('aria-label', 'Open support chat');\n trigger.setAttribute('aria-expanded', 'false');\n trigger.innerHTML = theme === 'dark' ? ICON_BOT : ICON_CHAT;\n this._shadow.appendChild(trigger);\n\n // Panel\n const panel = document.createElement('div');\n panel.className = `sd-panel${posClass ? ' ' + posClass : ''}`;\n panel.setAttribute('data-open', 'false');\n panel.setAttribute('role', 'dialog');\n panel.setAttribute('aria-label', agentName);\n panel.innerHTML = this._buildPanelHTML(agentName, accent, theme);\n this._shadow.appendChild(panel);\n }\n\n private _buildPanelHTML(agentName: string, accent: string, theme: Theme): string {\n const showPoweredBy = this._config.agent.poweredBy !== false;\n const avatarUrl = this._config.agent.avatarUrl;\n const footerHTML = showPoweredBy\n ? `<div class=\"sd-footer\">Powered by <a class=\"sd-footer-link\" href=\"https://github.com/mzeeshanwahid/sagedesk\" target=\"_blank\" rel=\"noopener noreferrer\">sagedesk</a></div>`\n : '';\n\n if (theme === 'dark') {\n const avatarContent = avatarUrl\n ? `<img class=\"sd-avatar-img\" src=\"${escapeHtml(avatarUrl)}\" alt=\"${escapeHtml(agentName)}\">`\n : ICON_BOT_SM;\n return `\n <div class=\"sd-panel-glow\" aria-hidden=\"true\"></div>\n <div class=\"sd-header\">\n <div class=\"sd-avatar-wrap\">\n <div class=\"sd-avatar\">${avatarContent}</div>\n <span class=\"sd-online-dot\"></span>\n </div>\n <div class=\"sd-agent-info\">\n <p class=\"sd-agent-name\">${escapeHtml(agentName)}</p>\n <div class=\"sd-status\">\n <span class=\"sd-status-dot\"></span>\n <span>Trained on this site · always on</span>\n </div>\n </div>\n <button class=\"sd-close\" aria-label=\"Close chat\">${ICON_CLOSE}</button>\n </div>\n <div class=\"sd-thread\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div>\n <div class=\"sd-chips\"></div>\n <div class=\"sd-composer\">\n <div class=\"sd-input-wrap\">\n <input class=\"sd-input\" type=\"text\" placeholder=\"Write a message…\" aria-label=\"Type your question\" autocomplete=\"off\" />\n <button class=\"sd-send\" aria-label=\"Send message\">${ICON_SEND}</button>\n </div>\n </div>\n ${footerHTML}\n `;\n }\n\n if (theme === 'light') {\n const avatarContent = avatarUrl\n ? `<img class=\"sd-avatar-img\" src=\"${escapeHtml(avatarUrl)}\" alt=\"${escapeHtml(agentName)}\">`\n : ICON_PERSON;\n return `\n <div class=\"sd-header\">\n <div class=\"sd-avatar-wrap\">\n <div class=\"sd-avatar\">${avatarContent}</div>\n </div>\n <div class=\"sd-agent-info\">\n <p class=\"sd-agent-name\">${escapeHtml(agentName)}</p>\n <div class=\"sd-status\">\n <span class=\"sd-status-dot\"></span>\n <span>Online · replies in under a minute</span>\n </div>\n </div>\n <button class=\"sd-close\" aria-label=\"Close chat\">${ICON_CLOSE}</button>\n </div>\n <div class=\"sd-trigger-label\" hidden>\n <div class=\"sd-trigger-label-main\">Chat with us</div>\n <div class=\"sd-trigger-label-sub\">We typically reply in 1m</div>\n </div>\n <div class=\"sd-thread\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div>\n <div class=\"sd-chips\"></div>\n <div class=\"sd-composer\">\n <div class=\"sd-input-wrap\">\n <input class=\"sd-input\" type=\"text\" placeholder=\"Write a message…\" aria-label=\"Type your question\" autocomplete=\"off\" />\n <div class=\"sd-input-actions\">\n <button class=\"sd-send\" aria-label=\"Send message\">${ICON_SEND}</button>\n </div>\n </div>\n </div>\n ${footerHTML}\n `;\n }\n\n // Classic (default)\n const avatarContent = avatarUrl\n ? `<img class=\"sd-avatar-img\" src=\"${escapeHtml(avatarUrl)}\" alt=\"${escapeHtml(agentName)}\">`\n : ICON_PERSON;\n return `\n <div class=\"sd-header\">\n <div class=\"sd-avatar-wrap\">\n <div class=\"sd-avatar\">${avatarContent}</div>\n <span class=\"sd-online-dot\"></span>\n </div>\n <div class=\"sd-agent-info\">\n <p class=\"sd-agent-name\">${escapeHtml(agentName)}</p>\n <div class=\"sd-status\">\n <span class=\"sd-status-dot\"></span>\n <span>Typically replies in under a minute</span>\n </div>\n </div>\n <button class=\"sd-close\" aria-label=\"Close chat\">${ICON_CLOSE}</button>\n </div>\n <div class=\"sd-thread\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div>\n <div class=\"sd-chips\"></div>\n <div class=\"sd-composer\">\n <div class=\"sd-input-wrap\">\n <input class=\"sd-input\" type=\"text\" placeholder=\"Write a message…\" aria-label=\"Type your question\" autocomplete=\"off\" />\n <button class=\"sd-send\" aria-label=\"Send message\">${ICON_SEND}</button>\n </div>\n </div>\n ${footerHTML}\n `;\n }\n\n private _buildTriggerHTML(theme: Theme): string {\n if (theme === 'light') {\n return `\n <span class=\"sd-trigger-label\">\n <span class=\"sd-trigger-label-main\">Chat with us</span>\n <span class=\"sd-trigger-label-sub\">We typically reply in 1m</span>\n </span>\n <span class=\"sd-trigger-circle\">${ICON_CHAT_SM}</span>\n `;\n }\n if (theme === 'dark') return ICON_BOT;\n return ICON_CHAT;\n }\n\n private _bindEvents(): void {\n const shadow = this._shadow;\n\n const trigger = shadow.querySelector('.sd-trigger') as HTMLButtonElement;\n const closeBtn = shadow.querySelector('.sd-close') as HTMLButtonElement;\n const input = shadow.querySelector('.sd-input') as HTMLInputElement;\n const sendBtn = shadow.querySelector('.sd-send') as HTMLButtonElement;\n const panel = shadow.querySelector('.sd-panel') as HTMLElement;\n\n // Re-render trigger inner for light theme (pill needs label+circle)\n if (this._theme === 'light') {\n trigger.innerHTML = this._buildTriggerHTML('light');\n }\n\n trigger.addEventListener('click', () => {\n if (this._isOpen) {\n this._close();\n } else {\n this._open();\n }\n });\n closeBtn.addEventListener('click', () => this._close());\n sendBtn.addEventListener('click', () => this._submit());\n\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); this._submit(); }\n });\n\n // Update send button active state as user types\n input.addEventListener('input', () => {\n sendBtn.classList.toggle('active', input.value.trim().length > 0);\n });\n\n panel.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') this._close();\n });\n\n // Focus trap\n panel.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n const focusable = Array.from(\n panel.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n )\n );\n if (focusable.length === 0) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = shadow.activeElement;\n if (e.shiftKey) {\n if (active === first) { e.preventDefault(); last.focus(); }\n } else {\n if (active === last) { e.preventDefault(); first.focus(); }\n }\n });\n }\n\n private async _open(): Promise<void> {\n if (this._isOpen) return;\n this._isOpen = true;\n\n const trigger = this._shadow.querySelector('.sd-trigger') as HTMLButtonElement;\n const panel = this._shadow.querySelector('.sd-panel') as HTMLElement;\n\n trigger.setAttribute('aria-expanded', 'true');\n panel.setAttribute('data-open', 'true');\n panel.removeAttribute('data-closing');\n\n if (this._messages.length === 0) {\n const greeting = this._config.agent.greeting ?? 'Hey, how can I help you today?';\n this._appendMessage({ role: 'bot', text: greeting });\n }\n\n this._renderChips();\n\n const input = this._shadow.querySelector('.sd-input') as HTMLInputElement;\n setTimeout(() => input.focus(), 50);\n\n if (!this._engineReady && !this._engineError) {\n this._startEngine();\n }\n }\n\n private _close(): void {\n if (!this._isOpen) return;\n\n const trigger = this._shadow.querySelector('.sd-trigger') as HTMLButtonElement;\n const panel = this._shadow.querySelector('.sd-panel') as HTMLElement;\n\n panel.setAttribute('data-closing', 'true');\n panel.addEventListener(\n 'animationend',\n () => {\n this._isOpen = false;\n panel.setAttribute('data-open', 'false');\n panel.removeAttribute('data-closing');\n trigger.setAttribute('aria-expanded', 'false');\n trigger.focus();\n },\n { once: true }\n );\n }\n\n private async _startEngine(): Promise<void> {\n try {\n this._index = await fetchIndex(this._config.indexUrl);\n } catch {\n this._engineError = 'Could not load knowledge base.';\n this._appendMessage({\n role: 'bot',\n text: \"I'm having trouble loading right now. Please try again in a moment.\",\n });\n return;\n }\n\n try {\n this._embedder = new EmbedderRuntime();\n await this._embedder.load(this._config.agent.model);\n this._engineReady = true;\n } catch {\n this._embedder = new EmbedderRuntime();\n this._engineReady = true;\n }\n }\n\n private async _submit(): Promise<void> {\n const input = this._shadow.querySelector('.sd-input') as HTMLInputElement;\n const text = input.value.trim();\n if (!text) return;\n\n const typingStart = Date.now();\n\n input.value = '';\n const sendBtn = this._shadow.querySelector('.sd-send') as HTMLButtonElement;\n sendBtn.classList.remove('active');\n\n this._hasSentMessage = true;\n this._appendMessage({ role: 'user', text });\n this._showTyping();\n\n if (!this._engineReady && !this._engineError) {\n await this._waitForEngine();\n }\n\n let botText: string;\n let isFallback = false;\n let mode: 'vector' | 'keyword' = 'keyword';\n\n if (this._engineError || !this._index) {\n botText = getFallback(this._config.agent);\n isFallback = true;\n } else {\n try {\n const res = await retrieve(text, this._index, this._embedder!, this._config.search);\n mode = res.mode;\n if (res.results.length > 0) {\n botText = buildAnswer(res.results);\n } else {\n botText = getFallback(this._config.agent);\n isFallback = true;\n }\n } catch {\n botText = getFallback(this._config.agent);\n isFallback = true;\n }\n }\n\n const elapsed = Date.now() - typingStart;\n // Artificial \"thinking\" delay: 3-5s in normal mode, 800ms-2.8s in degraded/fallback mode.\n const delayBase = (mode === 'keyword' || isFallback) ? 800 : 3000;\n const minTypingMsActual = delayBase + Math.random() * 2000;\n const remaining = minTypingMsActual - elapsed;\n if (remaining > 0) await new Promise((r) => setTimeout(r, remaining));\n\n this._hideTyping();\n this._appendMessage({ role: 'bot', text: botText, isFallback });\n }\n\n private _waitForEngine(): Promise<void> {\n return new Promise((resolve) => {\n const check = () => {\n if (this._engineReady || this._engineError) resolve();\n else setTimeout(check, 100);\n };\n check();\n });\n }\n\n private _appendMessage(msg: Pick<ChatMessage, 'role' | 'text' | 'isFallback'>): void {\n const thread = this._shadow.querySelector('.sd-thread') as HTMLElement;\n const theme = this._theme;\n const id = `msg-${++this._msgCounter}`;\n\n this._messages.push({\n id,\n role: msg.role,\n text: msg.text,\n isFallback: msg.isFallback,\n timestamp: new Date(),\n });\n\n const wrap = document.createElement('div');\n wrap.id = id;\n\n if (theme === 'light' && msg.role === 'bot') {\n wrap.className = 'sd-msg-wrap bot';\n const fallbackLabel = msg.isFallback\n ? `<p class=\"sd-fallback-label\">Not sure about that one</p>`\n : '';\n const agentName = escapeHtml(this._config.agent.name ?? 'Support');\n wrap.innerHTML = `\n <div class=\"sd-bot-avatar\" aria-hidden=\"true\"></div>\n <div class=\"sd-bot-body\">\n <div class=\"sd-bot-meta\">\n <span class=\"sd-bot-name\">${agentName}</span>\n <span class=\"sd-timestamp\">just now</span>\n </div>\n ${fallbackLabel}\n <div class=\"sd-bubble bot\">${escapeHtml(msg.text)}</div>\n </div>\n `;\n } else {\n wrap.className = `sd-msg-wrap ${msg.role}`;\n let inner = '';\n if (msg.isFallback) {\n inner += `<p class=\"sd-fallback-label\">Not sure about that one</p>`;\n }\n inner += `<div class=\"sd-bubble ${msg.role}\">${escapeHtml(msg.text)}</div>`;\n inner += `<span class=\"sd-timestamp\">just now</span>`;\n wrap.innerHTML = inner;\n }\n\n thread.appendChild(wrap);\n thread.scrollTop = thread.scrollHeight;\n this._renderChips();\n }\n\n private _showTyping(): void {\n if (this._isTyping) return;\n this._isTyping = true;\n\n const thread = this._shadow.querySelector('.sd-thread') as HTMLElement;\n const theme = this._theme;\n const indicator = document.createElement('div');\n indicator.id = 'sd-typing-indicator';\n\n if (theme === 'light') {\n indicator.className = 'sd-typing';\n indicator.innerHTML = `\n <div class=\"sd-typing-avatar\" aria-hidden=\"true\"></div>\n <div class=\"sd-typing-dots\">\n <span class=\"sd-dot\"></span><span class=\"sd-dot\"></span><span class=\"sd-dot\"></span>\n </div>\n `;\n } else {\n indicator.className = 'sd-typing';\n indicator.innerHTML = '<span class=\"sd-dot\"></span><span class=\"sd-dot\"></span><span class=\"sd-dot\"></span>';\n }\n\n thread.appendChild(indicator);\n thread.scrollTop = thread.scrollHeight;\n }\n\n private _hideTyping(): void {\n this._isTyping = false;\n const indicator = this._shadow.getElementById('sd-typing-indicator');\n if (indicator) indicator.remove();\n }\n\n private _renderChips(): void {\n const container = this._shadow.querySelector('.sd-chips') as HTMLElement;\n container.innerHTML = '';\n\n const allChips = extractChips(this._index ?? [], this._config.agent.suggestedChips);\n const askedTexts = new Set(\n this._messages\n .filter((m) => m.role === 'user')\n .map((m) => m.text.toLowerCase().trim())\n );\n const chips = allChips.filter((chip) => !askedTexts.has(chip.toLowerCase().trim()));\n\n if (chips.length === 0) {\n container.style.display = 'none';\n return;\n }\n container.style.display = '';\n\n const theme = this._theme;\n if (this._hasSentMessage) {\n container.classList.add('scrollable');\n } else {\n container.classList.remove('scrollable');\n }\n\n if (theme === 'classic') {\n const label = document.createElement('div');\n label.className = 'sd-chips-label';\n label.textContent = 'Suggested';\n container.appendChild(label);\n\n const row = document.createElement('div');\n row.className = 'sd-chips-row';\n container.appendChild(row);\n\n for (const chipText of chips) {\n const btn = document.createElement('button');\n btn.className = 'sd-chip';\n btn.textContent = chipText;\n btn.addEventListener('click', () => this._submitChip(chipText));\n row.appendChild(btn);\n }\n return;\n }\n\n if (theme === 'dark' && !this._hasSentMessage) {\n const label = document.createElement('div');\n label.className = 'sd-chips-label';\n label.textContent = 'Try asking';\n container.appendChild(label);\n }\n\n for (const chipText of chips) {\n const btn = document.createElement('button');\n btn.className = 'sd-chip';\n if (theme === 'dark') {\n btn.innerHTML = `${ICON_CHEVRON} ${escapeHtml(chipText)}`;\n } else {\n btn.textContent = chipText;\n }\n btn.addEventListener('click', () => this._submitChip(chipText));\n container.appendChild(btn);\n }\n }\n\n private _submitChip(chipText: string): void {\n const input = this._shadow.querySelector('.sd-input') as HTMLInputElement;\n input.value = chipText;\n this._submit();\n }\n}\n\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(/\\n/g, '<br>');\n}\n","import { SageDeskWidget } from './widget.js';\nimport type { SageDeskConfig } from '../core/types.js';\n\nconst ELEMENT_TAG = 'sagedesk-widget';\n\n// Validates required config fields and throws descriptive errors so developer\n// mistakes surface immediately with a full stack trace, not as silent warns.\nfunction assertConfig(config: SageDeskConfig): void {\n if (!config || typeof config !== 'object') {\n throw new Error('[sagedesk] init() requires a config object.');\n }\n if (!config.indexUrl || typeof config.indexUrl !== 'string') {\n throw new Error(\n '[sagedesk] config.indexUrl is required. ' +\n 'Run `npx sagedesk build` and pass the output path as indexUrl.'\n );\n }\n if (!config.agent || typeof config.agent !== 'object') {\n throw new Error('[sagedesk] config.agent is required.');\n }\n if (!config.agent.name || typeof config.agent.name !== 'string') {\n throw new Error('[sagedesk] config.agent.name is required.');\n }\n}\n\nfunction init(config: SageDeskConfig): void {\n // assertConfig is intentionally outside the try/catch so developer config\n // mistakes always surface as thrown errors rather than being swallowed.\n assertConfig(config);\n\n try {\n if (typeof customElements === 'undefined') return;\n if (typeof document === 'undefined') return;\n\n if (!customElements.get(ELEMENT_TAG)) {\n customElements.define(ELEMENT_TAG, SageDeskWidget);\n }\n\n const existing = document.querySelector(ELEMENT_TAG) as SageDeskWidget | null;\n if (existing) {\n existing.init(config);\n return;\n }\n\n const el = document.createElement(ELEMENT_TAG) as SageDeskWidget;\n document.body.appendChild(el);\n el.init(config);\n } catch (err) {\n // Never crash the host page for runtime errors\n console.warn('[sagedesk] Failed to initialise:', err);\n }\n}\n\n// Expose on window for script-tag usage\nif (typeof window !== 'undefined') {\n (window as Window & { SageDesk?: { init: typeof init } }).SageDesk = { init };\n}\n\nexport { init };\nexport type { SageDeskConfig };\nexport { SageDeskWidget };\n"],"mappings":"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","names":["results","avatarContent"]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"knowledge": [
|
|
3
|
+
{
|
|
4
|
+
"id": "about-1",
|
|
5
|
+
"topic": "about",
|
|
6
|
+
"queries": [
|
|
7
|
+
"Who built this site?",
|
|
8
|
+
"Who made this website?",
|
|
9
|
+
"Who is the developer?",
|
|
10
|
+
"Tell me about the author"
|
|
11
|
+
],
|
|
12
|
+
"answer": "This site was built by Jane Doe, a full-stack developer specialising in React and Node.js with over eight years of professional experience. Jane works with early-stage startups and growing product teams.",
|
|
13
|
+
"tags": ["about", "intro", "bio", "who"]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "services-1",
|
|
17
|
+
"topic": "services",
|
|
18
|
+
"queries": [
|
|
19
|
+
"What services do you offer?",
|
|
20
|
+
"What kind of work do you do?",
|
|
21
|
+
"What can you help me with?"
|
|
22
|
+
],
|
|
23
|
+
"answer": "We offer three core services: web and product development (React, Next.js, Node.js), API design and integration, and technical consulting for startups who need an experienced engineering partner. Engagements range from short one-week audits to ongoing monthly retainers.",
|
|
24
|
+
"tags": ["services", "work", "hire", "consulting"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "pricing-1",
|
|
28
|
+
"topic": "pricing",
|
|
29
|
+
"queries": [
|
|
30
|
+
"How much does it cost to hire you?",
|
|
31
|
+
"What are your rates?",
|
|
32
|
+
"How much do you charge?"
|
|
33
|
+
],
|
|
34
|
+
"answer": "Consulting and development work is priced on a project or retainer basis after an initial conversation. Project rates typically start at $3,000 for a scoped deliverable. Monthly retainers for ongoing work start at $2,500. Reach out through the contact page and we can discuss what fits your situation.",
|
|
35
|
+
"tags": ["pricing", "cost", "rates", "budget", "hire"]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "process-1",
|
|
39
|
+
"topic": "process",
|
|
40
|
+
"queries": [
|
|
41
|
+
"What does your development process look like?",
|
|
42
|
+
"How do you work?",
|
|
43
|
+
"What is your workflow?"
|
|
44
|
+
],
|
|
45
|
+
"answer": "Every engagement starts with a discovery call to understand your goals and constraints. From there I write a brief scope document, agree on milestones, and we kick off work. I provide weekly progress updates and use GitHub for all code so you always have full visibility. Most projects wrap up within four to eight weeks depending on scope.",
|
|
46
|
+
"tags": ["process", "workflow", "timeline", "how it works"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "stack-1",
|
|
50
|
+
"topic": "tech",
|
|
51
|
+
"queries": [
|
|
52
|
+
"What technologies do you work with?",
|
|
53
|
+
"What is your tech stack?",
|
|
54
|
+
"What programming languages do you use?",
|
|
55
|
+
"Can you work with React?",
|
|
56
|
+
"Do you know TypeScript?"
|
|
57
|
+
],
|
|
58
|
+
"answer": "Primary stack is TypeScript, React, Next.js, Node.js, and PostgreSQL. I'm comfortable with AWS infrastructure, Vercel deployments, Supabase, and Prisma. For mobile I use React Native. I'm happy to adapt to an existing stack if you already have one in place.",
|
|
59
|
+
"tags": ["tech", "stack", "technologies", "react", "node", "typescript"]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "contact-1",
|
|
63
|
+
"topic": "contact",
|
|
64
|
+
"queries": [
|
|
65
|
+
"How do I get in touch?",
|
|
66
|
+
"How can I contact you?",
|
|
67
|
+
"What is your email?",
|
|
68
|
+
"How do I reach out?"
|
|
69
|
+
],
|
|
70
|
+
"answer": "The best way to reach out is through the contact form at yoursite.com/contact. You can also email directly at hello@yoursite.com. I try to respond to all enquiries within one business day. If you're in a time-sensitive situation, mention that in your message and I'll prioritise it.",
|
|
71
|
+
"tags": ["contact", "email", "reach out", "get in touch", "hire"]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "availability-1",
|
|
75
|
+
"topic": "availability",
|
|
76
|
+
"queries": [
|
|
77
|
+
"Are you currently available for new projects?",
|
|
78
|
+
"Are you taking new clients?",
|
|
79
|
+
"When can you start?"
|
|
80
|
+
],
|
|
81
|
+
"answer": "Availability varies by month. The contact page has an up-to-date status. Even when fully booked I keep a short waitlist and can usually start new projects within two to three weeks. It's worth reaching out early if you have a specific start date in mind.",
|
|
82
|
+
"tags": ["availability", "booking", "schedule", "start date"]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sagedesk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Local RAG-powered support chat widget for any website. No API key required. All semantic search runs in the browser via WebAssembly.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Muhammad Zeeshan",
|
|
8
|
+
"email": "mzeeshanwa@gmail.com",
|
|
9
|
+
"url": "https://www.mzeeshan.dev"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/vanilla/index.d.ts",
|
|
14
|
+
"import": "./dist/vanilla/index.js",
|
|
15
|
+
"require": "./dist/vanilla/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./react": {
|
|
18
|
+
"types": "./dist/react/index.d.ts",
|
|
19
|
+
"import": "./dist/react/index.js",
|
|
20
|
+
"require": "./dist/react/index.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./next": {
|
|
23
|
+
"types": "./dist/next/index.d.ts",
|
|
24
|
+
"import": "./dist/next/index.js",
|
|
25
|
+
"require": "./dist/next/index.cjs"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"bin": {
|
|
29
|
+
"sagedesk": "./dist/cli/index.cjs"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"knowledge.example.json"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup",
|
|
38
|
+
"dev": "tsup --watch",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"prepublishOnly": "npm run build",
|
|
42
|
+
"release:patch": "npm version patch && git push --follow-tags",
|
|
43
|
+
"release:minor": "npm version minor && git push --follow-tags",
|
|
44
|
+
"release:major": "npm version major && git push --follow-tags"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@huggingface/transformers": "^3.8.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@testing-library/dom": "^10.4.1",
|
|
51
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
52
|
+
"@testing-library/react": "^16.3.2",
|
|
53
|
+
"@testing-library/user-event": "^14.6.1",
|
|
54
|
+
"@types/node": "^20.0.0",
|
|
55
|
+
"@types/react": "^18.0.0",
|
|
56
|
+
"@types/react-dom": "^18.0.0",
|
|
57
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
58
|
+
"@vitest/coverage-v8": "^1.6.1",
|
|
59
|
+
"chalk": "^5.3.0",
|
|
60
|
+
"commander": "^11.1.0",
|
|
61
|
+
"jsdom": "^29.1.1",
|
|
62
|
+
"next": "^14.0.0",
|
|
63
|
+
"ora": "^7.0.1",
|
|
64
|
+
"react": "^18.0.0",
|
|
65
|
+
"react-dom": "^18.0.0",
|
|
66
|
+
"tsup": "^8.0.0",
|
|
67
|
+
"typescript": "^5.0.0",
|
|
68
|
+
"vite": "^8.0.10",
|
|
69
|
+
"vitest": "^1.0.0"
|
|
70
|
+
},
|
|
71
|
+
"peerDependencies": {
|
|
72
|
+
"next": ">=13.0.0",
|
|
73
|
+
"react": ">=18.0.0",
|
|
74
|
+
"react-dom": ">=18.0.0"
|
|
75
|
+
},
|
|
76
|
+
"peerDependenciesMeta": {
|
|
77
|
+
"react": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"react-dom": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"next": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"keywords": [
|
|
88
|
+
"chat",
|
|
89
|
+
"widget",
|
|
90
|
+
"rag",
|
|
91
|
+
"support",
|
|
92
|
+
"wasm",
|
|
93
|
+
"webassembly",
|
|
94
|
+
"semantic-search",
|
|
95
|
+
"local-ai",
|
|
96
|
+
"no-api-key",
|
|
97
|
+
"chatbot",
|
|
98
|
+
"support-widget",
|
|
99
|
+
"help-widget",
|
|
100
|
+
"faq",
|
|
101
|
+
"knowledge-base",
|
|
102
|
+
"vector-search",
|
|
103
|
+
"embeddings",
|
|
104
|
+
"transformers",
|
|
105
|
+
"offline",
|
|
106
|
+
"privacy",
|
|
107
|
+
"react",
|
|
108
|
+
"nextjs",
|
|
109
|
+
"vanilla-js",
|
|
110
|
+
"web-component",
|
|
111
|
+
"floating-chat",
|
|
112
|
+
"customer-support",
|
|
113
|
+
"ai",
|
|
114
|
+
"nlp"
|
|
115
|
+
],
|
|
116
|
+
"license": "MIT",
|
|
117
|
+
"homepage": "https://github.com/mzeeshanwahid/sagedesk#readme",
|
|
118
|
+
"repository": {
|
|
119
|
+
"type": "git",
|
|
120
|
+
"url": "https://github.com/mzeeshanwahid/sagedesk.git"
|
|
121
|
+
},
|
|
122
|
+
"bugs": {
|
|
123
|
+
"url": "https://github.com/mzeeshanwahid/sagedesk/issues"
|
|
124
|
+
},
|
|
125
|
+
"engines": {
|
|
126
|
+
"node": ">=18.0.0",
|
|
127
|
+
"npm": ">=8.0.0"
|
|
128
|
+
},
|
|
129
|
+
"sideEffects": false
|
|
130
|
+
}
|