@bootstrapware/answers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +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 : \"Ask a question about this page.\"}\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 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"]}