help-layer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/dom-builder.js", "../src/floating.js", "../src/geometry.js", "../src/blocking-layer.js", "../src/config.js", "../src/overlap.js", "../src/markers.js", "../src/observer.js", "../src/matcher.js", "../src/popup.js", "../src/state.js", "../src/style.js", "../src/toggle.js", "../src/index.js"],
4
+ "sourcesContent": ["/**\n * Factory functions that create the DOM elements for markers, popups, and the blocking layer.\n * Event wiring and positioning are not done here (that is the caller's responsibility).\n *\n * Accessibility:\n * - Markers are <button> elements so they are focusable and can be activated with Enter/Space.\n * - The popup uses role=\"dialog\" + aria-labelledby (the title element) to describe itself to assistive tech.\n */\n\nconst POPUP_TITLE_ID = 'help-layer-popup-title';\n\nexport function createBlockingLayer() {\n const layer = document.createElement('div');\n layer.className = 'help-layer-blocking-layer';\n return layer;\n}\n\n/**\n * @param {string} title description title used for the assistive-tech label\n * @param {string} [label] character shown on the marker (default '?'). Visual only; does not affect the aria-label.\n */\nexport function createMarker(title, label = '?') {\n const marker = document.createElement('button');\n marker.type = 'button';\n marker.className = 'help-layer-marker';\n marker.textContent = label;\n marker.setAttribute('aria-label', `Help: ${title}`);\n return marker;\n}\n\n/**\n * Create the single popup shared across the whole library.\n * Also returns references to titleEl/textEl (used to update the content) and the close button closeEl.\n */\nexport function createPopup() {\n const root = document.createElement('div');\n root.className = 'help-layer-popup';\n root.setAttribute('role', 'dialog');\n root.setAttribute('aria-labelledby', POPUP_TITLE_ID);\n root.tabIndex = -1;\n\n const titleEl = document.createElement('div');\n titleEl.className = 'help-layer-popup__title';\n titleEl.id = POPUP_TITLE_ID;\n\n const textEl = document.createElement('div');\n textEl.className = 'help-layer-popup__text';\n\n // Explicit close affordance. Wiring the click is popup.js's job (only element creation here).\n const closeEl = document.createElement('button');\n closeEl.type = 'button';\n closeEl.className = 'help-layer-popup__close';\n closeEl.textContent = '\u00D7';\n closeEl.setAttribute('aria-label', 'Close');\n\n root.append(titleEl, textEl, closeEl);\n\n return { root, titleEl, textEl, closeEl };\n}\n", "/**\n * A thin wrapper around Floating UI (@floating-ui/dom).\n * Use of Floating UI is confined to this one file; other modules only call the\n * purpose-specific functions (anchorMarker / anchorPopup / watchReference /\n * makeVirtualElement). That way, if Floating UI is ever swapped out, the blast\n * radius stays limited to here.\n *\n * Floating UI in a nutshell (note for readers unfamiliar with the DOM):\n * - computePosition(reference, floating, options) computes the optimal placement\n * coordinates (x,y) for \"this exact moment\" and returns them (one-shot).\n * - autoUpdate(reference, floating, update) watches scroll, resize, and element-size\n * changes (internally using ResizeObserver, etc.) and calls update on every change.\n * Calling the returned cleanup stops watching. This is what makes the element \"stick\"\n * to its target.\n */\nimport { autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';\n\nimport { docRectToViewportRect } from './geometry.js';\n\n/**\n * Create a \"virtual reference element\" for free-placement items not bound to an element.\n * When getDocRect() returns document coordinates, this converts them to viewport\n * coordinates according to the current scroll. Because autoUpdate re-evaluates on every\n * scroll, the element sticks to the given coordinate while scrolling along with the page.\n * @param {() => {top:number,left:number,width?:number,height?:number}} getDocRect\n */\nexport function makeVirtualElement(getDocRect) {\n return {\n // Tell autoUpdate that this element's ancestor is body (= scroll is watched up to window).\n // Without this the virtual element isn't scroll-watched and won't follow page scroll.\n contextElement: document.body,\n getBoundingClientRect() {\n return docRectToViewportRect(getDocRect(), { x: window.scrollX, y: window.scrollY });\n },\n };\n}\n\nfunction place(el, x, y) {\n el.style.left = `${x}px`;\n el.style.top = `${y}px`;\n}\n\n// Half of the default marker size (22px). The amount used to overlap the marker onto the\n// target's corner with an \"inset\". (If the marker-size CSS variable is changed, the resulting\n// drift is left as existing behavior = not compensated for here.)\nconst MARKER_INSET = 11;\n\n/**\n * Derive the offset for overlapping the marker onto the target's corner from the placement.\n * mainAxis is always negative (bites inward past the target's edge). crossAxis flips sign by\n * alignment direction: `-end` (right/bottom-aligned) is negative to go inward, `-start`\n * (left/top-aligned) is positive to go inward.\n * @param {string} placement\n */\nfunction markerOffset(placement) {\n const isStart = placement.endsWith('-start');\n return { mainAxis: -MARKER_INSET, crossAxis: isStart ? MARKER_INSET : -MARKER_INSET };\n}\n\n/**\n * Overlap the marker onto a corner of the target (element or virtual element), stick it there, and keep it following.\n * @param {Element|object} reference\n * @param {HTMLElement} markerEl\n * @param {() => void} [onPlaced] called every time placement is finalized (used to trigger the overlap-avoidance pass, etc.)\n * @param {import('@floating-ui/dom').Placement} [placement] corner to overlap (top-end/top-start/bottom-end/bottom-start). Default 'top-end'\n * @returns {() => void} cleanup\n */\nexport function anchorMarker(reference, markerEl, onPlaced, placement = 'top-end') {\n const update = () => {\n computePosition(reference, markerEl, {\n placement,\n middleware: [offset(markerOffset(placement))],\n }).then(({ x, y }) => {\n place(markerEl, x, y);\n if (onPlaced) {\n onPlaced();\n }\n });\n };\n // animationFrame: true syncs repositioning to the rAF loop. With the default (scroll/resize\n // events only), computePosition resolves asynchronously, so left/top is written the frame after\n // the browser already painted the scroll \u2014 the marker lags a frame and visibly jitters.\n return autoUpdate(reference, markerEl, update, { animationFrame: true });\n}\n\n/**\n * Place the popup below the target, and at screen edges use flip (flip to the opposite side) /\n * shift (nudge) to avoid clipping. Only follows while visible.\n * @param {Element|object} reference\n * @param {HTMLElement} popupEl\n * @param {import('@floating-ui/dom').Placement} [placement] initial placement (Floating UI placement). Default 'bottom-start'\n * @returns {{ update: () => void, cleanup: () => void }}\n * calling update repositions immediately (used for reference-side transform moves that autoUpdate doesn't pick up, etc.).\n */\nexport function anchorPopup(reference, popupEl, placement = 'bottom-start') {\n const update = () => {\n computePosition(reference, popupEl, {\n placement,\n middleware: [offset(8), flip({ padding: 8 }), shift({ padding: 8 })],\n }).then(({ x, y }) => {\n place(popupEl, x, y);\n });\n };\n // animationFrame: true for the same smooth-tracking reason as anchorMarker. The reference here is\n // the marker element, which itself moves per frame, so the popup must track per frame to stay glued.\n const cleanup = autoUpdate(reference, popupEl, update, { animationFrame: true });\n return { update, cleanup };\n}\n\n/**\n * Watch a reference element's position/size changes and call onUpdate on every change.\n * (Used for non-placement purposes, e.g. keeping the blocking layer's clip-path hole following the toggle position.)\n * autoUpdate requires a floating element, so floatingEl is just passed as a dummy that\n * onUpdate doesn't actually position.\n * @param {Element} referenceEl\n * @param {HTMLElement} floatingEl\n * @param {() => void} onUpdate\n * @returns {() => void} cleanup\n */\nexport function watchReference(referenceEl, floatingEl, onUpdate) {\n return autoUpdate(referenceEl, floatingEl, onUpdate);\n}\n", "/**\n * Pure geometry calculations. Takes no DOM elements, only numbers already read off.\n *\n * Clamping things that overflow the viewport is handled by Floating UI's shift()\n * middleware. toDocumentPosition is used for the virtual-element math of free placement, etc.\n */\n\n/**\n * Given getBoundingClientRect() values (viewport-relative) and the scroll offset,\n * compute coordinates relative to the whole document.\n */\nexport function toDocumentPosition(rect, scroll) {\n return {\n top: rect.top + scroll.y,\n left: rect.left + scroll.x,\n };\n}\n\n/**\n * Convert a document-coordinate rect into a viewport-coordinate rect by subtracting\n * the current scroll offset. This is what the getBoundingClientRect of a Floating UI\n * virtual reference element (a free-placement marker) returns.\n * @param {{top:number,left:number,width?:number,height?:number}} docRect\n * @param {{x:number,y:number}} scroll\n */\nexport function docRectToViewportRect(docRect, scroll) {\n const width = docRect.width || 0;\n const height = docRect.height || 0;\n const left = docRect.left - scroll.x;\n const top = docRect.top - scroll.y;\n return {\n x: left,\n y: top,\n left,\n top,\n right: left + width,\n bottom: top + height,\n width,\n height,\n };\n}\n", "/**\n * Transparent interaction-blocking layer.\n *\n * Without touching the original app's event listeners at all, it keeps interactions from getting through. How it works:\n *\n * 1. Let the toggle show through via a clip-path \"hole\":\n * Set a clip-path polygon on the full-screen layer made of the outer rectangle + the toggle\n * rectangle (the hole). Inside the hole the layer isn't painted and hit-testing passes through,\n * so the toggle can be clicked natively without touching z-index at all. Unlike approaches that\n * shuffle z-index, this doesn't break depending on ancestor stacking contexts.\n * The hole is updated via autoUpdate to follow the toggle's scroll/resize.\n *\n * 2. Focus containment:\n * On ON, blur activeElement, and via focusin (capture) detect focus moving to anything other\n * than the library UI and pull it back to the toggle. Host listeners are not detached.\n *\n * 3. Key-input suppression:\n * Capture keydown/keyup in document's capture phase; for anything outside the library UI,\n * stopPropagation+preventDefault. Escape has dedicated handling (close popup / exit mode).\n * Inside the library UI (marker/popup/toggle), normal interactions like Tab are allowed.\n */\nimport { createBlockingLayer } from './dom-builder.js';\nimport { watchReference } from './floating.js';\n\nfunction buildClipPath(rect) {\n const x1 = rect.left;\n const y1 = rect.top;\n const x2 = rect.right;\n const y2 = rect.bottom;\n // A single stroke: outer ring (clockwise) -> bridge -> toggle rectangle (counter-clockwise).\n // Under the nonzero winding rule, making the inner ring wind opposite to the outer one punches a hole.\n return `polygon(\n 0px 0px, 100% 0px, 100% 100%, 0px 100%, 0px 0px,\n ${x1}px ${y1}px, ${x1}px ${y2}px, ${x2}px ${y2}px, ${x2}px ${y1}px, ${x1}px ${y1}px\n )`;\n}\n\nexport function activateBlockingLayer(state, {\n toggleEl,\n onBackgroundClick,\n isLibraryElement,\n onEscape,\n}) {\n const layer = createBlockingLayer();\n document.body.appendChild(layer);\n state.track(() => layer.remove());\n\n // --- 1. Keep the clip-path hole following the toggle position ---\n // If there's no toggle element (programmatic control only), keeping the whole surface blocked with no hole is correct.\n if (toggleEl) {\n const updateClip = () => {\n layer.style.clipPath = buildClipPath(toggleEl.getBoundingClientRect());\n };\n const cleanupClipWatch = watchReference(toggleEl, layer, updateClip);\n state.track(cleanupClipWatch);\n }\n\n // Clicking the background (the layer itself) closes the popup\n if (onBackgroundClick) {\n layer.addEventListener('click', onBackgroundClick);\n state.track(() => layer.removeEventListener('click', onBackgroundClick));\n }\n\n // --- 2. Focus containment ---\n // Turning ON happens via a click on the toggle, so don't blur if the active element is the toggle\n // itself (blurring would leave focus floating). For any other host element, make it let go.\n const activeEl = document.activeElement;\n if (\n activeEl instanceof HTMLElement &&\n activeEl !== document.body &&\n activeEl !== toggleEl\n ) {\n activeEl.blur();\n }\n\n const handleFocusIn = (event) => {\n if (isLibraryElement(event.target)) {\n return;\n }\n // If focus tries to move to a host element, take it back.\n // To the toggle if there is one; otherwise blur that element so it isn't handed to the host.\n event.stopPropagation();\n if (toggleEl) {\n toggleEl.focus({ preventScroll: true });\n } else if (event.target instanceof HTMLElement) {\n event.target.blur();\n }\n };\n document.addEventListener('focusin', handleFocusIn, true);\n state.track(() => document.removeEventListener('focusin', handleFocusIn, true));\n\n // --- 3. Key-input suppression + Escape ---\n // Shared logic that doesn't pass key input destined outside the library UI through to the host.\n const blockNonLibrary = (event) => {\n if (isLibraryElement(event.target)) {\n return;\n }\n event.stopPropagation();\n event.preventDefault();\n };\n // keyup / keypress: don't leak to the host, Escape included (Escape's real handling is on the keydown side).\n const blockKey = (event) => {\n if (event.key === 'Escape') {\n event.stopPropagation();\n event.preventDefault();\n return;\n }\n blockNonLibrary(event);\n };\n const handleKeydown = (event) => {\n if (event.key === 'Escape') {\n event.stopPropagation();\n event.preventDefault();\n if (onEscape) {\n onEscape();\n }\n return;\n }\n blockNonLibrary(event);\n };\n document.addEventListener('keydown', handleKeydown, true);\n document.addEventListener('keyup', blockKey, true);\n document.addEventListener('keypress', blockKey, true);\n state.track(() => {\n document.removeEventListener('keydown', handleKeydown, true);\n document.removeEventListener('keyup', blockKey, true);\n document.removeEventListener('keypress', blockKey, true);\n });\n\n return layer;\n}\n", "/**\n * Validation and normalization of the helpConfig object. A pure function that does no DOM work.\n */\n\n/**\n * @typedef {object} HelpEntry\n * @property {string} title heading shown on the marker / popup (non-empty)\n * @property {string} text description body (non-empty)\n * @property {{ top: number, left: number }} [position] if given, becomes a free placement not tied to an element\n */\n\n/**\n * The helpConfig itself. For element-bound entries the key is the `data-help-id` value;\n * for free placement it is any identifier.\n * @typedef {Object<string, HelpEntry>} HelpConfig\n */\n\nfunction isPlainObject(value) {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isValidPosition(position) {\n return (\n isPlainObject(position) &&\n typeof position.top === 'number' &&\n typeof position.left === 'number'\n );\n}\n\n/**\n * Validate the shape of helpConfig. Throws an Error on any problem (fail fast).\n */\nexport function validateConfig(config) {\n if (!isPlainObject(config)) {\n throw new Error('helpConfig must be a plain object');\n }\n\n for (const [key, entry] of Object.entries(config)) {\n if (!isPlainObject(entry)) {\n throw new Error(`helpConfig[\"${key}\"] must be an object`);\n }\n if (typeof entry.title !== 'string' || entry.title === '') {\n throw new Error(`helpConfig[\"${key}\"].title must be a non-empty string`);\n }\n if (typeof entry.text !== 'string' || entry.text === '') {\n throw new Error(`helpConfig[\"${key}\"].text must be a non-empty string`);\n }\n if (entry.position !== undefined && !isValidPosition(entry.position)) {\n throw new Error(`helpConfig[\"${key}\"].position must be { top: number, left: number }`);\n }\n }\n}\n\n/**\n * Convert a validated helpConfig into the shared array of \"help items\" the rendering\n * code works with. The target of kind:'element' is null at this point (DOM matching is\n * left to matcher.js).\n */\nexport function normalizeConfig(config) {\n return Object.entries(config).map(([key, entry]) => {\n if (isValidPosition(entry.position)) {\n return {\n key,\n title: entry.title,\n text: entry.text,\n kind: 'free',\n target: null,\n position: { top: entry.position.top, left: entry.position.left },\n };\n }\n\n return {\n key,\n title: entry.title,\n text: entry.text,\n kind: 'element',\n target: null,\n position: null,\n };\n });\n}\n", "/**\n * Overlap avoidance between markers (pure function).\n *\n * Takes an array of each marker's \"base position\" (the center coordinate Floating UI\n * decided on) and returns an array of extra offsets that push overlapping ones apart.\n * Touches no DOM.\n *\n * The algorithm is a simple iterative push-out (a lightweight force-based separation):\n * if two circles are closer than the minimum distance, push them apart in opposite\n * directions. Repeat a few times. Markers are small circles, so a circle-to-circle\n * distance test is enough.\n */\n\n/**\n * @param {Array<{x:number,y:number}>} centers base coordinate of each marker center\n * @param {object} [options]\n * @param {number} [options.minDistance] center-to-center distance closer than this counts as overlap\n * @param {number} [options.iterations] number of iterations\n * @returns {Array<{dx:number,dy:number}>} offset to add to each marker\n */\nexport function resolveOverlaps(centers, options = {}) {\n const minDistance = options.minDistance ?? 26;\n const iterations = options.iterations ?? 6;\n\n // Working positions (base + accumulated offset).\n const positions = centers.map((c) => ({ x: c.x, y: c.y }));\n\n for (let iter = 0; iter < iterations; iter++) {\n let moved = false;\n\n for (let i = 0; i < positions.length; i++) {\n for (let j = i + 1; j < positions.length; j++) {\n const a = positions[i];\n const b = positions[j];\n let dx = b.x - a.x;\n let dy = b.y - a.y;\n let dist = Math.hypot(dx, dy);\n\n if (dist >= minDistance) {\n continue;\n }\n\n // If the coordinates are exactly identical, separate in a deterministic direction (horizontal).\n if (dist === 0) {\n dx = 1;\n dy = 0;\n dist = 1;\n }\n\n const overlap = (minDistance - dist) / 2;\n const ux = dx / dist;\n const uy = dy / dist;\n\n a.x -= ux * overlap;\n a.y -= uy * overlap;\n b.x += ux * overlap;\n b.y += uy * overlap;\n moved = true;\n }\n }\n\n if (!moved) {\n break;\n }\n }\n\n return positions.map((p, i) => ({\n dx: p.x - centers[i].x,\n dy: p.y - centers[i].y,\n }));\n}\n", "/**\n * Marker manager.\n * Markers can be dynamically mounted/unmounted per help record (SPA dynamic-element support).\n * Each marker keeps following its target (an element, or the virtual element of a free placement)\n * via Floating UI's autoUpdate. On every finalized placement it triggers the overlap-avoidance pass,\n * debounced with rAF.\n *\n * Marker identifier (id):\n * - element-bound: the target element itself (distinguishes multiple elements with the same data-help-id)\n * - free placement: the config key string\n */\nimport { createMarker } from './dom-builder.js';\nimport { anchorMarker, makeVirtualElement } from './floating.js';\nimport { resolveOverlaps } from './overlap.js';\n\n// Temporary class added to the target element only while the marker is hovered/focused (matches the style.js definition).\nconst TARGET_HIGHLIGHT_CLASS = 'help-layer-target-highlight';\n\n/** @param {import('./matcher.js').HelpRecord} record */\nfunction referenceFor(record) {\n if (record.kind === 'free') {\n return makeVirtualElement(() => ({\n top: record.position.top,\n left: record.position.left,\n width: 0,\n height: 0,\n }));\n }\n return record.target;\n}\n\n/**\n * @param {object} state teardown registry\n * @param {object} options\n * @param {(record: import('./matcher.js').HelpRecord, markerEl: HTMLElement) => void} options.onMarkerClick\n * @param {() => void} [options.onOverlapResolved]\n * @param {string} [options.markerLabel] character shown on the marker (default '?')\n * @param {import('@floating-ui/dom').Placement} [options.markerPlacement] corner to overlap (default 'top-end')\n */\nexport function createMarkerManager(state, {\n onMarkerClick,\n onOverlapResolved,\n markerLabel = '?',\n markerPlacement = 'top-end',\n}) {\n /** @type {Map<Element|string, {record:import('./matcher.js').HelpRecord, el:HTMLElement, cleanup:() => void}>} */\n const markers = new Map();\n let rafId = null;\n // Don't schedule a new rAF during teardown (prevents a frame lingering after teardown).\n let tornDown = false;\n\n function runOverlapPass() {\n rafId = null;\n const entries = [...markers.values()];\n // With one marker or fewer, overlap is impossible. Skip getBoundingClientRect (forced reflow)\n // and the O(n^2) push-out math entirely (avoids a per-frame reflow while scrolling on screens\n // with few targets). However, right after dropping from 2 to 1, if the remaining one still has\n // a leftover push-out transform, clear it and let an open popup follow that move (in steady\n // state, with an empty transform, do nothing).\n if (entries.length <= 1) {\n const el = entries.length === 1 ? entries[0].el : null;\n if (el && el.style.transform) {\n el.style.transform = '';\n if (onOverlapResolved) {\n onOverlapResolved();\n }\n }\n return;\n }\n\n // Clear the accumulated transform to measure base centers, then reapply after resolving overlaps.\n entries.forEach((e) => { e.el.style.transform = ''; });\n const centers = entries.map((e) => {\n const r = e.el.getBoundingClientRect();\n return { x: r.left + r.width / 2, y: r.top + r.height / 2 };\n });\n const offsets = resolveOverlaps(centers);\n entries.forEach((e, i) => {\n const { dx, dy } = offsets[i];\n e.el.style.transform = (dx || dy) ? `translate(${dx}px, ${dy}px)` : '';\n });\n\n // Marker positions moved, so give an open popup etc. the chance to follow.\n if (onOverlapResolved) {\n onOverlapResolved();\n }\n }\n\n function scheduleOverlapPass() {\n if (rafId !== null || tornDown) {\n return;\n }\n rafId = requestAnimationFrame(runOverlapPass);\n }\n\n /** @param {import('./matcher.js').HelpRecord} record */\n function mount(record) {\n if (markers.has(record.id)) {\n return;\n }\n\n const el = createMarker(record.title, markerLabel);\n document.body.appendChild(el);\n\n const handleClick = () => onMarkerClick(record, el);\n el.addEventListener('click', handleClick);\n\n const cleanupAnchor = anchorMarker(referenceFor(record), el, scheduleOverlapPass, markerPlacement);\n\n // Target-element highlight (element-bound only; free placement has no target, so skip).\n // Show an outline on the target only while the marker is hovered/focused, to make clear \"which element this explains\".\n const target = record.kind === 'element' ? record.target : null;\n const addHighlight = () => target && target.classList.add(TARGET_HIGHLIGHT_CLASS);\n const removeHighlight = () => target && target.classList.remove(TARGET_HIGHLIGHT_CLASS);\n if (target) {\n el.addEventListener('mouseenter', addHighlight);\n el.addEventListener('mouseleave', removeHighlight);\n el.addEventListener('focus', addHighlight);\n el.addEventListener('blur', removeHighlight);\n }\n\n let done = false;\n const cleanup = () => {\n if (done) {\n return;\n }\n done = true;\n cleanupAnchor();\n el.removeEventListener('click', handleClick);\n if (target) {\n el.removeEventListener('mouseenter', addHighlight);\n el.removeEventListener('mouseleave', removeHighlight);\n el.removeEventListener('focus', addHighlight);\n el.removeEventListener('blur', removeHighlight);\n removeHighlight(); // don't leave the highlight on the target if unmounted while highlighted\n }\n el.remove();\n markers.delete(record.id);\n scheduleOverlapPass();\n };\n\n markers.set(record.id, { record, el, cleanup });\n }\n\n function unmount(id) {\n const entry = markers.get(id);\n if (entry) {\n entry.cleanup();\n }\n }\n\n function mountAll(records) {\n records.forEach(mount);\n }\n\n // Register a single teardown for the whole manager with state\n // (individual mount/unmount happen many times during a session, so they're bundled here).\n state.track(() => {\n tornDown = true;\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n [...markers.values()].forEach((entry) => entry.cleanup());\n });\n\n return {\n mount,\n unmount,\n mountAll,\n has(id) {\n return markers.has(id);\n },\n // Return the first entry matching the config key (either element-bound or free placement).\n // Used by the programmatic open(key).\n findByKey(key) {\n for (const entry of markers.values()) {\n if (entry.record.key === key) {\n return entry;\n }\n }\n return null;\n },\n };\n}\n", "/**\n * DOM observation and Shadow DOM-piercing traversal.\n *\n * A normal querySelectorAll does not cross shadow boundaries, so queryAllDeep walks open\n * shadowRoots recursively. A closed shadowRoot is unreachable from JS, so it is unsupported\n * (a known limitation).\n *\n * For SPA support, while ON a MutationObserver watches for additions/removals of data-help-id\n * elements and mounts/unmounts markers dynamically.\n */\n\nconst ELEMENT_NODE = 1;\n\n/**\n * Internal worker that traverses everything under root (including inside open shadowRoots) once,\n * collecting both selector-matching elements and shadowRoots at the same time. It uses a single\n * `querySelectorAll('*')` pass and does both the `matches` test and shadowRoot detection within it.\n * The goal is to cut what used to be two separate full scans (\"collect matches\" and \"find shadow\n * hosts\") down to one (lightening the hot path that reacts to host DOM changes while ON).\n * @param {ParentNode} root\n * @param {string} selector\n * @param {(el: Element) => void} [onMatch]\n * @param {(shadow: ShadowRoot) => void} [onShadowRoot]\n */\nfunction walkDeep(root, selector, onMatch, onShadowRoot) {\n if (typeof root.querySelectorAll !== 'function') {\n return;\n }\n // querySelectorAll('*') does not cross shadow boundaries, so run it flatly once per root and,\n // when a shadowRoot is found, recurse into it right there (depth-first).\n root.querySelectorAll('*').forEach((el) => {\n if (onMatch && el.matches(selector)) {\n onMatch(el);\n }\n if (el.shadowRoot) {\n if (onShadowRoot) {\n onShadowRoot(el.shadowRoot);\n }\n walkDeep(el.shadowRoot, selector, onMatch, onShadowRoot);\n }\n });\n}\n\n/**\n * Collect every element under root (including inside open shadowRoots) matching selector.\n * @param {ParentNode} root\n * @param {string} selector\n * @returns {Element[]}\n */\nexport function queryAllDeep(root, selector) {\n const results = [];\n walkDeep(root, selector, (el) => results.push(el));\n return results;\n}\n\n/**\n * Collect every open shadowRoot under root (excluding root itself).\n * @param {ParentNode} root\n * @returns {ShadowRoot[]}\n */\nexport function collectShadowRoots(root) {\n const roots = [];\n walkDeep(root, '*', null, (shadow) => roots.push(shadow));\n return roots;\n}\n\n/**\n * Traverse a node and its descendants (including shadow) once, returning both selector-matching\n * elements and the descendants' open shadowRoots together. Used in MutationObserver added-node\n * handling to fold \"collect matches\" and \"add shadow observation\" into one traversal per subtree.\n * @param {Node} node\n * @param {string} selector\n * @returns {{ matches: Element[], shadowRoots: ShadowRoot[] }}\n */\nfunction scanSubtree(node, selector) {\n /** @type {Element[]} */\n const matches = [];\n /** @type {ShadowRoot[]} */\n const shadowRoots = [];\n if (node.nodeType !== ELEMENT_NODE) {\n return { matches, shadowRoots };\n }\n // node itself is not included in querySelectorAll('*'), so test it separately (equivalent to the former matchingWithin).\n const el = /** @type {Element} */ (node);\n if (typeof el.matches === 'function' && el.matches(selector)) {\n matches.push(el);\n }\n // The added node itself may be a shadow host. walkDeep only inspects the shadowRoots of\n // descendants (querySelectorAll('*') never includes the root), so handle the node's own\n // shadowRoot here before descending into the light-DOM subtree.\n if (el.shadowRoot) {\n shadowRoots.push(el.shadowRoot);\n walkDeep(el.shadowRoot, selector, (m) => matches.push(m), (shadow) => shadowRoots.push(shadow));\n }\n walkDeep(el, selector, (m) => matches.push(m), (shadow) => shadowRoots.push(shadow));\n return { matches, shadowRoots };\n}\n\n/**\n * While ON, watch root and all shadowRoots under it, notifying on entry/exit of selector-matching elements.\n * If an added element has a new shadowRoot, that shadowRoot is also added to the observation.\n *\n * @param {object} params\n * @param {ParentNode} [params.root=document]\n * @param {string} params.selector\n * @param {(el: Element) => void} params.onAdded\n * @param {(el: Element) => void} params.onRemoved\n * @returns {{ disconnect(): void }}\n */\nexport function createMutationWatcher({ root = document, selector, onAdded, onRemoved }) {\n const observed = new Set();\n\n const handle = (records) => {\n for (const record of records) {\n // For added nodes, get both \"matching elements\" and \"shadowRoots to start observing\" in one traversal per subtree.\n record.addedNodes.forEach((node) => {\n const { matches, shadowRoots } = scanSubtree(node, selector);\n matches.forEach((el) => onAdded(el));\n shadowRoots.forEach(observe);\n });\n record.removedNodes.forEach((node) => {\n scanSubtree(node, selector).matches.forEach((el) => onRemoved(el));\n });\n }\n };\n\n const observer = new MutationObserver(handle);\n\n function observe(target) {\n if (observed.has(target)) {\n return;\n }\n observed.add(target);\n observer.observe(target, { childList: true, subtree: true });\n }\n\n observe(root);\n collectShadowRoots(root).forEach(observe);\n\n return {\n disconnect() {\n observer.disconnect();\n observed.clear();\n },\n };\n}\n", "/**\n * Map the help-item array returned by normalizeConfig() onto actual DOM elements or free\n * placements, producing the \"help records\" the marker manager works with. Reads the DOM only;\n * never writes.\n *\n * Behavior of this module:\n * - Searches with queryAllDeep for Shadow DOM support.\n * - Emits a marker for each of multiple elements sharing the same data-help-id (correct in practice).\n * Because of that, an element-bound record uses \"the element itself\" as its id (identity).\n * - A free-placement record uses the config key as its id.\n *\n * Resolution order for title/text:\n * - First look up config by the `data-help-id` value (config always wins).\n * - If config has no match, use the element's `data-help-title` / `data-help-text` as an inline definition.\n * This lets you adopt the library with markup alone, without a config object.\n */\nimport { queryAllDeep } from './observer.js';\n\n/**\n * One marker's worth of \"help record\". Produced by matcher; consumed by markers/popup/toggle/index \u2014 the shared contract.\n * Other modules reference it via `import('./matcher.js').HelpRecord` (the same style as config.js's HelpConfig).\n * @typedef {object} HelpRecord\n * @property {Element|string} id for element-bound, the target element itself; for free placement, the config key string\n * @property {'element'|'free'} kind\n * @property {string|null} key config key (null for an inline-definition-only element)\n * @property {string} title\n * @property {string} text\n * @property {Element} [target] the target element when kind:'element'\n * @property {{top:number,left:number}} [position] the placement coordinate when kind:'free'\n */\n\n// Attribute names used for inline definitions (fixed defaults so as not to grow the API).\nexport const TITLE_ATTR = 'data-help-title';\nexport const TEXT_ATTR = 'data-help-text';\n\n/**\n * Build the selector to scan. In addition to elements with `data-help-id` (default), also pick up\n * elements that only have an inline definition (`data-help-title`).\n * A single source of truth so collectElementRecords and the MutationObserver share the same condition.\n * @param {string} [attribute] attribute name marking targets (default 'data-help-id')\n */\nexport function targetSelector(attribute = 'data-help-id') {\n return `[${attribute}], [${TITLE_ATTR}]`;\n}\n\n/** Turn element-bound items into a key->item Map. */\nexport function elementConfigMap(items) {\n const map = new Map();\n for (const item of items) {\n if (item.kind === 'element') {\n map.set(item.key, item);\n }\n }\n return map;\n}\n\n/**\n * Turn free-placement items into records.\n * @returns {HelpRecord[]}\n */\nexport function freeRecords(items) {\n return items\n .filter((item) => item.kind === 'free')\n .map((item) => ({\n id: item.key,\n kind: 'free',\n key: item.key,\n title: item.title,\n text: item.text,\n position: item.position,\n }));\n}\n\n/**\n * Build the help record for a single element. title/text prefer config; if absent, fall back to\n * the element's data-help-title / data-help-text (inline definition). If neither source yields\n * both title and text, return null (not a target).\n * (Used by both the initial scan and SPA dynamic additions.)\n * @param {string} [attribute] attribute name marking targets (default 'data-help-id')\n * @returns {HelpRecord|null}\n */\nexport function recordForElement(el, configMap, attribute = 'data-help-id') {\n const key = el.getAttribute(attribute);\n // config wins. If there's no matching key, fall back to the inline attributes.\n const item = key != null ? configMap.get(key) : undefined;\n const title = item ? item.title : el.getAttribute(TITLE_ATTR);\n const text = item ? item.text : el.getAttribute(TEXT_ATTR);\n // If both title and text aren't present, it's not a target (treated as unregistered).\n if (!title || !text) {\n return null;\n }\n return {\n id: el,\n kind: 'element',\n key,\n title,\n text,\n target: el,\n };\n}\n\n/**\n * Scan target-attribute elements under root (including Shadow DOM) and collect element-bound records.\n * Targets not in config are warned about and ignored (non-fatal). silent:true suppresses the warning.\n * @param {object[]} items\n * @param {ParentNode} [root]\n * @param {object} [options]\n * @param {boolean} [options.silent] don't warn on unregistered keys\n * @param {string} [options.attribute] attribute name marking targets (default 'data-help-id')\n * @returns {HelpRecord[]}\n */\nexport function collectElementRecords(items, root = document, { silent = false, attribute = 'data-help-id' } = {}) {\n const configMap = elementConfigMap(items);\n const records = [];\n\n queryAllDeep(root, targetSelector(attribute)).forEach((el) => {\n const record = recordForElement(el, configMap, attribute);\n if (!record) {\n if (!silent) {\n const key = el.getAttribute(attribute);\n console.warn(\n key != null\n ? `[help-layer] element with ${attribute}=\"${key}\" has no matching helpConfig entry or inline ${TITLE_ATTR}/${TEXT_ATTR}`\n : `[help-layer] element needs both ${TITLE_ATTR} and ${TEXT_ATTR} (or a ${attribute} matching helpConfig)`,\n );\n }\n return;\n }\n records.push(record);\n });\n\n return records;\n}\n", "/**\n * The single popup shared across the whole library.\n * Placed on its target (the clicked marker) with Floating UI; at screen edges, flip/shift avoid\n * clipping. While visible it follows via autoUpdate.\n *\n * Accessibility:\n * - On open, move focus to the popup (role=dialog).\n * - On close, return focus to the trigger element (the marker).\n */\nimport { createPopup } from './dom-builder.js';\nimport { anchorPopup } from './floating.js';\n\n/**\n * @param {object} state teardown registry\n * @param {object} [options]\n * @param {() => void} [options.onClose] called when the popup closes (transitions from shown to hidden)\n * @param {(record: import('./matcher.js').HelpRecord) => (Node|null|undefined)} [options.render]\n * Escape hatch to render the body area with your own DOM node. Return a Node to display it;\n * if nothing is returned, fall back to safe text rendering (textContent). The title is always record.title.\n * Note: the return value is appendChild'd as-is without sanitization, so untrusted data must be neutralized by the caller.\n * @param {import('@floating-ui/dom').Placement} [options.popupPlacement] initial placement (default 'bottom-start')\n */\nexport function createPopupController(state, { onClose, render, popupPlacement = 'bottom-start' } = {}) {\n const { root, titleEl, textEl, closeEl } = createPopup();\n document.body.appendChild(root);\n\n // The close (\u00D7) button. root is removed on teardown, so explicitly detaching the listener isn't needed.\n closeEl.addEventListener('click', () => close());\n\n let openId = null;\n let triggerEl = null;\n let anchor = null;\n\n function stopAnchor() {\n if (anchor) {\n anchor.cleanup();\n anchor = null;\n }\n }\n\n /**\n * @param {import('./matcher.js').HelpRecord} record\n * @param {HTMLElement} referenceEl placement reference (the clicked marker element)\n */\n function open(record, referenceEl) {\n titleEl.textContent = record.title;\n // If render exists, replace the body with a custom Node; otherwise fall back to safe text rendering.\n const custom = render ? render(record) : null;\n textEl.textContent = '';\n if (custom) {\n textEl.appendChild(custom);\n } else {\n textEl.textContent = record.text;\n }\n root.style.display = 'block';\n openId = record.id;\n triggerEl = referenceEl;\n\n stopAnchor();\n anchor = anchorPopup(referenceEl, root, popupPlacement);\n\n // preventScroll: the popup is positioned asynchronously (computePosition().then), so at this\n // point it's still at its stale position; a default focus would scroll toward that, causing a\n // visible jump. flip/shift keep it in the viewport, so suppressing the scroll is safe.\n root.focus({ preventScroll: true });\n }\n\n // Reposition immediately, only when open.\n // (Called e.g. right after a marker shifts due to the overlap-avoidance transform.)\n function reposition() {\n if (anchor) {\n anchor.update();\n }\n }\n\n function hide() {\n // Call onClose only if it was open (catches both the close-path and teardown-path close routes at one point).\n const wasOpen = openId !== null;\n stopAnchor();\n openId = null;\n triggerEl = null;\n root.style.display = 'none';\n if (wasOpen && onClose) {\n onClose();\n }\n }\n\n /**\n * Close and return focus.\n * @param {HTMLElement} [focusTarget] explicit focus-return target.\n * If omitted, returns to the trigger element (the marker). In contexts where the trigger\n * disappears (SPA removal), pass another surviving element such as the toggle.\n */\n function close(focusTarget) {\n const returnTo = focusTarget ?? triggerEl;\n hide();\n // Return focus if the target is still in the DOM.\n if (returnTo && returnTo.isConnected && typeof returnTo.focus === 'function') {\n returnTo.focus({ preventScroll: true });\n }\n }\n\n state.track(() => {\n hide();\n root.remove();\n });\n\n return {\n root,\n isOpen(id) {\n return openId === id;\n },\n getOpenId() {\n return openId;\n },\n open,\n close,\n reposition,\n };\n}\n", "/**\n * Registry of teardown callbacks.\n * DOM, listeners, and style changes added while the mode is ON are unwound in\n * reverse order of creation (LIFO), so that dependent cleanups (e.g. detach an\n * internal listener, then remove its element) run in a natural order.\n */\nexport function createState() {\n const cleanupFns = [];\n\n return {\n track(fn) {\n cleanupFns.push(fn);\n },\n teardownAll() {\n while (cleanupFns.length > 0) {\n const cleanup = cleanupFns.pop();\n cleanup();\n }\n },\n };\n}\n", "/**\n * The z-index constants help-layer uses, and the CSS it injects.\n * Things that must sit above the blocking layer use Z_TOP (markers); the popup uses Z_POPUP so it\n * always paints in front of the markers (they share a stacking context as <body> children, so a tie\n * would otherwise be decided by DOM order and a remounted marker could cover an open popup).\n * The toggle is made visible through the clip-path \"hole\", so its z-index is left untouched.\n */\nexport const Z_BLOCKING_LAYER = 2147483000;\nexport const Z_TOP = 2147483001;\nexport const Z_POPUP = 2147483002;\n\nconst STYLE_ATTR = 'data-help-layer-style';\n\n// The theme is fully exposed via CSS custom properties. Users can change the look just by\n// overriding the following variables in host-side CSS (e.g. :root or any scope):\n// --help-layer-marker-size marker diameter (default 22px)\n// --help-layer-marker-bg marker background color (default #2563eb)\n// --help-layer-marker-color marker text color (default #fff)\n// --help-layer-popup-bg popup background color (default #fff)\n// --help-layer-popup-color popup text color (default #1f2933)\n// --help-layer-popup-max-width popup max width (default 280px)\n// --help-layer-popup-max-height body max height (default 50vh, scrolls when exceeded)\n// --help-layer-accent focus ring color (default #1d4ed8)\n// --help-layer-overlay-bg blocking-layer (scrim) background (default transparent; e.g. rgba(0,0,0,0.15))\n// --help-layer-overlay-cursor cursor over the blocked area (default default; e.g. not-allowed / help)\nconst CSS = `\n.help-layer-blocking-layer {\n position: fixed;\n inset: 0;\n /* Default transparent (unchanged). Set --help-layer-overlay-bg to tint it into a scrim that signals\n \"the host app is inactive\". The clip-path hole isn't painted, so the toggle stays untinted. */\n background: var(--help-layer-overlay-bg, transparent);\n /* Cursor over the blocked area only (the toggle shows through the hole and keeps its own cursor).\n e.g. not-allowed / help makes \"this won't respond\" obvious without needing a tint. */\n cursor: var(--help-layer-overlay-cursor, default);\n z-index: ${Z_BLOCKING_LAYER};\n}\n\n.help-layer-marker {\n /* reset of the button element */\n appearance: none;\n -webkit-appearance: none;\n margin: 0;\n padding: 0;\n border: none;\n position: absolute;\n top: 0;\n left: 0;\n width: var(--help-layer-marker-size, 22px);\n height: var(--help-layer-marker-size, 22px);\n border-radius: 50%;\n background: var(--help-layer-marker-bg, #2563eb);\n color: var(--help-layer-marker-color, #fff);\n font-family: sans-serif;\n font-size: 13px;\n font-weight: bold;\n line-height: var(--help-layer-marker-size, 22px);\n text-align: center;\n cursor: pointer;\n user-select: none;\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);\n z-index: ${Z_TOP};\n}\n\n.help-layer-marker:focus-visible {\n outline: 3px solid var(--help-layer-accent, #1d4ed8);\n outline-offset: 2px;\n}\n\n.help-layer-popup {\n position: absolute;\n top: 0;\n left: 0;\n display: none;\n max-width: var(--help-layer-popup-max-width, 280px);\n background: var(--help-layer-popup-bg, #fff);\n color: var(--help-layer-popup-color, #1f2933);\n border-radius: 6px;\n padding: 12px 14px;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);\n font-family: sans-serif;\n font-size: 13px;\n line-height: 1.5;\n z-index: ${Z_POPUP};\n}\n\n.help-layer-popup:focus {\n outline: none;\n}\n\n.help-layer-popup:focus-visible {\n outline: 3px solid var(--help-layer-accent, #1d4ed8);\n outline-offset: 2px;\n}\n\n.help-layer-popup__title {\n font-weight: bold;\n margin-bottom: 4px;\n /* Reserve space so it doesn't overlap the \u00D7 button at the top-right. */\n padding-right: 16px;\n}\n\n.help-layer-popup__text {\n /* Render the body's \\n as line breaks (still textContent, so no XSS risk). */\n white-space: pre-line;\n /* Keep long text from spilling off-screen; only the body scrolls within the popup. */\n max-height: var(--help-layer-popup-max-height, 50vh);\n overflow-y: auto;\n}\n\n.help-layer-popup__close {\n /* reset of the button element */\n appearance: none;\n -webkit-appearance: none;\n position: absolute;\n top: 6px;\n right: 6px;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: inherit;\n font-size: 16px;\n line-height: 1;\n cursor: pointer;\n}\n\n.help-layer-popup__close:hover {\n background: rgba(0, 0, 0, 0.08);\n}\n\n.help-layer-popup__close:focus-visible {\n outline: 2px solid var(--help-layer-accent, #1d4ed8);\n outline-offset: 1px;\n}\n\n/*\n * Show an outline on the target element only while the marker is hovered/focused (clarifies \"which element this explains\").\n * Make only the outline !important so it can beat host-side outline resets.\n */\n.help-layer-target-highlight {\n outline: 2px solid var(--help-layer-accent, #1d4ed8) !important;\n outline-offset: 2px !important;\n}\n\n/*\n * Dark-mode defaults. If the user specifies CSS variables, those always win via var(), so here we\n * only swap the dark fallback values (the properties themselves aren't re-declared).\n */\n@media (prefers-color-scheme: dark) {\n .help-layer-popup {\n background: var(--help-layer-popup-bg, #1f2933);\n color: var(--help-layer-popup-color, #e5e7eb);\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.55);\n }\n}\n`;\n\n/**\n * Inject a <style> tag into head and return that element.\n * @param {string} [nonce] nonce to allow this <style> under a strict CSP (style-src 'nonce-\u2026').\n * The nonce attribute is added only when provided. If omitted, nothing is added (as before).\n */\nexport function injectStyles(nonce) {\n const styleEl = document.createElement('style');\n styleEl.setAttribute(STYLE_ATTR, '');\n // Under a CSP running style-src 'nonce-\u2026', only a <style> with a matching nonce is applied.\n if (nonce) {\n styleEl.setAttribute('nonce', nonce);\n }\n styleEl.textContent = CSS;\n document.head.appendChild(styleEl);\n return styleEl;\n}\n\n/**\n * Remove the <style> tag injected by injectStyles().\n */\nexport function removeStyles(styleEl) {\n styleEl.remove();\n}\n", "/**\n * Orchestration of the help mode's ON/OFF.\n * Starts each subsystem (style injection, marker manager, popup, blocking layer, DOM observation)\n * and aggregates their teardown into the cleanup registry (state).\n */\nimport { activateBlockingLayer } from './blocking-layer.js';\nimport { normalizeConfig, validateConfig } from './config.js';\nimport { createMarkerManager } from './markers.js';\nimport {\n collectElementRecords,\n elementConfigMap,\n freeRecords,\n recordForElement,\n targetSelector,\n} from './matcher.js';\nimport { createMutationWatcher } from './observer.js';\nimport { createPopupController } from './popup.js';\nimport { createState } from './state.js';\nimport { injectStyles, removeStyles } from './style.js';\n\nfunction resolveToggleElement(toggle) {\n const toggleEl = typeof toggle === 'string' ? document.querySelector(toggle) : toggle;\n if (!toggleEl) {\n throw new Error(`help-layer: toggle element not found for selector \"${toggle}\"`);\n }\n return toggleEl;\n}\n\n/**\n * @param {object} params\n * @param {object} params.config helpConfig\n * @param {string|HTMLElement} [params.toggle] DOM element that switches ON/OFF (if omitted, programmatic control only)\n * @param {() => void} [params.onEnable] called right after the mode is turned ON\n * @param {() => void} [params.onDisable] called right after the mode is turned OFF\n * @param {(record: import('./matcher.js').HelpRecord) => void} [params.onOpen] called when a popup is opened\n * @param {() => void} [params.onClose] called when a popup is closed\n * @param {boolean} [params.silent] suppress the warning log for unregistered keys\n * @param {string} [params.attribute] attribute name marking targets (default 'data-help-id')\n * @param {(record: import('./matcher.js').HelpRecord) => (Node|null|undefined)} [params.render] render the popup body with your own Node\n * (the return value is inserted as-is without sanitization, so untrusted data must be neutralized by the caller)\n * @param {string} [params.markerLabel] character shown on the marker (default '?')\n * @param {import('@floating-ui/dom').Placement} [params.markerPlacement] corner to overlap the marker onto (default 'top-end')\n * @param {import('@floating-ui/dom').Placement} [params.popupPlacement] initial popup placement (default 'bottom-start')\n * @param {string} [params.nonce] nonce to allow the injected <style> under a strict CSP (style-src 'nonce-\u2026')\n */\nexport function createToggleController({\n config,\n toggle,\n onEnable,\n onDisable,\n onOpen,\n onClose,\n silent = false,\n attribute = 'data-help-id',\n render,\n markerLabel = '?',\n markerPlacement = 'top-end',\n popupPlacement = 'bottom-start',\n nonce,\n}) {\n let activeConfig = config;\n validateConfig(activeConfig);\n // The toggle element is optional. If omitted, it's driven solely by programmatic control like enable()/disable().\n const toggleEl = toggle != null ? resolveToggleElement(toggle) : null;\n\n let state = null;\n // References to the current subsystems that exist only while ON. Hoisted because open(key)/close() touch them too.\n let popup = null;\n let markers = null;\n\n // Only builds the side effects (onEnable/onDisable aren't called here; they fire on the enable/disable side).\n function turnOn() {\n if (state) {\n return;\n }\n state = createState();\n\n // On OFF, return focus to the toggle last (at the LIFO tail) (only when there is a toggle).\n if (toggleEl) {\n state.track(() => {\n if (toggleEl.isConnected && typeof toggleEl.focus === 'function') {\n toggleEl.focus({ preventScroll: true });\n }\n });\n }\n\n const styleEl = injectStyles(nonce);\n state.track(() => removeStyles(styleEl));\n\n const items = normalizeConfig(activeConfig);\n const configMap = elementConfigMap(items);\n\n popup = createPopupController(state, { onClose, render, popupPlacement });\n markers = createMarkerManager(state, {\n markerLabel,\n markerPlacement,\n onMarkerClick: (record, markerEl) => {\n if (popup.isOpen(record.id)) {\n popup.close();\n return;\n }\n popup.open(record, markerEl);\n if (onOpen) {\n onOpen(record);\n }\n },\n // When overlap avoidance moves a marker, make the open popup follow.\n onOverlapResolved: () => popup.reposition(),\n });\n\n // Initial mount (free placements + elements currently in the DOM, including Shadow DOM)\n markers.mountAll(freeRecords(items));\n markers.mountAll(collectElementRecords(items, document, { silent, attribute }));\n\n // SPA dynamic elements: follow additions/removals while ON\n const watcher = createMutationWatcher({\n selector: targetSelector(attribute),\n onAdded: (el) => {\n const record = recordForElement(el, configMap, attribute);\n if (record && !markers.has(record.id)) {\n markers.mount(record);\n }\n },\n onRemoved: (el) => {\n // Both the target and its marker disappear, so return focus to the toggle (or the default if absent).\n if (popup.isOpen(el)) {\n popup.close(toggleEl ?? undefined);\n }\n markers.unmount(el);\n },\n });\n state.track(() => watcher.disconnect());\n\n const isLibraryElement = (target) =>\n !!target &&\n ((toggleEl ? toggleEl.contains(target) : false) ||\n popup.root.contains(target) ||\n (typeof target.closest === 'function' && !!target.closest('.help-layer-marker')));\n\n activateBlockingLayer(state, {\n toggleEl,\n onBackgroundClick: () => popup.close(),\n isLibraryElement,\n onEscape: () => {\n if (popup.getOpenId() !== null) {\n popup.close();\n } else {\n disable();\n }\n },\n });\n }\n\n function turnOff() {\n if (state) {\n state.teardownAll();\n state = null;\n popup = null;\n markers = null;\n }\n }\n\n function enable() {\n if (state) {\n return;\n }\n turnOn();\n if (onEnable) {\n onEnable();\n }\n }\n\n function disable() {\n if (!state) {\n return;\n }\n turnOff();\n if (onDisable) {\n onDisable();\n }\n }\n\n function toggleMode() {\n if (state) {\n disable();\n } else {\n enable();\n }\n }\n\n // Open the description for a given key programmatically. When OFF, first enable() to create the markers.\n function openByKey(key) {\n if (!state) {\n enable();\n }\n if (!markers || !popup) {\n return;\n }\n const entry = markers.findByKey(key);\n if (!entry) {\n if (!silent) {\n console.warn(`[help-layer] open(): no help marker for key \"${key}\"`);\n }\n return;\n }\n popup.open(entry.record, entry.el);\n if (onOpen) {\n onOpen(entry.record);\n }\n }\n\n // Close the open description (does not turn the mode itself OFF).\n function closePopup() {\n if (popup) {\n popup.close();\n }\n }\n\n // Replace the helpConfig. If ON, rebuild silently (onEnable/onDisable are not fired).\n function update(newConfig) {\n validateConfig(newConfig);\n activeConfig = newConfig;\n if (state) {\n turnOff();\n turnOn();\n }\n }\n\n if (toggleEl) {\n toggleEl.addEventListener('click', toggleMode);\n }\n\n return {\n enable,\n disable,\n toggle: toggleMode,\n isActive() {\n return state !== null;\n },\n open: openByKey,\n close: closePopup,\n update,\n destroy() {\n disable();\n if (toggleEl) {\n toggleEl.removeEventListener('click', toggleMode);\n }\n },\n };\n}\n", "import { createToggleController } from './toggle.js';\n\n/**\n * Initialize the help mode.\n *\n * It can be toggled ON/OFF by clicking the toggle element, and also controlled programmatically\n * via the returned API. If `toggle` is omitted, there's no DOM toggle and it's programmatic-only.\n *\n * @param {object} options\n * @param {import('./config.js').HelpConfig} options.config - configuration that specifies targets by data-help-id or position.\n * Elements not in config can still be targets via the data-help-title / data-help-text inline definition (config wins)\n * @param {string|HTMLElement} [options.toggle] - toggle element that switches ON/OFF (CSS selector string or element). Optional\n * @param {() => void} [options.onEnable] - called right after the mode is turned ON\n * @param {() => void} [options.onDisable] - called right after the mode is turned OFF\n * @param {(record: import('./matcher.js').HelpRecord) => void} [options.onOpen] - called when a description popup is opened\n * @param {() => void} [options.onClose] - called when a description popup is closed\n * @param {boolean} [options.silent] - suppress the warning log for unregistered keys\n * @param {string} [options.attribute] - attribute name marking targets (default 'data-help-id')\n * @param {(record: import('./matcher.js').HelpRecord) => (Node|null|undefined)} [options.render] - render the popup body with your own DOM.\n * Return a Node to display it; if nothing is returned, fall back to safe text display (the title is always record.title).\n * \u26A0\uFE0F The return value is inserted as-is without sanitization. If it contains untrusted data, neutralize it on the caller side (XSS prevention)\n * @param {string} [options.markerLabel] - character shown on the marker (default '?')\n * @param {import('@floating-ui/dom').Placement} [options.markerPlacement] - corner to overlap the marker onto (default 'top-end')\n * @param {import('@floating-ui/dom').Placement} [options.popupPlacement] - initial popup placement (default 'bottom-start')\n * @param {string} [options.nonce] - nonce to allow the injected <style> under a strict CSP (style-src 'nonce-\u2026')\n * @returns {{\n * enable(): void,\n * disable(): void,\n * toggle(): void,\n * isActive(): boolean,\n * open(key: string): void,\n * close(): void,\n * update(config: import('./config.js').HelpConfig): void,\n * destroy(): void,\n * }} a handle to control the mode and fully clean up at the end.\n * open(key) opens the description for the given key (auto-enables when OFF). close() closes the open description.\n */\nexport function initHelpLayer(options) {\n return createToggleController(options);\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["POPUP_TITLE_ID", "createBlockingLayer", "layer", "createMarker", "title", "label", "marker", "createPopup", "root", "titleEl", "textEl", "closeEl", "autoUpdate", "computePosition", "flip", "offset", "shift", "docRectToViewportRect", "docRect", "scroll", "width", "height", "left", "top", "makeVirtualElement", "getDocRect", "docRectToViewportRect", "place", "el", "x", "y", "MARKER_INSET", "markerOffset", "placement", "isStart", "anchorMarker", "reference", "markerEl", "onPlaced", "autoUpdate", "computePosition", "offset", "anchorPopup", "popupEl", "update", "flip", "shift", "cleanup", "watchReference", "referenceEl", "floatingEl", "onUpdate", "buildClipPath", "rect", "x1", "y1", "x2", "y2", "activateBlockingLayer", "state", "toggleEl", "onBackgroundClick", "isLibraryElement", "onEscape", "layer", "createBlockingLayer", "cleanupClipWatch", "watchReference", "activeEl", "handleFocusIn", "event", "blockNonLibrary", "blockKey", "handleKeydown", "isPlainObject", "value", "isValidPosition", "position", "validateConfig", "config", "key", "entry", "normalizeConfig", "resolveOverlaps", "centers", "options", "minDistance", "iterations", "positions", "c", "iter", "moved", "i", "j", "a", "b", "dx", "dy", "dist", "overlap", "ux", "uy", "p", "TARGET_HIGHLIGHT_CLASS", "referenceFor", "record", "makeVirtualElement", "createMarkerManager", "state", "onMarkerClick", "onOverlapResolved", "markerLabel", "markerPlacement", "markers", "rafId", "tornDown", "runOverlapPass", "entries", "el", "e", "centers", "r", "offsets", "resolveOverlaps", "i", "dx", "dy", "scheduleOverlapPass", "mount", "createMarker", "handleClick", "cleanupAnchor", "anchorMarker", "target", "addHighlight", "removeHighlight", "done", "cleanup", "unmount", "id", "entry", "mountAll", "records", "key", "walkDeep", "root", "selector", "onMatch", "onShadowRoot", "el", "queryAllDeep", "results", "collectShadowRoots", "roots", "shadow", "scanSubtree", "node", "matches", "shadowRoots", "m", "createMutationWatcher", "onAdded", "onRemoved", "observed", "handle", "records", "record", "observe", "observer", "target", "TITLE_ATTR", "TEXT_ATTR", "targetSelector", "attribute", "elementConfigMap", "items", "map", "item", "freeRecords", "recordForElement", "el", "configMap", "key", "title", "text", "collectElementRecords", "root", "silent", "records", "queryAllDeep", "record", "createPopupController", "state", "onClose", "render", "popupPlacement", "root", "titleEl", "textEl", "closeEl", "createPopup", "close", "openId", "triggerEl", "anchor", "stopAnchor", "open", "record", "referenceEl", "custom", "anchorPopup", "reposition", "hide", "wasOpen", "focusTarget", "returnTo", "id", "createState", "cleanupFns", "fn", "STYLE_ATTR", "CSS", "injectStyles", "nonce", "styleEl", "removeStyles", "resolveToggleElement", "toggle", "toggleEl", "createToggleController", "config", "onEnable", "onDisable", "onOpen", "onClose", "silent", "attribute", "render", "markerLabel", "markerPlacement", "popupPlacement", "nonce", "activeConfig", "validateConfig", "state", "popup", "markers", "turnOn", "createState", "styleEl", "injectStyles", "removeStyles", "items", "normalizeConfig", "configMap", "elementConfigMap", "createPopupController", "createMarkerManager", "record", "markerEl", "freeRecords", "collectElementRecords", "watcher", "createMutationWatcher", "targetSelector", "el", "recordForElement", "activateBlockingLayer", "target", "disable", "turnOff", "enable", "toggleMode", "openByKey", "key", "entry", "closePopup", "update", "newConfig", "initHelpLayer", "options", "createToggleController"]
7
+ }
@@ -0,0 +1,139 @@
1
+ var HelpLayer=(()=>{var xt=Object.defineProperty;var _e=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var Fe=Object.prototype.hasOwnProperty;var Be=(t,e)=>{for(var n in e)xt(t,n,{get:e[n],enumerable:!0})},We=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ne(e))!Fe.call(t,i)&&i!==n&&xt(t,i,{get:()=>e[i],enumerable:!(o=_e(e,i))||o.enumerable});return t};var He=t=>We(xt({},"__esModule",{value:!0}),t);var bn={};Be(bn,{initHelpLayer:()=>vn});var $t="help-layer-popup-title";function _t(){let t=document.createElement("div");return t.className="help-layer-blocking-layer",t}function Nt(t,e="?"){let n=document.createElement("button");return n.type="button",n.className="help-layer-marker",n.textContent=e,n.setAttribute("aria-label",`Help: ${t}`),n}function Ft(){let t=document.createElement("div");t.className="help-layer-popup",t.setAttribute("role","dialog"),t.setAttribute("aria-labelledby",$t),t.tabIndex=-1;let e=document.createElement("div");e.className="help-layer-popup__title",e.id=$t;let n=document.createElement("div");n.className="help-layer-popup__text";let o=document.createElement("button");return o.type="button",o.className="help-layer-popup__close",o.textContent="\xD7",o.setAttribute("aria-label","Close"),t.append(e,n,o),{root:t,titleEl:e,textEl:n,closeEl:o}}var q=Math.min,$=Math.max,tt=Math.round,et=Math.floor,T=t=>({x:t,y:t}),ze={left:"right",right:"left",bottom:"top",top:"bottom"};function yt(t,e,n){return $(t,q(e,n))}function nt(t,e){return typeof t=="function"?t(e):t}function H(t){return t.split("-")[0]}function ot(t){return t.split("-")[1]}function wt(t){return t==="x"?"y":"x"}function vt(t){return t==="y"?"height":"width"}function _(t){let e=t[0];return e==="t"||e==="b"?"y":"x"}function bt(t){return wt(_(t))}function Ht(t,e,n){n===void 0&&(n=!1);let o=ot(t),i=bt(t),r=vt(i),s=i==="x"?o===(n?"end":"start")?"right":"left":o==="start"?"bottom":"top";return e.reference[r]>e.floating[r]&&(s=Q(s)),[s,Q(s)]}function zt(t){let e=Q(t);return[at(t),e,at(e)]}function at(t){return t.includes("start")?t.replace("start","end"):t.replace("end","start")}var Bt=["left","right"],Wt=["right","left"],Ie=["top","bottom"],Ve=["bottom","top"];function je(t,e,n){switch(t){case"top":case"bottom":return n?e?Wt:Bt:e?Bt:Wt;case"left":case"right":return e?Ie:Ve;default:return[]}}function It(t,e,n,o){let i=ot(t),r=je(H(t),n==="start",o);return i&&(r=r.map(s=>s+"-"+i),e&&(r=r.concat(r.map(at)))),r}function Q(t){let e=H(t);return ze[e]+t.slice(e.length)}function Xe(t){return{top:0,right:0,bottom:0,left:0,...t}}function Vt(t){return typeof t!="number"?Xe(t):{top:t,right:t,bottom:t,left:t}}function I(t){let{x:e,y:n,width:o,height:i}=t;return{width:o,height:i,top:n,left:e,right:e+o,bottom:n+i,x:e,y:n}}function jt(t,e,n){let{reference:o,floating:i}=t,r=_(e),s=bt(e),l=vt(s),a=H(e),p=r==="y",d=o.x+o.width/2-i.width/2,f=o.y+o.height/2-i.height/2,x=o[l]/2-i[l]/2,c;switch(a){case"top":c={x:d,y:o.y-i.height};break;case"bottom":c={x:d,y:o.y+o.height};break;case"right":c={x:o.x+o.width,y:f};break;case"left":c={x:o.x-i.width,y:f};break;default:c={x:o.x,y:o.y}}switch(ot(e)){case"start":c[s]-=x*(n&&p?-1:1);break;case"end":c[s]+=x*(n&&p?-1:1);break}return c}async function Xt(t,e){var n;e===void 0&&(e={});let{x:o,y:i,platform:r,rects:s,elements:l,strategy:a}=t,{boundary:p="clippingAncestors",rootBoundary:d="viewport",elementContext:f="floating",altBoundary:x=!1,padding:c=0}=nt(e,t),u=Vt(c),m=l[x?f==="floating"?"reference":"floating":f],h=I(await r.getClippingRect({element:(n=await(r.isElement==null?void 0:r.isElement(m)))==null||n?m:m.contextElement||await(r.getDocumentElement==null?void 0:r.getDocumentElement(l.floating)),boundary:p,rootBoundary:d,strategy:a})),y=f==="floating"?{x:o,y:i,width:s.floating.width,height:s.floating.height}:s.reference,w=await(r.getOffsetParent==null?void 0:r.getOffsetParent(l.floating)),v=await(r.isElement==null?void 0:r.isElement(w))?await(r.getScale==null?void 0:r.getScale(w))||{x:1,y:1}:{x:1,y:1},b=I(r.convertOffsetParentRelativeRectToViewportRelativeRect?await r.convertOffsetParentRelativeRectToViewportRelativeRect({elements:l,rect:y,offsetParent:w,strategy:a}):y);return{top:(h.top-b.top+u.top)/v.y,bottom:(b.bottom-h.bottom+u.bottom)/v.y,left:(h.left-b.left+u.left)/v.x,right:(b.right-h.right+u.right)/v.x}}var Ke=50,Kt=async(t,e,n)=>{let{placement:o="bottom",strategy:i="absolute",middleware:r=[],platform:s}=n,l=s.detectOverflow?s:{...s,detectOverflow:Xt},a=await(s.isRTL==null?void 0:s.isRTL(e)),p=await s.getElementRects({reference:t,floating:e,strategy:i}),{x:d,y:f}=jt(p,o,a),x=o,c=0,u={};for(let g=0;g<r.length;g++){let m=r[g];if(!m)continue;let{name:h,fn:y}=m,{x:w,y:v,data:b,reset:E}=await y({x:d,y:f,initialPlacement:o,placement:x,strategy:i,middlewareData:u,rects:p,platform:l,elements:{reference:t,floating:e}});d=w??d,f=v??f,u[h]={...u[h],...b},E&&c<Ke&&(c++,typeof E=="object"&&(E.placement&&(x=E.placement),E.rects&&(p=E.rects===!0?await s.getElementRects({reference:t,floating:e,strategy:i}):E.rects),{x:d,y:f}=jt(p,x,a)),g=-1)}return{x:d,y:f,placement:x,strategy:i,middlewareData:u}};var Yt=function(t){return t===void 0&&(t={}),{name:"flip",options:t,async fn(e){var n,o;let{placement:i,middlewareData:r,rects:s,initialPlacement:l,platform:a,elements:p}=e,{mainAxis:d=!0,crossAxis:f=!0,fallbackPlacements:x,fallbackStrategy:c="bestFit",fallbackAxisSideDirection:u="none",flipAlignment:g=!0,...m}=nt(t,e);if((n=r.arrow)!=null&&n.alignmentOffset)return{};let h=H(i),y=_(l),w=H(l)===l,v=await(a.isRTL==null?void 0:a.isRTL(p.floating)),b=x||(w||!g?[Q(l)]:zt(l)),E=u!=="none";!x&&E&&b.push(...It(l,g,u,v));let F=[l,...b],J=await a.detectOverflow(e,m),Y=[],R=((o=r.flip)==null?void 0:o.overflows)||[];if(d&&Y.push(J[h]),f){let B=Ht(i,s,v);Y.push(J[B[0]],J[B[1]])}if(R=[...R,{placement:i,overflows:Y}],!Y.every(B=>B<=0)){var L,lt;let B=(((L=r.flip)==null?void 0:L.index)||0)+1,A=F[B];if(A&&(!(f==="alignment"?y!==_(A):!1)||R.every(P=>_(P.placement)===y?P.overflows[0]>0:!0)))return{data:{index:B,overflows:R},reset:{placement:A}};let S=(lt=R.filter(z=>z.overflows[0]<=0).sort((z,P)=>z.overflows[1]-P.overflows[1])[0])==null?void 0:lt.placement;if(!S)switch(c){case"bestFit":{var ct;let z=(ct=R.filter(P=>{if(E){let W=_(P.placement);return W===y||W==="y"}return!0}).map(P=>[P.placement,P.overflows.filter(W=>W>0).reduce((W,$e)=>W+$e,0)]).sort((P,W)=>P[1]-W[1])[0])==null?void 0:ct[0];z&&(S=z);break}case"initialPlacement":S=l;break}if(i!==S)return{reset:{placement:S}}}return{}}}};var Ye=new Set(["left","top"]);async function qe(t,e){let{placement:n,platform:o,elements:i}=t,r=await(o.isRTL==null?void 0:o.isRTL(i.floating)),s=H(n),l=ot(n),a=_(n)==="y",p=Ye.has(s)?-1:1,d=r&&a?-1:1,f=nt(e,t),{mainAxis:x,crossAxis:c,alignmentAxis:u}=typeof f=="number"?{mainAxis:f,crossAxis:0,alignmentAxis:null}:{mainAxis:f.mainAxis||0,crossAxis:f.crossAxis||0,alignmentAxis:f.alignmentAxis};return l&&typeof u=="number"&&(c=l==="end"?u*-1:u),a?{x:c*d,y:x*p}:{x:x*p,y:c*d}}var qt=function(t){return t===void 0&&(t=0),{name:"offset",options:t,async fn(e){var n,o;let{x:i,y:r,placement:s,middlewareData:l}=e,a=await qe(e,t);return s===((n=l.offset)==null?void 0:n.placement)&&(o=l.arrow)!=null&&o.alignmentOffset?{}:{x:i+a.x,y:r+a.y,data:{...a,placement:s}}}}},Ut=function(t){return t===void 0&&(t={}),{name:"shift",options:t,async fn(e){let{x:n,y:o,placement:i,platform:r}=e,{mainAxis:s=!0,crossAxis:l=!1,limiter:a={fn:h=>{let{x:y,y:w}=h;return{x:y,y:w}}},...p}=nt(t,e),d={x:n,y:o},f=await r.detectOverflow(e,p),x=_(H(i)),c=wt(x),u=d[c],g=d[x];if(s){let h=c==="y"?"top":"left",y=c==="y"?"bottom":"right",w=u+f[h],v=u-f[y];u=yt(w,u,v)}if(l){let h=x==="y"?"top":"left",y=x==="y"?"bottom":"right",w=g+f[h],v=g-f[y];g=yt(w,g,v)}let m=a.fn({...e,[c]:u,[x]:g});return{...m,data:{x:m.x-n,y:m.y-o,enabled:{[c]:s,[x]:l}}}}}};function ft(){return typeof window<"u"}function j(t){return Gt(t)?(t.nodeName||"").toLowerCase():"#document"}function k(t){var e;return(t==null||(e=t.ownerDocument)==null?void 0:e.defaultView)||window}function D(t){var e;return(e=(Gt(t)?t.ownerDocument:t.document)||window.document)==null?void 0:e.documentElement}function Gt(t){return ft()?t instanceof Node||t instanceof k(t).Node:!1}function C(t){return ft()?t instanceof Element||t instanceof k(t).Element:!1}function M(t){return ft()?t instanceof HTMLElement||t instanceof k(t).HTMLElement:!1}function Zt(t){return!ft()||typeof ShadowRoot>"u"?!1:t instanceof ShadowRoot||t instanceof k(t).ShadowRoot}function Z(t){let{overflow:e,overflowX:n,overflowY:o,display:i}=O(t);return/auto|scroll|overlay|hidden|clip/.test(e+o+n)&&i!=="inline"&&i!=="contents"}function Jt(t){return/^(table|td|th)$/.test(j(t))}function it(t){try{if(t.matches(":popover-open"))return!0}catch{}try{return t.matches(":modal")}catch{return!1}}var Ue=/transform|translate|scale|rotate|perspective|filter/,Ze=/paint|layout|strict|content/,V=t=>!!t&&t!=="none",At;function ut(t){let e=C(t)?O(t):t;return V(e.transform)||V(e.translate)||V(e.scale)||V(e.rotate)||V(e.perspective)||!pt()&&(V(e.backdropFilter)||V(e.filter))||Ue.test(e.willChange||"")||Ze.test(e.contain||"")}function Qt(t){let e=N(t);for(;M(e)&&!X(e);){if(ut(e))return e;if(it(e))return null;e=N(e)}return null}function pt(){return At==null&&(At=typeof CSS<"u"&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),At}function X(t){return/^(html|body|#document)$/.test(j(t))}function O(t){return k(t).getComputedStyle(t)}function rt(t){return C(t)?{scrollLeft:t.scrollLeft,scrollTop:t.scrollTop}:{scrollLeft:t.scrollX,scrollTop:t.scrollY}}function N(t){if(j(t)==="html")return t;let e=t.assignedSlot||t.parentNode||Zt(t)&&t.host||D(t);return Zt(e)?e.host:e}function te(t){let e=N(t);return X(e)?t.ownerDocument?t.ownerDocument.body:t.body:M(e)&&Z(e)?e:te(e)}function U(t,e,n){var o;e===void 0&&(e=[]),n===void 0&&(n=!0);let i=te(t),r=i===((o=t.ownerDocument)==null?void 0:o.body),s=k(i);if(r){let l=dt(s);return e.concat(s,s.visualViewport||[],Z(i)?i:[],l&&n?U(l):[])}else return e.concat(i,U(i,[],n))}function dt(t){return t.parent&&Object.getPrototypeOf(t.parent)?t.frameElement:null}function ie(t){let e=O(t),n=parseFloat(e.width)||0,o=parseFloat(e.height)||0,i=M(t),r=i?t.offsetWidth:n,s=i?t.offsetHeight:o,l=tt(n)!==r||tt(o)!==s;return l&&(n=r,o=s),{width:n,height:o,$:l}}function Rt(t){return C(t)?t:t.contextElement}function G(t){let e=Rt(t);if(!M(e))return T(1);let n=e.getBoundingClientRect(),{width:o,height:i,$:r}=ie(e),s=(r?tt(n.width):n.width)/o,l=(r?tt(n.height):n.height)/i;return(!s||!Number.isFinite(s))&&(s=1),(!l||!Number.isFinite(l))&&(l=1),{x:s,y:l}}var Ge=T(0);function re(t){let e=k(t);return!pt()||!e.visualViewport?Ge:{x:e.visualViewport.offsetLeft,y:e.visualViewport.offsetTop}}function Je(t,e,n){return e===void 0&&(e=!1),!n||e&&n!==k(t)?!1:e}function K(t,e,n,o){e===void 0&&(e=!1),n===void 0&&(n=!1);let i=t.getBoundingClientRect(),r=Rt(t),s=T(1);e&&(o?C(o)&&(s=G(o)):s=G(t));let l=Je(r,n,o)?re(r):T(0),a=(i.left+l.x)/s.x,p=(i.top+l.y)/s.y,d=i.width/s.x,f=i.height/s.y;if(r){let x=k(r),c=o&&C(o)?k(o):o,u=x,g=dt(u);for(;g&&o&&c!==u;){let m=G(g),h=g.getBoundingClientRect(),y=O(g),w=h.left+(g.clientLeft+parseFloat(y.paddingLeft))*m.x,v=h.top+(g.clientTop+parseFloat(y.paddingTop))*m.y;a*=m.x,p*=m.y,d*=m.x,f*=m.y,a+=w,p+=v,u=k(g),g=dt(u)}}return I({width:d,height:f,x:a,y:p})}function mt(t,e){let n=rt(t).scrollLeft;return e?e.left+n:K(D(t)).left+n}function se(t,e){let n=t.getBoundingClientRect(),o=n.left+e.scrollLeft-mt(t,n),i=n.top+e.scrollTop;return{x:o,y:i}}function Qe(t){let{elements:e,rect:n,offsetParent:o,strategy:i}=t,r=i==="fixed",s=D(o),l=e?it(e.floating):!1;if(o===s||l&&r)return n;let a={scrollLeft:0,scrollTop:0},p=T(1),d=T(0),f=M(o);if((f||!f&&!r)&&((j(o)!=="body"||Z(s))&&(a=rt(o)),f)){let c=K(o);p=G(o),d.x=c.x+o.clientLeft,d.y=c.y+o.clientTop}let x=s&&!f&&!r?se(s,a):T(0);return{width:n.width*p.x,height:n.height*p.y,x:n.x*p.x-a.scrollLeft*p.x+d.x+x.x,y:n.y*p.y-a.scrollTop*p.y+d.y+x.y}}function tn(t){return Array.from(t.getClientRects())}function en(t){let e=D(t),n=rt(t),o=t.ownerDocument.body,i=$(e.scrollWidth,e.clientWidth,o.scrollWidth,o.clientWidth),r=$(e.scrollHeight,e.clientHeight,o.scrollHeight,o.clientHeight),s=-n.scrollLeft+mt(t),l=-n.scrollTop;return O(o).direction==="rtl"&&(s+=$(e.clientWidth,o.clientWidth)-i),{width:i,height:r,x:s,y:l}}var ee=25;function nn(t,e){let n=k(t),o=D(t),i=n.visualViewport,r=o.clientWidth,s=o.clientHeight,l=0,a=0;if(i){r=i.width,s=i.height;let d=pt();(!d||d&&e==="fixed")&&(l=i.offsetLeft,a=i.offsetTop)}let p=mt(o);if(p<=0){let d=o.ownerDocument,f=d.body,x=getComputedStyle(f),c=d.compatMode==="CSS1Compat"&&parseFloat(x.marginLeft)+parseFloat(x.marginRight)||0,u=Math.abs(o.clientWidth-f.clientWidth-c);u<=ee&&(r-=u)}else p<=ee&&(r+=p);return{width:r,height:s,x:l,y:a}}function on(t,e){let n=K(t,!0,e==="fixed"),o=n.top+t.clientTop,i=n.left+t.clientLeft,r=M(t)?G(t):T(1),s=t.clientWidth*r.x,l=t.clientHeight*r.y,a=i*r.x,p=o*r.y;return{width:s,height:l,x:a,y:p}}function ne(t,e,n){let o;if(e==="viewport")o=nn(t,n);else if(e==="document")o=en(D(t));else if(C(e))o=on(e,n);else{let i=re(t);o={x:e.x-i.x,y:e.y-i.y,width:e.width,height:e.height}}return I(o)}function le(t,e){let n=N(t);return n===e||!C(n)||X(n)?!1:O(n).position==="fixed"||le(n,e)}function rn(t,e){let n=e.get(t);if(n)return n;let o=U(t,[],!1).filter(l=>C(l)&&j(l)!=="body"),i=null,r=O(t).position==="fixed",s=r?N(t):t;for(;C(s)&&!X(s);){let l=O(s),a=ut(s);!a&&l.position==="fixed"&&(i=null),(r?!a&&!i:!a&&l.position==="static"&&!!i&&(i.position==="absolute"||i.position==="fixed")||Z(s)&&!a&&le(t,s))?o=o.filter(d=>d!==s):i=l,s=N(s)}return e.set(t,o),o}function sn(t){let{element:e,boundary:n,rootBoundary:o,strategy:i}=t,s=[...n==="clippingAncestors"?it(e)?[]:rn(e,this._c):[].concat(n),o],l=ne(e,s[0],i),a=l.top,p=l.right,d=l.bottom,f=l.left;for(let x=1;x<s.length;x++){let c=ne(e,s[x],i);a=$(c.top,a),p=q(c.right,p),d=q(c.bottom,d),f=$(c.left,f)}return{width:p-f,height:d-a,x:f,y:a}}function ln(t){let{width:e,height:n}=ie(t);return{width:e,height:n}}function cn(t,e,n){let o=M(e),i=D(e),r=n==="fixed",s=K(t,!0,r,e),l={scrollLeft:0,scrollTop:0},a=T(0);function p(){a.x=mt(i)}if(o||!o&&!r)if((j(e)!=="body"||Z(i))&&(l=rt(e)),o){let c=K(e,!0,r,e);a.x=c.x+e.clientLeft,a.y=c.y+e.clientTop}else i&&p();r&&!o&&i&&p();let d=i&&!o&&!r?se(i,l):T(0),f=s.left+l.scrollLeft-a.x-d.x,x=s.top+l.scrollTop-a.y-d.y;return{x:f,y:x,width:s.width,height:s.height}}function Et(t){return O(t).position==="static"}function oe(t,e){if(!M(t)||O(t).position==="fixed")return null;if(e)return e(t);let n=t.offsetParent;return D(t)===n&&(n=n.ownerDocument.body),n}function ce(t,e){let n=k(t);if(it(t))return n;if(!M(t)){let i=N(t);for(;i&&!X(i);){if(C(i)&&!Et(i))return i;i=N(i)}return n}let o=oe(t,e);for(;o&&Jt(o)&&Et(o);)o=oe(o,e);return o&&X(o)&&Et(o)&&!ut(o)?n:o||Qt(t)||n}var an=async function(t){let e=this.getOffsetParent||ce,n=this.getDimensions,o=await n(t.floating);return{reference:cn(t.reference,await e(t.floating),t.strategy),floating:{x:0,y:0,width:o.width,height:o.height}}};function fn(t){return O(t).direction==="rtl"}var un={convertOffsetParentRelativeRectToViewportRelativeRect:Qe,getDocumentElement:D,getClippingRect:sn,getOffsetParent:ce,getElementRects:an,getClientRects:tn,getDimensions:ln,getScale:G,isElement:C,isRTL:fn};function ae(t,e){return t.x===e.x&&t.y===e.y&&t.width===e.width&&t.height===e.height}function pn(t,e){let n=null,o,i=D(t);function r(){var l;clearTimeout(o),(l=n)==null||l.disconnect(),n=null}function s(l,a){l===void 0&&(l=!1),a===void 0&&(a=1),r();let p=t.getBoundingClientRect(),{left:d,top:f,width:x,height:c}=p;if(l||e(),!x||!c)return;let u=et(f),g=et(i.clientWidth-(d+x)),m=et(i.clientHeight-(f+c)),h=et(d),w={rootMargin:-u+"px "+-g+"px "+-m+"px "+-h+"px",threshold:$(0,q(1,a))||1},v=!0;function b(E){let F=E[0].intersectionRatio;if(F!==a){if(!v)return s();F?s(!1,F):o=setTimeout(()=>{s(!1,1e-7)},1e3)}F===1&&!ae(p,t.getBoundingClientRect())&&s(),v=!1}try{n=new IntersectionObserver(b,{...w,root:i.ownerDocument})}catch{n=new IntersectionObserver(b,w)}n.observe(t)}return s(!0),r}function ht(t,e,n,o){o===void 0&&(o={});let{ancestorScroll:i=!0,ancestorResize:r=!0,elementResize:s=typeof ResizeObserver=="function",layoutShift:l=typeof IntersectionObserver=="function",animationFrame:a=!1}=o,p=Rt(t),d=i||r?[...p?U(p):[],...e?U(e):[]]:[];d.forEach(h=>{i&&h.addEventListener("scroll",n,{passive:!0}),r&&h.addEventListener("resize",n)});let f=p&&l?pn(p,n):null,x=-1,c=null;s&&(c=new ResizeObserver(h=>{let[y]=h;y&&y.target===p&&c&&e&&(c.unobserve(e),cancelAnimationFrame(x),x=requestAnimationFrame(()=>{var w;(w=c)==null||w.observe(e)})),n()}),p&&!a&&c.observe(p),e&&c.observe(e));let u,g=a?K(t):null;a&&m();function m(){let h=K(t);g&&!ae(g,h)&&n(),g=h,u=requestAnimationFrame(m)}return n(),()=>{var h;d.forEach(y=>{i&&y.removeEventListener("scroll",n),r&&y.removeEventListener("resize",n)}),f?.(),(h=c)==null||h.disconnect(),c=null,a&&cancelAnimationFrame(u)}}var kt=qt;var fe=Ut,ue=Yt;var Ct=(t,e,n)=>{let o=new Map,i={platform:un,...n},r={...i.platform,_c:o};return Kt(t,e,{...i,platform:r})};function pe(t,e){let n=t.width||0,o=t.height||0,i=t.left-e.x,r=t.top-e.y;return{x:i,y:r,left:i,top:r,right:i+n,bottom:r+o,width:n,height:o}}function de(t){return{contextElement:document.body,getBoundingClientRect(){return pe(t(),{x:window.scrollX,y:window.scrollY})}}}function me(t,e,n){t.style.left=`${e}px`,t.style.top=`${n}px`}var Ot=11;function dn(t){let e=t.endsWith("-start");return{mainAxis:-Ot,crossAxis:e?Ot:-Ot}}function he(t,e,n,o="top-end"){return ht(t,e,()=>{Ct(t,e,{placement:o,middleware:[kt(dn(o))]}).then(({x:r,y:s})=>{me(e,r,s),n&&n()})},{animationFrame:!0})}function ge(t,e,n="bottom-start"){let o=()=>{Ct(t,e,{placement:n,middleware:[kt(8),ue({padding:8}),fe({padding:8})]}).then(({x:r,y:s})=>{me(e,r,s)})},i=ht(t,e,o,{animationFrame:!0});return{update:o,cleanup:i}}function xe(t,e,n){return ht(t,e,n)}function mn(t){let e=t.left,n=t.top,o=t.right,i=t.bottom;return`polygon(
2
+ 0px 0px, 100% 0px, 100% 100%, 0px 100%, 0px 0px,
3
+ ${e}px ${n}px, ${e}px ${i}px, ${o}px ${i}px, ${o}px ${n}px, ${e}px ${n}px
4
+ )`}function ye(t,{toggleEl:e,onBackgroundClick:n,isLibraryElement:o,onEscape:i}){let r=_t();if(document.body.appendChild(r),t.track(()=>r.remove()),e){let x=xe(e,r,()=>{r.style.clipPath=mn(e.getBoundingClientRect())});t.track(x)}n&&(r.addEventListener("click",n),t.track(()=>r.removeEventListener("click",n)));let s=document.activeElement;s instanceof HTMLElement&&s!==document.body&&s!==e&&s.blur();let l=f=>{o(f.target)||(f.stopPropagation(),e?e.focus({preventScroll:!0}):f.target instanceof HTMLElement&&f.target.blur())};document.addEventListener("focusin",l,!0),t.track(()=>document.removeEventListener("focusin",l,!0));let a=f=>{o(f.target)||(f.stopPropagation(),f.preventDefault())},p=f=>{if(f.key==="Escape"){f.stopPropagation(),f.preventDefault();return}a(f)},d=f=>{if(f.key==="Escape"){f.stopPropagation(),f.preventDefault(),i&&i();return}a(f)};return document.addEventListener("keydown",d,!0),document.addEventListener("keyup",p,!0),document.addEventListener("keypress",p,!0),t.track(()=>{document.removeEventListener("keydown",d,!0),document.removeEventListener("keyup",p,!0),document.removeEventListener("keypress",p,!0)}),r}function Lt(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function we(t){return Lt(t)&&typeof t.top=="number"&&typeof t.left=="number"}function St(t){if(!Lt(t))throw new Error("helpConfig must be a plain object");for(let[e,n]of Object.entries(t)){if(!Lt(n))throw new Error(`helpConfig["${e}"] must be an object`);if(typeof n.title!="string"||n.title==="")throw new Error(`helpConfig["${e}"].title must be a non-empty string`);if(typeof n.text!="string"||n.text==="")throw new Error(`helpConfig["${e}"].text must be a non-empty string`);if(n.position!==void 0&&!we(n.position))throw new Error(`helpConfig["${e}"].position must be { top: number, left: number }`)}}function ve(t){return Object.entries(t).map(([e,n])=>we(n.position)?{key:e,title:n.title,text:n.text,kind:"free",target:null,position:{top:n.position.top,left:n.position.left}}:{key:e,title:n.title,text:n.text,kind:"element",target:null,position:null})}function be(t,e={}){let n=e.minDistance??26,o=e.iterations??6,i=t.map(r=>({x:r.x,y:r.y}));for(let r=0;r<o;r++){let s=!1;for(let l=0;l<i.length;l++)for(let a=l+1;a<i.length;a++){let p=i[l],d=i[a],f=d.x-p.x,x=d.y-p.y,c=Math.hypot(f,x);if(c>=n)continue;c===0&&(f=1,x=0,c=1);let u=(n-c)/2,g=f/c,m=x/c;p.x-=g*u,p.y-=m*u,d.x+=g*u,d.y+=m*u,s=!0}if(!s)break}return i.map((r,s)=>({dx:r.x-t[s].x,dy:r.y-t[s].y}))}var Ae="help-layer-target-highlight";function hn(t){return t.kind==="free"?de(()=>({top:t.position.top,left:t.position.left,width:0,height:0})):t.target}function Ee(t,{onMarkerClick:e,onOverlapResolved:n,markerLabel:o="?",markerPlacement:i="top-end"}){let r=new Map,s=null,l=!1;function a(){s=null;let c=[...r.values()];if(c.length<=1){let m=c.length===1?c[0].el:null;m&&m.style.transform&&(m.style.transform="",n&&n());return}c.forEach(m=>{m.el.style.transform=""});let u=c.map(m=>{let h=m.el.getBoundingClientRect();return{x:h.left+h.width/2,y:h.top+h.height/2}}),g=be(u);c.forEach((m,h)=>{let{dx:y,dy:w}=g[h];m.el.style.transform=y||w?`translate(${y}px, ${w}px)`:""}),n&&n()}function p(){s!==null||l||(s=requestAnimationFrame(a))}function d(c){if(r.has(c.id))return;let u=Nt(c.title,o);document.body.appendChild(u);let g=()=>e(c,u);u.addEventListener("click",g);let m=he(hn(c),u,p,i),h=c.kind==="element"?c.target:null,y=()=>h&&h.classList.add(Ae),w=()=>h&&h.classList.remove(Ae);h&&(u.addEventListener("mouseenter",y),u.addEventListener("mouseleave",w),u.addEventListener("focus",y),u.addEventListener("blur",w));let v=!1,b=()=>{v||(v=!0,m(),u.removeEventListener("click",g),h&&(u.removeEventListener("mouseenter",y),u.removeEventListener("mouseleave",w),u.removeEventListener("focus",y),u.removeEventListener("blur",w),w()),u.remove(),r.delete(c.id),p())};r.set(c.id,{record:c,el:u,cleanup:b})}function f(c){let u=r.get(c);u&&u.cleanup()}function x(c){c.forEach(d)}return t.track(()=>{l=!0,s!==null&&(cancelAnimationFrame(s),s=null),[...r.values()].forEach(c=>c.cleanup())}),{mount:d,unmount:f,mountAll:x,has(c){return r.has(c)},findByKey(c){for(let u of r.values())if(u.record.key===c)return u;return null}}}function st(t,e,n,o){typeof t.querySelectorAll=="function"&&t.querySelectorAll("*").forEach(i=>{n&&i.matches(e)&&n(i),i.shadowRoot&&(o&&o(i.shadowRoot),st(i.shadowRoot,e,n,o))})}function ke(t,e){let n=[];return st(t,e,o=>n.push(o)),n}function gn(t){let e=[];return st(t,"*",null,n=>e.push(n)),e}function Re(t,e){let n=[],o=[];if(t.nodeType!==1)return{matches:n,shadowRoots:o};let i=t;return typeof i.matches=="function"&&i.matches(e)&&n.push(i),i.shadowRoot&&(o.push(i.shadowRoot),st(i.shadowRoot,e,r=>n.push(r),r=>o.push(r))),st(i,e,r=>n.push(r),r=>o.push(r)),{matches:n,shadowRoots:o}}function Ce({root:t=document,selector:e,onAdded:n,onRemoved:o}){let i=new Set,r=a=>{for(let p of a)p.addedNodes.forEach(d=>{let{matches:f,shadowRoots:x}=Re(d,e);f.forEach(c=>n(c)),x.forEach(l)}),p.removedNodes.forEach(d=>{Re(d,e).matches.forEach(f=>o(f))})},s=new MutationObserver(r);function l(a){i.has(a)||(i.add(a),s.observe(a,{childList:!0,subtree:!0}))}return l(t),gn(t).forEach(l),{disconnect(){s.disconnect(),i.clear()}}}var gt="data-help-title",Pt="data-help-text";function Tt(t="data-help-id"){return`[${t}], [${gt}]`}function Dt(t){let e=new Map;for(let n of t)n.kind==="element"&&e.set(n.key,n);return e}function Oe(t){return t.filter(e=>e.kind==="free").map(e=>({id:e.key,kind:"free",key:e.key,title:e.title,text:e.text,position:e.position}))}function Mt(t,e,n="data-help-id"){let o=t.getAttribute(n),i=o!=null?e.get(o):void 0,r=i?i.title:t.getAttribute(gt),s=i?i.text:t.getAttribute(Pt);return!r||!s?null:{id:t,kind:"element",key:o,title:r,text:s,target:t}}function Le(t,e=document,{silent:n=!1,attribute:o="data-help-id"}={}){let i=Dt(t),r=[];return ke(e,Tt(o)).forEach(s=>{let l=Mt(s,i,o);if(!l){if(!n){let a=s.getAttribute(o);console.warn(a!=null?`[help-layer] element with ${o}="${a}" has no matching helpConfig entry or inline ${gt}/${Pt}`:`[help-layer] element needs both ${gt} and ${Pt} (or a ${o} matching helpConfig)`)}return}r.push(l)}),r}function Se(t,{onClose:e,render:n,popupPlacement:o="bottom-start"}={}){let{root:i,titleEl:r,textEl:s,closeEl:l}=Ft();document.body.appendChild(i),l.addEventListener("click",()=>g());let a=null,p=null,d=null;function f(){d&&(d.cleanup(),d=null)}function x(m,h){r.textContent=m.title;let y=n?n(m):null;s.textContent="",y?s.appendChild(y):s.textContent=m.text,i.style.display="block",a=m.id,p=h,f(),d=ge(h,i,o),i.focus({preventScroll:!0})}function c(){d&&d.update()}function u(){let m=a!==null;f(),a=null,p=null,i.style.display="none",m&&e&&e()}function g(m){let h=m??p;u(),h&&h.isConnected&&typeof h.focus=="function"&&h.focus({preventScroll:!0})}return t.track(()=>{u(),i.remove()}),{root:i,isOpen(m){return a===m},getOpenId(){return a},open:x,close:g,reposition:c}}function Pe(){let t=[];return{track(e){t.push(e)},teardownAll(){for(;t.length>0;)t.pop()()}}}var xn="data-help-layer-style",yn=`
5
+ .help-layer-blocking-layer {
6
+ position: fixed;
7
+ inset: 0;
8
+ /* Default transparent (unchanged). Set --help-layer-overlay-bg to tint it into a scrim that signals
9
+ "the host app is inactive". The clip-path hole isn't painted, so the toggle stays untinted. */
10
+ background: var(--help-layer-overlay-bg, transparent);
11
+ /* Cursor over the blocked area only (the toggle shows through the hole and keeps its own cursor).
12
+ e.g. not-allowed / help makes "this won't respond" obvious without needing a tint. */
13
+ cursor: var(--help-layer-overlay-cursor, default);
14
+ z-index: 2147483000;
15
+ }
16
+
17
+ .help-layer-marker {
18
+ /* reset of the button element */
19
+ appearance: none;
20
+ -webkit-appearance: none;
21
+ margin: 0;
22
+ padding: 0;
23
+ border: none;
24
+ position: absolute;
25
+ top: 0;
26
+ left: 0;
27
+ width: var(--help-layer-marker-size, 22px);
28
+ height: var(--help-layer-marker-size, 22px);
29
+ border-radius: 50%;
30
+ background: var(--help-layer-marker-bg, #2563eb);
31
+ color: var(--help-layer-marker-color, #fff);
32
+ font-family: sans-serif;
33
+ font-size: 13px;
34
+ font-weight: bold;
35
+ line-height: var(--help-layer-marker-size, 22px);
36
+ text-align: center;
37
+ cursor: pointer;
38
+ user-select: none;
39
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
40
+ z-index: 2147483001;
41
+ }
42
+
43
+ .help-layer-marker:focus-visible {
44
+ outline: 3px solid var(--help-layer-accent, #1d4ed8);
45
+ outline-offset: 2px;
46
+ }
47
+
48
+ .help-layer-popup {
49
+ position: absolute;
50
+ top: 0;
51
+ left: 0;
52
+ display: none;
53
+ max-width: var(--help-layer-popup-max-width, 280px);
54
+ background: var(--help-layer-popup-bg, #fff);
55
+ color: var(--help-layer-popup-color, #1f2933);
56
+ border-radius: 6px;
57
+ padding: 12px 14px;
58
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
59
+ font-family: sans-serif;
60
+ font-size: 13px;
61
+ line-height: 1.5;
62
+ z-index: 2147483002;
63
+ }
64
+
65
+ .help-layer-popup:focus {
66
+ outline: none;
67
+ }
68
+
69
+ .help-layer-popup:focus-visible {
70
+ outline: 3px solid var(--help-layer-accent, #1d4ed8);
71
+ outline-offset: 2px;
72
+ }
73
+
74
+ .help-layer-popup__title {
75
+ font-weight: bold;
76
+ margin-bottom: 4px;
77
+ /* Reserve space so it doesn't overlap the \xD7 button at the top-right. */
78
+ padding-right: 16px;
79
+ }
80
+
81
+ .help-layer-popup__text {
82
+ /* Render the body's
83
+ as line breaks (still textContent, so no XSS risk). */
84
+ white-space: pre-line;
85
+ /* Keep long text from spilling off-screen; only the body scrolls within the popup. */
86
+ max-height: var(--help-layer-popup-max-height, 50vh);
87
+ overflow-y: auto;
88
+ }
89
+
90
+ .help-layer-popup__close {
91
+ /* reset of the button element */
92
+ appearance: none;
93
+ -webkit-appearance: none;
94
+ position: absolute;
95
+ top: 6px;
96
+ right: 6px;
97
+ width: 22px;
98
+ height: 22px;
99
+ padding: 0;
100
+ border: none;
101
+ border-radius: 4px;
102
+ background: transparent;
103
+ color: inherit;
104
+ font-size: 16px;
105
+ line-height: 1;
106
+ cursor: pointer;
107
+ }
108
+
109
+ .help-layer-popup__close:hover {
110
+ background: rgba(0, 0, 0, 0.08);
111
+ }
112
+
113
+ .help-layer-popup__close:focus-visible {
114
+ outline: 2px solid var(--help-layer-accent, #1d4ed8);
115
+ outline-offset: 1px;
116
+ }
117
+
118
+ /*
119
+ * Show an outline on the target element only while the marker is hovered/focused (clarifies "which element this explains").
120
+ * Make only the outline !important so it can beat host-side outline resets.
121
+ */
122
+ .help-layer-target-highlight {
123
+ outline: 2px solid var(--help-layer-accent, #1d4ed8) !important;
124
+ outline-offset: 2px !important;
125
+ }
126
+
127
+ /*
128
+ * Dark-mode defaults. If the user specifies CSS variables, those always win via var(), so here we
129
+ * only swap the dark fallback values (the properties themselves aren't re-declared).
130
+ */
131
+ @media (prefers-color-scheme: dark) {
132
+ .help-layer-popup {
133
+ background: var(--help-layer-popup-bg, #1f2933);
134
+ color: var(--help-layer-popup-color, #e5e7eb);
135
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.55);
136
+ }
137
+ }
138
+ `;function Te(t){let e=document.createElement("style");return e.setAttribute(xn,""),t&&e.setAttribute("nonce",t),e.textContent=yn,document.head.appendChild(e),e}function De(t){t.remove()}function wn(t){let e=typeof t=="string"?document.querySelector(t):t;if(!e)throw new Error(`help-layer: toggle element not found for selector "${t}"`);return e}function Me({config:t,toggle:e,onEnable:n,onDisable:o,onOpen:i,onClose:r,silent:s=!1,attribute:l="data-help-id",render:a,markerLabel:p="?",markerPlacement:d="top-end",popupPlacement:f="bottom-start",nonce:x}){let c=t;St(c);let u=e!=null?wn(e):null,g=null,m=null,h=null;function y(){if(g)return;g=Pe(),u&&g.track(()=>{u.isConnected&&typeof u.focus=="function"&&u.focus({preventScroll:!0})});let R=Te(x);g.track(()=>De(R));let L=ve(c),lt=Dt(L);m=Se(g,{onClose:r,render:a,popupPlacement:f}),h=Ee(g,{markerLabel:p,markerPlacement:d,onMarkerClick:(A,S)=>{if(m.isOpen(A.id)){m.close();return}m.open(A,S),i&&i(A)},onOverlapResolved:()=>m.reposition()}),h.mountAll(Oe(L)),h.mountAll(Le(L,document,{silent:s,attribute:l}));let ct=Ce({selector:Tt(l),onAdded:A=>{let S=Mt(A,lt,l);S&&!h.has(S.id)&&h.mount(S)},onRemoved:A=>{m.isOpen(A)&&m.close(u??void 0),h.unmount(A)}});g.track(()=>ct.disconnect()),ye(g,{toggleEl:u,onBackgroundClick:()=>m.close(),isLibraryElement:A=>!!A&&((u?u.contains(A):!1)||m.root.contains(A)||typeof A.closest=="function"&&!!A.closest(".help-layer-marker")),onEscape:()=>{m.getOpenId()!==null?m.close():b()}})}function w(){g&&(g.teardownAll(),g=null,m=null,h=null)}function v(){g||(y(),n&&n())}function b(){g&&(w(),o&&o())}function E(){g?b():v()}function F(R){if(g||v(),!h||!m)return;let L=h.findByKey(R);if(!L){s||console.warn(`[help-layer] open(): no help marker for key "${R}"`);return}m.open(L.record,L.el),i&&i(L.record)}function J(){m&&m.close()}function Y(R){St(R),c=R,g&&(w(),y())}return u&&u.addEventListener("click",E),{enable:v,disable:b,toggle:E,isActive(){return g!==null},open:F,close:J,update:Y,destroy(){b(),u&&u.removeEventListener("click",E)}}}function vn(t){return Me(t)}return He(bn);})();
139
+ //# sourceMappingURL=help-layer.iife.js.map