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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/vanilla/index.ts","../../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"],"sourcesContent":["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","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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n .replace(/\\n/g, '<br>');\n}\n"],"mappings":"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","names":["results","avatarContent"]}
1
+ {"version":3,"sources":["../../src/vanilla/index.ts","../../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"],"sourcesContent":["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","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\n// Inserts item at the correct descending-score position, then trims to maxLen.\n// Avoids Array.sort overhead on every insertion for small topK arrays.\nfunction insertSorted(arr: SearchResult[], item: SearchResult, maxLen: number): void {\n arr.push(item);\n let i = arr.length - 1;\n while (i > 0 && arr[i - 1].score < arr[i].score) {\n const tmp = arr[i - 1]; arr[i - 1] = arr[i]; arr[i] = tmp;\n i--;\n }\n if (arr.length > maxLen) arr.pop();\n}\n\nexport function search(\n queryVector: Float32Array,\n index: IndexChunk[],\n topK = 3,\n minScore = 0.42\n): SearchResult[] {\n const results: SearchResult[] = [];\n\n for (const chunk of index) {\n const score = dotProduct(queryVector, chunk.vector384 as Float32Array);\n if (score < minScore) continue;\n\n if (results.length < topK) {\n insertSorted(results, { chunk, score }, topK);\n } else if (score > results[topK - 1].score) {\n results[topK - 1] = { chunk, score };\n let i = topK - 1;\n while (i > 0 && results[i - 1].score < results[i].score) {\n const tmp = results[i - 1]; results[i - 1] = results[i]; results[i] = tmp;\n i--;\n }\n }\n }\n\n return results;\n}\n\nexport function keywordSearch(\n query: string,\n index: IndexChunk[],\n topK = 3\n): SearchResult[] {\n const terms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((w) => w.length > 2)\n .map((w) => w.replace(/[^a-z0-9]/g, ''));\n\n if (terms.length === 0) return [];\n\n const results: SearchResult[] = [];\n\n for (const chunk of index) {\n const chunkLower = chunk.textLower || chunk.text.toLowerCase();\n let matchCount = 0;\n for (const t of terms) {\n if (chunkLower.includes(t)) matchCount++;\n }\n const score = matchCount / terms.length;\n\n if (score <= 0) continue;\n\n if (results.length < topK) {\n insertSorted(results, { chunk, score }, topK);\n } else if (score > results[topK - 1].score) {\n results[topK - 1] = { chunk, score };\n let i = topK - 1;\n while (i > 0 && results[i - 1].score < results[i].score) {\n const tmp = results[i - 1]; results[i - 1] = results[i]; results[i] = tmp;\n i--;\n }\n }\n }\n\n return results;\n}\n\nexport async function loadIndex(url: string): Promise<IndexChunk[]> {\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Failed to fetch index (HTTP ${res.status}): ${url}`);\n }\n const data = await res.json();\n // Support both the new { meta, chunks } format and the legacy bare-array format.\n const chunks: IndexChunk[] = Array.isArray(data)\n ? data\n : (data as { chunks: IndexChunk[] }).chunks;\n\n // Materialize lowercase versions and convert vectors to Float32Array once at load time.\n for (const chunk of chunks) {\n chunk.textLower = chunk.text.toLowerCase();\n if (Array.isArray(chunk.vector384)) {\n chunk.vector384 = new Float32Array(chunk.vector384);\n }\n if (Array.isArray(chunk.vector768)) {\n chunk.vector768 = new Float32Array(chunk.vector768);\n }\n }\n\n return chunks;\n}\n","import { search, keywordSearch, loadIndex } from './search';\nimport type { EmbedderRuntime } from './embedder';\nimport type { IndexChunk, SearchResult, SageDeskConfig } from './types';\n\nexport async function fetchIndex(url: string): Promise<IndexChunk[]> {\n try {\n return await loadIndex(url);\n } catch (err) {\n throw new Error(`Could not load knowledge index: ${String(err)}`);\n }\n}\n\nexport async function retrieve(\n text: string,\n index: IndexChunk[],\n embedder: EmbedderRuntime,\n config?: SageDeskConfig['search']\n): Promise<{ results: SearchResult[]; mode: 'vector' | 'keyword' }> {\n const topK = config?.topK ?? 3;\n const minScore = config?.minScore ?? 0.42;\n\n if (embedder.isReady) {\n try {\n const vector = await embedder.embed(text);\n const results = search(vector, index, topK, minScore);\n return { results, mode: 'vector' };\n } catch {\n // Fall through to keyword search\n }\n }\n\n const results = keywordSearch(text, index, topK);\n return { results, mode: 'keyword' };\n}\n","import type { SearchResult } from './types';\n\nexport function buildAnswer(results: SearchResult[]): string {\n if (results.length === 0) return '';\n // Deduplicate by sourceId: query expansion produces multiple chunks per\n // source entry (same answer, different query phrasings) - show each source once.\n const seen = new Set<string>();\n const parts: string[] = [];\n for (const r of results) {\n if (!seen.has(r.chunk.sourceId)) {\n seen.add(r.chunk.sourceId);\n parts.push(r.chunk.text);\n }\n }\n return parts.join('\\n\\n');\n}\n\nexport function extractChips(\n index: { text: string; question?: string; sourceId?: string }[],\n override?: string[]\n): string[] {\n if (override && override.length > 0) return override.slice(0, 5);\n\n const chips: string[] = [];\n const seenText = new Set<string>();\n const seenSource = new Set<string>();\n\n for (const chunk of index) {\n if (chips.length >= 5) break;\n\n // Deduplicate by sourceId if available to ensure variety of answers.\n if (chunk.sourceId) {\n if (seenSource.has(chunk.sourceId)) continue;\n seenSource.add(chunk.sourceId);\n }\n\n const candidate = chunk.question ?? extractFirstSentence(chunk.text);\n if (candidate && !seenText.has(candidate)) {\n seenText.add(candidate);\n chips.push(candidate);\n }\n }\n\n return chips;\n}\n\nfunction extractFirstSentence(text: string): string {\n const match = text.match(/^[^\\n.!?]{10,80}[.!?\\n]?/);\n if (!match) return text.slice(0, 60);\n return match[0].trim();\n}\n","import type { AgentConfig } from './types';\n\nconst DEFAULT_POOL = [\n \"That one's a bit outside what I have notes on right now. Feel free to reach out directly and I'll make sure you get a proper answer.\",\n \"Hmm, I don't have a great answer for that one yet. You're welcome to get in touch and a real person will help.\",\n \"I want to give you the right answer, not a guess. If you reach out through the contact page someone will follow up with you.\",\n];\n\nlet rotationIndex = 0;\n\nexport function getFallback(config: AgentConfig): string {\n const pool =\n config.fallbackPool && config.fallbackPool.length > 0\n ? config.fallbackPool\n : config.fallback\n ? [config.fallback]\n : DEFAULT_POOL;\n\n const message = pool[rotationIndex % pool.length];\n rotationIndex = (rotationIndex + 1) % pool.length;\n\n if (config.contactUrl) {\n return `${message} You can reach us at: ${config.contactUrl}`;\n }\n\n return message;\n}\n","import { 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 avatarUrl = this._config.agent.avatarUrl;\n const footerHTML = `<div class=\"sd-footer\">Powered by <a class=\"sd-footer-link\" href=\"https://github.com/mzeeshanwahid/sagedesk\" target=\"_blank\" rel=\"noopener\">sagedesk</a></div>`;\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 if (!this._config.indexUrl) {\n this._engineError = 'indexUrl is required for the vanilla widget (local mode only).';\n console.warn('[sagedesk] indexUrl is required. Run `npx sagedesk build` and pass indexUrl.');\n return;\n }\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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n .replace(/\\n/g, '<br>');\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACIO,SAAS,gBAAgB,QAAgB,QAAe,WAAmB;AAChF,MAAI,UAAU,QAAS,QAAO,eAAe,MAAM;AACnD,MAAI,UAAU,OAAS,QAAO,cAAc,MAAM;AAClD,SAAO,iBAAiB,MAAM;AAChC;AAKO,SAAS,iBAAiB,QAAwB;AACvD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBASQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAciB,MAAM,4BAA4B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qDAW3B,MAAM;AAAA;AAAA;AAAA,iDAGV,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAmCf,MAAM,4BAA4B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BA8BpD,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAuDlB,MAAM;AAAA;AAAA;AAAA,oDAG8B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4DAyCE,MAAM;AAAA,WACvD,MAAM;AAAA;AAAA;AAAA;AAAA,8CAI6B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAYd,MAAM;AAAA,8CACE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAQhB,MAAM;AAAA;AAAA;AAAA;AAAA,gCAIV,MAAM;AAAA;AAAA;AAAA,8CAGQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBpD;AAMO,SAAS,eAAe,QAAwB;AACrD,SAAO;AAAA;AAAA;AAAA;AAAA,iBAIQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iDAgB0B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBASvC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAgCkB,MAAM,yBAAyB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAc9B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAuBb,MAAM,yBAAyB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAczC,MAAM;AAAA,WAC/B,MAAM;AAAA,0CACyB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAWR,MAAM,yBAAyB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CA+B/B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sCAUd,MAAM;AAAA,8CACE,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAWpB,MAAM;AAAA;AAAA;AAAA,8CAGQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBpD;AAMO,SAAS,cAAc,QAAwB;AACpD,SAAO;AAAA;AAAA;AAAA;AAAA,iBAIQ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4DASqC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4DAUN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iDAQjB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gFA4ByB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAY9C,MAAM,yBAAyB,MAAM;AAAA,6CAChC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+CAmBJ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCA+Bb,MAAM,yBAAyB,MAAM;AAAA;AAAA,oDAEzB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CA2CZ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kEAec,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wCAUhC,MAAM,yBAAyB,MAAM;AAAA;AAAA,oDAEzB,MAAM;AAAA;AAAA;AAAA;AAAA,8CAIZ,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBpD;AAIO,IAAM,YAAY;AAElB,IAAM,eAAe;AAErB,IAAM,cAAc;AAEpB,IAAM,YAAY;AAElB,IAAM,aAAa;AAMnB,IAAM,WAAW;AAEjB,IAAM,cAAc;AAEpB,IAAM,eAAe;;;AC7sB5B,IAAM,aAA4C;AAAA,EAChD,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,yCAAyC;AAAA,EACzC,qBAAqB;AACvB;AAEO,IAAM,mBAAN,MAAM,iBAAgB;AAAA,EAAtB;AACL,SAAQ,SAAS;AACjB,SAAQ,UAAU;AAAA;AAAA,EAOlB,MAAM,KAAK,QAAuB,oBAAmC;AACnE,QAAI,KAAK,OAAQ;AACjB,QAAI,KAAK,QAAS,OAAM,IAAI,MAAM,2CAA2C;AAE7E,QAAI,iBAAgB,iBAAiB;AACnC,YAAM,iBAAgB;AACtB,WAAK,SAAS;AACd;AAAA,IACF;AAEA,UAAM,UAAU,WAAW,KAAK;AAEhC,qBAAgB,mBAAmB,YAAY;AAC7C,UAAI;AACF,cAAM,EAAE,SAAS,IAAI,MAAM,OAAO,2BAA2B;AAC7D,yBAAgB,oBAAqB,MAAM;AAAA,UACzC;AAAA,UACA;AAAA,UACA,EAAE,OAAO,KAAK;AAAA,QAChB;AAAA,MACF,SAAS,KAAK;AACZ,yBAAgB,kBAAkB;AAClC,yBAAgB,oBAAoB;AACpC,cAAM;AAAA,MACR;AAAA,IACF,GAAG;AAEH,QAAI;AACF,YAAM,iBAAgB;AACtB,WAAK,SAAS;AAAA,IAChB,SAAS,KAAK;AACZ,WAAK,UAAU;AACf,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,MAAqC;AAC/C,QAAI,CAAC,iBAAgB,mBAAmB;AACtC,YAAM,KAAK,KAAK;AAAA,IAClB;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,iBAAgB,kBAAmB,MAAM;AAAA,QAC5D,SAAS;AAAA,QACT,WAAW;AAAA,MACb,CAAC;AACD,aAAO,OAAO;AAAA,IAChB,SAAS,KAAK;AACZ,YAAM,IAAI,MAAM,qBAAqB,OAAO,GAAG,CAAC,EAAE;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,OAAO,SAAe;AACpB,qBAAgB,oBAAoB;AACpC,qBAAgB,kBAAkB;AAAA,EACpC;AACF;AAAA;AAAA;AA1Ea,iBAMI,oBAAuC;AAN3C,iBAOI,kBAAwC;AAPlD,IAAM,kBAAN;;;ACVP,SAAS,WAAW,GAAiB,GAAyB;AAC5D,MAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,UAAM,IAAI,MAAM,oCAAoC,EAAE,MAAM,cAAc,EAAE,MAAM,GAAG;AAAA,EACvF;AACA,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,IAAK,QAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACpD,SAAO;AACT;AAIA,SAAS,aAAa,KAAqB,MAAoB,QAAsB;AACnF,MAAI,KAAK,IAAI;AACb,MAAI,IAAI,IAAI,SAAS;AACrB,SAAO,IAAI,KAAK,IAAI,IAAI,CAAC,EAAE,QAAQ,IAAI,CAAC,EAAE,OAAO;AAC/C,UAAM,MAAM,IAAI,IAAI,CAAC;AAAG,QAAI,IAAI,CAAC,IAAI,IAAI,CAAC;AAAG,QAAI,CAAC,IAAI;AACtD;AAAA,EACF;AACA,MAAI,IAAI,SAAS,OAAQ,KAAI,IAAI;AACnC;AAEO,SAAS,OACd,aACA,OACA,OAAO,GACP,WAAW,MACK;AAChB,QAAM,UAA0B,CAAC;AAEjC,aAAW,SAAS,OAAO;AACzB,UAAM,QAAQ,WAAW,aAAa,MAAM,SAAyB;AACrE,QAAI,QAAQ,SAAU;AAEtB,QAAI,QAAQ,SAAS,MAAM;AACzB,mBAAa,SAAS,EAAE,OAAO,MAAM,GAAG,IAAI;AAAA,IAC9C,WAAW,QAAQ,QAAQ,OAAO,CAAC,EAAE,OAAO;AAC1C,cAAQ,OAAO,CAAC,IAAI,EAAE,OAAO,MAAM;AACnC,UAAI,IAAI,OAAO;AACf,aAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,EAAE,QAAQ,QAAQ,CAAC,EAAE,OAAO;AACvD,cAAM,MAAM,QAAQ,IAAI,CAAC;AAAG,gBAAQ,IAAI,CAAC,IAAI,QAAQ,CAAC;AAAG,gBAAQ,CAAC,IAAI;AACtE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,cACd,OACA,OACA,OAAO,GACS;AAChB,QAAM,QAAQ,MACX,YAAY,EACZ,MAAM,KAAK,EACX,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,MAAM,EAAE,QAAQ,cAAc,EAAE,CAAC;AAEzC,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAEhC,QAAM,UAA0B,CAAC;AAEjC,aAAW,SAAS,OAAO;AACzB,UAAM,aAAa,MAAM,aAAa,MAAM,KAAK,YAAY;AAC7D,QAAI,aAAa;AACjB,eAAW,KAAK,OAAO;AACrB,UAAI,WAAW,SAAS,CAAC,EAAG;AAAA,IAC9B;AACA,UAAM,QAAQ,aAAa,MAAM;AAEjC,QAAI,SAAS,EAAG;AAEhB,QAAI,QAAQ,SAAS,MAAM;AACzB,mBAAa,SAAS,EAAE,OAAO,MAAM,GAAG,IAAI;AAAA,IAC9C,WAAW,QAAQ,QAAQ,OAAO,CAAC,EAAE,OAAO;AAC1C,cAAQ,OAAO,CAAC,IAAI,EAAE,OAAO,MAAM;AACnC,UAAI,IAAI,OAAO;AACf,aAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,EAAE,QAAQ,QAAQ,CAAC,EAAE,OAAO;AACvD,cAAM,MAAM,QAAQ,IAAI,CAAC;AAAG,gBAAQ,IAAI,CAAC,IAAI,QAAQ,CAAC;AAAG,gBAAQ,CAAC,IAAI;AACtE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAsB,UAAU,KAAoC;AAClE,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,+BAA+B,IAAI,MAAM,MAAM,GAAG,EAAE;AAAA,EACtE;AACA,QAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,QAAM,SAAuB,MAAM,QAAQ,IAAI,IAC3C,OACC,KAAkC;AAGvC,aAAW,SAAS,QAAQ;AAC1B,UAAM,YAAY,MAAM,KAAK,YAAY;AACzC,QAAI,MAAM,QAAQ,MAAM,SAAS,GAAG;AAClC,YAAM,YAAY,IAAI,aAAa,MAAM,SAAS;AAAA,IACpD;AACA,QAAI,MAAM,QAAQ,MAAM,SAAS,GAAG;AAClC,YAAM,YAAY,IAAI,aAAa,MAAM,SAAS;AAAA,IACpD;AAAA,EACF;AAEA,SAAO;AACT;;;AChHA,eAAsB,WAAW,KAAoC;AACnE,MAAI;AACF,WAAO,MAAM,UAAU,GAAG;AAAA,EAC5B,SAAS,KAAK;AACZ,UAAM,IAAI,MAAM,mCAAmC,OAAO,GAAG,CAAC,EAAE;AAAA,EAClE;AACF;AAEA,eAAsB,SACpB,MACA,OACA,UACA,QACkE;AAClE,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,WAAW,QAAQ,YAAY;AAErC,MAAI,SAAS,SAAS;AACpB,QAAI;AACF,YAAM,SAAS,MAAM,SAAS,MAAM,IAAI;AACxC,YAAMA,WAAU,OAAO,QAAQ,OAAO,MAAM,QAAQ;AACpD,aAAO,EAAE,SAAAA,UAAS,MAAM,SAAS;AAAA,IACnC,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,UAAU,cAAc,MAAM,OAAO,IAAI;AAC/C,SAAO,EAAE,SAAS,MAAM,UAAU;AACpC;;;AC/BO,SAAS,YAAY,SAAiC;AAC3D,MAAI,QAAQ,WAAW,EAAG,QAAO;AAGjC,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,QAAkB,CAAC;AACzB,aAAW,KAAK,SAAS;AACvB,QAAI,CAAC,KAAK,IAAI,EAAE,MAAM,QAAQ,GAAG;AAC/B,WAAK,IAAI,EAAE,MAAM,QAAQ;AACzB,YAAM,KAAK,EAAE,MAAM,IAAI;AAAA,IACzB;AAAA,EACF;AACA,SAAO,MAAM,KAAK,MAAM;AAC1B;AAEO,SAAS,aACd,OACA,UACU;AACV,MAAI,YAAY,SAAS,SAAS,EAAG,QAAO,SAAS,MAAM,GAAG,CAAC;AAE/D,QAAM,QAAkB,CAAC;AACzB,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,aAAa,oBAAI,IAAY;AAEnC,aAAW,SAAS,OAAO;AACzB,QAAI,MAAM,UAAU,EAAG;AAGvB,QAAI,MAAM,UAAU;AAClB,UAAI,WAAW,IAAI,MAAM,QAAQ,EAAG;AACpC,iBAAW,IAAI,MAAM,QAAQ;AAAA,IAC/B;AAEA,UAAM,YAAY,MAAM,YAAY,qBAAqB,MAAM,IAAI;AACnE,QAAI,aAAa,CAAC,SAAS,IAAI,SAAS,GAAG;AACzC,eAAS,IAAI,SAAS;AACtB,YAAM,KAAK,SAAS;AAAA,IACtB;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,qBAAqB,MAAsB;AAClD,QAAM,QAAQ,KAAK,MAAM,0BAA0B;AACnD,MAAI,CAAC,MAAO,QAAO,KAAK,MAAM,GAAG,EAAE;AACnC,SAAO,MAAM,CAAC,EAAE,KAAK;AACvB;;;AChDA,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAI,gBAAgB;AAEb,SAAS,YAAY,QAA6B;AACvD,QAAM,OACJ,OAAO,gBAAgB,OAAO,aAAa,SAAS,IAChD,OAAO,eACP,OAAO,WACL,CAAC,OAAO,QAAQ,IAChB;AAER,QAAM,UAAU,KAAK,gBAAgB,KAAK,MAAM;AAChD,mBAAiB,gBAAgB,KAAK,KAAK;AAE3C,MAAI,OAAO,YAAY;AACrB,WAAO,GAAG,OAAO,yBAAyB,OAAO,UAAU;AAAA,EAC7D;AAEA,SAAO;AACT;;;ACnBO,IAAM,iBAAN,cAA6B,YAAY;AAAA,EAAzC;AAAA;AAGL,SAAQ,SAA8B;AACtC,SAAQ,YAAoC;AAC5C,SAAQ,YAA2B,CAAC;AACpC,SAAQ,UAAU;AAClB,SAAQ,YAAY;AACpB,SAAQ,eAAe;AACvB,SAAQ,eAA8B;AACtC,SAAQ,cAAc;AACtB,SAAQ,kBAAkB;AAAA;AAAA,EAE1B,WAAW,qBAAqB;AAC9B,WAAO,CAAC,QAAQ;AAAA,EAClB;AAAA,EAEA,KAAK,QAA8B;AACjC,SAAK,UAAU;AACf,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,IAAY,SAAgB;AAC1B,WAAO,KAAK,QAAQ,MAAM,SAAS;AAAA,EACrC;AAAA,EAEQ,SAAe;AACrB,UAAM,SAAW,KAAK,QAAQ,MAAM,eAAe;AACnD,UAAM,WAAW,KAAK,QAAQ,MAAM,YAAY;AAChD,UAAM,QAAW,KAAK;AAEtB,SAAK,UAAU,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAEjD,UAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,UAAM,cAAc,gBAAgB,QAAQ,KAAK;AACjD,SAAK,QAAQ,YAAY,KAAK;AAE9B,SAAK,UAAU,QAAQ,UAAU,KAAK;AACtC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,UAAU,QAAgB,UAAkB,OAAoB;AACtE,UAAM,WAAa,aAAa,gBAAgB,aAAa;AAC7D,UAAM,YAAa,KAAK,QAAQ,MAAM,QAAQ;AAG9C,QAAI,UAAU,QAAQ;AACpB,YAAM,OAAO,SAAS,cAAc,KAAK;AACzC,WAAK,YAAY,kBAAkB,WAAW,MAAM,WAAW,EAAE;AACjE,WAAK,QAAQ,YAAY,IAAI;AAAA,IAC/B;AAGA,UAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,YAAQ,YAAY,aAAa,WAAW,MAAM,WAAW,EAAE;AAC/D,YAAQ,aAAa,cAAc,mBAAmB;AACtD,YAAQ,aAAa,iBAAiB,OAAO;AAC7C,YAAQ,YAAY,UAAU,SAAS,WAAW;AAClD,SAAK,QAAQ,YAAY,OAAO;AAGhC,UAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,UAAM,YAAY,WAAW,WAAW,MAAM,WAAW,EAAE;AAC3D,UAAM,aAAa,aAAa,OAAO;AACvC,UAAM,aAAa,QAAQ,QAAQ;AACnC,UAAM,aAAa,cAAc,SAAS;AAC1C,UAAM,YAAY,KAAK,gBAAgB,WAAW,QAAQ,KAAK;AAC/D,SAAK,QAAQ,YAAY,KAAK;AAAA,EAChC;AAAA,EAEQ,gBAAgB,WAAmB,QAAgB,OAAsB;AAC/E,UAAM,YAAgB,KAAK,QAAQ,MAAM;AACzC,UAAM,aAAgB;AAEtB,QAAI,UAAU,QAAQ;AACpB,YAAMC,iBAAgB,YAClB,mCAAmC,WAAW,SAAS,CAAC,UAAU,WAAW,SAAS,CAAC,OACvF;AACJ,aAAO;AAAA;AAAA;AAAA;AAAA,qCAIwBA,cAAa;AAAA;AAAA;AAAA;AAAA,uCAIX,WAAW,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6DAMC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gEAOP,SAAS;AAAA;AAAA;AAAA,UAG/D,UAAU;AAAA;AAAA,IAEhB;AAEA,QAAI,UAAU,SAAS;AACrB,YAAMA,iBAAgB,YAClB,mCAAmC,WAAW,SAAS,CAAC,UAAU,WAAW,SAAS,CAAC,OACvF;AACJ,aAAO;AAAA;AAAA;AAAA,qCAGwBA,cAAa;AAAA;AAAA;AAAA,uCAGX,WAAW,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6DAMC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kEAYL,SAAS;AAAA;AAAA;AAAA;AAAA,UAIjE,UAAU;AAAA;AAAA,IAEhB;AAGA,UAAM,gBAAgB,YAClB,mCAAmC,WAAW,SAAS,CAAC,UAAU,WAAW,SAAS,CAAC,OACvF;AACJ,WAAO;AAAA;AAAA;AAAA,mCAGwB,aAAa;AAAA;AAAA;AAAA;AAAA,qCAIX,WAAW,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2DAMC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8DAOP,SAAS;AAAA;AAAA;AAAA,QAG/D,UAAU;AAAA;AAAA,EAEhB;AAAA,EAEQ,kBAAkB,OAAsB;AAC9C,QAAI,UAAU,SAAS;AACrB,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA,0CAK6B,YAAY;AAAA;AAAA,IAElD;AACA,QAAI,UAAU,OAAQ,QAAO;AAC7B,WAAO;AAAA,EACT;AAAA,EAEQ,cAAoB;AAC1B,UAAM,SAAS,KAAK;AAEpB,UAAM,UAAW,OAAO,cAAc,aAAa;AACnD,UAAM,WAAW,OAAO,cAAc,WAAW;AACjD,UAAM,QAAW,OAAO,cAAc,WAAW;AACjD,UAAM,UAAW,OAAO,cAAc,UAAU;AAChD,UAAM,QAAW,OAAO,cAAc,WAAW;AAGjD,QAAI,KAAK,WAAW,SAAS;AAC3B,cAAQ,YAAY,KAAK,kBAAkB,OAAO;AAAA,IACpD;AAEA,YAAQ,iBAAiB,SAAS,MAAM;AACtC,UAAI,KAAK,SAAS;AAChB,aAAK,OAAO;AAAA,MACd,OAAO;AACL,aAAK,MAAM;AAAA,MACb;AAAA,IACF,CAAC;AACD,aAAS,iBAAiB,SAAS,MAAM,KAAK,OAAO,CAAC;AACtD,YAAQ,iBAAiB,SAAS,MAAM,KAAK,QAAQ,CAAC;AAEtD,UAAM,iBAAiB,WAAW,CAAC,MAAM;AACvC,UAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AAAE,UAAE,eAAe;AAAG,aAAK,QAAQ;AAAA,MAAG;AAAA,IAC9E,CAAC;AAGD,UAAM,iBAAiB,SAAS,MAAM;AACpC,cAAQ,UAAU,OAAO,UAAU,MAAM,MAAM,KAAK,EAAE,SAAS,CAAC;AAAA,IAClE,CAAC;AAED,UAAM,iBAAiB,WAAW,CAAC,MAAM;AACvC,UAAI,EAAE,QAAQ,SAAU,MAAK,OAAO;AAAA,IACtC,CAAC;AAGD,UAAM,iBAAiB,WAAW,CAAC,MAAqB;AACtD,UAAI,EAAE,QAAQ,MAAO;AACrB,YAAM,YAAY,MAAM;AAAA,QACtB,MAAM;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AACA,UAAI,UAAU,WAAW,EAAG;AAC5B,YAAM,QAAS,UAAU,CAAC;AAC1B,YAAM,OAAS,UAAU,UAAU,SAAS,CAAC;AAC7C,YAAM,SAAS,OAAO;AACtB,UAAI,EAAE,UAAU;AACd,YAAI,WAAW,OAAO;AAAE,YAAE,eAAe;AAAG,eAAK,MAAM;AAAA,QAAG;AAAA,MAC5D,OAAO;AACL,YAAI,WAAW,MAAM;AAAE,YAAE,eAAe;AAAG,gBAAM,MAAM;AAAA,QAAG;AAAA,MAC5D;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,QAAuB;AACnC,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AAEf,UAAM,UAAU,KAAK,QAAQ,cAAc,aAAa;AACxD,UAAM,QAAU,KAAK,QAAQ,cAAc,WAAW;AAEtD,YAAQ,aAAa,iBAAiB,MAAM;AAC5C,UAAM,aAAa,aAAa,MAAM;AACtC,UAAM,gBAAgB,cAAc;AAEpC,QAAI,KAAK,UAAU,WAAW,GAAG;AAC/B,YAAM,WAAW,KAAK,QAAQ,MAAM,YAAY;AAChD,WAAK,eAAe,EAAE,MAAM,OAAO,MAAM,SAAS,CAAC;AAAA,IACrD;AAEA,SAAK,aAAa;AAElB,UAAM,QAAQ,KAAK,QAAQ,cAAc,WAAW;AACpD,eAAW,MAAM,MAAM,MAAM,GAAG,EAAE;AAElC,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,cAAc;AAC5C,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEQ,SAAe;AACrB,QAAI,CAAC,KAAK,QAAS;AAEnB,UAAM,UAAU,KAAK,QAAQ,cAAc,aAAa;AACxD,UAAM,QAAU,KAAK,QAAQ,cAAc,WAAW;AAEtD,UAAM,aAAa,gBAAgB,MAAM;AACzC,UAAM;AAAA,MACJ;AAAA,MACA,MAAM;AACJ,aAAK,UAAU;AACf,cAAM,aAAa,aAAa,OAAO;AACvC,cAAM,gBAAgB,cAAc;AACpC,gBAAQ,aAAa,iBAAiB,OAAO;AAC7C,gBAAQ,MAAM;AAAA,MAChB;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AAAA,EACF;AAAA,EAEA,MAAc,eAA8B;AAC1C,QAAI,CAAC,KAAK,QAAQ,UAAU;AAC1B,WAAK,eAAe;AACpB,cAAQ,KAAK,8EAA8E;AAC3F;AAAA,IACF;AACA,QAAI;AACF,WAAK,SAAS,MAAM,WAAW,KAAK,QAAQ,QAAQ;AAAA,IACtD,QAAQ;AACN,WAAK,eAAe;AACpB,WAAK,eAAe;AAAA,QAClB,MAAM;AAAA,QACN,MAAM;AAAA,MACR,CAAC;AACD;AAAA,IACF;AAEA,QAAI;AACF,WAAK,YAAY,IAAI,gBAAgB;AACrC,YAAM,KAAK,UAAU,KAAK,KAAK,QAAQ,MAAM,KAAK;AAClD,WAAK,eAAe;AAAA,IACtB,QAAQ;AACN,WAAK,YAAY,IAAI,gBAAgB;AACrC,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAc,UAAyB;AACrC,UAAM,QAAQ,KAAK,QAAQ,cAAc,WAAW;AACpD,UAAM,OAAQ,MAAM,MAAM,KAAK;AAC/B,QAAI,CAAC,KAAM;AAEX,UAAM,cAAe,KAAK,IAAI;AAE9B,UAAM,QAAQ;AACd,UAAM,UAAU,KAAK,QAAQ,cAAc,UAAU;AACrD,YAAQ,UAAU,OAAO,QAAQ;AAEjC,SAAK,kBAAkB;AACvB,SAAK,eAAe,EAAE,MAAM,QAAQ,KAAK,CAAC;AAC1C,SAAK,YAAY;AAEjB,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,cAAc;AAC5C,YAAM,KAAK,eAAe;AAAA,IAC5B;AAEA,QAAI;AACJ,QAAI,aAAa;AACjB,QAAI,OAA6B;AAEjC,QAAI,KAAK,gBAAgB,CAAC,KAAK,QAAQ;AACrC,gBAAa,YAAY,KAAK,QAAQ,KAAK;AAC3C,mBAAa;AAAA,IACf,OAAO;AACL,UAAI;AACF,cAAM,MAAM,MAAM,SAAS,MAAM,KAAK,QAAQ,KAAK,WAAY,KAAK,QAAQ,MAAM;AAClF,eAAO,IAAI;AACX,YAAI,IAAI,QAAQ,SAAS,GAAG;AAC1B,oBAAU,YAAY,IAAI,OAAO;AAAA,QACnC,OAAO;AACL,oBAAa,YAAY,KAAK,QAAQ,KAAK;AAC3C,uBAAa;AAAA,QACf;AAAA,MACF,QAAQ;AACN,kBAAa,YAAY,KAAK,QAAQ,KAAK;AAC3C,qBAAa;AAAA,MACf;AAAA,IACF;AAEA,UAAM,UAAY,KAAK,IAAI,IAAI;AAE/B,UAAM,YAAa,SAAS,aAAa,aAAc,MAAM;AAC7D,UAAM,oBAAoB,YAAY,KAAK,OAAO,IAAI;AACtD,UAAM,YAAY,oBAAoB;AACtC,QAAI,YAAY,EAAG,OAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAEpE,SAAK,YAAY;AACjB,SAAK,eAAe,EAAE,MAAM,OAAO,MAAM,SAAS,WAAW,CAAC;AAAA,EAChE;AAAA,EAEQ,iBAAgC;AACtC,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,QAAQ,MAAM;AAClB,YAAI,KAAK,gBAAgB,KAAK,aAAc,SAAQ;AAAA,YAC/C,YAAW,OAAO,GAAG;AAAA,MAC5B;AACA,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA,EAEQ,eAAe,KAA8D;AACnF,UAAM,SAAS,KAAK,QAAQ,cAAc,YAAY;AACtD,UAAM,QAAS,KAAK;AACpB,UAAM,KAAS,OAAO,EAAE,KAAK,WAAW;AAExC,SAAK,UAAU,KAAK;AAAA,MAClB;AAAA,MACA,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,YAAY,IAAI;AAAA,MAChB,WAAW,oBAAI,KAAK;AAAA,IACtB,CAAC;AAED,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,KAAQ;AAEb,QAAI,UAAU,WAAW,IAAI,SAAS,OAAO;AAC3C,WAAK,YAAY;AACjB,YAAM,gBAAgB,IAAI,aACtB,6DACA;AACJ,YAAM,YAAY,WAAW,KAAK,QAAQ,MAAM,QAAQ,SAAS;AACjE,WAAK,YAAY;AAAA;AAAA;AAAA;AAAA,wCAIiB,SAAS;AAAA;AAAA;AAAA,YAGrC,aAAa;AAAA,uCACc,WAAW,IAAI,IAAI,CAAC;AAAA;AAAA;AAAA,IAGvD,OAAO;AACL,WAAK,YAAY,eAAe,IAAI,IAAI;AACxC,UAAI,QAAQ;AACZ,UAAI,IAAI,YAAY;AAClB,iBAAS;AAAA,MACX;AACA,eAAS,yBAAyB,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC;AACnE,eAAS;AACT,WAAK,YAAY;AAAA,IACnB;AAEA,WAAO,YAAY,IAAI;AACvB,WAAO,YAAY,OAAO;AAC1B,SAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,cAAoB;AAC1B,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AAEjB,UAAM,SAAY,KAAK,QAAQ,cAAc,YAAY;AACzD,UAAM,QAAY,KAAK;AACvB,UAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,cAAU,KAAQ;AAElB,QAAI,UAAU,SAAS;AACrB,gBAAU,YAAY;AACtB,gBAAU,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMxB,OAAO;AACL,gBAAU,YAAY;AACtB,gBAAU,YAAY;AAAA,IACxB;AAEA,WAAO,YAAY,SAAS;AAC5B,WAAO,YAAY,OAAO;AAAA,EAC5B;AAAA,EAEQ,cAAoB;AAC1B,SAAK,YAAY;AACjB,UAAM,YAAY,KAAK,QAAQ,eAAe,qBAAqB;AACnE,QAAI,UAAW,WAAU,OAAO;AAAA,EAClC;AAAA,EAEQ,eAAqB;AAC3B,UAAM,YAAY,KAAK,QAAQ,cAAc,WAAW;AACxD,cAAU,YAAY;AAEtB,UAAM,WAAW,aAAa,KAAK,UAAU,CAAC,GAAG,KAAK,QAAQ,MAAM,cAAc;AAClF,UAAM,aAAa,IAAI;AAAA,MACrB,KAAK,UACF,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,EAC/B,IAAI,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,CAAC;AAAA,IAC3C;AACA,UAAM,QAAQ,SAAS,OAAO,CAAC,SAAS,CAAC,WAAW,IAAI,KAAK,YAAY,EAAE,KAAK,CAAC,CAAC;AAElF,QAAI,MAAM,WAAW,GAAG;AACtB,gBAAU,MAAM,UAAU;AAC1B;AAAA,IACF;AACA,cAAU,MAAM,UAAU;AAE1B,UAAM,QAAQ,KAAK;AACnB,QAAI,KAAK,iBAAiB;AACxB,gBAAU,UAAU,IAAI,YAAY;AAAA,IACtC,OAAO;AACL,gBAAU,UAAU,OAAO,YAAY;AAAA,IACzC;AAEA,QAAI,UAAU,WAAW;AACvB,YAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,YAAM,YAAY;AAClB,YAAM,cAAc;AACpB,gBAAU,YAAY,KAAK;AAE3B,YAAM,MAAM,SAAS,cAAc,KAAK;AACxC,UAAI,YAAY;AAChB,gBAAU,YAAY,GAAG;AAEzB,iBAAW,YAAY,OAAO;AAC5B,cAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,YAAI,YAAY;AAChB,YAAI,cAAc;AAClB,YAAI,iBAAiB,SAAS,MAAM,KAAK,YAAY,QAAQ,CAAC;AAC9D,YAAI,YAAY,GAAG;AAAA,MACrB;AACA;AAAA,IACF;AAEA,QAAI,UAAU,UAAU,CAAC,KAAK,iBAAiB;AAC7C,YAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,YAAM,YAAY;AAClB,YAAM,cAAc;AACpB,gBAAU,YAAY,KAAK;AAAA,IAC7B;AAEA,eAAW,YAAY,OAAO;AAC5B,YAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,UAAI,YAAY;AAChB,UAAI,UAAU,QAAQ;AACpB,YAAI,YAAY,GAAG,YAAY,IAAI,WAAW,QAAQ,CAAC;AAAA,MACzD,OAAO;AACL,YAAI,cAAc;AAAA,MACpB;AACA,UAAI,iBAAiB,SAAS,MAAM,KAAK,YAAY,QAAQ,CAAC;AAC9D,gBAAU,YAAY,GAAG;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,YAAY,UAAwB;AAC1C,UAAM,QAAQ,KAAK,QAAQ,cAAc,WAAW;AACpD,UAAM,QAAQ;AACd,SAAK,QAAQ;AAAA,EACf;AACF;AAEA,SAAS,WAAW,KAAqB;AACvC,SAAO,IACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO,EACrB,QAAQ,OAAO,MAAM;AAC1B;;;APjiBA,IAAM,cAAc;AAIpB,SAAS,aAAa,QAA8B;AAClD,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,MAAI,CAAC,OAAO,YAAY,OAAO,OAAO,aAAa,UAAU;AAC3D,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,SAAS,OAAO,OAAO,UAAU,UAAU;AACrD,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AACA,MAAI,CAAC,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,SAAS,UAAU;AAC/D,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACF;AAEA,SAAS,KAAK,QAA8B;AAG1C,eAAa,MAAM;AAEnB,MAAI;AACF,QAAI,OAAO,mBAAmB,YAAa;AAC3C,QAAI,OAAO,aAAa,YAAa;AAErC,QAAI,CAAC,eAAe,IAAI,WAAW,GAAG;AACpC,qBAAe,OAAO,aAAa,cAAc;AAAA,IACnD;AAEA,UAAM,WAAW,SAAS,cAAc,WAAW;AACnD,QAAI,UAAU;AACZ,eAAS,KAAK,MAAM;AACpB;AAAA,IACF;AAEA,UAAM,KAAK,SAAS,cAAc,WAAW;AAC7C,aAAS,KAAK,YAAY,EAAE;AAC5B,OAAG,KAAK,MAAM;AAAA,EAChB,SAAS,KAAK;AAEZ,YAAQ,KAAK,oCAAoC,GAAG;AAAA,EACtD;AACF;AAGA,IAAI,OAAO,WAAW,aAAa;AACjC,EAAC,OAAyD,WAAW,EAAE,KAAK;AAC9E;","names":["results","avatarContent"]}
@@ -1,5 +1,6 @@
1
1
  type SageDeskModel = 'all-MiniLM-L6-v2' | 'bge-small-en-v1-5' | 'paraphrase-multilingual-MiniLM-L12-v2' | 'all-mpnet-base-v2';
2
2
  type Theme = 'classic' | 'light' | 'dark';
3
+ type SageDeskMode = 'local' | 'llm';
3
4
  interface AgentConfig {
4
5
  name: string;
5
6
  model?: SageDeskModel;
@@ -11,7 +12,6 @@ interface AgentConfig {
11
12
  position?: 'bottom-right' | 'bottom-left';
12
13
  avatarUrl?: string;
13
14
  contactUrl?: string;
14
- poweredBy?: boolean;
15
15
  suggestedChips?: string[];
16
16
  }
17
17
  interface SearchConfig {
@@ -19,7 +19,9 @@ interface SearchConfig {
19
19
  topK?: number;
20
20
  }
21
21
  interface SageDeskConfig {
22
- indexUrl: string;
22
+ mode?: SageDeskMode;
23
+ indexUrl?: string;
24
+ endpoint?: string;
23
25
  agent: AgentConfig;
24
26
  search?: SearchConfig;
25
27
  }
@@ -1,5 +1,6 @@
1
1
  type SageDeskModel = 'all-MiniLM-L6-v2' | 'bge-small-en-v1-5' | 'paraphrase-multilingual-MiniLM-L12-v2' | 'all-mpnet-base-v2';
2
2
  type Theme = 'classic' | 'light' | 'dark';
3
+ type SageDeskMode = 'local' | 'llm';
3
4
  interface AgentConfig {
4
5
  name: string;
5
6
  model?: SageDeskModel;
@@ -11,7 +12,6 @@ interface AgentConfig {
11
12
  position?: 'bottom-right' | 'bottom-left';
12
13
  avatarUrl?: string;
13
14
  contactUrl?: string;
14
- poweredBy?: boolean;
15
15
  suggestedChips?: string[];
16
16
  }
17
17
  interface SearchConfig {
@@ -19,7 +19,9 @@ interface SearchConfig {
19
19
  topK?: number;
20
20
  }
21
21
  interface SageDeskConfig {
22
- indexUrl: string;
22
+ mode?: SageDeskMode;
23
+ indexUrl?: string;
24
+ endpoint?: string;
23
25
  agent: AgentConfig;
24
26
  search?: SearchConfig;
25
27
  }
@@ -778,17 +778,33 @@ function dotProduct(a, b) {
778
778
  for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
779
779
  return dot;
780
780
  }
781
+ function insertSorted(arr, item, maxLen) {
782
+ arr.push(item);
783
+ let i = arr.length - 1;
784
+ while (i > 0 && arr[i - 1].score < arr[i].score) {
785
+ const tmp = arr[i - 1];
786
+ arr[i - 1] = arr[i];
787
+ arr[i] = tmp;
788
+ i--;
789
+ }
790
+ if (arr.length > maxLen) arr.pop();
791
+ }
781
792
  function search(queryVector, index, topK = 3, minScore = 0.42) {
782
793
  const results = [];
783
794
  for (const chunk of index) {
784
795
  const score = dotProduct(queryVector, chunk.vector384);
785
796
  if (score < minScore) continue;
786
797
  if (results.length < topK) {
787
- results.push({ chunk, score });
788
- results.sort((a, b) => b.score - a.score);
798
+ insertSorted(results, { chunk, score }, topK);
789
799
  } else if (score > results[topK - 1].score) {
790
800
  results[topK - 1] = { chunk, score };
791
- results.sort((a, b) => b.score - a.score);
801
+ let i = topK - 1;
802
+ while (i > 0 && results[i - 1].score < results[i].score) {
803
+ const tmp = results[i - 1];
804
+ results[i - 1] = results[i];
805
+ results[i] = tmp;
806
+ i--;
807
+ }
792
808
  }
793
809
  }
794
810
  return results;
@@ -799,15 +815,23 @@ function keywordSearch(query, index, topK = 3) {
799
815
  const results = [];
800
816
  for (const chunk of index) {
801
817
  const chunkLower = chunk.textLower || chunk.text.toLowerCase();
802
- const matchCount = terms.filter((t) => chunkLower.includes(t)).length;
818
+ let matchCount = 0;
819
+ for (const t of terms) {
820
+ if (chunkLower.includes(t)) matchCount++;
821
+ }
803
822
  const score = matchCount / terms.length;
804
823
  if (score <= 0) continue;
805
824
  if (results.length < topK) {
806
- results.push({ chunk, score });
807
- results.sort((a, b) => b.score - a.score);
825
+ insertSorted(results, { chunk, score }, topK);
808
826
  } else if (score > results[topK - 1].score) {
809
827
  results[topK - 1] = { chunk, score };
810
- results.sort((a, b) => b.score - a.score);
828
+ let i = topK - 1;
829
+ while (i > 0 && results[i - 1].score < results[i].score) {
830
+ const tmp = results[i - 1];
831
+ results[i - 1] = results[i];
832
+ results[i] = tmp;
833
+ i--;
834
+ }
811
835
  }
812
836
  }
813
837
  return results;
@@ -967,9 +991,8 @@ var SageDeskWidget = class extends HTMLElement {
967
991
  this._shadow.appendChild(panel);
968
992
  }
969
993
  _buildPanelHTML(agentName, accent, theme) {
970
- const showPoweredBy = this._config.agent.poweredBy !== false;
971
994
  const avatarUrl = this._config.agent.avatarUrl;
972
- const footerHTML = showPoweredBy ? `<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>` : "";
995
+ const footerHTML = `<div class="sd-footer">Powered by <a class="sd-footer-link" href="https://github.com/mzeeshanwahid/sagedesk" target="_blank" rel="noopener">sagedesk</a></div>`;
973
996
  if (theme === "dark") {
974
997
  const avatarContent2 = avatarUrl ? `<img class="sd-avatar-img" src="${escapeHtml(avatarUrl)}" alt="${escapeHtml(agentName)}">` : ICON_BOT_SM;
975
998
  return `
@@ -1164,6 +1187,11 @@ var SageDeskWidget = class extends HTMLElement {
1164
1187
  );
1165
1188
  }
1166
1189
  async _startEngine() {
1190
+ if (!this._config.indexUrl) {
1191
+ this._engineError = "indexUrl is required for the vanilla widget (local mode only).";
1192
+ console.warn("[sagedesk] indexUrl is required. Run `npx sagedesk build` and pass indexUrl.");
1193
+ return;
1194
+ }
1167
1195
  try {
1168
1196
  this._index = await fetchIndex(this._config.indexUrl);
1169
1197
  } catch {