@bootstrapware/answers 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +17 -25
- package/CHANGELOG.md +14 -0
- package/README.md +30 -3
- package/dist/answers.iife.js +9 -9
- package/dist/answers.iife.js.map +1 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +91 -10
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +17 -1
- package/dist/server.js +373 -48
- package/dist/server.js.map +1 -1
- package/dist/styles.css +33 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,15 @@ type AnswersProps = {
|
|
|
49
49
|
pageContext?: AnswersPageContext;
|
|
50
50
|
/** Empty-state copy. Omit it and the widget shows "Ask a question this site already answers." */
|
|
51
51
|
greeting?: string;
|
|
52
|
+
/** Clickable starter questions. Each chip asks that question. */
|
|
53
|
+
suggestedQuestions?: string[];
|
|
54
|
+
/**
|
|
55
|
+
* Keyboard shortcut that opens the widget and focuses the question field.
|
|
56
|
+
* `mod` is Cmd on macOS and Ctrl elsewhere. Example: `mod+k`.
|
|
57
|
+
*/
|
|
58
|
+
shortcut?: string;
|
|
59
|
+
/** Hosted API origin. Omit it and `createHostedAdapter` uses `https://answers.bootstrapware.co`. */
|
|
60
|
+
apiBaseUrl?: string;
|
|
52
61
|
onFallback?: (result: AnswersResult) => void;
|
|
53
62
|
/** Layout hook only. Skin the widget with the `--bsw-an-*` tokens. */
|
|
54
63
|
className?: string;
|
|
@@ -73,8 +82,17 @@ declare const LOCAL_CITATIONS: Record<Topic, AnswersCitation>;
|
|
|
73
82
|
declare function createLocalAdapter(): AnswersAdapter;
|
|
74
83
|
/** Returns the caller adapter. Question text stays on the customer backend. */
|
|
75
84
|
declare function createByoAdapter(impl: AnswersAdapter): AnswersAdapter;
|
|
85
|
+
/**
|
|
86
|
+
* Posts to Hosted `POST /api/v1/ask?appId=` with the publishable key.
|
|
87
|
+
* The widget uses this when `appId` and `publishableKey` are set and `adapter` is omitted.
|
|
88
|
+
*/
|
|
89
|
+
declare function createHostedAdapter(options: {
|
|
90
|
+
appId: string;
|
|
91
|
+
publishableKey: string;
|
|
92
|
+
apiBaseUrl?: string;
|
|
93
|
+
}): AnswersAdapter;
|
|
76
94
|
|
|
77
95
|
declare function mount(el: Element, props?: AnswersProps): () => void;
|
|
78
96
|
declare function unmount(el: Element): void;
|
|
79
97
|
|
|
80
|
-
export { Answers, type AnswersAdapter, type AnswersAskInput, type AnswersCitation, type AnswersFeedbackInput, type AnswersOutcome, type AnswersPageContext, type AnswersPresentation, type AnswersProps, type AnswersResult, type AnswersTheme, LOCAL_CITATIONS, LOCAL_FALLBACK, LOCAL_PROMPTS, QUESTION_MAX, WIDGET_ROOT_CLASS, createByoAdapter, createLocalAdapter, mount, unmount };
|
|
98
|
+
export { Answers, type AnswersAdapter, type AnswersAskInput, type AnswersCitation, type AnswersFeedbackInput, type AnswersOutcome, type AnswersPageContext, type AnswersPresentation, type AnswersProps, type AnswersResult, type AnswersTheme, LOCAL_CITATIONS, LOCAL_FALLBACK, LOCAL_PROMPTS, QUESTION_MAX, WIDGET_ROOT_CLASS, createByoAdapter, createHostedAdapter, createLocalAdapter, mount, unmount };
|
package/dist/index.js
CHANGED
|
@@ -200,6 +200,30 @@ function createLocalAdapter() {
|
|
|
200
200
|
function createByoAdapter(impl) {
|
|
201
201
|
return impl;
|
|
202
202
|
}
|
|
203
|
+
var HOSTED_ORIGIN = "https://answers.bootstrapware.co";
|
|
204
|
+
function createHostedAdapter(options) {
|
|
205
|
+
const base = (options.apiBaseUrl ?? HOSTED_ORIGIN).replace(/\/$/, "");
|
|
206
|
+
return {
|
|
207
|
+
async ask(input) {
|
|
208
|
+
const response = await fetch(`${base}/api/v1/ask?appId=${encodeURIComponent(options.appId)}`, {
|
|
209
|
+
method: "POST",
|
|
210
|
+
headers: {
|
|
211
|
+
Authorization: `Bearer ${options.publishableKey}`,
|
|
212
|
+
"Content-Type": "application/json"
|
|
213
|
+
},
|
|
214
|
+
body: JSON.stringify({
|
|
215
|
+
sessionId: input.sessionId,
|
|
216
|
+
question: input.question,
|
|
217
|
+
pageContext: input.pageContext
|
|
218
|
+
})
|
|
219
|
+
});
|
|
220
|
+
const json = await response.json().catch(() => ({}));
|
|
221
|
+
if (!response.ok) throw new Error(json.error?.message ?? "Could not ask.");
|
|
222
|
+
if (!json.data) throw new Error("Could not ask.");
|
|
223
|
+
return json.data;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
203
227
|
|
|
204
228
|
// src/frame.tsx
|
|
205
229
|
import { useEffect, useState } from "react";
|
|
@@ -207,6 +231,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
207
231
|
function EmbedFrame({
|
|
208
232
|
presentation = "section",
|
|
209
233
|
defaultOpen = false,
|
|
234
|
+
openSignal = 0,
|
|
210
235
|
label,
|
|
211
236
|
className,
|
|
212
237
|
theme,
|
|
@@ -219,6 +244,11 @@ function EmbedFrame({
|
|
|
219
244
|
setOpenKeySeen(openKey);
|
|
220
245
|
setOpen(defaultOpen);
|
|
221
246
|
}
|
|
247
|
+
const [signalSeen, setSignalSeen] = useState(openSignal);
|
|
248
|
+
if (openSignal !== signalSeen) {
|
|
249
|
+
setSignalSeen(openSignal);
|
|
250
|
+
if (openSignal > 0) setOpen(true);
|
|
251
|
+
}
|
|
222
252
|
const launcher = presentation === "launcher";
|
|
223
253
|
const floating = launcher || presentation === "window";
|
|
224
254
|
useEffect(() => {
|
|
@@ -403,6 +433,23 @@ function CitationSources({ citations }) {
|
|
|
403
433
|
] }, citation.id)) })
|
|
404
434
|
] });
|
|
405
435
|
}
|
|
436
|
+
function isMac() {
|
|
437
|
+
if (typeof navigator === "undefined") return false;
|
|
438
|
+
return /Mac|iPhone|iPad/i.test(navigator.userAgent) || /Mac/i.test(navigator.platform ?? "");
|
|
439
|
+
}
|
|
440
|
+
function matchesShortcut(event, spec, mac = isMac()) {
|
|
441
|
+
const parts = spec.toLowerCase().split("+").map((part) => part.trim()).filter(Boolean);
|
|
442
|
+
const key = parts[parts.length - 1];
|
|
443
|
+
if (!key || event.key.toLowerCase() !== key) return false;
|
|
444
|
+
const mods = new Set(parts.slice(0, -1));
|
|
445
|
+
const wantCtrl = mods.has("ctrl") || mods.has("mod") && !mac;
|
|
446
|
+
const wantMeta = mods.has("meta") || mods.has("mod") && mac;
|
|
447
|
+
if (wantCtrl !== event.ctrlKey) return false;
|
|
448
|
+
if (wantMeta !== event.metaKey) return false;
|
|
449
|
+
if (mods.has("alt") !== event.altKey) return false;
|
|
450
|
+
if (mods.has("shift") !== event.shiftKey) return false;
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
406
453
|
function pageContextForAsk(explicit) {
|
|
407
454
|
if (explicit) {
|
|
408
455
|
const url = typeof explicit.url === "string" ? explicit.url : void 0;
|
|
@@ -416,15 +463,18 @@ function Answers(props) {
|
|
|
416
463
|
const presentation = props.presentation ?? "launcher";
|
|
417
464
|
const sessionId = useSessionId(props.appId);
|
|
418
465
|
const localRef = useRef(null);
|
|
419
|
-
const
|
|
420
|
-
const
|
|
466
|
+
const hosted = !props.adapter && props.appId && props.publishableKey ? createHostedAdapter({ appId: props.appId, publishableKey: props.publishableKey, apiBaseUrl: props.apiBaseUrl }) : null;
|
|
467
|
+
const adapter = props.adapter ?? hosted ?? (props.publishableKey ? null : localRef.current ??= createLocalAdapter());
|
|
468
|
+
const mode = props.adapter ? "byo" : hosted ? "hosted" : props.publishableKey ? "unwired" : "local";
|
|
421
469
|
const [draft, setDraft] = useState2("");
|
|
470
|
+
const [openSignal, setOpenSignal] = useState2(0);
|
|
422
471
|
const [pending, setPending] = useState2(false);
|
|
423
472
|
const [pendingQuestion, setPendingQuestion] = useState2(null);
|
|
424
473
|
const [error, setError] = useState2(null);
|
|
425
474
|
const [turns, setTurns] = useState2([]);
|
|
426
475
|
const pendingRef = useRef(false);
|
|
427
476
|
const logRef = useRef(null);
|
|
477
|
+
const questionRef = useRef(null);
|
|
428
478
|
const feedbackLock = useRef(/* @__PURE__ */ new Set());
|
|
429
479
|
const onFallbackRef = useRef(props.onFallback);
|
|
430
480
|
onFallbackRef.current = props.onFallback;
|
|
@@ -433,22 +483,36 @@ function Answers(props) {
|
|
|
433
483
|
if (!log) return;
|
|
434
484
|
log.scrollTop = log.scrollHeight;
|
|
435
485
|
}, [turns, pending, pendingQuestion, error]);
|
|
486
|
+
useEffect2(() => {
|
|
487
|
+
const spec = props.shortcut?.trim();
|
|
488
|
+
if (!spec) return;
|
|
489
|
+
function onKey(event) {
|
|
490
|
+
if (!matchesShortcut(event, spec)) return;
|
|
491
|
+
event.preventDefault();
|
|
492
|
+
setOpenSignal((current) => current + 1);
|
|
493
|
+
}
|
|
494
|
+
window.addEventListener("keydown", onKey);
|
|
495
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
496
|
+
}, [props.shortcut]);
|
|
497
|
+
useEffect2(() => {
|
|
498
|
+
if (!openSignal) return;
|
|
499
|
+
questionRef.current?.focus();
|
|
500
|
+
}, [openSignal]);
|
|
436
501
|
const trimmed = draft.trim();
|
|
437
502
|
const length = codePointLength(trimmed);
|
|
438
503
|
const overLimit = length > QUESTION_MAX;
|
|
439
504
|
const inputId = useId();
|
|
440
|
-
async function
|
|
441
|
-
event.preventDefault();
|
|
505
|
+
async function askQuestion(raw) {
|
|
442
506
|
if (!adapter || pendingRef.current) return;
|
|
443
|
-
|
|
507
|
+
const question = raw.trim();
|
|
508
|
+
if (!question) {
|
|
444
509
|
setError("Enter a question.");
|
|
445
510
|
return;
|
|
446
511
|
}
|
|
447
|
-
if (
|
|
512
|
+
if (codePointLength(question) > QUESTION_MAX) {
|
|
448
513
|
setError(LIMIT_MESSAGE);
|
|
449
514
|
return;
|
|
450
515
|
}
|
|
451
|
-
const question = trimmed;
|
|
452
516
|
const pageContext = pageContextForAsk(props.pageContext);
|
|
453
517
|
pendingRef.current = true;
|
|
454
518
|
setPending(true);
|
|
@@ -456,8 +520,8 @@ function Answers(props) {
|
|
|
456
520
|
setDraft("");
|
|
457
521
|
setError(null);
|
|
458
522
|
try {
|
|
459
|
-
const
|
|
460
|
-
const result = sanitizeResult(
|
|
523
|
+
const rawResult = await adapter.ask({ sessionId, question, pageContext });
|
|
524
|
+
const result = sanitizeResult(rawResult);
|
|
461
525
|
if (!result) {
|
|
462
526
|
setDraft(question);
|
|
463
527
|
setError(ERROR_MESSAGE);
|
|
@@ -474,6 +538,10 @@ function Answers(props) {
|
|
|
474
538
|
setPendingQuestion(null);
|
|
475
539
|
}
|
|
476
540
|
}
|
|
541
|
+
function onSubmit(event) {
|
|
542
|
+
event.preventDefault();
|
|
543
|
+
void askQuestion(draft);
|
|
544
|
+
}
|
|
477
545
|
async function onFeedback(answerId, helpful) {
|
|
478
546
|
if (!adapter?.feedback || feedbackLock.current.has(answerId)) return;
|
|
479
547
|
const current = turns.find((turn) => turn.result.answerId === answerId);
|
|
@@ -549,6 +617,17 @@ function Answers(props) {
|
|
|
549
617
|
] }) })
|
|
550
618
|
] }) : null
|
|
551
619
|
] }) }),
|
|
620
|
+
props.suggestedQuestions?.length && turns.length === 0 && !pendingQuestion ? /* @__PURE__ */ jsx3("div", { className: "bsw-an__suggestions", children: props.suggestedQuestions.map((question) => /* @__PURE__ */ jsx3(
|
|
621
|
+
"button",
|
|
622
|
+
{
|
|
623
|
+
type: "button",
|
|
624
|
+
className: "bsw-an__chip",
|
|
625
|
+
disabled: !adapter || pending,
|
|
626
|
+
onClick: () => void askQuestion(question),
|
|
627
|
+
children: question
|
|
628
|
+
},
|
|
629
|
+
question
|
|
630
|
+
)) }) : null,
|
|
552
631
|
/* @__PURE__ */ jsxs2("form", { className: "bsw-an__composer", autoComplete: "off", onSubmit: (event) => void onSubmit(event), children: [
|
|
553
632
|
error ? /* @__PURE__ */ jsx3("p", { className: "bsw-an__error", role: "alert", children: error }) : null,
|
|
554
633
|
/* @__PURE__ */ jsx3("label", { className: "bsw-an__label", htmlFor: inputId, children: "Question" }),
|
|
@@ -557,6 +636,7 @@ function Answers(props) {
|
|
|
557
636
|
"textarea",
|
|
558
637
|
{
|
|
559
638
|
id: inputId,
|
|
639
|
+
ref: questionRef,
|
|
560
640
|
className: "bsw-an__textarea",
|
|
561
641
|
rows: 2,
|
|
562
642
|
value: draft,
|
|
@@ -592,7 +672,7 @@ function Answers(props) {
|
|
|
592
672
|
}
|
|
593
673
|
);
|
|
594
674
|
if (presentation === "section") return panel;
|
|
595
|
-
return /* @__PURE__ */ jsx3(EmbedFrame, { presentation, defaultOpen: props.defaultOpen, label: "Answers", className: props.className, theme: props.theme, children: panel });
|
|
675
|
+
return /* @__PURE__ */ jsx3(EmbedFrame, { presentation, defaultOpen: props.defaultOpen, openSignal, label: "Answers", className: props.className, theme: props.theme, children: panel });
|
|
596
676
|
}
|
|
597
677
|
|
|
598
678
|
// src/mount.tsx
|
|
@@ -622,6 +702,7 @@ export {
|
|
|
622
702
|
QUESTION_MAX,
|
|
623
703
|
WIDGET_ROOT_CLASS,
|
|
624
704
|
createByoAdapter,
|
|
705
|
+
createHostedAdapter,
|
|
625
706
|
createLocalAdapter,
|
|
626
707
|
mount,
|
|
627
708
|
unmount
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Answers.tsx","../src/result.ts","../src/types.ts","../src/adapters.ts","../src/frame.tsx","../src/markdown.tsx","../src/session.ts","../src/mount.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type FormEvent } from \"react\";\nimport { createLocalAdapter } from \"./adapters\";\nimport { EmbedFrame } from \"./frame\";\nimport { renderAnswerMarkdown } from \"./markdown\";\nimport { codePointLength, sanitizeResult } from \"./result\";\nimport { readSessionId, sessionStorageKey } from \"./session\";\nimport {\n QUESTION_MAX,\n WIDGET_ROOT_CLASS,\n type AnswersAdapter,\n type AnswersCitation,\n type AnswersPageContext,\n type AnswersProps,\n type AnswersResult,\n} from \"./types\";\n\ntype Turn = {\n question: string;\n result: AnswersResult;\n feedback: \"idle\" | \"pending\" | \"done\" | \"error\";\n};\n\nconst LIMIT_MESSAGE = \"Questions are limited to 1,000 characters.\";\nconst ERROR_MESSAGE = \"Something went wrong. Your question is still here. Try again.\";\nconst FEEDBACK_ERROR = \"Feedback could not be saved. Try again.\";\n\nfunction useSessionId(appId?: string): string {\n const key = sessionStorageKey(appId);\n const [state, setState] = useState(() => ({\n key,\n id: readSessionId(typeof window === \"undefined\" ? null : window.sessionStorage, appId),\n }));\n if (state.key !== key) {\n setState({\n key,\n id: readSessionId(typeof window === \"undefined\" ? null : window.sessionStorage, appId),\n });\n }\n return state.id;\n}\n\nfunction sourcesLabel(count: number): string {\n return count === 1 ? \"1 source\" : `${count} sources`;\n}\n\nfunction CitationSources({ citations }: { citations: AnswersCitation[] }) {\n if (!citations.length) return null;\n return (\n <details className=\"bsw-an__sources-wrap\">\n <summary className=\"bsw-an__sources-toggle\">{sourcesLabel(citations.length)}</summary>\n <ul className=\"bsw-an__sources\">\n {citations.map((citation) => (\n <li key={citation.id} className=\"bsw-an__source\" data-citation-id={citation.id}>\n {citation.url ? (\n <a href={citation.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {citation.title}\n </a>\n ) : (\n <span className=\"bsw-an__source-title\">{citation.title}</span>\n )}\n {citation.excerpt ? <p className=\"bsw-an__excerpt\">{citation.excerpt}</p> : null}\n </li>\n ))}\n </ul>\n </details>\n );\n}\n\nfunction pageContextForAsk(explicit: AnswersPageContext | undefined): AnswersPageContext {\n if (explicit) {\n const url = typeof explicit.url === \"string\" ? explicit.url : undefined;\n const title = typeof explicit.title === \"string\" ? explicit.title : undefined;\n return { url, title };\n }\n if (typeof window === \"undefined\") return {};\n return { url: window.location.href, title: document.title };\n}\n\nexport function Answers(props: AnswersProps) {\n const presentation = props.presentation ?? \"launcher\";\n const sessionId = useSessionId(props.appId);\n const localRef = useRef<AnswersAdapter | null>(null);\n const adapter = props.adapter ?? (props.publishableKey ? null : (localRef.current ??= createLocalAdapter()));\n const mode = props.adapter ? \"byo\" : props.publishableKey ? \"unwired\" : \"local\";\n const [draft, setDraft] = useState(\"\");\n const [pending, setPending] = useState(false);\n const [pendingQuestion, setPendingQuestion] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [turns, setTurns] = useState<Turn[]>([]);\n const pendingRef = useRef(false);\n const logRef = useRef<HTMLDivElement>(null);\n const feedbackLock = useRef(new Set<string>());\n const onFallbackRef = useRef(props.onFallback);\n onFallbackRef.current = props.onFallback;\n\n useEffect(() => {\n const log = logRef.current;\n if (!log) return;\n log.scrollTop = log.scrollHeight;\n }, [turns, pending, pendingQuestion, error]);\n\n const trimmed = draft.trim();\n const length = codePointLength(trimmed);\n const overLimit = length > QUESTION_MAX;\n const inputId = useId();\n\n async function onSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n if (!adapter || pendingRef.current) return;\n if (!trimmed) {\n setError(\"Enter a question.\");\n return;\n }\n if (overLimit) {\n setError(LIMIT_MESSAGE);\n return;\n }\n const question = trimmed;\n const pageContext = pageContextForAsk(props.pageContext);\n pendingRef.current = true;\n setPending(true);\n setPendingQuestion(question);\n setDraft(\"\");\n setError(null);\n try {\n const raw = await adapter.ask({ sessionId, question, pageContext });\n const result = sanitizeResult(raw);\n if (!result) {\n setDraft(question);\n setError(ERROR_MESSAGE);\n return;\n }\n if (result.outcome === \"unanswered\") onFallbackRef.current?.(result);\n setTurns((current) => [...current, { question, result, feedback: \"idle\" }]);\n } catch {\n setDraft(question);\n setError(ERROR_MESSAGE);\n } finally {\n pendingRef.current = false;\n setPending(false);\n setPendingQuestion(null);\n }\n }\n\n async function onFeedback(answerId: string, helpful: boolean) {\n if (!adapter?.feedback || feedbackLock.current.has(answerId)) return;\n const current = turns.find((turn) => turn.result.answerId === answerId);\n if (!current || current.feedback === \"pending\" || current.feedback === \"done\") return;\n feedbackLock.current.add(answerId);\n setTurns((items) => items.map((turn) => (turn.result.answerId === answerId ? { ...turn, feedback: \"pending\" } : turn)));\n try {\n await adapter.feedback({ sessionId, answerId, helpful });\n setTurns((items) => items.map((turn) => (turn.result.answerId === answerId ? { ...turn, feedback: \"done\" } : turn)));\n } catch {\n feedbackLock.current.delete(answerId);\n setTurns((items) => items.map((turn) => (turn.result.answerId === answerId ? { ...turn, feedback: \"error\" } : turn)));\n }\n }\n\n const panel = (\n <div\n className={[WIDGET_ROOT_CLASS, presentation === \"section\" ? props.className : \"\"].filter(Boolean).join(\" \")}\n data-theme={props.theme}\n data-mode={mode}\n >\n <header className=\"bsw-an__header\">\n <span className=\"bsw-an__mark\" aria-hidden=\"true\" />\n <div className=\"bsw-an__heading\">\n <h2 className=\"bsw-an__title\">Answers</h2>\n {mode === \"local\" ? <p className=\"bsw-an__note\">Sample answers for Northwind Outfitters.</p> : null}\n </div>\n </header>\n <div className=\"bsw-an__log\" ref={logRef}>\n {turns.length === 0 && !pendingQuestion ? (\n <p className=\"bsw-an__empty\">\n {mode === \"unwired\"\n ? \"This page is not ready to answer yet.\"\n : mode === \"local\"\n ? \"Ask a question. The sample covers hours, returns, and shipping for Northwind Outfitters.\"\n : props.greeting ?? \"Ask a question this site already answers.\"}\n </p>\n ) : (\n <ol className=\"bsw-an__turns\">\n {turns.map((turn, index) => {\n const citations = turn.result.outcome === \"answered\" ? (turn.result.citations ?? []) : [];\n const allowed = new Set(citations.map((citation) => citation.url).filter((url): url is string => Boolean(url)));\n return (\n <li key={`${turn.result.answerId}:${index}`} className=\"bsw-an__turn\" data-outcome={turn.result.outcome}>\n <div className=\"bsw-an__row bsw-an__row--visitor\">\n <p className=\"bsw-an__question\">{turn.question}</p>\n </div>\n <div className=\"bsw-an__row bsw-an__row--reply\">\n {turn.result.outcome === \"answered\" && turn.result.answer ? (\n <div className=\"bsw-an__answer\">{renderAnswerMarkdown(turn.result.answer, allowed)}</div>\n ) : (\n <p className=\"bsw-an__fallback\">{turn.result.fallback}</p>\n )}\n <CitationSources citations={citations} />\n {adapter?.feedback ? (\n turn.feedback === \"done\" ? (\n <p className=\"bsw-an__thanks\">Thanks for the feedback.</p>\n ) : (\n <div className=\"bsw-an__feedback\">\n <button\n type=\"button\"\n className=\"bsw-an__button bsw-an__button--ghost\"\n disabled={turn.feedback === \"pending\"}\n onClick={() => void onFeedback(turn.result.answerId, true)}\n >\n Helpful\n </button>\n <button\n type=\"button\"\n className=\"bsw-an__button bsw-an__button--ghost\"\n disabled={turn.feedback === \"pending\"}\n onClick={() => void onFeedback(turn.result.answerId, false)}\n >\n Not helpful\n </button>\n {turn.feedback === \"error\" ? (\n <p className=\"bsw-an__error\" role=\"alert\">\n {FEEDBACK_ERROR}\n </p>\n ) : null}\n </div>\n )\n ) : null}\n </div>\n </li>\n );\n })}\n {pendingQuestion ? (\n <li className=\"bsw-an__turn\" data-pending=\"true\">\n <div className=\"bsw-an__row bsw-an__row--visitor\">\n <p className=\"bsw-an__question\">{pendingQuestion}</p>\n </div>\n <div className=\"bsw-an__row bsw-an__row--reply\">\n <p className=\"bsw-an__status\" role=\"status\">\n <span className=\"bsw-an__dots\" aria-hidden=\"true\">\n <i />\n <i />\n <i />\n </span>\n Looking that up.\n </p>\n </div>\n </li>\n ) : null}\n </ol>\n )}\n </div>\n <form className=\"bsw-an__composer\" autoComplete=\"off\" onSubmit={(event) => void onSubmit(event)}>\n {error ? (\n <p className=\"bsw-an__error\" role=\"alert\">\n {error}\n </p>\n ) : null}\n <label className=\"bsw-an__label\" htmlFor={inputId}>\n Question\n </label>\n <div className=\"bsw-an__composer-row\">\n <textarea\n id={inputId}\n className=\"bsw-an__textarea\"\n rows={2}\n value={draft}\n disabled={!adapter || pending}\n placeholder={mode === \"local\" ? \"Ask about hours, returns, or shipping\" : \"Ask a question\"}\n enterKeyHint=\"send\"\n onChange={(event) => {\n setDraft(event.target.value);\n if (error) setError(null);\n }}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\" || event.shiftKey) return;\n event.preventDefault();\n event.currentTarget.form?.requestSubmit();\n }}\n />\n <button className=\"bsw-an__button\" type=\"submit\" disabled={!adapter || pending || !trimmed || overLimit}>\n Ask\n </button>\n </div>\n {overLimit ? (\n <p className=\"bsw-an__count\">\n {length} / 1,000\n <span className=\"bsw-an__error\"> {LIMIT_MESSAGE}</span>\n </p>\n ) : length > 800 ? (\n <p className=\"bsw-an__count\">{length} / 1,000</p>\n ) : null}\n </form>\n </div>\n );\n\n if (presentation === \"section\") return panel;\n return (\n <EmbedFrame presentation={presentation} defaultOpen={props.defaultOpen} label=\"Answers\" className={props.className} theme={props.theme}>\n {panel}\n </EmbedFrame>\n );\n}\n","import type { AnswersCitation, AnswersResult } from \"./types\";\n\nconst ANSWER_MAX = 8000;\nconst EXCERPT_MAX = 500;\nconst CITATION_MAX = 20;\nconst EMPTY_FALLBACK = \"No answer was found.\";\n\nexport function codePointLength(value: string): number {\n return Array.from(value).length;\n}\n\nexport function clipCodePoints(value: string, max: number): string {\n let count = 0;\n let out = \"\";\n for (const char of value) {\n if (count >= max) break;\n out += char;\n count += 1;\n }\n return out;\n}\n\nexport function safeHttpUrl(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n return undefined;\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") return undefined;\n return parsed.toString();\n}\n\nexport function sanitizeCitations(value: unknown): AnswersCitation[] {\n if (!Array.isArray(value)) return [];\n const out: AnswersCitation[] = [];\n const seen = new Set<string>();\n for (const item of value) {\n if (out.length >= CITATION_MAX) break;\n if (!item || typeof item !== \"object\") continue;\n const raw = item as Partial<AnswersCitation>;\n if (typeof raw.id !== \"string\" || !raw.id.trim()) continue;\n const id = raw.id.trim();\n if (seen.has(id)) continue;\n seen.add(id);\n const title = typeof raw.title === \"string\" && raw.title.trim() ? clipCodePoints(raw.title.trim(), 200) : id;\n const excerpt = typeof raw.excerpt === \"string\" && raw.excerpt.trim() ? clipCodePoints(raw.excerpt.trim(), EXCERPT_MAX) : undefined;\n const url = safeHttpUrl(raw.url);\n out.push({ id, title, excerpt, url });\n }\n return out;\n}\n\n/** Keeps answered text and citation ids. Unanswered drops any answer body and citations. */\nexport function sanitizeResult(input: unknown): AnswersResult | null {\n if (!input || typeof input !== \"object\") return null;\n const raw = input as Partial<AnswersResult>;\n if (typeof raw.answerId !== \"string\" || !raw.answerId.trim()) return null;\n if (typeof raw.sessionId !== \"string\") return null;\n const answerId = raw.answerId.trim();\n const sessionId = raw.sessionId;\n const fallback = typeof raw.fallback === \"string\" && raw.fallback.trim() ? raw.fallback.trim() : EMPTY_FALLBACK;\n\n if (raw.outcome === \"unanswered\") {\n return { sessionId, answerId, outcome: \"unanswered\", fallback };\n }\n if (raw.outcome !== \"answered\") return null;\n\n const answer = typeof raw.answer === \"string\" ? raw.answer.trim() : \"\";\n if (!answer) return { sessionId, answerId, outcome: \"unanswered\", fallback };\n return {\n sessionId,\n answerId,\n outcome: \"answered\",\n answer: clipCodePoints(answer, ANSWER_MAX),\n citations: sanitizeCitations(raw.citations),\n };\n}\n","export const QUESTION_MAX = 1000;\n\nexport const WIDGET_ROOT_CLASS = \"bsw-an\";\n\nexport type AnswersPageContext = {\n url?: string;\n title?: string;\n};\n\nexport type AnswersCitation = {\n id: string;\n title: string;\n url?: string;\n excerpt?: string;\n};\n\nexport type AnswersOutcome = \"answered\" | \"unanswered\";\n\nexport type AnswersAskInput = {\n sessionId: string;\n question: string;\n pageContext?: AnswersPageContext;\n};\n\nexport type AnswersResult = {\n sessionId: string;\n answerId: string;\n outcome: AnswersOutcome;\n answer?: string;\n citations?: AnswersCitation[];\n fallback?: string;\n};\n\nexport type AnswersFeedbackInput = {\n sessionId: string;\n answerId: string;\n helpful: boolean;\n /** Optional note. Webhook payloads omit this text. */\n reason?: string;\n};\n\nexport type AnswersAdapter = {\n ask: (input: AnswersAskInput) => Promise<AnswersResult>;\n feedback?: (input: AnswersFeedbackInput) => Promise<void> | void;\n};\n\nexport type AnswersPresentation = \"launcher\" | \"section\" | \"modal\" | \"window\";\n\nexport type AnswersTheme = \"light\" | \"dark\";\n\nexport type AnswersProps = {\n appId?: string;\n publishableKey?: string;\n adapter?: AnswersAdapter;\n presentation?: AnswersPresentation;\n defaultOpen?: boolean;\n theme?: AnswersTheme;\n pageContext?: AnswersPageContext;\n /** Empty-state copy. Omit it and the widget shows \"Ask a question this site already answers.\" */\n greeting?: string;\n onFallback?: (result: AnswersResult) => void;\n /** Layout hook only. Skin the widget with the `--bsw-an-*` tokens. */\n className?: string;\n};\n","import { codePointLength } from \"./result\";\nimport { QUESTION_MAX, type AnswersAdapter, type AnswersCitation, type AnswersResult } from \"./types\";\n\nexport const LOCAL_FALLBACK =\n \"We could not find that in the Northwind Outfitters sample. Ask about hours, returns, or shipping.\";\n\nexport const LOCAL_PROMPTS = {\n returns: \"What is your return window?\",\n returnsFollowUp: \"How long do I have?\",\n hours: \"What are your hours?\",\n shipping: \"Is shipping free?\",\n unsupported: \"Do you price match?\",\n} as const;\n\ntype Topic = \"hours\" | \"returns\" | \"shipping\";\n\nconst RETURNS_ANSWER = \"Returns are accepted within 30 days of delivery.\";\nconst RETURNS_FOLLOW = \"You have 30 days from delivery. Exchanges use that same window.\";\nconst HOURS_ANSWER =\n \"Northwind Outfitters is open Monday through Friday, 9am to 5pm US Eastern, and closed Saturday and Sunday.\";\nconst HOURS_FOLLOW = \"Saturday and Sunday are closed. Weekday hours are 9am to 5pm US Eastern.\";\nconst SHIPPING_ANSWER = \"Standard shipping is free on orders over $50.\";\nconst SHIPPING_FOLLOW = \"Free standard shipping starts when the order is over $50.\";\n\nexport const LOCAL_CITATIONS: Record<Topic, AnswersCitation> = {\n hours: {\n id: \"ane_hours\",\n title: \"Store hours\",\n url: \"https://northwind.example/help/hours\",\n excerpt: \"Open Monday through Friday, 9am to 5pm US Eastern. Closed Saturday and Sunday.\",\n },\n returns: {\n id: \"ane_returns\",\n title: \"Returns\",\n url: \"https://northwind.example/help/returns\",\n excerpt: \"Returns are accepted within 30 days of delivery. Exchanges use the same 30 day window.\",\n },\n shipping: {\n id: \"ane_shipping\",\n title: \"Shipping\",\n url: \"https://northwind.example/help/shipping\",\n excerpt: \"Standard shipping is free on orders over $50.\",\n },\n};\n\nconst TOPICS: { id: Topic; words: string[] }[] = [\n { id: \"returns\", words: [\"return\", \"returns\", \"refund\", \"refunds\", \"exchange\", \"exchanges\"] },\n { id: \"shipping\", words: [\"ship\", \"shipping\", \"delivery\"] },\n { id: \"hours\", words: [\"hour\", \"hours\", \"open\", \"opening\", \"weekend\", \"saturday\", \"sunday\", \"weekday\", \"weekdays\"] },\n];\n\nconst ANSWERS: Record<Topic, string> = {\n hours: HOURS_ANSWER,\n returns: RETURNS_ANSWER,\n shipping: SHIPPING_ANSWER,\n};\n\nconst FOLLOW_ANSWERS: Record<Topic, string> = {\n hours: HOURS_FOLLOW,\n returns: RETURNS_FOLLOW,\n shipping: SHIPPING_FOLLOW,\n};\n\nconst FOLLOW: { phrase: string; topic?: Topic }[] = [\n { phrase: \"how long\" },\n { phrase: \"how long do i have\" },\n { phrase: \"are you open then\", topic: \"hours\" },\n { phrase: \"what about then\" },\n { phrase: \"is there a minimum\", topic: \"shipping\" },\n { phrase: \"how much is free\", topic: \"shipping\" },\n { phrase: \"tell me more\" },\n];\n\nfunction normalize(question: string): string {\n return question\n .trim()\n .toLowerCase()\n .replace(/[?!.,'\"]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction hasWord(question: string, word: string): boolean {\n return new RegExp(`\\\\b${word}\\\\b`).test(question);\n}\n\nfunction matchDirect(question: string): Topic | null {\n for (const topic of TOPICS) {\n if (topic.words.some((word) => hasWord(question, word))) return topic.id;\n }\n return null;\n}\n\nfunction matchFollow(question: string, last: Topic | undefined): Topic | null {\n const hit = FOLLOW.find((item) => item.phrase === question);\n if (!hit || !last) return null;\n if (hit.topic && hit.topic !== last) return null;\n return last;\n}\n\nfunction limitProblem(question: string): string | null {\n const trimmed = question.trim();\n if (!trimmed) return \"Enter a question.\";\n if (codePointLength(trimmed) > QUESTION_MAX) return \"Questions are limited to 1,000 characters.\";\n return null;\n}\n\n/**\n * Deterministic sample company. Supported facts, a follow-up that needs the\n * previous topic, and an explicit unanswered fallback. No network.\n */\nexport function createLocalAdapter(): AnswersAdapter {\n const lastTopic = new Map<string, Topic>();\n let sequence = 0;\n\n return {\n async ask(input): Promise<AnswersResult> {\n const problem = limitProblem(input.question);\n if (problem) throw new Error(problem);\n const question = normalize(input.question);\n const direct = matchDirect(question);\n const topic = direct ?? matchFollow(question, lastTopic.get(input.sessionId));\n sequence += 1;\n const answerId = `anq_${sequence.toString(16).padStart(24, \"0\")}`;\n if (!topic) {\n return {\n sessionId: input.sessionId,\n answerId,\n outcome: \"unanswered\",\n fallback: LOCAL_FALLBACK,\n };\n }\n lastTopic.set(input.sessionId, topic);\n return {\n sessionId: input.sessionId,\n answerId,\n outcome: \"answered\",\n answer: direct ? ANSWERS[topic] : FOLLOW_ANSWERS[topic],\n citations: [{ ...LOCAL_CITATIONS[topic] }],\n };\n },\n async feedback() {},\n };\n}\n\n/** Returns the caller adapter. Question text stays on the customer backend. */\nexport function createByoAdapter(impl: AnswersAdapter): AnswersAdapter {\n return impl;\n}\n","import { useEffect, useState, type ReactNode } from \"react\";\nimport type { AnswersPresentation, AnswersTheme } from \"./types\";\n\nexport function EmbedFrame({\n presentation = \"section\",\n defaultOpen = false,\n label,\n className,\n theme,\n children,\n}: {\n presentation?: AnswersPresentation;\n defaultOpen?: boolean;\n label: string;\n className?: string;\n theme?: AnswersTheme;\n children: ReactNode;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n const openKey = `${presentation}\\0${defaultOpen ? \"1\" : \"0\"}`;\n const [openKeySeen, setOpenKeySeen] = useState(openKey);\n if (openKey !== openKeySeen) {\n setOpenKeySeen(openKey);\n setOpen(defaultOpen);\n }\n const launcher = presentation === \"launcher\";\n const floating = launcher || presentation === \"window\";\n useEffect(() => {\n if (presentation === \"section\" || !open) return;\n function onKey(event: KeyboardEvent) {\n if (event.key === \"Escape\") setOpen(false);\n }\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open, presentation]);\n if (presentation === \"section\") return children;\n const classes = [\n \"bsw-an-frame\",\n `bsw-an-frame--${presentation}`,\n open ? \"bsw-an-frame--open\" : \"\",\n floating ? \"bsw-an--launcher-host\" : \"\",\n floating && open ? \"bsw-an--launcher-open\" : \"\",\n floating && !open ? \"bsw-an--launcher-closed\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n return (\n <div className={classes} data-theme={theme} data-state={open ? \"open\" : \"closed\"}>\n {open && (presentation === \"modal\" || launcher) ? (\n <button type=\"button\" className=\"bsw-an-frame__backdrop\" aria-label={`Close ${label}`} onClick={() => setOpen(false)} />\n ) : null}\n <div\n className=\"bsw-an-frame__panel\"\n hidden={!open}\n role={open && presentation === \"modal\" ? \"dialog\" : open ? \"region\" : undefined}\n aria-modal={open && presentation === \"modal\" ? true : undefined}\n aria-label={open ? label : undefined}\n >\n <button type=\"button\" className=\"bsw-an-frame__close\" onClick={() => setOpen(false)}>\n <span className=\"bsw-an-frame__close-label\">Close</span>\n <span aria-hidden=\"true\">×</span>\n </button>\n {children}\n </div>\n {open ? null : (\n <button\n type=\"button\"\n className={launcher ? \"bsw-an-frame__fab\" : \"bsw-an-frame__trigger\"}\n aria-label={launcher ? \"Open answers\" : undefined}\n onClick={() => setOpen(true)}\n >\n {launcher ? \"Ask\" : label}\n </button>\n )}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\nimport { safeHttpUrl } from \"./result\";\n\nfunction inlineMarkup(text: string, allowed: ReadonlySet<string>, keyPrefix: string): ReactNode[] {\n const pattern = /(\\*\\*[^*\\n]+\\*\\*|(?<![A-Za-z0-9])_[^_\\n]+_(?![A-Za-z0-9])|`[^`\\n]+`|\\[[^\\]\\n]+\\]\\(https?:\\/\\/[^)\\s]+\\))/g;\n const parts: ReactNode[] = [];\n let last = 0;\n let index = 0;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(text))) {\n if (match.index > last) parts.push(text.slice(last, match.index));\n const token = match[0];\n const key = `${keyPrefix}-${index++}`;\n if (token.startsWith(\"**\") && token.endsWith(\"**\") && token.length >= 4) {\n parts.push(<strong key={key}>{token.slice(2, -2)}</strong>);\n } else if (token.startsWith(\"_\") && token.endsWith(\"_\") && token.length >= 3) {\n parts.push(<em key={key}>{token.slice(1, -1)}</em>);\n } else if (token.startsWith(\"`\") && token.endsWith(\"`\") && token.length >= 3) {\n parts.push(<code key={key}>{token.slice(1, -1)}</code>);\n } else if (token.startsWith(\"[\")) {\n const link = /^\\[([^\\]\\n]+)\\]\\((https?:\\/\\/[^)\\s]+)\\)$/.exec(token);\n const href = link?.[2] ?? \"\";\n const safe = safeHttpUrl(href);\n if (link && safe && (allowed.has(href) || allowed.has(safe))) {\n parts.push(\n <a key={key} href={safe} target=\"_blank\" rel=\"noopener noreferrer\">\n {link[1]}\n </a>,\n );\n } else {\n parts.push(token);\n }\n } else {\n parts.push(token);\n }\n last = match.index + token.length;\n }\n if (last < text.length) parts.push(text.slice(last));\n return parts;\n}\n\n/** Bold, italic, code, and simple lists. Links render only for citation URLs. HTML stays text. */\nexport function renderAnswerMarkdown(text: string, allowedUrls: ReadonlySet<string>): ReactNode {\n const lines = text.split(\"\\n\");\n const nodes: ReactNode[] = [];\n let paragraph: string[] = [];\n let list: string[] = [];\n let key = 0;\n\n function flushParagraph() {\n if (!paragraph.length) return;\n const content = paragraph.join(\"\\n\");\n nodes.push(<p key={`p-${key++}`}>{inlineMarkup(content, allowedUrls, `p-${key}`)}</p>);\n paragraph = [];\n }\n\n function flushList() {\n if (!list.length) return;\n const items = list;\n nodes.push(\n <ul key={`ul-${key++}`}>\n {items.map((item, index) => (\n <li key={`li-${key}-${index}`}>{inlineMarkup(item, allowedUrls, `li-${key}-${index}`)}</li>\n ))}\n </ul>,\n );\n list = [];\n }\n\n for (const line of lines) {\n const item = /^\\s*[-*]\\s+(.*)$/.exec(line);\n if (item) {\n flushParagraph();\n list.push(item[1] ?? \"\");\n continue;\n }\n if (!line.trim()) {\n flushParagraph();\n flushList();\n continue;\n }\n flushList();\n paragraph.push(line);\n }\n flushParagraph();\n flushList();\n return nodes;\n}\n","const SESSION_ID = /^ans_[0-9a-f]{24}$/;\n\nexport function sessionStorageKey(appId?: string): string {\n const scope = appId?.trim() ? appId.trim() : \"local\";\n return `bsw_an_session:${scope}`;\n}\n\nexport function createOpaqueSessionId(): string {\n const bytes = new Uint8Array(12);\n globalThis.crypto.getRandomValues(bytes);\n let hex = \"\";\n for (const byte of bytes) hex += byte.toString(16).padStart(2, \"0\");\n return `ans_${hex}`;\n}\n\n/** Reads the visitor session from sessionStorage. Never writes a cookie. */\nexport function readSessionId(storage: Storage | null, appId?: string): string {\n if (!storage) return createOpaqueSessionId();\n const key = sessionStorageKey(appId);\n const existing = storage.getItem(key);\n if (existing && SESSION_ID.test(existing)) return existing;\n const id = createOpaqueSessionId();\n storage.setItem(key, id);\n return id;\n}\n","import { createRoot, type Root } from \"react-dom/client\";\nimport { Answers } from \"./Answers\";\nimport type { AnswersProps } from \"./types\";\n\nconst roots = new WeakMap<Element, Root>();\n\nexport function mount(el: Element, props: AnswersProps = {}): () => void {\n let root = roots.get(el);\n if (!root) {\n root = createRoot(el);\n roots.set(el, root);\n }\n root.render(<Answers {...props} />);\n return () => unmount(el);\n}\n\nexport function unmount(el: Element): void {\n const root = roots.get(el);\n if (!root) return;\n root.unmount();\n roots.delete(el);\n}\n"],"mappings":"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","names":["useEffect","useState","jsx","jsx","jsxs","useState","useEffect","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/Answers.tsx","../src/result.ts","../src/types.ts","../src/adapters.ts","../src/frame.tsx","../src/markdown.tsx","../src/session.ts","../src/mount.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState, type FormEvent } from \"react\";\nimport { createHostedAdapter, createLocalAdapter } from \"./adapters\";\nimport { EmbedFrame } from \"./frame\";\nimport { renderAnswerMarkdown } from \"./markdown\";\nimport { codePointLength, sanitizeResult } from \"./result\";\nimport { readSessionId, sessionStorageKey } from \"./session\";\nimport {\n QUESTION_MAX,\n WIDGET_ROOT_CLASS,\n type AnswersAdapter,\n type AnswersCitation,\n type AnswersPageContext,\n type AnswersProps,\n type AnswersResult,\n} from \"./types\";\n\ntype Turn = {\n question: string;\n result: AnswersResult;\n feedback: \"idle\" | \"pending\" | \"done\" | \"error\";\n};\n\nconst LIMIT_MESSAGE = \"Questions are limited to 1,000 characters.\";\nconst ERROR_MESSAGE = \"Something went wrong. Your question is still here. Try again.\";\nconst FEEDBACK_ERROR = \"Feedback could not be saved. Try again.\";\n\nfunction useSessionId(appId?: string): string {\n const key = sessionStorageKey(appId);\n const [state, setState] = useState(() => ({\n key,\n id: readSessionId(typeof window === \"undefined\" ? null : window.sessionStorage, appId),\n }));\n if (state.key !== key) {\n setState({\n key,\n id: readSessionId(typeof window === \"undefined\" ? null : window.sessionStorage, appId),\n });\n }\n return state.id;\n}\n\nfunction sourcesLabel(count: number): string {\n return count === 1 ? \"1 source\" : `${count} sources`;\n}\n\nfunction CitationSources({ citations }: { citations: AnswersCitation[] }) {\n if (!citations.length) return null;\n return (\n <details className=\"bsw-an__sources-wrap\">\n <summary className=\"bsw-an__sources-toggle\">{sourcesLabel(citations.length)}</summary>\n <ul className=\"bsw-an__sources\">\n {citations.map((citation) => (\n <li key={citation.id} className=\"bsw-an__source\" data-citation-id={citation.id}>\n {citation.url ? (\n <a href={citation.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {citation.title}\n </a>\n ) : (\n <span className=\"bsw-an__source-title\">{citation.title}</span>\n )}\n {citation.excerpt ? <p className=\"bsw-an__excerpt\">{citation.excerpt}</p> : null}\n </li>\n ))}\n </ul>\n </details>\n );\n}\n\nfunction isMac(): boolean {\n if (typeof navigator === \"undefined\") return false;\n return /Mac|iPhone|iPad/i.test(navigator.userAgent) || /Mac/i.test(navigator.platform ?? \"\");\n}\n\n/** `mod+k` is Cmd on macOS and Ctrl elsewhere. */\nexport function matchesShortcut(\n event: { key: string; metaKey: boolean; ctrlKey: boolean; altKey: boolean; shiftKey: boolean },\n spec: string,\n mac = isMac(),\n): boolean {\n const parts = spec.toLowerCase().split(\"+\").map((part) => part.trim()).filter(Boolean);\n const key = parts[parts.length - 1];\n if (!key || event.key.toLowerCase() !== key) return false;\n const mods = new Set(parts.slice(0, -1));\n const wantCtrl = mods.has(\"ctrl\") || (mods.has(\"mod\") && !mac);\n const wantMeta = mods.has(\"meta\") || (mods.has(\"mod\") && mac);\n if (wantCtrl !== event.ctrlKey) return false;\n if (wantMeta !== event.metaKey) return false;\n if (mods.has(\"alt\") !== event.altKey) return false;\n if (mods.has(\"shift\") !== event.shiftKey) return false;\n return true;\n}\n\nfunction pageContextForAsk(explicit: AnswersPageContext | undefined): AnswersPageContext {\n if (explicit) {\n const url = typeof explicit.url === \"string\" ? explicit.url : undefined;\n const title = typeof explicit.title === \"string\" ? explicit.title : undefined;\n return { url, title };\n }\n if (typeof window === \"undefined\") return {};\n return { url: window.location.href, title: document.title };\n}\n\nexport function Answers(props: AnswersProps) {\n const presentation = props.presentation ?? \"launcher\";\n const sessionId = useSessionId(props.appId);\n const localRef = useRef<AnswersAdapter | null>(null);\n const hosted = !props.adapter && props.appId && props.publishableKey\n ? createHostedAdapter({ appId: props.appId, publishableKey: props.publishableKey, apiBaseUrl: props.apiBaseUrl })\n : null;\n const adapter = props.adapter ?? hosted ?? (props.publishableKey ? null : (localRef.current ??= createLocalAdapter()));\n const mode = props.adapter ? \"byo\" : hosted ? \"hosted\" : props.publishableKey ? \"unwired\" : \"local\";\n const [draft, setDraft] = useState(\"\");\n const [openSignal, setOpenSignal] = useState(0);\n const [pending, setPending] = useState(false);\n const [pendingQuestion, setPendingQuestion] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [turns, setTurns] = useState<Turn[]>([]);\n const pendingRef = useRef(false);\n const logRef = useRef<HTMLDivElement>(null);\n const questionRef = useRef<HTMLTextAreaElement>(null);\n const feedbackLock = useRef(new Set<string>());\n const onFallbackRef = useRef(props.onFallback);\n onFallbackRef.current = props.onFallback;\n\n useEffect(() => {\n const log = logRef.current;\n if (!log) return;\n log.scrollTop = log.scrollHeight;\n }, [turns, pending, pendingQuestion, error]);\n\n useEffect(() => {\n const spec = props.shortcut?.trim();\n if (!spec) return;\n function onKey(event: KeyboardEvent) {\n if (!matchesShortcut(event, spec!)) return;\n event.preventDefault();\n setOpenSignal((current) => current + 1);\n }\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [props.shortcut]);\n\n useEffect(() => {\n if (!openSignal) return;\n questionRef.current?.focus();\n }, [openSignal]);\n\n const trimmed = draft.trim();\n const length = codePointLength(trimmed);\n const overLimit = length > QUESTION_MAX;\n const inputId = useId();\n\n async function askQuestion(raw: string) {\n if (!adapter || pendingRef.current) return;\n const question = raw.trim();\n if (!question) {\n setError(\"Enter a question.\");\n return;\n }\n if (codePointLength(question) > QUESTION_MAX) {\n setError(LIMIT_MESSAGE);\n return;\n }\n const pageContext = pageContextForAsk(props.pageContext);\n pendingRef.current = true;\n setPending(true);\n setPendingQuestion(question);\n setDraft(\"\");\n setError(null);\n try {\n const rawResult = await adapter.ask({ sessionId, question, pageContext });\n const result = sanitizeResult(rawResult);\n if (!result) {\n setDraft(question);\n setError(ERROR_MESSAGE);\n return;\n }\n if (result.outcome === \"unanswered\") onFallbackRef.current?.(result);\n setTurns((current) => [...current, { question, result, feedback: \"idle\" }]);\n } catch {\n setDraft(question);\n setError(ERROR_MESSAGE);\n } finally {\n pendingRef.current = false;\n setPending(false);\n setPendingQuestion(null);\n }\n }\n\n function onSubmit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n void askQuestion(draft);\n }\n\n async function onFeedback(answerId: string, helpful: boolean) {\n if (!adapter?.feedback || feedbackLock.current.has(answerId)) return;\n const current = turns.find((turn) => turn.result.answerId === answerId);\n if (!current || current.feedback === \"pending\" || current.feedback === \"done\") return;\n feedbackLock.current.add(answerId);\n setTurns((items) => items.map((turn) => (turn.result.answerId === answerId ? { ...turn, feedback: \"pending\" } : turn)));\n try {\n await adapter.feedback({ sessionId, answerId, helpful });\n setTurns((items) => items.map((turn) => (turn.result.answerId === answerId ? { ...turn, feedback: \"done\" } : turn)));\n } catch {\n feedbackLock.current.delete(answerId);\n setTurns((items) => items.map((turn) => (turn.result.answerId === answerId ? { ...turn, feedback: \"error\" } : turn)));\n }\n }\n\n const panel = (\n <div\n className={[WIDGET_ROOT_CLASS, presentation === \"section\" ? props.className : \"\"].filter(Boolean).join(\" \")}\n data-theme={props.theme}\n data-mode={mode}\n >\n <header className=\"bsw-an__header\">\n <span className=\"bsw-an__mark\" aria-hidden=\"true\" />\n <div className=\"bsw-an__heading\">\n <h2 className=\"bsw-an__title\">Answers</h2>\n {mode === \"local\" ? <p className=\"bsw-an__note\">Sample answers for Northwind Outfitters.</p> : null}\n </div>\n </header>\n <div className=\"bsw-an__log\" ref={logRef}>\n {turns.length === 0 && !pendingQuestion ? (\n <p className=\"bsw-an__empty\">\n {mode === \"unwired\"\n ? \"This page is not ready to answer yet.\"\n : mode === \"local\"\n ? \"Ask a question. The sample covers hours, returns, and shipping for Northwind Outfitters.\"\n : props.greeting ?? \"Ask a question this site already answers.\"}\n </p>\n ) : (\n <ol className=\"bsw-an__turns\">\n {turns.map((turn, index) => {\n const citations = turn.result.outcome === \"answered\" ? (turn.result.citations ?? []) : [];\n const allowed = new Set(citations.map((citation) => citation.url).filter((url): url is string => Boolean(url)));\n return (\n <li key={`${turn.result.answerId}:${index}`} className=\"bsw-an__turn\" data-outcome={turn.result.outcome}>\n <div className=\"bsw-an__row bsw-an__row--visitor\">\n <p className=\"bsw-an__question\">{turn.question}</p>\n </div>\n <div className=\"bsw-an__row bsw-an__row--reply\">\n {turn.result.outcome === \"answered\" && turn.result.answer ? (\n <div className=\"bsw-an__answer\">{renderAnswerMarkdown(turn.result.answer, allowed)}</div>\n ) : (\n <p className=\"bsw-an__fallback\">{turn.result.fallback}</p>\n )}\n <CitationSources citations={citations} />\n {adapter?.feedback ? (\n turn.feedback === \"done\" ? (\n <p className=\"bsw-an__thanks\">Thanks for the feedback.</p>\n ) : (\n <div className=\"bsw-an__feedback\">\n <button\n type=\"button\"\n className=\"bsw-an__button bsw-an__button--ghost\"\n disabled={turn.feedback === \"pending\"}\n onClick={() => void onFeedback(turn.result.answerId, true)}\n >\n Helpful\n </button>\n <button\n type=\"button\"\n className=\"bsw-an__button bsw-an__button--ghost\"\n disabled={turn.feedback === \"pending\"}\n onClick={() => void onFeedback(turn.result.answerId, false)}\n >\n Not helpful\n </button>\n {turn.feedback === \"error\" ? (\n <p className=\"bsw-an__error\" role=\"alert\">\n {FEEDBACK_ERROR}\n </p>\n ) : null}\n </div>\n )\n ) : null}\n </div>\n </li>\n );\n })}\n {pendingQuestion ? (\n <li className=\"bsw-an__turn\" data-pending=\"true\">\n <div className=\"bsw-an__row bsw-an__row--visitor\">\n <p className=\"bsw-an__question\">{pendingQuestion}</p>\n </div>\n <div className=\"bsw-an__row bsw-an__row--reply\">\n <p className=\"bsw-an__status\" role=\"status\">\n <span className=\"bsw-an__dots\" aria-hidden=\"true\">\n <i />\n <i />\n <i />\n </span>\n Looking that up.\n </p>\n </div>\n </li>\n ) : null}\n </ol>\n )}\n </div>\n {props.suggestedQuestions?.length && turns.length === 0 && !pendingQuestion ? (\n <div className=\"bsw-an__suggestions\">\n {props.suggestedQuestions.map((question) => (\n <button\n key={question}\n type=\"button\"\n className=\"bsw-an__chip\"\n disabled={!adapter || pending}\n onClick={() => void askQuestion(question)}\n >\n {question}\n </button>\n ))}\n </div>\n ) : null}\n <form className=\"bsw-an__composer\" autoComplete=\"off\" onSubmit={(event) => void onSubmit(event)}>\n {error ? (\n <p className=\"bsw-an__error\" role=\"alert\">\n {error}\n </p>\n ) : null}\n <label className=\"bsw-an__label\" htmlFor={inputId}>\n Question\n </label>\n <div className=\"bsw-an__composer-row\">\n <textarea\n id={inputId}\n ref={questionRef}\n className=\"bsw-an__textarea\"\n rows={2}\n value={draft}\n disabled={!adapter || pending}\n placeholder={mode === \"local\" ? \"Ask about hours, returns, or shipping\" : \"Ask a question\"}\n enterKeyHint=\"send\"\n onChange={(event) => {\n setDraft(event.target.value);\n if (error) setError(null);\n }}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\" || event.shiftKey) return;\n event.preventDefault();\n event.currentTarget.form?.requestSubmit();\n }}\n />\n <button className=\"bsw-an__button\" type=\"submit\" disabled={!adapter || pending || !trimmed || overLimit}>\n Ask\n </button>\n </div>\n {overLimit ? (\n <p className=\"bsw-an__count\">\n {length} / 1,000\n <span className=\"bsw-an__error\"> {LIMIT_MESSAGE}</span>\n </p>\n ) : length > 800 ? (\n <p className=\"bsw-an__count\">{length} / 1,000</p>\n ) : null}\n </form>\n </div>\n );\n\n if (presentation === \"section\") return panel;\n return (\n <EmbedFrame presentation={presentation} defaultOpen={props.defaultOpen} openSignal={openSignal} label=\"Answers\" className={props.className} theme={props.theme}>\n {panel}\n </EmbedFrame>\n );\n}\n","import type { AnswersCitation, AnswersResult } from \"./types\";\n\nconst ANSWER_MAX = 8000;\nconst EXCERPT_MAX = 500;\nconst CITATION_MAX = 20;\nconst EMPTY_FALLBACK = \"No answer was found.\";\n\nexport function codePointLength(value: string): number {\n return Array.from(value).length;\n}\n\nexport function clipCodePoints(value: string, max: number): string {\n let count = 0;\n let out = \"\";\n for (const char of value) {\n if (count >= max) break;\n out += char;\n count += 1;\n }\n return out;\n}\n\nexport function safeHttpUrl(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n return undefined;\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") return undefined;\n return parsed.toString();\n}\n\nexport function sanitizeCitations(value: unknown): AnswersCitation[] {\n if (!Array.isArray(value)) return [];\n const out: AnswersCitation[] = [];\n const seen = new Set<string>();\n for (const item of value) {\n if (out.length >= CITATION_MAX) break;\n if (!item || typeof item !== \"object\") continue;\n const raw = item as Partial<AnswersCitation>;\n if (typeof raw.id !== \"string\" || !raw.id.trim()) continue;\n const id = raw.id.trim();\n if (seen.has(id)) continue;\n seen.add(id);\n const title = typeof raw.title === \"string\" && raw.title.trim() ? clipCodePoints(raw.title.trim(), 200) : id;\n const excerpt = typeof raw.excerpt === \"string\" && raw.excerpt.trim() ? clipCodePoints(raw.excerpt.trim(), EXCERPT_MAX) : undefined;\n const url = safeHttpUrl(raw.url);\n out.push({ id, title, excerpt, url });\n }\n return out;\n}\n\n/** Keeps answered text and citation ids. Unanswered drops any answer body and citations. */\nexport function sanitizeResult(input: unknown): AnswersResult | null {\n if (!input || typeof input !== \"object\") return null;\n const raw = input as Partial<AnswersResult>;\n if (typeof raw.answerId !== \"string\" || !raw.answerId.trim()) return null;\n if (typeof raw.sessionId !== \"string\") return null;\n const answerId = raw.answerId.trim();\n const sessionId = raw.sessionId;\n const fallback = typeof raw.fallback === \"string\" && raw.fallback.trim() ? raw.fallback.trim() : EMPTY_FALLBACK;\n\n if (raw.outcome === \"unanswered\") {\n return { sessionId, answerId, outcome: \"unanswered\", fallback };\n }\n if (raw.outcome !== \"answered\") return null;\n\n const answer = typeof raw.answer === \"string\" ? raw.answer.trim() : \"\";\n if (!answer) return { sessionId, answerId, outcome: \"unanswered\", fallback };\n return {\n sessionId,\n answerId,\n outcome: \"answered\",\n answer: clipCodePoints(answer, ANSWER_MAX),\n citations: sanitizeCitations(raw.citations),\n };\n}\n","export const QUESTION_MAX = 1000;\n\nexport const WIDGET_ROOT_CLASS = \"bsw-an\";\n\nexport type AnswersPageContext = {\n url?: string;\n title?: string;\n};\n\nexport type AnswersCitation = {\n id: string;\n title: string;\n url?: string;\n excerpt?: string;\n};\n\nexport type AnswersOutcome = \"answered\" | \"unanswered\";\n\nexport type AnswersAskInput = {\n sessionId: string;\n question: string;\n pageContext?: AnswersPageContext;\n};\n\nexport type AnswersResult = {\n sessionId: string;\n answerId: string;\n outcome: AnswersOutcome;\n answer?: string;\n citations?: AnswersCitation[];\n fallback?: string;\n};\n\nexport type AnswersFeedbackInput = {\n sessionId: string;\n answerId: string;\n helpful: boolean;\n /** Optional note. Webhook payloads omit this text. */\n reason?: string;\n};\n\nexport type AnswersAdapter = {\n ask: (input: AnswersAskInput) => Promise<AnswersResult>;\n feedback?: (input: AnswersFeedbackInput) => Promise<void> | void;\n};\n\nexport type AnswersPresentation = \"launcher\" | \"section\" | \"modal\" | \"window\";\n\nexport type AnswersTheme = \"light\" | \"dark\";\n\nexport type AnswersProps = {\n appId?: string;\n publishableKey?: string;\n adapter?: AnswersAdapter;\n presentation?: AnswersPresentation;\n defaultOpen?: boolean;\n theme?: AnswersTheme;\n pageContext?: AnswersPageContext;\n /** Empty-state copy. Omit it and the widget shows \"Ask a question this site already answers.\" */\n greeting?: string;\n /** Clickable starter questions. Each chip asks that question. */\n suggestedQuestions?: string[];\n /**\n * Keyboard shortcut that opens the widget and focuses the question field.\n * `mod` is Cmd on macOS and Ctrl elsewhere. Example: `mod+k`.\n */\n shortcut?: string;\n /** Hosted API origin. Omit it and `createHostedAdapter` uses `https://answers.bootstrapware.co`. */\n apiBaseUrl?: string;\n onFallback?: (result: AnswersResult) => void;\n /** Layout hook only. Skin the widget with the `--bsw-an-*` tokens. */\n className?: string;\n};\n","import { codePointLength } from \"./result\";\nimport { QUESTION_MAX, type AnswersAdapter, type AnswersCitation, type AnswersResult } from \"./types\";\n\nexport const LOCAL_FALLBACK =\n \"We could not find that in the Northwind Outfitters sample. Ask about hours, returns, or shipping.\";\n\nexport const LOCAL_PROMPTS = {\n returns: \"What is your return window?\",\n returnsFollowUp: \"How long do I have?\",\n hours: \"What are your hours?\",\n shipping: \"Is shipping free?\",\n unsupported: \"Do you price match?\",\n} as const;\n\ntype Topic = \"hours\" | \"returns\" | \"shipping\";\n\nconst RETURNS_ANSWER = \"Returns are accepted within 30 days of delivery.\";\nconst RETURNS_FOLLOW = \"You have 30 days from delivery. Exchanges use that same window.\";\nconst HOURS_ANSWER =\n \"Northwind Outfitters is open Monday through Friday, 9am to 5pm US Eastern, and closed Saturday and Sunday.\";\nconst HOURS_FOLLOW = \"Saturday and Sunday are closed. Weekday hours are 9am to 5pm US Eastern.\";\nconst SHIPPING_ANSWER = \"Standard shipping is free on orders over $50.\";\nconst SHIPPING_FOLLOW = \"Free standard shipping starts when the order is over $50.\";\n\nexport const LOCAL_CITATIONS: Record<Topic, AnswersCitation> = {\n hours: {\n id: \"ane_hours\",\n title: \"Store hours\",\n url: \"https://northwind.example/help/hours\",\n excerpt: \"Open Monday through Friday, 9am to 5pm US Eastern. Closed Saturday and Sunday.\",\n },\n returns: {\n id: \"ane_returns\",\n title: \"Returns\",\n url: \"https://northwind.example/help/returns\",\n excerpt: \"Returns are accepted within 30 days of delivery. Exchanges use the same 30 day window.\",\n },\n shipping: {\n id: \"ane_shipping\",\n title: \"Shipping\",\n url: \"https://northwind.example/help/shipping\",\n excerpt: \"Standard shipping is free on orders over $50.\",\n },\n};\n\nconst TOPICS: { id: Topic; words: string[] }[] = [\n { id: \"returns\", words: [\"return\", \"returns\", \"refund\", \"refunds\", \"exchange\", \"exchanges\"] },\n { id: \"shipping\", words: [\"ship\", \"shipping\", \"delivery\"] },\n { id: \"hours\", words: [\"hour\", \"hours\", \"open\", \"opening\", \"weekend\", \"saturday\", \"sunday\", \"weekday\", \"weekdays\"] },\n];\n\nconst ANSWERS: Record<Topic, string> = {\n hours: HOURS_ANSWER,\n returns: RETURNS_ANSWER,\n shipping: SHIPPING_ANSWER,\n};\n\nconst FOLLOW_ANSWERS: Record<Topic, string> = {\n hours: HOURS_FOLLOW,\n returns: RETURNS_FOLLOW,\n shipping: SHIPPING_FOLLOW,\n};\n\nconst FOLLOW: { phrase: string; topic?: Topic }[] = [\n { phrase: \"how long\" },\n { phrase: \"how long do i have\" },\n { phrase: \"are you open then\", topic: \"hours\" },\n { phrase: \"what about then\" },\n { phrase: \"is there a minimum\", topic: \"shipping\" },\n { phrase: \"how much is free\", topic: \"shipping\" },\n { phrase: \"tell me more\" },\n];\n\nfunction normalize(question: string): string {\n return question\n .trim()\n .toLowerCase()\n .replace(/[?!.,'\"]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nfunction hasWord(question: string, word: string): boolean {\n return new RegExp(`\\\\b${word}\\\\b`).test(question);\n}\n\nfunction matchDirect(question: string): Topic | null {\n for (const topic of TOPICS) {\n if (topic.words.some((word) => hasWord(question, word))) return topic.id;\n }\n return null;\n}\n\nfunction matchFollow(question: string, last: Topic | undefined): Topic | null {\n const hit = FOLLOW.find((item) => item.phrase === question);\n if (!hit || !last) return null;\n if (hit.topic && hit.topic !== last) return null;\n return last;\n}\n\nfunction limitProblem(question: string): string | null {\n const trimmed = question.trim();\n if (!trimmed) return \"Enter a question.\";\n if (codePointLength(trimmed) > QUESTION_MAX) return \"Questions are limited to 1,000 characters.\";\n return null;\n}\n\n/**\n * Deterministic sample company. Supported facts, a follow-up that needs the\n * previous topic, and an explicit unanswered fallback. No network.\n */\nexport function createLocalAdapter(): AnswersAdapter {\n const lastTopic = new Map<string, Topic>();\n let sequence = 0;\n\n return {\n async ask(input): Promise<AnswersResult> {\n const problem = limitProblem(input.question);\n if (problem) throw new Error(problem);\n const question = normalize(input.question);\n const direct = matchDirect(question);\n const topic = direct ?? matchFollow(question, lastTopic.get(input.sessionId));\n sequence += 1;\n const answerId = `anq_${sequence.toString(16).padStart(24, \"0\")}`;\n if (!topic) {\n return {\n sessionId: input.sessionId,\n answerId,\n outcome: \"unanswered\",\n fallback: LOCAL_FALLBACK,\n };\n }\n lastTopic.set(input.sessionId, topic);\n return {\n sessionId: input.sessionId,\n answerId,\n outcome: \"answered\",\n answer: direct ? ANSWERS[topic] : FOLLOW_ANSWERS[topic],\n citations: [{ ...LOCAL_CITATIONS[topic] }],\n };\n },\n async feedback() {},\n };\n}\n\n/** Returns the caller adapter. Question text stays on the customer backend. */\nexport function createByoAdapter(impl: AnswersAdapter): AnswersAdapter {\n return impl;\n}\n\nconst HOSTED_ORIGIN = \"https://answers.bootstrapware.co\";\n\n/**\n * Posts to Hosted `POST /api/v1/ask?appId=` with the publishable key.\n * The widget uses this when `appId` and `publishableKey` are set and `adapter` is omitted.\n */\nexport function createHostedAdapter(options: {\n appId: string;\n publishableKey: string;\n apiBaseUrl?: string;\n}): AnswersAdapter {\n const base = (options.apiBaseUrl ?? HOSTED_ORIGIN).replace(/\\/$/, \"\");\n return {\n async ask(input) {\n const response = await fetch(`${base}/api/v1/ask?appId=${encodeURIComponent(options.appId)}`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${options.publishableKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n sessionId: input.sessionId,\n question: input.question,\n pageContext: input.pageContext,\n }),\n });\n const json = (await response.json().catch(() => ({}))) as {\n data?: AnswersResult;\n error?: { message?: string };\n };\n if (!response.ok) throw new Error(json.error?.message ?? \"Could not ask.\");\n if (!json.data) throw new Error(\"Could not ask.\");\n return json.data;\n },\n };\n}\n","import { useEffect, useState, type ReactNode } from \"react\";\nimport type { AnswersPresentation, AnswersTheme } from \"./types\";\n\nexport function EmbedFrame({\n presentation = \"section\",\n defaultOpen = false,\n openSignal = 0,\n label,\n className,\n theme,\n children,\n}: {\n presentation?: AnswersPresentation;\n defaultOpen?: boolean;\n /** Increment to open the frame (keyboard shortcut). */\n openSignal?: number;\n label: string;\n className?: string;\n theme?: AnswersTheme;\n children: ReactNode;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n const openKey = `${presentation}\\0${defaultOpen ? \"1\" : \"0\"}`;\n const [openKeySeen, setOpenKeySeen] = useState(openKey);\n if (openKey !== openKeySeen) {\n setOpenKeySeen(openKey);\n setOpen(defaultOpen);\n }\n const [signalSeen, setSignalSeen] = useState(openSignal);\n if (openSignal !== signalSeen) {\n setSignalSeen(openSignal);\n if (openSignal > 0) setOpen(true);\n }\n const launcher = presentation === \"launcher\";\n const floating = launcher || presentation === \"window\";\n useEffect(() => {\n if (presentation === \"section\" || !open) return;\n function onKey(event: KeyboardEvent) {\n if (event.key === \"Escape\") setOpen(false);\n }\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open, presentation]);\n if (presentation === \"section\") return children;\n const classes = [\n \"bsw-an-frame\",\n `bsw-an-frame--${presentation}`,\n open ? \"bsw-an-frame--open\" : \"\",\n floating ? \"bsw-an--launcher-host\" : \"\",\n floating && open ? \"bsw-an--launcher-open\" : \"\",\n floating && !open ? \"bsw-an--launcher-closed\" : \"\",\n className ?? \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n return (\n <div className={classes} data-theme={theme} data-state={open ? \"open\" : \"closed\"}>\n {open && (presentation === \"modal\" || launcher) ? (\n <button type=\"button\" className=\"bsw-an-frame__backdrop\" aria-label={`Close ${label}`} onClick={() => setOpen(false)} />\n ) : null}\n <div\n className=\"bsw-an-frame__panel\"\n hidden={!open}\n role={open && presentation === \"modal\" ? \"dialog\" : open ? \"region\" : undefined}\n aria-modal={open && presentation === \"modal\" ? true : undefined}\n aria-label={open ? label : undefined}\n >\n <button type=\"button\" className=\"bsw-an-frame__close\" onClick={() => setOpen(false)}>\n <span className=\"bsw-an-frame__close-label\">Close</span>\n <span aria-hidden=\"true\">×</span>\n </button>\n {children}\n </div>\n {open ? null : (\n <button\n type=\"button\"\n className={launcher ? \"bsw-an-frame__fab\" : \"bsw-an-frame__trigger\"}\n aria-label={launcher ? \"Open answers\" : undefined}\n onClick={() => setOpen(true)}\n >\n {launcher ? \"Ask\" : label}\n </button>\n )}\n </div>\n );\n}\n","import type { ReactNode } from \"react\";\nimport { safeHttpUrl } from \"./result\";\n\nfunction inlineMarkup(text: string, allowed: ReadonlySet<string>, keyPrefix: string): ReactNode[] {\n const pattern = /(\\*\\*[^*\\n]+\\*\\*|(?<![A-Za-z0-9])_[^_\\n]+_(?![A-Za-z0-9])|`[^`\\n]+`|\\[[^\\]\\n]+\\]\\(https?:\\/\\/[^)\\s]+\\))/g;\n const parts: ReactNode[] = [];\n let last = 0;\n let index = 0;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(text))) {\n if (match.index > last) parts.push(text.slice(last, match.index));\n const token = match[0];\n const key = `${keyPrefix}-${index++}`;\n if (token.startsWith(\"**\") && token.endsWith(\"**\") && token.length >= 4) {\n parts.push(<strong key={key}>{token.slice(2, -2)}</strong>);\n } else if (token.startsWith(\"_\") && token.endsWith(\"_\") && token.length >= 3) {\n parts.push(<em key={key}>{token.slice(1, -1)}</em>);\n } else if (token.startsWith(\"`\") && token.endsWith(\"`\") && token.length >= 3) {\n parts.push(<code key={key}>{token.slice(1, -1)}</code>);\n } else if (token.startsWith(\"[\")) {\n const link = /^\\[([^\\]\\n]+)\\]\\((https?:\\/\\/[^)\\s]+)\\)$/.exec(token);\n const href = link?.[2] ?? \"\";\n const safe = safeHttpUrl(href);\n if (link && safe && (allowed.has(href) || allowed.has(safe))) {\n parts.push(\n <a key={key} href={safe} target=\"_blank\" rel=\"noopener noreferrer\">\n {link[1]}\n </a>,\n );\n } else {\n parts.push(token);\n }\n } else {\n parts.push(token);\n }\n last = match.index + token.length;\n }\n if (last < text.length) parts.push(text.slice(last));\n return parts;\n}\n\n/** Bold, italic, code, and simple lists. Links render only for citation URLs. HTML stays text. */\nexport function renderAnswerMarkdown(text: string, allowedUrls: ReadonlySet<string>): ReactNode {\n const lines = text.split(\"\\n\");\n const nodes: ReactNode[] = [];\n let paragraph: string[] = [];\n let list: string[] = [];\n let key = 0;\n\n function flushParagraph() {\n if (!paragraph.length) return;\n const content = paragraph.join(\"\\n\");\n nodes.push(<p key={`p-${key++}`}>{inlineMarkup(content, allowedUrls, `p-${key}`)}</p>);\n paragraph = [];\n }\n\n function flushList() {\n if (!list.length) return;\n const items = list;\n nodes.push(\n <ul key={`ul-${key++}`}>\n {items.map((item, index) => (\n <li key={`li-${key}-${index}`}>{inlineMarkup(item, allowedUrls, `li-${key}-${index}`)}</li>\n ))}\n </ul>,\n );\n list = [];\n }\n\n for (const line of lines) {\n const item = /^\\s*[-*]\\s+(.*)$/.exec(line);\n if (item) {\n flushParagraph();\n list.push(item[1] ?? \"\");\n continue;\n }\n if (!line.trim()) {\n flushParagraph();\n flushList();\n continue;\n }\n flushList();\n paragraph.push(line);\n }\n flushParagraph();\n flushList();\n return nodes;\n}\n","const SESSION_ID = /^ans_[0-9a-f]{24}$/;\n\nexport function sessionStorageKey(appId?: string): string {\n const scope = appId?.trim() ? appId.trim() : \"local\";\n return `bsw_an_session:${scope}`;\n}\n\nexport function createOpaqueSessionId(): string {\n const bytes = new Uint8Array(12);\n globalThis.crypto.getRandomValues(bytes);\n let hex = \"\";\n for (const byte of bytes) hex += byte.toString(16).padStart(2, \"0\");\n return `ans_${hex}`;\n}\n\n/** Reads the visitor session from sessionStorage. Never writes a cookie. */\nexport function readSessionId(storage: Storage | null, appId?: string): string {\n if (!storage) return createOpaqueSessionId();\n const key = sessionStorageKey(appId);\n const existing = storage.getItem(key);\n if (existing && SESSION_ID.test(existing)) return existing;\n const id = createOpaqueSessionId();\n storage.setItem(key, id);\n return id;\n}\n","import { createRoot, type Root } from \"react-dom/client\";\nimport { Answers } from \"./Answers\";\nimport type { AnswersProps } from \"./types\";\n\nconst roots = new WeakMap<Element, Root>();\n\nexport function mount(el: Element, props: AnswersProps = {}): () => void {\n let root = roots.get(el);\n if (!root) {\n root = createRoot(el);\n roots.set(el, root);\n }\n root.render(<Answers {...props} />);\n return () => unmount(el);\n}\n\nexport function unmount(el: Element): void {\n const root = roots.get(el);\n if (!root) return;\n root.unmount();\n roots.delete(el);\n}\n"],"mappings":";;;;AAAA,SAAS,aAAAA,YAAW,OAAO,QAAQ,YAAAC,iBAAgC;;;ACEnE,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,eAAe;AACrB,IAAM,iBAAiB;AAEhB,SAAS,gBAAgB,OAAuB;AACrD,SAAO,MAAM,KAAK,KAAK,EAAE;AAC3B;AAEO,SAAS,eAAe,OAAe,KAAqB;AACjE,MAAI,QAAQ;AACZ,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,QAAI,SAAS,IAAK;AAClB,WAAO;AACP,aAAS;AAAA,EACX;AACA,SAAO;AACT;AAEO,SAAS,YAAY,OAAoC;AAC9D,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,OAAO;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,aAAa,WAAW,OAAO,aAAa,SAAU,QAAO;AACxE,SAAO,OAAO,SAAS;AACzB;AAEO,SAAS,kBAAkB,OAAmC;AACnE,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,QAAM,MAAyB,CAAC;AAChC,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,QAAQ,OAAO;AACxB,QAAI,IAAI,UAAU,aAAc;AAChC,QAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAM,MAAM;AACZ,QAAI,OAAO,IAAI,OAAO,YAAY,CAAC,IAAI,GAAG,KAAK,EAAG;AAClD,UAAM,KAAK,IAAI,GAAG,KAAK;AACvB,QAAI,KAAK,IAAI,EAAE,EAAG;AAClB,SAAK,IAAI,EAAE;AACX,UAAM,QAAQ,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,IAAI,eAAe,IAAI,MAAM,KAAK,GAAG,GAAG,IAAI;AAC1G,UAAM,UAAU,OAAO,IAAI,YAAY,YAAY,IAAI,QAAQ,KAAK,IAAI,eAAe,IAAI,QAAQ,KAAK,GAAG,WAAW,IAAI;AAC1H,UAAM,MAAM,YAAY,IAAI,GAAG;AAC/B,QAAI,KAAK,EAAE,IAAI,OAAO,SAAS,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAGO,SAAS,eAAe,OAAsC;AACnE,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,MAAM;AACZ,MAAI,OAAO,IAAI,aAAa,YAAY,CAAC,IAAI,SAAS,KAAK,EAAG,QAAO;AACrE,MAAI,OAAO,IAAI,cAAc,SAAU,QAAO;AAC9C,QAAM,WAAW,IAAI,SAAS,KAAK;AACnC,QAAM,YAAY,IAAI;AACtB,QAAM,WAAW,OAAO,IAAI,aAAa,YAAY,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;AAEjG,MAAI,IAAI,YAAY,cAAc;AAChC,WAAO,EAAE,WAAW,UAAU,SAAS,cAAc,SAAS;AAAA,EAChE;AACA,MAAI,IAAI,YAAY,WAAY,QAAO;AAEvC,QAAM,SAAS,OAAO,IAAI,WAAW,WAAW,IAAI,OAAO,KAAK,IAAI;AACpE,MAAI,CAAC,OAAQ,QAAO,EAAE,WAAW,UAAU,SAAS,cAAc,SAAS;AAC3E,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,QAAQ,eAAe,QAAQ,UAAU;AAAA,IACzC,WAAW,kBAAkB,IAAI,SAAS;AAAA,EAC5C;AACF;;;AChFO,IAAM,eAAe;AAErB,IAAM,oBAAoB;;;ACC1B,IAAM,iBACX;AAEK,IAAM,gBAAgB;AAAA,EAC3B,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AACf;AAIA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AACvB,IAAM,eACJ;AACF,IAAM,eAAe;AACrB,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AAEjB,IAAM,kBAAkD;AAAA,EAC7D,OAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,IACL,SAAS;AAAA,EACX;AAAA,EACA,SAAS;AAAA,IACP,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,IACL,SAAS;AAAA,EACX;AAAA,EACA,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,KAAK;AAAA,IACL,SAAS;AAAA,EACX;AACF;AAEA,IAAM,SAA2C;AAAA,EAC/C,EAAE,IAAI,WAAW,OAAO,CAAC,UAAU,WAAW,UAAU,WAAW,YAAY,WAAW,EAAE;AAAA,EAC5F,EAAE,IAAI,YAAY,OAAO,CAAC,QAAQ,YAAY,UAAU,EAAE;AAAA,EAC1D,EAAE,IAAI,SAAS,OAAO,CAAC,QAAQ,SAAS,QAAQ,WAAW,WAAW,YAAY,UAAU,WAAW,UAAU,EAAE;AACrH;AAEA,IAAM,UAAiC;AAAA,EACrC,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AACZ;AAEA,IAAM,iBAAwC;AAAA,EAC5C,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AACZ;AAEA,IAAM,SAA8C;AAAA,EAClD,EAAE,QAAQ,WAAW;AAAA,EACrB,EAAE,QAAQ,qBAAqB;AAAA,EAC/B,EAAE,QAAQ,qBAAqB,OAAO,QAAQ;AAAA,EAC9C,EAAE,QAAQ,kBAAkB;AAAA,EAC5B,EAAE,QAAQ,sBAAsB,OAAO,WAAW;AAAA,EAClD,EAAE,QAAQ,oBAAoB,OAAO,WAAW;AAAA,EAChD,EAAE,QAAQ,eAAe;AAC3B;AAEA,SAAS,UAAU,UAA0B;AAC3C,SAAO,SACJ,KAAK,EACL,YAAY,EACZ,QAAQ,aAAa,EAAE,EACvB,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACV;AAEA,SAAS,QAAQ,UAAkB,MAAuB;AACxD,SAAO,IAAI,OAAO,MAAM,IAAI,KAAK,EAAE,KAAK,QAAQ;AAClD;AAEA,SAAS,YAAY,UAAgC;AACnD,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,MAAM,KAAK,CAAC,SAAS,QAAQ,UAAU,IAAI,CAAC,EAAG,QAAO,MAAM;AAAA,EACxE;AACA,SAAO;AACT;AAEA,SAAS,YAAY,UAAkB,MAAuC;AAC5E,QAAM,MAAM,OAAO,KAAK,CAAC,SAAS,KAAK,WAAW,QAAQ;AAC1D,MAAI,CAAC,OAAO,CAAC,KAAM,QAAO;AAC1B,MAAI,IAAI,SAAS,IAAI,UAAU,KAAM,QAAO;AAC5C,SAAO;AACT;AAEA,SAAS,aAAa,UAAiC;AACrD,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,gBAAgB,OAAO,IAAI,aAAc,QAAO;AACpD,SAAO;AACT;AAMO,SAAS,qBAAqC;AACnD,QAAM,YAAY,oBAAI,IAAmB;AACzC,MAAI,WAAW;AAEf,SAAO;AAAA,IACL,MAAM,IAAI,OAA+B;AACvC,YAAM,UAAU,aAAa,MAAM,QAAQ;AAC3C,UAAI,QAAS,OAAM,IAAI,MAAM,OAAO;AACpC,YAAM,WAAW,UAAU,MAAM,QAAQ;AACzC,YAAM,SAAS,YAAY,QAAQ;AACnC,YAAM,QAAQ,UAAU,YAAY,UAAU,UAAU,IAAI,MAAM,SAAS,CAAC;AAC5E,kBAAY;AACZ,YAAM,WAAW,OAAO,SAAS,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG,CAAC;AAC/D,UAAI,CAAC,OAAO;AACV,eAAO;AAAA,UACL,WAAW,MAAM;AAAA,UACjB;AAAA,UACA,SAAS;AAAA,UACT,UAAU;AAAA,QACZ;AAAA,MACF;AACA,gBAAU,IAAI,MAAM,WAAW,KAAK;AACpC,aAAO;AAAA,QACL,WAAW,MAAM;AAAA,QACjB;AAAA,QACA,SAAS;AAAA,QACT,QAAQ,SAAS,QAAQ,KAAK,IAAI,eAAe,KAAK;AAAA,QACtD,WAAW,CAAC,EAAE,GAAG,gBAAgB,KAAK,EAAE,CAAC;AAAA,MAC3C;AAAA,IACF;AAAA,IACA,MAAM,WAAW;AAAA,IAAC;AAAA,EACpB;AACF;AAGO,SAAS,iBAAiB,MAAsC;AACrE,SAAO;AACT;AAEA,IAAM,gBAAgB;AAMf,SAAS,oBAAoB,SAIjB;AACjB,QAAM,QAAQ,QAAQ,cAAc,eAAe,QAAQ,OAAO,EAAE;AACpE,SAAO;AAAA,IACL,MAAM,IAAI,OAAO;AACf,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,qBAAqB,mBAAmB,QAAQ,KAAK,CAAC,IAAI;AAAA,QAC5F,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,QAAQ,cAAc;AAAA,UAC/C,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,WAAW,MAAM;AAAA,UACjB,UAAU,MAAM;AAAA,UAChB,aAAa,MAAM;AAAA,QACrB,CAAC;AAAA,MACH,CAAC;AACD,YAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAIpD,UAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,KAAK,OAAO,WAAW,gBAAgB;AACzE,UAAI,CAAC,KAAK,KAAM,OAAM,IAAI,MAAM,gBAAgB;AAChD,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACF;;;ACzLA,SAAS,WAAW,gBAAgC;AA0D5C,cASA,YATA;AAvDD,SAAS,WAAW;AAAA,EACzB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,WAAW;AAC5C,QAAM,UAAU,GAAG,YAAY,KAAK,cAAc,MAAM,GAAG;AAC3D,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,OAAO;AACtD,MAAI,YAAY,aAAa;AAC3B,mBAAe,OAAO;AACtB,YAAQ,WAAW;AAAA,EACrB;AACA,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,UAAU;AACvD,MAAI,eAAe,YAAY;AAC7B,kBAAc,UAAU;AACxB,QAAI,aAAa,EAAG,SAAQ,IAAI;AAAA,EAClC;AACA,QAAM,WAAW,iBAAiB;AAClC,QAAM,WAAW,YAAY,iBAAiB;AAC9C,YAAU,MAAM;AACd,QAAI,iBAAiB,aAAa,CAAC,KAAM;AACzC,aAAS,MAAM,OAAsB;AACnC,UAAI,MAAM,QAAQ,SAAU,SAAQ,KAAK;AAAA,IAC3C;AACA,WAAO,iBAAiB,WAAW,KAAK;AACxC,WAAO,MAAM,OAAO,oBAAoB,WAAW,KAAK;AAAA,EAC1D,GAAG,CAAC,MAAM,YAAY,CAAC;AACvB,MAAI,iBAAiB,UAAW,QAAO;AACvC,QAAM,UAAU;AAAA,IACd;AAAA,IACA,iBAAiB,YAAY;AAAA,IAC7B,OAAO,uBAAuB;AAAA,IAC9B,WAAW,0BAA0B;AAAA,IACrC,YAAY,OAAO,0BAA0B;AAAA,IAC7C,YAAY,CAAC,OAAO,4BAA4B;AAAA,IAChD,aAAa;AAAA,EACf,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AACX,SACE,qBAAC,SAAI,WAAW,SAAS,cAAY,OAAO,cAAY,OAAO,SAAS,UACrE;AAAA,aAAS,iBAAiB,WAAW,YACpC,oBAAC,YAAO,MAAK,UAAS,WAAU,0BAAyB,cAAY,SAAS,KAAK,IAAI,SAAS,MAAM,QAAQ,KAAK,GAAG,IACpH;AAAA,IACJ;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,QAAQ,CAAC;AAAA,QACT,MAAM,QAAQ,iBAAiB,UAAU,WAAW,OAAO,WAAW;AAAA,QACtE,cAAY,QAAQ,iBAAiB,UAAU,OAAO;AAAA,QACtD,cAAY,OAAO,QAAQ;AAAA,QAE3B;AAAA,+BAAC,YAAO,MAAK,UAAS,WAAU,uBAAsB,SAAS,MAAM,QAAQ,KAAK,GAChF;AAAA,gCAAC,UAAK,WAAU,6BAA4B,mBAAK;AAAA,YACjD,oBAAC,UAAK,eAAY,QAAO,kBAAC;AAAA,aAC5B;AAAA,UACC;AAAA;AAAA;AAAA,IACH;AAAA,IACC,OAAO,OACN;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAW,WAAW,sBAAsB;AAAA,QAC5C,cAAY,WAAW,iBAAiB;AAAA,QACxC,SAAS,MAAM,QAAQ,IAAI;AAAA,QAE1B,qBAAW,QAAQ;AAAA;AAAA,IACtB;AAAA,KAEJ;AAEJ;;;ACvEiB,gBAAAC,YAAA;AAXjB,SAAS,aAAa,MAAc,SAA8B,WAAgC;AAChG,QAAM,UAAU;AAChB,QAAM,QAAqB,CAAC;AAC5B,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI;AACJ,SAAQ,QAAQ,QAAQ,KAAK,IAAI,GAAI;AACnC,QAAI,MAAM,QAAQ,KAAM,OAAM,KAAK,KAAK,MAAM,MAAM,MAAM,KAAK,CAAC;AAChE,UAAM,QAAQ,MAAM,CAAC;AACrB,UAAM,MAAM,GAAG,SAAS,IAAI,OAAO;AACnC,QAAI,MAAM,WAAW,IAAI,KAAK,MAAM,SAAS,IAAI,KAAK,MAAM,UAAU,GAAG;AACvE,YAAM,KAAK,gBAAAA,KAAC,YAAkB,gBAAM,MAAM,GAAG,EAAE,KAAvB,GAAyB,CAAS;AAAA,IAC5D,WAAW,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,UAAU,GAAG;AAC5E,YAAM,KAAK,gBAAAA,KAAC,QAAc,gBAAM,MAAM,GAAG,EAAE,KAAvB,GAAyB,CAAK;AAAA,IACpD,WAAW,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,UAAU,GAAG;AAC5E,YAAM,KAAK,gBAAAA,KAAC,UAAgB,gBAAM,MAAM,GAAG,EAAE,KAAvB,GAAyB,CAAO;AAAA,IACxD,WAAW,MAAM,WAAW,GAAG,GAAG;AAChC,YAAM,OAAO,2CAA2C,KAAK,KAAK;AAClE,YAAM,OAAO,OAAO,CAAC,KAAK;AAC1B,YAAM,OAAO,YAAY,IAAI;AAC7B,UAAI,QAAQ,SAAS,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,IAAI;AAC5D,cAAM;AAAA,UACJ,gBAAAA,KAAC,OAAY,MAAM,MAAM,QAAO,UAAS,KAAI,uBAC1C,eAAK,CAAC,KADD,GAER;AAAA,QACF;AAAA,MACF,OAAO;AACL,cAAM,KAAK,KAAK;AAAA,MAClB;AAAA,IACF,OAAO;AACL,YAAM,KAAK,KAAK;AAAA,IAClB;AACA,WAAO,MAAM,QAAQ,MAAM;AAAA,EAC7B;AACA,MAAI,OAAO,KAAK,OAAQ,OAAM,KAAK,KAAK,MAAM,IAAI,CAAC;AACnD,SAAO;AACT;AAGO,SAAS,qBAAqB,MAAc,aAA6C;AAC9F,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAM,QAAqB,CAAC;AAC5B,MAAI,YAAsB,CAAC;AAC3B,MAAI,OAAiB,CAAC;AACtB,MAAI,MAAM;AAEV,WAAS,iBAAiB;AACxB,QAAI,CAAC,UAAU,OAAQ;AACvB,UAAM,UAAU,UAAU,KAAK,IAAI;AACnC,UAAM,KAAK,gBAAAA,KAAC,OAAsB,uBAAa,SAAS,aAAa,KAAK,GAAG,EAAE,KAA5D,KAAK,KAAK,EAAoD,CAAI;AACrF,gBAAY,CAAC;AAAA,EACf;AAEA,WAAS,YAAY;AACnB,QAAI,CAAC,KAAK,OAAQ;AAClB,UAAM,QAAQ;AACd,UAAM;AAAA,MACJ,gBAAAA,KAAC,QACE,gBAAM,IAAI,CAAC,MAAM,UAChB,gBAAAA,KAAC,QAA+B,uBAAa,MAAM,aAAa,MAAM,GAAG,IAAI,KAAK,EAAE,KAA3E,MAAM,GAAG,IAAI,KAAK,EAA2D,CACvF,KAHM,MAAM,KAAK,EAIpB;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AAEA,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,mBAAmB,KAAK,IAAI;AACzC,QAAI,MAAM;AACR,qBAAe;AACf,WAAK,KAAK,KAAK,CAAC,KAAK,EAAE;AACvB;AAAA,IACF;AACA,QAAI,CAAC,KAAK,KAAK,GAAG;AAChB,qBAAe;AACf,gBAAU;AACV;AAAA,IACF;AACA,cAAU;AACV,cAAU,KAAK,IAAI;AAAA,EACrB;AACA,iBAAe;AACf,YAAU;AACV,SAAO;AACT;;;ACvFA,IAAM,aAAa;AAEZ,SAAS,kBAAkB,OAAwB;AACxD,QAAM,QAAQ,OAAO,KAAK,IAAI,MAAM,KAAK,IAAI;AAC7C,SAAO,kBAAkB,KAAK;AAChC;AAEO,SAAS,wBAAgC;AAC9C,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,aAAW,OAAO,gBAAgB,KAAK;AACvC,MAAI,MAAM;AACV,aAAW,QAAQ,MAAO,QAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAClE,SAAO,OAAO,GAAG;AACnB;AAGO,SAAS,cAAc,SAAyB,OAAwB;AAC7E,MAAI,CAAC,QAAS,QAAO,sBAAsB;AAC3C,QAAM,MAAM,kBAAkB,KAAK;AACnC,QAAM,WAAW,QAAQ,QAAQ,GAAG;AACpC,MAAI,YAAY,WAAW,KAAK,QAAQ,EAAG,QAAO;AAClD,QAAM,KAAK,sBAAsB;AACjC,UAAQ,QAAQ,KAAK,EAAE;AACvB,SAAO;AACT;;;ANyBM,gBAAAC,MAGI,QAAAC,aAHJ;AA3BN,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,iBAAiB;AAEvB,SAAS,aAAa,OAAwB;AAC5C,QAAM,MAAM,kBAAkB,KAAK;AACnC,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAS,OAAO;AAAA,IACxC;AAAA,IACA,IAAI,cAAc,OAAO,WAAW,cAAc,OAAO,OAAO,gBAAgB,KAAK;AAAA,EACvF,EAAE;AACF,MAAI,MAAM,QAAQ,KAAK;AACrB,aAAS;AAAA,MACP;AAAA,MACA,IAAI,cAAc,OAAO,WAAW,cAAc,OAAO,OAAO,gBAAgB,KAAK;AAAA,IACvF,CAAC;AAAA,EACH;AACA,SAAO,MAAM;AACf;AAEA,SAAS,aAAa,OAAuB;AAC3C,SAAO,UAAU,IAAI,aAAa,GAAG,KAAK;AAC5C;AAEA,SAAS,gBAAgB,EAAE,UAAU,GAAqC;AACxE,MAAI,CAAC,UAAU,OAAQ,QAAO;AAC9B,SACE,gBAAAD,MAAC,aAAQ,WAAU,wBACjB;AAAA,oBAAAD,KAAC,aAAQ,WAAU,0BAA0B,uBAAa,UAAU,MAAM,GAAE;AAAA,IAC5E,gBAAAA,KAAC,QAAG,WAAU,mBACX,oBAAU,IAAI,CAAC,aACd,gBAAAC,MAAC,QAAqB,WAAU,kBAAiB,oBAAkB,SAAS,IACzE;AAAA,eAAS,MACR,gBAAAD,KAAC,OAAE,MAAM,SAAS,KAAK,QAAO,UAAS,KAAI,uBACxC,mBAAS,OACZ,IAEA,gBAAAA,KAAC,UAAK,WAAU,wBAAwB,mBAAS,OAAM;AAAA,MAExD,SAAS,UAAU,gBAAAA,KAAC,OAAE,WAAU,mBAAmB,mBAAS,SAAQ,IAAO;AAAA,SARrE,SAAS,EASlB,CACD,GACH;AAAA,KACF;AAEJ;AAEA,SAAS,QAAiB;AACxB,MAAI,OAAO,cAAc,YAAa,QAAO;AAC7C,SAAO,mBAAmB,KAAK,UAAU,SAAS,KAAK,OAAO,KAAK,UAAU,YAAY,EAAE;AAC7F;AAGO,SAAS,gBACd,OACA,MACA,MAAM,MAAM,GACH;AACT,QAAM,QAAQ,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,OAAO,OAAO;AACrF,QAAM,MAAM,MAAM,MAAM,SAAS,CAAC;AAClC,MAAI,CAAC,OAAO,MAAM,IAAI,YAAY,MAAM,IAAK,QAAO;AACpD,QAAM,OAAO,IAAI,IAAI,MAAM,MAAM,GAAG,EAAE,CAAC;AACvC,QAAM,WAAW,KAAK,IAAI,MAAM,KAAM,KAAK,IAAI,KAAK,KAAK,CAAC;AAC1D,QAAM,WAAW,KAAK,IAAI,MAAM,KAAM,KAAK,IAAI,KAAK,KAAK;AACzD,MAAI,aAAa,MAAM,QAAS,QAAO;AACvC,MAAI,aAAa,MAAM,QAAS,QAAO;AACvC,MAAI,KAAK,IAAI,KAAK,MAAM,MAAM,OAAQ,QAAO;AAC7C,MAAI,KAAK,IAAI,OAAO,MAAM,MAAM,SAAU,QAAO;AACjD,SAAO;AACT;AAEA,SAAS,kBAAkB,UAA8D;AACvF,MAAI,UAAU;AACZ,UAAM,MAAM,OAAO,SAAS,QAAQ,WAAW,SAAS,MAAM;AAC9D,UAAM,QAAQ,OAAO,SAAS,UAAU,WAAW,SAAS,QAAQ;AACpE,WAAO,EAAE,KAAK,MAAM;AAAA,EACtB;AACA,MAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAC3C,SAAO,EAAE,KAAK,OAAO,SAAS,MAAM,OAAO,SAAS,MAAM;AAC5D;AAEO,SAAS,QAAQ,OAAqB;AAC3C,QAAM,eAAe,MAAM,gBAAgB;AAC3C,QAAM,YAAY,aAAa,MAAM,KAAK;AAC1C,QAAM,WAAW,OAA8B,IAAI;AACnD,QAAM,SAAS,CAAC,MAAM,WAAW,MAAM,SAAS,MAAM,iBAClD,oBAAoB,EAAE,OAAO,MAAM,OAAO,gBAAgB,MAAM,gBAAgB,YAAY,MAAM,WAAW,CAAC,IAC9G;AACJ,QAAM,UAAU,MAAM,WAAW,WAAW,MAAM,iBAAiB,OAAQ,SAAS,YAAY,mBAAmB;AACnH,QAAM,OAAO,MAAM,UAAU,QAAQ,SAAS,WAAW,MAAM,iBAAiB,YAAY;AAC5F,QAAM,CAAC,OAAO,QAAQ,IAAIE,UAAS,EAAE;AACrC,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,CAAC;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAwB,IAAI;AAC1E,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAiB,CAAC,CAAC;AAC7C,QAAM,aAAa,OAAO,KAAK;AAC/B,QAAM,SAAS,OAAuB,IAAI;AAC1C,QAAM,cAAc,OAA4B,IAAI;AACpD,QAAM,eAAe,OAAO,oBAAI,IAAY,CAAC;AAC7C,QAAM,gBAAgB,OAAO,MAAM,UAAU;AAC7C,gBAAc,UAAU,MAAM;AAE9B,EAAAC,WAAU,MAAM;AACd,UAAM,MAAM,OAAO;AACnB,QAAI,CAAC,IAAK;AACV,QAAI,YAAY,IAAI;AAAA,EACtB,GAAG,CAAC,OAAO,SAAS,iBAAiB,KAAK,CAAC;AAE3C,EAAAA,WAAU,MAAM;AACd,UAAM,OAAO,MAAM,UAAU,KAAK;AAClC,QAAI,CAAC,KAAM;AACX,aAAS,MAAM,OAAsB;AACnC,UAAI,CAAC,gBAAgB,OAAO,IAAK,EAAG;AACpC,YAAM,eAAe;AACrB,oBAAc,CAAC,YAAY,UAAU,CAAC;AAAA,IACxC;AACA,WAAO,iBAAiB,WAAW,KAAK;AACxC,WAAO,MAAM,OAAO,oBAAoB,WAAW,KAAK;AAAA,EAC1D,GAAG,CAAC,MAAM,QAAQ,CAAC;AAEnB,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,WAAY;AACjB,gBAAY,SAAS,MAAM;AAAA,EAC7B,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,SAAS,gBAAgB,OAAO;AACtC,QAAM,YAAY,SAAS;AAC3B,QAAM,UAAU,MAAM;AAEtB,iBAAe,YAAY,KAAa;AACtC,QAAI,CAAC,WAAW,WAAW,QAAS;AACpC,UAAM,WAAW,IAAI,KAAK;AAC1B,QAAI,CAAC,UAAU;AACb,eAAS,mBAAmB;AAC5B;AAAA,IACF;AACA,QAAI,gBAAgB,QAAQ,IAAI,cAAc;AAC5C,eAAS,aAAa;AACtB;AAAA,IACF;AACA,UAAM,cAAc,kBAAkB,MAAM,WAAW;AACvD,eAAW,UAAU;AACrB,eAAW,IAAI;AACf,uBAAmB,QAAQ;AAC3B,aAAS,EAAE;AACX,aAAS,IAAI;AACb,QAAI;AACF,YAAM,YAAY,MAAM,QAAQ,IAAI,EAAE,WAAW,UAAU,YAAY,CAAC;AACxE,YAAM,SAAS,eAAe,SAAS;AACvC,UAAI,CAAC,QAAQ;AACX,iBAAS,QAAQ;AACjB,iBAAS,aAAa;AACtB;AAAA,MACF;AACA,UAAI,OAAO,YAAY,aAAc,eAAc,UAAU,MAAM;AACnE,eAAS,CAAC,YAAY,CAAC,GAAG,SAAS,EAAE,UAAU,QAAQ,UAAU,OAAO,CAAC,CAAC;AAAA,IAC5E,QAAQ;AACN,eAAS,QAAQ;AACjB,eAAS,aAAa;AAAA,IACxB,UAAE;AACA,iBAAW,UAAU;AACrB,iBAAW,KAAK;AAChB,yBAAmB,IAAI;AAAA,IACzB;AAAA,EACF;AAEA,WAAS,SAAS,OAAmC;AACnD,UAAM,eAAe;AACrB,SAAK,YAAY,KAAK;AAAA,EACxB;AAEA,iBAAe,WAAW,UAAkB,SAAkB;AAC5D,QAAI,CAAC,SAAS,YAAY,aAAa,QAAQ,IAAI,QAAQ,EAAG;AAC9D,UAAM,UAAU,MAAM,KAAK,CAAC,SAAS,KAAK,OAAO,aAAa,QAAQ;AACtE,QAAI,CAAC,WAAW,QAAQ,aAAa,aAAa,QAAQ,aAAa,OAAQ;AAC/E,iBAAa,QAAQ,IAAI,QAAQ;AACjC,aAAS,CAAC,UAAU,MAAM,IAAI,CAAC,SAAU,KAAK,OAAO,aAAa,WAAW,EAAE,GAAG,MAAM,UAAU,UAAU,IAAI,IAAK,CAAC;AACtH,QAAI;AACF,YAAM,QAAQ,SAAS,EAAE,WAAW,UAAU,QAAQ,CAAC;AACvD,eAAS,CAAC,UAAU,MAAM,IAAI,CAAC,SAAU,KAAK,OAAO,aAAa,WAAW,EAAE,GAAG,MAAM,UAAU,OAAO,IAAI,IAAK,CAAC;AAAA,IACrH,QAAQ;AACN,mBAAa,QAAQ,OAAO,QAAQ;AACpC,eAAS,CAAC,UAAU,MAAM,IAAI,CAAC,SAAU,KAAK,OAAO,aAAa,WAAW,EAAE,GAAG,MAAM,UAAU,QAAQ,IAAI,IAAK,CAAC;AAAA,IACtH;AAAA,EACF;AAEA,QAAM,QACJ,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,mBAAmB,iBAAiB,YAAY,MAAM,YAAY,EAAE,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MAC1G,cAAY,MAAM;AAAA,MAClB,aAAW;AAAA,MAEX;AAAA,wBAAAA,MAAC,YAAO,WAAU,kBAChB;AAAA,0BAAAD,KAAC,UAAK,WAAU,gBAAe,eAAY,QAAO;AAAA,UAClD,gBAAAC,MAAC,SAAI,WAAU,mBACb;AAAA,4BAAAD,KAAC,QAAG,WAAU,iBAAgB,qBAAO;AAAA,YACpC,SAAS,UAAU,gBAAAA,KAAC,OAAE,WAAU,gBAAe,sDAAwC,IAAO;AAAA,aACjG;AAAA,WACF;AAAA,QACA,gBAAAA,KAAC,SAAI,WAAU,eAAc,KAAK,QAC/B,gBAAM,WAAW,KAAK,CAAC,kBACtB,gBAAAA,KAAC,OAAE,WAAU,iBACV,mBAAS,YACN,0CACA,SAAS,UACP,6FACA,MAAM,YAAY,6CAC1B,IAEA,gBAAAC,MAAC,QAAG,WAAU,iBACX;AAAA,gBAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,kBAAM,YAAY,KAAK,OAAO,YAAY,aAAc,KAAK,OAAO,aAAa,CAAC,IAAK,CAAC;AACxF,kBAAM,UAAU,IAAI,IAAI,UAAU,IAAI,CAAC,aAAa,SAAS,GAAG,EAAE,OAAO,CAAC,QAAuB,QAAQ,GAAG,CAAC,CAAC;AAC9G,mBACE,gBAAAA,MAAC,QAA4C,WAAU,gBAAe,gBAAc,KAAK,OAAO,SAC9F;AAAA,8BAAAD,KAAC,SAAI,WAAU,oCACb,0BAAAA,KAAC,OAAE,WAAU,oBAAoB,eAAK,UAAS,GACjD;AAAA,cACA,gBAAAC,MAAC,SAAI,WAAU,kCACZ;AAAA,qBAAK,OAAO,YAAY,cAAc,KAAK,OAAO,SACjD,gBAAAD,KAAC,SAAI,WAAU,kBAAkB,+BAAqB,KAAK,OAAO,QAAQ,OAAO,GAAE,IAEnF,gBAAAA,KAAC,OAAE,WAAU,oBAAoB,eAAK,OAAO,UAAS;AAAA,gBAExD,gBAAAA,KAAC,mBAAgB,WAAsB;AAAA,gBACtC,SAAS,WACR,KAAK,aAAa,SAChB,gBAAAA,KAAC,OAAE,WAAU,kBAAiB,sCAAwB,IAEtD,gBAAAC,MAAC,SAAI,WAAU,oBACb;AAAA,kCAAAD;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,UAAU,KAAK,aAAa;AAAA,sBAC5B,SAAS,MAAM,KAAK,WAAW,KAAK,OAAO,UAAU,IAAI;AAAA,sBAC1D;AAAA;AAAA,kBAED;AAAA,kBACA,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,UAAU,KAAK,aAAa;AAAA,sBAC5B,SAAS,MAAM,KAAK,WAAW,KAAK,OAAO,UAAU,KAAK;AAAA,sBAC3D;AAAA;AAAA,kBAED;AAAA,kBACC,KAAK,aAAa,UACjB,gBAAAA,KAAC,OAAE,WAAU,iBAAgB,MAAK,SAC/B,0BACH,IACE;AAAA,mBACN,IAEA;AAAA,iBACN;AAAA,iBAxCO,GAAG,KAAK,OAAO,QAAQ,IAAI,KAAK,EAyCzC;AAAA,UAEJ,CAAC;AAAA,UACA,kBACC,gBAAAC,MAAC,QAAG,WAAU,gBAAe,gBAAa,QACxC;AAAA,4BAAAD,KAAC,SAAI,WAAU,oCACb,0BAAAA,KAAC,OAAE,WAAU,oBAAoB,2BAAgB,GACnD;AAAA,YACA,gBAAAA,KAAC,SAAI,WAAU,kCACb,0BAAAC,MAAC,OAAE,WAAU,kBAAiB,MAAK,UACjC;AAAA,8BAAAA,MAAC,UAAK,WAAU,gBAAe,eAAY,QACzC;AAAA,gCAAAD,KAAC,OAAE;AAAA,gBACH,gBAAAA,KAAC,OAAE;AAAA,gBACH,gBAAAA,KAAC,OAAE;AAAA,iBACL;AAAA,cAAO;AAAA,eAET,GACF;AAAA,aACF,IACE;AAAA,WACN,GAEJ;AAAA,QACC,MAAM,oBAAoB,UAAU,MAAM,WAAW,KAAK,CAAC,kBAC1D,gBAAAA,KAAC,SAAI,WAAU,uBACZ,gBAAM,mBAAmB,IAAI,CAAC,aAC7B,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,UAAU,CAAC,WAAW;AAAA,YACtB,SAAS,MAAM,KAAK,YAAY,QAAQ;AAAA,YAEvC;AAAA;AAAA,UANI;AAAA,QAOP,CACD,GACH,IACE;AAAA,QACJ,gBAAAC,MAAC,UAAK,WAAU,oBAAmB,cAAa,OAAM,UAAU,CAAC,UAAU,KAAK,SAAS,KAAK,GAC3F;AAAA,kBACC,gBAAAD,KAAC,OAAE,WAAU,iBAAgB,MAAK,SAC/B,iBACH,IACE;AAAA,UACJ,gBAAAA,KAAC,WAAM,WAAU,iBAAgB,SAAS,SAAS,sBAEnD;AAAA,UACA,gBAAAC,MAAC,SAAI,WAAU,wBACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI;AAAA,gBACJ,KAAK;AAAA,gBACL,WAAU;AAAA,gBACV,MAAM;AAAA,gBACN,OAAO;AAAA,gBACP,UAAU,CAAC,WAAW;AAAA,gBACtB,aAAa,SAAS,UAAU,0CAA0C;AAAA,gBAC1E,cAAa;AAAA,gBACb,UAAU,CAAC,UAAU;AACnB,2BAAS,MAAM,OAAO,KAAK;AAC3B,sBAAI,MAAO,UAAS,IAAI;AAAA,gBAC1B;AAAA,gBACA,WAAW,CAAC,UAAU;AACpB,sBAAI,MAAM,QAAQ,WAAW,MAAM,SAAU;AAC7C,wBAAM,eAAe;AACrB,wBAAM,cAAc,MAAM,cAAc;AAAA,gBAC1C;AAAA;AAAA,YACF;AAAA,YACA,gBAAAA,KAAC,YAAO,WAAU,kBAAiB,MAAK,UAAS,UAAU,CAAC,WAAW,WAAW,CAAC,WAAW,WAAW,iBAEzG;AAAA,aACF;AAAA,UACC,YACC,gBAAAC,MAAC,OAAE,WAAU,iBACV;AAAA;AAAA,YAAO;AAAA,YACR,gBAAAA,MAAC,UAAK,WAAU,iBAAgB;AAAA;AAAA,cAAE;AAAA,eAAc;AAAA,aAClD,IACE,SAAS,MACX,gBAAAA,MAAC,OAAE,WAAU,iBAAiB;AAAA;AAAA,YAAO;AAAA,aAAQ,IAC3C;AAAA,WACN;AAAA;AAAA;AAAA,EACF;AAGF,MAAI,iBAAiB,UAAW,QAAO;AACvC,SACE,gBAAAD,KAAC,cAAW,cAA4B,aAAa,MAAM,aAAa,YAAwB,OAAM,WAAU,WAAW,MAAM,WAAW,OAAO,MAAM,OACtJ,iBACH;AAEJ;;;AO/WA,SAAS,kBAA6B;AAYxB,gBAAAI,YAAA;AARd,IAAM,QAAQ,oBAAI,QAAuB;AAElC,SAAS,MAAM,IAAa,QAAsB,CAAC,GAAe;AACvE,MAAI,OAAO,MAAM,IAAI,EAAE;AACvB,MAAI,CAAC,MAAM;AACT,WAAO,WAAW,EAAE;AACpB,UAAM,IAAI,IAAI,IAAI;AAAA,EACpB;AACA,OAAK,OAAO,gBAAAA,KAAC,WAAS,GAAG,OAAO,CAAE;AAClC,SAAO,MAAM,QAAQ,EAAE;AACzB;AAEO,SAAS,QAAQ,IAAmB;AACzC,QAAM,OAAO,MAAM,IAAI,EAAE;AACzB,MAAI,CAAC,KAAM;AACX,OAAK,QAAQ;AACb,QAAM,OAAO,EAAE;AACjB;","names":["useEffect","useState","jsx","jsx","jsxs","useState","useEffect","jsx"]}
|
package/dist/server.d.ts
CHANGED
|
@@ -176,6 +176,8 @@ declare function verifyAnswer(answer: string, cited: number[], passages: Passage
|
|
|
176
176
|
allowCode?: boolean;
|
|
177
177
|
}): Verification;
|
|
178
178
|
declare function citationsFor(cited: number[], passages: Passage[], answer: string, max: number): AnswerCitation[];
|
|
179
|
+
/** Cheap script or stopword guess. Null when the text does not lean one way. */
|
|
180
|
+
declare function detectLanguage(text: string): string | null;
|
|
179
181
|
/**
|
|
180
182
|
* Grounded answer pipeline: route + rewrite, hybrid retrieval, cited synthesis, then a figure and
|
|
181
183
|
* citation check. Two model calls and one embedding per question.
|
|
@@ -330,6 +332,9 @@ type RefreshReport = {
|
|
|
330
332
|
};
|
|
331
333
|
/** Same page: host (ignoring www and scheme) and path, ignoring a trailing slash, query, and fragment. */
|
|
332
334
|
declare function samePage(left: string, right: string): boolean;
|
|
335
|
+
/** `/llms.txt` and `/llms-full.txt` on an origin already being indexed. */
|
|
336
|
+
declare function agentIndexUrls(origin: string): string[];
|
|
337
|
+
declare function isAgentIndexUrl(raw: string): boolean;
|
|
333
338
|
/**
|
|
334
339
|
* BYO Answers backend. Runs on your server with your OpenAI key; questions and knowledge stay on
|
|
335
340
|
* your infrastructure. Pair with createByoAdapter in the browser.
|
|
@@ -427,6 +432,17 @@ declare function selectNamedEvidence<T extends EvidenceRow>(query: string, rows:
|
|
|
427
432
|
* Empty `named` keeps page order (everyone 1 except Q&A).
|
|
428
433
|
*/
|
|
429
434
|
declare function namedPathBucket(url: string | null | undefined, named: ReadonlySet<string>, qa?: boolean): number;
|
|
435
|
+
/** File names written in the question (`llms.txt`, `agents.md`), not bare words. */
|
|
436
|
+
declare function explicitFileNames(query: string): string[];
|
|
437
|
+
/** The passage path or title is the file the question named (`/llms.txt`). */
|
|
438
|
+
declare function matchesExplicitFile(url: string | null | undefined, title: string | null | undefined, files: readonly string[]): boolean;
|
|
439
|
+
/** Last segment of `/{product}/compare/{slug}`. Hub pages (`/compare`) have none. */
|
|
440
|
+
declare function compareSlug(url?: string | null): string | null;
|
|
441
|
+
/**
|
|
442
|
+
* A passage the question names outright: an explicit file (`llms.txt`) or the compare page for a
|
|
443
|
+
* named competitor. These stay in the context even when a product path would otherwise outrank them.
|
|
444
|
+
*/
|
|
445
|
+
declare function isPinnedPassage(query: string, url?: string | null, title?: string | null): boolean;
|
|
430
446
|
/** How many custom Q&A entries are always offered to the model when they look relevant. */
|
|
431
447
|
declare const QA_LANE = 2;
|
|
432
448
|
/** Embedding similarity at which a custom Q&A counts as relevant even with no shared words. */
|
|
@@ -448,4 +464,4 @@ declare function withQaLane<T extends {
|
|
|
448
464
|
id: string;
|
|
449
465
|
}>(ranked: readonly T[], lane: readonly T[], limit: number): T[];
|
|
450
466
|
|
|
451
|
-
export { type AnswerCitation, type AnswerDeps, type AnswerInput, type AnswerOutput, type AnswerReason, type AnswersBackend, type AnswersBackendOptions, CHUNK_MAX_CHARS, CHUNK_TARGET_CHARS, type ChatMessage, type Chunk, DEFAULT_ANSWER_MODEL, DEFAULT_EMBEDDING_MODEL, type DocumentInput, EMBEDDING_DIMENSIONS, EVIDENCE_LANE, type EvidenceRow, type ExtractedPage, FALLBACK_ANSWER_MODEL, type IndexedEntry, type LlmClient, LlmError, MemoryIndex, type MemoryIndexSnapshot, type OpenAiOptions, type PageSection, type Passage, QA_LANE, QA_LANE_MIN_SIMILARITY, type QaInput, type QaLaneCandidate, type ReasoningEffort, type RefreshReport, type RetrieveQuery, type Retriever, type SkippedPage, type Turn, answerMessages, answerQuestion, authFailure, capabilityVerbs, chunkPage, citationsFor, contentTokenOverlap, cosine, createAnswersBackend, createOpenAiClient, decodeCfEmail, decodeEntities, decodeProtectedEmails, distinctiveTokens, extractPage, extractText, figuresIn, firstPathSegment, inferSources, isReasoningModel, matchingQa, namedPathBucket, namedPathQuery, prepareMessages, reciprocalRankFusion, samePage, selectNamedEvidence, selectQaLane, sourceBlock, stem, subjectTokens, tokenize, verifyAnswer, withQaLane };
|
|
467
|
+
export { type AnswerCitation, type AnswerDeps, type AnswerInput, type AnswerOutput, type AnswerReason, type AnswersBackend, type AnswersBackendOptions, CHUNK_MAX_CHARS, CHUNK_TARGET_CHARS, type ChatMessage, type Chunk, DEFAULT_ANSWER_MODEL, DEFAULT_EMBEDDING_MODEL, type DocumentInput, EMBEDDING_DIMENSIONS, EVIDENCE_LANE, type EvidenceRow, type ExtractedPage, FALLBACK_ANSWER_MODEL, type IndexedEntry, type LlmClient, LlmError, MemoryIndex, type MemoryIndexSnapshot, type OpenAiOptions, type PageSection, type Passage, QA_LANE, QA_LANE_MIN_SIMILARITY, type QaInput, type QaLaneCandidate, type ReasoningEffort, type RefreshReport, type RetrieveQuery, type Retriever, type SkippedPage, type Turn, agentIndexUrls, answerMessages, answerQuestion, authFailure, capabilityVerbs, chunkPage, citationsFor, compareSlug, contentTokenOverlap, cosine, createAnswersBackend, createOpenAiClient, decodeCfEmail, decodeEntities, decodeProtectedEmails, detectLanguage, distinctiveTokens, explicitFileNames, extractPage, extractText, figuresIn, firstPathSegment, inferSources, isAgentIndexUrl, isPinnedPassage, isReasoningModel, matchesExplicitFile, matchingQa, namedPathBucket, namedPathQuery, prepareMessages, reciprocalRankFusion, samePage, selectNamedEvidence, selectQaLane, sourceBlock, stem, subjectTokens, tokenize, verifyAnswer, withQaLane };
|