@guidelytech/guide-runtime 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/capture-styles.ts","../src/capture-dom.ts","../src/capture-screen.ts","../src/capture.ts"],"sourcesContent":["/** How-to capture toolbar styles, injected into the Guidely shadow root when capture starts. */\nexport const CAPTURE_STYLES = `\n.g-cap{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);z-index:calc(var(--g-z) + 10);pointer-events:auto;display:flex;flex-direction:column;align-items:center;gap:6px;max-width:calc(100vw - 32px);font:500 13px/1.4 var(--g-font);color:#fff}\n.g-cap-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:6px 6px 6px 14px;background:#111827;border-radius:999px;box-shadow:0 6px 24px rgba(0,0,0,.28)}\n.g-cap-dot{position:relative;flex:none;width:10px;height:10px;border-radius:999px;background:#9ca3af}\n.g-cap[data-state=\"recording\"] .g-cap-dot{background:#ef4444}\n.g-cap[data-state=\"recording\"] .g-cap-dot::after{content:\"\";position:absolute;inset:0;border-radius:inherit;background:#ef4444;animation:g-pulse 1.4s ease-out infinite}\n.g-cap-status{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:min(340px,60vw)}\n.g-cap .g-btn{min-height:28px;padding:3px 12px;font-size:13px;border-radius:999px}\n.g-cap .g-btn-secondary{background:rgba(255,255,255,.14);color:#fff;border-color:transparent}\n.g-cap .g-btn-secondary:hover{background:rgba(255,255,255,.22)}\n.g-cap .g-btn-ghost{color:#d1d5db}\n.g-cap .g-btn-ghost:hover{background:rgba(255,255,255,.1);color:#fff}\n.g-cap .g-cap-warn{background:#f59e0b;color:#111827}\n.g-cap-note{max-width:min(420px,calc(100vw - 32px));padding:6px 12px;border-radius:10px;background:#7c2d12;box-shadow:0 6px 24px rgba(0,0,0,.28)}\n.g-cap-count{display:none;font-variant-numeric:tabular-nums}\n.g-cap[data-compact] .g-cap-row{gap:6px;padding:5px 10px;opacity:.85}\n.g-cap[data-compact] .g-cap-row>:not(.g-cap-dot):not(.g-cap-count):not(.g-cap-warn){display:none}\n.g-cap[data-compact] .g-cap-count{display:inline}\n.g-cap[data-captured] .g-cap-dot,.g-cap[data-captured] .g-cap-dot::after{background:#22c55e}\n`;\n","import type { CapturedElement, NormalizedRect } from \"@guidelytech/shared\";\nimport { normalizeText } from \"./selectors\";\n\n/**\n * Pure DOM helpers for how-to capture: which element an interaction is about, how to name it for\n * people, which fields and on-screen text are sensitive, and where things sit in a screenshot.\n */\n\n/** Elements a click is \"about\" when it lands on them or inside them. */\nexport const CAPTURE_INTERACTIVE_SELECTOR =\n \"a,button,input,select,textarea,label,summary,[role=button],[role=link],[role=tab],[role=menuitem],[role=menuitemcheckbox],[role=menuitemradio],[role=option],[role=checkbox],[role=radio],[role=switch],[role=combobox],[contenteditable]:not([contenteditable=false])\";\n\n/** Always-redacted elements (on top of the project's mask selectors). */\nexport const REDACT_SELECTOR = \"[data-guidely-redact]\";\n\nconst TEXT_INPUT_TYPES = new Set([\"\", \"text\", \"email\", \"search\", \"tel\", \"url\", \"number\", \"password\", \"date\", \"datetime-local\", \"month\", \"week\", \"time\"]);\n\nfunction closestSafe(el: Element, selector: string): Element | null {\n if (!selector) return null;\n try {\n return el.closest(selector);\n } catch {\n return null;\n }\n}\n\nfunction inputType(el: Element): string {\n return el.tagName === \"INPUT\" ? (el.getAttribute(\"type\") || \"\").toLowerCase() : \"\";\n}\n\nfunction isEditableHost(el: Element): boolean {\n const v = el.getAttribute(\"contenteditable\");\n return v != null && v.toLowerCase() !== \"false\";\n}\n\n/** Fields whose typed text becomes a `type` step (instead of a click). */\nexport function isTextEntry(el: Element): boolean {\n if (el.tagName === \"TEXTAREA\") return true;\n if (el.tagName === \"INPUT\") return TEXT_INPUT_TYPES.has(inputType(el));\n return isEditableHost(el) || !!closestSafe(el, \"[contenteditable]:not([contenteditable=false])\");\n}\n\n/** The element a pointer interaction is about: the closest interactive ancestor, else the target itself. */\nexport function actionTarget(target: Element): Element {\n const hit = closestSafe(target, CAPTURE_INTERACTIVE_SELECTOR) ?? target;\n // A contenteditable descendant belongs to its editing host.\n if (!isEditableHost(hit) && isTextEntry(hit) && hit.tagName !== \"INPUT\" && hit.tagName !== \"TEXTAREA\") return closestSafe(hit, \"[contenteditable]:not([contenteditable=false])\") ?? hit;\n return hit;\n}\n\n/** The control a `<label>` is for (clicking the label acts on it), if any. */\nexport function labelControl(el: Element): Element | null {\n if (el.tagName !== \"LABEL\") return null;\n const label = el as HTMLLabelElement;\n if (label.control) return label.control;\n const forId = label.getAttribute(\"for\");\n if (forId) return label.ownerDocument.getElementById(forId);\n return label.querySelector(\"input,select,textarea\");\n}\n\n/** Is the element (or an ancestor) covered by the mask selectors or `[data-guidely-redact]`? */\nexport function isMaskedElement(el: Element, maskSelector: string): boolean {\n return !!closestSafe(el, REDACT_SELECTOR) || !!closestSafe(el, maskSelector);\n}\n\nconst SENSITIVE_NAME = /pass|card|cvv|cvc|ssn|secret|token|otp/i;\nconst SENSITIVE_AUTOCOMPLETE = /cc-|one-time-code|current-password|new-password/i;\n\n/** Fields whose value is never recorded and whose pixels are redacted from screenshots. */\nexport function isSensitiveField(el: Element, maskSelector = \"\"): boolean {\n if (inputType(el) === \"password\") return true;\n if (SENSITIVE_AUTOCOMPLETE.test(el.getAttribute(\"autocomplete\") || \"\")) return true;\n if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (SENSITIVE_NAME.test(el.getAttribute(\"name\") || \"\") || SENSITIVE_NAME.test(el.id || \"\"))) return true;\n return isMaskedElement(el, maskSelector);\n}\n\n// ---------------------------------------------------------------------------\n// Sensitive text patterns\n// ---------------------------------------------------------------------------\n\nconst EMAIL = /[A-Z0-9._%+-]+@[A-Z0-9-]+(?:\\.[A-Z0-9-]+)*\\.[A-Z]{2,}/gi;\n/** A run of digit groups (optionally `+cc` and `(area)`) separated by single spaces, dots or dashes. */\nconst NUMBER_RUN = /(?:\\+\\d{1,3}[ .-]?)?(?:\\(\\d{2,4}\\)[ .-]?)?\\d+(?:[ .-]\\d+)*/g;\n\nexport type SensitiveNumberKind = \"card\" | \"ssn\" | \"phone\";\n\n/**\n * Classifies a number run: card (13–19 digits in groups of 4+), US SSN (3-2-4) or phone\n * (10–13 digits written with a country code, area-code parentheses or 3+ groups ending in 4 digits).\n * Dates, times, prices, versions and IP addresses do not match.\n */\nexport function classifyNumber(run: string): SensitiveNumberKind | null {\n const groups = run.match(/\\d+/g) ?? [];\n const lens = groups.map((g) => g.length);\n const digits = lens.reduce((a, b) => a + b, 0);\n const special = /[+(.]/.test(run);\n if (lens.length === 3 && lens[0] === 3 && lens[1] === 2 && lens[2] === 4 && /^\\d{3}[ -]\\d{2}[ -]\\d{4}$/.test(run)) return \"ssn\";\n if (digits >= 13 && digits <= 19 && !special && lens.every((l) => l >= 4)) return \"card\";\n if (digits >= 10 && digits <= 13 && (/[+(]/.test(run) || (lens.length >= 3 && lens[lens.length - 1] === 4 && lens.every((l) => l >= 2) && !run.includes(\".\")))) return \"phone\";\n return null;\n}\n\n/** Character ranges `[start, end)` of emails, card numbers, SSNs and phone numbers in `text`. */\nexport function findSensitiveText(text: string): Array<[number, number]> {\n const out: Array<[number, number]> = [];\n if (text.length < 7) return out;\n if (text.includes(\"@\")) {\n EMAIL.lastIndex = 0;\n for (let m = EMAIL.exec(text); m && out.length < 200; m = EMAIL.exec(text)) out.push([m.index, m.index + m[0].length]);\n }\n if (/\\d{3}/.test(text)) {\n NUMBER_RUN.lastIndex = 0;\n for (let m = NUMBER_RUN.exec(text); m && out.length < 200; m = NUMBER_RUN.exec(text)) {\n if (m[0].length >= 9 && classifyNumber(m[0])) out.push([m.index, m.index + m[0].length]);\n }\n }\n return out;\n}\n\n/** Typed values are recorded verbatim unless they contain something like a card number or SSN. */\nexport function looksLikeSecretValue(value: string): boolean {\n NUMBER_RUN.lastIndex = 0;\n for (let m = NUMBER_RUN.exec(value); m; m = NUMBER_RUN.exec(value)) {\n const kind = classifyNumber(m[0]);\n if (kind === \"card\" || kind === \"ssn\") return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Element description\n// ---------------------------------------------------------------------------\n\nconst ROLE_MAP: Record<string, string> = {\n button: \"button\",\n link: \"link\",\n tab: \"tab\",\n menuitem: \"menuitem\",\n menuitemcheckbox: \"menuitem\",\n menuitemradio: \"menuitem\",\n checkbox: \"checkbox\",\n radio: \"radio\",\n switch: \"switch\",\n option: \"option\",\n combobox: \"dropdown\",\n listbox: \"dropdown\",\n textbox: \"field\",\n searchbox: \"field\",\n spinbutton: \"field\",\n};\n\n/** Normalised role: button, link, tab, menuitem, checkbox, radio, switch, field, dropdown, option or element. */\nexport function captureRole(el: Element): string {\n const explicit = (el.getAttribute(\"role\") || \"\").trim().split(/\\s+/)[0]!.toLowerCase();\n if (ROLE_MAP[explicit]) return ROLE_MAP[explicit]!;\n const tag = el.tagName.toLowerCase();\n if (tag === \"a\") return el.hasAttribute(\"href\") ? \"link\" : \"element\";\n if (tag === \"button\" || tag === \"summary\") return \"button\";\n if (tag === \"select\") return \"dropdown\";\n if (tag === \"option\") return \"option\";\n if (tag === \"textarea\") return \"field\";\n if (tag === \"input\") {\n const t = inputType(el);\n if (t === \"checkbox\") return \"checkbox\";\n if (t === \"radio\") return \"radio\";\n if (t === \"button\" || t === \"submit\" || t === \"reset\" || t === \"image\") return \"button\";\n return \"field\";\n }\n if (isEditableHost(el)) return \"field\";\n return \"element\";\n}\n\nconst clip = (s: string, max = 80) => (s.length > max ? `${s.slice(0, max - 1).trimEnd()}…` : s);\n\nfunction textOf(el: Element): string {\n return normalizeText((el as HTMLElement).innerText ?? el.textContent);\n}\n\nfunction labelledBy(el: Element): string {\n const ids = (el.getAttribute(\"aria-labelledby\") || \"\").split(/\\s+/).filter(Boolean);\n return normalizeText(ids.map((id) => el.ownerDocument.getElementById(id)).filter((x): x is HTMLElement => !!x).map(textOf).join(\" \"));\n}\n\nfunction associatedLabel(el: Element): string {\n const labels = (el as HTMLInputElement).labels;\n if (labels && labels.length) return normalizeText(Array.from(labels).map(textOf).join(\" \"));\n if (el.id) {\n try {\n const l = el.ownerDocument.querySelector(`label[for=\"${el.id.replace(/[\"\\\\]/g, \"\\\\$&\")}\"]`);\n if (l) return textOf(l);\n } catch {\n /* ignore */\n }\n }\n const wrap = el.parentElement?.closest(\"label\");\n return wrap ? textOf(wrap) : \"\";\n}\n\n/**\n * Visible name of an element for people: aria-label, aria-labelledby, its `<label>`, its text\n * (not for fields, whose value is user data), placeholder, alt, title or name. Max 80 characters.\n */\nexport function captureLabel(el: Element): string {\n const attr = (name: string) => normalizeText(el.getAttribute(name));\n const tag = el.tagName;\n const field = tag === \"INPUT\" || tag === \"TEXTAREA\" || tag === \"SELECT\" || isEditableHost(el);\n const candidates: Array<() => string> = [\n () => attr(\"aria-label\"),\n () => labelledBy(el),\n () => (/^(INPUT|TEXTAREA|SELECT|METER|PROGRESS|OUTPUT)$/.test(tag) ? associatedLabel(el) : \"\"),\n () => (tag === \"INPUT\" && /^(button|submit|reset)$/.test(inputType(el)) ? normalizeText((el as HTMLInputElement).value) : \"\"),\n () => (field ? \"\" : textOf(el)),\n () => attr(\"placeholder\"),\n () => attr(\"alt\"),\n () => normalizeText(el.querySelector?.(\"img[alt]\")?.getAttribute(\"alt\")),\n () => attr(\"title\"),\n () => normalizeText(el.querySelector?.(\"svg title\")?.textContent),\n () => attr(\"name\"),\n ];\n for (const c of candidates) {\n const v = c();\n if (v) return clip(v);\n }\n return \"\";\n}\n\n/** People-facing description of the element; masked elements get no label (never their text). */\nexport function describeCapturedElement(el: Element, maskSelector = \"\"): CapturedElement {\n return { label: isMaskedElement(el, maskSelector) ? \"\" : captureLabel(el), role: captureRole(el), tag: el.tagName.toLowerCase() };\n}\n\n/** The value a `type` step records: trimmed, at most 200 characters. */\nexport function fieldValue(el: Element): string {\n const raw = el.tagName === \"INPUT\" || el.tagName === \"TEXTAREA\" ? (el as HTMLInputElement).value : textOf(el);\n return (raw || \"\").trim().slice(0, 200);\n}\n\n/** Text of the selected option(s) of a `<select>`. */\nexport function selectedOptionText(el: HTMLSelectElement): string {\n return normalizeText(Array.from(el.selectedOptions ?? []).map((o) => o.text || o.value).join(\", \")).slice(0, 200);\n}\n\n// ---------------------------------------------------------------------------\n// Geometry\n// ---------------------------------------------------------------------------\n\nexport interface Box {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nconst clamp01 = (n: number) => Math.min(1, Math.max(0, n));\nconst round = (n: number) => Math.round(n * 10000) / 10000;\n\n/**\n * Maps a viewport rect (CSS px) to fractions of a screenshot of that viewport, padded by `pad`\n * CSS px and clamped to the frame. Returns null when nothing of it is on screen.\n */\nexport function normalizeRect(rect: Box, viewport: { width: number; height: number }, pad = 4): NormalizedRect | null {\n if (!viewport.width || !viewport.height || rect.width <= 0 || rect.height <= 0) return null;\n const x0 = clamp01((rect.x - pad) / viewport.width);\n const y0 = clamp01((rect.y - pad) / viewport.height);\n const x1 = clamp01((rect.x + rect.width + pad) / viewport.width);\n const y1 = clamp01((rect.y + rect.height + pad) / viewport.height);\n if (x1 <= x0 || y1 <= y0) return null;\n return { x: round(x0), y: round(y0), width: round(x1 - x0), height: round(y1 - y0) };\n}\n\n/** Scales a viewport rect (CSS px) to frame pixels, clipped to the frame. */\nexport function toFrameRect(rect: Box, scaleX: number, scaleY: number, frame: { width: number; height: number }, pad = 0): Box | null {\n const x0 = Math.max(0, Math.floor((rect.x - pad) * scaleX));\n const y0 = Math.max(0, Math.floor((rect.y - pad) * scaleY));\n const x1 = Math.min(frame.width, Math.ceil((rect.x + rect.width + pad) * scaleX));\n const y1 = Math.min(frame.height, Math.ceil((rect.y + rect.height + pad) * scaleY));\n return x1 > x0 && y1 > y0 ? { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } : null;\n}\n\nfunction intersectsViewport(r: Box, vw: number, vh: number): boolean {\n return r.width > 0 && r.height > 0 && r.x < vw && r.y < vh && r.x + r.width > 0 && r.y + r.height > 0;\n}\n\nconst SKIP_TEXT_PARENTS = /^(SCRIPT|STYLE|NOSCRIPT|TEMPLATE|TEXTAREA|OPTION)$/;\n\nexport interface RedactionOptions {\n maskSelector?: string;\n /** Stop after this many text nodes (keeps the synchronous scan cheap on huge pages). */\n maxTextNodes?: number;\n}\n\n/**\n * Viewport rects (CSS px) to blur in a screenshot: password and other sensitive fields, masked and\n * `[data-guidely-redact]` elements, field values and visible text that look like emails, card\n * numbers, SSNs or phone numbers. Runs synchronously so it matches the frame grabbed with it.\n */\nexport function collectRedactions(doc: Document, options: RedactionOptions = {}): Box[] {\n const win = doc.defaultView;\n const vw = win?.innerWidth ?? 0;\n const vh = win?.innerHeight ?? 0;\n const out: Box[] = [];\n const push = (r: Box) => {\n if (intersectsViewport(r, vw, vh)) out.push({ x: r.x, y: r.y, width: r.width, height: r.height });\n };\n const root = doc.body;\n if (!root) return out;\n const mask = options.maskSelector || \"\";\n const blocks = new Set<Element>();\n try {\n root.querySelectorAll(mask ? `${REDACT_SELECTOR},${mask}` : REDACT_SELECTOR).forEach((el) => blocks.add(el));\n } catch {\n root.querySelectorAll(REDACT_SELECTOR).forEach((el) => blocks.add(el));\n }\n root.querySelectorAll(\"input,textarea,select,[contenteditable]:not([contenteditable=false])\").forEach((el) => {\n if (blocks.has(el)) return;\n const t = inputType(el);\n if (t === \"hidden\" || t === \"checkbox\" || t === \"radio\" || t === \"button\" || t === \"submit\" || t === \"reset\") return;\n if (isSensitiveField(el, mask)) blocks.add(el);\n else if ((el.tagName === \"INPUT\" || el.tagName === \"TEXTAREA\") && findSensitiveText((el as HTMLInputElement).value || \"\").length) blocks.add(el);\n });\n blocks.forEach((el) => push(el.getBoundingClientRect()));\n\n const max = options.maxTextNodes ?? 5000;\n const walker = doc.createTreeWalker(root, 4 /* NodeFilter.SHOW_TEXT */);\n let range: Range | null = null;\n let seen = 0;\n for (let node = walker.nextNode(); node && seen < max; node = walker.nextNode()) {\n const text = node.nodeValue || \"\";\n if (text.length < 7) continue;\n seen++;\n const parent = node.parentElement;\n if (!parent || SKIP_TEXT_PARENTS.test(parent.tagName)) continue;\n const hits = findSensitiveText(text);\n if (!hits.length) continue;\n // Text inside an element that is already blurred as a whole needs no second pass.\n if (Array.from(blocks).some((b) => b.contains(parent))) continue;\n range ??= doc.createRange();\n for (const [start, end] of hits) {\n try {\n range.setStart(node, start);\n range.setEnd(node, end);\n Array.from(range.getClientRects()).forEach(push);\n } catch {\n /* node changed under us */\n }\n }\n }\n return out;\n}\n","import { toFrameRect, type Box } from \"./capture-dom\";\n\n/**\n * Screenshots for how-to capture: a tab-capture stream (`getDisplayMedia`) played into a hidden\n * video, frames grabbed synchronously into a canvas, sensitive regions blurred into the pixels and\n * the result encoded — all in the browser, so raw pixels of redacted regions never leave it.\n */\n\nexport type ScreenResult = { ok: true; screen: ScreenSource } | { ok: false; reason: \"unsupported\" | \"denied\" | \"wrong-surface\" | \"error\" };\n\nexport interface ScreenSource {\n /** True while the stream delivers frames. */\n live(): boolean;\n /** Called once when the user stops sharing (or the stream ends otherwise). */\n onEnded(cb: () => void): void;\n /** Draws the current frame into a new canvas (synchronously). Null when no frame is available. */\n grab(): HTMLCanvasElement | null;\n /** Does the current frame show the page as it is after `changedAt` (a `performance.now()` time)? */\n freshSince(changedAt: number): boolean;\n /** Resolves when the next frame arrives (or after `timeoutMs`). */\n nextFrame(timeoutMs?: number): Promise<void>;\n stop(): void;\n}\n\n/** Can this browser capture the tab at all? */\nexport function canCaptureScreen(): boolean {\n return typeof navigator !== \"undefined\" && !!navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia === \"function\";\n}\n\n/**\n * Asks the user to share this tab (must run from a user gesture) and plays the stream into a\n * hidden `<video>` appended to `container`. Rejects windows and screens: their frames do not\n * match the page's viewport, so highlights and redactions would land in the wrong place.\n */\nexport async function requestTabCapture(container: Node, doc: Document = document): Promise<ScreenResult> {\n if (!canCaptureScreen()) return { ok: false, reason: \"unsupported\" };\n let stream: MediaStream;\n try {\n stream = await navigator.mediaDevices.getDisplayMedia({\n video: { displaySurface: \"browser\", frameRate: 5 },\n audio: false,\n preferCurrentTab: true,\n selfBrowserSurface: \"include\",\n surfaceSwitching: \"exclude\",\n } as DisplayMediaStreamOptions);\n } catch (err) {\n return { ok: false, reason: (err as { name?: string })?.name === \"NotAllowedError\" ? \"denied\" : \"error\" };\n }\n const track = stream.getVideoTracks()[0];\n if (!track) {\n stream.getTracks().forEach((t) => t.stop());\n return { ok: false, reason: \"error\" };\n }\n const surface = (track.getSettings?.() as { displaySurface?: string } | undefined)?.displaySurface;\n if (surface && surface !== \"browser\") {\n stream.getTracks().forEach((t) => t.stop());\n return { ok: false, reason: \"wrong-surface\" };\n }\n const video = doc.createElement(\"video\");\n video.muted = true;\n video.playsInline = true;\n video.setAttribute(\"aria-hidden\", \"true\");\n // Kept rendered (not display:none) so browsers keep decoding frames.\n video.style.cssText = \"position:fixed;left:0;top:0;width:2px;height:2px;opacity:0;pointer-events:none\";\n try {\n video.srcObject = stream;\n container.appendChild(video);\n } catch {\n stream.getTracks().forEach((t) => t.stop());\n return { ok: false, reason: \"error\" };\n }\n try {\n await video.play();\n if (!video.videoWidth) await new Promise<void>((resolve) => {\n const done = () => resolve();\n video.addEventListener(\"loadeddata\", done, { once: true });\n setTimeout(done, 1500);\n });\n } catch {\n /* frames may still arrive; grab() returns null until they do */\n }\n let ended = false;\n const listeners: Array<() => void> = [];\n // Frame timing: `requestVideoFrameCallback` where supported, else assume one frame period (5 fps) of lag.\n type FrameVideo = HTMLVideoElement & { requestVideoFrameCallback?: (cb: (now: number) => void) => number };\n const fv = video as FrameVideo;\n const rvfc = typeof fv.requestVideoFrameCallback === \"function\";\n let frameAt = 0;\n let waiters: Array<() => void> = [];\n const onFrame = (now: number) => {\n frameAt = now;\n waiters.splice(0).forEach((w) => w());\n if (!ended) fv.requestVideoFrameCallback!(onFrame);\n };\n if (rvfc) fv.requestVideoFrameCallback!(onFrame);\n const end = () => {\n if (ended) return;\n ended = true;\n stream.getTracks().forEach((t) => t.stop());\n video.srcObject = null;\n video.remove();\n listeners.splice(0).forEach((cb) => cb());\n };\n track.addEventListener(\"ended\", end);\n return {\n ok: true,\n screen: {\n live: () => !ended && track.readyState === \"live\",\n onEnded: (cb) => (ended ? cb() : listeners.push(cb)),\n grab: () => (ended ? null : grabFrame(video, doc)),\n freshSince: (changedAt) => (rvfc ? frameAt > changedAt : performance.now() - changedAt > 250),\n nextFrame: (timeoutMs = 500) =>\n new Promise<void>((resolve) => {\n const t = setTimeout(done, rvfc ? timeoutMs : 250);\n function done() {\n clearTimeout(t);\n waiters = waiters.filter((w) => w !== done);\n resolve();\n }\n if (rvfc) waiters.push(done);\n }),\n stop: () => {\n listeners.length = 0;\n end();\n },\n },\n };\n}\n\n/** Copies the video's current frame into a new canvas. */\nexport function grabFrame(video: HTMLVideoElement, doc: Document = document): HTMLCanvasElement | null {\n const w = video.videoWidth;\n const h = video.videoHeight;\n if (!w || !h) return null;\n const canvas = doc.createElement(\"canvas\");\n canvas.width = w;\n canvas.height = h;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return null;\n ctx.drawImage(video, 0, 0, w, h);\n return canvas;\n}\n\n/**\n * Blurs viewport rects (CSS px) into the canvas by averaging each region down to a few samples per\n * line of text and scaling it back up, which leaves nothing legible to recover. Returns the number\n * of regions blurred.\n */\nexport function redactCanvas(canvas: HTMLCanvasElement, rects: Box[], viewport: { width: number; height: number }): number {\n const ctx = canvas.getContext(\"2d\");\n if (!ctx || !rects.length || !viewport.width || !viewport.height) return 0;\n const sx = canvas.width / viewport.width;\n const sy = canvas.height / viewport.height;\n const tmp = canvas.ownerDocument.createElement(\"canvas\");\n const tctx = tmp.getContext(\"2d\");\n if (!tctx) return 0;\n let count = 0;\n for (const rect of rects) {\n const r = toFrameRect(rect, sx, sy, canvas, 4);\n if (!r) continue;\n // One sample per ~line height (at least 16 CSS px) in both directions.\n const block = Math.max(16 * sy, Math.min(r.height, 48 * sy));\n tmp.width = Math.max(1, Math.round(r.width / block));\n tmp.height = Math.max(1, Math.round(r.height / block));\n tctx.imageSmoothingEnabled = true;\n tctx.drawImage(canvas, r.x, r.y, r.width, r.height, 0, 0, tmp.width, tmp.height);\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(tmp, 0, 0, tmp.width, tmp.height, r.x, r.y, r.width, r.height);\n // A light tint so readers can tell the region was hidden on purpose.\n ctx.fillStyle = \"rgba(120,120,120,.28)\";\n ctx.fillRect(r.x, r.y, r.width, r.height);\n count++;\n }\n return count;\n}\n\nfunction scaled(canvas: HTMLCanvasElement, maxWidth: number): HTMLCanvasElement {\n if (canvas.width <= maxWidth) return canvas;\n const out = canvas.ownerDocument.createElement(\"canvas\");\n out.width = maxWidth;\n out.height = Math.round((canvas.height * maxWidth) / canvas.width);\n const ctx = out.getContext(\"2d\");\n if (!ctx) return canvas;\n ctx.imageSmoothingQuality = \"high\";\n ctx.drawImage(canvas, 0, 0, out.width, out.height);\n return out;\n}\n\nconst toBlob = (canvas: HTMLCanvasElement, type: string, quality: number) =>\n new Promise<Blob | null>((resolve) => {\n try {\n canvas.toBlob((b) => resolve(b), type, quality);\n } catch {\n resolve(null);\n }\n });\n\n/** Encodes as WebP (JPEG where WebP encoding is unsupported), at most `maxWidth` wide and `maxBytes` big. */\nexport async function encodeScreenshot(canvas: HTMLCanvasElement, options: { maxWidth?: number; maxBytes?: number } = {}): Promise<Blob | null> {\n const src = scaled(canvas, options.maxWidth ?? 2560);\n const maxBytes = options.maxBytes ?? 8 * 1024 * 1024;\n let blob = await toBlob(src, \"image/webp\", 0.85);\n // Browsers without a WebP encoder silently return PNG.\n if (!blob || blob.type !== \"image/webp\") blob = await toBlob(src, \"image/jpeg\", 0.85);\n if (blob && blob.size > maxBytes) blob = await toBlob(src, \"image/jpeg\", 0.6);\n return blob && blob.size <= maxBytes ? blob : null;\n}\n\n/** Synchronous JPEG data URL (≤ 1600 px wide), for persisting an unsent screenshot while the page unloads. */\nexport function screenshotDataUrl(canvas: HTMLCanvasElement): string | null {\n try {\n return scaled(canvas, 1600).toDataURL(\"image/jpeg\", 0.7);\n } catch {\n return null;\n }\n}\n","import type { CaptureStepInput, HowToAction } from \"@guidelytech/shared\";\nimport { CAPTURE_STYLES } from \"./capture-styles\";\nimport {\n actionTarget,\n collectRedactions,\n describeCapturedElement,\n fieldValue,\n isMaskedElement,\n isSensitiveField,\n isTextEntry,\n labelControl,\n looksLikeSecretValue,\n normalizeRect,\n selectedOptionText,\n} from \"./capture-dom\";\nimport { canCaptureScreen, redactCanvas, requestTabCapture, type ScreenSource } from \"./capture-screen\";\nimport { createScope } from \"./host\";\nimport { composedTarget, describeElement, isGuidelyElement } from \"./selectors\";\n\nexport * from \"./capture-dom\";\nexport { canCaptureScreen, encodeScreenshot, grabFrame, redactCanvas, requestTabCapture, screenshotDataUrl } from \"./capture-screen\";\nexport type { ScreenResult, ScreenSource } from \"./capture-screen\";\n\n/** A captured step before the SDK gives it a `clientId`. */\nexport type CaptureStepDraft = Omit<CaptureStepInput, \"clientId\">;\n\nexport interface CapturedStep {\n step: CaptureStepDraft;\n /**\n * The screenshot, already redacted (encode it with `encodeScreenshot`); null without screenshots.\n * A promise when the current frame predates a scroll or resize: it resolves with the next frame,\n * after `step.highlight` and `step.redactions` were updated to match it.\n */\n frame: HTMLCanvasElement | Promise<HTMLCanvasElement | null> | null;\n}\n\nexport interface CaptureOptions {\n /** How-to title shown in the toolbar before recording starts. */\n title: string;\n /** Steps already recorded (a recording continues across full page loads). */\n stepCount?: number;\n /** False when the server cannot store screenshots: record steps only. Default true. */\n screenshots?: boolean;\n /**\n * Continue a recording after a full page load: start recording straight away. `navigate` adds a\n * step for the new page; `screenshots` offers \"Screenshots paused — Resume\" (the stream is gone).\n */\n resume?: { navigate: boolean; screenshots: boolean };\n /** Comma-joined CSS selectors whose text and pixels are never captured. */\n maskSelector?: string;\n onStep(step: CapturedStep): void;\n /** Recording started; `screenshots` is whether the user chose to take screenshots. */\n onStart?(info: { screenshots: boolean }): void;\n onFinish(): void;\n onCancel(): void;\n doc?: Document;\n}\n\nexport interface CaptureSession {\n /** Shows \"Saving…\" (and stops recording) while queued uploads finish. */\n setSaving(saving: boolean): void;\n isRecording(): boolean;\n stop(): void;\n}\n\n/** A repeat press on the same element within this window is a double click, not a new step. */\nexport const DOUBLE_CLICK_MS = 400;\n/** URL changes this soon after an interaction were caused by it (no separate navigate step). */\nexport const NAV_AFTER_ACTION_MS = 1500;\n/** Wait for the page to settle before capturing a navigate step. */\nexport const NAV_SETTLE_MS = 600;\n\ntype Phase = \"ready\" | \"recording\" | \"paused\" | \"saving\";\n\nconst NOTICES = {\n \"wrong-surface\": \"Share this tab (not a window or the whole screen) so screenshots match the page.\",\n denied: \"Screen sharing was cancelled. Start again, or record without screenshots.\",\n error: \"Screen capture could not start. Try again, or record without screenshots.\",\n unsupported: \"This browser cannot capture screenshots; steps are recorded without them.\",\n lost: \"Screenshots paused: sharing stopped or the page reloaded.\",\n} as const;\n\nfunction button(doc: Document, text: string, cls: string, attr: string): HTMLButtonElement {\n const b = doc.createElement(\"button\");\n b.type = \"button\";\n b.className = `g-btn ${cls}`;\n b.textContent = text;\n b.setAttribute(attr, \"\");\n return b;\n}\n\n/** The focused element inside a shadow-DOM subtree, if any. */\nfunction deepActiveElementIn(el: Element): Element | null {\n const root = el.getRootNode() as Document | ShadowRoot;\n return root.activeElement ?? null;\n}\n\nfunction deepActiveElement(doc: Document): Element | null {\n let el = doc.activeElement;\n while (el?.shadowRoot?.activeElement) el = el.shadowRoot.activeElement;\n return el;\n}\n\n/**\n * Starts how-to capture: a Shadow-DOM toolbar (bottom centre, a small pill while recording) and passive capture-phase\n * listeners that turn clicks, typing, selections and navigations into steps. The app keeps\n * receiving every event; nothing is prevented.\n */\nexport function startCapture(options: CaptureOptions): CaptureSession {\n const doc = options.doc ?? document;\n const win = doc.defaultView ?? window;\n const mask = options.maskSelector ?? \"\";\n const shotsAllowed = options.screenshots !== false;\n const scope = createScope(null, { theme: \"light\", animation: \"fade\" }, doc);\n const style = doc.createElement(\"style\");\n style.textContent = CAPTURE_STYLES;\n scope.el.appendChild(style);\n\n const bar = doc.createElement(\"div\");\n bar.className = \"g-cap\";\n bar.setAttribute(\"data-guidely-capture-bar\", \"\");\n bar.setAttribute(\"role\", \"toolbar\");\n bar.setAttribute(\"aria-label\", \"Guidely how-to recorder\");\n const note = doc.createElement(\"div\");\n note.className = \"g-cap-note\";\n note.setAttribute(\"data-guidely-capture-notice\", \"\");\n note.hidden = true;\n const row = doc.createElement(\"div\");\n row.className = \"g-cap-row\";\n const dot = doc.createElement(\"span\");\n dot.className = \"g-cap-dot\";\n const status = doc.createElement(\"span\");\n status.className = \"g-cap-status\";\n status.setAttribute(\"aria-live\", \"polite\");\n const count_ = doc.createElement(\"span\");\n count_.className = \"g-cap-count\";\n count_.setAttribute(\"aria-hidden\", \"true\");\n const start = button(doc, \"Start recording\", \"g-btn-primary\", \"data-guidely-capture-start\");\n const noShots = button(doc, \"Record without screenshots\", \"g-btn-secondary\", \"data-guidely-capture-no-screenshots\");\n const resumeShots = button(doc, \"Screenshots paused — Resume\", \"g-cap-warn\", \"data-guidely-capture-resume-screenshots\");\n const pause = button(doc, \"Pause\", \"g-btn-secondary\", \"data-guidely-capture-pause\");\n const done = button(doc, \"Done\", \"g-btn-primary\", \"data-guidely-capture-done\");\n const cancel = button(doc, \"Cancel\", \"g-btn-ghost\", \"data-guidely-capture-cancel\");\n row.append(dot, count_, status, start, noShots, resumeShots, pause, done, cancel);\n bar.append(note, row);\n scope.el.appendChild(bar);\n\n let phase: Phase = \"ready\";\n let count = options.stepCount ?? 0;\n let screen: ScreenSource | null = null;\n /** The user chose screenshots (so a lost stream shows \"Resume\"). */\n let wantShots = false;\n let requesting = false;\n let stopped = false;\n let lastAction = 0;\n /** `performance.now()` of the last scroll or resize: frames older than that do not match the DOM. */\n let movedAt = -1;\n let lastPress: { el: Element; at: number } | null = null;\n let lastUrl = win.location.href;\n let navTimer: ReturnType<typeof setTimeout> | null = null;\n let pulseTimer: ReturnType<typeof setTimeout> | null = null;\n /** `performance.now()` of the last keystroke in a field: a `type` step needs a frame that shows it. */\n let typedAt = -1;\n /**\n * While recording, the bar is a small pill (dot and step count) unless the pointer or focus is on\n * it, so screenshots show as little of it as possible.\n */\n let expanded = false; /** Field value at focus (or when last recorded), so each edit is recorded once. */\n const baseline = new WeakMap<Element, string>();\n\n const recording = () => phase === \"recording\" && !stopped;\n const steps = (n: number) => `${n} step${n === 1 ? \"\" : \"s\"}`;\n\n const render = () => {\n bar.setAttribute(\"data-state\", phase);\n bar.toggleAttribute(\"data-compact\", phase === \"recording\" && !expanded);\n const ready = phase === \"ready\";\n const active = phase === \"recording\" || phase === \"paused\";\n start.hidden = !ready;\n start.disabled = requesting;\n noShots.hidden = !ready || !shotsAllowed || !canCaptureScreen();\n noShots.disabled = requesting;\n resumeShots.hidden = !active || !wantShots || !!screen?.live();\n resumeShots.disabled = requesting;\n pause.hidden = !active;\n pause.textContent = phase === \"paused\" ? \"Resume\" : \"Pause\";\n done.hidden = !active;\n cancel.hidden = phase === \"saving\";\n if (ready) status.textContent = requesting ? \"Choose this tab to share…\" : `Ready to record “${options.title}”`;\n else if (phase === \"saving\") status.textContent = \"Saving steps…\";\n else status.textContent = `${phase === \"paused\" ? \"Paused\" : \"Recording\"} · ${steps(count)}`;\n count_.textContent = String(count);\n };\n\n const showNotice = (text: string | null) => {\n note.textContent = text ?? \"\";\n note.hidden = !text;\n };\n\n /** Briefly turns the dot green: a step was captured. */\n const pulse = () => {\n bar.setAttribute(\"data-captured\", \"\");\n if (pulseTimer) clearTimeout(pulseTimer);\n pulseTimer = setTimeout(() => bar.removeAttribute(\"data-captured\"), 700);\n };\n\n /** Grabs the frame, blurs sensitive regions and sets the highlight; all from the same moment's layout. */\n const shoot = (step: CaptureStepDraft, highlightEl: Element | null): HTMLCanvasElement | null => {\n const frame = screen?.live() ? screen.grab() : null;\n if (!frame) return null;\n const viewport = { width: win.innerWidth, height: win.innerHeight };\n try {\n step.redactions = redactCanvas(frame, collectRedactions(doc, { maskSelector: mask }), viewport);\n } catch {\n // Never upload a frame that could not be redacted.\n return null;\n }\n const highlight = highlightEl?.isConnected ? normalizeRect(highlightEl.getBoundingClientRect(), viewport) : null;\n if (highlight) step.highlight = highlight;\n else delete step.highlight;\n return frame;\n };\n\n /** Captures one step. The frame is grabbed synchronously, before the app reacts to the event. */\n const record = (action: HowToAction, el: Element | null, extra: { value?: string; valueRedacted?: boolean; highlightEl?: Element | null } = {}) => {\n const step: CaptureStepDraft = { action, url: win.location.href, pageTitle: (doc.title || \"\").slice(0, 300), redactions: 0, occurredAt: new Date().toISOString() };\n let highlightEl: Element | null = null;\n if (el) {\n const masked = isMaskedElement(el, mask);\n step.element = describeCapturedElement(el, mask);\n step.selectors = describeElement(el).selectors.filter((s) => !masked || s.kind !== \"text\");\n const box = extra.highlightEl?.getBoundingClientRect();\n highlightEl = extra.highlightEl && box && (box.width || box.height) ? extra.highlightEl : el;\n }\n let frame: CapturedStep[\"frame\"] = null;\n if (screen?.live()) {\n const s = screen;\n // A frame older than the last scroll shows content where it no longer is (redactions would miss it), and one\n // older than the last keystroke may not show the typed text yet: use the next one. A click keeps the current\n // frame, which shows the page before the app reacted.\n const changedAt = action === \"type\" || action === \"select\" ? Math.max(movedAt, typedAt) : movedAt;\n if (s.freshSince(changedAt)) frame = shoot(step, highlightEl);\n else frame = s.nextFrame().then(() => shoot(step, highlightEl));\n }\n if (extra.value !== undefined) step.value = extra.value;\n if (extra.valueRedacted) step.valueRedacted = true;\n if (action !== \"navigate\") lastAction = Date.now();\n count++;\n render();\n pulse();\n options.onStep({ step, frame });\n };\n\n /** Records a `type` step when the field's value changed since focus (or since it was last recorded). */\n const flushField = (el: Element) => {\n if (!recording() || !isTextEntry(el)) return;\n const host = el.tagName === \"INPUT\" || el.tagName === \"TEXTAREA\" ? el : actionTarget(el);\n const value = fieldValue(host);\n if (value === (baseline.get(host) ?? \"\")) return;\n baseline.set(host, value);\n if (!value) return;\n if (isSensitiveField(host, mask) || looksLikeSecretValue(value)) record(\"type\", host, { valueRedacted: true });\n else record(\"type\", host, { value });\n };\n\n const onPointerDown = (e: PointerEvent) => {\n if (!recording() || e.button !== 0) return;\n const target = composedTarget(e);\n if (!target || isGuidelyElement(target)) return;\n // Typing into the focused field happened before this click: record it first.\n const active = deepActiveElement(doc);\n if (active && active !== target && !active.contains(target) && isTextEntry(active)) flushField(active);\n let el = actionTarget(target);\n let highlightEl: Element | null = null;\n const control = labelControl(el);\n if (control) {\n if (isTextEntry(control) || control.tagName === \"SELECT\") return; // the label only focuses the field\n highlightEl = el;\n el = control;\n }\n if (isTextEntry(el) || el.tagName === \"SELECT\" || el.closest(\"select\")) return; // `change` records these\n if ((el as HTMLButtonElement).disabled) return;\n const now = Date.now();\n if (lastPress && lastPress.el === el && now - lastPress.at < DOUBLE_CLICK_MS) {\n lastPress.at = now;\n return;\n }\n lastPress = { el, at: now };\n record(\"click\", el, { highlightEl });\n };\n\n const onChange = (e: Event) => {\n if (!recording()) return;\n const el = composedTarget(e);\n if (!el || isGuidelyElement(el)) return;\n if (el.tagName === \"SELECT\") {\n const sensitive = isSensitiveField(el, mask);\n const value = sensitive ? \"\" : selectedOptionText(el as HTMLSelectElement);\n record(\"select\", el, sensitive ? { valueRedacted: true } : value ? { value } : {});\n return;\n }\n const type = el.tagName === \"INPUT\" ? (el.getAttribute(\"type\") || \"\").toLowerCase() : \"\";\n if (type === \"checkbox\" || type === \"radio\") {\n // Toggled with the keyboard (no pointer press on it or its label just now).\n const pressed = lastPress && Date.now() - lastPress.at < 1000 && lastPress.el === el;\n if (!pressed) record(\"click\", el);\n return;\n }\n if (isTextEntry(el)) flushField(el);\n };\n\n const onFocusIn = (e: FocusEvent) => {\n const el = composedTarget(e);\n if (el && !isGuidelyElement(el) && isTextEntry(el)) {\n const host = el.tagName === \"INPUT\" || el.tagName === \"TEXTAREA\" ? el : actionTarget(el);\n baseline.set(host, fieldValue(host));\n }\n };\n\n const onFocusOut = (e: FocusEvent) => {\n const el = composedTarget(e);\n // Inputs report edits through `change`; contenteditable regions only through blur.\n if (el && el.tagName !== \"INPUT\" && el.tagName !== \"TEXTAREA\" && isTextEntry(el)) flushField(el);\n };\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key !== \"Enter\" || e.isComposing) return;\n const el = composedTarget(e);\n if (el && (el.tagName === \"INPUT\" || ((e.metaKey || e.ctrlKey) && isTextEntry(el)))) flushField(el);\n };\n\n const checkUrl = () => {\n const url = win.location.href;\n if (url === lastUrl) return;\n lastUrl = url;\n if (!recording() || Date.now() - lastAction < NAV_AFTER_ACTION_MS) return;\n if (navTimer) clearTimeout(navTimer);\n navTimer = setTimeout(() => {\n navTimer = null;\n if (recording()) record(\"navigate\", null);\n }, NAV_SETTLE_MS);\n };\n const poll = setInterval(checkUrl, 300);\n\n const onMove = () => {\n movedAt = performance.now();\n };\n const onInput = (e: Event) => {\n const el = composedTarget(e);\n if (el && !isGuidelyElement(el)) typedAt = performance.now();\n };\n const expand = (open: boolean) => {\n if (expanded === open) return;\n expanded = open;\n render();\n };\n const onBarEnter = () => expand(true);\n const onBarLeave = () => expand(bar.contains(deepActiveElementIn(bar)));\n const onBarFocusOut = (e: FocusEvent) => expand(!!e.relatedTarget && bar.contains(e.relatedTarget as Node));\n bar.addEventListener(\"pointerenter\", onBarEnter);\n bar.addEventListener(\"pointerleave\", onBarLeave);\n bar.addEventListener(\"focusin\", onBarEnter);\n bar.addEventListener(\"focusout\", onBarFocusOut);\n\n const listen = { capture: true, passive: true } as const;\n doc.addEventListener(\"scroll\", onMove, listen);\n win.addEventListener(\"resize\", onMove);\n doc.addEventListener(\"pointerdown\", onPointerDown, listen);\n doc.addEventListener(\"change\", onChange, listen);\n doc.addEventListener(\"input\", onInput, listen);\n doc.addEventListener(\"focusin\", onFocusIn, listen);\n doc.addEventListener(\"focusout\", onFocusOut, listen);\n doc.addEventListener(\"keydown\", onKeyDown, listen);\n win.addEventListener(\"popstate\", checkUrl);\n win.addEventListener(\"hashchange\", checkUrl);\n\n const onLost = () => {\n screen = null;\n if (!stopped && wantShots) showNotice(NOTICES.lost);\n render();\n };\n\n /** Asks for the tab stream (needs the click that called it). */\n const requestScreen = async (): Promise<boolean> => {\n requesting = true;\n showNotice(null);\n render();\n const res = await requestTabCapture(scope.el, doc);\n requesting = false;\n if (stopped) {\n if (res.ok) res.screen.stop();\n return false;\n }\n if (res.ok) {\n screen = res.screen;\n screen.onEnded(onLost);\n } else showNotice(NOTICES[res.reason]);\n render();\n return res.ok;\n };\n\n const begin = (shots: boolean) => {\n wantShots = shots;\n phase = \"recording\";\n // Collapse to the pill even though the pointer is still on the Start button, and take the first\n // screenshot from a frame drawn after that.\n expanded = false;\n movedAt = performance.now();\n primeActive();\n lastUrl = win.location.href;\n render();\n options.onStart?.({ screenshots: shots });\n record(\"navigate\", null);\n };\n\n start.addEventListener(\"click\", () => {\n if (requesting) return;\n if (!shotsAllowed || !canCaptureScreen()) {\n if (shotsAllowed) showNotice(NOTICES.unsupported);\n return begin(false);\n }\n void requestScreen().then((ok) => {\n if (ok) begin(true);\n });\n });\n noShots.addEventListener(\"click\", () => {\n showNotice(null);\n begin(false);\n });\n resumeShots.addEventListener(\"click\", () => {\n if (!requesting) void requestScreen();\n });\n pause.addEventListener(\"click\", () => {\n if (phase === \"recording\") {\n flushActive();\n phase = \"paused\";\n } else if (phase === \"paused\") {\n phase = \"recording\";\n lastUrl = win.location.href;\n }\n render();\n });\n /** Typing still in the focused field (Safari does not move focus to a clicked button). */\n const flushActive = () => {\n const active = deepActiveElement(doc);\n if (active && !isGuidelyElement(active) && isTextEntry(active)) flushField(active);\n };\n const primeActive = () => {\n const active = deepActiveElement(doc);\n if (active && !isGuidelyElement(active) && isTextEntry(active)) baseline.set(active, fieldValue(active));\n };\n\n done.addEventListener(\"click\", () => {\n if (phase === \"saving\") return;\n flushActive();\n options.onFinish();\n });\n cancel.addEventListener(\"click\", () => {\n stop();\n options.onCancel();\n });\n\n const stop = () => {\n if (stopped) return;\n stopped = true;\n clearInterval(poll);\n if (navTimer) clearTimeout(navTimer);\n if (pulseTimer) clearTimeout(pulseTimer);\n doc.removeEventListener(\"scroll\", onMove, listen);\n win.removeEventListener(\"resize\", onMove);\n doc.removeEventListener(\"pointerdown\", onPointerDown, listen);\n doc.removeEventListener(\"change\", onChange, listen);\n doc.removeEventListener(\"input\", onInput, listen);\n doc.removeEventListener(\"focusin\", onFocusIn, listen);\n doc.removeEventListener(\"focusout\", onFocusOut, listen);\n doc.removeEventListener(\"keydown\", onKeyDown, listen);\n win.removeEventListener(\"popstate\", checkUrl);\n win.removeEventListener(\"hashchange\", checkUrl);\n screen?.stop();\n screen = null;\n scope.destroy();\n };\n\n if (options.resume) {\n wantShots = shotsAllowed && options.resume.screenshots && canCaptureScreen();\n phase = \"recording\";\n if (wantShots) showNotice(NOTICES.lost);\n primeActive();\n render();\n if (options.resume.navigate) record(\"navigate\", null);\n } else render();\n\n return {\n setSaving: (saving) => {\n if (stopped) return;\n phase = saving ? \"saving\" : \"recording\";\n render();\n },\n isRecording: recording,\n stop,\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/capture-styles.ts","../src/capture.ts"],"sourcesContent":["/** How-to capture toolbar styles, injected into the Guidely shadow root when capture starts. */\nexport const CAPTURE_STYLES = `\n.g-cap{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);z-index:calc(var(--g-z) + 10);pointer-events:auto;display:flex;flex-direction:column;align-items:center;gap:6px;max-width:calc(100vw - 32px);font:500 13px/1.4 var(--g-font);color:#fff}\n.g-cap-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:6px 6px 6px 14px;background:#111827;border-radius:999px;box-shadow:0 6px 24px rgba(0,0,0,.28)}\n.g-cap-dot{position:relative;flex:none;width:10px;height:10px;border-radius:999px;background:#9ca3af}\n.g-cap[data-state=\"recording\"] .g-cap-dot{background:#ef4444}\n.g-cap[data-state=\"recording\"] .g-cap-dot::after{content:\"\";position:absolute;inset:0;border-radius:inherit;background:#ef4444;animation:g-pulse 1.4s ease-out infinite}\n.g-cap-status{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:min(340px,60vw)}\n.g-cap .g-btn{min-height:28px;padding:3px 12px;font-size:13px;border-radius:999px}\n.g-cap .g-btn-secondary{background:rgba(255,255,255,.14);color:#fff;border-color:transparent}\n.g-cap .g-btn-secondary:hover{background:rgba(255,255,255,.22)}\n.g-cap .g-btn-ghost{color:#d1d5db}\n.g-cap .g-btn-ghost:hover{background:rgba(255,255,255,.1);color:#fff}\n.g-cap .g-cap-warn{background:#f59e0b;color:#111827}\n.g-cap-note{max-width:min(420px,calc(100vw - 32px));padding:6px 12px;border-radius:10px;background:#7c2d12;box-shadow:0 6px 24px rgba(0,0,0,.28)}\n.g-cap-count{display:none;font-variant-numeric:tabular-nums}\n.g-cap[data-compact] .g-cap-row{gap:6px;padding:5px 10px;opacity:.85}\n.g-cap[data-compact] .g-cap-row>:not(.g-cap-dot):not(.g-cap-count):not(.g-cap-warn){display:none}\n.g-cap[data-compact] .g-cap-count{display:inline}\n.g-cap[data-captured] .g-cap-dot,.g-cap[data-captured] .g-cap-dot::after{background:#22c55e}\n`;\n","import type { CaptureStepInput, HowToAction } from \"@guidelytech/shared\";\nimport { CAPTURE_STYLES } from \"./capture-styles\";\nimport {\n actionTarget,\n collectRedactions,\n describeCapturedElement,\n fieldValue,\n isMaskedElement,\n isSensitiveField,\n isTextEntry,\n labelControl,\n looksLikeSecretValue,\n normalizeRect,\n selectedOptionText,\n} from \"./capture-dom\";\nimport { canCaptureScreen, redactCanvas, requestTabCapture, type ScreenSource } from \"./capture-screen\";\nimport { createScope } from \"./host\";\nimport { composedTarget, describeElement, isGuidelyElement } from \"./selectors\";\n\nexport * from \"./capture-dom\";\nexport { canCaptureScreen, encodeScreenshot, grabFrame, redactCanvas, requestTabCapture, screenshotDataUrl } from \"./capture-screen\";\nexport type { ScreenResult, ScreenSource } from \"./capture-screen\";\n\n/** A captured step before the SDK gives it a `clientId`. */\nexport type CaptureStepDraft = Omit<CaptureStepInput, \"clientId\">;\n\nexport interface CapturedStep {\n step: CaptureStepDraft;\n /**\n * The screenshot, already redacted (encode it with `encodeScreenshot`); null without screenshots.\n * A promise when the current frame predates a scroll or resize: it resolves with the next frame,\n * after `step.highlight` and `step.redactions` were updated to match it.\n */\n frame: HTMLCanvasElement | Promise<HTMLCanvasElement | null> | null;\n}\n\nexport interface CaptureOptions {\n /** How-to title shown in the toolbar before recording starts. */\n title: string;\n /** Steps already recorded (a recording continues across full page loads). */\n stepCount?: number;\n /** False when the server cannot store screenshots: record steps only. Default true. */\n screenshots?: boolean;\n /**\n * Continue a recording after a full page load: start recording straight away. `navigate` adds a\n * step for the new page; `screenshots` offers \"Screenshots paused — Resume\" (the stream is gone).\n */\n resume?: { navigate: boolean; screenshots: boolean };\n /** Comma-joined CSS selectors whose text and pixels are never captured. */\n maskSelector?: string;\n onStep(step: CapturedStep): void;\n /** Recording started; `screenshots` is whether the user chose to take screenshots. */\n onStart?(info: { screenshots: boolean }): void;\n onFinish(): void;\n onCancel(): void;\n doc?: Document;\n}\n\nexport interface CaptureSession {\n /** Shows \"Saving…\" (and stops recording) while queued uploads finish. */\n setSaving(saving: boolean): void;\n isRecording(): boolean;\n stop(): void;\n}\n\n/** A repeat press on the same element within this window is a double click, not a new step. */\nexport const DOUBLE_CLICK_MS = 400;\n/** URL changes this soon after an interaction were caused by it (no separate navigate step). */\nexport const NAV_AFTER_ACTION_MS = 1500;\n/** Wait for the page to settle before capturing a navigate step. */\nexport const NAV_SETTLE_MS = 600;\n\ntype Phase = \"ready\" | \"recording\" | \"paused\" | \"saving\";\n\nconst NOTICES = {\n \"wrong-surface\": \"Share this tab (not a window or the whole screen) so screenshots match the page.\",\n denied: \"Screen sharing was cancelled. Start again, or record without screenshots.\",\n error: \"Screen capture could not start. Try again, or record without screenshots.\",\n unsupported: \"This browser cannot capture screenshots; steps are recorded without them.\",\n lost: \"Screenshots paused: sharing stopped or the page reloaded.\",\n} as const;\n\nfunction button(doc: Document, text: string, cls: string, attr: string): HTMLButtonElement {\n const b = doc.createElement(\"button\");\n b.type = \"button\";\n b.className = `g-btn ${cls}`;\n b.textContent = text;\n b.setAttribute(attr, \"\");\n return b;\n}\n\n/** The focused element inside a shadow-DOM subtree, if any. */\nfunction deepActiveElementIn(el: Element): Element | null {\n const root = el.getRootNode() as Document | ShadowRoot;\n return root.activeElement ?? null;\n}\n\nfunction deepActiveElement(doc: Document): Element | null {\n let el = doc.activeElement;\n while (el?.shadowRoot?.activeElement) el = el.shadowRoot.activeElement;\n return el;\n}\n\n/**\n * Starts how-to capture: a Shadow-DOM toolbar (bottom centre, a small pill while recording) and passive capture-phase\n * listeners that turn clicks, typing, selections and navigations into steps. The app keeps\n * receiving every event; nothing is prevented.\n */\nexport function startCapture(options: CaptureOptions): CaptureSession {\n const doc = options.doc ?? document;\n const win = doc.defaultView ?? window;\n const mask = options.maskSelector ?? \"\";\n const shotsAllowed = options.screenshots !== false;\n const scope = createScope(null, { theme: \"light\", animation: \"fade\" }, doc);\n const style = doc.createElement(\"style\");\n style.textContent = CAPTURE_STYLES;\n scope.el.appendChild(style);\n\n const bar = doc.createElement(\"div\");\n bar.className = \"g-cap\";\n bar.setAttribute(\"data-guidely-capture-bar\", \"\");\n bar.setAttribute(\"role\", \"toolbar\");\n bar.setAttribute(\"aria-label\", \"Guidely how-to recorder\");\n const note = doc.createElement(\"div\");\n note.className = \"g-cap-note\";\n note.setAttribute(\"data-guidely-capture-notice\", \"\");\n note.hidden = true;\n const row = doc.createElement(\"div\");\n row.className = \"g-cap-row\";\n const dot = doc.createElement(\"span\");\n dot.className = \"g-cap-dot\";\n const status = doc.createElement(\"span\");\n status.className = \"g-cap-status\";\n status.setAttribute(\"aria-live\", \"polite\");\n const count_ = doc.createElement(\"span\");\n count_.className = \"g-cap-count\";\n count_.setAttribute(\"aria-hidden\", \"true\");\n const start = button(doc, \"Start recording\", \"g-btn-primary\", \"data-guidely-capture-start\");\n const noShots = button(doc, \"Record without screenshots\", \"g-btn-secondary\", \"data-guidely-capture-no-screenshots\");\n const resumeShots = button(doc, \"Screenshots paused — Resume\", \"g-cap-warn\", \"data-guidely-capture-resume-screenshots\");\n const pause = button(doc, \"Pause\", \"g-btn-secondary\", \"data-guidely-capture-pause\");\n const done = button(doc, \"Done\", \"g-btn-primary\", \"data-guidely-capture-done\");\n const cancel = button(doc, \"Cancel\", \"g-btn-ghost\", \"data-guidely-capture-cancel\");\n row.append(dot, count_, status, start, noShots, resumeShots, pause, done, cancel);\n bar.append(note, row);\n scope.el.appendChild(bar);\n\n let phase: Phase = \"ready\";\n let count = options.stepCount ?? 0;\n let screen: ScreenSource | null = null;\n /** The user chose screenshots (so a lost stream shows \"Resume\"). */\n let wantShots = false;\n let requesting = false;\n let stopped = false;\n let lastAction = 0;\n /** `performance.now()` of the last scroll or resize: frames older than that do not match the DOM. */\n let movedAt = -1;\n let lastPress: { el: Element; at: number } | null = null;\n let lastUrl = win.location.href;\n let navTimer: ReturnType<typeof setTimeout> | null = null;\n let pulseTimer: ReturnType<typeof setTimeout> | null = null;\n /** `performance.now()` of the last keystroke in a field: a `type` step needs a frame that shows it. */\n let typedAt = -1;\n /**\n * While recording, the bar is a small pill (dot and step count) unless the pointer or focus is on\n * it, so screenshots show as little of it as possible.\n */\n let expanded = false; /** Field value at focus (or when last recorded), so each edit is recorded once. */\n const baseline = new WeakMap<Element, string>();\n\n const recording = () => phase === \"recording\" && !stopped;\n const steps = (n: number) => `${n} step${n === 1 ? \"\" : \"s\"}`;\n\n const render = () => {\n bar.setAttribute(\"data-state\", phase);\n bar.toggleAttribute(\"data-compact\", phase === \"recording\" && !expanded);\n const ready = phase === \"ready\";\n const active = phase === \"recording\" || phase === \"paused\";\n start.hidden = !ready;\n start.disabled = requesting;\n noShots.hidden = !ready || !shotsAllowed || !canCaptureScreen();\n noShots.disabled = requesting;\n resumeShots.hidden = !active || !wantShots || !!screen?.live();\n resumeShots.disabled = requesting;\n pause.hidden = !active;\n pause.textContent = phase === \"paused\" ? \"Resume\" : \"Pause\";\n done.hidden = !active;\n cancel.hidden = phase === \"saving\";\n if (ready) status.textContent = requesting ? \"Choose this tab to share…\" : `Ready to record “${options.title}”`;\n else if (phase === \"saving\") status.textContent = \"Saving steps…\";\n else status.textContent = `${phase === \"paused\" ? \"Paused\" : \"Recording\"} · ${steps(count)}`;\n count_.textContent = String(count);\n };\n\n const showNotice = (text: string | null) => {\n note.textContent = text ?? \"\";\n note.hidden = !text;\n };\n\n /** Briefly turns the dot green: a step was captured. */\n const pulse = () => {\n bar.setAttribute(\"data-captured\", \"\");\n if (pulseTimer) clearTimeout(pulseTimer);\n pulseTimer = setTimeout(() => bar.removeAttribute(\"data-captured\"), 700);\n };\n\n /** Grabs the frame, blurs sensitive regions and sets the highlight; all from the same moment's layout. */\n const shoot = (step: CaptureStepDraft, highlightEl: Element | null): HTMLCanvasElement | null => {\n const frame = screen?.live() ? screen.grab() : null;\n if (!frame) return null;\n const viewport = { width: win.innerWidth, height: win.innerHeight };\n try {\n step.redactions = redactCanvas(frame, collectRedactions(doc, { maskSelector: mask }), viewport);\n } catch {\n // Never upload a frame that could not be redacted.\n return null;\n }\n const highlight = highlightEl?.isConnected ? normalizeRect(highlightEl.getBoundingClientRect(), viewport) : null;\n if (highlight) step.highlight = highlight;\n else delete step.highlight;\n return frame;\n };\n\n /** Captures one step. The frame is grabbed synchronously, before the app reacts to the event. */\n const record = (action: HowToAction, el: Element | null, extra: { value?: string; valueRedacted?: boolean; highlightEl?: Element | null } = {}) => {\n const step: CaptureStepDraft = { action, url: win.location.href, pageTitle: (doc.title || \"\").slice(0, 300), redactions: 0, occurredAt: new Date().toISOString() };\n let highlightEl: Element | null = null;\n if (el) {\n const masked = isMaskedElement(el, mask);\n step.element = describeCapturedElement(el, mask);\n step.selectors = describeElement(el).selectors.filter((s) => !masked || s.kind !== \"text\");\n const box = extra.highlightEl?.getBoundingClientRect();\n highlightEl = extra.highlightEl && box && (box.width || box.height) ? extra.highlightEl : el;\n }\n let frame: CapturedStep[\"frame\"] = null;\n if (screen?.live()) {\n const s = screen;\n // A frame older than the last scroll shows content where it no longer is (redactions would miss it), and one\n // older than the last keystroke may not show the typed text yet: use the next one. A click keeps the current\n // frame, which shows the page before the app reacted.\n const changedAt = action === \"type\" || action === \"select\" ? Math.max(movedAt, typedAt) : movedAt;\n if (s.freshSince(changedAt)) frame = shoot(step, highlightEl);\n else frame = s.nextFrame().then(() => shoot(step, highlightEl));\n }\n if (extra.value !== undefined) step.value = extra.value;\n if (extra.valueRedacted) step.valueRedacted = true;\n if (action !== \"navigate\") lastAction = Date.now();\n count++;\n render();\n pulse();\n options.onStep({ step, frame });\n };\n\n /** Records a `type` step when the field's value changed since focus (or since it was last recorded). */\n const flushField = (el: Element) => {\n if (!recording() || !isTextEntry(el)) return;\n const host = el.tagName === \"INPUT\" || el.tagName === \"TEXTAREA\" ? el : actionTarget(el);\n const value = fieldValue(host);\n if (value === (baseline.get(host) ?? \"\")) return;\n baseline.set(host, value);\n if (!value) return;\n if (isSensitiveField(host, mask) || looksLikeSecretValue(value)) record(\"type\", host, { valueRedacted: true });\n else record(\"type\", host, { value });\n };\n\n const onPointerDown = (e: PointerEvent) => {\n if (!recording() || e.button !== 0) return;\n const target = composedTarget(e);\n if (!target || isGuidelyElement(target)) return;\n // Typing into the focused field happened before this click: record it first.\n const active = deepActiveElement(doc);\n if (active && active !== target && !active.contains(target) && isTextEntry(active)) flushField(active);\n let el = actionTarget(target);\n let highlightEl: Element | null = null;\n const control = labelControl(el);\n if (control) {\n if (isTextEntry(control) || control.tagName === \"SELECT\") return; // the label only focuses the field\n highlightEl = el;\n el = control;\n }\n if (isTextEntry(el) || el.tagName === \"SELECT\" || el.closest(\"select\")) return; // `change` records these\n if ((el as HTMLButtonElement).disabled) return;\n const now = Date.now();\n if (lastPress && lastPress.el === el && now - lastPress.at < DOUBLE_CLICK_MS) {\n lastPress.at = now;\n return;\n }\n lastPress = { el, at: now };\n record(\"click\", el, { highlightEl });\n };\n\n const onChange = (e: Event) => {\n if (!recording()) return;\n const el = composedTarget(e);\n if (!el || isGuidelyElement(el)) return;\n if (el.tagName === \"SELECT\") {\n const sensitive = isSensitiveField(el, mask);\n const value = sensitive ? \"\" : selectedOptionText(el as HTMLSelectElement);\n record(\"select\", el, sensitive ? { valueRedacted: true } : value ? { value } : {});\n return;\n }\n const type = el.tagName === \"INPUT\" ? (el.getAttribute(\"type\") || \"\").toLowerCase() : \"\";\n if (type === \"checkbox\" || type === \"radio\") {\n // Toggled with the keyboard (no pointer press on it or its label just now).\n const pressed = lastPress && Date.now() - lastPress.at < 1000 && lastPress.el === el;\n if (!pressed) record(\"click\", el);\n return;\n }\n if (isTextEntry(el)) flushField(el);\n };\n\n const onFocusIn = (e: FocusEvent) => {\n const el = composedTarget(e);\n if (el && !isGuidelyElement(el) && isTextEntry(el)) {\n const host = el.tagName === \"INPUT\" || el.tagName === \"TEXTAREA\" ? el : actionTarget(el);\n baseline.set(host, fieldValue(host));\n }\n };\n\n const onFocusOut = (e: FocusEvent) => {\n const el = composedTarget(e);\n // Inputs report edits through `change`; contenteditable regions only through blur.\n if (el && el.tagName !== \"INPUT\" && el.tagName !== \"TEXTAREA\" && isTextEntry(el)) flushField(el);\n };\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key !== \"Enter\" || e.isComposing) return;\n const el = composedTarget(e);\n if (el && (el.tagName === \"INPUT\" || ((e.metaKey || e.ctrlKey) && isTextEntry(el)))) flushField(el);\n };\n\n const checkUrl = () => {\n const url = win.location.href;\n if (url === lastUrl) return;\n lastUrl = url;\n if (!recording() || Date.now() - lastAction < NAV_AFTER_ACTION_MS) return;\n if (navTimer) clearTimeout(navTimer);\n navTimer = setTimeout(() => {\n navTimer = null;\n if (recording()) record(\"navigate\", null);\n }, NAV_SETTLE_MS);\n };\n const poll = setInterval(checkUrl, 300);\n\n const onMove = () => {\n movedAt = performance.now();\n };\n const onInput = (e: Event) => {\n const el = composedTarget(e);\n if (el && !isGuidelyElement(el)) typedAt = performance.now();\n };\n const expand = (open: boolean) => {\n if (expanded === open) return;\n expanded = open;\n render();\n };\n const onBarEnter = () => expand(true);\n const onBarLeave = () => expand(bar.contains(deepActiveElementIn(bar)));\n const onBarFocusOut = (e: FocusEvent) => expand(!!e.relatedTarget && bar.contains(e.relatedTarget as Node));\n bar.addEventListener(\"pointerenter\", onBarEnter);\n bar.addEventListener(\"pointerleave\", onBarLeave);\n bar.addEventListener(\"focusin\", onBarEnter);\n bar.addEventListener(\"focusout\", onBarFocusOut);\n\n const listen = { capture: true, passive: true } as const;\n doc.addEventListener(\"scroll\", onMove, listen);\n win.addEventListener(\"resize\", onMove);\n doc.addEventListener(\"pointerdown\", onPointerDown, listen);\n doc.addEventListener(\"change\", onChange, listen);\n doc.addEventListener(\"input\", onInput, listen);\n doc.addEventListener(\"focusin\", onFocusIn, listen);\n doc.addEventListener(\"focusout\", onFocusOut, listen);\n doc.addEventListener(\"keydown\", onKeyDown, listen);\n win.addEventListener(\"popstate\", checkUrl);\n win.addEventListener(\"hashchange\", checkUrl);\n\n const onLost = () => {\n screen = null;\n if (!stopped && wantShots) showNotice(NOTICES.lost);\n render();\n };\n\n /** Asks for the tab stream (needs the click that called it). */\n const requestScreen = async (): Promise<boolean> => {\n requesting = true;\n showNotice(null);\n render();\n const res = await requestTabCapture(scope.el, doc);\n requesting = false;\n if (stopped) {\n if (res.ok) res.screen.stop();\n return false;\n }\n if (res.ok) {\n screen = res.screen;\n screen.onEnded(onLost);\n } else showNotice(NOTICES[res.reason]);\n render();\n return res.ok;\n };\n\n const begin = (shots: boolean) => {\n wantShots = shots;\n phase = \"recording\";\n // Collapse to the pill even though the pointer is still on the Start button, and take the first\n // screenshot from a frame drawn after that.\n expanded = false;\n movedAt = performance.now();\n primeActive();\n lastUrl = win.location.href;\n render();\n options.onStart?.({ screenshots: shots });\n record(\"navigate\", null);\n };\n\n start.addEventListener(\"click\", () => {\n if (requesting) return;\n if (!shotsAllowed || !canCaptureScreen()) {\n if (shotsAllowed) showNotice(NOTICES.unsupported);\n return begin(false);\n }\n void requestScreen().then((ok) => {\n if (ok) begin(true);\n });\n });\n noShots.addEventListener(\"click\", () => {\n showNotice(null);\n begin(false);\n });\n resumeShots.addEventListener(\"click\", () => {\n if (!requesting) void requestScreen();\n });\n pause.addEventListener(\"click\", () => {\n if (phase === \"recording\") {\n flushActive();\n phase = \"paused\";\n } else if (phase === \"paused\") {\n phase = \"recording\";\n lastUrl = win.location.href;\n }\n render();\n });\n /** Typing still in the focused field (Safari does not move focus to a clicked button). */\n const flushActive = () => {\n const active = deepActiveElement(doc);\n if (active && !isGuidelyElement(active) && isTextEntry(active)) flushField(active);\n };\n const primeActive = () => {\n const active = deepActiveElement(doc);\n if (active && !isGuidelyElement(active) && isTextEntry(active)) baseline.set(active, fieldValue(active));\n };\n\n done.addEventListener(\"click\", () => {\n if (phase === \"saving\") return;\n flushActive();\n options.onFinish();\n });\n cancel.addEventListener(\"click\", () => {\n stop();\n options.onCancel();\n });\n\n const stop = () => {\n if (stopped) return;\n stopped = true;\n clearInterval(poll);\n if (navTimer) clearTimeout(navTimer);\n if (pulseTimer) clearTimeout(pulseTimer);\n doc.removeEventListener(\"scroll\", onMove, listen);\n win.removeEventListener(\"resize\", onMove);\n doc.removeEventListener(\"pointerdown\", onPointerDown, listen);\n doc.removeEventListener(\"change\", onChange, listen);\n doc.removeEventListener(\"input\", onInput, listen);\n doc.removeEventListener(\"focusin\", onFocusIn, listen);\n doc.removeEventListener(\"focusout\", onFocusOut, listen);\n doc.removeEventListener(\"keydown\", onKeyDown, listen);\n win.removeEventListener(\"popstate\", checkUrl);\n win.removeEventListener(\"hashchange\", checkUrl);\n screen?.stop();\n screen = null;\n scope.destroy();\n };\n\n if (options.resume) {\n wantShots = shotsAllowed && options.resume.screenshots && canCaptureScreen();\n phase = \"recording\";\n if (wantShots) showNotice(NOTICES.lost);\n primeActive();\n render();\n if (options.resume.navigate) record(\"navigate\", null);\n } else render();\n\n return {\n setSaving: (saving) => {\n if (stopped) return;\n phase = saving ? \"saving\" : \"recording\";\n render();\n },\n isRecording: recording,\n stop,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,7 +1,3 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
-
5
1
  // src/styles.ts
6
2
  var STYLES = `
7
3
  :host{all:initial;position:static;display:block;contain:none}
@@ -350,7 +346,6 @@ function createScope(theme, appearance, doc = document) {
350
346
  var cssScopeSeq = 0;
351
347
 
352
348
  export {
353
- __publicField,
354
349
  STYLES,
355
350
  HOST_ID,
356
351
  DEFAULT_THEME,
@@ -361,4 +356,4 @@ export {
361
356
  applyTheme,
362
357
  createScope
363
358
  };
364
- //# sourceMappingURL=chunk-KQGSHCNW.js.map
359
+ //# sourceMappingURL=chunk-63M4VHQG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/styles.ts","../src/host.ts"],"sourcesContent":["/** All runtime CSS, injected once into the Shadow DOM host. Tokens are CSS custom properties. */\nexport const STYLES = `\n:host{all:initial;position:static;display:block;contain:none}\n*,*::before,*::after{box-sizing:border-box}\n.g-scope [hidden]{display:none!important}\n.g-scope{\n --g-primary:#4f46e5;--g-bg:#ffffff;--g-text:#111827;--g-radius:12px;--g-font:Inter,system-ui,-apple-system,sans-serif;--g-z:2147483000;--g-overlay:.5;\n --g-muted:color-mix(in srgb,var(--g-text) 62%,transparent);--g-border:color-mix(in srgb,var(--g-text) 12%,transparent);\n --g-subtle:color-mix(in srgb,var(--g-text) 5%,transparent);--g-shadow:0 10px 40px rgba(0,0,0,.18),0 2px 8px rgba(0,0,0,.08);\n --g-primary-text:#ffffff;\n font-family:var(--g-font);font-size:14px;line-height:1.5;color:var(--g-text);-webkit-font-smoothing:antialiased;\n}\n.g-scope[data-theme=\"dark\"]{--g-shadow:0 10px 40px rgba(0,0,0,.5),0 2px 8px rgba(0,0,0,.3)}\n.g-live{position:fixed;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n.g-layer{position:fixed;inset:0;z-index:var(--g-z);pointer-events:none}\n.g-overlay{position:fixed;inset:0;background:rgba(0,0,0,var(--g-overlay));z-index:calc(var(--g-z) + 3);pointer-events:auto}\n.g-overlay.g-transparent{background:transparent}\n.g-spot-rect{position:fixed;background:rgba(0,0,0,var(--g-overlay));z-index:calc(var(--g-z) + 3);pointer-events:auto}\n.g-spot-ring{position:fixed;z-index:calc(var(--g-z) + 3);pointer-events:none;border-radius:6px;box-shadow:0 0 0 2px var(--g-primary),0 0 0 6px color-mix(in srgb,var(--g-primary) 30%,transparent)}\n.g-card{\n position:fixed;z-index:calc(var(--g-z) + 4);pointer-events:auto;background:var(--g-bg);color:var(--g-text);border-radius:var(--g-radius);\n box-shadow:var(--g-shadow);border:1px solid var(--g-border);width:min(360px,calc(100vw - 32px));max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);overflow:auto;outline:none;\n}\n.g-card.g-modal{left:50%;top:50%;transform:translate(-50%,-50%);width:min(480px,calc(100vw - 32px))}\n.g-card.g-tooltip,.g-card.g-popover,.g-card.g-spotlight-card,.g-card.g-hotspot-card{top:0;left:0;width:min(320px,calc(100vw - 32px))}\n.g-card.g-slideout{width:min(360px,calc(100vw - 32px));margin:16px}\n.g-pos-bottom-right{right:0;bottom:0}.g-pos-bottom-left{left:0;bottom:0}.g-pos-top-right{right:0;top:0}.g-pos-top-left{left:0;top:0}\n.g-pos-center{left:50%;top:50%;transform:translate(-50%,-50%)}\n.g-pos-top{top:0;left:0;right:0}.g-pos-bottom{bottom:0;left:0;right:0}\n.g-banner{position:fixed;left:0;right:0;z-index:calc(var(--g-z) + 1);pointer-events:auto;background:var(--g-primary);color:var(--g-primary-text);padding:10px 48px 10px 16px;display:flex;align-items:center;gap:12px;font-size:14px;box-shadow:0 2px 10px rgba(0,0,0,.15);max-width:100vw}\n.g-banner .g-body{flex:1;min-width:0}\n.g-banner .g-body a{color:inherit;text-decoration:underline}\n.g-banner .g-close{color:inherit;right:8px;top:50%;transform:translateY(-50%)}\n.g-banner .g-btn{background:rgba(255,255,255,.18);color:inherit;border-color:transparent;min-height:36px}\n.g-banner.g-pos-top{top:0}.g-banner.g-pos-bottom{bottom:0}\n.g-inner{padding:20px}\n.g-close{position:absolute;top:8px;right:8px;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;border:0;background:transparent;color:var(--g-muted);border-radius:999px;cursor:pointer;font:inherit;font-size:20px;line-height:1}\n.g-close:hover,.g-close:focus-visible{background:var(--g-subtle);color:var(--g-text);outline:none}\n.g-close:focus-visible,.g-btn:focus-visible,.g-task:focus-visible,.g-choice:focus-visible,.g-scale button:focus-visible,.g-launcher:focus-visible,.g-input:focus-visible,.g-rc-item:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}\n.g-image{display:block;width:100%;max-height:220px;object-fit:cover;border-radius:var(--g-radius) var(--g-radius) 0 0}\n.g-video{position:relative;display:block;width:100%;aspect-ratio:16/9;max-height:240px;overflow:hidden;background:#0f172a;border-radius:var(--g-radius) var(--g-radius) 0 0;cursor:pointer}\n.g-video-poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}\n.g-video-play{position:absolute;left:50%;top:50%;width:56px;height:56px;margin:-28px 0 0 -28px;display:flex;align-items:center;justify-content:center;padding-left:3px;box-sizing:border-box;border-radius:999px;background:rgba(15,23,42,.72);color:#fff;box-shadow:0 4px 14px rgba(0,0,0,.3);transition:transform .15s ease,background .15s ease}\n.g-video:hover .g-video-play,.g-video:focus-visible .g-video-play{transform:scale(1.08);background:var(--g-primary);color:var(--g-primary-text)}\n.g-video:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px}\n.g-title{margin:0 28px 6px 0;font-size:16px;font-weight:600;line-height:1.3}\n.g-body{font-size:14px;color:var(--g-text)}\n.g-body p{margin:0 0 8px}.g-body p:last-child{margin-bottom:0}\n.g-body ul,.g-body ol{margin:6px 0;padding-left:20px}\n.g-body a{color:var(--g-primary)}\n.g-body img{max-width:100%;height:auto;border-radius:6px}\n.g-body video,.g-body iframe{display:block;width:100%;max-width:100%;border:0;border-radius:6px;margin:8px 0;background:#000}\n.g-body video{height:auto;max-height:60vh}\n.g-body iframe{aspect-ratio:16/9;height:auto}\n.g-body .gd-pdf{margin:10px 0;border:1px solid var(--g-border);border-radius:10px;background:var(--g-subtle);overflow:hidden}\n.g-body a.gd-file{display:flex;align-items:center;gap:8px;padding:8px 10px;color:var(--g-text);font-size:13px;font-weight:500;text-decoration:none;min-height:40px;overflow-wrap:anywhere}\n.g-body a.gd-file::before{content:\"PDF\";flex:none;font-size:9px;font-weight:700;letter-spacing:.04em;line-height:1;color:#fff;background:#e5484d;border-radius:4px;padding:4px 5px}\n.g-body a.gd-file::after{content:\"⤢\";margin-left:auto;padding-left:8px;color:var(--g-muted);font-size:15px}\n.g-body a.gd-file:hover::after,.g-body a.gd-file:focus-visible::after{color:var(--g-primary)}\n.g-body a.gd-file:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px;border-radius:10px}\n.g-body > a.gd-file,.g-body p > a.gd-file{margin:10px 0;border:1px solid var(--g-border);border-radius:10px;background:var(--g-subtle)}\n.g-body > a.gd-file::after,.g-body p > a.gd-file::after,.g-body .gd-pdf:not([data-g-ready]) > a.gd-file::after{content:\"↗\"}\n.g-pdf-viewport{position:relative}\n.g-body .gd-pdf-pages{display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain;scrollbar-width:none;padding:0 10px 10px;scroll-padding:0 10px}\n.g-body .gd-pdf-pages::-webkit-scrollbar{display:none}\n.g-body .gd-pdf-pages:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px}\n.g-body .gd-pdf-pages img{flex:0 0 100%;width:100%;height:auto;margin:0;scroll-snap-align:center;background:#fff;border-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,.18);cursor:zoom-in}\n.g-pdf-viewport .gd-pdf-pages{padding:0;scroll-padding:0;margin:0 10px 10px}\n.g-pdf-count{position:absolute;right:18px;bottom:18px;padding:2px 8px;border-radius:999px;background:rgba(17,17,17,.72);color:#fff;font-size:11px;font-weight:500;pointer-events:none;font-variant-numeric:tabular-nums}\n.g-pdf-nav{position:absolute;top:calc(50% - 21px);width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:999px;background:rgba(17,17,17,.6);color:#fff;font:inherit;font-size:20px;line-height:1;cursor:pointer;opacity:0;transition:opacity .15s}\n.g-pdf-prev{left:16px}.g-pdf-next{right:16px}\n.g-pdf-viewport:hover .g-pdf-nav,.g-pdf-nav:focus-visible{opacity:1}\n.g-pdf-nav:disabled,.g-pdf-single .g-pdf-nav,.g-pdf-single .g-pdf-count{display:none}\n.g-pdf-nav:focus-visible{outline:2px solid #fff;outline-offset:1px}\n@media (hover:none){.g-pdf-nav{display:none}}\n.g-pdf-reader{position:fixed;inset:0;z-index:calc(var(--g-z) + 10);display:flex;flex-direction:column;background:#0b0b0d;pointer-events:auto;color:#fff}\n.g-pdf-bar{display:flex;align-items:center;gap:10px;padding:8px 8px 8px 16px;min-height:56px}\n.g-pdf-title{flex:1;min-width:0;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.g-pdf-bar .g-btn{min-height:36px;color:#fff;background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.18)}\n.g-pdf-close{position:static;color:#fff}\n.g-pdf-close:hover,.g-pdf-close:focus-visible{background:rgba(255,255,255,.14);color:#fff}\n.g-pdf-scroll{position:relative;flex:1;overflow-y:auto;overscroll-behavior:contain;padding:4px 16px 24px;display:flex;flex-direction:column;align-items:center;gap:12px;outline:none}\n.g-pdf-scroll img{display:block;width:100%;max-width:900px;height:auto;background:#fff;border-radius:2px;box-shadow:0 2px 12px rgba(0,0,0,.4)}\n.g-body code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;background:var(--g-subtle);padding:1px 4px;border-radius:4px}\n.g-body pre{background:var(--g-subtle);padding:10px;border-radius:6px;overflow:auto}\n.g-body blockquote{margin:6px 0;padding-left:10px;border-left:3px solid var(--g-border);color:var(--g-muted)}\n.g-footer{display:flex;align-items:center;gap:8px;margin-top:14px;flex-wrap:wrap}\n.g-progress{font-size:12px;color:var(--g-muted);margin-right:auto}\n.g-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:8px 14px;border-radius:calc(var(--g-radius) * .66);border:1px solid transparent;font:inherit;font-size:14px;font-weight:500;cursor:pointer;text-decoration:none;transition:background .15s,color .15s,border-color .15s}\n.g-btn-primary{background:var(--g-primary);color:var(--g-primary-text)}\n.g-btn-primary:hover{filter:brightness(1.08)}\n.g-btn-secondary{background:var(--g-subtle);color:var(--g-text);border-color:var(--g-border)}\n.g-btn-secondary:hover{background:color-mix(in srgb,var(--g-text) 10%,transparent)}\n.g-btn-ghost{background:transparent;color:var(--g-muted)}\n.g-btn-ghost:hover{background:var(--g-subtle);color:var(--g-text)}\n.g-btn-link{background:transparent;color:var(--g-primary);padding-left:4px;padding-right:4px}\n.g-btn-link:hover{text-decoration:underline}\n.g-btn:disabled{opacity:.5;cursor:not-allowed}\n.g-arrow{position:absolute;width:12px;height:12px;background:var(--g-bg);transform:rotate(45deg);border:1px solid var(--g-border);z-index:-1}\n.g-hotspot{position:fixed;z-index:calc(var(--g-z) + 1);pointer-events:auto;border-radius:999px;background:var(--g-primary);border:2px solid #fff;box-shadow:0 0 0 2px color-mix(in srgb,var(--g-primary) 40%,transparent);cursor:pointer;padding:0;margin:0}\n.g-hotspot::after{content:\"\";position:absolute;inset:-4px;border-radius:999px;border:2px solid var(--g-primary);opacity:0}\n.g-hotspot.g-pulse::after{animation:g-pulse 1.8s ease-out infinite}\n.g-hotspot-hit{position:absolute;inset:-14px}\n.g-custom{position:fixed;z-index:calc(var(--g-z) + 4);pointer-events:auto;left:50%;top:50%;transform:translate(-50%,-50%);max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);overflow:auto}\n/* animations */\n.g-scope[data-anim=\"fade\"] .g-card,.g-scope[data-anim=\"fade\"] .g-banner,.g-scope[data-anim=\"fade\"] .g-panel{animation:g-fade .18s ease-out}\n.g-scope[data-anim=\"slide\"] .g-card,.g-scope[data-anim=\"slide\"] .g-panel{animation:g-slide .22s ease-out}\n.g-scope[data-anim=\"none\"] *{animation:none!important;transition:none!important}\n@keyframes g-fade{from{opacity:0}to{opacity:1}}\n@keyframes g-slide{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}\n@keyframes g-pulse{0%{transform:scale(.6);opacity:.9}100%{transform:scale(2.2);opacity:0}}\n@media (prefers-reduced-motion:reduce){.g-scope *{animation:none!important;transition:none!important}}\n/* launcher + panel (checklist / resource center) */\n.g-launcher{position:fixed;z-index:calc(var(--g-z) + 1);pointer-events:auto;display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:10px 16px;border-radius:999px;border:0;background:var(--g-primary);color:var(--g-primary-text);font:inherit;font-size:14px;font-weight:600;cursor:pointer;box-shadow:var(--g-shadow);margin:16px}\n.g-launcher .g-ring{width:22px;height:22px;border-radius:999px;background:conic-gradient(#fff var(--g-pct,0%),rgba(255,255,255,.3) 0);display:inline-flex;align-items:center;justify-content:center;font-size:10px;position:relative}\n.g-launcher .g-ring::after{content:\"\";position:absolute;inset:3px;border-radius:999px;background:var(--g-primary)}\n.g-launcher .g-count{position:relative;z-index:1}\n.g-panel{position:fixed;z-index:calc(var(--g-z) + 2);pointer-events:auto;width:min(380px,calc(100vw - 32px));max-height:min(620px,calc(100vh - 100px));display:flex;flex-direction:column;background:var(--g-bg);color:var(--g-text);border-radius:var(--g-radius);box-shadow:var(--g-shadow);border:1px solid var(--g-border);margin:16px;bottom:60px;overflow:hidden}\n.g-panel-head{padding:16px 48px 12px 20px;border-bottom:1px solid var(--g-border);position:relative}\n.g-panel-title{margin:0;font-size:16px;font-weight:600}\n.g-panel-sub{margin:4px 0 0;font-size:12px;color:var(--g-muted)}\n.g-panel-body{overflow:auto;padding:8px 0;flex:1}\n.g-help{display:flex;flex-direction:column;min-height:0;flex:1}\n.g-bar{height:6px;border-radius:999px;background:var(--g-subtle);overflow:hidden;margin-top:10px}\n.g-bar > span{display:block;height:100%;background:var(--g-primary);transition:width .3s}\n.g-task{display:flex;gap:12px;align-items:flex-start;width:100%;padding:12px 20px;border:0;background:transparent;text-align:left;font:inherit;color:inherit;cursor:pointer;min-height:44px}\n.g-task:hover{background:var(--g-subtle)}\n.g-task[disabled]{cursor:not-allowed;opacity:.55}\n.g-task .g-check{flex:none;width:22px;height:22px;border-radius:999px;border:2px solid var(--g-border);display:inline-flex;align-items:center;justify-content:center;font-size:13px;margin-top:1px;color:#fff}\n.g-task.g-done .g-check{background:var(--g-primary);border-color:var(--g-primary)}\n.g-task.g-done .g-task-title{text-decoration:line-through;color:var(--g-muted)}\n.g-task-title{font-weight:500}\n.g-task-desc{font-size:12px;color:var(--g-muted);margin-top:2px}\n.g-panel-foot{padding:10px 20px;border-top:1px solid var(--g-border);display:flex;justify-content:flex-end;gap:8px}\n.g-complete{padding:28px 20px;text-align:center}\n.g-complete .g-title{margin-right:0;font-size:18px}\n/* survey */\n.g-question{margin-bottom:16px}\n.g-q-title{font-weight:600;margin:0 0 4px}\n.g-q-desc{font-size:12px;color:var(--g-muted);margin:0 0 8px}\n.g-scale{display:flex;gap:4px;flex-wrap:wrap}\n.g-scale button{min-width:34px;height:40px;flex:1;padding:0 4px;border-radius:6px;border:1px solid var(--g-border);background:var(--g-subtle);color:var(--g-text);font:inherit;cursor:pointer}\n.g-scale button[aria-pressed=\"true\"],.g-scale button:hover{background:var(--g-primary);color:var(--g-primary-text);border-color:var(--g-primary)}\n.g-scale-labels{display:flex;justify-content:space-between;font-size:11px;color:var(--g-muted);margin-top:4px}\n.g-stars button,.g-thumbs button{font-size:22px;background:transparent;border:0;cursor:pointer;color:var(--g-muted);min-width:40px;height:40px;font:inherit}\n.g-stars button[aria-pressed=\"true\"],.g-stars button.g-on{color:#f59e0b}\n.g-thumbs button[aria-pressed=\"true\"]{color:var(--g-primary)}\n.g-choice{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--g-border);border-radius:8px;margin-bottom:6px;cursor:pointer;min-height:40px;font:inherit;background:transparent;color:inherit;width:100%;text-align:left}\n.g-choice[aria-checked=\"true\"]{border-color:var(--g-primary);background:color-mix(in srgb,var(--g-primary) 8%,transparent)}\n.g-choice .g-dot{width:16px;height:16px;border-radius:999px;border:2px solid var(--g-border);flex:none}\n.g-choice.g-multi .g-dot{border-radius:4px}\n.g-choice[aria-checked=\"true\"] .g-dot{background:var(--g-primary);border-color:var(--g-primary)}\n.g-input{width:100%;min-height:40px;padding:8px 10px;border:1px solid var(--g-border);border-radius:8px;background:var(--g-bg);color:var(--g-text);font:inherit;resize:vertical}\n.g-error{color:#dc2626;font-size:12px;margin-top:4px}\n/* resource center */\n.g-rc-search{padding:12px 20px 4px}\n.g-rc-section{padding:8px 20px 4px;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--g-muted);font-weight:600}\n.g-rc-section[data-depth]{text-transform:none;letter-spacing:0;font-size:12px}\n.g-rc-item{display:flex;align-items:flex-start;gap:10px;width:100%;padding:10px 20px;border:0;background:transparent;font:inherit;color:inherit;text-align:left;cursor:pointer;min-height:44px;text-decoration:none}\n.g-rc-item:hover{background:var(--g-subtle)}\n.g-rc-item .g-rc-title{font-weight:500}\n.g-rc-item .g-rc-sum{font-size:12px;color:var(--g-muted);margin-top:2px}\n.g-rc-item .g-rc-icon{flex:none;width:20px;text-align:center;color:var(--g-primary)}\n.g-rc-row{display:flex;align-items:center;gap:6px;padding-right:16px}\n.g-rc-row>.g-rc-item{flex:1;min-width:0;padding-right:4px}\n.g-rc-row:hover{background:var(--g-subtle)}\n.g-rc-row:hover>.g-rc-item{background:transparent}\n.g-rc-showme{flex:none;min-height:32px;padding:4px 10px;font-size:12px;white-space:nowrap}\n.g-help-showme{margin:4px 0 12px}\n.g-rc-article{padding:16px 20px}\n.g-resource-center .g-panel{transition:width .2s ease,max-height .2s ease}\n.g-resource-center .g-panel.g-panel-reading{width:min(720px,calc(100vw - 32px));max-height:calc(100vh - 100px)}\n.g-rc-back{margin-bottom:8px}\n.g-rc-empty{padding:24px 20px;color:var(--g-muted);text-align:center}\n.g-faq-a{padding:0 20px 12px 50px;font-size:13px;color:var(--g-muted)}\n.g-faq-a[hidden]{display:none}\n/* authoring */\n.g-auth-bar{position:fixed;top:0;left:0;right:0;z-index:calc(var(--g-z) + 10);pointer-events:auto;display:flex;align-items:center;gap:12px;padding:8px 16px;background:#111827;color:#fff;font:500 13px/1.4 var(--g-font);box-shadow:0 2px 10px rgba(0,0,0,.3)}\n.g-auth-bar .g-dot-live{width:8px;height:8px;border-radius:999px;background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,.3)}\n.g-auth-bar .g-spacer{flex:1}\n.g-auth-bar .g-btn{min-height:32px;padding:4px 12px;font-size:13px}\n.g-auth-bar .g-btn-secondary{background:rgba(255,255,255,.12);color:#fff;border-color:transparent}\n.g-auth-outline{position:fixed;z-index:calc(var(--g-z) + 9);pointer-events:none;border:2px solid var(--g-primary);background:color-mix(in srgb,var(--g-primary) 12%,transparent);border-radius:4px;transition:all .06s linear}\n.g-auth-label{position:absolute;left:-2px;top:-24px;background:var(--g-primary);color:#fff;font:600 11px/1 var(--g-font);padding:5px 7px;border-radius:4px;white-space:nowrap;max-width:320px;overflow:hidden;text-overflow:ellipsis}\n.g-auth-outline.g-picked{border-style:dashed;border-color:#22c55e;background:rgba(34,197,94,.12)}\n.g-auth-outline.g-picked .g-auth-label{background:#16a34a}\n.g-highlight{position:fixed;z-index:calc(var(--g-z) + 8);pointer-events:none;border:2px dashed #f59e0b;background:rgba(245,158,11,.12);border-radius:4px}\n@media (max-width:480px){.g-card.g-modal,.g-panel{width:calc(100vw - 32px)}.g-inner{padding:16px}}\n/* resource center: unread badge + \"What's new\" */\n.g-launcher-badge{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:#dc2626;color:#fff;font-size:11px;font-weight:700;line-height:20px;text-align:center;box-shadow:0 0 0 2px var(--g-bg)}\n.g-rc-news{padding:10px 20px 12px;border-bottom:1px solid var(--g-border)}\n.g-rc-news:last-of-type{border-bottom:0}\n.g-rc-news-head{display:flex;align-items:baseline;gap:8px}\n.g-rc-news-title{margin:0;font-size:14px;font-weight:600;line-height:1.35;flex:1}\n.g-rc-news-new{flex:none;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--g-primary-text);background:var(--g-primary);border-radius:999px;padding:2px 7px}\n.g-rc-news-date{display:block;font-size:12px;color:var(--g-muted);margin:2px 0 6px}\n.g-rc-news-body{font-size:13px}\n.g-rc-news-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}\n.g-rc-news-actions .g-btn{min-height:32px;padding:6px 12px;font-size:13px}\n/* accessibility */\n.g-sr-only{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}\n.g-hotspot:focus-visible,.g-custom:focus-visible,.g-card:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}\n@media (forced-colors:active){.g-card,.g-panel,.g-banner{border:1px solid CanvasText}.g-btn{border:1px solid ButtonText}}\n`;\n","import type { Appearance, ProjectTheme } from \"@guidelytech/shared\";\nimport { readableTextOn, scopeCustomCss } from \"@guidelytech/shared/runtime\";\nimport { STYLES } from \"./styles\";\n\nexport const HOST_ID = \"guidely-root\";\n\nexport interface HostHandle {\n host: HTMLElement;\n shadow: ShadowRoot;\n /** Polite live region for screen-reader announcements. */\n live: HTMLElement;\n announce(text: string): void;\n destroy(): void;\n}\n\nlet current: HostHandle | null = null;\n\n/** Default project theme used when the config has not loaded yet. */\nexport const DEFAULT_THEME: ProjectTheme = {\n primaryColor: \"#4f46e5\",\n backgroundColor: \"#ffffff\",\n textColor: \"#111827\",\n borderRadius: 12,\n fontFamily: \"Inter, system-ui, -apple-system, sans-serif\",\n};\n\n/** Returns (creating when needed) the single Shadow-DOM host appended to `document.body`. */\nexport function ensureHost(doc: Document = document): HostHandle {\n if (current && current.host.isConnected && current.host.ownerDocument === doc) return current;\n let host = doc.getElementById(HOST_ID) as HTMLElement | null;\n if (!host) {\n host = doc.createElement(\"div\");\n host.id = HOST_ID;\n host.setAttribute(\"data-guidely\", \"\");\n (doc.body || doc.documentElement).appendChild(host);\n }\n const shadow = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n if (!shadow.querySelector(\"style[data-g-base]\")) {\n const style = doc.createElement(\"style\");\n style.setAttribute(\"data-g-base\", \"\");\n style.textContent = STYLES;\n shadow.appendChild(style);\n }\n let live = shadow.querySelector<HTMLElement>(\".g-live\");\n if (!live) {\n live = doc.createElement(\"div\");\n live.className = \"g-live\";\n live.setAttribute(\"aria-live\", \"polite\");\n live.setAttribute(\"aria-atomic\", \"true\");\n shadow.appendChild(live);\n }\n const liveEl = live;\n current = {\n host,\n shadow,\n live: liveEl,\n announce(text: string) {\n liveEl.textContent = \"\";\n setTimeout(() => {\n liveEl.textContent = text;\n }, 30);\n },\n destroy() {\n host?.remove();\n if (current?.host === host) current = null;\n },\n };\n return current;\n}\n\n/** Removes the host from the document (all overlays with it). */\nexport function destroyHost(): void {\n current?.destroy();\n const stray = typeof document !== \"undefined\" ? document.getElementById(HOST_ID) : null;\n stray?.remove();\n current = null;\n}\n\nexport type ResolvedThemeMode = \"light\" | \"dark\";\n\nfunction prefersDark(): boolean {\n try {\n return typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" && window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n } catch {\n return false;\n }\n}\n\nexport function prefersReducedMotion(): boolean {\n try {\n return typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n } catch {\n return false;\n }\n}\n\nexport interface ThemeVars {\n primary: string;\n bg: string;\n text: string;\n radius: number;\n font: string;\n z: number;\n overlay: number;\n mode: ResolvedThemeMode;\n animation: Appearance[\"animation\"];\n}\n\n/** Merges project theme + guide appearance into CSS custom property values. */\nexport function resolveTheme(theme: Partial<ProjectTheme> | null | undefined, appearance?: Partial<Appearance> | null): ThemeVars {\n const t = { ...DEFAULT_THEME, ...(theme || {}) };\n const a = appearance || {};\n const requested = a.theme ?? \"auto\";\n const mode: ResolvedThemeMode = requested === \"auto\" ? (prefersDark() ? \"dark\" : \"light\") : requested;\n const dark = mode === \"dark\";\n return {\n primary: a.primaryColor || t.primaryColor,\n bg: a.backgroundColor || (dark ? \"#1f2937\" : t.backgroundColor),\n text: a.textColor || (dark ? \"#f9fafb\" : t.textColor),\n radius: a.borderRadius ?? t.borderRadius,\n font: a.fontFamily || t.fontFamily,\n z: a.zIndex ?? 2147483000,\n overlay: a.overlayOpacity ?? 0.5,\n mode,\n animation: prefersReducedMotion() ? \"none\" : (a.animation ?? \"fade\"),\n };\n}\n\n/** Applies theme variables to a scope element. */\nexport function applyTheme(el: HTMLElement, vars: ThemeVars): void {\n el.style.setProperty(\"--g-primary\", vars.primary);\n // Button labels, banners and badges on the primary colour use whichever of white or dark text reads better (WCAG AA).\n if (/^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(vars.primary)) el.style.setProperty(\"--g-primary-text\", readableTextOn(vars.primary));\n el.style.setProperty(\"--g-bg\", vars.bg);\n el.style.setProperty(\"--g-text\", vars.text);\n el.style.setProperty(\"--g-radius\", `${vars.radius}px`);\n el.style.setProperty(\"--g-font\", vars.font);\n el.style.setProperty(\"--g-z\", String(vars.z));\n el.style.setProperty(\"--g-overlay\", String(vars.overlay));\n el.setAttribute(\"data-theme\", vars.mode);\n el.setAttribute(\"data-anim\", vars.animation);\n}\n\n/**\n * Creates a themed scope container inside the shadow root. Each guide/widget renders inside its\n * own scope so appearance settings never leak between guides.\n */\nexport function createScope(theme?: Partial<ProjectTheme> | null, appearance?: Partial<Appearance> | null, doc: Document = document): { el: HTMLElement; vars: ThemeVars; destroy(): void } {\n const handle = ensureHost(doc);\n const el = doc.createElement(\"div\");\n el.className = \"g-scope\";\n const vars = resolveTheme(theme, appearance);\n applyTheme(el, vars);\n handle.shadow.appendChild(el);\n // Custom CSS (theme first, then the guide's) is sanitized and prefixed with this scope only. The\n // theme's CSS styles guides (scopes created with an appearance), not the resource center or messenger.\n const css = [appearance ? theme?.customCss : undefined, appearance?.customCss].filter((c): c is string => !!c && !!c.trim());\n let style: HTMLStyleElement | null = null;\n if (css.length) {\n const id = `c${++cssScopeSeq}`;\n el.setAttribute(\"data-g-css\", id);\n style = doc.createElement(\"style\");\n style.setAttribute(\"data-g-custom\", id);\n style.textContent = css.map((c) => scopeCustomCss(c, `.g-scope[data-g-css=\"${id}\"]`)).join(\"\\n\");\n handle.shadow.insertBefore(style, el);\n }\n let mq: MediaQueryList | null = null;\n const onChange = () => applyTheme(el, resolveTheme(theme, appearance));\n if ((appearance?.theme ?? \"auto\") === \"auto\" && typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n try {\n mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n mq.addEventListener?.(\"change\", onChange);\n } catch {\n mq = null;\n }\n }\n return {\n el,\n vars,\n destroy() {\n mq?.removeEventListener?.(\"change\", onChange);\n style?.remove();\n el.remove();\n },\n };\n}\n\nlet cssScopeSeq = 0;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/styles.ts","../src/host.ts"],"sourcesContent":["/** All runtime CSS, injected once into the Shadow DOM host. Tokens are CSS custom properties. */\nexport const STYLES = `\n:host{all:initial;position:static;display:block;contain:none}\n*,*::before,*::after{box-sizing:border-box}\n.g-scope [hidden]{display:none!important}\n.g-scope{\n --g-primary:#4f46e5;--g-bg:#ffffff;--g-text:#111827;--g-radius:12px;--g-font:Inter,system-ui,-apple-system,sans-serif;--g-z:2147483000;--g-overlay:.5;\n --g-muted:color-mix(in srgb,var(--g-text) 62%,transparent);--g-border:color-mix(in srgb,var(--g-text) 12%,transparent);\n --g-subtle:color-mix(in srgb,var(--g-text) 5%,transparent);--g-shadow:0 10px 40px rgba(0,0,0,.18),0 2px 8px rgba(0,0,0,.08);\n --g-primary-text:#ffffff;\n font-family:var(--g-font);font-size:14px;line-height:1.5;color:var(--g-text);-webkit-font-smoothing:antialiased;\n}\n.g-scope[data-theme=\"dark\"]{--g-shadow:0 10px 40px rgba(0,0,0,.5),0 2px 8px rgba(0,0,0,.3)}\n.g-live{position:fixed;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n.g-layer{position:fixed;inset:0;z-index:var(--g-z);pointer-events:none}\n.g-overlay{position:fixed;inset:0;background:rgba(0,0,0,var(--g-overlay));z-index:calc(var(--g-z) + 3);pointer-events:auto}\n.g-overlay.g-transparent{background:transparent}\n.g-spot-rect{position:fixed;background:rgba(0,0,0,var(--g-overlay));z-index:calc(var(--g-z) + 3);pointer-events:auto}\n.g-spot-ring{position:fixed;z-index:calc(var(--g-z) + 3);pointer-events:none;border-radius:6px;box-shadow:0 0 0 2px var(--g-primary),0 0 0 6px color-mix(in srgb,var(--g-primary) 30%,transparent)}\n.g-card{\n position:fixed;z-index:calc(var(--g-z) + 4);pointer-events:auto;background:var(--g-bg);color:var(--g-text);border-radius:var(--g-radius);\n box-shadow:var(--g-shadow);border:1px solid var(--g-border);width:min(360px,calc(100vw - 32px));max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);overflow:auto;outline:none;\n}\n.g-card.g-modal{left:50%;top:50%;transform:translate(-50%,-50%);width:min(480px,calc(100vw - 32px))}\n.g-card.g-tooltip,.g-card.g-popover,.g-card.g-spotlight-card,.g-card.g-hotspot-card{top:0;left:0;width:min(320px,calc(100vw - 32px))}\n.g-card.g-slideout{width:min(360px,calc(100vw - 32px));margin:16px}\n.g-pos-bottom-right{right:0;bottom:0}.g-pos-bottom-left{left:0;bottom:0}.g-pos-top-right{right:0;top:0}.g-pos-top-left{left:0;top:0}\n.g-pos-center{left:50%;top:50%;transform:translate(-50%,-50%)}\n.g-pos-top{top:0;left:0;right:0}.g-pos-bottom{bottom:0;left:0;right:0}\n.g-banner{position:fixed;left:0;right:0;z-index:calc(var(--g-z) + 1);pointer-events:auto;background:var(--g-primary);color:var(--g-primary-text);padding:10px 48px 10px 16px;display:flex;align-items:center;gap:12px;font-size:14px;box-shadow:0 2px 10px rgba(0,0,0,.15);max-width:100vw}\n.g-banner .g-body{flex:1;min-width:0}\n.g-banner .g-body a{color:inherit;text-decoration:underline}\n.g-banner .g-close{color:inherit;right:8px;top:50%;transform:translateY(-50%)}\n.g-banner .g-btn{background:rgba(255,255,255,.18);color:inherit;border-color:transparent;min-height:36px}\n.g-banner.g-pos-top{top:0}.g-banner.g-pos-bottom{bottom:0}\n.g-inner{padding:20px}\n.g-close{position:absolute;top:8px;right:8px;width:40px;height:40px;display:inline-flex;align-items:center;justify-content:center;border:0;background:transparent;color:var(--g-muted);border-radius:999px;cursor:pointer;font:inherit;font-size:20px;line-height:1}\n.g-close:hover,.g-close:focus-visible{background:var(--g-subtle);color:var(--g-text);outline:none}\n.g-close:focus-visible,.g-btn:focus-visible,.g-task:focus-visible,.g-choice:focus-visible,.g-scale button:focus-visible,.g-launcher:focus-visible,.g-input:focus-visible,.g-rc-item:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}\n.g-image{display:block;width:100%;max-height:220px;object-fit:cover;border-radius:var(--g-radius) var(--g-radius) 0 0}\n.g-video{position:relative;display:block;width:100%;aspect-ratio:16/9;max-height:240px;overflow:hidden;background:#0f172a;border-radius:var(--g-radius) var(--g-radius) 0 0;cursor:pointer}\n.g-video-poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}\n.g-video-play{position:absolute;left:50%;top:50%;width:56px;height:56px;margin:-28px 0 0 -28px;display:flex;align-items:center;justify-content:center;padding-left:3px;box-sizing:border-box;border-radius:999px;background:rgba(15,23,42,.72);color:#fff;box-shadow:0 4px 14px rgba(0,0,0,.3);transition:transform .15s ease,background .15s ease}\n.g-video:hover .g-video-play,.g-video:focus-visible .g-video-play{transform:scale(1.08);background:var(--g-primary);color:var(--g-primary-text)}\n.g-video:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px}\n.g-title{margin:0 28px 6px 0;font-size:16px;font-weight:600;line-height:1.3}\n.g-body{font-size:14px;color:var(--g-text)}\n.g-body p{margin:0 0 8px}.g-body p:last-child{margin-bottom:0}\n.g-body ul,.g-body ol{margin:6px 0;padding-left:20px}\n.g-body a{color:var(--g-primary)}\n.g-body img{max-width:100%;height:auto;border-radius:6px}\n.g-body video,.g-body iframe{display:block;width:100%;max-width:100%;border:0;border-radius:6px;margin:8px 0;background:#000}\n.g-body video{height:auto;max-height:60vh}\n.g-body iframe{aspect-ratio:16/9;height:auto}\n.g-body .gd-pdf{margin:10px 0;border:1px solid var(--g-border);border-radius:10px;background:var(--g-subtle);overflow:hidden}\n.g-body a.gd-file{display:flex;align-items:center;gap:8px;padding:8px 10px;color:var(--g-text);font-size:13px;font-weight:500;text-decoration:none;min-height:40px;overflow-wrap:anywhere}\n.g-body a.gd-file::before{content:\"PDF\";flex:none;font-size:9px;font-weight:700;letter-spacing:.04em;line-height:1;color:#fff;background:#e5484d;border-radius:4px;padding:4px 5px}\n.g-body a.gd-file::after{content:\"⤢\";margin-left:auto;padding-left:8px;color:var(--g-muted);font-size:15px}\n.g-body a.gd-file:hover::after,.g-body a.gd-file:focus-visible::after{color:var(--g-primary)}\n.g-body a.gd-file:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px;border-radius:10px}\n.g-body > a.gd-file,.g-body p > a.gd-file{margin:10px 0;border:1px solid var(--g-border);border-radius:10px;background:var(--g-subtle)}\n.g-body > a.gd-file::after,.g-body p > a.gd-file::after,.g-body .gd-pdf:not([data-g-ready]) > a.gd-file::after{content:\"↗\"}\n.g-pdf-viewport{position:relative}\n.g-body .gd-pdf-pages{display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;overscroll-behavior-x:contain;scrollbar-width:none;padding:0 10px 10px;scroll-padding:0 10px}\n.g-body .gd-pdf-pages::-webkit-scrollbar{display:none}\n.g-body .gd-pdf-pages:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px}\n.g-body .gd-pdf-pages img{flex:0 0 100%;width:100%;height:auto;margin:0;scroll-snap-align:center;background:#fff;border-radius:4px;box-shadow:0 1px 3px rgba(0,0,0,.18);cursor:zoom-in}\n.g-pdf-viewport .gd-pdf-pages{padding:0;scroll-padding:0;margin:0 10px 10px}\n.g-pdf-count{position:absolute;right:18px;bottom:18px;padding:2px 8px;border-radius:999px;background:rgba(17,17,17,.72);color:#fff;font-size:11px;font-weight:500;pointer-events:none;font-variant-numeric:tabular-nums}\n.g-pdf-nav{position:absolute;top:calc(50% - 21px);width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;border:0;border-radius:999px;background:rgba(17,17,17,.6);color:#fff;font:inherit;font-size:20px;line-height:1;cursor:pointer;opacity:0;transition:opacity .15s}\n.g-pdf-prev{left:16px}.g-pdf-next{right:16px}\n.g-pdf-viewport:hover .g-pdf-nav,.g-pdf-nav:focus-visible{opacity:1}\n.g-pdf-nav:disabled,.g-pdf-single .g-pdf-nav,.g-pdf-single .g-pdf-count{display:none}\n.g-pdf-nav:focus-visible{outline:2px solid #fff;outline-offset:1px}\n@media (hover:none){.g-pdf-nav{display:none}}\n.g-pdf-reader{position:fixed;inset:0;z-index:calc(var(--g-z) + 10);display:flex;flex-direction:column;background:#0b0b0d;pointer-events:auto;color:#fff}\n.g-pdf-bar{display:flex;align-items:center;gap:10px;padding:8px 8px 8px 16px;min-height:56px}\n.g-pdf-title{flex:1;min-width:0;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.g-pdf-bar .g-btn{min-height:36px;color:#fff;background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.18)}\n.g-pdf-close{position:static;color:#fff}\n.g-pdf-close:hover,.g-pdf-close:focus-visible{background:rgba(255,255,255,.14);color:#fff}\n.g-pdf-scroll{position:relative;flex:1;overflow-y:auto;overscroll-behavior:contain;padding:4px 16px 24px;display:flex;flex-direction:column;align-items:center;gap:12px;outline:none}\n.g-pdf-scroll img{display:block;width:100%;max-width:900px;height:auto;background:#fff;border-radius:2px;box-shadow:0 2px 12px rgba(0,0,0,.4)}\n.g-body code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;background:var(--g-subtle);padding:1px 4px;border-radius:4px}\n.g-body pre{background:var(--g-subtle);padding:10px;border-radius:6px;overflow:auto}\n.g-body blockquote{margin:6px 0;padding-left:10px;border-left:3px solid var(--g-border);color:var(--g-muted)}\n.g-footer{display:flex;align-items:center;gap:8px;margin-top:14px;flex-wrap:wrap}\n.g-progress{font-size:12px;color:var(--g-muted);margin-right:auto}\n.g-btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:8px 14px;border-radius:calc(var(--g-radius) * .66);border:1px solid transparent;font:inherit;font-size:14px;font-weight:500;cursor:pointer;text-decoration:none;transition:background .15s,color .15s,border-color .15s}\n.g-btn-primary{background:var(--g-primary);color:var(--g-primary-text)}\n.g-btn-primary:hover{filter:brightness(1.08)}\n.g-btn-secondary{background:var(--g-subtle);color:var(--g-text);border-color:var(--g-border)}\n.g-btn-secondary:hover{background:color-mix(in srgb,var(--g-text) 10%,transparent)}\n.g-btn-ghost{background:transparent;color:var(--g-muted)}\n.g-btn-ghost:hover{background:var(--g-subtle);color:var(--g-text)}\n.g-btn-link{background:transparent;color:var(--g-primary);padding-left:4px;padding-right:4px}\n.g-btn-link:hover{text-decoration:underline}\n.g-btn:disabled{opacity:.5;cursor:not-allowed}\n.g-arrow{position:absolute;width:12px;height:12px;background:var(--g-bg);transform:rotate(45deg);border:1px solid var(--g-border);z-index:-1}\n.g-hotspot{position:fixed;z-index:calc(var(--g-z) + 1);pointer-events:auto;border-radius:999px;background:var(--g-primary);border:2px solid #fff;box-shadow:0 0 0 2px color-mix(in srgb,var(--g-primary) 40%,transparent);cursor:pointer;padding:0;margin:0}\n.g-hotspot::after{content:\"\";position:absolute;inset:-4px;border-radius:999px;border:2px solid var(--g-primary);opacity:0}\n.g-hotspot.g-pulse::after{animation:g-pulse 1.8s ease-out infinite}\n.g-hotspot-hit{position:absolute;inset:-14px}\n.g-custom{position:fixed;z-index:calc(var(--g-z) + 4);pointer-events:auto;left:50%;top:50%;transform:translate(-50%,-50%);max-width:calc(100vw - 32px);max-height:calc(100vh - 32px);overflow:auto}\n/* animations */\n.g-scope[data-anim=\"fade\"] .g-card,.g-scope[data-anim=\"fade\"] .g-banner,.g-scope[data-anim=\"fade\"] .g-panel{animation:g-fade .18s ease-out}\n.g-scope[data-anim=\"slide\"] .g-card,.g-scope[data-anim=\"slide\"] .g-panel{animation:g-slide .22s ease-out}\n.g-scope[data-anim=\"none\"] *{animation:none!important;transition:none!important}\n@keyframes g-fade{from{opacity:0}to{opacity:1}}\n@keyframes g-slide{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}\n@keyframes g-pulse{0%{transform:scale(.6);opacity:.9}100%{transform:scale(2.2);opacity:0}}\n@media (prefers-reduced-motion:reduce){.g-scope *{animation:none!important;transition:none!important}}\n/* launcher + panel (checklist / resource center) */\n.g-launcher{position:fixed;z-index:calc(var(--g-z) + 1);pointer-events:auto;display:inline-flex;align-items:center;gap:8px;min-height:44px;padding:10px 16px;border-radius:999px;border:0;background:var(--g-primary);color:var(--g-primary-text);font:inherit;font-size:14px;font-weight:600;cursor:pointer;box-shadow:var(--g-shadow);margin:16px}\n.g-launcher .g-ring{width:22px;height:22px;border-radius:999px;background:conic-gradient(#fff var(--g-pct,0%),rgba(255,255,255,.3) 0);display:inline-flex;align-items:center;justify-content:center;font-size:10px;position:relative}\n.g-launcher .g-ring::after{content:\"\";position:absolute;inset:3px;border-radius:999px;background:var(--g-primary)}\n.g-launcher .g-count{position:relative;z-index:1}\n.g-panel{position:fixed;z-index:calc(var(--g-z) + 2);pointer-events:auto;width:min(380px,calc(100vw - 32px));max-height:min(620px,calc(100vh - 100px));display:flex;flex-direction:column;background:var(--g-bg);color:var(--g-text);border-radius:var(--g-radius);box-shadow:var(--g-shadow);border:1px solid var(--g-border);margin:16px;bottom:60px;overflow:hidden}\n.g-panel-head{padding:16px 48px 12px 20px;border-bottom:1px solid var(--g-border);position:relative}\n.g-panel-title{margin:0;font-size:16px;font-weight:600}\n.g-panel-sub{margin:4px 0 0;font-size:12px;color:var(--g-muted)}\n.g-panel-body{overflow:auto;padding:8px 0;flex:1}\n.g-help{display:flex;flex-direction:column;min-height:0;flex:1}\n.g-bar{height:6px;border-radius:999px;background:var(--g-subtle);overflow:hidden;margin-top:10px}\n.g-bar > span{display:block;height:100%;background:var(--g-primary);transition:width .3s}\n.g-task{display:flex;gap:12px;align-items:flex-start;width:100%;padding:12px 20px;border:0;background:transparent;text-align:left;font:inherit;color:inherit;cursor:pointer;min-height:44px}\n.g-task:hover{background:var(--g-subtle)}\n.g-task[disabled]{cursor:not-allowed;opacity:.55}\n.g-task .g-check{flex:none;width:22px;height:22px;border-radius:999px;border:2px solid var(--g-border);display:inline-flex;align-items:center;justify-content:center;font-size:13px;margin-top:1px;color:#fff}\n.g-task.g-done .g-check{background:var(--g-primary);border-color:var(--g-primary)}\n.g-task.g-done .g-task-title{text-decoration:line-through;color:var(--g-muted)}\n.g-task-title{font-weight:500}\n.g-task-desc{font-size:12px;color:var(--g-muted);margin-top:2px}\n.g-panel-foot{padding:10px 20px;border-top:1px solid var(--g-border);display:flex;justify-content:flex-end;gap:8px}\n.g-complete{padding:28px 20px;text-align:center}\n.g-complete .g-title{margin-right:0;font-size:18px}\n/* survey */\n.g-question{margin-bottom:16px}\n.g-q-title{font-weight:600;margin:0 0 4px}\n.g-q-desc{font-size:12px;color:var(--g-muted);margin:0 0 8px}\n.g-scale{display:flex;gap:4px;flex-wrap:wrap}\n.g-scale button{min-width:34px;height:40px;flex:1;padding:0 4px;border-radius:6px;border:1px solid var(--g-border);background:var(--g-subtle);color:var(--g-text);font:inherit;cursor:pointer}\n.g-scale button[aria-pressed=\"true\"],.g-scale button:hover{background:var(--g-primary);color:var(--g-primary-text);border-color:var(--g-primary)}\n.g-scale-labels{display:flex;justify-content:space-between;font-size:11px;color:var(--g-muted);margin-top:4px}\n.g-stars button,.g-thumbs button{font-size:22px;background:transparent;border:0;cursor:pointer;color:var(--g-muted);min-width:40px;height:40px;font:inherit}\n.g-stars button[aria-pressed=\"true\"],.g-stars button.g-on{color:#f59e0b}\n.g-thumbs button[aria-pressed=\"true\"]{color:var(--g-primary)}\n.g-choice{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--g-border);border-radius:8px;margin-bottom:6px;cursor:pointer;min-height:40px;font:inherit;background:transparent;color:inherit;width:100%;text-align:left}\n.g-choice[aria-checked=\"true\"]{border-color:var(--g-primary);background:color-mix(in srgb,var(--g-primary) 8%,transparent)}\n.g-choice .g-dot{width:16px;height:16px;border-radius:999px;border:2px solid var(--g-border);flex:none}\n.g-choice.g-multi .g-dot{border-radius:4px}\n.g-choice[aria-checked=\"true\"] .g-dot{background:var(--g-primary);border-color:var(--g-primary)}\n.g-input{width:100%;min-height:40px;padding:8px 10px;border:1px solid var(--g-border);border-radius:8px;background:var(--g-bg);color:var(--g-text);font:inherit;resize:vertical}\n.g-error{color:#dc2626;font-size:12px;margin-top:4px}\n/* resource center */\n.g-rc-search{padding:12px 20px 4px}\n.g-rc-section{padding:8px 20px 4px;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--g-muted);font-weight:600}\n.g-rc-section[data-depth]{text-transform:none;letter-spacing:0;font-size:12px}\n.g-rc-item{display:flex;align-items:flex-start;gap:10px;width:100%;padding:10px 20px;border:0;background:transparent;font:inherit;color:inherit;text-align:left;cursor:pointer;min-height:44px;text-decoration:none}\n.g-rc-item:hover{background:var(--g-subtle)}\n.g-rc-item .g-rc-title{font-weight:500}\n.g-rc-item .g-rc-sum{font-size:12px;color:var(--g-muted);margin-top:2px}\n.g-rc-item .g-rc-icon{flex:none;width:20px;text-align:center;color:var(--g-primary)}\n.g-rc-row{display:flex;align-items:center;gap:6px;padding-right:16px}\n.g-rc-row>.g-rc-item{flex:1;min-width:0;padding-right:4px}\n.g-rc-row:hover{background:var(--g-subtle)}\n.g-rc-row:hover>.g-rc-item{background:transparent}\n.g-rc-showme{flex:none;min-height:32px;padding:4px 10px;font-size:12px;white-space:nowrap}\n.g-help-showme{margin:4px 0 12px}\n.g-rc-article{padding:16px 20px}\n.g-resource-center .g-panel{transition:width .2s ease,max-height .2s ease}\n.g-resource-center .g-panel.g-panel-reading{width:min(720px,calc(100vw - 32px));max-height:calc(100vh - 100px)}\n.g-rc-back{margin-bottom:8px}\n.g-rc-empty{padding:24px 20px;color:var(--g-muted);text-align:center}\n.g-faq-a{padding:0 20px 12px 50px;font-size:13px;color:var(--g-muted)}\n.g-faq-a[hidden]{display:none}\n/* authoring */\n.g-auth-bar{position:fixed;top:0;left:0;right:0;z-index:calc(var(--g-z) + 10);pointer-events:auto;display:flex;align-items:center;gap:12px;padding:8px 16px;background:#111827;color:#fff;font:500 13px/1.4 var(--g-font);box-shadow:0 2px 10px rgba(0,0,0,.3)}\n.g-auth-bar .g-dot-live{width:8px;height:8px;border-radius:999px;background:#22c55e;box-shadow:0 0 0 3px rgba(34,197,94,.3)}\n.g-auth-bar .g-spacer{flex:1}\n.g-auth-bar .g-btn{min-height:32px;padding:4px 12px;font-size:13px}\n.g-auth-bar .g-btn-secondary{background:rgba(255,255,255,.12);color:#fff;border-color:transparent}\n.g-auth-outline{position:fixed;z-index:calc(var(--g-z) + 9);pointer-events:none;border:2px solid var(--g-primary);background:color-mix(in srgb,var(--g-primary) 12%,transparent);border-radius:4px;transition:all .06s linear}\n.g-auth-label{position:absolute;left:-2px;top:-24px;background:var(--g-primary);color:#fff;font:600 11px/1 var(--g-font);padding:5px 7px;border-radius:4px;white-space:nowrap;max-width:320px;overflow:hidden;text-overflow:ellipsis}\n.g-auth-outline.g-picked{border-style:dashed;border-color:#22c55e;background:rgba(34,197,94,.12)}\n.g-auth-outline.g-picked .g-auth-label{background:#16a34a}\n.g-highlight{position:fixed;z-index:calc(var(--g-z) + 8);pointer-events:none;border:2px dashed #f59e0b;background:rgba(245,158,11,.12);border-radius:4px}\n@media (max-width:480px){.g-card.g-modal,.g-panel{width:calc(100vw - 32px)}.g-inner{padding:16px}}\n/* resource center: unread badge + \"What's new\" */\n.g-launcher-badge{position:absolute;top:-4px;right:-4px;min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:#dc2626;color:#fff;font-size:11px;font-weight:700;line-height:20px;text-align:center;box-shadow:0 0 0 2px var(--g-bg)}\n.g-rc-news{padding:10px 20px 12px;border-bottom:1px solid var(--g-border)}\n.g-rc-news:last-of-type{border-bottom:0}\n.g-rc-news-head{display:flex;align-items:baseline;gap:8px}\n.g-rc-news-title{margin:0;font-size:14px;font-weight:600;line-height:1.35;flex:1}\n.g-rc-news-new{flex:none;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--g-primary-text);background:var(--g-primary);border-radius:999px;padding:2px 7px}\n.g-rc-news-date{display:block;font-size:12px;color:var(--g-muted);margin:2px 0 6px}\n.g-rc-news-body{font-size:13px}\n.g-rc-news-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}\n.g-rc-news-actions .g-btn{min-height:32px;padding:6px 12px;font-size:13px}\n/* accessibility */\n.g-sr-only{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}\n.g-hotspot:focus-visible,.g-custom:focus-visible,.g-card:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}\n@media (forced-colors:active){.g-card,.g-panel,.g-banner{border:1px solid CanvasText}.g-btn{border:1px solid ButtonText}}\n`;\n","import type { Appearance, ProjectTheme } from \"@guidelytech/shared\";\nimport { readableTextOn, scopeCustomCss } from \"@guidelytech/shared/runtime\";\nimport { STYLES } from \"./styles\";\n\nexport const HOST_ID = \"guidely-root\";\n\nexport interface HostHandle {\n host: HTMLElement;\n shadow: ShadowRoot;\n /** Polite live region for screen-reader announcements. */\n live: HTMLElement;\n announce(text: string): void;\n destroy(): void;\n}\n\nlet current: HostHandle | null = null;\n\n/** Default project theme used when the config has not loaded yet. */\nexport const DEFAULT_THEME: ProjectTheme = {\n primaryColor: \"#4f46e5\",\n backgroundColor: \"#ffffff\",\n textColor: \"#111827\",\n borderRadius: 12,\n fontFamily: \"Inter, system-ui, -apple-system, sans-serif\",\n};\n\n/** Returns (creating when needed) the single Shadow-DOM host appended to `document.body`. */\nexport function ensureHost(doc: Document = document): HostHandle {\n if (current && current.host.isConnected && current.host.ownerDocument === doc) return current;\n let host = doc.getElementById(HOST_ID) as HTMLElement | null;\n if (!host) {\n host = doc.createElement(\"div\");\n host.id = HOST_ID;\n host.setAttribute(\"data-guidely\", \"\");\n (doc.body || doc.documentElement).appendChild(host);\n }\n const shadow = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n if (!shadow.querySelector(\"style[data-g-base]\")) {\n const style = doc.createElement(\"style\");\n style.setAttribute(\"data-g-base\", \"\");\n style.textContent = STYLES;\n shadow.appendChild(style);\n }\n let live = shadow.querySelector<HTMLElement>(\".g-live\");\n if (!live) {\n live = doc.createElement(\"div\");\n live.className = \"g-live\";\n live.setAttribute(\"aria-live\", \"polite\");\n live.setAttribute(\"aria-atomic\", \"true\");\n shadow.appendChild(live);\n }\n const liveEl = live;\n current = {\n host,\n shadow,\n live: liveEl,\n announce(text: string) {\n liveEl.textContent = \"\";\n setTimeout(() => {\n liveEl.textContent = text;\n }, 30);\n },\n destroy() {\n host?.remove();\n if (current?.host === host) current = null;\n },\n };\n return current;\n}\n\n/** Removes the host from the document (all overlays with it). */\nexport function destroyHost(): void {\n current?.destroy();\n const stray = typeof document !== \"undefined\" ? document.getElementById(HOST_ID) : null;\n stray?.remove();\n current = null;\n}\n\nexport type ResolvedThemeMode = \"light\" | \"dark\";\n\nfunction prefersDark(): boolean {\n try {\n return typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" && window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n } catch {\n return false;\n }\n}\n\nexport function prefersReducedMotion(): boolean {\n try {\n return typeof window !== \"undefined\" && typeof window.matchMedia === \"function\" && window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n } catch {\n return false;\n }\n}\n\nexport interface ThemeVars {\n primary: string;\n bg: string;\n text: string;\n radius: number;\n font: string;\n z: number;\n overlay: number;\n mode: ResolvedThemeMode;\n animation: Appearance[\"animation\"];\n}\n\n/** Merges project theme + guide appearance into CSS custom property values. */\nexport function resolveTheme(theme: Partial<ProjectTheme> | null | undefined, appearance?: Partial<Appearance> | null): ThemeVars {\n const t = { ...DEFAULT_THEME, ...(theme || {}) };\n const a = appearance || {};\n const requested = a.theme ?? \"auto\";\n const mode: ResolvedThemeMode = requested === \"auto\" ? (prefersDark() ? \"dark\" : \"light\") : requested;\n const dark = mode === \"dark\";\n return {\n primary: a.primaryColor || t.primaryColor,\n bg: a.backgroundColor || (dark ? \"#1f2937\" : t.backgroundColor),\n text: a.textColor || (dark ? \"#f9fafb\" : t.textColor),\n radius: a.borderRadius ?? t.borderRadius,\n font: a.fontFamily || t.fontFamily,\n z: a.zIndex ?? 2147483000,\n overlay: a.overlayOpacity ?? 0.5,\n mode,\n animation: prefersReducedMotion() ? \"none\" : (a.animation ?? \"fade\"),\n };\n}\n\n/** Applies theme variables to a scope element. */\nexport function applyTheme(el: HTMLElement, vars: ThemeVars): void {\n el.style.setProperty(\"--g-primary\", vars.primary);\n // Button labels, banners and badges on the primary colour use whichever of white or dark text reads better (WCAG AA).\n if (/^#[0-9a-f]{3}([0-9a-f]{3})?$/i.test(vars.primary)) el.style.setProperty(\"--g-primary-text\", readableTextOn(vars.primary));\n el.style.setProperty(\"--g-bg\", vars.bg);\n el.style.setProperty(\"--g-text\", vars.text);\n el.style.setProperty(\"--g-radius\", `${vars.radius}px`);\n el.style.setProperty(\"--g-font\", vars.font);\n el.style.setProperty(\"--g-z\", String(vars.z));\n el.style.setProperty(\"--g-overlay\", String(vars.overlay));\n el.setAttribute(\"data-theme\", vars.mode);\n el.setAttribute(\"data-anim\", vars.animation);\n}\n\n/**\n * Creates a themed scope container inside the shadow root. Each guide/widget renders inside its\n * own scope so appearance settings never leak between guides.\n */\nexport function createScope(theme?: Partial<ProjectTheme> | null, appearance?: Partial<Appearance> | null, doc: Document = document): { el: HTMLElement; vars: ThemeVars; destroy(): void } {\n const handle = ensureHost(doc);\n const el = doc.createElement(\"div\");\n el.className = \"g-scope\";\n const vars = resolveTheme(theme, appearance);\n applyTheme(el, vars);\n handle.shadow.appendChild(el);\n // Custom CSS (theme first, then the guide's) is sanitized and prefixed with this scope only. The\n // theme's CSS styles guides (scopes created with an appearance), not the resource center or messenger.\n const css = [appearance ? theme?.customCss : undefined, appearance?.customCss].filter((c): c is string => !!c && !!c.trim());\n let style: HTMLStyleElement | null = null;\n if (css.length) {\n const id = `c${++cssScopeSeq}`;\n el.setAttribute(\"data-g-css\", id);\n style = doc.createElement(\"style\");\n style.setAttribute(\"data-g-custom\", id);\n style.textContent = css.map((c) => scopeCustomCss(c, `.g-scope[data-g-css=\"${id}\"]`)).join(\"\\n\");\n handle.shadow.insertBefore(style, el);\n }\n let mq: MediaQueryList | null = null;\n const onChange = () => applyTheme(el, resolveTheme(theme, appearance));\n if ((appearance?.theme ?? \"auto\") === \"auto\" && typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n try {\n mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n mq.addEventListener?.(\"change\", onChange);\n } catch {\n mq = null;\n }\n }\n return {\n el,\n vars,\n destroy() {\n mq?.removeEventListener?.(\"change\", onChange);\n style?.remove();\n el.remove();\n },\n };\n}\n\nlet cssScopeSeq = 0;\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __publicField
3
- } from "./chunk-KQGSHCNW.js";
3
+ } from "./chunk-V6TY7KAL.js";
4
4
 
5
5
  // src/selectors.ts
6
6
  import { matchUrlPattern } from "@guidelytech/shared/runtime";
@@ -460,4 +460,4 @@ export {
460
460
  componentAnchor,
461
461
  describeElement
462
462
  };
463
- //# sourceMappingURL=chunk-YU6SKJK4.js.map
463
+ //# sourceMappingURL=chunk-EPR4LWA2.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  prefersReducedMotion
3
- } from "./chunk-KQGSHCNW.js";
3
+ } from "./chunk-63M4VHQG.js";
4
4
 
5
5
  // src/sanitize.ts
6
6
  import { EMBED_IFRAME_ATTRS, VIDEO_ATTRS, isAllowedEmbedSrc, isSafeMediaUrl } from "@guidelytech/shared/runtime";
@@ -582,4 +582,4 @@ export {
582
582
  el,
583
583
  createHelpView
584
584
  };
585
- //# sourceMappingURL=chunk-KEQ2VEEC.js.map
585
+ //# sourceMappingURL=chunk-QFKMXMM4.js.map