@bootstrapware/answers 0.2.13 → 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/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 adapter = props.adapter ?? (props.publishableKey ? null : localRef.current ??= createLocalAdapter());
420
- const mode = props.adapter ? "byo" : props.publishableKey ? "unwired" : "local";
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 onSubmit(event) {
441
- event.preventDefault();
505
+ async function askQuestion(raw) {
442
506
  if (!adapter || pendingRef.current) return;
443
- if (!trimmed) {
507
+ const question = raw.trim();
508
+ if (!question) {
444
509
  setError("Enter a question.");
445
510
  return;
446
511
  }
447
- if (overLimit) {
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 raw = await adapter.ask({ sessionId, question, pageContext });
460
- const result = sanitizeResult(raw);
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":"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","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.
@@ -430,6 +432,17 @@ declare function selectNamedEvidence<T extends EvidenceRow>(query: string, rows:
430
432
  * Empty `named` keeps page order (everyone 1 except Q&A).
431
433
  */
432
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;
433
446
  /** How many custom Q&A entries are always offered to the model when they look relevant. */
434
447
  declare const QA_LANE = 2;
435
448
  /** Embedding similarity at which a custom Q&A counts as relevant even with no shared words. */
@@ -451,4 +464,4 @@ declare function withQaLane<T extends {
451
464
  id: string;
452
465
  }>(ranked: readonly T[], lane: readonly T[], limit: number): T[];
453
466
 
454
- 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, contentTokenOverlap, cosine, createAnswersBackend, createOpenAiClient, decodeCfEmail, decodeEntities, decodeProtectedEmails, distinctiveTokens, extractPage, extractText, figuresIn, firstPathSegment, inferSources, isAgentIndexUrl, 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 };